vlrdevapi 2.4.0__tar.gz → 3.0.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.
Files changed (237) hide show
  1. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/.github/workflows/docs-check.yml +13 -24
  2. vlrdevapi-3.0.0/.nvmrc +1 -0
  3. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/.readthedocs.yaml +3 -2
  4. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/CHANGELOG.md +159 -0
  5. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/CODE_OF_CONDUCT.md +2 -1
  6. vlrdevapi-3.0.0/PKG-INFO +394 -0
  7. vlrdevapi-3.0.0/README.md +345 -0
  8. vlrdevapi-3.0.0/SECURITY.md +11 -0
  9. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/SUPPORT.md +2 -2
  10. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/pyproject.toml +24 -12
  11. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/scripts/check_mdx_examples.py +32 -0
  12. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/scripts/download_fixtures.py +131 -1
  13. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/__init__.py +1 -1
  14. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/__init__.pyi +13 -1
  15. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_base.py +65 -23
  16. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_client.py +4 -4
  17. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_event/info/namespace.pyi +6 -1
  18. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_event/info/parser.py +10 -9
  19. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_event/list/namespace.pyi +6 -1
  20. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_event/list/parser.py +3 -2
  21. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_event/matches/models.py +7 -0
  22. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_event/matches/namespace.py +1 -1
  23. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_event/matches/namespace.pyi +5 -1
  24. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_event/matches/parser.py +24 -7
  25. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_event/namespace.pyi +5 -2
  26. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_event/stages/namespace.pyi +6 -1
  27. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_event/stages/parser.py +3 -2
  28. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_event/standings/namespace.pyi +6 -1
  29. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_event/standings/parser.py +6 -4
  30. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_event/teams/namespace.pyi +6 -1
  31. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_event/teams/parser.py +2 -1
  32. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_matches/common.py +7 -45
  33. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_matches/completed/namespace.pyi +6 -1
  34. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_matches/completed/parser.py +3 -2
  35. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_matches/live/namespace.pyi +6 -1
  36. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_matches/live/parser.py +2 -1
  37. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_matches/namespace.pyi +6 -2
  38. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_matches/upcoming/namespace.pyi +6 -1
  39. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_matches/upcoming/parser.py +3 -2
  40. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_news/article/parser.py +5 -4
  41. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_news/common.py +1 -1
  42. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_news/list/parser.py +2 -1
  43. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_player/__init__.py +1 -1
  44. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_player/_common_parsing.py +27 -9
  45. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_player/agents/namespace.pyi +5 -1
  46. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_player/agents/parser.py +1 -1
  47. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_player/info/models.py +6 -0
  48. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_player/info/namespace.py +2 -2
  49. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_player/info/namespace.pyi +6 -1
  50. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_player/info/parser.py +4 -1
  51. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_player/matches/namespace.pyi +7 -2
  52. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_player/matches/parser.py +4 -14
  53. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_player/namespace.py +46 -5
  54. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_player/namespace.pyi +13 -2
  55. vlrdevapi-3.0.0/src/vlrdevapi/_player/placements/__init__.py +11 -0
  56. vlrdevapi-3.0.0/src/vlrdevapi/_player/placements/models.py +48 -0
  57. vlrdevapi-3.0.0/src/vlrdevapi/_player/placements/namespace.py +63 -0
  58. vlrdevapi-3.0.0/src/vlrdevapi/_player/placements/namespace.pyi +21 -0
  59. vlrdevapi-3.0.0/src/vlrdevapi/_player/placements/parser.py +147 -0
  60. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_player/profile/namespace.pyi +6 -1
  61. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_player/profile/parser.py +1 -1
  62. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_player/teams/namespace.pyi +6 -1
  63. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_player/teams/parser.py +1 -1
  64. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_series/__init__.py +3 -1
  65. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_series/_utils.py +1 -1
  66. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_series/economy/__init__.py +6 -0
  67. vlrdevapi-3.0.0/src/vlrdevapi/_series/economy/models.py +221 -0
  68. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_series/economy/namespace.py +26 -17
  69. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_series/economy/namespace.pyi +4 -2
  70. vlrdevapi-3.0.0/src/vlrdevapi/_series/economy/parser.py +405 -0
  71. vlrdevapi-3.0.0/src/vlrdevapi/_series/info/__init__.py +17 -0
  72. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_series/info/models.py +42 -0
  73. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_series/info/namespace.pyi +6 -1
  74. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_series/info/parser.py +133 -13
  75. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_series/match_namespace.py +42 -1
  76. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_series/match_namespace.pyi +16 -6
  77. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_series/namespace.py +22 -2
  78. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_series/namespace.pyi +10 -1
  79. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_series/performance/namespace.pyi +6 -1
  80. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_series/performance/parser.py +2 -1
  81. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_series/players/namespace.pyi +6 -1
  82. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_series/players/parser.py +54 -16
  83. vlrdevapi-3.0.0/src/vlrdevapi/_series/replay/__init__.py +25 -0
  84. vlrdevapi-3.0.0/src/vlrdevapi/_series/replay/models.py +344 -0
  85. vlrdevapi-3.0.0/src/vlrdevapi/_series/replay/namespace.py +214 -0
  86. vlrdevapi-3.0.0/src/vlrdevapi/_series/replay/namespace.pyi +18 -0
  87. vlrdevapi-3.0.0/src/vlrdevapi/_series/replay/parser.py +431 -0
  88. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_series/rounds/models.py +42 -11
  89. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_series/rounds/namespace.py +85 -10
  90. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_series/rounds/namespace.pyi +3 -1
  91. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_series/rounds/parser.py +19 -10
  92. vlrdevapi-3.0.0/src/vlrdevapi/_series/vods/__init__.py +8 -0
  93. vlrdevapi-3.0.0/src/vlrdevapi/_series/vods/models.py +63 -0
  94. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_series/vods/namespace.py +10 -10
  95. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_series/vods/namespace.pyi +6 -1
  96. vlrdevapi-3.0.0/src/vlrdevapi/_series/vods/parser.py +135 -0
  97. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_team/completed_matches/models.py +12 -1
  98. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_team/completed_matches/namespace.py +46 -7
  99. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_team/completed_matches/namespace.pyi +12 -3
  100. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_team/completed_matches/parser.py +23 -9
  101. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_team/info/namespace.py +3 -9
  102. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_team/info/namespace.pyi +6 -1
  103. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_team/info/parser.py +1 -1
  104. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_team/namespace.py +16 -3
  105. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_team/namespace.pyi +11 -5
  106. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_team/placements/namespace.pyi +6 -1
  107. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_team/placements/parser.py +4 -3
  108. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_team/roster/namespace.pyi +6 -1
  109. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_team/roster/parser.py +7 -5
  110. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_team/stats/namespace.py +1 -1
  111. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_team/stats/namespace.pyi +5 -2
  112. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_team/stats/parser.py +2 -1
  113. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_team/transactions/namespace.pyi +6 -1
  114. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_team/transactions/parser.py +2 -1
  115. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_team/upcoming_matches/models.py +8 -1
  116. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_team/upcoming_matches/namespace.pyi +6 -1
  117. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_team/upcoming_matches/parser.py +27 -11
  118. vlrdevapi-3.0.0/src/vlrdevapi/_utils/pagination.py +78 -0
  119. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_utils/team_enrichment.py +6 -5
  120. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_utils/team_parsing.py +1 -1
  121. vlrdevapi-3.0.0/src/vlrdevapi/_utils/text.py +50 -0
  122. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/commons/datetime.py +1 -3
  123. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/commons/timezone.py +1 -1
  124. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/exceptions.py +6 -0
  125. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/fetcher.py +11 -2
  126. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/validators.py +21 -12
  127. vlrdevapi-3.0.0/uv.lock +1165 -0
  128. vlrdevapi-2.4.0/PKG-INFO +0 -226
  129. vlrdevapi-2.4.0/README.md +0 -178
  130. vlrdevapi-2.4.0/SECURITY.md +0 -9
  131. vlrdevapi-2.4.0/src/vlrdevapi/_series/economy/models.py +0 -85
  132. vlrdevapi-2.4.0/src/vlrdevapi/_series/economy/parser.py +0 -144
  133. vlrdevapi-2.4.0/src/vlrdevapi/_series/info/__init__.py +0 -4
  134. vlrdevapi-2.4.0/src/vlrdevapi/_series/vods/__init__.py +0 -4
  135. vlrdevapi-2.4.0/src/vlrdevapi/_series/vods/models.py +0 -28
  136. vlrdevapi-2.4.0/src/vlrdevapi/_series/vods/parser.py +0 -30
  137. vlrdevapi-2.4.0/src/vlrdevapi/_utils/pagination.py +0 -37
  138. vlrdevapi-2.4.0/uv.lock +0 -1049
  139. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/.gitattributes +0 -0
  140. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  141. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  142. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/.github/workflows/cleanup-caches.yml +0 -0
  143. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/.github/workflows/publish.yml +0 -0
  144. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/.github/workflows/test.yml +0 -0
  145. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/.gitignore +0 -0
  146. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/.python-version +0 -0
  147. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/CONTRIBUTING.md +0 -0
  148. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/LICENCE +0 -0
  149. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_cache.py +0 -0
  150. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_event/__init__.py +0 -0
  151. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_event/_common.py +0 -0
  152. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_event/info/__init__.py +0 -0
  153. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_event/info/models.py +0 -0
  154. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_event/info/namespace.py +0 -0
  155. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_event/list/__init__.py +0 -0
  156. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_event/list/models.py +0 -0
  157. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_event/list/namespace.py +0 -0
  158. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_event/matches/__init__.py +0 -0
  159. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_event/namespace.py +0 -0
  160. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_event/stages/__init__.py +0 -0
  161. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_event/stages/models.py +0 -0
  162. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_event/stages/namespace.py +0 -0
  163. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_event/standings/__init__.py +0 -0
  164. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_event/standings/models.py +0 -0
  165. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_event/standings/namespace.py +0 -0
  166. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_event/teams/__init__.py +1 -1
  167. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_event/teams/models.py +0 -0
  168. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_event/teams/namespace.py +0 -0
  169. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_matches/__init__.py +0 -0
  170. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_matches/completed/__init__.py +0 -0
  171. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_matches/completed/models.py +0 -0
  172. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_matches/completed/namespace.py +0 -0
  173. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_matches/live/__init__.py +0 -0
  174. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_matches/live/models.py +0 -0
  175. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_matches/live/namespace.py +0 -0
  176. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_matches/namespace.py +0 -0
  177. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_matches/upcoming/__init__.py +0 -0
  178. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_matches/upcoming/models.py +0 -0
  179. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_matches/upcoming/namespace.py +0 -0
  180. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_news/__init__.py +0 -0
  181. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_news/article/__init__.py +0 -0
  182. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_news/article/models.py +0 -0
  183. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_news/article/namespace.py +0 -0
  184. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_news/article/namespace.pyi +0 -0
  185. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_news/list/__init__.py +0 -0
  186. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_news/list/models.py +0 -0
  187. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_news/list/namespace.py +0 -0
  188. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_news/list/namespace.pyi +0 -0
  189. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_news/namespace.py +0 -0
  190. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_news/namespace.pyi +0 -0
  191. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_player/agents/__init__.py +0 -0
  192. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_player/agents/models.py +0 -0
  193. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_player/agents/namespace.py +0 -0
  194. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_player/info/__init__.py +0 -0
  195. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_player/matches/__init__.py +0 -0
  196. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_player/matches/models.py +0 -0
  197. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_player/matches/namespace.py +0 -0
  198. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_player/profile/__init__.py +0 -0
  199. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_player/profile/models.py +0 -0
  200. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_player/profile/namespace.py +0 -0
  201. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_player/teams/__init__.py +0 -0
  202. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_player/teams/models.py +0 -0
  203. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_player/teams/namespace.py +0 -0
  204. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_series/info/namespace.py +0 -0
  205. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_series/performance/__init__.py +0 -0
  206. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_series/performance/models.py +0 -0
  207. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_series/performance/namespace.py +0 -0
  208. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_series/players/__init__.py +0 -0
  209. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_series/players/models.py +0 -0
  210. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_series/players/namespace.py +0 -0
  211. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_series/rounds/__init__.py +0 -0
  212. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_team/__init__.py +0 -0
  213. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_team/completed_matches/__init__.py +0 -0
  214. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_team/info/__init__.py +0 -0
  215. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_team/info/models.py +0 -0
  216. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_team/placements/__init__.py +0 -0
  217. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_team/placements/models.py +0 -0
  218. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_team/placements/namespace.py +0 -0
  219. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_team/roster/__init__.py +0 -0
  220. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_team/roster/models.py +0 -0
  221. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_team/roster/namespace.py +0 -0
  222. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_team/stats/__init__.py +0 -0
  223. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_team/stats/models.py +0 -0
  224. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_team/transactions/__init__.py +0 -0
  225. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_team/transactions/models.py +0 -0
  226. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_team/transactions/namespace.py +0 -0
  227. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_team/upcoming_matches/__init__.py +0 -0
  228. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_team/upcoming_matches/namespace.py +0 -0
  229. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_utils/__init__.py +0 -0
  230. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/_utils/paths.py +0 -0
  231. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/commons/__init__.py +0 -0
  232. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/commons/countries.py +0 -0
  233. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/commons/mappings.py +0 -0
  234. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/commons/prizes.py +0 -0
  235. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/commons/regions.py +0 -0
  236. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/src/vlrdevapi/py.typed +0 -0
  237. {vlrdevapi-2.4.0 → vlrdevapi-3.0.0}/wrangler.jsonc +0 -0
