innerspot 0.0.1.dev2__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.
- innerspot-0.0.1.dev2/.gitignore +33 -0
- innerspot-0.0.1.dev2/.python-version +1 -0
- innerspot-0.0.1.dev2/LICENSE +21 -0
- innerspot-0.0.1.dev2/PKG-INFO +149 -0
- innerspot-0.0.1.dev2/README.md +122 -0
- innerspot-0.0.1.dev2/innerspot/__init__.py +515 -0
- innerspot-0.0.1.dev2/innerspot/_auth.py +467 -0
- innerspot-0.0.1.dev2/innerspot/_base.py +52 -0
- innerspot-0.0.1.dev2/innerspot/_cache.py +75 -0
- innerspot-0.0.1.dev2/innerspot/_constants.py +14 -0
- innerspot-0.0.1.dev2/innerspot/_utils.py +133 -0
- innerspot-0.0.1.dev2/innerspot/_version.py +34 -0
- innerspot-0.0.1.dev2/innerspot/errors.py +55 -0
- innerspot-0.0.1.dev2/innerspot/models.py +331 -0
- innerspot-0.0.1.dev2/innerspot/proto/__init__.py +0 -0
- innerspot-0.0.1.dev2/innerspot/proto/definitions/content_ratings.proto +25 -0
- innerspot-0.0.1.dev2/innerspot/proto/definitions/entity_extension_data.proto +30 -0
- innerspot-0.0.1.dev2/innerspot/proto/definitions/extended_metadata.proto +53 -0
- innerspot-0.0.1.dev2/innerspot/proto/definitions/extension_kind.proto +238 -0
- innerspot-0.0.1.dev2/innerspot/proto/definitions/metadata.proto +344 -0
- innerspot-0.0.1.dev2/innerspot/proto/generated/__init__.py +0 -0
- innerspot-0.0.1.dev2/innerspot/proto/generated/content_ratings_pb2.py +42 -0
- innerspot-0.0.1.dev2/innerspot/proto/generated/entity_extension_data_pb2.py +45 -0
- innerspot-0.0.1.dev2/innerspot/proto/generated/extended_metadata_pb2.py +54 -0
- innerspot-0.0.1.dev2/innerspot/proto/generated/extension_kind_pb2.py +36 -0
- innerspot-0.0.1.dev2/innerspot/proto/generated/metadata_pb2.py +109 -0
- innerspot-0.0.1.dev2/innerspot/providers/__init__.py +5 -0
- innerspot-0.0.1.dev2/innerspot/providers/partner.py +728 -0
- innerspot-0.0.1.dev2/innerspot/providers/scraping.py +203 -0
- innerspot-0.0.1.dev2/innerspot/providers/spclient.py +691 -0
- innerspot-0.0.1.dev2/innerspot/py.typed +0 -0
- innerspot-0.0.1.dev2/pyproject.toml +70 -0
- innerspot-0.0.1.dev2/scripts/extract_hashes.py +293 -0
- innerspot-0.0.1.dev2/scripts/generate_proto.sh +49 -0
- innerspot-0.0.1.dev2/scripts/test.py +6 -0
- innerspot-0.0.1.dev2/tests/__init__.py +0 -0
- innerspot-0.0.1.dev2/tests/conftest.py +61 -0
- innerspot-0.0.1.dev2/tests/test_anonymous.py +505 -0
- innerspot-0.0.1.dev2/tests/test_auth.py +106 -0
- innerspot-0.0.1.dev2/tests/test_cache.py +109 -0
- innerspot-0.0.1.dev2/tests/test_cookie.py +294 -0
- innerspot-0.0.1.dev2/tests/test_models.py +352 -0
- innerspot-0.0.1.dev2/tests/test_utils.py +190 -0
- innerspot-0.0.1.dev2/uv.lock +432 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
innerspot/_version.py
|
|
4
|
+
*.py[cod]
|
|
5
|
+
*.egg-info/
|
|
6
|
+
dist/
|
|
7
|
+
build/
|
|
8
|
+
*.egg
|
|
9
|
+
|
|
10
|
+
# Environment
|
|
11
|
+
.env
|
|
12
|
+
|
|
13
|
+
# Local workspace
|
|
14
|
+
.scratch/
|
|
15
|
+
|
|
16
|
+
# Virtual environments
|
|
17
|
+
.venv/
|
|
18
|
+
|
|
19
|
+
# Cache
|
|
20
|
+
.ruff_cache/
|
|
21
|
+
.mypy_cache/
|
|
22
|
+
.pytest_cache/
|
|
23
|
+
.innerspot.cache
|
|
24
|
+
|
|
25
|
+
# IDE
|
|
26
|
+
.vscode/
|
|
27
|
+
.idea/
|
|
28
|
+
*.swp
|
|
29
|
+
*.swo
|
|
30
|
+
|
|
31
|
+
# OS
|
|
32
|
+
.DS_Store
|
|
33
|
+
Thumbs.db
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.13
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026-present Adnan Ahmad
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: innerspot
|
|
3
|
+
Version: 0.0.1.dev2
|
|
4
|
+
Summary: Spotify internal API client using protobuf, GraphQL, and web scraping.
|
|
5
|
+
Project-URL: Homepage, https://github.com/viperadnan-git/innerspot
|
|
6
|
+
Project-URL: Repository, https://github.com/viperadnan-git/innerspot
|
|
7
|
+
Project-URL: Issues, https://github.com/viperadnan-git/innerspot/issues
|
|
8
|
+
Author-email: Adnan Ahmad <viperadnan@gmail.com>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: api,client,graphql,metadata,music,protobuf,spotify
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
Classifier: Typing :: Typed
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Requires-Dist: httpx>=0.28.1
|
|
24
|
+
Requires-Dist: protobuf>=5.29.0
|
|
25
|
+
Requires-Dist: pydantic>=2.10.0
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# innerspot
|
|
29
|
+
|
|
30
|
+
A Python client for Spotify's internal APIs using protobuf, GraphQL, and web scraping.
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install innerspot
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Quick Start
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
from innerspot import SpotifyClient
|
|
42
|
+
|
|
43
|
+
# No auth needed for basic metadata and search
|
|
44
|
+
with SpotifyClient() as client:
|
|
45
|
+
track = client.get_track("4PTG3Z6ehGkBFwjybzWkR8")
|
|
46
|
+
print(f"{track.name} by {track.artists[0].name}")
|
|
47
|
+
|
|
48
|
+
album = client.get_album("6PFPjumGRpZnBzqnDci6qJ")
|
|
49
|
+
print(f"{album.name} ({album.total_tracks} tracks)")
|
|
50
|
+
|
|
51
|
+
artist = client.get_artist("06HL4z0CvFAxyc27GXpf02")
|
|
52
|
+
print(f"{artist.name} - {artist.popularity} popularity")
|
|
53
|
+
|
|
54
|
+
results = client.search("Never Gonna Give You Up")
|
|
55
|
+
print(f"{len(results.tracks.items)} tracks found")
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Authentication
|
|
59
|
+
|
|
60
|
+
**SpotifyWeb** (default) — uses the web player's TOTP auth flow:
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
from innerspot import SpotifyClient, SpotifyWeb
|
|
64
|
+
|
|
65
|
+
# Anonymous (metadata, search, home sections)
|
|
66
|
+
client = SpotifyClient()
|
|
67
|
+
|
|
68
|
+
# With sp_dc cookie (adds lyrics, radio, spclient search)
|
|
69
|
+
client = SpotifyClient(auth=SpotifyWeb(sp_dc="your_sp_dc_cookie"))
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**SpotifyEmbed** — simpler embed page token (no TOTP, limited permissions):
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
from innerspot import SpotifyClient, SpotifyEmbed
|
|
76
|
+
|
|
77
|
+
client = SpotifyClient(auth=SpotifyEmbed())
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**SpotifyPKCE** — full account access with interactive OAuth login:
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
from innerspot import SpotifyClient, SpotifyPKCE
|
|
84
|
+
|
|
85
|
+
auth = SpotifyPKCE() # opens browser for login
|
|
86
|
+
client = SpotifyClient(auth=auth)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Features
|
|
90
|
+
|
|
91
|
+
- **Tracks**: `get_track()`, `get_isrc_track()`, radio/recommendations
|
|
92
|
+
- **Albums**: `get_album()`, new releases
|
|
93
|
+
- **Artists**: `get_artist()`, `get_artists()`, `get_artist_discography()`
|
|
94
|
+
- **Playlists**: `get_playlist()`, `get_playlist_with_tracks()`, user playlists
|
|
95
|
+
- **Search**: `search()`, `search_genre()`
|
|
96
|
+
- **Lyrics**: `get_lyrics()` (requires sp_dc)
|
|
97
|
+
- **Browse**: `get_home_sections()`, `get_charts()`, `get_featured_playlists()`
|
|
98
|
+
- **Radio**: `get_radio()`, `iter_genre_tracks()`, `iter_artist_radio()`, `iter_track_radio()`
|
|
99
|
+
|
|
100
|
+
## Data Providers
|
|
101
|
+
|
|
102
|
+
Providers are accessible directly or through convenience methods with fallback:
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
# Direct provider access
|
|
106
|
+
track = client.spclient.get_track("4PTG3Z6ehGkBFwjybzWkR8")
|
|
107
|
+
track = client.partner.get_track("4PTG3Z6ehGkBFwjybzWkR8")
|
|
108
|
+
|
|
109
|
+
# Convenience with fallback (spclient → partner → scraping)
|
|
110
|
+
track = client.get_track("4PTG3Z6ehGkBFwjybzWkR8")
|
|
111
|
+
|
|
112
|
+
# Low-level 1:1 API access
|
|
113
|
+
data = client.spclient.extended_metadata([(uri, kind, cls)])
|
|
114
|
+
results = client.spclient.search("test")
|
|
115
|
+
lyrics = client.spclient.color_lyrics("4PTG3Z6ehGkBFwjybzWkR8")
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
1. **SpClientProvider** (protobuf) — Spotify's internal metadata API, highest fidelity
|
|
119
|
+
2. **PartnerProvider** (GraphQL) — Spotify's partner API, includes play counts and search
|
|
120
|
+
3. **ScrapingProvider** (HTML) — Fallback via open.spotify.com page scraping
|
|
121
|
+
|
|
122
|
+
## Token Caching
|
|
123
|
+
|
|
124
|
+
Tokens are cached to `.innerspot.cache` by default. Custom cache handlers:
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
from innerspot import SpotifyClient, SpotifyWeb, MemoryCacheHandler
|
|
128
|
+
|
|
129
|
+
client = SpotifyClient(
|
|
130
|
+
auth=SpotifyWeb(cache_handler=MemoryCacheHandler())
|
|
131
|
+
)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Disclaimer
|
|
135
|
+
|
|
136
|
+
> [!WARNING]
|
|
137
|
+
> This library uses Spotify's **internal, undocumented APIs** that are not intended for third-party use. These APIs can change or break without notice.
|
|
138
|
+
>
|
|
139
|
+
> - This project is for **educational and research purposes only**
|
|
140
|
+
> - Using this library may violate [Spotify's Terms of Service](https://www.spotify.com/legal/end-user-agreement/) and [Developer Terms](https://developer.spotify.com/terms)
|
|
141
|
+
> - Your Spotify account could be **suspended or banned**
|
|
142
|
+
> - The authors assume **no liability** for any consequences of using this software
|
|
143
|
+
> - Do **not** use this for commercial purposes or at scale
|
|
144
|
+
>
|
|
145
|
+
> **By using this library, you accept full responsibility for any consequences.**
|
|
146
|
+
|
|
147
|
+
## License
|
|
148
|
+
|
|
149
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# innerspot
|
|
2
|
+
|
|
3
|
+
A Python client for Spotify's internal APIs using protobuf, GraphQL, and web scraping.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install innerspot
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from innerspot import SpotifyClient
|
|
15
|
+
|
|
16
|
+
# No auth needed for basic metadata and search
|
|
17
|
+
with SpotifyClient() as client:
|
|
18
|
+
track = client.get_track("4PTG3Z6ehGkBFwjybzWkR8")
|
|
19
|
+
print(f"{track.name} by {track.artists[0].name}")
|
|
20
|
+
|
|
21
|
+
album = client.get_album("6PFPjumGRpZnBzqnDci6qJ")
|
|
22
|
+
print(f"{album.name} ({album.total_tracks} tracks)")
|
|
23
|
+
|
|
24
|
+
artist = client.get_artist("06HL4z0CvFAxyc27GXpf02")
|
|
25
|
+
print(f"{artist.name} - {artist.popularity} popularity")
|
|
26
|
+
|
|
27
|
+
results = client.search("Never Gonna Give You Up")
|
|
28
|
+
print(f"{len(results.tracks.items)} tracks found")
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Authentication
|
|
32
|
+
|
|
33
|
+
**SpotifyWeb** (default) — uses the web player's TOTP auth flow:
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
from innerspot import SpotifyClient, SpotifyWeb
|
|
37
|
+
|
|
38
|
+
# Anonymous (metadata, search, home sections)
|
|
39
|
+
client = SpotifyClient()
|
|
40
|
+
|
|
41
|
+
# With sp_dc cookie (adds lyrics, radio, spclient search)
|
|
42
|
+
client = SpotifyClient(auth=SpotifyWeb(sp_dc="your_sp_dc_cookie"))
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**SpotifyEmbed** — simpler embed page token (no TOTP, limited permissions):
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
from innerspot import SpotifyClient, SpotifyEmbed
|
|
49
|
+
|
|
50
|
+
client = SpotifyClient(auth=SpotifyEmbed())
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**SpotifyPKCE** — full account access with interactive OAuth login:
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
from innerspot import SpotifyClient, SpotifyPKCE
|
|
57
|
+
|
|
58
|
+
auth = SpotifyPKCE() # opens browser for login
|
|
59
|
+
client = SpotifyClient(auth=auth)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Features
|
|
63
|
+
|
|
64
|
+
- **Tracks**: `get_track()`, `get_isrc_track()`, radio/recommendations
|
|
65
|
+
- **Albums**: `get_album()`, new releases
|
|
66
|
+
- **Artists**: `get_artist()`, `get_artists()`, `get_artist_discography()`
|
|
67
|
+
- **Playlists**: `get_playlist()`, `get_playlist_with_tracks()`, user playlists
|
|
68
|
+
- **Search**: `search()`, `search_genre()`
|
|
69
|
+
- **Lyrics**: `get_lyrics()` (requires sp_dc)
|
|
70
|
+
- **Browse**: `get_home_sections()`, `get_charts()`, `get_featured_playlists()`
|
|
71
|
+
- **Radio**: `get_radio()`, `iter_genre_tracks()`, `iter_artist_radio()`, `iter_track_radio()`
|
|
72
|
+
|
|
73
|
+
## Data Providers
|
|
74
|
+
|
|
75
|
+
Providers are accessible directly or through convenience methods with fallback:
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
# Direct provider access
|
|
79
|
+
track = client.spclient.get_track("4PTG3Z6ehGkBFwjybzWkR8")
|
|
80
|
+
track = client.partner.get_track("4PTG3Z6ehGkBFwjybzWkR8")
|
|
81
|
+
|
|
82
|
+
# Convenience with fallback (spclient → partner → scraping)
|
|
83
|
+
track = client.get_track("4PTG3Z6ehGkBFwjybzWkR8")
|
|
84
|
+
|
|
85
|
+
# Low-level 1:1 API access
|
|
86
|
+
data = client.spclient.extended_metadata([(uri, kind, cls)])
|
|
87
|
+
results = client.spclient.search("test")
|
|
88
|
+
lyrics = client.spclient.color_lyrics("4PTG3Z6ehGkBFwjybzWkR8")
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
1. **SpClientProvider** (protobuf) — Spotify's internal metadata API, highest fidelity
|
|
92
|
+
2. **PartnerProvider** (GraphQL) — Spotify's partner API, includes play counts and search
|
|
93
|
+
3. **ScrapingProvider** (HTML) — Fallback via open.spotify.com page scraping
|
|
94
|
+
|
|
95
|
+
## Token Caching
|
|
96
|
+
|
|
97
|
+
Tokens are cached to `.innerspot.cache` by default. Custom cache handlers:
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
from innerspot import SpotifyClient, SpotifyWeb, MemoryCacheHandler
|
|
101
|
+
|
|
102
|
+
client = SpotifyClient(
|
|
103
|
+
auth=SpotifyWeb(cache_handler=MemoryCacheHandler())
|
|
104
|
+
)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Disclaimer
|
|
108
|
+
|
|
109
|
+
> [!WARNING]
|
|
110
|
+
> This library uses Spotify's **internal, undocumented APIs** that are not intended for third-party use. These APIs can change or break without notice.
|
|
111
|
+
>
|
|
112
|
+
> - This project is for **educational and research purposes only**
|
|
113
|
+
> - Using this library may violate [Spotify's Terms of Service](https://www.spotify.com/legal/end-user-agreement/) and [Developer Terms](https://developer.spotify.com/terms)
|
|
114
|
+
> - Your Spotify account could be **suspended or banned**
|
|
115
|
+
> - The authors assume **no liability** for any consequences of using this software
|
|
116
|
+
> - Do **not** use this for commercial purposes or at scale
|
|
117
|
+
>
|
|
118
|
+
> **By using this library, you accept full responsibility for any consequences.**
|
|
119
|
+
|
|
120
|
+
## License
|
|
121
|
+
|
|
122
|
+
[MIT](LICENSE)
|