spotify-profile-monitor 2.6__tar.gz → 2.7__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.6
3
+ Version: 2.7
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
@@ -24,13 +24,16 @@ Requires-Dist: pytz>=2020.1
24
24
  Requires-Dist: tzlocal>=4.0
25
25
  Requires-Dist: python-dotenv>=0.19
26
26
  Requires-Dist: spotipy>=2.24.0
27
+ Requires-Dist: wcwidth>=0.2.7
27
28
  Dynamic: license-file
28
29
 
29
30
  # spotify_profile_monitor
30
31
 
31
- OSINT tool for real-time monitoring of Spotify users' activities and profile changes including playlists.
32
+ OSINT tool for real-time monitoring of **Spotify users' activities and profile changes including playlists**.
32
33
 
33
- NOTE: If you want to track Spotify friends' music activity, check out another tool I developed: [spotify_monitor](https://github.com/misiektoja/spotify_monitor).
34
+ If you want to track Spotify friends' music activity, check out another tool I developed: [spotify_monitor](https://github.com/misiektoja/spotify_monitor).
35
+
36
+ 🛠️ If you're looking for debug tools to get Spotify Web Player access tokens and extract secret keys: [click here](#debugging-tools)
34
37
 
35
38
  <a id="features"></a>
36
39
  ## Features
