spotify-profile-monitor 3.1__tar.gz → 3.2__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.
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: spotify_profile_monitor
3
- Version: 3.1
4
- Summary: Tool implementing real-time tracking of Spotify users activities and profile changes including playlists
3
+ Version: 3.2
4
+ Summary: Track Spotify profile, follower and playlist changes in real time
5
5
  Author-email: Michal Szymanski <misiektoja-pypi@rm-rf.ninja>
6
6
  License-Expression: GPL-3.0-or-later
7
7
  Project-URL: Homepage, https://github.com/misiektoja/spotify_profile_monitor
@@ -9,6 +9,15 @@ Project-URL: Source, https://github.com/misiektoja/spotify_profile_monitor
9
9
  Project-URL: Changelog, https://github.com/misiektoja/spotify_profile_monitor/blob/main/RELEASE_NOTES.md
10
10
  Keywords: spotify,monitoring,tracking,real-time,playlists,osint
11
11
  Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.6
13
+ Classifier: Programming Language :: Python :: 3.7
14
+ Classifier: Programming Language :: Python :: 3.8
15
+ Classifier: Programming Language :: Python :: 3.9
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: Programming Language :: Python :: 3.14
12
21
  Classifier: Operating System :: OS Independent
13
22
  Classifier: Operating System :: Microsoft :: Windows
14
23
  Classifier: Operating System :: POSIX :: Linux
@@ -29,38 +38,61 @@ Dynamic: license-file
29
38
 
30
39
  # spotify_profile_monitor
31
40
 
32
- OSINT tool for real-time monitoring of **Spotify users' activities and profile changes including playlists**.
41
+ <p align="left">
42
+ <img src="https://img.shields.io/github/v/release/misiektoja/spotify_profile_monitor?style=flat-square&color=blue" alt="GitHub Release" />
43
+ <img src="https://img.shields.io/pypi/v/spotify_profile_monitor?style=flat-square&color=teal" alt="PyPI Version" />
44
+ <img src="https://img.shields.io/github/stars/misiektoja/spotify_profile_monitor?style=flat-square&color=magenta" alt="GitHub Stars" />
45
+ <img src="https://img.shields.io/badge/python-3.6+-blueviolet?style=flat-square" alt="Python Versions" />
46
+ <img src="https://img.shields.io/github/license/misiektoja/spotify_profile_monitor?style=flat-square&color=blue" alt="License" />
47
+ <img src="https://img.shields.io/github/last-commit/misiektoja/spotify_profile_monitor?style=flat-square&color=green" alt="Last Commit" />
48
+ <img src="https://img.shields.io/badge/maintenance-active-brightgreen?style=flat-square" alt="Maintenance" />
49
+ </p>
33
50
 
34
- If you want to track Spotify friends' music activity, check out another tool I developed: [spotify_monitor](https://github.com/misiektoja/spotify_monitor).
51
+ Powerful Spotify tool for real-time tracking of profile changes, playlist updates, follower growth, collaborators and more - delivered straight to your terminal or inbox.
35
52
 
36
- 🛠️ If you're looking for debug tools to get Spotify Web Player access tokens and extract secret keys: [click here](#debugging-tools)
53
+ ### 🚀 Quick Install
54
+ ```sh
55
+ pip install spotify_profile_monitor
56
+ ```
57
+
58
+ <p align="center">
59
+ <img src="https://raw.githubusercontent.com/misiektoja/spotify_profile_monitor/refs/heads/main/assets/spotify_profile_monitor.png" alt="spotify_profile_monitor_screenshot" width="90%"/>
60
+ </p>
37
61
 
38
62
  <a id="features"></a>
39
63
  ## Features
40
64
 
41
- - **Real-time tracking** of Spotify user activities and profile changes:
42
- - addition/removal of **followings and followers**
43
- - addition/removal of **playlists**
44
- - addition/removal of **tracks in playlists** (including collaborator info for newly added tracks)
45
- - **playlists name and description** changes
46
- - **number of likes** for playlists
47
- - **number of collaborators** for playlists
48
- - **profile picture** changes
49
- - **username** changes
50
- - **Email notifications** for various events (as listed above)
51
- - **Attaching changed profile pictures** directly to email notifications
52
- - **Displaying the profile picture** right in your terminal (if you have `imgcat` installed)
53
- - Additional functionalities on top of the monitoring mode allowing to display **detailed info about the user**, **list of followers & followings**, **recently played artists** and possibility to **search for users** in Spotify catalog with specific names
54
- - Ability to **display and export** the list of tracks for a specific playlist (including **Liked Songs** for the user who owns the Spotify access token)
55
- - **Saving all profile changes** (including playlists) with timestamps to the **CSV file**
56
- - **Clickable** **Spotify**, **Apple Music**, **YouTube Music**, **Amazon Music**, **Deezer**, **Tidal**, **Genius Lyrics**, **AZLyrics**, **Tekstowo.pl**, **Musixmatch** and **Lyrics.com** search URLs printed in the console and included in email notifications (configurable per service)
57
- - Support for **hybrid authentication approach** to get a **Spotify access token** (`sp_dc cookie`/`desktop client` + `OAuth app`, or standalone `OAuth app`/`OAuth user`)
58
- - Possibility to **control the running copy** of the script via signals
59
- - **Functional, procedural Python** (minimal OOP)
65
+ ### 👤 Profile Monitoring
66
+ - **Real-time tracking**: Monitor Spotify user activities and profile changes.
67
+ - **Social Network**: Detect addition/removal of **followings** and **followers**.
68
+ - **Identity Changes**: Track **profile picture** and **username** changes.
69
+
70
+ ### 📜 Playlist Tracking
71
+ - **Content Updates**: Monitor addition/removal of **tracks in playlists**.
72
+ - **Collaborator Info**: Identify who added which track in **collaborative playlists**.
73
+ - **Social Proof**: Monitor **likes** and **collaborators** count for playlists.
74
+ - **Metadata**: Track **name** and **description** changes.
75
+
76
+ ### 📊 Advanced Tools
77
+ - **Deep Insights**: Display detailed info about users, followers and followings.
78
+ - **Historical Data**: View **recently played artists** and **search for users** by name.
79
+ - **Export Power**: Display and export tracks from any playlist (including **Liked Songs**).
80
+ - **Global Search**: Instant links to **Spotify, YouTube Music, Apple Music, Tidal, lyrics** and more.
81
+
82
+ ### 🔔 Smart Interactions
83
+ - **Instant Alerts**: Detailed **Email notifications** for all profile and playlist changes.
84
+ - **Visual Reports**: Attach changed profile pictures directly to emails.
85
+ - **Terminal Graphics**: Display profile pictures right in your terminal (via `imgcat`).
86
+
87
+ ### ⚙️ Power Features
88
+ - **Auth Flexibility**: Hybrid support for `sp_dc` cookie, Desktop Client and OAuth.
89
+ - **CSV Logging**: Save all changes with full timestamps to a CSV file.
90
+ - **Flexible Config**: Support for files, dotenv and environment variables.
91
+ - **Signal Control**: Manage the running script via system signals (`SIGHUP`, `USR1`, etc.).
60
92
 
