syncify-py 1.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.
- syncify_py-1.0.0/PKG-INFO +262 -0
- syncify_py-1.0.0/README.md +237 -0
- syncify_py-1.0.0/pyproject.toml +40 -0
- syncify_py-1.0.0/setup.cfg +4 -0
- syncify_py-1.0.0/setup.py +41 -0
- syncify_py-1.0.0/syncify/__init__.py +6 -0
- syncify_py-1.0.0/syncify/__main__.py +79 -0
- syncify_py-1.0.0/syncify/spotify/Spotify_playlist_info.py +299 -0
- syncify_py-1.0.0/syncify/spotify/Spotify_track_info.py +177 -0
- syncify_py-1.0.0/syncify/spotify/__init__.py +1 -0
- syncify_py-1.0.0/syncify/spotify/utils.py +36 -0
- syncify_py-1.0.0/syncify_py.egg-info/PKG-INFO +262 -0
- syncify_py-1.0.0/syncify_py.egg-info/SOURCES.txt +15 -0
- syncify_py-1.0.0/syncify_py.egg-info/dependency_links.txt +1 -0
- syncify_py-1.0.0/syncify_py.egg-info/entry_points.txt +2 -0
- syncify_py-1.0.0/syncify_py.egg-info/requires.txt +7 -0
- syncify_py-1.0.0/syncify_py.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: syncify-py
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Spotify track and playlist metadata library
|
|
5
|
+
Author: adelelawady
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: spotify,playlist,track,metadata
|
|
8
|
+
Classifier: Development Status :: 4 - Beta
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Requires-Python: >=3.7.0
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
Requires-Dist: requests>=2.28.0
|
|
19
|
+
Requires-Dist: beautifulsoup4>=4.12.0
|
|
20
|
+
Requires-Dist: selenium>=4.15.0
|
|
21
|
+
Requires-Dist: webdriver-manager>=4.0.0
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Requires-Dist: mutagen>=1.47.0; extra == "dev"
|
|
24
|
+
Dynamic: requires-python
|
|
25
|
+
|
|
26
|
+
<!--
|
|
27
|
+
NOTE:
|
|
28
|
+
- This README assumes the GitHub repo will be published as: adelelawady/Syncify
|
|
29
|
+
- Update REPO_NAME below if you rename the repository.
|
|
30
|
+
-->
|
|
31
|
+
|
|
32
|
+
<div align="center">
|
|
33
|
+
|
|
34
|
+

|
|
35
|
+

|
|
36
|
+

|
|
37
|
+

|
|
38
|
+

|
|
39
|
+

|
|
40
|
+

|
|
41
|
+

|
|
42
|
+