@@ -89,14 +92,17 @@ NOTE: If you want to track Spotify friends' music activity, check out another to
89
92
  * [Check Intervals](#check-intervals)
90
93
  * [Signal Controls (macOS/Linux/Unix)](#signal-controls-macoslinuxunix)
91
94
  * [Coloring Log Output with GRC](#coloring-log-output-with-grc)
92
- 6. [Change Log](#change-log)
93
- 7. [License](#license)
95
+ 6. [Debugging Tools](#debugging-tools)
96
+ * [Access Token Retrieval via sp_dc Cookie and TOTP](#access-token-retrieval-via-sp_dc-cookie-and-totp)
97
+ * [Secret Key Extraction from Spotify Web Player Bundles](#secret-key-extraction-from-spotify-web-player-bundles)
98
+ 7. [Change Log](#change-log)
99
+ 8. [License](#license)
94
100
 
95
101
  <a id="requirements"></a>
96
102
  ## Requirements
97
103
 
98
104
  * Python 3.6 or higher
99
- * Libraries: `requests`, `python-dateutil`, `urllib3`, `pyotp`, `pytz`, `tzlocal`, `python-dotenv`, [Spotipy](https://github.com/spotipy-dev/spotipy)
105
+ * Libraries: `requests`, `python-dateutil`, `urllib3`, `pyotp`, `pytz`, `tzlocal`, `python-dotenv`, [Spotipy](https://github.com/spotipy-dev/spotipy), `wcwidth`
100
106
 
101
107
  Tested on:
102
108
 
@@ -124,7 +130,7 @@ Download the *[spotify_profile_monitor.py](https://raw.githubusercontent.com/mis
124
130
  Install dependencies via pip:
125
131
 
126
132
  ```sh
127
- pip install requests python-dateutil urllib3 pyotp pytz tzlocal python-dotenv spotipy
133
+ pip install requests python-dateutil urllib3 pyotp pytz tzlocal python-dotenv spotipy wcwidth
128
134
  ```
129
135
 
130
136
  Alternatively, from the downloaded *[requirements.txt](https://raw.githubusercontent.com/misiektoja/spotify_profile_monitor/refs/heads/main/requirements.txt)*:
@@ -241,6 +247,8 @@ If your `sp_dc` cookie expires, the tool will notify you via the console and ema
241
247
 
242
248
  If you store the `SP_DC_COOKIE` in a dotenv file you can update its value and send a `SIGHUP` signal to reload the file with the new `sp_dc` cookie without restarting the tool. More info in [Storing Secrets](#storing-secrets) and [Signal Controls (macOS/Linux/Unix)](#signal-controls-macoslinuxunix).
243
249
 
250
+ > **NOTE:** secrets used for TOTP generation (`SECRET_CIPHER_DICT`) expire every two days, that's why since v2.7 the tool fetches it from remote URL (see `SECRET_CIPHER_DICT_URL`); you can also 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 [Secret Key Extraction from Spotify Web Player Bundles](#secret-key-extraction-from-spotify-web-player-bundles) for more info).
251
+
244
252
  <a id="spotify-desktop-client"></a>
245
253
  #### Spotify Desktop Client
246
254
 
@@ -811,6 +819,76 @@ Example:
811
819
  grc tail -F -n 100 spotify_profile_monitor_<user_uri_id/file_suffix>.log
812
820
  ```
813
821
 
822
+ <a id="debugging-tools"></a>
823
+ ## Debugging Tools
824
+
825
+ To help with troubleshooting and development, two debug utilities are available in the [debug](https://github.com/misiektoja/spotify_monitor/tree/dev/debug) directory of the related [spotify_monitor](https://github.com/misiektoja/spotify_monitor) project.
826
+
827
+ <a id="access-token-retrieval-via-sp_dc-cookie-and-totp"></a>
828
+ ### Access Token Retrieval via sp_dc Cookie and TOTP
829
+
830
+ The [spotify_monitor_totp_test](https://github.com/misiektoja/spotify_monitor/blob/dev/debug/spotify_monitor_totp_test.py) tool retrieves a Spotify access token using a Web Player `sp_dc` cookie and TOTP parameters.
831
+
832
+ Download from [here](https://github.com/misiektoja/spotify_monitor/blob/dev/debug/spotify_monitor_totp_test.py) or:
833
+
834
+ ```sh
835
+ wget https://raw.githubusercontent.com/misiektoja/spotify_monitor/refs/heads/dev/debug/spotify_monitor_totp_test.py
836
+ ```
837
+
838
+ Install requirements:
839
+
840
+ ```sh
841
+ pip install requests python-dateutil pyotp
842
+ ```
843
+
844
+ Run:
845
+
846
+ ```sh
847
+ python3 spotify_monitor_totp_test.py --sp-dc "your_sp_dc_cookie_value"
848
+ ```
849
+
850
+ You should get a valid Spotify access token, example output:
851
+
852
+ <p align="center">
853
+ <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
+ </p>
855
+
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.
857
+
858
+ <a id="secret-key-extraction-from-spotify-web-player-bundles"></a>
859
+ ### Secret Key Extraction from Spotify Web Player Bundles
860
+
861
+ 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
+
863
+ Download from [here](https://github.com/misiektoja/spotify_monitor/blob/dev/debug/spotify_monitor_secret_grabber.py) or:
864
+
865
+ ```sh
866
+ wget https://raw.githubusercontent.com/misiektoja/spotify_monitor/refs/heads/dev/debug/spotify_monitor_secret_grabber.py
867
+ ```
868
+
869
+ Install requirements:
870
+
871
+ ```sh
872
+ pip install playwright
873
+ playwright install
874
+ ```
875
+
876
+ Run:
877
+
878
+ ```sh
879
+ python3 spotify_monitor_secret_grabber.py
880
+ ```
881
+
882
+ You should get output similar to below:
883
+
884
+ <p align="center">
885
+ <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
+ </p>
887
+
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`).
889
+
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`).
891
+
814
892
  <a id="change-log"></a>
815
893
  ## Change Log
816
894
 
@@ -1,8 +1,10 @@
1
1
  # spotify_profile_monitor
2
2
 
3
- OSINT tool for real-time monitoring of Spotify users' activities and profile changes including playlists.
3
+ OSINT tool for real-time monitoring of **Spotify users' activities and profile changes including playlists**.
4
4
 
5
- NOTE: If you want to track Spotify friends' music activity, check out another tool I developed: [spotify_monitor](https://github.com/misiektoja/spotify_monitor).
5
+ If you want to track Spotify friends' music activity, check out another tool I developed: [spotify_monitor](https://github.com/misiektoja/spotify_monitor).
6
+
7
+ 🛠️ If you're looking for debug tools to get Spotify Web Player access tokens and extract secret keys: [click here](#debugging-tools)
6
8
 
7
9
  <a id="features"></a>
8
10
  ## Features
@@ -61,14 +63,17 @@ NOTE: If you want to track Spotify friends' music activity, check out another to
61
63
  * [Check Intervals](#check-intervals)
62
64
  * [Signal Controls (macOS/Linux/Unix)](#signal-controls-macoslinuxunix)
63
65
  * [Coloring Log Output with GRC](#coloring-log-output-with-grc)
64
- 6. [Change Log](#change-log)
65
- 7. [License](#license)
66
+ 6. [Debugging Tools](#debugging-tools)
67
+ * [Access Token Retrieval via sp_dc Cookie and TOTP](#access-token-retrieval-via-sp_dc-cookie-and-totp)
68
+ * [Secret Key Extraction from Spotify Web Player Bundles](#secret-key-extraction-from-spotify-web-player-bundles)
69
+ 7. [Change Log](#change-log)
70
+ 8. [License](#license)
66
71
 
67
72
  <a id="requirements"></a>
68
73
  ## Requirements
69
74
 
70
75
  * Python 3.6 or higher
71
- * Libraries: `requests`, `python-dateutil`, `urllib3`, `pyotp`, `pytz`, `tzlocal`, `python-dotenv`, [Spotipy](https://github.com/spotipy-dev/spotipy)
76
+ * Libraries: `requests`, `python-dateutil`, `urllib3`, `pyotp`, `pytz`, `tzlocal`, `python-dotenv`, [Spotipy](https://github.com/spotipy-dev/spotipy), `wcwidth`
72
77
 
73
78
  Tested on:
74
79
 
@@ -96,7 +101,7 @@ Download the *[spotify_profile_monitor.py](https://raw.githubusercontent.com/mis
96
101
  Install dependencies via pip:
97
102
 
98
103
  ```sh
99
- pip install requests python-dateutil urllib3 pyotp pytz tzlocal python-dotenv spotipy
104
+ pip install requests python-dateutil urllib3 pyotp pytz tzlocal python-dotenv spotipy wcwidth
100
105
  ```
101
106
 
102
107
  Alternatively, from the downloaded *[requirements.txt](https://raw.githubusercontent.com/misiektoja/spotify_profile_monitor/refs/heads/main/requirements.txt)*:
@@ -213,6 +218,8 @@ If your `sp_dc` cookie expires, the tool will notify you via the console and ema
213
218
 
214
219
  If you store the `SP_DC_COOKIE` in a dotenv file you can update its value and send a `SIGHUP` signal to reload the file with the new `sp_dc` cookie without restarting the tool. More info in [Storing Secrets](#storing-secrets) and [Signal Controls (macOS/Linux/Unix)](#signal-controls-macoslinuxunix).
215
220
 
221
+ > **NOTE:** secrets used for TOTP generation (`SECRET_CIPHER_DICT`) expire every two days, that's why since v2.7 the tool fetches it from remote URL (see `SECRET_CIPHER_DICT_URL`); you can also 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 [Secret Key Extraction from Spotify Web Player Bundles](#secret-key-extraction-from-spotify-web-player-bundles) for more info).
222
+
216
223
  <a id="spotify-desktop-client"></a>
217
224
  #### Spotify Desktop Client
218
225
 
@@ -783,6 +790,76 @@ Example:
783
790
  grc tail -F -n 100 spotify_profile_monitor_<user_uri_id/file_suffix>.log
784
791
  ```
785
792
 
793
+ <a id="debugging-tools"></a>
794
+ ## Debugging Tools
795
+
796
+ To help with troubleshooting and development, two debug utilities are available in the [debug](https://github.com/misiektoja/spotify_monitor/tree/dev/debug) directory of the related [spotify_monitor](https://github.com/misiektoja/spotify_monitor) project.
797
+
798
+ <a id="access-token-retrieval-via-sp_dc-cookie-and-totp"></a>
799
+ ### Access Token Retrieval via sp_dc Cookie and TOTP
800
+
801
+ The [spotify_monitor_totp_test](https://github.com/misiektoja/spotify_monitor/blob/dev/debug/spotify_monitor_totp_test.py) tool retrieves a Spotify access token using a Web Player `sp_dc` cookie and TOTP parameters.
802
+
803
+ Download from [here](https://github.com/misiektoja/spotify_monitor/blob/dev/debug/spotify_monitor_totp_test.py) or:
804
+
805
+ ```sh
806
+ wget https://raw.githubusercontent.com/misiektoja/spotify_monitor/refs/heads/dev/debug/spotify_monitor_totp_test.py
807
+ ```
808
+
809
+ Install requirements:
810
+
811
+ ```sh
812
+ pip install requests python-dateutil pyotp
813
+ ```
814
+
815
+ Run:
816
+
817
+ ```sh
818
+ python3 spotify_monitor_totp_test.py --sp-dc "your_sp_dc_cookie_value"
819
+ ```
820
+
821
+ You should get a valid Spotify access token, example output:
822
+
823
+ <p align="center">
824
+ <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
+ </p>
826
+
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.
828
+
829
+ <a id="secret-key-extraction-from-spotify-web-player-bundles"></a>
830
+ ### Secret Key Extraction from Spotify Web Player Bundles
831
+
832
+ 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
+
834
+ Download from [here](https://github.com/misiektoja/spotify_monitor/blob/dev/debug/spotify_monitor_secret_grabber.py) or:
835
+
836
+ ```sh
837
+ wget https://raw.githubusercontent.com/misiektoja/spotify_monitor/refs/heads/dev/debug/spotify_monitor_secret_grabber.py
838
+ ```
839
+
840
+ Install requirements:
841
+
842
+ ```sh
843
+ pip install playwright
844
+ playwright install
845
+ ```
846
+
847
+ Run:
848
+
849
+ ```sh
850
+ python3 spotify_monitor_secret_grabber.py
851
+ ```
852
+
853
+ You should get output similar to below:
854
+
855
+ <p align="center">
856
+ <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
+ </p>
858
+
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`).
860
+
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`).
862
+
786
863
  <a id="change-log"></a>
787
864
  ## Change Log
788
865
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "spotify_profile_monitor"
7
- version = "2.6"
7
+ version = "2.7"
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"
@@ -20,6 +20,7 @@ dependencies = [
20
20
  "tzlocal>=4.0",
21
21
  "python-dotenv>=0.19",
22
22
  "spotipy>=2.24.0",
23
+ "wcwidth>=0.2.7",
23
24
  ]
24
25
  classifiers = [
25
26
  "Programming Language :: Python :: 3",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: spotify_profile_monitor
3
- Version: 2.6
3
+ Version: 2.7
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
@@ -24,13 +24,16 @@ Requires-Dist: pytz>=2020.1
24
24
  Requires-Dist: tzlocal>=4.0
25
25
  Requires-Dist: python-dotenv>=0.19
26
26
  Requires-Dist: spotipy>=2.24.0
27
+ Requires-Dist: wcwidth>=0.2.7
27
28
  Dynamic: license-file
28
29
 
29
30
  # spotify_profile_monitor
30
31
 
31
- OSINT tool for real-time monitoring of Spotify users' activities and profile changes including playlists.
32
+ OSINT tool for real-time monitoring of **Spotify users' activities and profile changes including playlists**.
32
33
 
33
- NOTE: If you want to track Spotify friends' music activity, check out another tool I developed: [spotify_monitor](https://github.com/misiektoja/spotify_monitor).
34
+ If you want to track Spotify friends' music activity, check out another tool I developed: [spotify_monitor](https://github.com/misiektoja/spotify_monitor).
35
+
36
+ 🛠️ If you're looking for debug tools to get Spotify Web Player access tokens and extract secret keys: [click here](#debugging-tools)
34
37
 
35
38
  <a id="features"></a>
36
39
  ## Features
@@ -89,14 +92,17 @@ NOTE: If you want to track Spotify friends' music activity, check out another to
89
92
  * [Check Intervals](#check-intervals)
90
93
  * [Signal Controls (macOS/Linux/Unix)](#signal-controls-macoslinuxunix)
91
94
  * [Coloring Log Output with GRC](#coloring-log-output-with-grc)
92
- 6. [Change Log](#change-log)
93
- 7. [License](#license)
95
+ 6. [Debugging Tools](#debugging-tools)
96
+ * [Access Token Retrieval via sp_dc Cookie and TOTP](#access-token-retrieval-via-sp_dc-cookie-and-totp)
97
+ * [Secret Key Extraction from Spotify Web Player Bundles](#secret-key-extraction-from-spotify-web-player-bundles)
98
+ 7. [Change Log](#change-log)
99
+ 8. [License](#license)
94
100
 
95
101
  <a id="requirements"></a>
96
102
  ## Requirements
97
103
 
98
104
  * Python 3.6 or higher
99
- * Libraries: `requests`, `python-dateutil`, `urllib3`, `pyotp`, `pytz`, `tzlocal`, `python-dotenv`, [Spotipy](https://github.com/spotipy-dev/spotipy)
105
+ * Libraries: `requests`, `python-dateutil`, `urllib3`, `pyotp`, `pytz`, `tzlocal`, `python-dotenv`, [Spotipy](https://github.com/spotipy-dev/spotipy), `wcwidth`
100
106
 
101
107
  Tested on:
102
108
 
@@ -124,7 +130,7 @@ Download the *[spotify_profile_monitor.py](https://raw.githubusercontent.com/mis
124
130
  Install dependencies via pip:
125
131
 
126
132
  ```sh
127
- pip install requests python-dateutil urllib3 pyotp pytz tzlocal python-dotenv spotipy
133
+ pip install requests python-dateutil urllib3 pyotp pytz tzlocal python-dotenv spotipy wcwidth
128
134
  ```
129
135
 
130
136
  Alternatively, from the downloaded *[requirements.txt](https://raw.githubusercontent.com/misiektoja/spotify_profile_monitor/refs/heads/main/requirements.txt)*:
@@ -241,6 +247,8 @@ If your `sp_dc` cookie expires, the tool will notify you via the console and ema
241
247
 
242
248
  If you store the `SP_DC_COOKIE` in a dotenv file you can update its value and send a `SIGHUP` signal to reload the file with the new `sp_dc` cookie without restarting the tool. More info in [Storing Secrets](#storing-secrets) and [Signal Controls (macOS/Linux/Unix)](#signal-controls-macoslinuxunix).
243
249
 
250
+ > **NOTE:** secrets used for TOTP generation (`SECRET_CIPHER_DICT`) expire every two days, that's why since v2.7 the tool fetches it from remote URL (see `SECRET_CIPHER_DICT_URL`); you can also 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 [Secret Key Extraction from Spotify Web Player Bundles](#secret-key-extraction-from-spotify-web-player-bundles) for more info).
251
+
244
252
  <a id="spotify-desktop-client"></a>
245
253
  #### Spotify Desktop Client
246
254
 
@@ -811,6 +819,76 @@ Example:
811
819
  grc tail -F -n 100 spotify_profile_monitor_<user_uri_id/file_suffix>.log
812
820
  ```
813
821
 
822
+ <a id="debugging-tools"></a>
823
+ ## Debugging Tools
824
+
825
+ To help with troubleshooting and development, two debug utilities are available in the [debug](https://github.com/misiektoja/spotify_monitor/tree/dev/debug) directory of the related [spotify_monitor](https://github.com/misiektoja/spotify_monitor) project.
826
+
827
+ <a id="access-token-retrieval-via-sp_dc-cookie-and-totp"></a>
828
+ ### Access Token Retrieval via sp_dc Cookie and TOTP
829
+
830
+ The [spotify_monitor_totp_test](https://github.com/misiektoja/spotify_monitor/blob/dev/debug/spotify_monitor_totp_test.py) tool retrieves a Spotify access token using a Web Player `sp_dc` cookie and TOTP parameters.
831
+
832
+ Download from [here](https://github.com/misiektoja/spotify_monitor/blob/dev/debug/spotify_monitor_totp_test.py) or:
833
+
834
+ ```sh
835
+ wget https://raw.githubusercontent.com/misiektoja/spotify_monitor/refs/heads/dev/debug/spotify_monitor_totp_test.py
836
+ ```
837
+
838
+ Install requirements:
839
+
840
+ ```sh
841
+ pip install requests python-dateutil pyotp
842
+ ```
843
+
844
+ Run:
845
+
846
+ ```sh
847
+ python3 spotify_monitor_totp_test.py --sp-dc "your_sp_dc_cookie_value"
848
+ ```
849
+
850
+ You should get a valid Spotify access token, example output:
851
+
852
+ <p align="center">
853
+ <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
+ </p>
855
+
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.
857
+
858
+ <a id="secret-key-extraction-from-spotify-web-player-bundles"></a>
859
+ ### Secret Key Extraction from Spotify Web Player Bundles
860
+
861
+ 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
+
863
+ Download from [here](https://github.com/misiektoja/spotify_monitor/blob/dev/debug/spotify_monitor_secret_grabber.py) or:
864
+
865
+ ```sh
866
+ wget https://raw.githubusercontent.com/misiektoja/spotify_monitor/refs/heads/dev/debug/spotify_monitor_secret_grabber.py
867
+ ```
868
+
869
+ Install requirements:
870
+
871
+ ```sh
872
+ pip install playwright
873
+ playwright install
874
+ ```
875
+
876
+ Run:
877
+
878
+ ```sh
879
+ python3 spotify_monitor_secret_grabber.py
880
+ ```
881
+
882
+ You should get output similar to below:
883
+
884
+ <p align="center">
885
+ <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
+ </p>
887
+
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`).
889
+
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`).
891
+
814
892
  <a id="change-log"></a>
815
893
  ## Change Log
816
894
 
@@ -6,3 +6,4 @@ pytz>=2020.1
6
6
  tzlocal>=4.0
7
7
  python-dotenv>=0.19
8
8
  spotipy>=2.24.0
9
+ wcwidth>=0.2.7
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env python3
2
2
  """
3
3
  Author: Michal Szymanski <misiektoja-github@rm-rf.ninja>
4
- v2.6
4
+ v2.7
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/
@@ -16,9 +16,10 @@ pytz
16
16
  tzlocal (optional)
17
17
  python-dotenv (optional)
18
18
  spotipy (optional, needed when the token source is set to oauth_app)
19
+ wcwidth (optional, needed by TRUNCATE_CHARS feature)
19
20
  """
20
21
 
21
- VERSION = "2.6"
22
+ VERSION = "2.7"
22
23
 
23
24
  # ---------------------------
24
25
  # CONFIGURATION SECTION START
@@ -246,14 +247,38 @@ TRUNCATE_CHARS = 0
246
247
  # Value used by signal handlers to increase or decrease profile check interval (SPOTIFY_CHECK_INTERVAL); in seconds
247
248
  SPOTIFY_CHECK_SIGNAL_VALUE = 300 # 5 minutes
248
249
 
250
+ # ---------------------------------------------------------------------
251
+
252
+ # The section below is used when the token source is set to 'cookie'
253
+
249
254
  # Maximum number of attempts to get a valid access token in a single run of the spotify_get_access_token_from_sp_dc() function
250
- # Used only when the token source is set to 'cookie'
251
- TOKEN_MAX_RETRIES = 10
255
+ TOKEN_MAX_RETRIES = 3
252
256
 
253
257
  # Interval between access token retry attempts; in seconds
254
- # Used only when the token source is set to 'cookie'
255
258
  TOKEN_RETRY_TIMEOUT = 0.5 # 0.5 second
256
259
 
260
+ # Mapping of TOTP version identifiers to the secrets needed for TOTP generation
261
+ # Newest secrets are downloaded automatically from SECRET_CIPHER_DICT_URL (see below)
262
+ # Can also be fetched via spotify_monitor_secret_grabber.py utility - see debug dir
263
+ 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
+ "6": [21, 24, 85, 46, 48, 35, 33, 8, 11, 63, 76, 12, 55, 77, 14, 7, 54],
271
+ "5": [12, 56, 76, 33, 88, 44, 88, 33, 78, 78, 11, 66, 22, 22, 55, 69, 54],
272
+ }
273
+
274
+ # Remote URL used to fetch updated secrets needed for TOTP generation
275
+ # Set to empty string to disable
276
+ SECRET_CIPHER_DICT_URL = "https://github.com/Thereallo1026/spotify-secrets/blob/main/secrets/secretDict.json?raw=true"
277
+
278
+ # Identifier used to select the appropriate secret from SECRET_CIPHER_DICT when generating a TOTP token
279
+ # Set to 0 to auto-select the highest available version
280
+ TOTP_VER = 0
281
+
257
282
  # ---------------------------------------------------------------------
258
283
 
259
284
  # The section below is used when the token source is set to 'oauth_app'
@@ -521,6 +546,9 @@ CLEAR_SCREEN = False
521
546
  SPOTIFY_CHECK_SIGNAL_VALUE = 0
522
547
  TOKEN_MAX_RETRIES = 0
523
548
  TOKEN_RETRY_TIMEOUT = 0.0
549
+ SECRET_CIPHER_DICT = {}
550
+ SECRET_CIPHER_DICT_URL = ""
551
+ TOTP_VER = 0
524
552
  TRUNCATE_CHARS = 0
525
553
 
526
554
  exec(CONFIG_BLOCK, globals())
@@ -554,9 +582,6 @@ TOKEN_URL = "https://open.spotify.com/api/token"
554
582
  # URL of the endpoint to get server time needed to create TOTP object
555
583
  SERVER_TIME_URL = "https://open.spotify.com/"
556
584
 
557
- # Identifier used to select the appropriate encrypted secret from secret_cipher_dict when generating a TOTP token
558
- TOTP_VER = 10
559
-
560
585
  # Variables for caching functionality of the Spotify client token to avoid unnecessary refreshing
561
586
  SP_CACHED_CLIENT_TOKEN = None
562
587
  SP_CLIENT_TOKEN_EXPIRES_AT = 0
@@ -661,14 +686,30 @@ SESSION.mount("http://", adapter)
661
686
 
662
687
 
663
688
  # Truncates each line of a string to a specified number of characters including tab expansion and multi-line support
664
- def truncate_string_per_line(message, truncate_chars, tabsize=8):
689
+ def truncate_string_per_line(message, truncate_width, tabsize=8):
690
+ try:
691
+ from wcwidth import wcwidth
692
+ except ImportError:
693
+ return message
694
+
665
695
  lines = message.split('\n')
666
696
  truncated_lines = []
667
697
 
668
698
  for line in lines:
669
- expanded_line = line.expandtabs(tabsize=tabsize)
670
- truncated_line = expanded_line[:truncate_chars]
671
- truncated_lines.append(truncated_line)
699
+ expanded_line = line.expandtabs(tabsize)
700
+ current_width = 0
701
+ truncated = ''
702
+
703
+ for char in expanded_line:
704
+ char_width = wcwidth(char)
705
+ if char_width < 0:
706
+ char_width = 0 # Non-printable or unknown width
707
+ if current_width + char_width > truncate_width:
708
+ break
709
+ truncated += char
710
+ current_width += char_width
711
+
712
+ truncated_lines.append(truncated)
672
713
 
673
714
  return '\n'.join(truncated_lines)
674
715
 
@@ -1437,15 +1478,10 @@ def fetch_server_time(session: req.Session, ua: str) -> int:
1437
1478
  def generate_totp():
1438
1479
  import pyotp
1439
1480
 
1440
- secret_cipher_dict = {
1441
- "10": [61, 110, 58, 98, 35, 79, 117, 69, 102, 72, 92, 102, 69, 93, 41, 101, 42, 75],
1442
- "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],
1443
- "8": [37, 84, 32, 76, 87, 90, 87, 47, 13, 75, 48, 54, 44, 28, 19, 21, 22],
1444
- "7": [59, 91, 66, 74, 30, 66, 74, 38, 46, 50, 72, 61, 44, 71, 86, 39, 89],
1445
- "6": [21, 24, 85, 46, 48, 35, 33, 8, 11, 63, 76, 12, 55, 77, 14, 7, 54],
1446
- "5": [12, 56, 76, 33, 88, 44, 88, 33, 78, 78, 11, 66, 22, 22, 55, 69, 54],
1447
- }
1448
- secret_cipher_bytes = secret_cipher_dict[str(TOTP_VER)]
1481
+ if str((ver := TOTP_VER or max(map(int, SECRET_CIPHER_DICT)))) not in SECRET_CIPHER_DICT:
1482
+ raise Exception(f"generate_totp(): Defined TOTP_VER ({ver}) is missing in SECRET_CIPHER_DICT")
1483
+
1484
+ secret_cipher_bytes = SECRET_CIPHER_DICT[str(ver)]
1449
1485
 
1450
1486
  transformed = [e ^ ((t % 33) + 9) for t, e in enumerate(secret_cipher_bytes)]
1451
1487
  joined = "".join(str(num) for num in transformed)
@@ -1455,6 +1491,34 @@ def generate_totp():
1455
1491
  return pyotp.TOTP(secret, digits=6, interval=30)
1456
1492
 
1457
1493
 
1494
+ def fetch_and_update_secrets():
1495
+ global SECRET_CIPHER_DICT
1496
+
1497
+ if not SECRET_CIPHER_DICT_URL:
1498
+ return False
1499
+
1500
+ try:
1501
+ response = req.get(SECRET_CIPHER_DICT_URL, timeout=FUNCTION_TIMEOUT, verify=VERIFY_SSL)
1502
+ response.raise_for_status()
1503
+ secrets = response.json()
1504
+
1505
+ if not isinstance(secrets, dict) or not secrets:
1506
+ raise ValueError("fetch_and_update_secrets(): Fetched payload not a non‑empty dict")
1507
+
1508
+ for key, value in secrets.items():
1509
+ if not isinstance(key, str) or not key.isdigit():
1510
+ raise ValueError(f"fetch_and_update_secrets(): Invalid key format: {key}")
1511
+ if not isinstance(value, list) or not all(isinstance(x, int) for x in value):
1512
+ raise ValueError(f"fetch_and_update_secrets(): Invalid value format for key {key}")
1513
+
1514
+ SECRET_CIPHER_DICT = secrets
1515
+ return True
1516
+
1517
+ except Exception as e:
1518
+ print(f"fetch_and_update_secrets(): Failed to get new secrets: {e}")
1519
+ return False
1520
+
1521
+
1458
1522
  # Refreshes the Spotify access token using the sp_dc cookie, tries first with mode "transport" and if needed with "init"
1459
1523
  def refresh_access_token_from_sp_dc(sp_dc: str) -> dict:
1460
1524
  transport = True
@@ -1554,29 +1618,52 @@ def spotify_get_access_token_from_sp_dc(sp_dc: str):
1554
1618
  max_retries = TOKEN_MAX_RETRIES
1555
1619
  retry = 0
1556
1620
 
1557
- while retry < max_retries:
1558
- token_data = refresh_access_token_from_sp_dc(sp_dc)
1559
- token = token_data["access_token"]
1560
- client_id = token_data.get("client_id", "")
1561
- length = token_data["length"]
1621
+ last_error = ""
1562
1622
 
1563
- SP_CACHED_ACCESS_TOKEN = token
1564
- SP_ACCESS_TOKEN_EXPIRES_AT = token_data["expires_at"]
1565
- SP_CACHED_CLIENT_ID = client_id
1566
-
1567
- if SP_CACHED_ACCESS_TOKEN is None or not check_token_validity(SP_CACHED_ACCESS_TOKEN, SP_CACHED_CLIENT_ID, USER_AGENT):
1623
+ while retry < max_retries:
1624
+ try:
1625
+ token_data = refresh_access_token_from_sp_dc(sp_dc)
1626
+ token = token_data["access_token"]
1627
+ client_id = token_data.get("client_id", "")
1628
+ length = token_data["length"]
1629
+
1630
+ SP_CACHED_ACCESS_TOKEN = token
1631
+ SP_ACCESS_TOKEN_EXPIRES_AT = token_data["expires_at"]
1632
+ SP_CACHED_CLIENT_ID = client_id
1633
+
1634
+ if SP_CACHED_ACCESS_TOKEN is None or not check_token_validity(SP_CACHED_ACCESS_TOKEN, SP_CACHED_CLIENT_ID, USER_AGENT):
1635
+ retry += 1
1636
+ time.sleep(TOKEN_RETRY_TIMEOUT)
1637
+ else:
1638
+ break
1639
+ except Exception as e:
1640
+ last_error = str(e)
1568
1641
  retry += 1
1569
- time.sleep(TOKEN_RETRY_TIMEOUT)
1570
- else:
1571
- break
1642
+ if retry < max_retries:
1643
+ time.sleep(TOKEN_RETRY_TIMEOUT)
1572
1644
 
1573
1645
  if retry == max_retries:
1574
- if SP_CACHED_ACCESS_TOKEN is not None:
1575
- print(f"* Token appears to be still invalid after {max_retries} attempts, returning token anyway")
1576
- print_cur_ts("Timestamp:\t\t\t")
1577
- return SP_CACHED_ACCESS_TOKEN
1578
- else:
1579
- raise RuntimeError(f"Failed to obtain a valid Spotify access token after {max_retries} attempts")
1646
+
1647
+ if fetch_and_update_secrets():
1648
+ try:
1649
+ token_data = refresh_access_token_from_sp_dc(sp_dc)
1650
+ token = token_data["access_token"]
1651
+ client_id = token_data.get("client_id", "")
1652
+ length = token_data["length"]
1653
+
1654
+ SP_CACHED_ACCESS_TOKEN = token
1655
+ SP_ACCESS_TOKEN_EXPIRES_AT = token_data["expires_at"]
1656
+ SP_CACHED_CLIENT_ID = client_id
1657
+
1658
+ if SP_CACHED_ACCESS_TOKEN and check_token_validity(SP_CACHED_ACCESS_TOKEN, SP_CACHED_CLIENT_ID, USER_AGENT):
1659
+ return SP_CACHED_ACCESS_TOKEN
1660
+ except Exception as e:
1661
+ last_error = str(e)
1662
+
1663
+ error_msg = f"Failed to obtain a valid Spotify access token after {max_retries} attempts"
1664
+ if last_error:
1665
+ error_msg += f": {last_error}"
1666
+ raise RuntimeError(error_msg)
1580
1667
 
1581
1668
  return SP_CACHED_ACCESS_TOKEN
1582
1669