spotify-profile-monitor 2.7__tar.gz → 2.9__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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: spotify_profile_monitor
3
- Version: 2.7
3
+ Version: 2.9
4
4
  Summary: Tool implementing real-time tracking of Spotify users activities and profile changes including playlists
5
5
  Author-email: Michal Szymanski <misiektoja-pypi@rm-rf.ninja>
6
6
  License-Expression: GPL-3.0-or-later
@@ -38,24 +38,25 @@ OSINT tool for real-time monitoring of **Spotify users' activities and profile c
38
38
  <a id="features"></a>
39
39
  ## Features
40
40
 
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 and Genius Lyrics search URLs printed in the console & included in email notifications
57
- - Support for four different methods to get a Spotify access token (`sp_dc cookie`, `desktop client`, `OAuth app`, `OAuth user`)
58
- - Possibility to control the running copy of the script via signals
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 **four different methods to get a Spotify access token** (`sp_dc cookie`, `desktop client`, `OAuth app`, `OAuth user`)
58
+ - Possibility to **control the running copy** of the script via signals
59
+ - **Functional, procedural Python** (minimal OOP)
59
60
 
60
61
  <p align="center">
61
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%"/>
@@ -68,6 +69,7 @@ OSINT tool for real-time monitoring of **Spotify users' activities and profile c
68
69
  2. [Installation](#installation)
69
70
  * [Install from PyPI](#install-from-pypi)
70
71
  * [Manual Installation](#manual-installation)
72
+ * [Upgrading](#upgrading)
71
73
  3. [Quick Start](#quick-start)
72
74
  4. [Configuration](#configuration)
73
75
  * [Configuration File](#configuration-file)
@@ -96,7 +98,8 @@ OSINT tool for real-time monitoring of **Spotify users' activities and profile c
96
98
  * [Access Token Retrieval via sp_dc Cookie and TOTP](#access-token-retrieval-via-sp_dc-cookie-and-totp)
97
99
  * [Secret Key Extraction from Spotify Web Player Bundles](#secret-key-extraction-from-spotify-web-player-bundles)
98
100
  7. [Change Log](#change-log)
99
- 8. [License](#license)
101
+ 8. [Maintainers](#maintainers)
102
+ 9. [License](#license)
100
103
 
101
104
  <a id="requirements"></a>
102
105
  ## Requirements
@@ -106,8 +109,8 @@ OSINT tool for real-time monitoring of **Spotify users' activities and profile c
106
109
 
107
110
  Tested on:
108
111
 
109
- * **macOS**: Ventura, Sonoma, Sequoia
110
- * **Linux**: Raspberry Pi OS (Bullseye, Bookworm), Ubuntu 24, Rocky Linux 8.x/9.x, Kali Linux 2024/2025
112
+ * **macOS**: Ventura, Sonoma, Sequoia, Tahoe
113
+ * **Linux**: Raspberry Pi OS (Bullseye, Bookworm, Trixie), Ubuntu 24/25, Rocky Linux 8.x/9.x, Kali Linux 2024/2025
111
114
  * **Windows**: 10, 11
112
115
 
113
116
  It should work on other versions of macOS, Linux, Unix and Windows as well.
@@ -139,6 +142,17 @@ Alternatively, from the downloaded *[requirements.txt](https://raw.githubusercon
139
142
  pip install -r requirements.txt
140
143
  ```
141
144
 
145
+ <a id="upgrading"></a>
146
+ ### Upgrading
147
+
148
+ To upgrade to the latest version when installed from PyPI:
149
+
150
+ ```sh
151
+ pip install spotify_profile_monitor -U
152
+ ```
153
+
154
+ If you installed manually, download the newest *[spotify_profile_monitor.py](https://raw.githubusercontent.com/misiektoja/spotify_profile_monitor/refs/heads/main/spotify_profile_monitor.py)* file to replace your existing installation.
155
+
142
156
  <a id="quick-start"></a>
143
157
  ## Quick Start
144
158
 
@@ -178,6 +192,8 @@ spotify_profile_monitor --generate-config > spotify_profile_monitor.conf
178
192
 
179
193
  Edit the `spotify_profile_monitor.conf` file and change any desired configuration options (detailed comments are provided for each).
180
194
 
195
+ **New in v2.9:** The configuration file includes options to enable/disable music service URLs (Apple Music, YouTube Music, Amazon Music, Deezer, Tidal) and lyrics service URLs (Genius, AZLyrics, Tekstowo.pl, Musixmatch, Lyrics.com) in console and email outputs.
196
+
181
197
  <a id="spotify-access-token-source"></a>
182
198
  ### Spotify access token source
183
199
 
@@ -309,6 +325,8 @@ This method uses an official Spotify Web API (Client Credentials OAuth flow).
309
325
  - Create a new app
310
326
 
311
327
  - For **Redirect URL**, use: http://127.0.0.1:1234
328
+ - The URL must match exactly as shown, including not having a / at the end
329
+ - When copying the link via right-click, some browsers may add an extra / to the URL
312
330
 
313
331
  - Select **Web API** as the intended API
314
332
 
@@ -343,6 +361,8 @@ This method uses an official Spotify Web API (Authorization Code OAuth flow).
343
361
  - Create a new app
344
362
 
345
363
  - For **Redirect URL**, use: http://127.0.0.1:1234
364
+ - The URL must match exactly as shown, including not having a / at the end
365
+ - When copying the link via right-click, some browsers may add an extra / to the URL
346
366
 
347
367
  - Select **Web API** as the intended API
348
368
 
@@ -362,7 +382,7 @@ You can use the same client ID and secret values as those used for the [Spotify
362
382
  Example:
363
383
 
364
384
  ```sh
365
- spotify_profile_monitor --token-source oauth_user -r "your_spotify_user_client_id:your_spotify_user_client_secret" <spotify_user_uri_id>
385
+ spotify_profile_monitor --token-source oauth_user -n "your_spotify_user_client_id:your_spotify_user_client_secret" <spotify_user_uri_id>
366
386
  ```
367
387
 
368
388
  The tool takes care of refreshing the access token so it should remain valid indefinitely.
@@ -685,7 +705,7 @@ Make sure you defined your SMTP settings earlier (see [SMTP settings](#smtp-sett
685
705
  Example email:
686
706
 
687
707
  <p align="center">
688
- <img src="https://raw.githubusercontent.com/misiektoja/spotify_profile_monitor/refs/heads/main/assets/spotify_profile_monitor_email_notifications.png" alt="spotify_profile_monitor_email_notifications" width="80%"/>
708
+ <img src="https://raw.githubusercontent.com/misiektoja/spotify_profile_monitor/refs/heads/main/assets/spotify_profile_monitor_email_notifications.png" alt="spotify_profile_monitor_email_notifications" width="90%"/>
689
709
  </p>
690
710
 
691
711
  <a id="csv-export"></a>
@@ -853,13 +873,15 @@ You should get a valid Spotify access token, example output:
853
873
  <img src="https://raw.githubusercontent.com/misiektoja/spotify_monitor/refs/heads/main/assets/spotify_monitor_totp_test.png" alt="spotify_monitor_totp_test" width="100%"/>
854
874
  </p>
855
875
 
856
- > **NOTE:** secrets used for TOTP generation (`SECRET_CIPHER_DICT`) expire every two days; you can either run the [spotify_monitor_secret_grabber](https://github.com/misiektoja/spotify_monitor/blob/dev/debug/spotify_monitor_secret_grabber.py) and extract it by yourself (see [here](#secret-key-extraction-from-spotify-web-player-bundles) for more info) or you can pass `--fetch-secrets` flag in [spotify_monitor_totp_test](https://github.com/misiektoja/spotify_monitor/blob/dev/debug/spotify_monitor_totp_test.py) (available since v1.6). There is also a [Thereallo1026/spotify-secrets](https://github.com/Thereallo1026/spotify-secrets) repo which offers JSON files that are automatically updated with current secrets.
876
+ > **NOTE:** secrets used for TOTP generation (`SECRET_CIPHER_DICT`) expire every two days; you can either run the [spotify_monitor_secret_grabber](https://github.com/misiektoja/spotify_monitor/blob/dev/debug/spotify_monitor_secret_grabber.py) and extract it by yourself (see [here](#secret-key-extraction-from-spotify-web-player-bundles) for more info) or you can pass `--fetch-secrets` flag in `spotify_monitor_totp_test` (available since v1.6). There is also a [xyloflake/spot-secrets-go/](https://github.com/xyloflake/spot-secrets-go/) repo which offers JSON files that are automatically updated with current secrets (you can pass `--download-secrets` flag in `spotify_monitor_totp_test` to get it automatically from remote URL, available since v1.8).
857
877
 
858
878
  <a id="secret-key-extraction-from-spotify-web-player-bundles"></a>
859
879
  ### Secret Key Extraction from Spotify Web Player Bundles
860
880
 
861
881
  The [spotify_monitor_secret_grabber](https://github.com/misiektoja/spotify_monitor/blob/dev/debug/spotify_monitor_secret_grabber.py) tool automatically extracts secret keys used for TOTP generation in Spotify Web Player JavaScript bundles.
862
882
 
883
+ > 💡 **Quick tip:** The easiest and recommended way to run this tool is via Docker. Jump directly to the [Docker usage section below](#-secret-key-extraction-via-docker-recommended-easiest-way).
884
+
863
885
  Download from [here](https://github.com/misiektoja/spotify_monitor/blob/dev/debug/spotify_monitor_secret_grabber.py) or:
864
886
 
865
887
  ```sh
@@ -873,7 +895,7 @@ pip install playwright
873
895
  playwright install
874
896
  ```
875
897
 
876
- Run:
898
+ Run interactively (default output mode):
877
899
 
878
900
  ```sh
879
901
  python3 spotify_monitor_secret_grabber.py
@@ -885,15 +907,100 @@ You should get output similar to below:
885
907
  <img src="https://raw.githubusercontent.com/misiektoja/spotify_monitor/refs/heads/main/assets/spotify_monitor_secret_grabber.png" alt="spotify_monitor_secret_grabber" width="100%"/>
886
908
  </p>
887
909
 
888
- You can now update the secrets used for TOTP generation (for example `SECRET_CIPHER_DICT` in `spotify_monitor_totp_test`, `spotify_monitor` and `spotify_profile_monitor`).
910
+ Show help:
911
+ ```sh
912
+ python3 spotify_monitor_secret_grabber.py -h
913
+ ```
914
+
915
+ ---
916
+
917
+ <a id="cli-output-modes"></a>
918
+ ### CLI Output Modes
919
+
920
+ The script supports several output modes for different use cases:
921
+
922
+ | Flag | Description | Output |
923
+ |------|--------------|--------|
924
+ | `--secret` | Prints plain JSON array of extracted secrets | `[{"version": X, "secret": "..."}, ...]` |
925
+ | `--secretbytes` | Prints JSON array with ASCII byte values | `[{"version": X, "secret": [..]}, ...]` |
926
+ | `--secretdict` | Prints JSON object/dict mapping version → byte list | `{"X": [..], "Y": [..]}` |
927
+ | `--all` | Extracts secrets and **writes all three outputs** to local files | `secrets.json`, `secretBytes.json`, `secretDict.json` |
928
+
929
+ Print extracted secrets in specific format, for example Python-friendly secret bytes (JSON object/dict) and save to indicated file:
930
+
931
+ ```sh
932
+ python3 spotify_monitor_secret_grabber.py --secretdict > secretDict.json
933
+ ```
934
+
935
+ Or, to generate and save all secret formats to files (`secrets.json`, `secretBytes.json`, `secretDict.json`) at once:
936
+
937
+ ```sh
938
+ python3 spotify_monitor_secret_grabber.py --all
939
+ ```
940
+
941
+ Default file paths and names can be configured directly in the `OUTPUT_FILES` dictionary at the top of the script.
942
+
943
+ ---
944
+
945
+ <a id="-secret-key-extraction-via-docker-recommended-easiest-way"></a>
946
+ ### 🐳 Secret Key Extraction via Docker (Recommended Easiest Way)
889
947
 
890
- > **NOTE:** you can also use [Thereallo1026/spotify-secrets](https://github.com/Thereallo1026/spotify-secrets) repo which offers JSON files that are automatically updated with current secrets (its secret extraction code is based on `spotify_monitor_secret_grabber`).
948
+ A prebuilt multi-architecture image is available on Docker Hub: [`misiektoja/spotify-secrets-grabber`](https://hub.docker.com/r/misiektoja/spotify-secrets-grabber)
949
+
950
+ This image works on:
951
+ - macOS (Intel & Apple Silicon)
952
+ - Linux (x86_64 and ARM64)
953
+ - Windows (Docker Desktop / WSL2)
954
+ - Raspberry Pi 4/5 (64-bit OS)
955
+
956
+ Run interactively (default output mode):
957
+
958
+ ```sh
959
+ docker run --rm misiektoja/spotify-secrets-grabber
960
+ ```
961
+
962
+ Show help:
963
+ ```sh
964
+ docker run --rm misiektoja/spotify-secrets-grabber -h
965
+ ```
966
+
967
+ Print extracted secrets in specific format, for example Python-friendly secret bytes (JSON object/dict) and save to indicated file:
968
+ ```sh
969
+ docker run --rm misiektoja/spotify-secrets-grabber --secretdict > secretDict.json
970
+ ```
971
+
972
+ Or, to generate and save all secret formats to files (`secrets.json`, `secretBytes.json`, `secretDict.json`) at once:
973
+
974
+ ```sh
975
+ docker run --rm -v .:/work -w /work misiektoja/spotify-secrets-grabber --all
976
+ ```
977
+
978
+ *For SELinux hosts (Fedora/RHEL), use `-v .:/work:Z`.*
979
+
980
+ <a id="optional-use-docker-compose-one-command-for-all-oss"></a>
981
+ Or optionally use Docker Compose (a preconfigured [compose.yaml](https://github.com/misiektoja/spotify_monitor/blob/dev/debug/spotify_monitor_secret_grabber_docker/compose.yaml) file is included in the repo):
982
+
983
+ ```sh
984
+ docker compose run --rm spotify-secrets-grabber --all
985
+ ```
986
+
987
+ This will save all files into your current directory on any system (macOS, Linux or Windows).
988
+
989
+ ---
990
+
991
+ You can now update the secrets used for TOTP generation (for example `SECRET_CIPHER_DICT` in `spotify_monitor_totp_test`, `spotify_monitor` and `spotify_profile_monitor`) either manually or by referencing an external `secretDict.json` file, which can be hosted in another repo or stored locally. See the description of `SECRET_CIPHER_DICT_URL` in those files for details.
891
992
 
892
993
  <a id="change-log"></a>
893
994
  ## Change Log
894
995
 
895
996
  See [RELEASE_NOTES.md](https://github.com/misiektoja/spotify_profile_monitor/blob/main/RELEASE_NOTES.md) for details.
896
997
 
998
+ <a id="maintainers"></a>
999
+ ## Maintainers
1000
+
1001
+ [![Maintainer: misiektoja](https://img.shields.io/badge/maintainer-misiektoja-blue)](https://github.com/misiektoja)
1002
+ [![Maintainer: tomballgithub](https://img.shields.io/badge/maintainer-tomballgithub-blue)](https://github.com/tomballgithub)
1003
+
897
1004
  <a id="license"></a>
898
1005
  ## License
899
1006
 
@@ -9,24 +9,25 @@ OSINT tool for real-time monitoring of **Spotify users' activities and profile c
9
9
  <a id="features"></a>
10
10
  ## Features
11
11
 
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 and Genius Lyrics search URLs printed in the console & included in email notifications
28
- - Support for four different methods to get a Spotify access token (`sp_dc cookie`, `desktop client`, `OAuth app`, `OAuth user`)
29
- - Possibility to control the running copy of the script via signals
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 **four different methods to get a Spotify access token** (`sp_dc cookie`, `desktop client`, `OAuth app`, `OAuth user`)
29
+ - Possibility to **control the running copy** of the script via signals
30
+ - **Functional, procedural Python** (minimal OOP)
30
31
 
31
32
  <p align="center">
32
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%"/>
@@ -39,6 +40,7 @@ OSINT tool for real-time monitoring of **Spotify users' activities and profile c
39
40
  2. [Installation](#installation)
40
41
  * [Install from PyPI](#install-from-pypi)
41
42
  * [Manual Installation](#manual-installation)
43
+ * [Upgrading](#upgrading)
42
44
  3. [Quick Start](#quick-start)
43
45
  4. [Configuration](#configuration)
44
46
  * [Configuration File](#configuration-file)
@@ -67,7 +69,8 @@ OSINT tool for real-time monitoring of **Spotify users' activities and profile c
67
69
  * [Access Token Retrieval via sp_dc Cookie and TOTP](#access-token-retrieval-via-sp_dc-cookie-and-totp)
68
70
  * [Secret Key Extraction from Spotify Web Player Bundles](#secret-key-extraction-from-spotify-web-player-bundles)
69
71
  7. [Change Log](#change-log)
70
- 8. [License](#license)
72
+ 8. [Maintainers](#maintainers)
73
+ 9. [License](#license)
71
74
 
72
75
  <a id="requirements"></a>
73
76
  ## Requirements
@@ -77,8 +80,8 @@ OSINT tool for real-time monitoring of **Spotify users' activities and profile c
77
80
 
78
81
  Tested on:
79
82
 
80
- * **macOS**: Ventura, Sonoma, Sequoia
81
- * **Linux**: Raspberry Pi OS (Bullseye, Bookworm), Ubuntu 24, Rocky Linux 8.x/9.x, Kali Linux 2024/2025
83
+ * **macOS**: Ventura, Sonoma, Sequoia, Tahoe
84
+ * **Linux**: Raspberry Pi OS (Bullseye, Bookworm, Trixie), Ubuntu 24/25, Rocky Linux 8.x/9.x, Kali Linux 2024/2025
82
85
  * **Windows**: 10, 11
83
86
 
84
87
  It should work on other versions of macOS, Linux, Unix and Windows as well.
@@ -110,6 +113,17 @@ Alternatively, from the downloaded *[requirements.txt](https://raw.githubusercon
110
113
  pip install -r requirements.txt
111
114
  ```
112
115
 
116
+ <a id="upgrading"></a>
117
+ ### Upgrading
118
+
119
+ To upgrade to the latest version when installed from PyPI:
120
+
121
+ ```sh
122
+ pip install spotify_profile_monitor -U
123
+ ```
124
+
125
+ If you installed manually, download the newest *[spotify_profile_monitor.py](https://raw.githubusercontent.com/misiektoja/spotify_profile_monitor/refs/heads/main/spotify_profile_monitor.py)* file to replace your existing installation.
126
+
113
127
  <a id="quick-start"></a>
114
128
  ## Quick Start
115
129
 
@@ -149,6 +163,8 @@ spotify_profile_monitor --generate-config > spotify_profile_monitor.conf
149
163
 
150
164
  Edit the `spotify_profile_monitor.conf` file and change any desired configuration options (detailed comments are provided for each).
151
165
 
166
+ **New in v2.9:** The configuration file includes options to enable/disable music service URLs (Apple Music, YouTube Music, Amazon Music, Deezer, Tidal) and lyrics service URLs (Genius, AZLyrics, Tekstowo.pl, Musixmatch, Lyrics.com) in console and email outputs.
167
+
152
168
  <a id="spotify-access-token-source"></a>
153
169
  ### Spotify access token source
154
170
 
@@ -280,6 +296,8 @@ This method uses an official Spotify Web API (Client Credentials OAuth flow).
280
296
  - Create a new app
281
297
 
282
298
  - For **Redirect URL**, use: http://127.0.0.1:1234
299
+ - The URL must match exactly as shown, including not having a / at the end
300
+ - When copying the link via right-click, some browsers may add an extra / to the URL
283
301
 
284
302
  - Select **Web API** as the intended API
285
303
 
@@ -314,6 +332,8 @@ This method uses an official Spotify Web API (Authorization Code OAuth flow).
314
332
  - Create a new app
315
333
 
316
334
  - For **Redirect URL**, use: http://127.0.0.1:1234
335
+ - The URL must match exactly as shown, including not having a / at the end
336
+ - When copying the link via right-click, some browsers may add an extra / to the URL
317
337
 
318
338
  - Select **Web API** as the intended API
319
339
 
@@ -333,7 +353,7 @@ You can use the same client ID and secret values as those used for the [Spotify
333
353
  Example:
334
354
 
335
355
  ```sh
336
- spotify_profile_monitor --token-source oauth_user -r "your_spotify_user_client_id:your_spotify_user_client_secret" <spotify_user_uri_id>
356
+ spotify_profile_monitor --token-source oauth_user -n "your_spotify_user_client_id:your_spotify_user_client_secret" <spotify_user_uri_id>
337
357
  ```
338
358
 
339
359
  The tool takes care of refreshing the access token so it should remain valid indefinitely.
@@ -656,7 +676,7 @@ Make sure you defined your SMTP settings earlier (see [SMTP settings](#smtp-sett
656
676
  Example email:
657
677
 
658
678
  <p align="center">
659
- <img src="https://raw.githubusercontent.com/misiektoja/spotify_profile_monitor/refs/heads/main/assets/spotify_profile_monitor_email_notifications.png" alt="spotify_profile_monitor_email_notifications" width="80%"/>
679
+ <img src="https://raw.githubusercontent.com/misiektoja/spotify_profile_monitor/refs/heads/main/assets/spotify_profile_monitor_email_notifications.png" alt="spotify_profile_monitor_email_notifications" width="90%"/>
660
680
  </p>
661
681
 
662
682
  <a id="csv-export"></a>
@@ -824,13 +844,15 @@ You should get a valid Spotify access token, example output:
824
844
  <img src="https://raw.githubusercontent.com/misiektoja/spotify_monitor/refs/heads/main/assets/spotify_monitor_totp_test.png" alt="spotify_monitor_totp_test" width="100%"/>
825
845
  </p>
826
846
 
827
- > **NOTE:** secrets used for TOTP generation (`SECRET_CIPHER_DICT`) expire every two days; you can either run the [spotify_monitor_secret_grabber](https://github.com/misiektoja/spotify_monitor/blob/dev/debug/spotify_monitor_secret_grabber.py) and extract it by yourself (see [here](#secret-key-extraction-from-spotify-web-player-bundles) for more info) or you can pass `--fetch-secrets` flag in [spotify_monitor_totp_test](https://github.com/misiektoja/spotify_monitor/blob/dev/debug/spotify_monitor_totp_test.py) (available since v1.6). There is also a [Thereallo1026/spotify-secrets](https://github.com/Thereallo1026/spotify-secrets) repo which offers JSON files that are automatically updated with current secrets.
847
+ > **NOTE:** secrets used for TOTP generation (`SECRET_CIPHER_DICT`) expire every two days; you can either run the [spotify_monitor_secret_grabber](https://github.com/misiektoja/spotify_monitor/blob/dev/debug/spotify_monitor_secret_grabber.py) and extract it by yourself (see [here](#secret-key-extraction-from-spotify-web-player-bundles) for more info) or you can pass `--fetch-secrets` flag in `spotify_monitor_totp_test` (available since v1.6). There is also a [xyloflake/spot-secrets-go/](https://github.com/xyloflake/spot-secrets-go/) repo which offers JSON files that are automatically updated with current secrets (you can pass `--download-secrets` flag in `spotify_monitor_totp_test` to get it automatically from remote URL, available since v1.8).
828
848
 
829
849
  <a id="secret-key-extraction-from-spotify-web-player-bundles"></a>
830
850
  ### Secret Key Extraction from Spotify Web Player Bundles
831
851
 
832
852
  The [spotify_monitor_secret_grabber](https://github.com/misiektoja/spotify_monitor/blob/dev/debug/spotify_monitor_secret_grabber.py) tool automatically extracts secret keys used for TOTP generation in Spotify Web Player JavaScript bundles.
833
853
 
854
+ > 💡 **Quick tip:** The easiest and recommended way to run this tool is via Docker. Jump directly to the [Docker usage section below](#-secret-key-extraction-via-docker-recommended-easiest-way).
855
+
834
856
  Download from [here](https://github.com/misiektoja/spotify_monitor/blob/dev/debug/spotify_monitor_secret_grabber.py) or:
835
857
 
836
858
  ```sh
@@ -844,7 +866,7 @@ pip install playwright
844
866
  playwright install
845
867
  ```
846
868
 
847
- Run:
869
+ Run interactively (default output mode):
848
870
 
849
871
  ```sh
850
872
  python3 spotify_monitor_secret_grabber.py
@@ -856,15 +878,100 @@ You should get output similar to below:
856
878
  <img src="https://raw.githubusercontent.com/misiektoja/spotify_monitor/refs/heads/main/assets/spotify_monitor_secret_grabber.png" alt="spotify_monitor_secret_grabber" width="100%"/>
857
879
  </p>
858
880
 
859
- You can now update the secrets used for TOTP generation (for example `SECRET_CIPHER_DICT` in `spotify_monitor_totp_test`, `spotify_monitor` and `spotify_profile_monitor`).
881
+ Show help:
882
+ ```sh
883
+ python3 spotify_monitor_secret_grabber.py -h
884
+ ```
885
+
886
+ ---
887
+
888
+ <a id="cli-output-modes"></a>
889
+ ### CLI Output Modes
890
+
891
+ The script supports several output modes for different use cases:
892
+
893
+ | Flag | Description | Output |
894
+ |------|--------------|--------|
895
+ | `--secret` | Prints plain JSON array of extracted secrets | `[{"version": X, "secret": "..."}, ...]` |
896
+ | `--secretbytes` | Prints JSON array with ASCII byte values | `[{"version": X, "secret": [..]}, ...]` |
897
+ | `--secretdict` | Prints JSON object/dict mapping version → byte list | `{"X": [..], "Y": [..]}` |
898
+ | `--all` | Extracts secrets and **writes all three outputs** to local files | `secrets.json`, `secretBytes.json`, `secretDict.json` |
899
+
900
+ Print extracted secrets in specific format, for example Python-friendly secret bytes (JSON object/dict) and save to indicated file:
901
+
902
+ ```sh
903
+ python3 spotify_monitor_secret_grabber.py --secretdict > secretDict.json
904
+ ```
905
+
906
+ Or, to generate and save all secret formats to files (`secrets.json`, `secretBytes.json`, `secretDict.json`) at once:
907
+
908
+ ```sh
909
+ python3 spotify_monitor_secret_grabber.py --all
910
+ ```
911
+
912
+ Default file paths and names can be configured directly in the `OUTPUT_FILES` dictionary at the top of the script.
913
+
914
+ ---
915
+
916
+ <a id="-secret-key-extraction-via-docker-recommended-easiest-way"></a>
917
+ ### 🐳 Secret Key Extraction via Docker (Recommended Easiest Way)
860
918
 
861
- > **NOTE:** you can also use [Thereallo1026/spotify-secrets](https://github.com/Thereallo1026/spotify-secrets) repo which offers JSON files that are automatically updated with current secrets (its secret extraction code is based on `spotify_monitor_secret_grabber`).
919
+ A prebuilt multi-architecture image is available on Docker Hub: [`misiektoja/spotify-secrets-grabber`](https://hub.docker.com/r/misiektoja/spotify-secrets-grabber)
920
+
921
+ This image works on:
922
+ - macOS (Intel & Apple Silicon)
923
+ - Linux (x86_64 and ARM64)
924
+ - Windows (Docker Desktop / WSL2)
925
+ - Raspberry Pi 4/5 (64-bit OS)
926
+
927
+ Run interactively (default output mode):
928
+
929
+ ```sh
930
+ docker run --rm misiektoja/spotify-secrets-grabber
931
+ ```
932
+
933
+ Show help:
934
+ ```sh
935
+ docker run --rm misiektoja/spotify-secrets-grabber -h
936
+ ```
937
+
938
+ Print extracted secrets in specific format, for example Python-friendly secret bytes (JSON object/dict) and save to indicated file:
939
+ ```sh
940
+ docker run --rm misiektoja/spotify-secrets-grabber --secretdict > secretDict.json
941
+ ```
942
+
943
+ Or, to generate and save all secret formats to files (`secrets.json`, `secretBytes.json`, `secretDict.json`) at once:
944
+
945
+ ```sh
946
+ docker run --rm -v .:/work -w /work misiektoja/spotify-secrets-grabber --all
947
+ ```
948
+
949
+ *For SELinux hosts (Fedora/RHEL), use `-v .:/work:Z`.*
950
+
951
+ <a id="optional-use-docker-compose-one-command-for-all-oss"></a>
952
+ Or optionally use Docker Compose (a preconfigured [compose.yaml](https://github.com/misiektoja/spotify_monitor/blob/dev/debug/spotify_monitor_secret_grabber_docker/compose.yaml) file is included in the repo):
953
+
954
+ ```sh
955
+ docker compose run --rm spotify-secrets-grabber --all
956
+ ```
957
+
958
+ This will save all files into your current directory on any system (macOS, Linux or Windows).
959
+
960
+ ---
961
+
962
+ You can now update the secrets used for TOTP generation (for example `SECRET_CIPHER_DICT` in `spotify_monitor_totp_test`, `spotify_monitor` and `spotify_profile_monitor`) either manually or by referencing an external `secretDict.json` file, which can be hosted in another repo or stored locally. See the description of `SECRET_CIPHER_DICT_URL` in those files for details.
862
963
 
863
964
  <a id="change-log"></a>
864
965
  ## Change Log
865
966
 
866
967
  See [RELEASE_NOTES.md](https://github.com/misiektoja/spotify_profile_monitor/blob/main/RELEASE_NOTES.md) for details.
867
968
 
969
+ <a id="maintainers"></a>
970
+ ## Maintainers
971
+
972
+ [![Maintainer: misiektoja](https://img.shields.io/badge/maintainer-misiektoja-blue)](https://github.com/misiektoja)
973
+ [![Maintainer: tomballgithub](https://img.shields.io/badge/maintainer-tomballgithub-blue)](https://github.com/tomballgithub)
974
+
868
975
  <a id="license"></a>
869
976
  ## License
870
977
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "spotify_profile_monitor"
7
- version = "2.7"
7
+ version = "2.9"
8
8
  description = "Tool implementing real-time tracking of Spotify users activities and profile changes including playlists"
9
9
  readme = "README.md"
10
10
  license = "GPL-3.0-or-later"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: spotify_profile_monitor
3
- Version: 2.7
3
+ Version: 2.9
4
4
  Summary: Tool implementing real-time tracking of Spotify users activities and profile changes including playlists
5
5
  Author-email: Michal Szymanski <misiektoja-pypi@rm-rf.ninja>
6
6
  License-Expression: GPL-3.0-or-later
@@ -38,24 +38,25 @@ OSINT tool for real-time monitoring of **Spotify users' activities and profile c
38
38
  <a id="features"></a>
39
39
  ## Features
40
40
 
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 and Genius Lyrics search URLs printed in the console & included in email notifications
57
- - Support for four different methods to get a Spotify access token (`sp_dc cookie`, `desktop client`, `OAuth app`, `OAuth user`)
58
- - Possibility to control the running copy of the script via signals
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 **four different methods to get a Spotify access token** (`sp_dc cookie`, `desktop client`, `OAuth app`, `OAuth user`)
58
+ - Possibility to **control the running copy** of the script via signals
59
+ - **Functional, procedural Python** (minimal OOP)
59
60
 
60
61
  <p align="center">
61
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%"/>
@@ -68,6 +69,7 @@ OSINT tool for real-time monitoring of **Spotify users' activities and profile c
68
69
  2. [Installation](#installation)
69
70
  * [Install from PyPI](#install-from-pypi)
70
71
  * [Manual Installation](#manual-installation)
72
+ * [Upgrading](#upgrading)
71
73
  3. [Quick Start](#quick-start)
72
74
  4. [Configuration](#configuration)
73
75
  * [Configuration File](#configuration-file)
@@ -96,7 +98,8 @@ OSINT tool for real-time monitoring of **Spotify users' activities and profile c
96
98
  * [Access Token Retrieval via sp_dc Cookie and TOTP](#access-token-retrieval-via-sp_dc-cookie-and-totp)
97
99
  * [Secret Key Extraction from Spotify Web Player Bundles](#secret-key-extraction-from-spotify-web-player-bundles)
98
100
  7. [Change Log](#change-log)
99
- 8. [License](#license)
101
+ 8. [Maintainers](#maintainers)
102
+ 9. [License](#license)
100
103
 
101
104
  <a id="requirements"></a>
102
105
  ## Requirements
@@ -106,8 +109,8 @@ OSINT tool for real-time monitoring of **Spotify users' activities and profile c
106
109
 
107
110
  Tested on:
108
111
 
109
- * **macOS**: Ventura, Sonoma, Sequoia
110
- * **Linux**: Raspberry Pi OS (Bullseye, Bookworm), Ubuntu 24, Rocky Linux 8.x/9.x, Kali Linux 2024/2025
112
+ * **macOS**: Ventura, Sonoma, Sequoia, Tahoe
113
+ * **Linux**: Raspberry Pi OS (Bullseye, Bookworm, Trixie), Ubuntu 24/25, Rocky Linux 8.x/9.x, Kali Linux 2024/2025
111
114
  * **Windows**: 10, 11
112
115
 
113
116
  It should work on other versions of macOS, Linux, Unix and Windows as well.
@@ -139,6 +142,17 @@ Alternatively, from the downloaded *[requirements.txt](https://raw.githubusercon
139
142
  pip install -r requirements.txt
140
143
  ```
141
144
 
145
+ <a id="upgrading"></a>
146
+ ### Upgrading
147
+
148
+ To upgrade to the latest version when installed from PyPI:
149
+
150
+ ```sh
151
+ pip install spotify_profile_monitor -U
152
+ ```
153
+
154
+ If you installed manually, download the newest *[spotify_profile_monitor.py](https://raw.githubusercontent.com/misiektoja/spotify_profile_monitor/refs/heads/main/spotify_profile_monitor.py)* file to replace your existing installation.
155
+
142
156
  <a id="quick-start"></a>
143
157
  ## Quick Start
144
158
 
@@ -178,6 +192,8 @@ spotify_profile_monitor --generate-config > spotify_profile_monitor.conf
178
192
 
179
193
  Edit the `spotify_profile_monitor.conf` file and change any desired configuration options (detailed comments are provided for each).
180
194
 
195
+ **New in v2.9:** The configuration file includes options to enable/disable music service URLs (Apple Music, YouTube Music, Amazon Music, Deezer, Tidal) and lyrics service URLs (Genius, AZLyrics, Tekstowo.pl, Musixmatch, Lyrics.com) in console and email outputs.
196
+
181
197
  <a id="spotify-access-token-source"></a>
182
198
  ### Spotify access token source
183
199
 
@@ -309,6 +325,8 @@ This method uses an official Spotify Web API (Client Credentials OAuth flow).
309
325
  - Create a new app
310
326
 
311
327
  - For **Redirect URL**, use: http://127.0.0.1:1234
328
+ - The URL must match exactly as shown, including not having a / at the end
329
+ - When copying the link via right-click, some browsers may add an extra / to the URL
312
330
 
313
331
  - Select **Web API** as the intended API
314
332
 
@@ -343,6 +361,8 @@ This method uses an official Spotify Web API (Authorization Code OAuth flow).
343
361
  - Create a new app
344
362
 
345
363
  - For **Redirect URL**, use: http://127.0.0.1:1234
364
+ - The URL must match exactly as shown, including not having a / at the end
365
+ - When copying the link via right-click, some browsers may add an extra / to the URL
346
366
 
347
367
  - Select **Web API** as the intended API
348
368
 
@@ -362,7 +382,7 @@ You can use the same client ID and secret values as those used for the [Spotify
362
382
  Example:
363
383
 
364
384
  ```sh
365
- spotify_profile_monitor --token-source oauth_user -r "your_spotify_user_client_id:your_spotify_user_client_secret" <spotify_user_uri_id>
385
+ spotify_profile_monitor --token-source oauth_user -n "your_spotify_user_client_id:your_spotify_user_client_secret" <spotify_user_uri_id>
366
386
  ```
367
387
 
368
388
  The tool takes care of refreshing the access token so it should remain valid indefinitely.
@@ -685,7 +705,7 @@ Make sure you defined your SMTP settings earlier (see [SMTP settings](#smtp-sett
685
705
  Example email:
686
706
 
687
707
  <p align="center">
688
- <img src="https://raw.githubusercontent.com/misiektoja/spotify_profile_monitor/refs/heads/main/assets/spotify_profile_monitor_email_notifications.png" alt="spotify_profile_monitor_email_notifications" width="80%"/>
708
+ <img src="https://raw.githubusercontent.com/misiektoja/spotify_profile_monitor/refs/heads/main/assets/spotify_profile_monitor_email_notifications.png" alt="spotify_profile_monitor_email_notifications" width="90%"/>
689
709
  </p>
690
710
 
691
711
  <a id="csv-export"></a>
@@ -853,13 +873,15 @@ You should get a valid Spotify access token, example output:
853
873
  <img src="https://raw.githubusercontent.com/misiektoja/spotify_monitor/refs/heads/main/assets/spotify_monitor_totp_test.png" alt="spotify_monitor_totp_test" width="100%"/>
854
874
  </p>
855
875
 
856
- > **NOTE:** secrets used for TOTP generation (`SECRET_CIPHER_DICT`) expire every two days; you can either run the [spotify_monitor_secret_grabber](https://github.com/misiektoja/spotify_monitor/blob/dev/debug/spotify_monitor_secret_grabber.py) and extract it by yourself (see [here](#secret-key-extraction-from-spotify-web-player-bundles) for more info) or you can pass `--fetch-secrets` flag in [spotify_monitor_totp_test](https://github.com/misiektoja/spotify_monitor/blob/dev/debug/spotify_monitor_totp_test.py) (available since v1.6). There is also a [Thereallo1026/spotify-secrets](https://github.com/Thereallo1026/spotify-secrets) repo which offers JSON files that are automatically updated with current secrets.
876
+ > **NOTE:** secrets used for TOTP generation (`SECRET_CIPHER_DICT`) expire every two days; you can either run the [spotify_monitor_secret_grabber](https://github.com/misiektoja/spotify_monitor/blob/dev/debug/spotify_monitor_secret_grabber.py) and extract it by yourself (see [here](#secret-key-extraction-from-spotify-web-player-bundles) for more info) or you can pass `--fetch-secrets` flag in `spotify_monitor_totp_test` (available since v1.6). There is also a [xyloflake/spot-secrets-go/](https://github.com/xyloflake/spot-secrets-go/) repo which offers JSON files that are automatically updated with current secrets (you can pass `--download-secrets` flag in `spotify_monitor_totp_test` to get it automatically from remote URL, available since v1.8).
857
877
 
858
878
  <a id="secret-key-extraction-from-spotify-web-player-bundles"></a>
859
879
  ### Secret Key Extraction from Spotify Web Player Bundles
860
880
 
861
881
  The [spotify_monitor_secret_grabber](https://github.com/misiektoja/spotify_monitor/blob/dev/debug/spotify_monitor_secret_grabber.py) tool automatically extracts secret keys used for TOTP generation in Spotify Web Player JavaScript bundles.
862
882
 
883
+ > 💡 **Quick tip:** The easiest and recommended way to run this tool is via Docker. Jump directly to the [Docker usage section below](#-secret-key-extraction-via-docker-recommended-easiest-way).
884
+
863
885
  Download from [here](https://github.com/misiektoja/spotify_monitor/blob/dev/debug/spotify_monitor_secret_grabber.py) or:
864
886
 
865
887
  ```sh
@@ -873,7 +895,7 @@ pip install playwright
873
895
  playwright install
874
896
  ```
875
897
 
876
- Run:
898
+ Run interactively (default output mode):
877
899
 
878
900
  ```sh
879
901
  python3 spotify_monitor_secret_grabber.py
@@ -885,15 +907,100 @@ You should get output similar to below:
885
907
  <img src="https://raw.githubusercontent.com/misiektoja/spotify_monitor/refs/heads/main/assets/spotify_monitor_secret_grabber.png" alt="spotify_monitor_secret_grabber" width="100%"/>
886
908
  </p>
887
909
 
888
- You can now update the secrets used for TOTP generation (for example `SECRET_CIPHER_DICT` in `spotify_monitor_totp_test`, `spotify_monitor` and `spotify_profile_monitor`).
910
+ Show help:
911
+ ```sh
912
+ python3 spotify_monitor_secret_grabber.py -h
913
+ ```
914
+
915
+ ---
916
+
917
+ <a id="cli-output-modes"></a>
918
+ ### CLI Output Modes
919
+
920
+ The script supports several output modes for different use cases:
921
+
922
+ | Flag | Description | Output |
923
+ |------|--------------|--------|
924
+ | `--secret` | Prints plain JSON array of extracted secrets | `[{"version": X, "secret": "..."}, ...]` |
925
+ | `--secretbytes` | Prints JSON array with ASCII byte values | `[{"version": X, "secret": [..]}, ...]` |
926
+ | `--secretdict` | Prints JSON object/dict mapping version → byte list | `{"X": [..], "Y": [..]}` |
927
+ | `--all` | Extracts secrets and **writes all three outputs** to local files | `secrets.json`, `secretBytes.json`, `secretDict.json` |
928
+
929
+ Print extracted secrets in specific format, for example Python-friendly secret bytes (JSON object/dict) and save to indicated file:
930
+
931
+ ```sh
932
+ python3 spotify_monitor_secret_grabber.py --secretdict > secretDict.json
933
+ ```
934
+
935
+ Or, to generate and save all secret formats to files (`secrets.json`, `secretBytes.json`, `secretDict.json`) at once:
936
+
937
+ ```sh
938
+ python3 spotify_monitor_secret_grabber.py --all
939
+ ```
940
+
941
+ Default file paths and names can be configured directly in the `OUTPUT_FILES` dictionary at the top of the script.
942
+
943
+ ---
944
+
945
+ <a id="-secret-key-extraction-via-docker-recommended-easiest-way"></a>
946
+ ### 🐳 Secret Key Extraction via Docker (Recommended Easiest Way)
889
947
 
890
- > **NOTE:** you can also use [Thereallo1026/spotify-secrets](https://github.com/Thereallo1026/spotify-secrets) repo which offers JSON files that are automatically updated with current secrets (its secret extraction code is based on `spotify_monitor_secret_grabber`).
948
+ A prebuilt multi-architecture image is available on Docker Hub: [`misiektoja/spotify-secrets-grabber`](https://hub.docker.com/r/misiektoja/spotify-secrets-grabber)
949
+
950
+ This image works on:
951
+ - macOS (Intel & Apple Silicon)
952
+ - Linux (x86_64 and ARM64)
953
+ - Windows (Docker Desktop / WSL2)
954
+ - Raspberry Pi 4/5 (64-bit OS)
955
+
956
+ Run interactively (default output mode):
957
+
958
+ ```sh
959
+ docker run --rm misiektoja/spotify-secrets-grabber
960
+ ```
961
+
962
+ Show help:
963
+ ```sh
964
+ docker run --rm misiektoja/spotify-secrets-grabber -h
965
+ ```
966
+
967
+ Print extracted secrets in specific format, for example Python-friendly secret bytes (JSON object/dict) and save to indicated file:
968
+ ```sh
969
+ docker run --rm misiektoja/spotify-secrets-grabber --secretdict > secretDict.json
970
+ ```
971
+
972
+ Or, to generate and save all secret formats to files (`secrets.json`, `secretBytes.json`, `secretDict.json`) at once:
973
+
974
+ ```sh
975
+ docker run --rm -v .:/work -w /work misiektoja/spotify-secrets-grabber --all
976
+ ```
977
+
978
+ *For SELinux hosts (Fedora/RHEL), use `-v .:/work:Z`.*
979
+
980
+ <a id="optional-use-docker-compose-one-command-for-all-oss"></a>
981
+ Or optionally use Docker Compose (a preconfigured [compose.yaml](https://github.com/misiektoja/spotify_monitor/blob/dev/debug/spotify_monitor_secret_grabber_docker/compose.yaml) file is included in the repo):
982
+
983
+ ```sh
984
+ docker compose run --rm spotify-secrets-grabber --all
985
+ ```
986
+
987
+ This will save all files into your current directory on any system (macOS, Linux or Windows).
988
+
989
+ ---
990
+
991
+ You can now update the secrets used for TOTP generation (for example `SECRET_CIPHER_DICT` in `spotify_monitor_totp_test`, `spotify_monitor` and `spotify_profile_monitor`) either manually or by referencing an external `secretDict.json` file, which can be hosted in another repo or stored locally. See the description of `SECRET_CIPHER_DICT_URL` in those files for details.
891
992
 
892
993
  <a id="change-log"></a>
893
994
  ## Change Log
894
995
 
895
996
  See [RELEASE_NOTES.md](https://github.com/misiektoja/spotify_profile_monitor/blob/main/RELEASE_NOTES.md) for details.
896
997
 
998
+ <a id="maintainers"></a>
999
+ ## Maintainers
1000
+
1001
+ [![Maintainer: misiektoja](https://img.shields.io/badge/maintainer-misiektoja-blue)](https://github.com/misiektoja)
1002
+ [![Maintainer: tomballgithub](https://img.shields.io/badge/maintainer-tomballgithub-blue)](https://github.com/tomballgithub)
1003
+
897
1004
  <a id="license"></a>
898
1005
  ## License
899
1006
 
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env python3
2
2
  """
3
3
  Author: Michal Szymanski <misiektoja-github@rm-rf.ninja>
4
- v2.7
4
+ v2.9
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 (optional, needed when the token source is set to oauth_app)
19
19
  wcwidth (optional, needed by TRUNCATE_CHARS feature)
20
20
  """
21
21
 
22
- VERSION = "2.7"
22
+ VERSION = "2.9"
23
23
 
24
24
  # ---------------------------
25
25
  # CONFIGURATION SECTION START
@@ -247,6 +247,38 @@ TRUNCATE_CHARS = 0
247
247
  # Value used by signal handlers to increase or decrease profile check interval (SPOTIFY_CHECK_INTERVAL); in seconds
248
248
  SPOTIFY_CHECK_SIGNAL_VALUE = 300 # 5 minutes
249
249
 
250
+ # Whether to show Apple Music URL in console and emails
251
+ ENABLE_APPLE_MUSIC_URL = True
252
+
253
+ # Whether to show YouTube Music URL in console and emails
254
+ ENABLE_YOUTUBE_MUSIC_URL = True
255
+
256
+ # Whether to show Amazon Music URL in console and emails
257
+ ENABLE_AMAZON_MUSIC_URL = False
258
+
259
+ # Whether to show Deezer URL in console and emails
260
+ ENABLE_DEEZER_URL = False
261
+
262
+ # Whether to show Tidal URL in console and emails
263
+ # Note: Tidal requires users to be logged in to their account in the web browser to use the search functionality
264
+ ENABLE_TIDAL_URL = False
265
+
266
+ # Whether to show Genius lyrics URL in console and emails
267
+ ENABLE_GENIUS_LYRICS_URL = True
268
+
269
+ # Whether to show AZLyrics URL in console and emails
270
+ ENABLE_AZLYRICS_URL = False
271
+
272
+ # Whether to show Tekstowo.pl lyrics URL in console and emails
273
+ ENABLE_TEKSTOWO_URL = False
274
+
275
+ # Whether to show Musixmatch lyrics URL in console and emails
276
+ # Note: Musixmatch requires users to be logged in to their account in the web browser to use the search functionality
277
+ ENABLE_MUSIXMATCH_URL = False
278
+
279
+ # Whether to show Lyrics.com lyrics URL in console and emails
280
+ ENABLE_LYRICS_COM_URL = False
281
+
250
282
  # ---------------------------------------------------------------------
251
283
 
252
284
  # The section below is used when the token source is set to 'cookie'
@@ -261,19 +293,17 @@ TOKEN_RETRY_TIMEOUT = 0.5 # 0.5 second
261
293
  # Newest secrets are downloaded automatically from SECRET_CIPHER_DICT_URL (see below)
262
294
  # Can also be fetched via spotify_monitor_secret_grabber.py utility - see debug dir
263
295
  SECRET_CIPHER_DICT = {
264
- "12": [107, 81, 49, 57, 67, 93, 87, 81, 69, 67, 40, 93, 48, 50, 46, 91, 94, 113, 41, 108, 77, 107, 34],
265
- "11": [111, 45, 40, 73, 95, 74, 35, 85, 105, 107, 60, 110, 55, 72, 69, 70, 114, 83, 63, 88, 91],
266
- "10": [61, 110, 58, 98, 35, 79, 117, 69, 102, 72, 92, 102, 69, 93, 41, 101, 42, 75],
267
- "9": [109, 101, 90, 99, 66, 92, 116, 108, 85, 70, 86, 49, 68, 54, 87, 50, 72, 121, 52, 64, 57, 43, 36, 81, 97, 72, 53, 41, 78, 56],
268
- "8": [37, 84, 32, 76, 87, 90, 87, 47, 13, 75, 48, 54, 44, 28, 19, 21, 22],
269
- "7": [59, 91, 66, 74, 30, 66, 74, 38, 46, 50, 72, 61, 44, 71, 86, 39, 89],
270
296
  "6": [21, 24, 85, 46, 48, 35, 33, 8, 11, 63, 76, 12, 55, 77, 14, 7, 54],
271
297
  "5": [12, 56, 76, 33, 88, 44, 88, 33, 78, 78, 11, 66, 22, 22, 55, 69, 54],
272
298
  }
273
299
 
274
- # Remote URL used to fetch updated secrets needed for TOTP generation
300
+ # Remote or local URL used to fetch updated secrets needed for TOTP generation
275
301
  # Set to empty string to disable
276
- SECRET_CIPHER_DICT_URL = "https://github.com/Thereallo1026/spotify-secrets/blob/main/secrets/secretDict.json?raw=true"
302
+ # If you used "spotify_monitor_secret_grabber.py --secretdict > secretDict.json" specify the file location below
303
+ # SECRET_CIPHER_DICT_URL = "https://github.com/Thereallo1026/spotify-secrets/blob/main/secrets/secretDict.json?raw=true"
304
+ SECRET_CIPHER_DICT_URL = "https://github.com/xyloflake/spot-secrets-go/blob/main/secrets/secretDict.json?raw=true"
305
+ # SECRET_CIPHER_DICT_URL = file:///C:/your_path/secretDict.json
306
+ # SECRET_CIPHER_DICT_URL = "file:///your_path/secretDict.json"
277
307
 
278
308
  # Identifier used to select the appropriate secret from SECRET_CIPHER_DICT when generating a TOTP token
279
309
  # Set to 0 to auto-select the highest available version
@@ -544,6 +574,16 @@ DISABLE_LOGGING = False
544
574
  HORIZONTAL_LINE = 0
545
575
  CLEAR_SCREEN = False
546
576
  SPOTIFY_CHECK_SIGNAL_VALUE = 0
577
+ ENABLE_APPLE_MUSIC_URL = False
578
+ ENABLE_YOUTUBE_MUSIC_URL = False
579
+ ENABLE_AMAZON_MUSIC_URL = False
580
+ ENABLE_DEEZER_URL = False
581
+ ENABLE_TIDAL_URL = False
582
+ ENABLE_GENIUS_LYRICS_URL = False
583
+ ENABLE_AZLYRICS_URL = False
584
+ ENABLE_TEKSTOWO_URL = False
585
+ ENABLE_MUSIXMATCH_URL = False
586
+ ENABLE_LYRICS_COM_URL = False
547
587
  TOKEN_MAX_RETRIES = 0
548
588
  TOKEN_RETRY_TIMEOUT = 0.0
549
589
  SECRET_CIPHER_DICT = {}
@@ -1294,17 +1334,126 @@ def reload_secrets_signal_handler(sig, frame):
1294
1334
  print_cur_ts("Timestamp:\t\t\t")
1295
1335
 
1296
1336
 
1297
- # Returns Apple & Genius search URLs for specified track
1337
+ # Returns Apple & lyrics search URLs for specified track
1298
1338
  def get_apple_genius_search_urls(artist, track):
1299
- genius_search_string = f"{artist} {track}"
1300
- youtube_music_search_string = quote_plus(f"{artist} {track}")
1301
- if re.search(re_search_str, genius_search_string, re.IGNORECASE):
1302
- genius_search_string = re.sub(re_replace_str, '', genius_search_string, flags=re.IGNORECASE)
1303
- apple_search_string = quote(f"{artist} {track}")
1339
+ spotify_search_string = f"{artist} {track}"
1340
+ youtube_music_search_string = quote_plus(spotify_search_string)
1341
+ # Clean search string for lyrics services (remove remaster, extended, etc.)
1342
+ lyrics_search_string = spotify_search_string
1343
+ if re.search(re_search_str, lyrics_search_string, re.IGNORECASE):
1344
+ lyrics_search_string = re.sub(re_replace_str, '', lyrics_search_string, flags=re.IGNORECASE)
1345
+ apple_search_string = quote(spotify_search_string)
1304
1346
  apple_search_url = f"https://music.apple.com/pl/search?term={apple_search_string}"
1305
- genius_search_url = f"https://genius.com/search?q={quote_plus(genius_search_string)}"
1347
+ genius_search_url = f"https://genius.com/search?q={quote_plus(lyrics_search_string)}"
1348
+ azlyrics_search_url = f"https://www.azlyrics.com/search/?q={quote_plus(lyrics_search_string)}"
1349
+ tekstowo_search_url = f"https://www.tekstowo.pl/szukaj,{quote_plus(lyrics_search_string)}.html"
1350
+ musixmatch_search_url = f"https://www.musixmatch.com/search?query={quote_plus(lyrics_search_string)}"
1351
+ lyrics_com_search_url = f"https://www.lyrics.com/serp.php?st={quote_plus(lyrics_search_string)}&qtype=1"
1306
1352
  youtube_music_search_url = f"https://music.youtube.com/search?q={youtube_music_search_string}"
1307
- return apple_search_url, genius_search_url, youtube_music_search_url
1353
+ amazon_music_search_url = f"https://music.amazon.com/search/{quote_plus(spotify_search_string)}"
1354
+ deezer_search_url = f"https://www.deezer.com/search/{quote_plus(spotify_search_string)}"
1355
+ tidal_search_url = f"https://tidal.com/search?q={quote_plus(spotify_search_string)}"
1356
+ return apple_search_url, genius_search_url, azlyrics_search_url, tekstowo_search_url, musixmatch_search_url, lyrics_com_search_url, youtube_music_search_url, amazon_music_search_url, deezer_search_url, tidal_search_url
1357
+
1358
+
1359
+ # Formats lyrics URLs for console output based on configuration
1360
+ def format_lyrics_urls_console(genius_url, azlyrics_url, tekstowo_url, musixmatch_url, lyrics_com_url):
1361
+ lines = []
1362
+ if ENABLE_GENIUS_LYRICS_URL:
1363
+ lines.append(f"Genius lyrics URL: {genius_url}")
1364
+ if ENABLE_AZLYRICS_URL:
1365
+ lines.append(f"AZLyrics URL: {azlyrics_url}")
1366
+ if ENABLE_TEKSTOWO_URL:
1367
+ lines.append(f"Tekstowo.pl URL: {tekstowo_url}")
1368
+ if ENABLE_MUSIXMATCH_URL:
1369
+ lines.append(f"Musixmatch URL: {musixmatch_url}")
1370
+ if ENABLE_LYRICS_COM_URL:
1371
+ lines.append(f"Lyrics.com URL: {lyrics_com_url}")
1372
+ return "\n".join(lines) if lines else ""
1373
+
1374
+
1375
+ # Formats lyrics URLs for plain text email body based on configuration
1376
+ def format_lyrics_urls_email_text(genius_url, azlyrics_url, tekstowo_url, musixmatch_url, lyrics_com_url):
1377
+ lines = []
1378
+ if ENABLE_GENIUS_LYRICS_URL:
1379
+ lines.append(f"Genius lyrics URL: {genius_url}")
1380
+ if ENABLE_AZLYRICS_URL:
1381
+ lines.append(f"AZLyrics URL: {azlyrics_url}")
1382
+ if ENABLE_TEKSTOWO_URL:
1383
+ lines.append(f"Tekstowo.pl URL: {tekstowo_url}")
1384
+ if ENABLE_MUSIXMATCH_URL:
1385
+ lines.append(f"Musixmatch URL: {musixmatch_url}")
1386
+ if ENABLE_LYRICS_COM_URL:
1387
+ lines.append(f"Lyrics.com URL: {lyrics_com_url}")
1388
+ return "\n".join(lines) if lines else ""
1389
+
1390
+
1391
+ # Formats lyrics URLs for HTML email body based on configuration
1392
+ def format_lyrics_urls_email_html(genius_url, azlyrics_url, tekstowo_url, musixmatch_url, lyrics_com_url, artist, track):
1393
+ lines = []
1394
+ escaped_artist = escape(artist)
1395
+ escaped_track = escape(track)
1396
+ if ENABLE_GENIUS_LYRICS_URL:
1397
+ lines.append(f'Genius lyrics URL: <a href="{genius_url}">{escaped_artist} - {escaped_track}</a>')
1398
+ if ENABLE_AZLYRICS_URL:
1399
+ lines.append(f'AZLyrics URL: <a href="{azlyrics_url}">{escaped_artist} - {escaped_track}</a>')
1400
+ if ENABLE_TEKSTOWO_URL:
1401
+ lines.append(f'Tekstowo.pl URL: <a href="{tekstowo_url}">{escaped_artist} - {escaped_track}</a>')
1402
+ if ENABLE_MUSIXMATCH_URL:
1403
+ lines.append(f'Musixmatch URL: <a href="{musixmatch_url}">{escaped_artist} - {escaped_track}</a>')
1404
+ if ENABLE_LYRICS_COM_URL:
1405
+ lines.append(f'Lyrics.com URL: <a href="{lyrics_com_url}">{escaped_artist} - {escaped_track}</a>')
1406
+ return "<br>".join(lines) if lines else ""
1407
+
1408
+
1409
+ # Formats music service URLs for console output based on configuration
1410
+ def format_music_urls_console(apple_music_url, youtube_music_url, amazon_music_url, deezer_url, tidal_url):
1411
+ lines = []
1412
+ if ENABLE_APPLE_MUSIC_URL:
1413
+ lines.append(f"Apple Music URL: {apple_music_url}")
1414
+ if ENABLE_YOUTUBE_MUSIC_URL:
1415
+ lines.append(f"YouTube Music URL: {youtube_music_url}")
1416
+ if ENABLE_AMAZON_MUSIC_URL:
1417
+ lines.append(f"Amazon Music URL: {amazon_music_url}")
1418
+ if ENABLE_DEEZER_URL:
1419
+ lines.append(f"Deezer URL: {deezer_url}")
1420
+ if ENABLE_TIDAL_URL:
1421
+ lines.append(f"Tidal URL: {tidal_url}")
1422
+ return "\n".join(lines) if lines else ""
1423
+
1424
+
1425
+ # Formats music service URLs for plain text email body based on configuration
1426
+ def format_music_urls_email_text(apple_music_url, youtube_music_url, amazon_music_url, deezer_url, tidal_url):
1427
+ lines = []
1428
+ if ENABLE_APPLE_MUSIC_URL:
1429
+ lines.append(f"Apple Music URL: {apple_music_url}")
1430
+ if ENABLE_YOUTUBE_MUSIC_URL:
1431
+ lines.append(f"YouTube Music URL: {youtube_music_url}")
1432
+ if ENABLE_AMAZON_MUSIC_URL:
1433
+ lines.append(f"Amazon Music URL: {amazon_music_url}")
1434
+ if ENABLE_DEEZER_URL:
1435
+ lines.append(f"Deezer URL: {deezer_url}")
1436
+ if ENABLE_TIDAL_URL:
1437
+ lines.append(f"Tidal URL: {tidal_url}")
1438
+ return "\n".join(lines) if lines else ""
1439
+
1440
+
1441
+ # Formats music service URLs for HTML email body based on configuration
1442
+ def format_music_urls_email_html(apple_music_url, youtube_music_url, amazon_music_url, deezer_url, tidal_url, artist, track):
1443
+ lines = []
1444
+ escaped_artist = escape(artist)
1445
+ escaped_track = escape(track)
1446
+ if ENABLE_APPLE_MUSIC_URL:
1447
+ lines.append(f'Apple Music URL: <a href="{apple_music_url}">{escaped_artist} - {escaped_track}</a>')
1448
+ if ENABLE_YOUTUBE_MUSIC_URL:
1449
+ lines.append(f'YouTube Music URL: <a href="{youtube_music_url}">{escaped_artist} - {escaped_track}</a>')
1450
+ if ENABLE_AMAZON_MUSIC_URL:
1451
+ lines.append(f'Amazon Music URL: <a href="{amazon_music_url}">{escaped_artist} - {escaped_track}</a>')
1452
+ if ENABLE_DEEZER_URL:
1453
+ lines.append(f'Deezer URL: <a href="{deezer_url}">{escaped_artist} - {escaped_track}</a>')
1454
+ if ENABLE_TIDAL_URL:
1455
+ lines.append(f'Tidal URL: <a href="{tidal_url}">{escaped_artist} - {escaped_track}</a>')
1456
+ return "<br>".join(lines) if lines else ""
1308
1457
 
1309
1458
 
1310
1459
  # Extracts Spotify ID from URI or URL and return cleaned name
@@ -1498,12 +1647,43 @@ def fetch_and_update_secrets():
1498
1647
  return False
1499
1648
 
1500
1649
  try:
1501
- response = req.get(SECRET_CIPHER_DICT_URL, timeout=FUNCTION_TIMEOUT, verify=VERIFY_SSL)
1502
- response.raise_for_status()
1503
- secrets = response.json()
1650
+ if SECRET_CIPHER_DICT_URL.startswith("file:"):
1651
+ import os
1652
+ from urllib.parse import urlparse, unquote
1653
+
1654
+ parsed = urlparse(SECRET_CIPHER_DICT_URL)
1655
+
1656
+ if parsed.netloc:
1657
+ raw_path = f"/{parsed.netloc}{parsed.path or ''}"
1658
+ else:
1659
+ if SECRET_CIPHER_DICT_URL.startswith("file://"):
1660
+ raw_path = parsed.path or SECRET_CIPHER_DICT_URL[len("file://"):]
1661
+ else:
1662
+ raw_path = parsed.path or SECRET_CIPHER_DICT_URL[len("file:"):]
1663
+
1664
+ raw_path = unquote(raw_path)
1665
+
1666
+ if raw_path.startswith("/~"):
1667
+ raw_path = raw_path[1:]
1668
+
1669
+ if not raw_path.startswith("/") and not raw_path.startswith("~"):
1670
+ raw_path = "/" + raw_path
1671
+
1672
+ path = os.path.expanduser(os.path.expandvars(raw_path))
1673
+
1674
+ print(f"Loading Spotify web-player TOTP secrets from file: {path}")
1675
+ with open(path, "r", encoding="utf-8") as f:
1676
+ secrets = json.load(f)
1677
+ print("─" * HORIZONTAL_LINE)
1678
+ else:
1679
+ print(f"Fetching Spotify web-player TOTP secrets from URL: {SECRET_CIPHER_DICT_URL}")
1680
+ response = req.get(SECRET_CIPHER_DICT_URL, timeout=FUNCTION_TIMEOUT, verify=VERIFY_SSL)
1681
+ response.raise_for_status()
1682
+ secrets = response.json()
1683
+ print("─" * HORIZONTAL_LINE)
1504
1684
 
1505
1685
  if not isinstance(secrets, dict) or not secrets:
1506
- raise ValueError("fetch_and_update_secrets(): Fetched payload not a nonempty dict")
1686
+ raise ValueError("fetch_and_update_secrets(): Fetched payload not a non-empty dict")
1507
1687
 
1508
1688
  for key, value in secrets.items():
1509
1689
  if not isinstance(key, str) or not key.isdigit():
@@ -2520,7 +2700,12 @@ def is_token_owner(access_token, user_uri_id) -> bool:
2520
2700
 
2521
2701
  # Returns detailed info about playlist with specified URI (with possibility to get its tracks as well)
2522
2702
  def spotify_get_playlist_info(access_token, playlist_uri, get_tracks):
2523
- playlist_id = playlist_uri.split(':', 2)[2]
2703
+ parts = playlist_uri.split(':')
2704
+ if len(parts) == 3:
2705
+ playlist_id = parts[2]
2706
+ else:
2707
+ playlist_id = "invalid_playlist"
2708
+ print(f"Invalid playlist format")
2524
2709
 
2525
2710
  if get_tracks:
2526
2711
  url1 = f"https://api.spotify.com/v1/playlists/{playlist_id}?fields=name,description,owner,followers,external_urls,tracks.total,collaborative,images"
@@ -2911,7 +3096,11 @@ def spotify_list_tracks_for_playlist(sp_accessToken, playlist_url, csv_file_name
2911
3096
  user_id_name_mapping = {}
2912
3097
  user_track_counts = Counter()
2913
3098
 
2914
- playlist_uri = spotify_convert_url_to_uri(playlist_url)
3099
+ pattern = re.compile(r'^[a-zA-Z0-9]{22}$')
3100
+ if (pattern.match(playlist_url)):
3101
+ playlist_uri = f"::{playlist_url}"
3102
+ else:
3103
+ playlist_uri = spotify_convert_url_to_uri(playlist_url)
2915
3104
 
2916
3105
  sp_playlist_data = spotify_get_playlist_info(sp_accessToken, playlist_uri, True)
2917
3106
 
@@ -3893,7 +4082,8 @@ def spotify_profile_monitor_uri(user_uri_id, csv_file_name, playlists_to_skip):
3893
4082
 
3894
4083
  out = f"Monitoring user {user_uri_id}"
3895
4084
  print(out)
3896
- print("-" * len(out))
4085
+ # print("-" * len(out))
4086
+ print("─" * HORIZONTAL_LINE)
3897
4087
 
3898
4088
  try:
3899
4089
  if TOKEN_SOURCE == "client":
@@ -3939,9 +4129,10 @@ def spotify_profile_monitor_uri(user_uri_id, csv_file_name, playlists_to_skip):
3939
4129
 
3940
4130
  if user_info:
3941
4131
  if TOKEN_SOURCE == "oauth_app":
3942
- print(f"Token belongs to:\t\t{user_info.get('client_id', '')} (via {TOKEN_SOURCE})\n")
4132
+ print(f"Token belongs to:\t\t{user_info.get('client_id', '')} (via {TOKEN_SOURCE})")
3943
4133
  else:
3944
- print(f"Token belongs to:\t\t{user_info.get('display_name', '')} (via {TOKEN_SOURCE})\n\t\t\t\t[ {user_info.get('spotify_url')} ]\n")
4134
+ print(f"Token belongs to:\t\t{user_info.get('display_name', '')} (via {TOKEN_SOURCE})\n\t\t\t\t[ {user_info.get('spotify_url')} ]")
4135
+ print("─" * HORIZONTAL_LINE)
3945
4136
 
3946
4137
  username = sp_user_data["sp_username"]
3947
4138
  image_url = sp_user_data["sp_user_image_url"]
@@ -4754,12 +4945,35 @@ def spotify_profile_monitor_uri(user_uri_id, csv_file_name, playlists_to_skip):
4754
4945
 
4755
4946
  for f_dict in added_tracks:
4756
4947
  if "artist" in f_dict and "track" in f_dict:
4757
- apple_search_url, genius_search_url, youtube_music_search_url = get_apple_genius_search_urls(f_dict["artist"], f_dict["track"])
4948
+ apple_search_url, genius_search_url, azlyrics_search_url, tekstowo_search_url, musixmatch_search_url, lyrics_com_search_url, youtube_music_search_url, amazon_music_search_url, deezer_search_url, tidal_search_url = get_apple_genius_search_urls(f_dict["artist"], f_dict["track"])
4758
4949
  tempuri = f'spotify:user:{f_dict["added_by_id"]}'
4759
- added_track = f'- {f_dict["artist"]} - {f_dict["track"]} [ {get_date_from_ts(f_dict["added_at"])}, {f_dict["added_by"]} ]\n[ Spotify URL: {spotify_convert_uri_to_url(f_dict["uri"])} ]\n[ Apple Music URL: {apple_search_url} ]\n[ YouTube Music URL: {youtube_music_search_url} ]\n[ Genius URL: {genius_search_url} ]\n[ Collaborator URL: {spotify_convert_uri_to_url(tempuri)} ]\n\n'
4760
- p_message_added_tracks += added_track
4950
+ music_urls_output = format_music_urls_console(apple_search_url, youtube_music_search_url, amazon_music_search_url, deezer_search_url, tidal_search_url)
4951
+ lyrics_urls_output = format_lyrics_urls_console(genius_search_url, azlyrics_search_url, tekstowo_search_url, musixmatch_search_url, lyrics_com_search_url)
4952
+ music_urls_text = format_music_urls_email_text(apple_search_url, youtube_music_search_url, amazon_music_search_url, deezer_search_url, tidal_search_url)
4953
+ lyrics_urls_text = format_lyrics_urls_email_text(genius_search_url, azlyrics_search_url, tekstowo_search_url, musixmatch_search_url, lyrics_com_search_url)
4954
+ added_track_console = f'- {f_dict["artist"]} - {f_dict["track"]} [ {get_date_from_ts(f_dict["added_at"])}, {f_dict["added_by"]} ]\n[ Spotify URL: {spotify_convert_uri_to_url(f_dict["uri"])} ]\n'
4955
+ if music_urls_output:
4956
+ for line in music_urls_output.split("\n"):
4957
+ if line:
4958
+ added_track_console += f"[ {line} ]\n"
4959
+ if lyrics_urls_output:
4960
+ for line in lyrics_urls_output.split("\n"):
4961
+ if line:
4962
+ added_track_console += f"[ {line} ]\n"
4963
+ added_track_console += f'[ Collaborator URL: {spotify_convert_uri_to_url(tempuri)} ]\n\n'
4964
+ added_track_email = f'- {f_dict["artist"]} - {f_dict["track"]} [ {get_date_from_ts(f_dict["added_at"])}, {f_dict["added_by"]} ]\n[ Spotify URL: {spotify_convert_uri_to_url(f_dict["uri"])} ]\n'
4965
+ if music_urls_text:
4966
+ for line in music_urls_text.split("\n"):
4967
+ if line:
4968
+ added_track_email += f"[ {line} ]\n"
4969
+ if lyrics_urls_text:
4970
+ for line in lyrics_urls_text.split("\n"):
4971
+ if line:
4972
+ added_track_email += f"[ {line} ]\n"
4973
+ added_track_email += f'[ Collaborator URL: {spotify_convert_uri_to_url(tempuri)} ]\n\n'
4974
+ p_message_added_tracks += added_track_email
4761
4975
  added_at_dt = f_dict['added_at']
4762
- print(added_track, end="")
4976
+ print(added_track_console, end="")
4763
4977
  try:
4764
4978
  if csv_file_name:
4765
4979
  write_csv_entry(csv_file_name, convert_to_local_naive(added_at_dt), "Added Track", p_name, f_dict['added_by'], f_dict["artist"] + " - " + f_dict["track"])
@@ -4772,11 +4986,34 @@ def spotify_profile_monitor_uri(user_uri_id, csv_file_name, playlists_to_skip):
4772
4986
 
4773
4987
  for f_dict in removed_tracks:
4774
4988
  if "artist" in f_dict and "track" in f_dict:
4775
- apple_search_url, genius_search_url, youtube_music_search_url = get_apple_genius_search_urls(f_dict["artist"], f_dict["track"])
4989
+ apple_search_url, genius_search_url, azlyrics_search_url, tekstowo_search_url, musixmatch_search_url, lyrics_com_search_url, youtube_music_search_url, amazon_music_search_url, deezer_search_url, tidal_search_url = get_apple_genius_search_urls(f_dict["artist"], f_dict["track"])
4776
4990
  tempuri = f'spotify:user:{f_dict["added_by_id"]}'
4777
- removed_track = f'- {f_dict["artist"]} - {f_dict["track"]} [ {get_date_from_ts(f_dict["added_at"])}, {f_dict["added_by"]} ]\n[ Spotify URL: {spotify_convert_uri_to_url(f_dict["uri"])} ]\n[ Apple Music URL: {apple_search_url} ]\n[ YouTube Music URL: {youtube_music_search_url} ]\n[ Genius URL: {genius_search_url} ]\n[ Collaborator URL: {spotify_convert_uri_to_url(tempuri)} ]\n\n'
4778
- p_message_removed_tracks += removed_track
4779
- print(removed_track, end="")
4991
+ music_urls_output = format_music_urls_console(apple_search_url, youtube_music_search_url, amazon_music_search_url, deezer_search_url, tidal_search_url)
4992
+ lyrics_urls_output = format_lyrics_urls_console(genius_search_url, azlyrics_search_url, tekstowo_search_url, musixmatch_search_url, lyrics_com_search_url)
4993
+ music_urls_text = format_music_urls_email_text(apple_search_url, youtube_music_search_url, amazon_music_search_url, deezer_search_url, tidal_search_url)
4994
+ lyrics_urls_text = format_lyrics_urls_email_text(genius_search_url, azlyrics_search_url, tekstowo_search_url, musixmatch_search_url, lyrics_com_search_url)
4995
+ removed_track_console = f'- {f_dict["artist"]} - {f_dict["track"]} [ {get_date_from_ts(f_dict["added_at"])}, {f_dict["added_by"]} ]\n[ Spotify URL: {spotify_convert_uri_to_url(f_dict["uri"])} ]\n'
4996
+ if music_urls_output:
4997
+ for line in music_urls_output.split("\n"):
4998
+ if line:
4999
+ removed_track_console += f"[ {line} ]\n"
5000
+ if lyrics_urls_output:
5001
+ for line in lyrics_urls_output.split("\n"):
5002
+ if line:
5003
+ removed_track_console += f"[ {line} ]\n"
5004
+ removed_track_console += f'[ Collaborator URL: {spotify_convert_uri_to_url(tempuri)} ]\n\n'
5005
+ removed_track_email = f'- {f_dict["artist"]} - {f_dict["track"]} [ {get_date_from_ts(f_dict["added_at"])}, {f_dict["added_by"]} ]\n[ Spotify URL: {spotify_convert_uri_to_url(f_dict["uri"])} ]\n'
5006
+ if music_urls_text:
5007
+ for line in music_urls_text.split("\n"):
5008
+ if line:
5009
+ removed_track_email += f"[ {line} ]\n"
5010
+ if lyrics_urls_text:
5011
+ for line in lyrics_urls_text.split("\n"):
5012
+ if line:
5013
+ removed_track_email += f"[ {line} ]\n"
5014
+ removed_track_email += f'[ Collaborator URL: {spotify_convert_uri_to_url(tempuri)} ]\n\n'
5015
+ p_message_removed_tracks += removed_track_email
5016
+ print(removed_track_console, end="")
4780
5017
  try:
4781
5018
  if csv_file_name:
4782
5019
  write_csv_entry(csv_file_name, now_local_naive(), "Removed Track", p_name, f_dict["artist"] + " - " + f_dict["track"], "")
@@ -5165,16 +5402,6 @@ def main():
5165
5402
 
5166
5403
  args = parser.parse_args()
5167
5404
 
5168
- if args.export_for_spotify_monitor:
5169
- CLEAN_OUTPUT = True
5170
-
5171
- if not CLEAN_OUTPUT:
5172
- stdout_bck = sys.stdout
5173
-
5174
- clear_screen(CLEAR_SCREEN)
5175
-
5176
- print(f"Spotify Profile Monitoring Tool v{VERSION}\n")
5177
-
5178
5405
  if len(sys.argv) == 1:
5179
5406
  parser.print_help(sys.stderr)
5180
5407
  sys.exit(1)
@@ -5229,6 +5456,20 @@ def main():
5229
5456
  if val is not None:
5230
5457
  globals()[secret] = val
5231
5458
 
5459
+ if args.export_for_spotify_monitor:
5460
+ if not args.list_tracks_for_playlist and not args.list_liked_tracks:
5461
+ print(f"* Error: The 'export for spotify monitor' feature is only supported with -l and -x command line options !")
5462
+ sys.exit(2)
5463
+ else:
5464
+ CLEAN_OUTPUT = True
5465
+
5466
+ if not CLEAN_OUTPUT:
5467
+ stdout_bck = sys.stdout
5468
+
5469
+ clear_screen(CLEAR_SCREEN)
5470
+
5471
+ print(f"Spotify Profile Monitoring Tool v{VERSION}\n")
5472
+
5232
5473
  local_tz = None
5233
5474
  if LOCAL_TIMEZONE == "Auto":
5234
5475
  if get_localzone is not None: