spotapi 2.0.0b1__tar.gz → 2.0.0b2__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 (109) hide show
  1. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/.gitignore +165 -165
  2. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/CHANGELOG.md +89 -89
  3. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/CODE_OF_CONDUCT.md +128 -128
  4. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/CONTRIBUTING.md +246 -246
  5. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/LEGAL_NOTICE.md +45 -45
  6. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/LICENSE +674 -674
  7. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/MIGRATION.md +181 -181
  8. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/PKG-INFO +7 -7
  9. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/README.md +239 -239
  10. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/docs/album.md +58 -58
  11. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/docs/artist.md +107 -107
  12. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/docs/creator.md +55 -55
  13. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/docs/family.md +61 -61
  14. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/docs/language.md +89 -89
  15. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/docs/login.md +102 -102
  16. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/docs/password.md +37 -37
  17. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/docs/player.md +183 -183
  18. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/docs/playlist.md +178 -178
  19. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/docs/public.md +142 -142
  20. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/docs/song.md +125 -125
  21. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/docs/status.md +147 -147
  22. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/docs/user.md +67 -67
  23. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/docs/websocket.md +28 -28
  24. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/example.py +9 -6
  25. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/pyproject.toml +85 -85
  26. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/requirements.txt +2 -2
  27. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/setup.py +61 -61
  28. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/sync/__init__.py +25 -25
  29. spotapi-2.0.0b2/spotapi/sync/_tests/.env.sample +3 -0
  30. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/sync/_tests/__init__.py +1 -1
  31. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/sync/_tests/annotations_test.py +222 -222
  32. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/sync/_tests/client_refresh_test.py +224 -224
  33. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/sync/_tests/features/artist_test.py +196 -196
  34. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/sync/_tests/features/event_test.py +53 -53
  35. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/sync/_tests/features/player_test.py +112 -112
  36. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/sync/_tests/features/private_playlist_test.py +70 -70
  37. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/sync/_tests/features/public_playlist_test.py +32 -32
  38. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/sync/_tests/features/session.py +36 -36
  39. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/sync/_tests/features/song_test.py +67 -67
  40. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/sync/_tests/features/status_test.py +126 -126
  41. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/sync/_tests/features/user_test.py +63 -63
  42. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/sync/album.py +96 -96
  43. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/sync/artist.py +287 -287
  44. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/sync/client.py +314 -314
  45. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/sync/creator.py +203 -203
  46. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/sync/exceptions/__init__.py +1 -1
  47. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/sync/exceptions/errors.py +111 -111
  48. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/sync/family.py +162 -162
  49. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/sync/http/__init__.py +2 -2
  50. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/sync/http/data.py +43 -43
  51. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/sync/http/request.py +248 -248
  52. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/sync/login.py +356 -356
  53. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/sync/password.py +97 -97
  54. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/sync/player.py +348 -348
  55. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/sync/playlist.py +417 -417
  56. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/sync/podcast.py +129 -129
  57. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/sync/public.py +117 -117
  58. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/sync/solvers/__init__.py +15 -15
  59. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/sync/solvers/capmonster.py +145 -145
  60. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/sync/solvers/capsolver.py +144 -144
  61. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/sync/song.py +288 -288
  62. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/sync/status.py +226 -226
  63. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/sync/types/__init__.py +2 -2
  64. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/sync/types/alias.py +29 -29
  65. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/sync/types/annotations.py +201 -201
  66. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/sync/types/data.py +413 -413
  67. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/sync/types/interfaces.py +57 -57
  68. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/sync/user.py +128 -128
  69. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/sync/utils/__init__.py +3 -3
  70. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/sync/utils/logger.py +91 -91
  71. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/sync/utils/saver.py +357 -357
  72. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/sync/utils/strings.py +130 -130
  73. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/sync/websocket.py +167 -167
  74. spotapi-2.0.0b2/spotapi/v2/__init__.py +11 -0
  75. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/v2/client.py +101 -101
  76. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/v2/connection/http.py +26 -1
  77. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/v2/connection/types.py +76 -76
  78. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/v2/connection/websocket.py +199 -199
  79. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/v2/datastruct/__init__.py +9 -9
  80. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/v2/datastruct/event_handler.py +62 -62
  81. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/v2/datastruct/object_dict.py +52 -5
  82. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/v2/datastruct/pool.py +155 -156
  83. spotapi-2.0.0b2/spotapi/v2/playlist.py +225 -0
  84. spotapi-2.0.0b2/spotapi/v2/public.py +370 -0
  85. spotapi-2.0.0b1/spotapi/v2/base.py → spotapi-2.0.0b2/spotapi/v2/query.py +117 -100
  86. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/v2/session.py +32 -1
  87. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/v2/specialized/__init__.py +3 -3
  88. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/v2/specialized/data_wrappers/__init__.py +2 -0
  89. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/v2/specialized/data_wrappers/album.py +93 -93
  90. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/v2/specialized/data_wrappers/artist.py +66 -66
  91. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/v2/specialized/data_wrappers/common.py +139 -139
  92. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/v2/specialized/data_wrappers/playlist.py +81 -81
  93. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/v2/specialized/data_wrappers/podcast.py +75 -75
  94. spotapi-2.0.0b2/spotapi/v2/specialized/data_wrappers/profile_attributes.py +29 -0
  95. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/v2/specialized/data_wrappers/track.py +113 -113
  96. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/v2/specialized/totp.py +66 -66
  97. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/v2/types/__init__.py +1 -4
  98. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/v2/types/exceptions.py +5 -0
  99. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/v2/types/logger.py +724 -724
  100. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/v2/utils/__init__.py +1 -4
  101. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/v2/utils/cache.py +62 -62
  102. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/v2/utils/random.py +22 -22
  103. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/v2/utils/strings.py +2 -3
  104. spotapi-2.0.0b1/spotapi/sync/_tests/.env.sample +0 -3
  105. spotapi-2.0.0b1/spotapi/v2/__init__.py +0 -5
  106. spotapi-2.0.0b1/spotapi/v2/public.py +0 -151
  107. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/image.png +0 -0
  108. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/v2/connection/__init__.py +0 -0
  109. {spotapi-2.0.0b1 → spotapi-2.0.0b2}/spotapi/v2/py.typed +0 -0
