vlrdevapi 3.0.0__tar.gz → 3.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/.github/workflows/docs-check.yml +1 -1
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/.github/workflows/publish.yml +2 -2
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/.github/workflows/test.yml +6 -2
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/.gitignore +3 -1
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/CHANGELOG.md +50 -0
- vlrdevapi-3.0.0/README.md → vlrdevapi-3.1.0/PKG-INFO +157 -7
- vlrdevapi-3.0.0/PKG-INFO → vlrdevapi-3.1.0/README.md +86 -56
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/pyproject.toml +34 -2
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/scripts/download_fixtures.py +10 -7
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/__init__.py +1 -1
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_event/info/models.py +23 -1
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_event/list/models.py +32 -2
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_event/matches/models.py +32 -1
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_event/stages/models.py +17 -1
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_event/standings/models.py +27 -1
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_event/teams/models.py +43 -1
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_matches/completed/models.py +45 -1
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_matches/live/models.py +38 -1
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_matches/upcoming/models.py +42 -1
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_news/article/models.py +24 -2
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_news/list/models.py +25 -1
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_news/namespace.py +10 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_player/_common_parsing.py +2 -2
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_player/agents/models.py +47 -1
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_player/info/models.py +25 -2
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_player/matches/models.py +32 -1
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_player/placements/models.py +23 -1
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_player/profile/models.py +27 -2
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_player/teams/models.py +31 -1
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_series/economy/models.py +70 -3
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_series/info/models.py +61 -2
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_series/performance/models.py +64 -1
- vlrdevapi-3.1.0/src/vlrdevapi/_series/players/models.py +280 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_series/players/namespace.py +2 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_series/players/parser.py +63 -35
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_series/replay/models.py +68 -1
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_series/rounds/models.py +43 -1
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_series/vods/models.py +37 -1
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_team/completed_matches/models.py +35 -1
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_team/info/models.py +24 -1
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_team/info/namespace.py +0 -1
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_team/placements/models.py +36 -1
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_team/roster/models.py +25 -1
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_team/stats/models.py +27 -2
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_team/transactions/models.py +23 -1
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_team/upcoming_matches/models.py +32 -1
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/commons/__init__.py +2 -0
- vlrdevapi-3.1.0/src/vlrdevapi/commons/export.py +306 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/exceptions.py +5 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/fetcher.py +9 -1
- vlrdevapi-3.1.0/src/vlrdevapi/mcp/__init__.py +5 -0
- vlrdevapi-3.1.0/src/vlrdevapi/mcp/_common.py +69 -0
- vlrdevapi-3.1.0/src/vlrdevapi/mcp/server.py +33 -0
- vlrdevapi-3.1.0/src/vlrdevapi/mcp/tools/__init__.py +35 -0
- vlrdevapi-3.1.0/src/vlrdevapi/mcp/tools/composite.py +179 -0
- vlrdevapi-3.1.0/src/vlrdevapi/mcp/tools/events.py +88 -0
- vlrdevapi-3.1.0/src/vlrdevapi/mcp/tools/matches.py +40 -0
- vlrdevapi-3.1.0/src/vlrdevapi/mcp/tools/news.py +30 -0
- vlrdevapi-3.1.0/src/vlrdevapi/mcp/tools/players.py +74 -0
- vlrdevapi-3.1.0/src/vlrdevapi/mcp/tools/series.py +105 -0
- vlrdevapi-3.1.0/src/vlrdevapi/mcp/tools/teams.py +91 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/validators.py +2 -1
- vlrdevapi-3.1.0/uv.lock +2111 -0
- vlrdevapi-3.0.0/src/vlrdevapi/_series/players/models.py +0 -84
- vlrdevapi-3.0.0/uv.lock +0 -1165
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/.gitattributes +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/.github/workflows/cleanup-caches.yml +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/.nvmrc +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/.python-version +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/.readthedocs.yaml +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/CODE_OF_CONDUCT.md +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/CONTRIBUTING.md +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/LICENCE +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/SECURITY.md +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/SUPPORT.md +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/scripts/check_mdx_examples.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/__init__.pyi +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_base.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_cache.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_client.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_event/__init__.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_event/_common.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_event/info/__init__.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_event/info/namespace.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_event/info/namespace.pyi +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_event/info/parser.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_event/list/__init__.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_event/list/namespace.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_event/list/namespace.pyi +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_event/list/parser.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_event/matches/__init__.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_event/matches/namespace.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_event/matches/namespace.pyi +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_event/matches/parser.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_event/namespace.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_event/namespace.pyi +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_event/stages/__init__.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_event/stages/namespace.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_event/stages/namespace.pyi +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_event/stages/parser.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_event/standings/__init__.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_event/standings/namespace.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_event/standings/namespace.pyi +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_event/standings/parser.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_event/teams/__init__.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_event/teams/namespace.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_event/teams/namespace.pyi +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_event/teams/parser.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_matches/__init__.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_matches/common.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_matches/completed/__init__.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_matches/completed/namespace.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_matches/completed/namespace.pyi +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_matches/completed/parser.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_matches/live/__init__.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_matches/live/namespace.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_matches/live/namespace.pyi +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_matches/live/parser.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_matches/namespace.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_matches/namespace.pyi +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_matches/upcoming/__init__.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_matches/upcoming/namespace.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_matches/upcoming/namespace.pyi +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_matches/upcoming/parser.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_news/__init__.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_news/article/__init__.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_news/article/namespace.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_news/article/namespace.pyi +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_news/article/parser.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_news/common.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_news/list/__init__.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_news/list/namespace.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_news/list/namespace.pyi +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_news/list/parser.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_news/namespace.pyi +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_player/__init__.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_player/agents/__init__.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_player/agents/namespace.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_player/agents/namespace.pyi +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_player/agents/parser.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_player/info/__init__.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_player/info/namespace.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_player/info/namespace.pyi +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_player/info/parser.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_player/matches/__init__.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_player/matches/namespace.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_player/matches/namespace.pyi +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_player/matches/parser.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_player/namespace.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_player/namespace.pyi +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_player/placements/__init__.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_player/placements/namespace.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_player/placements/namespace.pyi +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_player/placements/parser.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_player/profile/__init__.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_player/profile/namespace.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_player/profile/namespace.pyi +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_player/profile/parser.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_player/teams/__init__.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_player/teams/namespace.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_player/teams/namespace.pyi +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_player/teams/parser.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_series/__init__.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_series/_utils.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_series/economy/__init__.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_series/economy/namespace.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_series/economy/namespace.pyi +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_series/economy/parser.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_series/info/__init__.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_series/info/namespace.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_series/info/namespace.pyi +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_series/info/parser.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_series/match_namespace.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_series/match_namespace.pyi +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_series/namespace.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_series/namespace.pyi +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_series/performance/__init__.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_series/performance/namespace.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_series/performance/namespace.pyi +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_series/performance/parser.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_series/players/__init__.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_series/players/namespace.pyi +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_series/replay/__init__.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_series/replay/namespace.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_series/replay/namespace.pyi +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_series/replay/parser.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_series/rounds/__init__.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_series/rounds/namespace.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_series/rounds/namespace.pyi +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_series/rounds/parser.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_series/vods/__init__.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_series/vods/namespace.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_series/vods/namespace.pyi +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_series/vods/parser.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_team/__init__.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_team/completed_matches/__init__.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_team/completed_matches/namespace.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_team/completed_matches/namespace.pyi +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_team/completed_matches/parser.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_team/info/__init__.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_team/info/namespace.pyi +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_team/info/parser.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_team/namespace.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_team/namespace.pyi +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_team/placements/__init__.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_team/placements/namespace.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_team/placements/namespace.pyi +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_team/placements/parser.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_team/roster/__init__.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_team/roster/namespace.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_team/roster/namespace.pyi +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_team/roster/parser.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_team/stats/__init__.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_team/stats/namespace.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_team/stats/namespace.pyi +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_team/stats/parser.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_team/transactions/__init__.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_team/transactions/namespace.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_team/transactions/namespace.pyi +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_team/transactions/parser.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_team/upcoming_matches/__init__.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_team/upcoming_matches/namespace.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_team/upcoming_matches/namespace.pyi +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_team/upcoming_matches/parser.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_utils/__init__.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_utils/pagination.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_utils/paths.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_utils/team_enrichment.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_utils/team_parsing.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/_utils/text.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/commons/countries.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/commons/datetime.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/commons/mappings.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/commons/prizes.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/commons/regions.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/commons/timezone.py +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/src/vlrdevapi/py.typed +0 -0
- {vlrdevapi-3.0.0 → vlrdevapi-3.1.0}/wrangler.jsonc +0 -0
|
@@ -22,7 +22,7 @@ jobs:
|
|
|
22
22
|
python-version: '3.12'
|
|
23
23
|
|
|
24
24
|
- name: Install package
|
|
25
|
-
run: uv sync --group dev
|
|
25
|
+
run: uv sync --group dev --all-extras
|
|
26
26
|
|
|
27
27
|
# Restore fixture cache scoped to the current ref (branch or main) and version prefix (v3).
|
|
28
28
|
# This ensures caches are never shared across unrelated branches or older versions.
|
|
@@ -13,7 +13,7 @@ jobs:
|
|
|
13
13
|
runs-on: ubuntu-latest
|
|
14
14
|
strategy:
|
|
15
15
|
matrix:
|
|
16
|
-
python-version: ['3.11', '3.12', '3.13', '3.14']
|
|
16
|
+
python-version: ['3.11', '3.12', '3.13', '3.14', '3.15']
|
|
17
17
|
|
|
18
18
|
steps:
|
|
19
19
|
- uses: actions/checkout@v7
|
|
@@ -23,7 +23,7 @@ jobs:
|
|
|
23
23
|
python-version: ${{ matrix.python-version }}
|
|
24
24
|
|
|
25
25
|
- name: Install dependencies
|
|
26
|
-
run: uv sync --group dev
|
|
26
|
+
run: uv sync --group dev --all-extras
|
|
27
27
|
|
|
28
28
|
- name: Download fixtures
|
|
29
29
|
run: uv run python scripts/download_fixtures.py
|
|
@@ -5,6 +5,8 @@ on:
|
|
|
5
5
|
paths:
|
|
6
6
|
- 'src/**'
|
|
7
7
|
- 'tests/**'
|
|
8
|
+
- 'pyproject.toml'
|
|
9
|
+
- 'uv.lock'
|
|
8
10
|
- 'scripts/download_fixtures.py'
|
|
9
11
|
- '.github/workflows/test.yml'
|
|
10
12
|
push:
|
|
@@ -12,6 +14,8 @@ on:
|
|
|
12
14
|
paths:
|
|
13
15
|
- 'src/**'
|
|
14
16
|
- 'tests/**'
|
|
17
|
+
- 'pyproject.toml'
|
|
18
|
+
- 'uv.lock'
|
|
15
19
|
- 'scripts/download_fixtures.py'
|
|
16
20
|
- '.github/workflows/test.yml'
|
|
17
21
|
|
|
@@ -20,7 +24,7 @@ jobs:
|
|
|
20
24
|
runs-on: ubuntu-latest
|
|
21
25
|
strategy:
|
|
22
26
|
matrix:
|
|
23
|
-
python-version: ['3.11', '3.12', '3.13', '3.14']
|
|
27
|
+
python-version: ['3.11', '3.12', '3.13', '3.14', '3.15']
|
|
24
28
|
|
|
25
29
|
steps:
|
|
26
30
|
- uses: actions/checkout@v7
|
|
@@ -30,7 +34,7 @@ jobs:
|
|
|
30
34
|
python-version: ${{ matrix.python-version }}
|
|
31
35
|
|
|
32
36
|
- name: Install dependencies
|
|
33
|
-
run: uv sync --group dev
|
|
37
|
+
run: uv sync --group dev --all-extras
|
|
34
38
|
|
|
35
39
|
- name: Download fixtures
|
|
36
40
|
run: uv run python scripts/download_fixtures.py
|
|
@@ -5,6 +5,55 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
7
7
|
|
|
8
|
+
## [3.1.0] - 2026-10-25
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Multi-Format Tabular Export Layer** — All Pydantic models now inherit from `ExportableModel`, enabling seamless tabular serialization into popular data science formats:
|
|
13
|
+
- `.to_records()` — extracts clean, unnested lists of Python dictionaries with semantic flattening across every namespace.
|
|
14
|
+
- `.to_df()` — converts records directly into a `pandas.DataFrame`.
|
|
15
|
+
- `.to_polars()` — converts records directly into a native `polars.DataFrame` with typed nested arrays (`pl.List(pl.String)`).
|
|
16
|
+
- `.to_csv(path=None)` — exports to standard CSV format with semicolon-delimited lists (`"Player;IGL"`) and in-memory string output when `path` is omitted.
|
|
17
|
+
- `.to_parquet(path, index=False)` — writes Apache Parquet files via pandas and pyarrow.
|
|
18
|
+
- `.to_excel(path, sheet_name="Sheet1", index=False)` — exports Excel `.xlsx` spreadsheets via pandas and openpyxl.
|
|
19
|
+
- `.to_json(path=None, indent=2)` — exports clean serialized JSON to string or disk with comprehensive recursive datetime and list handling.
|
|
20
|
+
- `.to_markdown(columns=None)` — generates GitHub-flavored markdown tables with proper column alignments, pipe escaping, and zero dependencies.
|
|
21
|
+
- **Granular Optional Dependency Extras** — Data export dependencies are strictly optional and modular:
|
|
22
|
+
- `vlrdevapi[pandas]`: installs `pandas>=2.0.0`
|
|
23
|
+
- `vlrdevapi[polars]`: installs `polars>=1.0.0`
|
|
24
|
+
- `vlrdevapi[excel]`: installs `openpyxl>=3.1.0` and `pandas>=2.0.0`
|
|
25
|
+
- `vlrdevapi[parquet]`: installs `pyarrow>=14.0.0` and `pandas>=2.0.0`
|
|
26
|
+
- `vlrdevapi[mcp]`: installs `mcp>=1.2.0`
|
|
27
|
+
- `vlrdevapi[all]`: installs all optional tabular, file, and MCP dependencies.
|
|
28
|
+
- Missing dependencies provide actionable, friendly install prompts with exact pip commands instead of generic import errors.
|
|
29
|
+
- **First-Class Model Context Protocol (MCP) Server** — Built-in MCP server for AI agents and LLMs (Claude Desktop, Cursor, Zed, Antigravity):
|
|
30
|
+
- CLI executable `vlrdevapi-mcp` (or `python -m vlrdevapi.mcp.server`, `uvx vlrdevapi-mcp`).
|
|
31
|
+
- Standard `stdio` transport with structured, formatted tool definitions.
|
|
32
|
+
- 40 tools covering all endpoints across Matches, Series, Teams, Players, Events, and News.
|
|
33
|
+
- High-level composite tools for complex agent tasks:
|
|
34
|
+
- `get_match_full_context(series_id)` — aggregates overview, player statistics, and streams/VODs into a single response.
|
|
35
|
+
- `get_team_overview(team_id)` — combines team info, roster, upcoming schedule, and tournament placements.
|
|
36
|
+
- `compare_teams(team_id_1, team_id_2, last_days=60)` — side-by-side comparison of two teams with rosters and recent map statistics/forms.
|
|
37
|
+
- `get_player_career_summary(player_id)` — consolidated dossier with profile, current teams, agent pool ratings, and tournament placements.
|
|
38
|
+
- `get_h2h_matchup(team_id_1, team_id_2)` — head-to-head match history between two organizations.
|
|
39
|
+
- `get_tournament_dossier(event_id)` — tournament overview with participating teams, stages, and standings.
|
|
40
|
+
- `get_live_and_upcoming_schedule(limit)` — live ticker combined with upcoming match schedule.
|
|
41
|
+
- `get_news_digest(limit, include_top_article_body)` — latest esports headlines with optional top article body.
|
|
42
|
+
- `series_map_summary(series_id, game_id)` — targeted map-level overview combining economy, replay timeline, and player stats.
|
|
43
|
+
- **Python 3.15 Ready** — Added Python 3.15 to the CI/CD test and publish workflows, verified across uv python matrix.
|
|
44
|
+
- **Production/Stable Status** — Package status promoted to `Development Status :: 5 - Production/Stable` in package metadata.
|
|
45
|
+
|
|
46
|
+
### Improved
|
|
47
|
+
|
|
48
|
+
- **Semantic Model Flattening** — Tabular export extractors (`.to_records()`) now generate clean, non-redundant rows:
|
|
49
|
+
- Upcoming/completed matches omit static status columns and resolve unannounced teams as `"TBD"`.
|
|
50
|
+
- Player agent statistics provide unnested, clean numeric columns (`acs`, `kd`, `adr`, `kast`, `kpr`, `apr`).
|
|
51
|
+
- Series player stats properly segregate map rows, labeling the aggregate map view as `"All Maps"`.
|
|
52
|
+
- Team rosters separate `primary_role` and multi-role lists.
|
|
53
|
+
- **Unified Serialization in MCP Tools** — MCP server tools now leverage `ExportableModel.to_dict()` and `ExportableModel.to_json()`, eliminating redundant ad-hoc JSON conversion and drastically speeding up tool invocations.
|
|
54
|
+
- **CSV Multi-Value List Formatting** — Agent lists and team roles are formatted with standard semicolon delimiters (`"neon;raze;jett"`) instead of Python `repr()` strings, ensuring full compatibility with spreadsheet applications and analytical parsers.
|
|
55
|
+
- **Parser Corrections** — Fixed swapped KAST and ADR columns in `player.agents()` table extraction to match VLR.gg's actual table headers.
|
|
56
|
+
|
|
8
57
|
## [3.0.0] - 2026-10-23
|
|
9
58
|
|
|
10
59
|
### Breaking
|
|
@@ -436,6 +485,7 @@ The API surface, module structure, and type system are all new.
|
|
|
436
485
|
- `aiohttp` and `asyncio` dependencies.
|
|
437
486
|
- v1.x match/event/team parsing modules.
|
|
438
487
|
|
|
488
|
+
[3.1.0]: https://github.com/Vanshbordia/vlrdevapi/compare/v3.0.0...v3.1.0
|
|
439
489
|
[3.0.0]: https://github.com/Vanshbordia/vlrdevapi/compare/v2.4.1...v3.0.0
|
|
440
490
|
[2.4.1]: https://github.com/Vanshbordia/vlrdevapi/compare/v2.4.0...v2.4.1
|
|
441
491
|
[2.4.0]: https://github.com/Vanshbordia/vlrdevapi/compare/v2.3.0...v2.4.0
|
|
@@ -1,3 +1,74 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: vlrdevapi
|
|
3
|
+
Version: 3.1.0
|
|
4
|
+
Summary: A Python library to scrape data from vlr.gg
|
|
5
|
+
Project-URL: Homepage, https://vlrdevapi.pages.dev/
|
|
6
|
+
Project-URL: Repository, https://github.com/Vanshbordia/vlrdevapi
|
|
7
|
+
Project-URL: Documentation, https://vlrdevapi.pages.dev/docs/
|
|
8
|
+
License: MIT License
|
|
9
|
+
|
|
10
|
+
Copyright (c) 2026 Vansh Bordia
|
|
11
|
+
|
|
12
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
13
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
14
|
+
in the Software without restriction, including without limitation the rights
|
|
15
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
16
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
17
|
+
furnished to do so, subject to the following conditions:
|
|
18
|
+
|
|
19
|
+
The above copyright notice and this permission notice shall be included in all
|
|
20
|
+
copies or substantial portions of the Software.
|
|
21
|
+
|
|
22
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
23
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
24
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
25
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
26
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
27
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
28
|
+
SOFTWARE.
|
|
29
|
+
License-File: LICENCE
|
|
30
|
+
Keywords: api,esports,python,scraper,sdk,valorant,valorant esports,vct,vlr.gg,vlrgg
|
|
31
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
32
|
+
Classifier: Intended Audience :: Developers
|
|
33
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
34
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
35
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
36
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
37
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
38
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
39
|
+
Classifier: Topic :: Games/Entertainment
|
|
40
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
41
|
+
Requires-Python: >=3.11
|
|
42
|
+
Requires-Dist: httpx>=0.28.1
|
|
43
|
+
Requires-Dist: pydantic>=2.14.1
|
|
44
|
+
Requires-Dist: selectolax>=1.0.0
|
|
45
|
+
Provides-Extra: all
|
|
46
|
+
Requires-Dist: mcp>=1.2.0; extra == 'all'
|
|
47
|
+
Requires-Dist: openpyxl>=3.1.0; extra == 'all'
|
|
48
|
+
Requires-Dist: pandas>=2.0.0; extra == 'all'
|
|
49
|
+
Requires-Dist: polars>=1.0.0; extra == 'all'
|
|
50
|
+
Requires-Dist: pyarrow>=14.0.0; extra == 'all'
|
|
51
|
+
Provides-Extra: arrow
|
|
52
|
+
Requires-Dist: pandas>=2.0.0; extra == 'arrow'
|
|
53
|
+
Requires-Dist: pyarrow>=14.0.0; extra == 'arrow'
|
|
54
|
+
Provides-Extra: excel
|
|
55
|
+
Requires-Dist: openpyxl>=3.1.0; extra == 'excel'
|
|
56
|
+
Requires-Dist: pandas>=2.0.0; extra == 'excel'
|
|
57
|
+
Provides-Extra: mcp
|
|
58
|
+
Requires-Dist: mcp>=1.2.0; extra == 'mcp'
|
|
59
|
+
Provides-Extra: pandas
|
|
60
|
+
Requires-Dist: pandas>=2.0.0; extra == 'pandas'
|
|
61
|
+
Provides-Extra: parquet
|
|
62
|
+
Requires-Dist: pandas>=2.0.0; extra == 'parquet'
|
|
63
|
+
Requires-Dist: pyarrow>=14.0.0; extra == 'parquet'
|
|
64
|
+
Provides-Extra: polars
|
|
65
|
+
Requires-Dist: polars>=1.0.0; extra == 'polars'
|
|
66
|
+
Provides-Extra: test
|
|
67
|
+
Requires-Dist: pytest-cov>=7.1.0; extra == 'test'
|
|
68
|
+
Requires-Dist: pytest>=9.1.1; extra == 'test'
|
|
69
|
+
Requires-Dist: respx>=0.23.1; extra == 'test'
|
|
70
|
+
Description-Content-Type: text/markdown
|
|
71
|
+
|
|
1
72
|
<div align="center">
|
|
2
73
|
|
|
3
74
|
<img src="official-docs/public/logo.png" width="120" alt="vlrdevapi logo">
|
|
@@ -6,8 +77,10 @@
|
|
|
6
77
|
|
|
7
78
|
**The modern, type-safe Python library for VLR.gg Valorant esports data**
|
|
8
79
|
|
|
9
|
-
[](https://pypi.org/project/vlrdevapi/)
|
|
81
|
+
[](https://pypi.org/project/vlrdevapi/)
|
|
82
|
+
[](https://pypi.org/project/vlrdevapi/)
|
|
83
|
+
[](https://vlrdevapi.pages.dev/docs/mcp/)
|
|
11
84
|
[](https://opensource.org/licenses/MIT)
|
|
12
85
|
[](https://vlrdevapi.pages.dev/docs/)
|
|
13
86
|
|
|
@@ -25,6 +98,8 @@ Whether you need live match tickers, tournament brackets, historical player rati
|
|
|
25
98
|
|
|
26
99
|
## Highlights
|
|
27
100
|
|
|
101
|
+
- **Universal Tabular Data Exports** — Seamlessly export any response into `pandas.DataFrame` (`.to_df()`), native `polars.DataFrame` (`.to_polars()`), `CSV` (`.to_csv()`), `Apache Parquet` (`.to_parquet()`), `Excel` (`.to_excel()`), formatted `JSON` (`.to_json()`), and `Markdown` (`.to_markdown()`).
|
|
102
|
+
- **Model Context Protocol (MCP) Server** — Connect Claude Desktop, Cursor, Zed, and AI agents directly to live Valorant data via the built-in `vlrdevapi-mcp` stdio server with 40 tools.
|
|
28
103
|
- **Complete Valorant Esports Coverage** — Tournaments, match schedules, live scoreboards, player careers, team rosters, roster transactions, and esports news.
|
|
29
104
|
- **2D Kill-Feed Replay Timelines** — Spatial `(x, y)` kill coordinates, viewing angles, kill distances, plant/defuse events, spike detonations (`boom`), resurrections, alive player snapshots, and buy-phase **player loadouts** (weapons, shields, spent credits, remaining bank).
|
|
30
105
|
- **Round-by-Round Economy** — Full team buy matrices (`loadout_value`, `spent`, `bank`, buy classifications like `Eco`, `Anti-eco`, `Semi-buy`, `Semi-rifle`, and `Full buy`).
|
|
@@ -41,14 +116,23 @@ Whether you need live match tickers, tournament brackets, historical player rati
|
|
|
41
116
|
Install `vlrdevapi` using [uv](https://docs.astral.sh/uv/) (recommended) or `pip`:
|
|
42
117
|
|
|
43
118
|
```bash
|
|
44
|
-
#
|
|
119
|
+
# Basic installation
|
|
120
|
+
pip install vlrdevapi
|
|
121
|
+
# or with uv
|
|
45
122
|
uv add vlrdevapi
|
|
46
123
|
|
|
47
|
-
#
|
|
48
|
-
pip install vlrdevapi
|
|
124
|
+
# With optional extras
|
|
125
|
+
pip install "vlrdevapi[pandas]" # pandas.DataFrame (.to_df())
|
|
126
|
+
pip install "vlrdevapi[polars]" # polars.DataFrame (.to_polars())
|
|
127
|
+
pip install "vlrdevapi[excel]" # Excel .xlsx (.to_excel())
|
|
128
|
+
pip install "vlrdevapi[parquet]" # Apache Parquet (.to_parquet())
|
|
129
|
+
pip install "vlrdevapi[mcp]" # MCP server (vlrdevapi-mcp)
|
|
130
|
+
|
|
131
|
+
# Install with all optional extras
|
|
132
|
+
pip install "vlrdevapi[all]"
|
|
49
133
|
```
|
|
50
134
|
|
|
51
|
-
> **Requirements:** Python 3.11 or
|
|
135
|
+
> **Requirements:** Python 3.11, 3.12, 3.13, 3.14, or 3.15.
|
|
52
136
|
|
|
53
137
|
---
|
|
54
138
|
|
|
@@ -252,11 +336,77 @@ Learn more in the [News Reference Documentation](https://vlrdevapi.pages.dev/doc
|
|
|
252
336
|
|
|
253
337
|
---
|
|
254
338
|
|
|
339
|
+
## Tabular Data Exports (Data Science & Analytics)
|
|
340
|
+
|
|
341
|
+
Every response model inherits from `ExportableModel`, enabling instant conversion to dataframes, spreadsheets, and serialized files:
|
|
342
|
+
|
|
343
|
+
```python
|
|
344
|
+
import vlrdevapi
|
|
345
|
+
|
|
346
|
+
# 1. Fetch player statistics for a grand final
|
|
347
|
+
players = vlrdevapi.series(754737).players()
|
|
348
|
+
|
|
349
|
+
# 2. Convert to pandas DataFrame
|
|
350
|
+
df = players.to_df()
|
|
351
|
+
print(df[["name", "team_name", "rating", "acs", "kills", "deaths"]].head())
|
|
352
|
+
|
|
353
|
+
# 3. Convert to Polars DataFrame (preserving typed list columns)
|
|
354
|
+
polars_df = players.to_polars()
|
|
355
|
+
|
|
356
|
+
# 4. Export to CSV (semicolon-delimited lists, e.g. "neon;raze;jett")
|
|
357
|
+
players.to_csv("grand_final_players.csv")
|
|
358
|
+
|
|
359
|
+
# 5. Export to Apache Parquet
|
|
360
|
+
players.to_parquet("grand_final_players.parquet")
|
|
361
|
+
|
|
362
|
+
# 6. Export to Excel (.xlsx)
|
|
363
|
+
players.to_excel("grand_final_players.xlsx", sheet_name="PlayerStats")
|
|
364
|
+
|
|
365
|
+
# 7. Export to JSON (in-memory string or written to disk)
|
|
366
|
+
json_payload = players.to_json(indent=2)
|
|
367
|
+
|
|
368
|
+
# 8. Print GitHub-flavored Markdown table
|
|
369
|
+
print(players.to_markdown())
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
---
|
|
373
|
+
|
|
374
|
+
## Model Context Protocol (MCP) Server
|
|
375
|
+
|
|
376
|
+
VLRdevAPI includes a native MCP server for AI assistants and autonomous coding agents:
|
|
377
|
+
|
|
378
|
+
```bash
|
|
379
|
+
# Run server over stdio
|
|
380
|
+
vlrdevapi-mcp
|
|
381
|
+
|
|
382
|
+
# Or zero-install with uvx
|
|
383
|
+
uvx --from "vlrdevapi[mcp]" vlrdevapi-mcp
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
### Claude Desktop & Cursor Integration
|
|
387
|
+
|
|
388
|
+
Add to your `claude_desktop_config.json` or `.cursor/mcp.json`:
|
|
389
|
+
|
|
390
|
+
```json
|
|
391
|
+
{
|
|
392
|
+
"mcpServers": {
|
|
393
|
+
"vlrdevapi": {
|
|
394
|
+
"command": "uvx",
|
|
395
|
+
"args": ["--from", "vlrdevapi[mcp]", "vlrdevapi-mcp"]
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
The MCP server provides **40 tools** covering matches, series, teams, players, tournaments, and editorial news, plus high-level composite tools (`get_match_full_context`, `get_team_overview`, `compare_teams`, `get_player_career_summary`, `get_h2h_matchup`, `get_tournament_dossier`, `get_live_and_upcoming_schedule`, `get_news_digest`, `series_map_summary`).
|
|
402
|
+
|
|
403
|
+
---
|
|
404
|
+
|
|
255
405
|
## Configuration & Error Handling
|
|
256
406
|
|
|
257
407
|
### Rate Limiting & Concurrency
|
|
258
408
|
|
|
259
|
-
`vlrdevapi` includes sensible defaults (`
|
|
409
|
+
`vlrdevapi` includes sensible defaults (`3.0` requests per second) to respect VLR.gg servers. When using `VLRClient`, you can customize client settings:
|
|
260
410
|
|
|
261
411
|
```python
|
|
262
412
|
from zoneinfo import ZoneInfo
|
|
@@ -1,52 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.5
|
|
2
|
-
Name: vlrdevapi
|
|
3
|
-
Version: 3.0.0
|
|
4
|
-
Summary: A Python library to scrape data from vlr.gg
|
|
5
|
-
Project-URL: Homepage, https://vlrdevapi.pages.dev/
|
|
6
|
-
Project-URL: Repository, https://github.com/Vanshbordia/vlrdevapi
|
|
7
|
-
Project-URL: Documentation, https://vlrdevapi.pages.dev/docs/
|
|
8
|
-
License: MIT License
|
|
9
|
-
|
|
10
|
-
Copyright (c) 2026 Vansh Bordia
|
|
11
|
-
|
|
12
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
13
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
14
|
-
in the Software without restriction, including without limitation the rights
|
|
15
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
16
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
17
|
-
furnished to do so, subject to the following conditions:
|
|
18
|
-
|
|
19
|
-
The above copyright notice and this permission notice shall be included in all
|
|
20
|
-
copies or substantial portions of the Software.
|
|
21
|
-
|
|
22
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
23
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
24
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
25
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
26
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
27
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
28
|
-
SOFTWARE.
|
|
29
|
-
License-File: LICENCE
|
|
30
|
-
Keywords: api,esports,python,scraper,sdk,valorant,valorant esports,vct,vlr.gg,vlrgg
|
|
31
|
-
Classifier: Development Status :: 3 - Alpha
|
|
32
|
-
Classifier: Intended Audience :: Developers
|
|
33
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
34
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
35
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
36
|
-
Classifier: Programming Language :: Python :: 3.13
|
|
37
|
-
Classifier: Programming Language :: Python :: 3.14
|
|
38
|
-
Classifier: Topic :: Games/Entertainment
|
|
39
|
-
Classifier: Topic :: Internet :: WWW/HTTP
|
|
40
|
-
Requires-Python: >=3.11
|
|
41
|
-
Requires-Dist: httpx>=0.28.1
|
|
42
|
-
Requires-Dist: pydantic>=2.14.1
|
|
43
|
-
Requires-Dist: selectolax>=1.0.0
|
|
44
|
-
Provides-Extra: test
|
|
45
|
-
Requires-Dist: pytest-cov>=7.1.0; extra == 'test'
|
|
46
|
-
Requires-Dist: pytest>=9.1.1; extra == 'test'
|
|
47
|
-
Requires-Dist: respx>=0.23.1; extra == 'test'
|
|
48
|
-
Description-Content-Type: text/markdown
|
|
49
|
-
|
|
50
1
|
<div align="center">
|
|
51
2
|
|
|
52
3
|
<img src="official-docs/public/logo.png" width="120" alt="vlrdevapi logo">
|
|
@@ -55,8 +6,10 @@ Description-Content-Type: text/markdown
|
|
|
55
6
|
|
|
56
7
|
**The modern, type-safe Python library for VLR.gg Valorant esports data**
|
|
57
8
|
|
|
58
|
-
[](https://pypi.org/project/vlrdevapi/)
|
|
10
|
+
[](https://pypi.org/project/vlrdevapi/)
|
|
11
|
+
[](https://pypi.org/project/vlrdevapi/)
|
|
12
|
+
[](https://vlrdevapi.pages.dev/docs/mcp/)
|
|
60
13
|
[](https://opensource.org/licenses/MIT)
|
|
61
14
|
[](https://vlrdevapi.pages.dev/docs/)
|
|
62
15
|
|
|
@@ -74,6 +27,8 @@ Whether you need live match tickers, tournament brackets, historical player rati
|
|
|
74
27
|
|
|
75
28
|
## Highlights
|
|
76
29
|
|
|
30
|
+
- **Universal Tabular Data Exports** — Seamlessly export any response into `pandas.DataFrame` (`.to_df()`), native `polars.DataFrame` (`.to_polars()`), `CSV` (`.to_csv()`), `Apache Parquet` (`.to_parquet()`), `Excel` (`.to_excel()`), formatted `JSON` (`.to_json()`), and `Markdown` (`.to_markdown()`).
|
|
31
|
+
- **Model Context Protocol (MCP) Server** — Connect Claude Desktop, Cursor, Zed, and AI agents directly to live Valorant data via the built-in `vlrdevapi-mcp` stdio server with 40 tools.
|
|
77
32
|
- **Complete Valorant Esports Coverage** — Tournaments, match schedules, live scoreboards, player careers, team rosters, roster transactions, and esports news.
|
|
78
33
|
- **2D Kill-Feed Replay Timelines** — Spatial `(x, y)` kill coordinates, viewing angles, kill distances, plant/defuse events, spike detonations (`boom`), resurrections, alive player snapshots, and buy-phase **player loadouts** (weapons, shields, spent credits, remaining bank).
|
|
79
34
|
- **Round-by-Round Economy** — Full team buy matrices (`loadout_value`, `spent`, `bank`, buy classifications like `Eco`, `Anti-eco`, `Semi-buy`, `Semi-rifle`, and `Full buy`).
|
|
@@ -90,14 +45,23 @@ Whether you need live match tickers, tournament brackets, historical player rati
|
|
|
90
45
|
Install `vlrdevapi` using [uv](https://docs.astral.sh/uv/) (recommended) or `pip`:
|
|
91
46
|
|
|
92
47
|
```bash
|
|
93
|
-
#
|
|
48
|
+
# Basic installation
|
|
49
|
+
pip install vlrdevapi
|
|
50
|
+
# or with uv
|
|
94
51
|
uv add vlrdevapi
|
|
95
52
|
|
|
96
|
-
#
|
|
97
|
-
pip install vlrdevapi
|
|
53
|
+
# With optional extras
|
|
54
|
+
pip install "vlrdevapi[pandas]" # pandas.DataFrame (.to_df())
|
|
55
|
+
pip install "vlrdevapi[polars]" # polars.DataFrame (.to_polars())
|
|
56
|
+
pip install "vlrdevapi[excel]" # Excel .xlsx (.to_excel())
|
|
57
|
+
pip install "vlrdevapi[parquet]" # Apache Parquet (.to_parquet())
|
|
58
|
+
pip install "vlrdevapi[mcp]" # MCP server (vlrdevapi-mcp)
|
|
59
|
+
|
|
60
|
+
# Install with all optional extras
|
|
61
|
+
pip install "vlrdevapi[all]"
|
|
98
62
|
```
|
|
99
63
|
|
|
100
|
-
> **Requirements:** Python 3.11 or
|
|
64
|
+
> **Requirements:** Python 3.11, 3.12, 3.13, 3.14, or 3.15.
|
|
101
65
|
|
|
102
66
|
---
|
|
103
67
|
|
|
@@ -301,11 +265,77 @@ Learn more in the [News Reference Documentation](https://vlrdevapi.pages.dev/doc
|
|
|
301
265
|
|
|
302
266
|
---
|
|
303
267
|
|
|
268
|
+
## Tabular Data Exports (Data Science & Analytics)
|
|
269
|
+
|
|
270
|
+
Every response model inherits from `ExportableModel`, enabling instant conversion to dataframes, spreadsheets, and serialized files:
|
|
271
|
+
|
|
272
|
+
```python
|
|
273
|
+
import vlrdevapi
|
|
274
|
+
|
|
275
|
+
# 1. Fetch player statistics for a grand final
|
|
276
|
+
players = vlrdevapi.series(754737).players()
|
|
277
|
+
|
|
278
|
+
# 2. Convert to pandas DataFrame
|
|
279
|
+
df = players.to_df()
|
|
280
|
+
print(df[["name", "team_name", "rating", "acs", "kills", "deaths"]].head())
|
|
281
|
+
|
|
282
|
+
# 3. Convert to Polars DataFrame (preserving typed list columns)
|
|
283
|
+
polars_df = players.to_polars()
|
|
284
|
+
|
|
285
|
+
# 4. Export to CSV (semicolon-delimited lists, e.g. "neon;raze;jett")
|
|
286
|
+
players.to_csv("grand_final_players.csv")
|
|
287
|
+
|
|
288
|
+
# 5. Export to Apache Parquet
|
|
289
|
+
players.to_parquet("grand_final_players.parquet")
|
|
290
|
+
|
|
291
|
+
# 6. Export to Excel (.xlsx)
|
|
292
|
+
players.to_excel("grand_final_players.xlsx", sheet_name="PlayerStats")
|
|
293
|
+
|
|
294
|
+
# 7. Export to JSON (in-memory string or written to disk)
|
|
295
|
+
json_payload = players.to_json(indent=2)
|
|
296
|
+
|
|
297
|
+
# 8. Print GitHub-flavored Markdown table
|
|
298
|
+
print(players.to_markdown())
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
## Model Context Protocol (MCP) Server
|
|
304
|
+
|
|
305
|
+
VLRdevAPI includes a native MCP server for AI assistants and autonomous coding agents:
|
|
306
|
+
|
|
307
|
+
```bash
|
|
308
|
+
# Run server over stdio
|
|
309
|
+
vlrdevapi-mcp
|
|
310
|
+
|
|
311
|
+
# Or zero-install with uvx
|
|
312
|
+
uvx --from "vlrdevapi[mcp]" vlrdevapi-mcp
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### Claude Desktop & Cursor Integration
|
|
316
|
+
|
|
317
|
+
Add to your `claude_desktop_config.json` or `.cursor/mcp.json`:
|
|
318
|
+
|
|
319
|
+
```json
|
|
320
|
+
{
|
|
321
|
+
"mcpServers": {
|
|
322
|
+
"vlrdevapi": {
|
|
323
|
+
"command": "uvx",
|
|
324
|
+
"args": ["--from", "vlrdevapi[mcp]", "vlrdevapi-mcp"]
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
The MCP server provides **40 tools** covering matches, series, teams, players, tournaments, and editorial news, plus high-level composite tools (`get_match_full_context`, `get_team_overview`, `compare_teams`, `get_player_career_summary`, `get_h2h_matchup`, `get_tournament_dossier`, `get_live_and_upcoming_schedule`, `get_news_digest`, `series_map_summary`).
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
304
334
|
## Configuration & Error Handling
|
|
305
335
|
|
|
306
336
|
### Rate Limiting & Concurrency
|
|
307
337
|
|
|
308
|
-
`vlrdevapi` includes sensible defaults (`
|
|
338
|
+
`vlrdevapi` includes sensible defaults (`3.0` requests per second) to respect VLR.gg servers. When using `VLRClient`, you can customize client settings:
|
|
309
339
|
|
|
310
340
|
```python
|
|
311
341
|
from zoneinfo import ZoneInfo
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "vlrdevapi"
|
|
7
|
-
version = "3.
|
|
7
|
+
version = "3.1.0"
|
|
8
8
|
description = "A Python library to scrape data from vlr.gg"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -22,13 +22,14 @@ keywords = [
|
|
|
22
22
|
]
|
|
23
23
|
license = {file = "LICENCE"}
|
|
24
24
|
classifiers = [
|
|
25
|
-
"Development Status ::
|
|
25
|
+
"Development Status :: 5 - Production/Stable",
|
|
26
26
|
"Intended Audience :: Developers",
|
|
27
27
|
"License :: OSI Approved :: MIT License",
|
|
28
28
|
"Programming Language :: Python :: 3.11",
|
|
29
29
|
"Programming Language :: Python :: 3.12",
|
|
30
30
|
"Programming Language :: Python :: 3.13",
|
|
31
31
|
"Programming Language :: Python :: 3.14",
|
|
32
|
+
"Programming Language :: Python :: 3.15",
|
|
32
33
|
"Topic :: Games/Entertainment",
|
|
33
34
|
"Topic :: Internet :: WWW/HTTP",
|
|
34
35
|
]
|
|
@@ -49,6 +50,37 @@ test = [
|
|
|
49
50
|
"respx>=0.23.1",
|
|
50
51
|
"pytest-cov>=7.1.0",
|
|
51
52
|
]
|
|
53
|
+
mcp = [
|
|
54
|
+
"mcp>=1.2.0",
|
|
55
|
+
]
|
|
56
|
+
pandas = [
|
|
57
|
+
"pandas>=2.0.0",
|
|
58
|
+
]
|
|
59
|
+
polars = [
|
|
60
|
+
"polars>=1.0.0",
|
|
61
|
+
]
|
|
62
|
+
excel = [
|
|
63
|
+
"openpyxl>=3.1.0",
|
|
64
|
+
"pandas>=2.0.0",
|
|
65
|
+
]
|
|
66
|
+
parquet = [
|
|
67
|
+
"pyarrow>=14.0.0",
|
|
68
|
+
"pandas>=2.0.0",
|
|
69
|
+
]
|
|
70
|
+
arrow = [
|
|
71
|
+
"pyarrow>=14.0.0",
|
|
72
|
+
"pandas>=2.0.0",
|
|
73
|
+
]
|
|
74
|
+
all = [
|
|
75
|
+
"mcp>=1.2.0",
|
|
76
|
+
"pandas>=2.0.0",
|
|
77
|
+
"polars>=1.0.0",
|
|
78
|
+
"openpyxl>=3.1.0",
|
|
79
|
+
"pyarrow>=14.0.0",
|
|
80
|
+
]
|
|
81
|
+
|
|
82
|
+
[project.scripts]
|
|
83
|
+
vlrdevapi-mcp = "vlrdevapi.mcp.server:main"
|
|
52
84
|
|
|
53
85
|
[dependency-groups]
|
|
54
86
|
dev = [
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
from pathlib import Path
|
|
3
|
-
from typing import Dict
|
|
4
3
|
|
|
5
4
|
import httpx
|
|
6
5
|
|
|
@@ -19,7 +18,7 @@ HEADERS = {
|
|
|
19
18
|
}
|
|
20
19
|
|
|
21
20
|
|
|
22
|
-
ROUTES: list[tuple[str, str,
|
|
21
|
+
ROUTES: list[tuple[str, str, dict[str, str]]] = []
|
|
23
22
|
|
|
24
23
|
# ── Matches ────────────────────────────────────────────────────
|
|
25
24
|
ROUTES += [
|
|
@@ -275,6 +274,11 @@ ROUTES += [
|
|
|
275
274
|
"team/14137/overview_light.html",
|
|
276
275
|
{},
|
|
277
276
|
),
|
|
277
|
+
(
|
|
278
|
+
"/team/8050/mibr-gc",
|
|
279
|
+
"team/8050/overview_light.html",
|
|
280
|
+
{},
|
|
281
|
+
),
|
|
278
282
|
(
|
|
279
283
|
"/team/matches/120/?core_id=all&group=completed&page=2",
|
|
280
284
|
"team/120/completed_matches_page2.html",
|
|
@@ -487,8 +491,8 @@ for _gid in GAMES_BO5_2026:
|
|
|
487
491
|
]
|
|
488
492
|
|
|
489
493
|
# ── Upcoming series (best_of + TBD map tabs) ─────────────────
|
|
490
|
-
SERIES_ID_UPCOMING =
|
|
491
|
-
SERIES_SLUG_UPCOMING = "
|
|
494
|
+
SERIES_ID_UPCOMING = 773433
|
|
495
|
+
SERIES_SLUG_UPCOMING = "mibr-gc-vs-gentle-mates-gc-game-changers-2026-championship-s-o-paulo-ubsf"
|
|
492
496
|
|
|
493
497
|
ROUTES.append(
|
|
494
498
|
(
|
|
@@ -529,7 +533,7 @@ for _sid, _sslug, _gid in SERIES_LOG_SAMPLES:
|
|
|
529
533
|
# Unplayed map (Abyss, game 4 of the 2026 GF): graceful empty replay data.
|
|
530
534
|
ROUTES.append(
|
|
531
535
|
(
|
|
532
|
-
|
|
536
|
+
"/754737/loud-vs-team-vitality-valorant-champions-2026-gf/?game=283290&tab=logs",
|
|
533
537
|
"series/754737_loud-vs-team-vitality-valorant-champions-2026-gf/game_283290_logs.html",
|
|
534
538
|
{},
|
|
535
539
|
),
|
|
@@ -560,8 +564,7 @@ def _parse_last_page(html: str) -> int | None:
|
|
|
560
564
|
for el in pagination.css("a.btn.mod-page, span.btn.mod-page"):
|
|
561
565
|
try:
|
|
562
566
|
num = int(el.text(strip=True))
|
|
563
|
-
|
|
564
|
-
max_page = num
|
|
567
|
+
max_page = max(max_page, num)
|
|
565
568
|
except ValueError:
|
|
566
569
|
continue
|
|
567
570
|
return max_page if max_page > 0 else None
|