@@ -24,36 +24,26 @@ jobs:
24
24
  - name: Install package
25
25
  run: uv sync --group dev
26
26
 
27
- # main always validates live against fresh vlr.gg data; never uses the
28
- # cache. Branch/PR runs reuse fixtures cached by earlier runs in the
29
- # same PR (within a 2h TTL), falling back to live fetches for anything
30
- # missing, so the check is correct even with a partially-stale cache.
31
- - name: Run MDX examples check (main, live)
32
- if: github.ref_name == 'main'
33
- run: uv run scripts/check_mdx_examples.py --live
34
-
35
- # actions/cache v6.1.0, pinned to a reviewed commit SHA.
36
- - name: Restore fixture cache (branch)
27
+ # Restore fixture cache scoped to the current ref (branch or main) and version prefix (v3).
28
+ # This ensures caches are never shared across unrelated branches or older versions.
29
+ - name: Restore fixture cache
37
30
  id: mdx-fixtures
38
- if: github.ref_name != 'main'
39
31
  uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9
40
32
  with:
41
33
  path: fixtures.tar.gz
42
- key: mdx-fixtures-${{ github.ref_name }}-run-${{ github.run_id }}
34
+ key: mdx-fixtures-v3-${{ github.ref_name }}-run-${{ github.run_id }}
43
35
  restore-keys: |