|
|
43
|
+
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
# 🚀 Syncify
|
|
47
|
+
|
|
48
|
+
**Syncify** is a Python **library + CLI** that fetches **Spotify track and playlist metadata** directly from `open.spotify.com` pages — great for quick metadata lookups, playlist introspection, and tooling where you don’t want to wire up OAuth.
|
|
49
|
+
|
|
50
|
+
> **Heads up**: Syncify scrapes Spotify’s web UI (via Selenium). Selectors can break if Spotify updates their site.
|
|
51
|
+
|
|
52
|
+
## ✨ Features
|
|
53
|
+
|
|
54
|
+
- **Track metadata**: title, artist, cover image URL, and track ID from a track URL
|
|
55
|
+
- **Playlist metadata**: playlist title, cover image URL, playlist ID, and **all track URLs**
|
|
56
|
+
- **CLI-first**: run `syncify <url>` or `python -m syncify ...`
|
|
57
|
+
- **Auto-detect URLs**: mix track + playlist URLs in one command
|
|
58
|
+
- **No OAuth setup**: does **not** require Spotify API keys/tokens
|
|
59
|
+
|
|
60
|
+
## 🧠 How It Works
|
|
61
|
+
|
|
62
|
+
- **Input**: Spotify track/playlist URLs (`https://open.spotify.com/track/...`, `https://open.spotify.com/playlist/...`)
|
|
63
|
+
- **URL detection**: a lightweight regex-based detector determines whether each URL is a Track or Playlist
|
|
64
|
+
- **Extraction**:
|
|
65
|
+
- **Tracks**: Selenium loads the page and extracts title/artist/image from page elements
|
|
66
|
+
- **Playlists**: Selenium loads the page, scrolls through the track list, and collects every track link
|
|
67
|
+
- **Output**:
|
|
68
|
+
- **Library**: returns dataclasses (`TrackDetails`, `PlaylistDetails`)
|
|
69
|
+
- **CLI**: prints a readable summary plus playlist track URLs
|
|
70
|
+
|
|
71
|
+
## 🛠 Tech Stack
|
|
72
|
+
|
|
73
|
+
- **Language**: Python
|
|
74
|
+
- **Automation/scraping**: Selenium (headless Chrome)
|
|
75
|
+
- **Driver management**: `webdriver-manager` (fallback if Selenium driver resolution fails)
|
|
76
|
+
- **HTML parsing (small helper)**: BeautifulSoup4
|
|
77
|
+
- **HTTP**: `requests`
|
|
78
|
+
|
|
79
|
+
## 📦 Installation
|
|
80
|
+
|
|
81
|
+
### Prerequisites
|
|
82
|
+
|
|
83
|
+
- **Python**: 3.9+ recommended (packaging allows older, but tested targets are 3.9–3.12)
|
|
84
|
+
- **Google Chrome** installed (used by Selenium)
|
|
85
|
+
|
|
86
|
+
### Install from GitHub (recommended)
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
pip install "git+https://github.com/adelelawady/Syncify.git"
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Install locally (for development)
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
git clone https://github.com/adelelawady/Syncify.git
|
|
96
|
+
cd Syncify
|
|
97
|
+
pip install -e ".[dev]"
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Install from source tree (non-editable)
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
pip install .
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## ⚙️ Configuration
|
|
107
|
+
|
|
108
|
+
Syncify has **no required environment variables**.
|
|
109
|
+
|
|
110
|
+
### Runtime requirements
|
|
111
|
+
|
|
112
|
+
- **Chrome available on PATH / installed normally**
|
|
113
|
+
- **Chromedriver** is handled automatically via Selenium’s driver resolution, with a fallback to `webdriver-manager`.
|
|
114
|
+
|
|
115
|
+
### Troubleshooting
|
|
116
|
+
|
|
117
|
+
- If Selenium can’t start Chrome:
|
|
118
|
+
- Ensure Chrome is installed and up to date.
|
|
119
|
+
- Try upgrading Selenium and webdriver-manager:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
pip install -U selenium webdriver-manager
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
- If playlist results are incomplete:
|
|
126
|
+
- Spotify’s UI loads tracks lazily; the scraper scrolls, but very large playlists may take longer.
|
|
127
|
+
|
|
128
|
+
## 🚀 Usage
|
|
129
|
+
|
|
130
|
+
## **As a library**
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
from syncify import get_track, get_playlist
|
|
134
|
+
|
|
135
|
+
track = get_track("https://open.spotify.com/track/5nJ4Zzqc2UjwSaIcv7bGjx")
|
|
136
|
+
print(track.track_title, "-", track.artist_title)
|
|
137
|
+
print(track.track_image_url)
|
|
138
|
+
|
|
139
|
+
playlist = get_playlist("https://open.spotify.com/playlist/5YOevUTnavVClJ0hAslu0N")
|
|
140
|
+
print(playlist.title)
|
|
141
|
+
print("Tracks:", len(playlist.track_urls))
|
|
142
|
+
print(playlist.track_urls[:5])
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## **As a CLI**
|
|
146
|
+
|
|
147
|
+
After installation, you can use either:
|
|
148
|
+
|
|
149
|
+
- `syncify ...` (console script), or
|
|
150
|
+
- `python -m syncify ...` (module execution)
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
# Auto-detect URL type (track or playlist)
|
|
154
|
+
syncify https://open.spotify.com/track/5nJ4Zzqc2UjwSaIcv7bGjx
|
|
155
|
+
syncify https://open.spotify.com/playlist/5YOevUTnavVClJ0hAslu0N
|
|
156
|
+
|
|
157
|
+
# Explicit type
|
|
158
|
+
syncify --track https://open.spotify.com/track/...
|
|
159
|
+
syncify --playlist https://open.spotify.com/playlist/...
|
|
160
|
+
|
|
161
|
+
# Multiple URLs (mixed types supported)
|
|
162
|
+
syncify <url1> <url2> <url3>
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
CLI flags:
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
syncify --track <URL>
|
|
169
|
+
syncify --playlist <URL>
|
|
170
|
+
syncify <URL> [URL ...]
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## 📡 API Reference
|
|
174
|
+
|
|
175
|
+
### `get_track(url: str) -> TrackDetails`
|
|
176
|
+
|
|
177
|
+
Fetch metadata for a Spotify track URL.
|
|
178
|
+
|
|
179
|
+
| Field | Type | Description |
|
|
180
|
+
|---|---:|---|
|
|
181
|
+
| `spotify_url` | `str` | Original Spotify URL |
|
|
182
|
+
| `track_id` | `str` | Spotify track ID |
|
|
183
|
+
| `track_title` | `str` | Song title |
|
|
184
|
+
| `artist_title` | `str` | Artist name |
|
|
185
|
+
| `track_image_url` | `str` | Cover image URL |
|
|
186
|
+
|
|
187
|
+
### `get_playlist(url: str) -> PlaylistDetails`
|
|
188
|
+
|
|
189
|
+
Fetch metadata for a Spotify playlist URL.
|
|
190
|
+
|
|
191
|
+
| Field | Type | Description |
|
|
192
|
+
|---|---:|---|
|
|
193
|
+
| `playlist_url` | `str` | Original Spotify URL |
|
|
194
|
+
| `playlist_id` | `str` | Spotify playlist ID |
|
|
195
|
+
| `title` | `str` | Playlist title |
|
|
196
|
+
| `playlist_image_url` | `str` | Cover image URL |
|
|
197
|
+
| `track_urls` | `list[str]` | Track URLs in the playlist |
|
|
198
|
+
|
|
199
|
+
## 📂 Project Structure
|
|
200
|
+
|
|
201
|
+
```text
|
|
202
|
+
Syncify/
|
|
203
|
+
├─ syncify/
|
|
204
|
+
│ ├─ __init__.py # Public API exports
|
|
205
|
+
│ ├─ __main__.py # CLI: `python -m syncify` / `syncify`
|
|
206
|
+
│ └─ spotify/
|
|
207
|
+
│ ├─ Spotify_track_info.py
|
|
208
|
+
│ ├─ Spotify_playlist_info.py
|
|
209
|
+
│ ├─ utils.py
|
|
210
|
+
│ └─ __init__.py
|
|
211
|
+
├─ main.py # Convenience script wrapper
|
|
212
|
+
├─ pyproject.toml # Modern packaging + dependencies
|
|
213
|
+
├─ setup.py # Legacy packaging (mirrors pyproject)
|
|
214
|
+
├─ requirements.txt # Dev-friendly requirements list
|
|
215
|
+
└─ README.md
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## 🧪 Development
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
git clone https://github.com/adelelawady/Syncify.git
|
|
222
|
+
cd Syncify
|
|
223
|
+
python -m venv .venv
|
|
224
|
+
|
|
225
|
+
# Windows PowerShell
|
|
226
|
+
.venv\Scripts\Activate.ps1
|
|
227
|
+
|
|
228
|
+
pip install -e ".[dev]"
|
|
229
|
+
|
|
230
|
+
# Run the CLI against a URL
|
|
231
|
+
python -m syncify https://open.spotify.com/track/<id>
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Suggested checks:
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
python -c "from syncify import get_track; print(get_track('https://open.spotify.com/track/<id>').track_title)"
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
## 🤝 Contributing
|
|
241
|
+
|
|
242
|
+
Contributions are welcome!
|
|
243
|
+
|
|
244
|
+
- **Bugs/requests**: open an issue with a minimal repro (URL + expected vs actual output)
|
|
245
|
+
- **PRs**:
|
|
246
|
+
- Keep changes focused and include a clear description
|
|
247
|
+
- Prefer small, well-scoped improvements to selectors and parsing logic
|
|
248
|
+
- Avoid committing local artifacts (`.venv/`, `build/`, `syncify.egg-info/`)
|
|
249
|
+
|
|
250
|
+
If you’re adding new scraping logic, please include:
|
|
251
|
+
- A sample Spotify URL (track/playlist) that the change targets
|
|
252
|
+
- A note about which DOM selectors were relied on and why
|
|
253
|
+
|
|
254
|
+
## 📜 License
|
|
255
|
+
|
|
256
|
+
**MIT** (as declared in package metadata).
|
|
257
|
+
|
|
258
|
+
> Tip: consider adding a top-level `LICENSE` file so GitHub can display the license automatically.
|
|
259
|
+
|
|
260
|
+
## ⭐ Support
|
|
261
|
+
|
|
262
|
+
If you find Syncify useful, please **star** the repo — it helps others discover the project and motivates continued maintenance.
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
NOTE:
|
|
3
|
+
- This README assumes the GitHub repo will be published as: adelelawady/Syncify
|
|
4
|
+
- Update REPO_NAME below if you rename the repository.
|
|
5
|
+
-->
|
|
6
|
+
|
|
7
|
+
<div align="center">
|
|
8
|
+
|
|
9
|
+

|
|
10
|
+

|
|
11
|
+

|
|
12
|
+

|
|
13
|
+

|
|
14
|
+

|
|
15
|
+

|
|
16
|
+

|
|
17
|
+

|
|
18
|
+
|
|
19
|
+
</div>
|
|
20
|
+
|
|
21
|
+
# 🚀 Syncify
|
|
22
|
+
|
|
23
|
+
**Syncify** is a Python **library + CLI** that fetches **Spotify track and playlist metadata** directly from `open.spotify.com` pages — great for quick metadata lookups, playlist introspection, and tooling where you don’t want to wire up OAuth.
|
|
24
|
+
|
|
25
|
+
> **Heads up**: Syncify scrapes Spotify’s web UI (via Selenium). Selectors can break if Spotify updates their site.
|
|
26
|
+
|
|
27
|
+
## ✨ Features
|
|
28
|
+
|
|
29
|
+
- **Track metadata**: title, artist, cover image URL, and track ID from a track URL
|
|
30
|
+
- **Playlist metadata**: playlist title, cover image URL, playlist ID, and **all track URLs**
|
|
31
|
+
- **CLI-first**: run `syncify <url>` or `python -m syncify ...`
|
|
32
|
+
- **Auto-detect URLs**: mix track + playlist URLs in one command
|
|
33
|
+
- **No OAuth setup**: does **not** require Spotify API keys/tokens
|
|
34
|
+
|
|
35
|
+
## 🧠 How It Works
|
|
36
|
+
|
|
37
|
+
- **Input**: Spotify track/playlist URLs (`https://open.spotify.com/track/...`, `https://open.spotify.com/playlist/...`)
|
|
38
|
+
- **URL detection**: a lightweight regex-based detector determines whether each URL is a Track or Playlist
|
|
39
|
+
- **Extraction**:
|
|
40
|
+
- **Tracks**: Selenium loads the page and extracts title/artist/image from page elements
|
|
41
|
+
- **Playlists**: Selenium loads the page, scrolls through the track list, and collects every track link
|
|
42
|
+
- **Output**:
|
|
43
|
+
- **Library**: returns dataclasses (`TrackDetails`, `PlaylistDetails`)
|
|
44
|
+
- **CLI**: prints a readable summary plus playlist track URLs
|
|
45
|
+
|
|
46
|
+
## 🛠 Tech Stack
|
|
47
|
+
|
|
48
|
+
- **Language**: Python
|
|
49
|
+
- **Automation/scraping**: Selenium (headless Chrome)
|
|
50
|
+
- **Driver management**: `webdriver-manager` (fallback if Selenium driver resolution fails)
|
|
51
|
+
- **HTML parsing (small helper)**: BeautifulSoup4
|
|
52
|
+
- **HTTP**: `requests`
|
|
53
|
+
|
|
54
|
+
## 📦 Installation
|
|
55
|
+
|
|
56
|
+
### Prerequisites
|
|
57
|
+
|
|
58
|
+
- **Python**: 3.9+ recommended (packaging allows older, but tested targets are 3.9–3.12)
|
|
59
|
+
- **Google Chrome** installed (used by Selenium)
|
|
60
|
+
|
|
61
|
+
### Install from GitHub (recommended)
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
pip install "git+https://github.com/adelelawady/Syncify.git"
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Install locally (for development)
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
git clone https://github.com/adelelawady/Syncify.git
|
|
71
|
+
cd Syncify
|
|
72
|
+
pip install -e ".[dev]"
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Install from source tree (non-editable)
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
pip install .
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## ⚙️ Configuration
|
|
82
|
+
|
|
83
|
+
Syncify has **no required environment variables**.
|
|
84
|
+
|
|
85
|
+
### Runtime requirements
|
|
86
|
+
|
|
87
|
+
- **Chrome available on PATH / installed normally**
|
|
88
|
+
- **Chromedriver** is handled automatically via Selenium’s driver resolution, with a fallback to `webdriver-manager`.
|
|
89
|
+
|
|
90
|
+
### Troubleshooting
|
|
91
|
+
|
|
92
|
+
- If Selenium can’t start Chrome:
|
|
93
|
+
- Ensure Chrome is installed and up to date.
|
|
94
|
+
- Try upgrading Selenium and webdriver-manager:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
pip install -U selenium webdriver-manager
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
- If playlist results are incomplete:
|
|
101
|
+
- Spotify’s UI loads tracks lazily; the scraper scrolls, but very large playlists may take longer.
|
|
102
|
+
|
|
103
|
+
## 🚀 Usage
|
|
104
|
+
|
|
105
|
+
## **As a library**
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
from syncify import get_track, get_playlist
|
|
109
|
+
|
|
110
|
+
track = get_track("https://open.spotify.com/track/5nJ4Zzqc2UjwSaIcv7bGjx")
|
|
111
|
+
print(track.track_title, "-", track.artist_title)
|
|
112
|
+
print(track.track_image_url)
|
|
113
|
+
|
|
114
|
+
playlist = get_playlist("https://open.spotify.com/playlist/5YOevUTnavVClJ0hAslu0N")
|
|
115
|
+
print(playlist.title)
|
|
116
|
+
print("Tracks:", len(playlist.track_urls))
|
|
117
|
+
print(playlist.track_urls[:5])
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## **As a CLI**
|
|
121
|
+
|
|
122
|
+
After installation, you can use either:
|
|
123
|
+
|
|
124
|
+
- `syncify ...` (console script), or
|
|
125
|
+
- `python -m syncify ...` (module execution)
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
# Auto-detect URL type (track or playlist)
|
|
129
|
+
syncify https://open.spotify.com/track/5nJ4Zzqc2UjwSaIcv7bGjx
|
|
130
|
+
syncify https://open.spotify.com/playlist/5YOevUTnavVClJ0hAslu0N
|
|
131
|
+
|
|
132
|
+
# Explicit type
|
|
133
|
+
syncify --track https://open.spotify.com/track/...
|
|
134
|
+
syncify --playlist https://open.spotify.com/playlist/...
|
|
135
|
+
|
|
136
|
+
# Multiple URLs (mixed types supported)
|
|
137
|
+
syncify <url1> <url2> <url3>
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
CLI flags:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
syncify --track <URL>
|
|
144
|
+
syncify --playlist <URL>
|
|
145
|
+
syncify <URL> [URL ...]
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## 📡 API Reference
|
|
149
|
+
|
|
150
|
+
### `get_track(url: str) -> TrackDetails`
|
|
151
|
+
|
|
152
|
+
Fetch metadata for a Spotify track URL.
|
|
153
|
+
|
|
154
|
+
| Field | Type | Description |
|
|
155
|
+
|---|---:|---|
|
|
156
|
+
| `spotify_url` | `str` | Original Spotify URL |
|
|
157
|
+
| `track_id` | `str` | Spotify track ID |
|
|
158
|
+
| `track_title` | `str` | Song title |
|
|
159
|
+
| `artist_title` | `str` | Artist name |
|
|
160
|
+
| `track_image_url` | `str` | Cover image URL |
|
|
161
|
+
|
|
162
|
+
### `get_playlist(url: str) -> PlaylistDetails`
|
|
163
|
+
|
|
164
|
+
Fetch metadata for a Spotify playlist URL.
|
|
165
|
+
|
|
166
|
+
| Field | Type | Description |
|
|
167
|
+
|---|---:|---|
|
|
168
|
+
| `playlist_url` | `str` | Original Spotify URL |
|
|
169
|
+
| `playlist_id` | `str` | Spotify playlist ID |
|
|
170
|
+
| `title` | `str` | Playlist title |
|
|
171
|
+
| `playlist_image_url` | `str` | Cover image URL |
|
|
172
|
+
| `track_urls` | `list[str]` | Track URLs in the playlist |
|
|
173
|
+
|
|
174
|
+
## 📂 Project Structure
|
|
175
|
+
|
|
176
|
+
```text
|
|
177
|
+
Syncify/
|
|
178
|
+
├─ syncify/
|
|
179
|
+
│ ├─ __init__.py # Public API exports
|
|
180
|
+
│ ├─ __main__.py # CLI: `python -m syncify` / `syncify`
|
|
181
|
+
│ └─ spotify/
|
|
182
|
+
│ ├─ Spotify_track_info.py
|
|
183
|
+
│ ├─ Spotify_playlist_info.py
|
|
184
|
+
│ ├─ utils.py
|
|
185
|
+
│ └─ __init__.py
|
|
186
|
+
├─ main.py # Convenience script wrapper
|
|
187
|
+
├─ pyproject.toml # Modern packaging + dependencies
|
|
188
|
+
├─ setup.py # Legacy packaging (mirrors pyproject)
|
|
189
|
+
├─ requirements.txt # Dev-friendly requirements list
|
|
190
|
+
└─ README.md
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## 🧪 Development
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
git clone https://github.com/adelelawady/Syncify.git
|
|
197
|
+
cd Syncify
|
|
198
|
+
python -m venv .venv
|
|
199
|
+
|
|
200
|
+
# Windows PowerShell
|
|
201
|
+
.venv\Scripts\Activate.ps1
|
|
202
|
+
|
|
203
|
+
pip install -e ".[dev]"
|
|
204
|
+
|
|
205
|
+
# Run the CLI against a URL
|
|
206
|
+
python -m syncify https://open.spotify.com/track/<id>
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Suggested checks:
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
python -c "from syncify import get_track; print(get_track('https://open.spotify.com/track/<id>').track_title)"
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## 🤝 Contributing
|
|
216
|
+
|
|
217
|
+
Contributions are welcome!
|
|
218
|
+
|
|
219
|
+
- **Bugs/requests**: open an issue with a minimal repro (URL + expected vs actual output)
|
|
220
|
+
- **PRs**:
|
|
221
|
+
- Keep changes focused and include a clear description
|
|
222
|
+
- Prefer small, well-scoped improvements to selectors and parsing logic
|
|
223
|
+
- Avoid committing local artifacts (`.venv/`, `build/`, `syncify.egg-info/`)
|
|
224
|
+
|
|
225
|
+
If you’re adding new scraping logic, please include:
|
|
226
|
+
- A sample Spotify URL (track/playlist) that the change targets
|
|
227
|
+
- A note about which DOM selectors were relied on and why
|
|
228
|
+
|
|
229
|
+
## 📜 License
|
|
230
|
+
|
|
231
|
+
**MIT** (as declared in package metadata).
|
|
232
|
+
|
|
233
|
+
> Tip: consider adding a top-level `LICENSE` file so GitHub can display the license automatically.
|
|
234
|
+
|
|
235
|
+
## ⭐ Support
|
|
236
|
+
|
|
237
|
+
If you find Syncify useful, please **star** the repo — it helps others discover the project and motivates continued maintenance.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "syncify-py"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Spotify track and playlist metadata library"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.7"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [{ name = "adelelawady" }]
|
|
13
|
+
keywords = ["spotify", "playlist", "track", "metadata"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Programming Language :: Python :: 3.9",
|
|
20
|
+
"Programming Language :: Python :: 3.10",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
dependencies = [
|
|
26
|
+
"requests>=2.28.0",
|
|
27
|
+
"beautifulsoup4>=4.12.0",
|
|
28
|
+
"selenium>=4.15.0",
|
|
29
|
+
"webdriver-manager>=4.0.0",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[project.optional-dependencies]
|
|
33
|
+
dev = ["mutagen>=1.47.0"]
|
|
34
|
+
|
|
35
|
+
[project.scripts]
|
|
36
|
+
syncify = "syncify.__main__:main"
|
|
37
|
+
|
|
38
|
+
[tool.setuptools.packages.find]
|
|
39
|
+
where = ["."]
|
|
40
|
+
include = ["syncify*"]
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from setuptools import find_packages, setup
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
setup(
|
|
5
|
+
name="syncify-py",
|
|
6
|
+
version="1.0.0",
|
|
7
|
+
description="Spotify track and playlist metadata library",
|
|
8
|
+
long_description=open("README.md", encoding="utf-8").read(),
|
|
9
|
+
long_description_content_type="text/markdown",
|
|
10
|
+
license="MIT",
|
|
11
|
+
python_requires=">=3.7.0",
|
|
12
|
+
packages=find_packages(include=["syncify*"]),
|
|
13
|
+
include_package_data=True,
|
|
14
|
+
install_requires=[
|
|
15
|
+
"requests>=2.28.0",
|
|
16
|
+
"beautifulsoup4>=4.12.0",
|
|
17
|
+
"selenium>=4.15.0",
|
|
18
|
+
"webdriver-manager>=4.0.0",
|
|
19
|
+
],
|
|
20
|
+
extras_require={
|
|
21
|
+
"dev": ["mutagen>=1.47.0"],
|
|
22
|
+
},
|
|
23
|
+
entry_points={
|
|
24
|
+
"console_scripts": [
|
|
25
|
+
"syncify=syncify.__main__:main",
|
|
26
|
+
]
|
|
27
|
+
},
|
|
28
|
+
classifiers=[
|
|
29
|
+
"Development Status :: 4 - Beta",
|
|
30
|
+
"Intended Audience :: Developers",
|
|
31
|
+
"License :: OSI Approved :: MIT License",
|
|
32
|
+
"Programming Language :: Python :: 3",
|
|
33
|
+
"Programming Language :: Python :: 3.9",
|
|
34
|
+
"Programming Language :: Python :: 3.10",
|
|
35
|
+
"Programming Language :: Python :: 3.11",
|
|
36
|
+
"Programming Language :: Python :: 3.12",
|
|
37
|
+
],
|
|
38
|
+
keywords=["spotify", "playlist", "track", "metadata"],
|
|
39
|
+
author="adelelawady",
|
|
40
|
+
)
|
|
41
|
+
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"""Syncify - Spotify track and playlist metadata library."""
|
|
2
|
+
|
|
3
|
+
from syncify.spotify.Spotify_playlist_info import PlaylistDetails, get_playlist
|
|
4
|
+
from syncify.spotify.Spotify_track_info import TrackDetails, get_track
|
|
5
|
+
|
|
6
|
+
__all__ = ["get_track", "get_playlist", "TrackDetails", "PlaylistDetails"]
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""CLI entry point for python -m syncify."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
from syncify.spotify.Spotify_playlist_info import PlaylistDetails, get_playlist
|
|
7
|
+
from syncify.spotify.Spotify_track_info import TrackDetails, get_track
|
|
8
|
+
from syncify.spotify.utils import get_link_type
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _print_track(details: TrackDetails) -> None:
|
|
12
|
+
print("Track:")
|
|
13
|
+
print(f" URL : {details.spotify_url or '(empty)'}")
|
|
14
|
+
print(f" ID : {details.track_id or '(empty)'}")
|
|
15
|
+
print(f" Title : {details.track_title or '(empty)'}")
|
|
16
|
+
print(f" Artist : {details.artist_title or '(empty)'}")
|
|
17
|
+
print(f" Image : {details.track_image_url or '(empty)'}")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _print_playlist(details: PlaylistDetails) -> None:
|
|
21
|
+
print("Playlist:")
|
|
22
|
+
print(f" URL : {details.playlist_url or '(empty)'}")
|
|
23
|
+
print(f" ID : {details.playlist_id or '(empty)'}")
|
|
24
|
+
print(f" Title : {details.title or '(empty)'}")
|
|
25
|
+
print(f" Tracks : {len(details.track_urls)}")
|
|
26
|
+
print(f" Image : {details.playlist_image_url or '(empty)'}")
|
|
27
|
+
for i, url in enumerate(details.track_urls, 1):
|
|
28
|
+
print(f" {i:>3}. {url}")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _run(urls: list[str]) -> int:
|
|
32
|
+
for i, url in enumerate(urls, 1):
|
|
33
|
+
link_type = get_link_type(url)
|
|
34
|
+
print("=" * 60)
|
|
35
|
+
print(f"[{i}] {url}")
|
|
36
|
+
print(f"Type: {link_type or 'Invalid'}")
|
|
37
|
+
|
|
38
|
+
if link_type == "Track":
|
|
39
|
+
try:
|
|
40
|
+
_print_track(get_track(url))
|
|
41
|
+
except Exception as e:
|
|
42
|
+
print(f"Error: {e}")
|
|
43
|
+
return 1
|
|
44
|
+
elif link_type == "Playlist":
|
|
45
|
+
try:
|
|
46
|
+
_print_playlist(get_playlist(url))
|
|
47
|
+
except Exception as e:
|
|
48
|
+
print(f"Error: {e}")
|
|
49
|
+
return 1
|
|
50
|
+
else:
|
|
51
|
+
print("Invalid Spotify URL. Use track or playlist links.")
|
|
52
|
+
return 1
|
|
53
|
+
return 0
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def main() -> int:
|
|
57
|
+
parser = argparse.ArgumentParser(description="Fetch Spotify track or playlist details.")
|
|
58
|
+
group = parser.add_mutually_exclusive_group()
|
|
59
|
+
group.add_argument("--track", metavar="URL", help="Fetch track details")
|
|
60
|
+
group.add_argument("--playlist", metavar="URL", help="Fetch playlist details")
|
|
61
|
+
parser.add_argument("urls", nargs="*", metavar="URL", help="Spotify URLs (auto-detect)")
|
|
62
|
+
args = parser.parse_args()
|
|
63
|
+
|
|
64
|
+
if args.track:
|
|
65
|
+
urls = [args.track]
|
|
66
|
+
elif args.playlist:
|
|
67
|
+
urls = [args.playlist]
|
|
68
|
+
elif args.urls:
|
|
69
|
+
urls = args.urls
|
|
70
|
+
else:
|
|
71
|
+
print("Usage: python -m syncify <url> [url ...]")
|
|
72
|
+
print(" python -m syncify --track <url>")
|
|
73
|
+
print(" python -m syncify --playlist <url>")
|
|
74
|
+
return 1
|
|
75
|
+
return _run(urls)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
if __name__ == "__main__":
|
|
79
|
+
sys.exit(main())
|