@@ -1,166 +1,166 @@
1
- # Byte-compiled / optimized / DLL files
2
- __pycache__/
3
- *.py[cod]
4
- *$py.class
5
-
6
- # C extensions
7
- *.so
8
-
9
- # Distribution / packaging
10
- .vscode/
11
- .Python
12
- build/
13
- develop-eggs/
14
- dist/
15
- downloads/
16
- eggs/
17
- .eggs/
18
- lib/
19
- lib64/
20
- parts/
21
- sdist/
22
- var/
23
- wheels/
24
- share/python-wheels/
25
- *.egg-info/
26
- .installed.cfg
27
- *.egg
28
- MANIFEST
29
-
30
- # PyInstaller
31
- # Usually these files are written by a python script from a template
32
- # before PyInstaller builds the exe, so as to inject date/other infos into it.
33
- *.manifest
34
- *.spec
35
-
36
- # Installer logs
37
- pip-log.txt
38
- pip-delete-this-directory.txt
39
-
40
- # Unit test / coverage reports
41
- htmlcov/
42
- .tox/
43
- .nox/
44
- .coverage
45
- .coverage.*
46
- .cache
47
- nosetests.xml
48
- coverage.xml
49
- *.cover
50
- *.py,cover
51
- .hypothesis/
52
- .pytest_cache/
53
- cover/
54
-
55
- # Translations
56
- *.mo
57
- *.pot
58
-
59
- # Django stuff:
60
- *.log
61
- local_settings.py
62
- db.sqlite3
63
- db.sqlite3-journal
64
-
65
- # Flask stuff:
66
- instance/
67
- .webassets-cache
68
-
69
- # Scrapy stuff:
70
- .scrapy
71
-
72
- # Sphinx documentation
73
- docs/_build/
74
-
75
- # PyBuilder
76
- .pybuilder/
77
- target/
78
-
79
- # Jupyter Notebook
80
- .ipynb_checkpoints
81
-
82
- # IPython
83
- profile_default/
84
- ipython_config.py
85
-
86
- # pyenv
87
- # For a library or package, you might want to ignore these files since the code is
88
- # intended to run in multiple environments; otherwise, check them in:
89
- # .python-version
90
-
91
- # pipenv
92
- # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
93
- # However, in case of collaboration, if having platform-specific dependencies or dependencies
94
- # having no cross-platform support, pipenv may install dependencies that don't work, or not
95
- # install all needed dependencies.
96
- #Pipfile.lock
97
-
98
- # poetry
99
- # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
100
- # This is especially recommended for binary packages to ensure reproducibility, and is more
101
- # commonly ignored for libraries.
102
- # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
103
- #poetry.lock
104
-
105
- # pdm
106
- # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
107
- #pdm.lock
108
- # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
109
- # in version control.
110
- # https://pdm.fming.dev/latest/usage/project/#working-with-version-control
111
- .pdm.toml
112
- .pdm-python
113
- .pdm-build/
114
-
115
- # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
116
- __pypackages__/
117
-
118
- # Celery stuff
119
- celerybeat-schedule
120
- celerybeat.pid
121
-
122
- # SageMath parsed files
123
- *.sage.py
124
-
125
- # Environments
126
- .env
127
- .venv
128
- env/
129
- venv/
130
- ENV/
131
- env.bak/
132
- venv.bak/
133
-
134
- # Spyder project settings
135
- .spyderproject
136
- .spyproject
137
-
138
- # Rope project settings
139
- .ropeproject
140
-
141
- # mkdocs documentation
142
- /site
143
-
144
- # mypy
145
- .mypy_cache/
146
- .dmypy.json
147
- dmypy.json
148
-
149
- # Pyre type checker
150
- .pyre/
151
-
152
- # pytype static type analyzer
153
- .pytype/
154
-
155
- # Cython debug symbols
156
- cython_debug/
157
-
158
- # PyCharm
159
- # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
160
- # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
161
- # and can be added to the global gitignore or merged into this file. For a more nuclear
162
- # option (not recommended) you can uncomment the following to ignore the entire idea folder.
163
- #.idea/
164
- main.py
165
- build.bat
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .vscode/
11
+ .Python
12
+ build/
13
+ develop-eggs/
14
+ dist/
15
+ downloads/
16
+ eggs/
17
+ .eggs/
18
+ lib/
19
+ lib64/
20
+ parts/
21
+ sdist/
22
+ var/
23
+ wheels/
24
+ share/python-wheels/
25
+ *.egg-info/
26
+ .installed.cfg
27
+ *.egg
28
+ MANIFEST
29
+
30
+ # PyInstaller
31
+ # Usually these files are written by a python script from a template
32
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
33
+ *.manifest
34
+ *.spec
35
+
36
+ # Installer logs
37
+ pip-log.txt
38
+ pip-delete-this-directory.txt
39
+
40
+ # Unit test / coverage reports
41
+ htmlcov/
42
+ .tox/
43
+ .nox/
44
+ .coverage
45
+ .coverage.*
46
+ .cache
47
+ nosetests.xml
48
+ coverage.xml
49
+ *.cover
50
+ *.py,cover
51
+ .hypothesis/
52
+ .pytest_cache/
53
+ cover/
54
+
55
+ # Translations
56
+ *.mo
57
+ *.pot
58
+
59
+ # Django stuff:
60
+ *.log
61
+ local_settings.py
62
+ db.sqlite3
63
+ db.sqlite3-journal
64
+
65
+ # Flask stuff:
66
+ instance/
67
+ .webassets-cache
68
+
69
+ # Scrapy stuff:
70
+ .scrapy
71
+
72
+ # Sphinx documentation
73
+ docs/_build/
74
+
75
+ # PyBuilder
76
+ .pybuilder/
77
+ target/
78
+
79
+ # Jupyter Notebook
80
+ .ipynb_checkpoints
81
+
82
+ # IPython
83
+ profile_default/
84
+ ipython_config.py
85
+
86
+ # pyenv
87
+ # For a library or package, you might want to ignore these files since the code is
88
+ # intended to run in multiple environments; otherwise, check them in:
89
+ # .python-version
90
+
91
+ # pipenv
92
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
93
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
94
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
95
+ # install all needed dependencies.
96
+ #Pipfile.lock
97
+
98
+ # poetry
99
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
100
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
101
+ # commonly ignored for libraries.
102
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
103
+ #poetry.lock
104
+
105
+ # pdm
106
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
107
+ #pdm.lock
108
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
109
+ # in version control.
110
+ # https://pdm.fming.dev/latest/usage/project/#working-with-version-control
111
+ .pdm.toml
112
+ .pdm-python
113
+ .pdm-build/
114
+
115
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
116
+ __pypackages__/
117
+
118
+ # Celery stuff
119
+ celerybeat-schedule
120
+ celerybeat.pid
121
+
122
+ # SageMath parsed files
123
+ *.sage.py
124
+
125
+ # Environments
126
+ .env
127
+ .venv
128
+ env/
129
+ venv/
130
+ ENV/
131
+ env.bak/
132
+ venv.bak/
133
+
134
+ # Spyder project settings
135
+ .spyderproject
136
+ .spyproject
137
+
138
+ # Rope project settings
139
+ .ropeproject
140
+
141
+ # mkdocs documentation
142
+ /site
143
+
144
+ # mypy
145
+ .mypy_cache/
146
+ .dmypy.json
147
+ dmypy.json
148
+
149
+ # Pyre type checker
150
+ .pyre/
151
+
152
+ # pytype static type analyzer
153
+ .pytype/
154
+
155
+ # Cython debug symbols
156
+ cython_debug/
157
+
158
+ # PyCharm
159
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
160
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
161
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
162
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
163
+ #.idea/
164
+ main.py
165
+ build.bat
166
166
  sessions.json