44
- mdx-fixtures-${{ github.ref_name }}-
36
+ mdx-fixtures-v3-${{ github.ref_name }}-
45
37
 
46
- - name: Unpack fixtures (branch)
47
- if: github.ref_name != 'main'
38
+ - name: Unpack fixtures
48
39
  run: |
49
40
  if [ -f fixtures.tar.gz ]; then
50
41
  mkdir -p .cache
51
42
  tar -C .cache -xzf fixtures.tar.gz
52
43
  fi
53
44
 
54
- - name: Check fixture cache age (branch, 2h TTL)
45
+ - name: Check fixture cache age (2h TTL)
55
46
  id: cache-age
56
- if: github.ref_name != 'main'
57
47
  run: |
58
48
  matched="${{ steps.mdx-fixtures.outputs.cache-matched-key }}"
59
49
  now=$(date +%s)
@@ -73,8 +63,7 @@ jobs:
73
63
  fi
74
64
  echo "now=$now" >> "$GITHUB_OUTPUT"
75
65
 
76
- - name: Run MDX examples check (branch)
77
- if: github.ref_name != 'main'
66
+ - name: Run MDX examples check
78
67
  run: |
79
68
  if [ "${{ steps.cache-age.outputs.fresh }}" == "true" ]; then
80
69
  uv run scripts/check_mdx_examples.py --skip-warm
@@ -84,8 +73,8 @@ jobs:
84
73
 
85
74
  # always(): a run that fails validation still saved its warm fixtures,
86
75
  # so the next fix commit restores and validates fast instead of warming.
87
- - name: Package fixtures (branch)
88
- if: always() && github.ref_name != 'main' && steps.cache-age.outputs.fresh == 'false'
76
+ - name: Package fixtures
77
+ if: always() && steps.cache-age.outputs.fresh == 'false'
89
78
  run: |
90
79
  mkdir -p .cache/mdx-html
91
80
  if [ -n "$(ls -A .cache/mdx-html)" ]; then
@@ -94,9 +83,9 @@ jobs:
94
83
  rm -f fixtures.tar.gz
95
84
  fi
96
85
 
97
- - name: Save fixture cache (branch)
98
- if: always() && github.ref_name != 'main' && steps.cache-age.outputs.fresh == 'false'
86
+ - name: Save fixture cache
87
+ if: always() && steps.cache-age.outputs.fresh == 'false'
99
88
  uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9
100
89
  with:
101
90
  path: fixtures.tar.gz
102
- key: mdx-fixtures-${{ github.ref_name }}-${{ steps.cache-age.outputs.now }}
91
+ key: mdx-fixtures-v3-${{ github.ref_name }}-${{ steps.cache-age.outputs.now }}
vlrdevapi-3.0.0/.nvmrc ADDED
@@ -0,0 +1 @@
1
+ 26.10.0
@@ -6,10 +6,11 @@ build:
6
6
  python: latest