61
- <p align="center">
62
- <img src="https://raw.githubusercontent.com/misiektoja/spotify_profile_monitor/refs/heads/main/assets/spotify_profile_monitor.png" alt="spotify_profile_monitor_screenshot" width="100%"/>
63
- </p>
93
+ If you want to track Spotify friends' music activity, check out another tool I developed: [spotify_monitor](https://github.com/misiektoja/spotify_monitor).
94
+
95
+ 🛠️ If you're looking for debug tools to get Spotify Web Player access tokens and extract secret keys: [click here](#debugging-tools)
64
96
 
65
97
  <a id="table-of-contents"></a>
66
98
  ## Table of Contents
@@ -185,10 +217,15 @@ Most settings can be configured via command-line arguments.
185
217
  If you want to have it stored persistently, generate a default config template and save it to a file named `spotify_profile_monitor.conf`:
186
218
 
187
219
  ```sh
220
+ # On macOS, Linux or Windows Command Prompt (cmd.exe)
188
221
  spotify_profile_monitor --generate-config > spotify_profile_monitor.conf
189
222
 
223
+ # On Windows PowerShell (recommended to avoid encoding issues)
224
+ spotify_profile_monitor --generate-config spotify_profile_monitor.conf
190
225
  ```
191
226
 
227
+ > **IMPORTANT**: On **Windows PowerShell**, using redirection (`>`) can cause the file to be encoded in UTF-16, which will lead to "null bytes" errors when running the tool. It is highly recommended to provide the filename directly as an argument to `--generate-config` to ensure UTF-8 encoding.
228
+
192
229
  Edit the `spotify_profile_monitor.conf` file and change any desired configuration options (detailed comments are provided for each).
193
230
 
