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.
@@ -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
+ ![GitHub stars](https://img.shields.io/github/stars/adelelawady/Syncify?style=for-the-badge)
35
+ ![GitHub forks](https://img.shields.io/github/forks/adelelawady/Syncify?style=for-the-badge)
36
+ ![License](https://img.shields.io/github/license/adelelawady/Syncify?style=for-the-badge)
37
+ ![Repo size](https://img.shields.io/github/repo-size/adelelawady/Syncify?style=for-the-badge)
38
+ ![Last commit](https://img.shields.io/github/last-commit/adelelawady/Syncify?style=for-the-badge)
39
+ ![Issues](https://img.shields.io/github/issues/adelelawady/Syncify?style=for-the-badge)
40
+ ![Top language](https://img.shields.io/github/languages/top/adelelawady/Syncify?style=for-the-badge)
41
+ ![Python](https://img.shields.io/badge/Python-3.9%2B-3776AB?style=for-the-badge&logo=python&logoColor=white)
42
+ ![Selenium](https://img.shields.io/badge/Selenium-43B02A?style=for-the-badge&logo=selenium&logoColor=white)
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
+ ![GitHub stars](https://img.shields.io/github/stars/adelelawady/Syncify?style=for-the-badge)
10
+ ![GitHub forks](https://img.shields.io/github/forks/adelelawady/Syncify?style=for-the-badge)
11
+ ![License](https://img.shields.io/github/license/adelelawady/Syncify?style=for-the-badge)
12
+ ![Repo size](https://img.shields.io/github/repo-size/adelelawady/Syncify?style=for-the-badge)
13
+ ![Last commit](https://img.shields.io/github/last-commit/adelelawady/Syncify?style=for-the-badge)
14
+ ![Issues](https://img.shields.io/github/issues/adelelawady/Syncify?style=for-the-badge)
15
+ ![Top language](https://img.shields.io/github/languages/top/adelelawady/Syncify?style=for-the-badge)
16
+ ![Python](https://img.shields.io/badge/Python-3.9%2B-3776AB?style=for-the-badge&logo=python&logoColor=white)
17
+ ![Selenium](https://img.shields.io/badge/Selenium-43B02A?style=for-the-badge&logo=selenium&logoColor=white)
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,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -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())