7
7
  jobs:
8
8
  install:
9
- - pip install zensical mkdocstrings mkdocstrings-python
9
+ - pip install uv
10
+ - uv sync --group dev
10
11
  build:
11
12
  html:
12
- - cd docs-py && zensical build
13
+ - cd docs-py && uv run zensical build
13
14
  post_build:
14
15
  - mkdir -p $READTHEDOCS_OUTPUT/html/
15
16
  - cp --recursive docs-py/site/* $READTHEDOCS_OUTPUT/html/
@@ -5,6 +5,163 @@ 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.0.0] - 2026-10-23
9
+
10
+ ### Breaking
11
+
12
+ - **`BuyType` re-tiered to match vlr.gg** — the economy tab no longer uses
13
+ its old four spend tiers. `SEMI_ECO` and `SEMI_BUY` are removed and the
14
+ remaining tiers follow VLR's current categories based on total team
15
+ spend: `ECO` (under 10k), `HALF_BUY` (10k–18k), and `FULL_BUY` (18k+).
16
+ A new `PISTOL` member marks pistol-round buys (previously reported as
17
+ `ECO`). Code switching on `BuyType.SEMI_ECO` / `BuyType.SEMI_BUY` needs
18
+ updating; string values are `"Pistol"`, `"Eco"`, `"Half-buy"`,
19
+ `"Full-buy"`.
20
+
21
+ ### Added
22
+
23
+ - **Buy-type matrix** — `EconomyData` now carries the economy tab's
24
+ buy-type matrix as `buy_matrix_team1` / `buy_matrix_team2`
25
+ (`TeamBuyMatrix`): the Pistol / Conv. / Bonus summary and the
26
+ `On Eco` / `On Half buy` / `On Full buy` records
27
+ (`BuyTierRecord.vs_eco` / `vs_half` / `vs_full`), each a
28
+ `won`/`total` `EconScore`. Unplayed matchups are `None`.
29
+ `series.economy(game_id="all")` on the parser also now returns the
30
+ all-maps aggregate matrix instead of an empty result.
31
+ - **Convenience accessors** — flat summary shortcuts
32
+ (`pistol_team1` / `converted_team1` / `bonus_team1` and the
33
+ `_team2` counterparts), `full_buy` / `half_buy` / `eco` aliases on
34
+ `TeamBuyMatrix`, and a combined `total` on `BuyTierRecord` for the
35
+ overall won/total on a tier regardless of the opponent's buy type.
36
+ - **Round economy in `series.rounds`** — `RoundData` now carries the
37
+ per-round economy from the economy tab: `bank_team1`/`bank_team2`,
38
+ `spent_team1`/`spent_team2`, `buy_type_team1`/`buy_type_team2`, and
39
+ `is_pistol_round`. `series.rounds` fetches and merges the economy tab
40
+ automatically (economy fields stay `None`/empty when that page is
41
+ unavailable), making it the single round-by-round source alongside
42
+ `win_type` and the winner's `side`.
43
+ - **Series replay (logs tab)** — new `series.replay(series_id, game_id)`
44
+ returning the per-round kill-feed timeline (`ReplayData` /
45
+ `ReplayRound` / `ReplayEvent`): events typed by the `ReplayEventType`
46
+ enum (`KILL`, `PLANT`, `DEFUSE`, plus derived `BOOM` detonation and
47
+ `RESURRECT` events with `derived=True`, since vlr.gg logs neither as
48
+ feed rows), round clocks in both display (`clock`) and numeric
49
+ (`seconds`) form, kill cause (weapon or ability, e.g. `Sheriff`,
50
+ `Overdrive`, `Tour de Force`, `Spike`), killer/victim positions with
51
+ distance in minimap units, spike sites, and per-event alive snapshots
52
+ (name, agent, team, side, minimap coordinates, facing angle). Each
53
+ `ReplayRound` also includes `loadouts` (`list[PlayerRoundLoadout]`)
54
+ and the `loadout_for(player)` helper, capturing every player's
55
+ equipped weapon at round start, armor (supporting `No armor`,
56
+ `Light armor`, `Heavy armor`, and `Regen Shield`), and credits
57
+ left / spent in the buy phase. Real vlr.gg team and player IDs are
58
+ resolved from the game's overview tab
59
+ (one extra request, `team1_id`/`team2_id` and `player_id` fields) and
60
+ matched strictly within each player's team so duplicate names never
61
+ cross teams; unplayed maps or maps without round data return an
62
+ empty timeline gracefully.
63
+ - **Series head-to-head history** — `SeriesInfo.head_to_head` exposes
64
+ previous meetings between the two teams, parsed from the same overview
65
+ page. Each `SeriesHeadToHeadMatch` includes the series ID/URL, event,
66
+ stage, date, both team names/scores, and which side won.
67
+ - **Series streams in `series.vods()`** — `SeriesVods.streams` now contains
68
+ live broadcasts and watch-party streams alongside the existing `vods`
69
+ list. Each `SeriesStream` exposes the platform, channel, URL, stream
70
+ type, and country code. `SeriesVod` also gains `platform`, `map_number`,
71
+ and `start_seconds`.
72
+ - **Player total winnings** — `PlayerInfo.total_winnings` and
73
+ `total_winnings_currency` now expose the total from the player's Event
74
+ Placements card.
75
+ - **New `player.placements()` namespace** — returns `PlayerPlacements` with
76
+ individual `PlayerEventPlacement` rows (event, year, stage, placement,
77
+ prize, team) and the player's total winnings.
78
+ - **New grand-final fixtures** — series 130685 (LOUD vs OpTic, Champions
79
+ 2022 GF) and 754737 (LOUD vs Vitality, Champions 2026 GF) are now
80
+ covered by tests across economy, info/pick-bans, players, rounds, and
81
+ performance parsers.
82
+
83
+ ### Fixed
84
+
85
+ - **`team.upcoming_matches()` no longer returns completed matches** — the
86
+ parser now scopes its search to the heading's section wrapper instead of
87
+ the whole team-summary column, so "Recent Results" is excluded.
88
+ - **`series.info()` on upcoming/live series** — `best_of` is now read from
89
+ whichever match-header note contains the format (vlr.gg renders the
90
+ countdown twice before `BoN`), and `TBD` map tabs are no longer marked
91
+ `played`.
92
+ - **`team.completed_matches()` pagination** — added `limit` (default `50`)
93
+ and `return_all`; extra pages are fetched only when the requested limit
94
+ needs them. `TeamCompletedMatches.has_next_page` reports whether more
95
+ matches exist beyond the returned results.
96
+ - **Team match `stage`/`bracket`** — both fields are now parsed separately
97
+ from the team matches page (e.g. `stage="Playoffs"`, `bracket="LBF"`),
98
+ and enrichment no longer concatenates them into `"Playoffs - LBF"`.
99
+ - **Parser whitespace** — shared `normalize_whitespace()` collapses raw tabs,
100
+ newlines, and repeated spaces in parsed event/stage/bracket/role values
101
+ instead of leaving values such as `"Playoffs ⋅\n\t\t\tLBF"`.
102
+ - **`events.matches()` team countries** — `MatchTeam` now exposes
103
+ `country_name`, parsed from the team flag on the event matches page.
104
+ - **Thread-local HTTP client lifecycle** — parallel requests now reuse
105
+ thread-local clients and close every client after fetching or enrichment,
106
+ reducing client-creation overhead and preventing resource leaks.
107
+
108
+ ### Changed
109
+
110
+ - **`RoundData.win_type` is now a `RoundWinType` enum** instead of a
111
+ human-readable string. Use `round.win_type.full_name` for display text and
112
+ `round.win_type.value` (`"elimination"`, `"defuse"`, `"spike_detonation"`,
113
+ `"time_expired"`) for the raw value.
114
+
115
+ ### Documentation
116
+
117
+ - Series economy reference updated for the new `BuyType` values,
118
+ buy-matrix fields, and nested models; series rounds reference updated
119
+ for the merged per-round economy fields; new series replay reference
120
+ page for the kill-feed timeline module.
121
+ - Updated the `team.completed_matches`, `team.upcoming_matches`,
122
+ `events.matches`, and `series.rounds` references for `bracket`,
123
+ `country_name`, and the `RoundWinType` enum.
124
+ - Updated `series.info`, `series.vods`, and `player.info`, and added a
125
+ `player.placements` reference for head-to-head history, streams, VOD
126
+ metadata, and event placements.
127
+
128
+ ## [2.4.1] - 2026-10-17
129
+
130
+ ### Fixed
131
+
132
+ - **Series players/stats parsing** — newer vlr.gg overview tables add a
133
+ dedicated `.mod-agents` cell before the stat columns. Stat cells are now
134
+ mapped dynamically around that column, and agents are read from the new
135
+ cell. This fixes `SideStats` validation errors and misaligned values
136
+ (rating, ACS, KDA, KAST, ADR, HS%, first kills/deaths).
137
+ - **Series economy parsing** — the round-by-round economy grid now uses the
138
+ newer `.vlr-rounds.ecr` markup (`.ecr-bank`, `.ecc-c`, `.ecc-sq`). Banks,
139
+ spends, buy types, and round winners are read from it, with the legacy
140
+ `.wf-table-inset.mod-econ` table kept as a fallback.
141
+ - **Series VODs parsing** — VODs are now read from the newer `.sm-vods`
142
+ module (`.sm-vod-name` labels, `a.sm-ext` links), keeping the legacy
143
+ `.match-vods` container as a fallback.
144
+ - **Series game map names** — the game header now renders the duration as a
145
+ sibling of the map name. The map name is read from `.map-name`, so the
146
+ duration is no longer concatenated (e.g. `Pearl` instead of `Pearl35:48`).
147
+
148
+ ### Changed
149
+
150
+ - **Series economy team matching** — team/abbreviation resolution is now
151
+ case-insensitive, so team IDs resolve for pages that render short tags.
152
+ - **HTML parsing backend** — parsing now uses selectolax's Lexbor engine
153
+ (`selectolax.lexbor`) instead of the discontinued Modest engine
154
+ (`selectolax.parser`), which selectolax removed in 1.0. This is an internal
155
+ change: the public API, CSS selectors, and parsed output are unchanged.
156
+ - **Dependencies** — raised the `selectolax` minimum to `>=1.0.0` and moved
157
+ `pydantic` to the 2.14 series.
158
+
159
+ ### Documentation
160
+
161
+ - **Changelog** — this release is mirrored across `CHANGELOG.md`, the
162
+ ReadTheDocs changelog (`docs-py/docs/changelog.md`), and the official
163
+ site changelog (`official-docs/app/(home)/changelog/page.tsx`).
164
+
8
165
  ## [2.4.0] - 2026-08-19
9
166
 
10
167
  ### Added
@@ -279,6 +436,8 @@ The API surface, module structure, and type system are all new.
279
436
  - `aiohttp` and `asyncio` dependencies.
280
437
  - v1.x match/event/team parsing modules.
281
438
 
439
+ [3.0.0]: https://github.com/Vanshbordia/vlrdevapi/compare/v2.4.1...v3.0.0
440
+ [2.4.1]: https://github.com/Vanshbordia/vlrdevapi/compare/v2.4.0...v2.4.1
282
441
  [2.4.0]: https://github.com/Vanshbordia/vlrdevapi/compare/v2.3.0...v2.4.0
283
442
  [2.3.0]: https://github.com/Vanshbordia/vlrdevapi/compare/v2.2.0...v2.3.0
284
443
  [2.2.0]: https://github.com/Vanshbordia/vlrdevapi/compare/v2.1.0...v2.2.0
@@ -15,5 +15,6 @@ diverse, inclusive, and healthy community.
15
15
  ## Enforcement
16
16
 
17
17
  Instances of abusive, harassing, or otherwise unacceptable behavior may be
18
- reported to the project maintainers at vanshb@riftwatch.org. All complaints
18
+ reported to the project maintainers via the repository at
19
+ https://github.com/vanshbordia/vlrdevapi. All complaints
19
20
  will be reviewed and investigated.
@@ -0,0 +1,394 @@
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
+ <div align="center">
51
+
52
+ <img src="official-docs/public/logo.png" width="120" alt="vlrdevapi logo">
53
+
54
+ # vlrdevapi
55
+
56
+ **The modern, type-safe Python library for VLR.gg Valorant esports data**
57
+
58
+ [![PyPI version](https://img.shields.io/pypi/v/vlrdevapi.svg?color=fd4556)](https://pypi.org/project/vlrdevapi/)
59
+ [![Python Version](https://img.shields.io/pypi/pyversions/vlrdevapi.svg)](https://pypi.org/project/vlrdevapi/)
60
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
61
+ [![Documentation](https://img.shields.io/badge/docs-vlrdevapi.pages.dev-blue.svg)](https://vlrdevapi.pages.dev/docs/)
62
+
63
+ [Documentation](https://vlrdevapi.pages.dev/docs/) · [Quickstart](https://vlrdevapi.pages.dev/docs/quickstart/) · [API Reference](https://vlrdevapi.pages.dev/docs/reference/) · [Guides](https://vlrdevapi.pages.dev/guides/) · [Changelog](https://vlrdevapi.pages.dev/changelog/) · [PyPI](https://pypi.org/project/vlrdevapi/)
64
+
65
+ </div>
66
+
67
+ ---
68
+
69
+ **vlrdevapi** is a high-performance, type-safe Python client for extracting and analyzing Valorant esports data from [VLR.gg](https://www.vlr.gg/). Built for competitive analysts, data scientists, Discord bot creators, and esports engineers, it delivers structured [Pydantic](https://docs.pydantic.dev/) models with full type hinting, built-in rate limiting, automatic retries, and comprehensive coverage across all VLR.gg entities.
70
+
71
+ Whether you need live match tickers, tournament brackets, historical player ratings, round-by-round economy buy matrices, or 2D minimap kill-feed replay timelines with player loadouts, **vlrdevapi** provides a clean, idiomatic interface.
72
+
73
+ ---
74
+
75
+ ## Highlights
76
+
77
+ - **Complete Valorant Esports Coverage** — Tournaments, match schedules, live scoreboards, player careers, team rosters, roster transactions, and esports news.
78
+ - **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
+ - **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`).
80
+ - **Strictly Typed Pydantic Models** — Every endpoint returns validated models with full field annotations, descriptive docstrings, and IDE autocompletion.
81
+ - **Production-Ready Reliability** — Built-in token-bucket rate limiter, exponential backoff retries, thread-safe connection pooling, and LRU-cached enrichment.
82
+ - **Flexible Access Patterns** — Use lightweight module-level calls (`vlrdevapi.player.info(...)`), curried ID chaining (`vlrdevapi.player(11225).info()`), or a managed context manager (`with VLRClient() as client:`).
83
+ - **Timezone Awareness** — Seamlessly convert match schedules and timestamps into any target timezone via Python's standard `zoneinfo`.
84
+ - **Managed with [uv](https://docs.astral.sh/uv/)** — Blazing fast dependency resolution and testing workflows.
85
+
86
+ ---
87
+
88
+ ## Installation
89
+
90
+ Install `vlrdevapi` using [uv](https://docs.astral.sh/uv/) (recommended) or `pip`:
91
+
92
+ ```bash
93
+ # Using uv (recommended)
94
+ uv add vlrdevapi
95
+
96
+ # Using pip
97
+ pip install vlrdevapi
98
+ ```
99
+
100
+ > **Requirements:** Python 3.11 or higher.
101
+
102
+ ---
103
+
104
+ ## Quickstart
105
+
106
+ ### 1. Direct Module-Level Access (Zero Setup)
107
+
108
+ Fetch data immediately without instantiating a client:
109
+
110
+ ```python
111
+ import vlrdevapi
112
+
113
+ # Get player profile and country
114
+ player = vlrdevapi.player.info(player_id=11225)
115
+ print(f"Player: {player.name} ({player.country})")
116
+
117
+ # Get series overview
118
+ series = vlrdevapi.series.info(series_id=542272)
119
+ print(f"Match: {series.team1.name} vs {series.team2.name} - Score: {series.score1}:{series.score2}")
120
+
121
+ # Get event details
122
+ event = vlrdevapi.event.info(event_id=2863)
123
+ print(f"Event: {event.name} ({event.start_date} to {event.end_date})")
124
+ ```
125
+
126
+ ### 2. Curried Method Chaining
127
+
128
+ Bind an ID once, then query multiple sub-methods without repeating the identifier:
129
+
130
+ ```python
131
+ import vlrdevapi
132
+
133
+ # Bind team 2593 (FNATIC) once
134
+ fnc = vlrdevapi.team(2593)
135
+
136
+ info = fnc.info()
137
+ print(f"{info.name} [{info.tag}] - Country: {info.country}")
138
+
139
+ roster = fnc.roster()
140
+ print(f"Active Players: {[p.ign for p in roster.players]}")
141
+
142
+ placements = fnc.placements()
143
+ print(f"Total Prize Earnings: {placements.total_winnings_currency}{placements.total_winnings:,}")
144
+ ```
145
+
146
+ ### 3. Managed Client Session (`VLRClient`)
147
+
148
+ For production applications, high-throughput scripts, or custom concurrency tuning, use `VLRClient`:
149
+
150
+ ```python
151
+ from vlrdevapi import VLRClient
152
+
153
+ with VLRClient(timeout=30, requests_per_second=8, max_retries=3) as client:
154
+ # Live matches ticker
155
+ live_matches = client.matches.live()
156
+ for m in live_matches.matches:
157
+ print(f"LIVE: {m.team1.name} vs {m.team2.name} ({m.event})")
158
+
159
+ # Series performance & economy
160
+ series = client.series(542272)
161
+ econ = series.economy(game_id=1)
162
+ print(f"Map 1 Rounds Analyzed: {len(econ.rounds)}")
163
+ ```
164
+
165
+ ---
166
+
167
+ ## API Overview & Reference
168
+
169
+ | Module | Key Methods | Description | Official Docs |
170
+ |:---|:---|:---|:---|
171
+ | **[Matches](https://vlrdevapi.pages.dev/docs/reference/matches/)** | `upcoming()`, `live()`, `completed()` | Live scores, upcoming schedules, and paginated match history | [Docs →](https://vlrdevapi.pages.dev/docs/reference/matches/) |
172
+ | **[Series](https://vlrdevapi.pages.dev/docs/reference/series/)** | `info()`, `replay()`, `economy()`, `players()`, `rounds()`, `vods()`, `performance()` | Deep map analytics, kill-feed timeline, buy matrices, player stats, and streams | [Docs →](https://vlrdevapi.pages.dev/docs/reference/series/) |
173
+ | **[Players](https://vlrdevapi.pages.dev/docs/reference/player/)** | `info()`, `profile()`, `agents()`, `teams()`, `matches()`, `placements()` | Historical ratings (ACS, K/D, ADR, KAST), agent pools, career history, and placements | [Docs →](https://vlrdevapi.pages.dev/docs/reference/player/) |
174
+ | **[Teams](https://vlrdevapi.pages.dev/docs/reference/team/)** | `info()`, `roster()`, `placements()`, `transactions()`, `completed_matches()`, `upcoming_matches()` | Active rosters, staff, roster transfers, historical match records, and tournament earnings | [Docs →](https://vlrdevapi.pages.dev/docs/reference/team/) |
175
+ | **[Events](https://vlrdevapi.pages.dev/docs/reference/events/)** | `info()`, `stages()`, `matches()`, `standings()`, `teams()`, `list()` | Tournament brackets, group/playoff stages, standings, prize pools, and participating teams | [Docs →](https://vlrdevapi.pages.dev/docs/reference/events/) |
176
+ | **[News](https://vlrdevapi.pages.dev/docs/reference/news/)** | `list()`, `article()` | VLR.gg editorial articles, breaking roster news, author details, and full Markdown content | [Docs →](https://vlrdevapi.pages.dev/docs/reference/news/) |
177
+
178
+ ---
179
+
180
+ ## Code Examples
181
+
182
+ ### 1. Spatial Replay Timeline & Round Loadouts
183
+
184
+ Inspect round-by-round kill events with 2D minimap locations, viewing angles, distances, and player equipment:
185
+
186
+ ```python
187
+ import vlrdevapi
188
+
189
+ # Fetch replay timeline for Game 3 of a series (Sunset)
190
+ replay = vlrdevapi.series.replay(series_id=754737, game_id=283288)
191
+ round_1 = replay.round(1)
192
+
193
+ if round_1:
194
+ print(f"Round 1 Attacking Side: {round_1.attacking}")
195
+
196
+ # Check buy-phase loadout for a player
197
+ loadout = round_1.loadout_for("Derke")
198
+ if loadout:
199
+ print(f"Derke: Weapon={loadout.weapon}, Armor={loadout.armor}, Spent=${loadout.spent}")
200
+
201
+ # Iterate through kill events
202
+ for event in round_1.events:
203
+ if event.type.value == "kill":
204
+ killer = event.actor.name if event.actor else "World"
205
+ victim = event.victim.name if event.victim else "Unknown"
206
+ dist = f"{event.distance_units:.1f}u" if event.distance_units else "N/A"
207
+ print(f" [{event.clock}] {killer} killed {victim} with {event.cause} (dist: {dist})")
208
+ if event.pos_x is not None:
209
+ print(f" Victim pos: ({event.pos_x}, {event.pos_y}) | Killer pos: ({event.from_x}, {event.from_y})")
210
+ ```
211
+
212
+ Learn more in the [Series Replay Documentation](https://vlrdevapi.pages.dev/docs/reference/series/replay/).
213
+
214
+ ---
215
+
216
+ ### 2. Round Economy & Team Buy Matrices
217
+
218
+ Analyze team spending, bank reserves, and buy types across rounds:
219
+
220
+ ```python
221
+ import vlrdevapi
222
+
223
+ economy = vlrdevapi.series.economy(series_id=754737, game_id=283288)
224
+
225
+ for r in economy.rounds[:5]:
226
+ winner_name = r.winner.name if r.winner else "Unknown"
227
+ print(f"Round {r.round_number}: Winner = {winner_name}")
228
+ print(f" {economy.team1}: {r.buy_type_team1} (Spent: ${r.spent_team1:,}, Bank: ${r.bank_team1:,})")
229
+ print(f" {economy.team2}: {r.buy_type_team2} (Spent: ${r.spent_team2:,}, Bank: ${r.bank_team2:,})")
230
+ ```
231
+
232
+ Learn more in the [Series Economy Documentation](https://vlrdevapi.pages.dev/docs/reference/series/economy/).
233
+
234
+ ---
235
+
236
+ ### 3. Player Career Statistics & Agent Pools
237
+
238
+ Extract ACS, K/D, KAST, and ADR broken down by agent:
239
+
240
+ ```python
241
+ import vlrdevapi
242
+
243
+ # Query player agent stats over the last 60 days
244
+ agent_stats = vlrdevapi.player.agents(player_id=11225, timespan="60d")
245
+
246
+ for a in agent_stats.agents[:4]:
247
+ print(f"{a.agent:<10} | Pick Rate: {a.use} | Rating: {a.rating} | ACS: {a.acs} | K/D: {a.kd}")
248
+ ```
249
+
250
+ Learn more in the [Player Reference Documentation](https://vlrdevapi.pages.dev/docs/reference/player/).
251
+
252
+ ---
253
+
254
+ ### 4. Tournament Brackets & Standings
255
+
256
+ Fetch event stages, match schedules, and final prize distributions:
257
+
258
+ ```python
259
+ import vlrdevapi
260
+
261
+ event_id = 2863 # e.g., Champions 2026
262
+ event = vlrdevapi.event(event_id)
263
+
264
+ # Event stages (Group Stage, Playoffs, etc.)
265
+ for stage in event.stages():
266
+ print(f"Stage: {stage.name} (ID: {stage.id})")
267
+
268
+ # Final event standings & prize earnings
269
+ standings = event.standings()
270
+ for stage in standings.stages[:1]:
271
+ print(f"--- {stage.stage_name} ---")
272
+ for row in stage.standings[:3]:
273
+ team_name = row.team.name if row.team else "TBD"
274
+ prize = f"{row.prize_currency}{row.prize_money:,}" if row.prize_money else "N/A"
275
+ print(f" #{row.place}: {team_name} - Prize: {prize}")
276
+ ```
277
+
278
+ Learn more in the [Events Reference Documentation](https://vlrdevapi.pages.dev/docs/reference/events/).
279
+
280
+ ---
281
+
282
+ ### 5. News Articles with Full Markdown
283
+
284
+ Read VLR.gg editorial content formatted in clean GitHub-flavored Markdown:
285
+
286
+ ```python
287
+ import vlrdevapi
288
+
289
+ # Get latest articles
290
+ feed = vlrdevapi.news(page=1)
291
+ latest = feed.news[0]
292
+
293
+ # Retrieve full article body
294
+ article = vlrdevapi.news.article(latest.id)
295
+ print(f"Title: {article.title}")
296
+ print(f"Author: {article.author} ({article.date})")
297
+ print(f"Length: {len(article.content_md):,} characters")
298
+ ```
299
+
300
+ Learn more in the [News Reference Documentation](https://vlrdevapi.pages.dev/docs/reference/news/).
301
+
302
+ ---
303
+
304
+ ## Configuration & Error Handling
305
+
306
+ ### Rate Limiting & Concurrency
307
+
308
+ `vlrdevapi` includes sensible defaults (`4.0` requests per second) to respect VLR.gg servers. When using `VLRClient`, you can customize client settings:
309
+
310
+ ```python
311
+ from zoneinfo import ZoneInfo
312
+ from vlrdevapi import VLRClient
313
+
314
+ client = VLRClient(
315
+ timeout=20, # Timeout per request (seconds)
316
+ requests_per_second=6, # Max rate limit
317
+ max_retries=4, # Exponential backoff retries
318
+ source_tz=ZoneInfo("America/New_York"), # Target timezone for schedules
319
+ )
320
+ ```
321
+
322
+ ### Typed Exceptions
323
+
324
+ Catch errors with specific, typed exception classes from `vlrdevapi.exceptions`:
325
+
326
+ ```python
327
+ import vlrdevapi
328
+ from vlrdevapi.exceptions import (
329
+ NotFoundError,
330
+ RateLimitError,
331
+ RequestError,
332
+ ValidationError,
333
+ )
334
+
335
+ try:
336
+ player = vlrdevapi.player.info(player_id=99999999)
337
+ except NotFoundError:
338
+ print("Player not found on VLR.gg (404).")
339
+ except RateLimitError:
340
+ print("Exceeded VLR.gg rate limits. Please slow down requests.")
341
+ except ValidationError as e:
342
+ print(f"Invalid parameter passed: {e}")
343
+ except RequestError as e:
344
+ print(f"Network error occurred: {e}")
345
+ ```
346
+
347
+ ---
348
+
349
+ ## Documentation & Guides
350
+
351
+ Comprehensive documentation, tutorials, and deep-dives are available on the official documentation portal:
352
+
353
+ - **[Documentation Home](https://vlrdevapi.pages.dev/docs/)** — Complete getting-started guide and architectural overview.
354
+ - **[Quickstart Guide](https://vlrdevapi.pages.dev/docs/quickstart/)** — First queries in under two minutes.
355
+ - **[Series Replay Guide](https://vlrdevapi.pages.dev/docs/reference/series/replay/)** — Mapping 2D replay coordinates, viewing angles, and equipment.
356
+ - **[Series Economy Guide](https://vlrdevapi.pages.dev/docs/reference/series/economy/)** — Round spending and buy matrix mechanics.
357
+ - **[Guides & Tutorials](https://vlrdevapi.pages.dev/guides/)** — Real-world guides for Discord bots, Pandas analysis, and Polars pipelines.
358
+ - **[Frequently Asked Questions](https://vlrdevapi.pages.dev/faq/)** — Common questions on rate limits, IDs, and data structures.
359
+ - **[Changelog](https://vlrdevapi.pages.dev/changelog/)** — Version history and release notes.
360
+
361
+ ---
362
+
363
+ ## Development
364
+
365
+ Contributions and bug reports are welcome! This repository uses `uv` for package management and testing:
366
+
367
+ ```bash
368
+ # 1. Clone the repository
369
+ git clone https://github.com/Vanshbordia/vlrdevapi.git
370
+ cd vlrdevapi
371
+
372
+ # 2. Install dependencies with uv
373
+ uv sync --group dev
374
+
375
+ # 3. Run the unit test suite
376
+ uv run pytest tests/ -v
377
+
378
+ # 4. Run static type checking and linter
379
+ uv run ruff check src/ tests/
380
+ ```
381
+
382
+ Please read our [Contributing Guide](CONTRIBUTING.md) and [Code of Conduct](CODE_OF_CONDUCT.md) before submitting pull requests.
383
+
384
+ ---
385
+
386
+ ## License
387
+
388
+ This project is licensed under the [MIT License](LICENCE).
389
+
390
+ ---
391
+
392
+ ## Disclaimer
393
+
394
+ `vlrdevapi` is an independent open-source library. It is not affiliated with, maintained, authorized, or endorsed by [VLR.gg](https://www.vlr.gg/) or [Riot Games](https://www.riotgames.com/). Valorant and all associated properties are trademarks or registered trademarks of Riot Games, Inc. Please use responsibly and respect VLR.gg's bandwidth and server infrastructure.