194
231
  **New in v3.1:** The tool now uses a hybrid authentication approach. OAuth app credentials (`SP_APP_CLIENT_ID`, `SP_APP_CLIENT_SECRET`) are required for playlist and user information retrieval when using either `cookie` or `client` token source methods. See [Spotify OAuth App](#spotify-oauth-app) section for setup instructions.
@@ -1,66 +1,60 @@
1
- Metadata-Version: 2.4
2
- Name: spotify_profile_monitor
3
- Version: 3.1
4
- Summary: Tool implementing real-time tracking of Spotify users activities and profile changes including playlists
5
- Author-email: Michal Szymanski <misiektoja-pypi@rm-rf.ninja>
6
- License-Expression: GPL-3.0-or-later
7
- Project-URL: Homepage, https://github.com/misiektoja/spotify_profile_monitor
8
- Project-URL: Source, https://github.com/misiektoja/spotify_profile_monitor
9
- Project-URL: Changelog, https://github.com/misiektoja/spotify_profile_monitor/blob/main/RELEASE_NOTES.md
10
- Keywords: spotify,monitoring,tracking,real-time,playlists,osint
11
- Classifier: Programming Language :: Python :: 3
12
- Classifier: Operating System :: OS Independent
13
- Classifier: Operating System :: Microsoft :: Windows
14
- Classifier: Operating System :: POSIX :: Linux
15
- Classifier: Operating System :: MacOS :: MacOS X
16
- Requires-Python: >=3.6
17
- Description-Content-Type: text/markdown
18
- License-File: LICENSE
19
- Requires-Dist: requests>=2.0
20
- Requires-Dist: python-dateutil>=2.8
21
- Requires-Dist: urllib3>=2.0.7
22
- Requires-Dist: pyotp>=2.9.0
23
- Requires-Dist: pytz>=2020.1
24
- Requires-Dist: tzlocal>=4.0
25
- Requires-Dist: python-dotenv>=0.19
26
- Requires-Dist: spotipy>=2.24.0
27
- Requires-Dist: wcwidth>=0.2.7
28
- Dynamic: license-file
29
-
30
1
  # spotify_profile_monitor
31
2
 
32
- OSINT tool for real-time monitoring of **Spotify users' activities and profile changes including playlists**.
3
+ <p align="left">
4
+ <img src="https://img.shields.io/github/v/release/misiektoja/spotify_profile_monitor?style=flat-square&color=blue" alt="GitHub Release" />
5
+ <img src="https://img.shields.io/pypi/v/spotify_profile_monitor?style=flat-square&color=teal" alt="PyPI Version" />
6
+ <img src="https://img.shields.io/github/stars/misiektoja/spotify_profile_monitor?style=flat-square&color=magenta" alt="GitHub Stars" />
7
+ <img src="https://img.shields.io/badge/python-3.6+-blueviolet?style=flat-square" alt="Python Versions" />
8
+ <img src="https://img.shields.io/github/license/misiektoja/spotify_profile_monitor?style=flat-square&color=blue" alt="License" />
9
+ <img src="https://img.shields.io/github/last-commit/misiektoja/spotify_profile_monitor?style=flat-square&color=green" alt="Last Commit" />
10
+ <img src="https://img.shields.io/badge/maintenance-active-brightgreen?style=flat-square" alt="Maintenance" />
11
+ </p>
33
12
 
34
- If you want to track Spotify friends' music activity, check out another tool I developed: [spotify_monitor](https://github.com/misiektoja/spotify_monitor).
13
+ Powerful Spotify tool for real-time tracking of profile changes, playlist updates, follower growth, collaborators and more - delivered straight to your terminal or inbox.
35
14
 
36
- 🛠️ If you're looking for debug tools to get Spotify Web Player access tokens and extract secret keys: [click here](#debugging-tools)
15
+ ### 🚀 Quick Install
16
+ ```sh
17
+ pip install spotify_profile_monitor
18
+ ```
19
+
20
+ <p align="center">
21
+ <img src="https://raw.githubusercontent.com/misiektoja/spotify_profile_monitor/refs/heads/main/assets/spotify_profile_monitor.png" alt="spotify_profile_monitor_screenshot" width="90%"/>
22
+ </p>
37
23
 
38
24
  <a id="features"></a>
39
25
  ## Features
40
26
 
41
- - **Real-time tracking** of Spotify user activities and profile changes:
42
- - addition/removal of **followings and followers**
43
- - addition/removal of **playlists**
44
- - addition/removal of **tracks in playlists** (including collaborator info for newly added tracks)
45
- - **playlists name and description** changes
46
- - **number of likes** for playlists
47
- - **number of collaborators** for playlists
48
- - **profile picture** changes
49
- - **username** changes
50
- - **Email notifications** for various events (as listed above)
51
- - **Attaching changed profile pictures** directly to email notifications
52
- - **Displaying the profile picture** right in your terminal (if you have `imgcat` installed)
53
- - Additional functionalities on top of the monitoring mode allowing to display **detailed info about the user**, **list of followers & followings**, **recently played artists** and possibility to **search for users** in Spotify catalog with specific names
54
- - Ability to **display and export** the list of tracks for a specific playlist (including **Liked Songs** for the user who owns the Spotify access token)
55
- - **Saving all profile changes** (including playlists) with timestamps to the **CSV file**
56
- - **Clickable** **Spotify**, **Apple Music**, **YouTube Music**, **Amazon Music**, **Deezer**, **Tidal**, **Genius Lyrics**, **AZLyrics**, **Tekstowo.pl**, **Musixmatch** and **Lyrics.com** search URLs printed in the console and included in email notifications (configurable per service)
57
- - Support for **hybrid authentication approach** to get a **Spotify access token** (`sp_dc cookie`/`desktop client` + `OAuth app`, or standalone `OAuth app`/`OAuth user`)
58
- - Possibility to **control the running copy** of the script via signals
59
- - **Functional, procedural Python** (minimal OOP)
27
+ ### 👤 Profile Monitoring
28
+ - **Real-time tracking**: Monitor Spotify user activities and profile changes.
29
+ - **Social Network**: Detect addition/removal of **followings** and **followers**.
30
+ - **Identity Changes**: Track **profile picture** and **username** changes.
31
+
32
+ ### 📜 Playlist Tracking
33
+ - **Content Updates**: Monitor addition/removal of **tracks in playlists**.
34
+ - **Collaborator Info**: Identify who added which track in **collaborative playlists**.
35
+ - **Social Proof**: Monitor **likes** and **collaborators** count for playlists.
36
+ - **Metadata**: Track **name** and **description** changes.
37
+
38
+ ### 📊 Advanced Tools
39
+ - **Deep Insights**: Display detailed info about users, followers and followings.
40
+ - **Historical Data**: View **recently played artists** and **search for users** by name.
41
+ - **Export Power**: Display and export tracks from any playlist (including **Liked Songs**).
42
+ - **Global Search**: Instant links to **Spotify, YouTube Music, Apple Music, Tidal, lyrics** and more.
43
+
44
+ ### 🔔 Smart Interactions
45
+ - **Instant Alerts**: Detailed **Email notifications** for all profile and playlist changes.
46
+ - **Visual Reports**: Attach changed profile pictures directly to emails.
47
+ - **Terminal Graphics**: Display profile pictures right in your terminal (via `imgcat`).
48
+
49
+ ### ⚙️ Power Features
50
+ - **Auth Flexibility**: Hybrid support for `sp_dc` cookie, Desktop Client and OAuth.
51
+ - **CSV Logging**: Save all changes with full timestamps to a CSV file.
52
+ - **Flexible Config**: Support for files, dotenv and environment variables.
53
+ - **Signal Control**: Manage the running script via system signals (`SIGHUP`, `USR1`, etc.).
60
54
 
61
- <p align="center">
62
- <img src="https://raw.githubusercontent.com/misiektoja/spotify_profile_monitor/refs/heads/main/assets/spotify_profile_monitor.png" alt="spotify_profile_monitor_screenshot" width="100%"/>
63
- </p>
55
+ If you want to track Spotify friends' music activity, check out another tool I developed: [spotify_monitor](https://github.com/misiektoja/spotify_monitor).
56
+
57
+ 🛠️ If you're looking for debug tools to get Spotify Web Player access tokens and extract secret keys: [click here](#debugging-tools)
64
58
 
65
59
  <a id="table-of-contents"></a>
66
60
  ## Table of Contents
@@ -185,10 +179,15 @@ Most settings can be configured via command-line arguments.
185
179
  If you want to have it stored persistently, generate a default config template and save it to a file named `spotify_profile_monitor.conf`:
186
180
 
187
181
  ```sh
182
+ # On macOS, Linux or Windows Command Prompt (cmd.exe)
188
183
  spotify_profile_monitor --generate-config > spotify_profile_monitor.conf
189
184
 
185
+ # On Windows PowerShell (recommended to avoid encoding issues)
186
+ spotify_profile_monitor --generate-config spotify_profile_monitor.conf
190
187
  ```
191
188
 
189
+ > **IMPORTANT**: On **Windows PowerShell**, using redirection (`>`) can cause the file to be encoded in UTF-16, which will lead to "null bytes" errors when running the tool. It is highly recommended to provide the filename directly as an argument to `--generate-config` to ensure UTF-8 encoding.
190
+
192
191
  Edit the `spotify_profile_monitor.conf` file and change any desired configuration options (detailed comments are provided for each).
193
192
 
194
193
  **New in v3.1:** The tool now uses a hybrid authentication approach. OAuth app credentials (`SP_APP_CLIENT_ID`, `SP_APP_CLIENT_SECRET`) are required for playlist and user information retrieval when using either `cookie` or `client` token source methods. See [Spotify OAuth App](#spotify-oauth-app) section for setup instructions.
@@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "spotify_profile_monitor"
7
- version = "3.1"
8
- description = "Tool implementing real-time tracking of Spotify users activities and profile changes including playlists"
7
+ version = "3.2"
8
+ description = "Track Spotify profile, follower and playlist changes in real time"
9
9
  readme = "README.md"
10
10
  license = "GPL-3.0-or-later"
11
11
  license-files = ["LICEN[CS]E*"]
@@ -24,6 +24,15 @@ dependencies = [
24
24
  ]
25
25
  classifiers = [
26
26
  "Programming Language :: Python :: 3",
27
+ "Programming Language :: Python :: 3.6",
28
+ "Programming Language :: Python :: 3.7",
29
+ "Programming Language :: Python :: 3.8",
30
+ "Programming Language :: Python :: 3.9",
31
+ "Programming Language :: Python :: 3.10",
32
+ "Programming Language :: Python :: 3.11",
33
+ "Programming Language :: Python :: 3.12",
34
+ "Programming Language :: Python :: 3.13",
35
+ "Programming Language :: Python :: 3.14",
27
36
  "Operating System :: OS Independent",
28
37
  "Operating System :: Microsoft :: Windows",
29
38
  "Operating System :: POSIX :: Linux",
@@ -1,37 +1,98 @@
1
+ Metadata-Version: 2.4
2
+ Name: spotify_profile_monitor
3
+ Version: 3.2
4
+ Summary: Track Spotify profile, follower and playlist changes in real time
5
+ Author-email: Michal Szymanski <misiektoja-pypi@rm-rf.ninja>
6
+ License-Expression: GPL-3.0-or-later
7
+ Project-URL: Homepage, https://github.com/misiektoja/spotify_profile_monitor
8
+ Project-URL: Source, https://github.com/misiektoja/spotify_profile_monitor
9
+ Project-URL: Changelog, https://github.com/misiektoja/spotify_profile_monitor/blob/main/RELEASE_NOTES.md
10
+ Keywords: spotify,monitoring,tracking,real-time,playlists,osint
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.6
13
+ Classifier: Programming Language :: Python :: 3.7
14
+ Classifier: Programming Language :: Python :: 3.8
15
+ Classifier: Programming Language :: Python :: 3.9
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: Programming Language :: Python :: 3.14
21
+ Classifier: Operating System :: OS Independent
22
+ Classifier: Operating System :: Microsoft :: Windows
23
+ Classifier: Operating System :: POSIX :: Linux
24
+ Classifier: Operating System :: MacOS :: MacOS X
25
+ Requires-Python: >=3.6
26
+ Description-Content-Type: text/markdown
27
+ License-File: LICENSE
28
+ Requires-Dist: requests>=2.0
29
+ Requires-Dist: python-dateutil>=2.8
30
+ Requires-Dist: urllib3>=2.0.7
31
+ Requires-Dist: pyotp>=2.9.0
32
+ Requires-Dist: pytz>=2020.1
33
+ Requires-Dist: tzlocal>=4.0
34
+ Requires-Dist: python-dotenv>=0.19
35
+ Requires-Dist: spotipy>=2.24.0
36
+ Requires-Dist: wcwidth>=0.2.7
37
+ Dynamic: license-file
38
+
1
39
  # spotify_profile_monitor
2
40
 
3
- OSINT tool for real-time monitoring of **Spotify users' activities and profile changes including playlists**.
41
+ <p align="left">
42
+ <img src="https://img.shields.io/github/v/release/misiektoja/spotify_profile_monitor?style=flat-square&color=blue" alt="GitHub Release" />
43
+ <img src="https://img.shields.io/pypi/v/spotify_profile_monitor?style=flat-square&color=teal" alt="PyPI Version" />
44
+ <img src="https://img.shields.io/github/stars/misiektoja/spotify_profile_monitor?style=flat-square&color=magenta" alt="GitHub Stars" />
45
+ <img src="https://img.shields.io/badge/python-3.6+-blueviolet?style=flat-square" alt="Python Versions" />
46
+ <img src="https://img.shields.io/github/license/misiektoja/spotify_profile_monitor?style=flat-square&color=blue" alt="License" />
47
+ <img src="https://img.shields.io/github/last-commit/misiektoja/spotify_profile_monitor?style=flat-square&color=green" alt="Last Commit" />
48
+ <img src="https://img.shields.io/badge/maintenance-active-brightgreen?style=flat-square" alt="Maintenance" />
49
+ </p>
4
50
 
5
- If you want to track Spotify friends' music activity, check out another tool I developed: [spotify_monitor](https://github.com/misiektoja/spotify_monitor).
51
+ Powerful Spotify tool for real-time tracking of profile changes, playlist updates, follower growth, collaborators and more - delivered straight to your terminal or inbox.
6
52
 
7
- 🛠️ If you're looking for debug tools to get Spotify Web Player access tokens and extract secret keys: [click here](#debugging-tools)
53
+ ### 🚀 Quick Install
54
+ ```sh
55
+ pip install spotify_profile_monitor
56
+ ```
57
+
58
+ <p align="center">
59
+ <img src="https://raw.githubusercontent.com/misiektoja/spotify_profile_monitor/refs/heads/main/assets/spotify_profile_monitor.png" alt="spotify_profile_monitor_screenshot" width="90%"/>
60
+ </p>
8
61
 
9
62
  <a id="features"></a>
10
63
  ## Features
11
64
 
12
- - **Real-time tracking** of Spotify user activities and profile changes:
13
- - addition/removal of **followings and followers**
14
- - addition/removal of **playlists**
15
- - addition/removal of **tracks in playlists** (including collaborator info for newly added tracks)
16
- - **playlists name and description** changes
17
- - **number of likes** for playlists
18
- - **number of collaborators** for playlists
19
- - **profile picture** changes
20
- - **username** changes
21
- - **Email notifications** for various events (as listed above)
22
- - **Attaching changed profile pictures** directly to email notifications
23
- - **Displaying the profile picture** right in your terminal (if you have `imgcat` installed)
24
- - Additional functionalities on top of the monitoring mode allowing to display **detailed info about the user**, **list of followers & followings**, **recently played artists** and possibility to **search for users** in Spotify catalog with specific names
25
- - Ability to **display and export** the list of tracks for a specific playlist (including **Liked Songs** for the user who owns the Spotify access token)
26
- - **Saving all profile changes** (including playlists) with timestamps to the **CSV file**
27
- - **Clickable** **Spotify**, **Apple Music**, **YouTube Music**, **Amazon Music**, **Deezer**, **Tidal**, **Genius Lyrics**, **AZLyrics**, **Tekstowo.pl**, **Musixmatch** and **Lyrics.com** search URLs printed in the console and included in email notifications (configurable per service)
28
- - Support for **hybrid authentication approach** to get a **Spotify access token** (`sp_dc cookie`/`desktop client` + `OAuth app`, or standalone `OAuth app`/`OAuth user`)
29
- - Possibility to **control the running copy** of the script via signals
30
- - **Functional, procedural Python** (minimal OOP)
65
+ ### 👤 Profile Monitoring
66
+ - **Real-time tracking**: Monitor Spotify user activities and profile changes.
67
+ - **Social Network**: Detect addition/removal of **followings** and **followers**.
68
+ - **Identity Changes**: Track **profile picture** and **username** changes.
69
+
70
+ ### 📜 Playlist Tracking
71
+ - **Content Updates**: Monitor addition/removal of **tracks in playlists**.
72
+ - **Collaborator Info**: Identify who added which track in **collaborative playlists**.
73
+ - **Social Proof**: Monitor **likes** and **collaborators** count for playlists.
74
+ - **Metadata**: Track **name** and **description** changes.
75
+
76
+ ### 📊 Advanced Tools
77
+ - **Deep Insights**: Display detailed info about users, followers and followings.
78
+ - **Historical Data**: View **recently played artists** and **search for users** by name.
79
+ - **Export Power**: Display and export tracks from any playlist (including **Liked Songs**).
80
+ - **Global Search**: Instant links to **Spotify, YouTube Music, Apple Music, Tidal, lyrics** and more.
81
+
82
+ ### 🔔 Smart Interactions
83
+ - **Instant Alerts**: Detailed **Email notifications** for all profile and playlist changes.
84
+ - **Visual Reports**: Attach changed profile pictures directly to emails.
85
+ - **Terminal Graphics**: Display profile pictures right in your terminal (via `imgcat`).
86
+
87
+ ### ⚙️ Power Features
88
+ - **Auth Flexibility**: Hybrid support for `sp_dc` cookie, Desktop Client and OAuth.
89
+ - **CSV Logging**: Save all changes with full timestamps to a CSV file.
90
+ - **Flexible Config**: Support for files, dotenv and environment variables.
91
+ - **Signal Control**: Manage the running script via system signals (`SIGHUP`, `USR1`, etc.).
31
92
 
32
- <p align="center">
33
- <img src="https://raw.githubusercontent.com/misiektoja/spotify_profile_monitor/refs/heads/main/assets/spotify_profile_monitor.png" alt="spotify_profile_monitor_screenshot" width="100%"/>
34
- </p>
93
+ If you want to track Spotify friends' music activity, check out another tool I developed: [spotify_monitor](https://github.com/misiektoja/spotify_monitor).
94
+
95
+ 🛠️ If you're looking for debug tools to get Spotify Web Player access tokens and extract secret keys: [click here](#debugging-tools)
35
96
 
36
97
  <a id="table-of-contents"></a>
37
98
  ## Table of Contents
@@ -156,10 +217,15 @@ Most settings can be configured via command-line arguments.
156
217
  If you want to have it stored persistently, generate a default config template and save it to a file named `spotify_profile_monitor.conf`:
157
218
 
158
219
  ```sh
220
+ # On macOS, Linux or Windows Command Prompt (cmd.exe)
159
221
  spotify_profile_monitor --generate-config > spotify_profile_monitor.conf
160
222
 
223
+ # On Windows PowerShell (recommended to avoid encoding issues)
224
+ spotify_profile_monitor --generate-config spotify_profile_monitor.conf
161
225
  ```
162
226
 
227
+ > **IMPORTANT**: On **Windows PowerShell**, using redirection (`>`) can cause the file to be encoded in UTF-16, which will lead to "null bytes" errors when running the tool. It is highly recommended to provide the filename directly as an argument to `--generate-config` to ensure UTF-8 encoding.
228
+
163
229
  Edit the `spotify_profile_monitor.conf` file and change any desired configuration options (detailed comments are provided for each).
164
230
 
165
231
  **New in v3.1:** The tool now uses a hybrid authentication approach. OAuth app credentials (`SP_APP_CLIENT_ID`, `SP_APP_CLIENT_SECRET`) are required for playlist and user information retrieval when using either `cookie` or `client` token source methods. See [Spotify OAuth App](#spotify-oauth-app) section for setup instructions.
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env python3
2
2
  """
3
3
  Author: Michal Szymanski <misiektoja-github@rm-rf.ninja>
4
- v3.1
4
+ v3.2
5
5
 
6
6
  OSINT tool implementing real-time tracking of Spotify users activities and profile changes including playlists:
7
7
  https://github.com/misiektoja/spotify_profile_monitor/
@@ -19,7 +19,7 @@ spotipy
19
19
  wcwidth (optional, needed by TRUNCATE_CHARS feature)
20
20
  """
21
21
 
22
- VERSION = "3.1"
22
+ VERSION = "3.2"
23
23
 
24
24
  # ---------------------------
25
25
  # CONFIGURATION SECTION START
@@ -192,6 +192,12 @@ RECENTLY_PLAYED_ARTISTS_LIMIT_INFO = 15
192
192
  # To avoid false alarms, we delay notifications until this happens PLAYLISTS_DISAPPEARED_COUNTER times in a row
193
193
  PLAYLISTS_DISAPPEARED_COUNTER = 3
194
194
 
195
+ # Occasionally, the Spotify API glitches and returns incomplete/empty playlists list
196
+ # (e.g. network issues, API transient failures or playlists temporarily not visible)
197
+ # To avoid false alarms, we delay playlist change notifications until the same change is seen
198
+ # PLAYLISTS_CHANGE_COUNTER times in a row (set to 0 to disable this protection)
199
+ PLAYLISTS_CHANGE_COUNTER = 2
200
+
195
201
  # Occasionally, the Spotify API glitches and returns an empty list of user followers / followings
196
202
  # To avoid false alarms, we delay notifications until this happens FOLLOWERS_FOLLOWINGS_DISAPPEARED_COUNTER times in a row
197
203
  FOLLOWERS_FOLLOWINGS_DISAPPEARED_COUNTER = 3
@@ -331,7 +337,6 @@ SECRET_CIPHER_DICT = {
331
337
  # Remote or local URL used to fetch updated secrets needed for TOTP generation
332
338
  # Set to empty string to disable
333
339
  # If you used "spotify_monitor_secret_grabber.py --secretdict > secretDict.json" specify the file location below
334
- # SECRET_CIPHER_DICT_URL = "https://github.com/Thereallo1026/spotify-secrets/blob/main/secrets/secretDict.json?raw=true"
335
340
  SECRET_CIPHER_DICT_URL = "https://github.com/xyloflake/spot-secrets-go/blob/main/secrets/secretDict.json?raw=true"
336
341
  # SECRET_CIPHER_DICT_URL = file:///C:/your_path/secretDict.json
337
342
  # SECRET_CIPHER_DICT_URL = "file:///your_path/secretDict.json"
@@ -564,6 +569,7 @@ RECENTLY_PLAYED_ARTISTS_LIMIT_INFO = 0
564
569
  PLAYLISTS_DISAPPEARED_COUNTER = 0
565
570
  FOLLOWERS_FOLLOWINGS_DISAPPEARED_COUNTER = 0
566
571
  COLLABORATORS_CHANGE_COUNTER = 0
572
+ PLAYLISTS_CHANGE_COUNTER = 0
567
573
  USER_AGENT = ""
568
574
  LIVENESS_CHECK_INTERVAL = 0
569
575
  CHECK_INTERNET_URL = ""
@@ -648,6 +654,10 @@ GLITCH_CACHE = {}
648
654
  COLLABORATORS_BASELINE_CACHE = {}
649
655
  COLLABORATORS_PENDING_CACHE = {}
650
656
 
657
+ # Tracks transient playlists glitches to suppress false alerts
658
+ PLAYLISTS_BASELINE_CACHE = {}
659
+ PLAYLISTS_PENDING_CACHE = {}
660
+
651
661
  LIVENESS_CHECK_COUNTER = LIVENESS_CHECK_INTERVAL / SPOTIFY_CHECK_INTERVAL
652
662
 
653
663
  stdout_bck = None
@@ -774,7 +784,8 @@ class Logger(object):
774
784
  self.logfile = open(filename, "a", buffering=1, encoding="utf-8")
775
785
 
776
786
  def write(self, message):
777
- self.logfile.write(message)
787
+ # Expand tabs for file output (stdout remains untouched)
788
+ self.logfile.write(message.expandtabs(8))
778
789
  if (TRUNCATE_CHARS):
779
790
  message = truncate_string_per_line(message, TRUNCATE_CHARS)
780
791
  self.terminal.write(message)
@@ -1691,6 +1702,8 @@ def fetch_and_update_secrets():
1691
1702
  path = os.path.expanduser(os.path.expandvars(raw_path))
1692
1703
 
1693
1704
  print(f"Loading Spotify web-player TOTP secrets from file: {path}")
1705
+ if os.path.getsize(path) == 0:
1706
+ raise ValueError(f"Secret file is empty: {path}")
1694
1707
  with open(path, "r", encoding="utf-8") as f:
1695
1708
  secrets = json.load(f)
1696
1709
  print("─" * HORIZONTAL_LINE)
@@ -1698,21 +1711,26 @@ def fetch_and_update_secrets():
1698
1711
  print(f"Fetching Spotify web-player TOTP secrets from URL: {SECRET_CIPHER_DICT_URL}")
1699
1712
  response = req.get(SECRET_CIPHER_DICT_URL, timeout=FUNCTION_TIMEOUT, verify=VERIFY_SSL)
1700
1713
  response.raise_for_status()
1714
+ if not response.text.strip():
1715
+ raise ValueError("Fetched payload is empty")
1701
1716
  secrets = response.json()
1702
1717
  print("─" * HORIZONTAL_LINE)
1703
1718
 
1704
1719
  if not isinstance(secrets, dict) or not secrets:
1705
- raise ValueError("fetch_and_update_secrets(): Fetched payload not a non-empty dict")
1720
+ raise ValueError("Fetched payload not a non-empty dict")
1706
1721
 
1707
1722
  for key, value in secrets.items():
1708
1723
  if not isinstance(key, str) or not key.isdigit():
1709
- raise ValueError(f"fetch_and_update_secrets(): Invalid key format: {key}")
1724
+ raise ValueError(f"Invalid key format: {key}")
1710
1725
  if not isinstance(value, list) or not all(isinstance(x, int) for x in value):
1711
- raise ValueError(f"fetch_and_update_secrets(): Invalid value format for key {key}")
1726
+ raise ValueError(f"Invalid value format for key {key}")
1712
1727
 
1713
1728
  SECRET_CIPHER_DICT = secrets
1714
1729
  return True
1715
1730
 
1731
+ except json.JSONDecodeError as e:
1732
+ print(f"fetch_and_update_secrets(): Failed to parse secrets (invalid JSON format): {e}")
1733
+ return False
1716
1734
  except Exception as e:
1717
1735
  print(f"fetch_and_update_secrets(): Failed to get new secrets: {e}")
1718
1736
  return False
@@ -5301,6 +5319,13 @@ def spotify_profile_monitor_uri(user_uri_id, csv_file_name, playlists_to_skip):
5301
5319
  else:
5302
5320
  # No change vs stable baseline; clear any pending candidate
5303
5321
  if p_uri in COLLABORATORS_PENDING_CACHE:
5322
+ # If we had a pending change and we're back to stable baseline, this was a transient glitch that resolved - suppress notification
5323
+ suppress_collab_notification = True
5324
+ # Update the old values to match current stable baseline so the notification condition check fails
5325
+ p_collaborators_old = len(stable_ids)
5326
+ p_collaborators_list_old = stable_map
5327
+ p_collaborators = len(current_ids)
5328
+ p_collaborators_list = (p_collaborators_list or {}) if isinstance(p_collaborators_list, dict) else {}
5304
5329
  try:
5305
5330
  del COLLABORATORS_PENDING_CACHE[p_uri]
5306
5331
  except Exception:
@@ -5622,11 +5647,97 @@ def spotify_profile_monitor_uri(user_uri_id, csv_file_name, playlists_to_skip):
5622
5647
  if not error_while_processing:
5623
5648
  list_of_playlists_old = list_of_playlists
5624
5649
 
5625
- if playlists_count != playlists_old_count:
5650
+ # Suppress transient playlist glitches by confirming changes across multiple checks and keep a stable
5651
+ # baseline to avoid baseline poisoning
5652
+ global PLAYLISTS_BASELINE_CACHE
5653
+ global PLAYLISTS_PENDING_CACHE
5654
+
5655
+ # Helper to extract URI strings from playlist dict list for comparison
5656
+ def extract_playlist_uris(playlist_list):
5657
+ if not playlist_list:
5658
+ return frozenset()
5659
+ return frozenset(
5660
+ p.get("uri") if isinstance(p, dict) else p
5661
+ for p in playlist_list
5662
+ if p
5663
+ )
5664
+
5665
+ user_playlists_key = f"user:{user_uri_id}"
5666
+ stable_entry = PLAYLISTS_BASELINE_CACHE.get(user_playlists_key)
5667
+ if stable_entry is None:
5668
+ # Initialize baseline from previously persisted playlist snapshot (if available)
5669
+ stable_uris = extract_playlist_uris(playlists_old)
5670
+ stable_count = playlists_old_count
5671
+ # Store both the URI set (for comparison) and the full list (for restoration)
5672
+ PLAYLISTS_BASELINE_CACHE[user_playlists_key] = {"uris": stable_uris, "count": stable_count, "playlist_list": list(playlists_old) if playlists_old else []}
5673
+ stable_entry = PLAYLISTS_BASELINE_CACHE[user_playlists_key]
5674
+
5675
+ stable_uris = stable_entry.get("uris") or frozenset()
5676
+ stable_count = stable_entry.get("count", 0)
5677
+ stable_playlist_list = stable_entry.get("playlist_list") or []
5678
+ current_uris = extract_playlist_uris(playlists)
5679
+ suppress_playlists_notification = False
5680
+
5681
+ if current_uris != stable_uris:
5682
+ # Playlists have changed vs stable baseline
5683
+ # Skip PLAYLISTS_CHANGE_COUNTER protection when dropping to 0 - let PLAYLISTS_DISAPPEARED_COUNTER handle that case
5684
+ dropping_to_zero = len(current_uris) == 0
5685
+
5686
+ if not dropping_to_zero:
5687
+ pending = PLAYLISTS_PENDING_CACHE.get(user_playlists_key)
5688
+ if pending and pending.get("new_uris") == current_uris:
5689
+ pending["streak"] = int(pending.get("streak", 0)) + 1
5690
+ else:
5691
+ pending = {"new_uris": current_uris, "new_count": len(current_uris), "streak": 1, "first_seen_ts": time.time(), "playlist_list": list(playlists) if playlists else []}
5692
+ PLAYLISTS_PENDING_CACHE[user_playlists_key] = pending
5693
+
5694
+ if PLAYLISTS_CHANGE_COUNTER and int(pending.get("streak", 0)) < int(PLAYLISTS_CHANGE_COUNTER):
5695
+ print(f"* Spotify API: suspected transient playlist change for user '{username}' ({stable_count} -> {len(current_uris)}), streak {pending.get('streak')}/{PLAYLISTS_CHANGE_COUNTER}; will confirm next check\n")
5696
+ print(f"Check interval:\t\t\t{display_time(SPOTIFY_CHECK_INTERVAL)} ({get_range_of_dates_from_tss(int(time.time()) - SPOTIFY_CHECK_INTERVAL, int(time.time()), short=True)})")
5697
+ print_cur_ts("Timestamp:\t\t\t")
5698
+ suppress_playlists_notification = True
5699
+ else:
5700
+ # Change confirmed - update variables for notification
5701
+ # Restore playlists_old from the stored dict list, not from URI strings
5702
+ playlists_old = stable_playlist_list
5703
+ playlists_old_count = stable_count
5704
+ playlists_count = len(current_uris)
5705
+ # playlists already contains current dict list, no change needed
5706
+
5707
+ # Update stable baseline and clear pending
5708
+ PLAYLISTS_BASELINE_CACHE[user_playlists_key] = {
5709
+ "uris": current_uris,
5710
+ "count": playlists_count,
5711
+ "playlist_list": list(playlists) if playlists else []
5712
+ }
5713
+ try:
5714
+ del PLAYLISTS_PENDING_CACHE[user_playlists_key]
5715
+ except Exception:
5716
+ pass
5717
+ # else: dropping to 0, let PLAYLISTS_DISAPPEARED_COUNTER handle it below
5718
+ else:
5719
+ # No change vs stable baseline; clear any pending candidate
5720
+ if user_playlists_key in PLAYLISTS_PENDING_CACHE:
5721
+ # If we had a pending change and we're back to stable baseline, this was a transient glitch that resolved - suppress notification
5722
+ suppress_playlists_notification = True
5723
+ # Update the old values to match current stable baseline so the notification condition check fails
5724
+ playlists_old_count = stable_count
5725
+ playlists_old = stable_playlist_list
5726
+ playlists_count = len(current_uris)
5727
+ # playlists already contains current dict list, no change needed
5728
+ print(f"* Spotify API: Playlists for user '{username}' reverted to baseline ({stable_count}) after transient glitch; suppressing notification\n")
5729
+ print(f"Check interval:\t\t\t{display_time(SPOTIFY_CHECK_INTERVAL)} ({get_range_of_dates_from_tss(int(time.time()) - SPOTIFY_CHECK_INTERVAL, int(time.time()), short=True)})")
5730
+ print_cur_ts("Timestamp:\t\t\t")
5731
+ try:
5732
+ del PLAYLISTS_PENDING_CACHE[user_playlists_key]
5733
+ except Exception:
5734
+ pass
5735
+
5736
+ if not suppress_playlists_notification and playlists_count != playlists_old_count:
5626
5737
  if playlists_count == 0:
5627
5738
  playlists_zeroed_counter += 1
5628
5739
  if playlists_zeroed_counter == PLAYLISTS_DISAPPEARED_COUNTER:
5629
- print(f"* Spotify API: Playlists count dropped from {playlists_old_count} to 0 and has been 0 for {playlists_zeroed_counter} checks; accepting 0 as the new baseline")
5740
+ print(f"* Spotify API: Playlists count dropped from {playlists_old_count} to 0 and has been 0 for {playlists_zeroed_counter} checks; accepting 0 as the new baseline\n")
5630
5741
  spotify_print_changed_followers_followings_playlists(
5631
5742
  username, playlists, playlists_old, playlists_count, playlists_old_count, "Playlists", "for", "Added playlists to profile", "Added Playlist", "Removed playlists from profile", "Removed Playlist", playlists_file, csv_file_name, PROFILE_NOTIFICATION, True, sp_accessToken)
5632
5743
  print(f"Check interval:\t\t\t{display_time(SPOTIFY_CHECK_INTERVAL)} ({get_range_of_dates_from_tss(int(time.time()) - SPOTIFY_CHECK_INTERVAL, int(time.time()), short=True)})")
@@ -5634,13 +5745,15 @@ def spotify_profile_monitor_uri(user_uri_id, csv_file_name, playlists_to_skip):
5634
5745
  playlists_old_count = playlists_count
5635
5746
  playlists_old = playlists
5636
5747
  playlists_zeroed_counter = 0
5748
+ # Update baseline after accepting 0 as new baseline
5749
+ PLAYLISTS_BASELINE_CACHE[user_playlists_key] = {"uris": frozenset(), "count": 0, "playlist_list": []}
5637
5750
  elif playlists_zeroed_counter < PLAYLISTS_DISAPPEARED_COUNTER:
5638
- print(f"* Spotify API: Playlists count dropped from {playlists_old_count} to 0, streak {playlists_zeroed_counter}/{PLAYLISTS_DISAPPEARED_COUNTER}; old count and list retained")
5751
+ print(f"* Spotify API: Playlists count dropped from {playlists_old_count} to 0, streak {playlists_zeroed_counter}/{PLAYLISTS_DISAPPEARED_COUNTER}; old count and list retained\n")
5639
5752
  print(f"Check interval:\t\t\t{display_time(SPOTIFY_CHECK_INTERVAL)} ({get_range_of_dates_from_tss(int(time.time()) - SPOTIFY_CHECK_INTERVAL, int(time.time()), short=True)})")
5640
5753
  print_cur_ts("Timestamp:\t\t\t")
5641
5754
  else:
5642
5755
  if playlists_old_count == 0 and playlists_zeroed_counter >= PLAYLISTS_DISAPPEARED_COUNTER:
5643
- print(f"* Spotify API: Playlists count recovered to {playlists_count}; previously was 0 for {playlists_zeroed_counter} checks (old baseline was {playlists_old_count})")
5756
+ print(f"* Spotify API: Playlists count recovered to {playlists_count}; previously was 0 for {playlists_zeroed_counter} checks (old baseline was {playlists_old_count})\n")
5644
5757
 
5645
5758
  spotify_print_changed_followers_followings_playlists(username, playlists, playlists_old, playlists_count, playlists_old_count, "Playlists", "for", "Added playlists to profile", "Added Playlist", "Removed playlists from profile", "Removed Playlist", playlists_file, csv_file_name, PROFILE_NOTIFICATION, True, sp_accessToken)
5646
5759
  print(f"Check interval:\t\t\t{display_time(SPOTIFY_CHECK_INTERVAL)} ({get_range_of_dates_from_tss(int(time.time()) - SPOTIFY_CHECK_INTERVAL, int(time.time()), short=True)})")
@@ -5648,14 +5761,20 @@ def spotify_profile_monitor_uri(user_uri_id, csv_file_name, playlists_to_skip):
5648
5761
  playlists_old_count = playlists_count
5649
5762
  playlists_old = playlists
5650
5763
  playlists_zeroed_counter = 0
5764
+ # Update baseline after confirmed change
5765
+ PLAYLISTS_BASELINE_CACHE[user_playlists_key] = {
5766
+ "uris": extract_playlist_uris(playlists),
5767
+ "count": playlists_count,
5768
+ "playlist_list": list(playlists) if playlists else []
5769
+ }
5651
5770
 
5652
- elif playlists_count == playlists_old_count:
5771
+ elif not suppress_playlists_notification and playlists_count == playlists_old_count:
5653
5772
  if playlists_count == 0:
5654
5773
  playlists_zeroed_counter = 0
5655
5774
  playlists_old = playlists
5656
5775
  else:
5657
5776
  if playlists_zeroed_counter > 0:
5658
- print(f"* Spotify API: Playlists count recovered to {playlists_count} (matching old baseline) after a streak of {playlists_zeroed_counter} checks")
5777
+ print(f"* Spotify API: Playlists count recovered to {playlists_count} (matching old baseline) after a streak of {playlists_zeroed_counter} checks\n")
5659
5778
  print(f"Check interval:\t\t\t{display_time(SPOTIFY_CHECK_INTERVAL)} ({get_range_of_dates_from_tss(int(time.time()) - SPOTIFY_CHECK_INTERVAL, int(time.time()), short=True)})")
5660
5779
  print_cur_ts("Timestamp:\t\t\t")
5661
5780
  playlists_zeroed_counter = 0
@@ -5674,7 +5793,22 @@ def main():
5674
5793
  global CLI_CONFIG_PATH, DOTENV_FILE, LOCAL_TIMEZONE, LIVENESS_CHECK_COUNTER, SP_DC_COOKIE, SP_APP_CLIENT_ID, SP_APP_CLIENT_SECRET, SP_USER_CLIENT_ID, SP_USER_CLIENT_SECRET, LOGIN_REQUEST_BODY_FILE, CLIENTTOKEN_REQUEST_BODY_FILE, REFRESH_TOKEN, LOGIN_URL, USER_AGENT, DEVICE_ID, SYSTEM_ID, USER_URI_ID, CSV_FILE, PLAYLISTS_TO_SKIP_FILE, FILE_SUFFIX, DISABLE_LOGGING, SP_LOGFILE, PROFILE_NOTIFICATION, SPOTIFY_CHECK_INTERVAL, SPOTIFY_ERROR_INTERVAL, FOLLOWERS_FOLLOWINGS_NOTIFICATION, ERROR_NOTIFICATION, DETECT_CHANGED_PROFILE_PIC, DETECT_CHANGES_IN_PLAYLISTS, GET_ALL_PLAYLISTS, imgcat_exe, SMTP_PASSWORD, SP_SHA256, stdout_bck, APP_VERSION, CPU_ARCH, OS_BUILD, PLATFORM, OS_MAJOR, OS_MINOR, CLIENT_MODEL, TOKEN_SOURCE, ALARM_TIMEOUT, pyotp, CLEAN_OUTPUT, USER_AGENT, SP_APP_TOKENS_FILE, SP_USER_TOKENS_FILE, TRUNCATE_CHARS
5675
5794
 
5676
5795
  if "--generate-config" in sys.argv:
5677
- print(CONFIG_BLOCK.strip("\n"))
5796
+ config_content = CONFIG_BLOCK.strip("\n") + "\n"
5797
+ # Check if a filename was provided after --generate-config
5798
+ try:
5799
+ idx = sys.argv.index("--generate-config")
5800
+ if idx + 1 < len(sys.argv) and not sys.argv[idx + 1].startswith("-"):
5801
+ # Write directly to file (bypasses PowerShell UTF-16 encoding issue on Windows)
5802
+ output_file = sys.argv[idx + 1]
5803
+ with open(output_file, "w", encoding="utf-8") as f:
5804
+ f.write(config_content)
5805
+ print(f"Config written to: {output_file}")
5806
+ sys.exit(0)
5807
+ except (ValueError, IndexError):
5808
+ pass
5809
+ # No filename provided - write to stdout using buffer to ensure UTF-8
5810
+ sys.stdout.buffer.write(config_content.encode("utf-8"))
5811
+ sys.stdout.buffer.flush()
5678
5812
  sys.exit(0)
5679
5813
 
5680
5814
  if "--version" in sys.argv:
@@ -5715,8 +5849,11 @@ def main():
5715
5849
  )
5716
5850
  conf.add_argument(
5717
5851
  "--generate-config",
5718
- action="store_true",
5719
- help="Print default config template and exit",
5852
+ dest="generate_config",
5853
+ nargs="?",
5854
+ const=True,
5855
+ metavar="FILENAME",
5856
+ help="Print default config template and exit (on Windows PowerShell, specify a filename to avoid redirect encoding issues)",
5720
5857
  )
5721
5858
  conf.add_argument(
5722
5859
  "--env-file",