@@ -1,90 +1,90 @@
1
- # Changelog
2
-
3
- All notable changes to SpotAPI are documented here.
4
-
5
- The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6
- SpotAPI adheres to [Semantic Versioning](https://semver.org/).
7
-
8
- ---
9
-
10
- ## [Unreleased] — `async-v2` beta
11
-
12
- ### Added
13
-
14
- - **Full async rewrite** — every public method is now a coroutine or
15
- `AsyncGenerator`. No sync wrappers, no `asyncio.run()` hidden inside the
16
- library.
17
- - **`Public` search generators** — `search_tracks`, `search_artists`,
18
- `search_albums`, `search_playlists`, `search_podcasts` all return
19
- `AsyncGenerator[Sequence[T]]` and paginate automatically.
20
- - **Typed data wrappers** — `Track`, `Artist`, `Album`, `Playlist`, `Podcast`
21
- are fully typed `@dataclass(slots=True)` classes with `__repr__`.
22
- - **Shared visual types** — `_common.py` extracts the repeated color/theme
23
- structs that Spotify returns on every entity, eliminating ~300 lines of
24
- duplication across the data wrapper modules.
25
- - **`from_dict` deserializer** — recursive, camelCase-aware dataclass factory
26
- with `lru_cache`-backed annotation resolution.
27
- - **`ObjectDict`** — attribute-access `dict` subclass for ergonomic raw JSON
28
- traversal, with optional thread-safe write locking.
29
- - **`Pool[T]`** — generic async object pool with factory, pre-warm, capped
30
- eviction, and teardown callbacks.
31
- - **`EventDispatcher`** — async event system used by `WebSocketClient`.
32
- - **`WebSocketClient`** — async WebSocket wrapper with heartbeat manager,
33
- `@event` decorator, and `asynccontextmanager`-based connection lifecycle.
34
- - **`HTTPClient`** — `wreq`-backed HTTP client with:
35
- - Exponential backoff + jitter retry strategy.
36
- - Randomised browser profile emulation (Chrome, Edge, Firefox, Opera).
37
- - Randomised OS emulation (Windows 30×, macOS 6×, Linux 2×).
38
- - Shared `ClientPool` for connection reuse.
39
- - **`BundleSession`** — parses Spotify's HTML to extract JS bundle URLs,
40
- fetches and caches them, and derives persisted query hashes.
41
- - **`AuthSession`** — TOTP-based access token + client token acquisition with
42
- a background auto-refresh task. No CAPTCHA solver required.
43
- - **`timed_cache`** — TTL-aware async/sync cache decorator used for the
44
- expensive bundle-fetch step.
45
- - **Five loggers**: `LoggerColour`, `StandardLogger`, `NoopLogger`,
46
- `InbuiltLogger`, `JsonLogger`, and `MultiLogger` fan-out — all implementing
47
- `LoggerProtocol`.
48
- - **`py.typed` marker** — signals full PEP 561 type-checker support.
49
- - `pyproject.toml` replaces `setup.py`.
50
-
51
- ### Fixed
52
-
53
- - `types/__init__.py` had duplicate star imports causing symbols to be
54
- registered twice; replaced with explicit named imports.
55
- - `utils/__init__.py` used a broken double-import pattern that shadowed the
56
- module-level `__all__`; replaced with explicit named imports.
57
- - `Pool.put()` called the deprecated `asyncio.get_event_loop()` (deprecated
58
- in Python 3.11); replaced with `asyncio.get_running_loop()`.
59
- - `_BaseLogger.log()` acquired the threading lock then returned early without
60
- releasing it when `is_enabled()` was `False`; replaced the manual
61
- acquire/release with a `with` statement via `contextlib.nullcontext`.
62
- - `totp.py` had an unreachable `return FALLBACK` statement after a
63
- `raise RuntimeError`; removed the dead code.
64
- - Data wrappers (`track.py`, `artist.py`, `album.py`, `playlist.py`,
65
- `podcast.py`) each redefined identical color/theme dataclasses; extracted
66
- to `_common.py` and imported from there.
67
-
68
- ### Changed
69
-
70
- - Package layout reorganised into `connection/`, `datastruct/`,
71
- `specialized/`, `types/`, `utils/` sub-packages.
72
- - `setup.py` replaced by `pyproject.toml` (Hatchling build backend).
73
- - Minimum Python version raised from 3.11 to **3.11**.
74
-
75
- ### Removed
76
-
77
- - `annotations.py` runtime type-enforcement decorator (`@enforce_types`,
78
- `@enforce`) — moved to a dev-only tool; it has no callers in v2 and
79
- imposes overhead on every method call.
80
- - `parse_json_string` from `utils/strings.py` — v1 leftover with no callers
81
- in v2 (Spotify's `correlationId` is now parsed via the `appServerConfig`
82
- base64 blob).
83
- - `setup.py`, `requirements.txt` — superseded by `pyproject.toml`.
84
-
85
- ---
86
-
87
- ## [1.2.8] — 2026-06-14 *(latest stable)*
88
-
89
- > See the [main branch](https://github.com/Aran404/SpotAPI/tree/main) for the
1
+ # Changelog
2
+
3
+ All notable changes to SpotAPI are documented here.
4
+
5
+ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6
+ SpotAPI adheres to [Semantic Versioning](https://semver.org/).
7
+
8
+ ---
9
+
10
+ ## [Unreleased] — `async-v2` beta
11
+
12
+ ### Added
13
+
14
+ - **Full async rewrite** — every public method is now a coroutine or
15
+ `AsyncGenerator`. No sync wrappers, no `asyncio.run()` hidden inside the
16
+ library.
17
+ - **`Public` search generators** — `search_tracks`, `search_artists`,
18
+ `search_albums`, `search_playlists`, `search_podcasts` all return
19
+ `AsyncGenerator[Sequence[T]]` and paginate automatically.
20
+ - **Typed data wrappers** — `Track`, `Artist`, `Album`, `Playlist`, `Podcast`
21
+ are fully typed `@dataclass(slots=True)` classes with `__repr__`.
22
+ - **Shared visual types** — `_common.py` extracts the repeated color/theme
23
+ structs that Spotify returns on every entity, eliminating ~300 lines of
24
+ duplication across the data wrapper modules.
25
+ - **`from_dict` deserializer** — recursive, camelCase-aware dataclass factory
26
+ with `lru_cache`-backed annotation resolution.
27
+ - **`ObjectDict`** — attribute-access `dict` subclass for ergonomic raw JSON
28
+ traversal, with optional thread-safe write locking.
29
+ - **`Pool[T]`** — generic async object pool with factory, pre-warm, capped
30
+ eviction, and teardown callbacks.
31
+ - **`EventDispatcher`** — async event system used by `WebSocketClient`.
32
+ - **`WebSocketClient`** — async WebSocket wrapper with heartbeat manager,
33
+ `@event` decorator, and `asynccontextmanager`-based connection lifecycle.
34
+ - **`HTTPClient`** — `wreq`-backed HTTP client with:
35
+ - Exponential backoff + jitter retry strategy.
36
+ - Randomised browser profile emulation (Chrome, Edge, Firefox, Opera).
37
+ - Randomised OS emulation (Windows 30×, macOS 6×, Linux 2×).
38
+ - Shared `ClientPool` for connection reuse.
39
+ - **`BundleSession`** — parses Spotify's HTML to extract JS bundle URLs,
40
+ fetches and caches them, and derives persisted query hashes.
41
+ - **`AuthSession`** — TOTP-based access token + client token acquisition with
42
+ a background auto-refresh task. No CAPTCHA solver required.
43
+ - **`timed_cache`** — TTL-aware async/sync cache decorator used for the
44
+ expensive bundle-fetch step.
45
+ - **Five loggers**: `LoggerColour`, `StandardLogger`, `NoopLogger`,
46
+ `InbuiltLogger`, `JsonLogger`, and `MultiLogger` fan-out — all implementing
47
+ `LoggerProtocol`.
48
+ - **`py.typed` marker** — signals full PEP 561 type-checker support.
49
+ - `pyproject.toml` replaces `setup.py`.
50
+
51
+ ### Fixed
52
+
53
+ - `types/__init__.py` had duplicate star imports causing symbols to be
54
+ registered twice; replaced with explicit named imports.
55
+ - `utils/__init__.py` used a broken double-import pattern that shadowed the
56
+ module-level `__all__`; replaced with explicit named imports.
57
+ - `Pool.put()` called the deprecated `asyncio.get_event_loop()` (deprecated
58
+ in Python 3.11); replaced with `asyncio.get_running_loop()`.
59
+ - `_BaseLogger.log()` acquired the threading lock then returned early without
60
+ releasing it when `is_enabled()` was `False`; replaced the manual
61
+ acquire/release with a `with` statement via `contextlib.nullcontext`.
62
+ - `totp.py` had an unreachable `return FALLBACK` statement after a
63
+ `raise RuntimeError`; removed the dead code.
64
+ - Data wrappers (`track.py`, `artist.py`, `album.py`, `playlist.py`,
65
+ `podcast.py`) each redefined identical color/theme dataclasses; extracted
66
+ to `_common.py` and imported from there.
67
+
68
+ ### Changed
69
+
70
+ - Package layout reorganised into `connection/`, `datastruct/`,
71
+ `specialized/`, `types/`, `utils/` sub-packages.
72
+ - `setup.py` replaced by `pyproject.toml` (Hatchling build backend).
73
+ - Minimum Python version raised from 3.11 to **3.11**.
74
+
75
+ ### Removed
76
+
77
+ - `annotations.py` runtime type-enforcement decorator (`@enforce_types`,
78
+ `@enforce`) — moved to a dev-only tool; it has no callers in v2 and
79
+ imposes overhead on every method call.
80
+ - `parse_json_string` from `utils/strings.py` — v1 leftover with no callers
81
+ in v2 (Spotify's `correlationId` is now parsed via the `appServerConfig`
82
+ base64 blob).
83
+ - `setup.py`, `requirements.txt` — superseded by `pyproject.toml`.
84
+
85
+ ---
86
+
87
+ ## [1.2.8] — 2026-06-14 *(latest stable)*
88
+
89
+ > See the [main branch](https://github.com/Aran404/SpotAPI/tree/main) for the
90
90
  > v1 changelog.