spotify-profile-monitor 2.5.3__tar.gz → 2.6__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.
- {spotify_profile_monitor-2.5.3 → spotify_profile_monitor-2.6}/PKG-INFO +28 -6
- {spotify_profile_monitor-2.5.3 → spotify_profile_monitor-2.6}/README.md +27 -5
- {spotify_profile_monitor-2.5.3 → spotify_profile_monitor-2.6}/pyproject.toml +1 -1
- {spotify_profile_monitor-2.5.3 → spotify_profile_monitor-2.6}/spotify_profile_monitor.egg-info/PKG-INFO +28 -6
- {spotify_profile_monitor-2.5.3 → spotify_profile_monitor-2.6}/spotify_profile_monitor.py +94 -15
- {spotify_profile_monitor-2.5.3 → spotify_profile_monitor-2.6}/LICENSE +0 -0
- {spotify_profile_monitor-2.5.3 → spotify_profile_monitor-2.6}/setup.cfg +0 -0
- {spotify_profile_monitor-2.5.3 → spotify_profile_monitor-2.6}/spotify_profile_monitor.egg-info/SOURCES.txt +0 -0
- {spotify_profile_monitor-2.5.3 → spotify_profile_monitor-2.6}/spotify_profile_monitor.egg-info/dependency_links.txt +0 -0
- {spotify_profile_monitor-2.5.3 → spotify_profile_monitor-2.6}/spotify_profile_monitor.egg-info/entry_points.txt +0 -0
- {spotify_profile_monitor-2.5.3 → spotify_profile_monitor-2.6}/spotify_profile_monitor.egg-info/requires.txt +0 -0
- {spotify_profile_monitor-2.5.3 → spotify_profile_monitor-2.6}/spotify_profile_monitor.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: spotify_profile_monitor
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.6
|
|
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
|
|
@@ -246,14 +246,23 @@ If you store the `SP_DC_COOKIE` in a dotenv file you can update its value and se
|
|
|
246
246
|
|
|
247
247
|
This is the alternative method used to obtain a Spotify access token which simulates a login from the real Spotify desktop app using credentials intercepted from a real session.
|
|
248
248
|
|
|
249
|
-
|
|
249
|
+
- Run an intercepting proxy of your choice (like [Proxyman](https://proxyman.com) - the trial version is sufficient)
|
|
250
250
|
|
|
251
|
-
-
|
|
251
|
+
- Enable SSL traffic decryption for `spotify.com` domain
|
|
252
|
+
- in Proxyman: click **Tools → SSL Proxying List → + button → Add Domain → paste `*.spotify.com` → Add**
|
|
252
253
|
|
|
253
|
-
- Launch the Spotify desktop client and look for POST requests to `https://
|
|
254
|
-
- Note: The `login` part is suffixed with one or more digits (e.g. `login5`).
|
|
254
|
+
- Launch the Spotify desktop client, then switch to your intercepting proxy (like Proxyman) and look for POST requests to `https://login5.spotify.com/v3/login`
|
|
255
255
|
|
|
256
|
-
- If you don't see this request,
|
|
256
|
+
- If you don't see this request, try following steps (stop once it works):
|
|
257
|
+
- restart the Spotify desktop client
|
|
258
|
+
- log out from the Spotify desktop client and log back in
|
|
259
|
+
- point Spotify at the intercepting proxy directly in its settings, i.e. in **Spotify → Settings → Proxy Settings**, set:
|
|
260
|
+
- **proxy type**: `HTTP`
|
|
261
|
+
- **host**: `127.0.0.1` (IP/FQDN of your proxy, for Proxyman use the IP you see at the top bar)
|
|
262
|
+
- **port**: `9090` (port of your proxy, for Proxyman use the port you see at the top bar)
|
|
263
|
+
- restart the app; since QUIC (HTTP/3) requires raw UDP and can't tunnel over HTTP CONNECT, Spotify will downgrade to TCP-only HTTP/2 or 1.1, which intercepting proxy can decrypt
|
|
264
|
+
- block Spotify's UDP port 443 at the OS level with a firewall of your choice - this prevents QUIC (HTTP/3), forcing TLS over TCP and letting intercepting proxy perform MITM
|
|
265
|
+
- try an older version of the Spotify desktop client
|
|
257
266
|
|
|
258
267
|
- Export the login request body (a binary Protobuf payload) to a file (e.g. ***login-request-body-file***)
|
|
259
268
|
- In Proxyman: **right click the request → Export → Request Body → Save File**.
|
|
@@ -514,6 +523,19 @@ spotify_profile_monitor <spotify_user_uri_id> -k
|
|
|
514
523
|
|
|
515
524
|
It is helpful in the case of playlists created by another user added to another user profile.
|
|
516
525
|
|
|
526
|
+
Some users don't list all their public playlists on their profile, but if you know a playlist's URI, you can still monitor it.
|
|
527
|
+
|
|
528
|
+
To do so, add entries to the `ADD_PLAYLISTS_TO_MONITOR` configuration option. Example:
|
|
529
|
+
|
|
530
|
+
```python
|
|
531
|
+
ADD_PLAYLISTS_TO_MONITOR = [
|
|
532
|
+
{'uri': 'spotify:playlist:{playlist_id1}', 'owner_name': '{user_id}', 'owner_uri': 'spotify:user:{user_id}'},
|
|
533
|
+
{'uri': 'spotify:playlist:{playlist_id2}', 'owner_name': '{user_id}', 'owner_uri': 'spotify:user:{user_id}'}
|
|
534
|
+
]
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
Replace `{playlist_id1}` and `{playlist_id2}` with the playlists URI IDs you want to monitor and `{user_id}` with the owner's URI ID (`spotify_user_uri_id`).
|
|
538
|
+
|
|
517
539
|
If you want to completely disable detection of changes in user's public playlists (like added/removed tracks in playlists, playlists name and description changes, number of likes for playlists):
|
|
518
540
|
- set `DETECT_CHANGES_IN_PLAYLISTS` to `False`
|
|
519
541
|
- or use the `-q` flag
|
|
@@ -218,14 +218,23 @@ If you store the `SP_DC_COOKIE` in a dotenv file you can update its value and se
|
|
|
218
218
|
|
|
219
219
|
This is the alternative method used to obtain a Spotify access token which simulates a login from the real Spotify desktop app using credentials intercepted from a real session.
|
|
220
220
|
|
|
221
|
-
|
|
221
|
+
- Run an intercepting proxy of your choice (like [Proxyman](https://proxyman.com) - the trial version is sufficient)
|
|
222
222
|
|
|
223
|
-
-
|
|
223
|
+
- Enable SSL traffic decryption for `spotify.com` domain
|
|
224
|
+
- in Proxyman: click **Tools → SSL Proxying List → + button → Add Domain → paste `*.spotify.com` → Add**
|
|
224
225
|
|
|
225
|
-
- Launch the Spotify desktop client and look for POST requests to `https://
|
|
226
|
-
- Note: The `login` part is suffixed with one or more digits (e.g. `login5`).
|
|
226
|
+
- Launch the Spotify desktop client, then switch to your intercepting proxy (like Proxyman) and look for POST requests to `https://login5.spotify.com/v3/login`
|
|
227
227
|
|
|
228
|
-
- If you don't see this request,
|
|
228
|
+
- If you don't see this request, try following steps (stop once it works):
|
|
229
|
+
- restart the Spotify desktop client
|
|
230
|
+
- log out from the Spotify desktop client and log back in
|
|
231
|
+
- point Spotify at the intercepting proxy directly in its settings, i.e. in **Spotify → Settings → Proxy Settings**, set:
|
|
232
|
+
- **proxy type**: `HTTP`
|
|
233
|
+
- **host**: `127.0.0.1` (IP/FQDN of your proxy, for Proxyman use the IP you see at the top bar)
|
|
234
|
+
- **port**: `9090` (port of your proxy, for Proxyman use the port you see at the top bar)
|
|
235
|
+
- restart the app; since QUIC (HTTP/3) requires raw UDP and can't tunnel over HTTP CONNECT, Spotify will downgrade to TCP-only HTTP/2 or 1.1, which intercepting proxy can decrypt
|
|
236
|
+
- block Spotify's UDP port 443 at the OS level with a firewall of your choice - this prevents QUIC (HTTP/3), forcing TLS over TCP and letting intercepting proxy perform MITM
|
|
237
|
+
- try an older version of the Spotify desktop client
|
|
229
238
|
|
|
230
239
|
- Export the login request body (a binary Protobuf payload) to a file (e.g. ***login-request-body-file***)
|
|
231
240
|
- In Proxyman: **right click the request → Export → Request Body → Save File**.
|
|
@@ -486,6 +495,19 @@ spotify_profile_monitor <spotify_user_uri_id> -k
|
|
|
486
495
|
|
|
487
496
|
It is helpful in the case of playlists created by another user added to another user profile.
|
|
488
497
|
|
|
498
|
+
Some users don't list all their public playlists on their profile, but if you know a playlist's URI, you can still monitor it.
|
|
499
|
+
|
|
500
|
+
To do so, add entries to the `ADD_PLAYLISTS_TO_MONITOR` configuration option. Example:
|
|
501
|
+
|
|
502
|
+
```python
|
|
503
|
+
ADD_PLAYLISTS_TO_MONITOR = [
|
|
504
|
+
{'uri': 'spotify:playlist:{playlist_id1}', 'owner_name': '{user_id}', 'owner_uri': 'spotify:user:{user_id}'},
|
|
505
|
+
{'uri': 'spotify:playlist:{playlist_id2}', 'owner_name': '{user_id}', 'owner_uri': 'spotify:user:{user_id}'}
|
|
506
|
+
]
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
Replace `{playlist_id1}` and `{playlist_id2}` with the playlists URI IDs you want to monitor and `{user_id}` with the owner's URI ID (`spotify_user_uri_id`).
|
|
510
|
+
|
|
489
511
|
If you want to completely disable detection of changes in user's public playlists (like added/removed tracks in playlists, playlists name and description changes, number of likes for playlists):
|
|
490
512
|
- set `DETECT_CHANGES_IN_PLAYLISTS` to `False`
|
|
491
513
|
- or use the `-q` flag
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "spotify_profile_monitor"
|
|
7
|
-
version = "2.
|
|
7
|
+
version = "2.6"
|
|
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.
|
|
3
|
+
Version: 2.6
|
|
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
|
|
@@ -246,14 +246,23 @@ If you store the `SP_DC_COOKIE` in a dotenv file you can update its value and se
|
|
|
246
246
|
|
|
247
247
|
This is the alternative method used to obtain a Spotify access token which simulates a login from the real Spotify desktop app using credentials intercepted from a real session.
|
|
248
248
|
|
|
249
|
-
|
|
249
|
+
- Run an intercepting proxy of your choice (like [Proxyman](https://proxyman.com) - the trial version is sufficient)
|
|
250
250
|
|
|
251
|
-
-
|
|
251
|
+
- Enable SSL traffic decryption for `spotify.com` domain
|
|
252
|
+
- in Proxyman: click **Tools → SSL Proxying List → + button → Add Domain → paste `*.spotify.com` → Add**
|
|
252
253
|
|
|
253
|
-
- Launch the Spotify desktop client and look for POST requests to `https://
|
|
254
|
-
- Note: The `login` part is suffixed with one or more digits (e.g. `login5`).
|
|
254
|
+
- Launch the Spotify desktop client, then switch to your intercepting proxy (like Proxyman) and look for POST requests to `https://login5.spotify.com/v3/login`
|
|
255
255
|
|
|
256
|
-
- If you don't see this request,
|
|
256
|
+
- If you don't see this request, try following steps (stop once it works):
|
|
257
|
+
- restart the Spotify desktop client
|
|
258
|
+
- log out from the Spotify desktop client and log back in
|
|
259
|
+
- point Spotify at the intercepting proxy directly in its settings, i.e. in **Spotify → Settings → Proxy Settings**, set:
|
|
260
|
+
- **proxy type**: `HTTP`
|
|
261
|
+
- **host**: `127.0.0.1` (IP/FQDN of your proxy, for Proxyman use the IP you see at the top bar)
|
|
262
|
+
- **port**: `9090` (port of your proxy, for Proxyman use the port you see at the top bar)
|
|
263
|
+
- restart the app; since QUIC (HTTP/3) requires raw UDP and can't tunnel over HTTP CONNECT, Spotify will downgrade to TCP-only HTTP/2 or 1.1, which intercepting proxy can decrypt
|
|
264
|
+
- block Spotify's UDP port 443 at the OS level with a firewall of your choice - this prevents QUIC (HTTP/3), forcing TLS over TCP and letting intercepting proxy perform MITM
|
|
265
|
+
- try an older version of the Spotify desktop client
|
|
257
266
|
|
|
258
267
|
- Export the login request body (a binary Protobuf payload) to a file (e.g. ***login-request-body-file***)
|
|
259
268
|
- In Proxyman: **right click the request → Export → Request Body → Save File**.
|
|
@@ -514,6 +523,19 @@ spotify_profile_monitor <spotify_user_uri_id> -k
|
|
|
514
523
|
|
|
515
524
|
It is helpful in the case of playlists created by another user added to another user profile.
|
|
516
525
|
|
|
526
|
+
Some users don't list all their public playlists on their profile, but if you know a playlist's URI, you can still monitor it.
|
|
527
|
+
|
|
528
|
+
To do so, add entries to the `ADD_PLAYLISTS_TO_MONITOR` configuration option. Example:
|
|
529
|
+
|
|
530
|
+
```python
|
|
531
|
+
ADD_PLAYLISTS_TO_MONITOR = [
|
|
532
|
+
{'uri': 'spotify:playlist:{playlist_id1}', 'owner_name': '{user_id}', 'owner_uri': 'spotify:user:{user_id}'},
|
|
533
|
+
{'uri': 'spotify:playlist:{playlist_id2}', 'owner_name': '{user_id}', 'owner_uri': 'spotify:user:{user_id}'}
|
|
534
|
+
]
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
Replace `{playlist_id1}` and `{playlist_id2}` with the playlists URI IDs you want to monitor and `{user_id}` with the owner's URI ID (`spotify_user_uri_id`).
|
|
538
|
+
|
|
517
539
|
If you want to completely disable detection of changes in user's public playlists (like added/removed tracks in playlists, playlists name and description changes, number of likes for playlists):
|
|
518
540
|
- set `DETECT_CHANGES_IN_PLAYLISTS` to `False`
|
|
519
541
|
- or use the `-q` flag
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
"""
|
|
3
3
|
Author: Michal Szymanski <misiektoja-github@rm-rf.ninja>
|
|
4
|
-
v2.
|
|
4
|
+
v2.6
|
|
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/
|
|
@@ -18,7 +18,7 @@ python-dotenv (optional)
|
|
|
18
18
|
spotipy (optional, needed when the token source is set to oauth_app)
|
|
19
19
|
"""
|
|
20
20
|
|
|
21
|
-
VERSION = "2.
|
|
21
|
+
VERSION = "2.6"
|
|
22
22
|
|
|
23
23
|
# ---------------------------
|
|
24
24
|
# CONFIGURATION SECTION START
|
|
@@ -137,6 +137,17 @@ DETECT_CHANGES_IN_PLAYLISTS = True
|
|
|
137
137
|
# Can also be enabled via the -k flag
|
|
138
138
|
GET_ALL_PLAYLISTS = False
|
|
139
139
|
|
|
140
|
+
# Some users don't list all their public playlists on their profile, but if you know a playlist's URI, you can still monitor it
|
|
141
|
+
#
|
|
142
|
+
# Example:
|
|
143
|
+
#
|
|
144
|
+
# ADD_PLAYLISTS_TO_MONITOR = [
|
|
145
|
+
# {'uri': 'spotify:playlist:{playlist_id1}', 'owner_name': '{user_id}', 'owner_uri': 'spotify:user:{user_id}'},
|
|
146
|
+
# {'uri': 'spotify:playlist:{playlist_id2}', 'owner_name': '{user_id}', 'owner_uri': 'spotify:user:{user_id}'}
|
|
147
|
+
# ]
|
|
148
|
+
# Replace {playlist_id1} and {playlist_id2} with the playlists URI IDs you want to monitor and {user_id} with the owner's URI ID
|
|
149
|
+
ADD_PLAYLISTS_TO_MONITOR = []
|
|
150
|
+
|
|
140
151
|
# Ignore Spotify-owned playlists when monitoring?
|
|
141
152
|
# Set to True to avoid tracking Spotify-generated playlists that often change frequently (likes, tracks etc.)
|
|
142
153
|
IGNORE_SPOTIFY_PLAYLISTS = True
|
|
@@ -225,6 +236,13 @@ HORIZONTAL_LINE = 113
|
|
|
225
236
|
# Whether to clear the terminal screen after starting the tool
|
|
226
237
|
CLEAR_SCREEN = True
|
|
227
238
|
|
|
239
|
+
# Max characters per line when printing to screen to avoid line wrapping
|
|
240
|
+
# Does not affect log file output
|
|
241
|
+
# Set to 999 to auto-detect terminal width
|
|
242
|
+
# Applies only when DISABLE_LOGGING is False
|
|
243
|
+
# Can also be set via the --truncate flag
|
|
244
|
+
TRUNCATE_CHARS = 0
|
|
245
|
+
|
|
228
246
|
# Value used by signal handlers to increase or decrease profile check interval (SPOTIFY_CHECK_INTERVAL); in seconds
|
|
229
247
|
SPOTIFY_CHECK_SIGNAL_VALUE = 300 # 5 minutes
|
|
230
248
|
|
|
@@ -478,6 +496,7 @@ IMGCAT_PATH = ""
|
|
|
478
496
|
SP_SHA256 = ""
|
|
479
497
|
DETECT_CHANGES_IN_PLAYLISTS = False
|
|
480
498
|
GET_ALL_PLAYLISTS = False
|
|
499
|
+
ADD_PLAYLISTS_TO_MONITOR = []
|
|
481
500
|
IGNORE_SPOTIFY_PLAYLISTS = False
|
|
482
501
|
PLAYLISTS_LIMIT = 0
|
|
483
502
|
RECENTLY_PLAYED_ARTISTS_LIMIT = 0
|
|
@@ -502,6 +521,7 @@ CLEAR_SCREEN = False
|
|
|
502
521
|
SPOTIFY_CHECK_SIGNAL_VALUE = 0
|
|
503
522
|
TOKEN_MAX_RETRIES = 0
|
|
504
523
|
TOKEN_RETRY_TIMEOUT = 0.0
|
|
524
|
+
TRUNCATE_CHARS = 0
|
|
505
525
|
|
|
506
526
|
exec(CONFIG_BLOCK, globals())
|
|
507
527
|
|
|
@@ -531,6 +551,12 @@ SP_CACHED_CLIENT_ID = ""
|
|
|
531
551
|
# URL of the Spotify Web Player endpoint to get access token
|
|
532
552
|
TOKEN_URL = "https://open.spotify.com/api/token"
|
|
533
553
|
|
|
554
|
+
# URL of the endpoint to get server time needed to create TOTP object
|
|
555
|
+
SERVER_TIME_URL = "https://open.spotify.com/"
|
|
556
|
+
|
|
557
|
+
# Identifier used to select the appropriate encrypted secret from secret_cipher_dict when generating a TOTP token
|
|
558
|
+
TOTP_VER = 10
|
|
559
|
+
|
|
534
560
|
# Variables for caching functionality of the Spotify client token to avoid unnecessary refreshing
|
|
535
561
|
SP_CACHED_CLIENT_TOKEN = None
|
|
536
562
|
SP_CLIENT_TOKEN_EXPIRES_AT = 0
|
|
@@ -634,6 +660,19 @@ SESSION.mount("https://", adapter)
|
|
|
634
660
|
SESSION.mount("http://", adapter)
|
|
635
661
|
|
|
636
662
|
|
|
663
|
+
# 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):
|
|
665
|
+
lines = message.split('\n')
|
|
666
|
+
truncated_lines = []
|
|
667
|
+
|
|
668
|
+
for line in lines:
|
|
669
|
+
expanded_line = line.expandtabs(tabsize=tabsize)
|
|
670
|
+
truncated_line = expanded_line[:truncate_chars]
|
|
671
|
+
truncated_lines.append(truncated_line)
|
|
672
|
+
|
|
673
|
+
return '\n'.join(truncated_lines)
|
|
674
|
+
|
|
675
|
+
|
|
637
676
|
# Logger class to output messages to stdout and log file
|
|
638
677
|
class Logger(object):
|
|
639
678
|
def __init__(self, filename):
|
|
@@ -641,8 +680,10 @@ class Logger(object):
|
|
|
641
680
|
self.logfile = open(filename, "a", buffering=1, encoding="utf-8")
|
|
642
681
|
|
|
643
682
|
def write(self, message):
|
|
644
|
-
self.terminal.write(message)
|
|
645
683
|
self.logfile.write(message)
|
|
684
|
+
if (TRUNCATE_CHARS):
|
|
685
|
+
message = truncate_string_per_line(message, TRUNCATE_CHARS)
|
|
686
|
+
self.terminal.write(message)
|
|
646
687
|
self.terminal.flush()
|
|
647
688
|
self.logfile.flush()
|
|
648
689
|
|
|
@@ -1375,7 +1416,7 @@ def fetch_server_time(session: req.Session, ua: str) -> int:
|
|
|
1375
1416
|
if platform.system() != 'Windows':
|
|
1376
1417
|
signal.signal(signal.SIGALRM, timeout_handler)
|
|
1377
1418
|
signal.alarm(FUNCTION_TIMEOUT + 2)
|
|
1378
|
-
response = session.head(
|
|
1419
|
+
response = session.head(SERVER_TIME_URL, headers=headers, timeout=FUNCTION_TIMEOUT, verify=VERIFY_SSL)
|
|
1379
1420
|
response.raise_for_status()
|
|
1380
1421
|
except TimeoutException as e:
|
|
1381
1422
|
raise Exception(f"fetch_server_time() head network request timeout after {display_time(FUNCTION_TIMEOUT + 2)}: {e}")
|
|
@@ -1385,7 +1426,11 @@ def fetch_server_time(session: req.Session, ua: str) -> int:
|
|
|
1385
1426
|
if platform.system() != 'Windows':
|
|
1386
1427
|
signal.alarm(0)
|
|
1387
1428
|
|
|
1388
|
-
|
|
1429
|
+
date_hdr = response.headers.get("Date")
|
|
1430
|
+
if not date_hdr:
|
|
1431
|
+
raise Exception("fetch_server_time() missing 'Date' header")
|
|
1432
|
+
|
|
1433
|
+
return int(parsedate_to_datetime(date_hdr).timestamp())
|
|
1389
1434
|
|
|
1390
1435
|
|
|
1391
1436
|
# Creates a TOTP object using a secret derived from transformed cipher bytes
|
|
@@ -1393,12 +1438,14 @@ def generate_totp():
|
|
|
1393
1438
|
import pyotp
|
|
1394
1439
|
|
|
1395
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],
|
|
1396
1443
|
"8": [37, 84, 32, 76, 87, 90, 87, 47, 13, 75, 48, 54, 44, 28, 19, 21, 22],
|
|
1397
1444
|
"7": [59, 91, 66, 74, 30, 66, 74, 38, 46, 50, 72, 61, 44, 71, 86, 39, 89],
|
|
1398
1445
|
"6": [21, 24, 85, 46, 48, 35, 33, 8, 11, 63, 76, 12, 55, 77, 14, 7, 54],
|
|
1399
|
-
"5": [12, 56, 76, 33, 88, 44, 88, 33, 78, 78, 11, 66, 22, 22, 55, 69, 54]
|
|
1446
|
+
"5": [12, 56, 76, 33, 88, 44, 88, 33, 78, 78, 11, 66, 22, 22, 55, 69, 54],
|
|
1400
1447
|
}
|
|
1401
|
-
secret_cipher_bytes = secret_cipher_dict[
|
|
1448
|
+
secret_cipher_bytes = secret_cipher_dict[str(TOTP_VER)]
|
|
1402
1449
|
|
|
1403
1450
|
transformed = [e ^ ((t % 33) + 9) for t, e in enumerate(secret_cipher_bytes)]
|
|
1404
1451
|
joined = "".join(str(num) for num in transformed)
|
|
@@ -1413,7 +1460,6 @@ def refresh_access_token_from_sp_dc(sp_dc: str) -> dict:
|
|
|
1413
1460
|
transport = True
|
|
1414
1461
|
init = True
|
|
1415
1462
|
session = req.Session()
|
|
1416
|
-
session.cookies.set("sp_dc", sp_dc)
|
|
1417
1463
|
data: dict = {}
|
|
1418
1464
|
token = ""
|
|
1419
1465
|
|
|
@@ -1427,13 +1473,17 @@ def refresh_access_token_from_sp_dc(sp_dc: str) -> dict:
|
|
|
1427
1473
|
"productType": "web-player",
|
|
1428
1474
|
"totp": otp_value,
|
|
1429
1475
|
"totpServer": otp_value,
|
|
1430
|
-
"totpVer":
|
|
1431
|
-
"sTime": server_time,
|
|
1432
|
-
"cTime": client_time,
|
|
1433
|
-
"buildDate": time.strftime("%Y-%m-%d", time.gmtime(server_time)),
|
|
1434
|
-
"buildVer": f"web-player_{time.strftime('%Y-%m-%d', time.gmtime(server_time))}_{server_time * 1000}_{secrets.token_hex(4)}",
|
|
1476
|
+
"totpVer": TOTP_VER,
|
|
1435
1477
|
}
|
|
1436
1478
|
|
|
1479
|
+
if TOTP_VER < 10:
|
|
1480
|
+
params.update({
|
|
1481
|
+
"sTime": server_time,
|
|
1482
|
+
"cTime": client_time,
|
|
1483
|
+
"buildDate": time.strftime("%Y-%m-%d", time.gmtime(server_time)),
|
|
1484
|
+
"buildVer": f"web-player_{time.strftime('%Y-%m-%d', time.gmtime(server_time))}_{server_time * 1000}_{secrets.token_hex(4)}",
|
|
1485
|
+
})
|
|
1486
|
+
|
|
1437
1487
|
headers = {
|
|
1438
1488
|
"User-Agent": USER_AGENT,
|
|
1439
1489
|
"Accept": "application/json",
|
|
@@ -3828,6 +3878,10 @@ def spotify_profile_monitor_uri(user_uri_id, csv_file_name, playlists_to_skip):
|
|
|
3828
3878
|
playlists_count = sp_user_data["sp_user_public_playlists_count"]
|
|
3829
3879
|
playlists = sp_user_data["sp_user_public_playlists_uris"]
|
|
3830
3880
|
|
|
3881
|
+
if ADD_PLAYLISTS_TO_MONITOR:
|
|
3882
|
+
playlists.extend(ADD_PLAYLISTS_TO_MONITOR)
|
|
3883
|
+
playlists_count += len(ADD_PLAYLISTS_TO_MONITOR)
|
|
3884
|
+
|
|
3831
3885
|
recently_played_artists = sp_user_data["sp_user_recently_played_artists"]
|
|
3832
3886
|
|
|
3833
3887
|
print(f"Username:\t\t\t{username}")
|
|
@@ -4220,6 +4274,10 @@ def spotify_profile_monitor_uri(user_uri_id, csv_file_name, playlists_to_skip):
|
|
|
4220
4274
|
playlists_count = sp_user_data["sp_user_public_playlists_count"]
|
|
4221
4275
|
playlists = sp_user_data["sp_user_public_playlists_uris"]
|
|
4222
4276
|
|
|
4277
|
+
if ADD_PLAYLISTS_TO_MONITOR:
|
|
4278
|
+
playlists.extend(ADD_PLAYLISTS_TO_MONITOR)
|
|
4279
|
+
playlists_count += len(ADD_PLAYLISTS_TO_MONITOR)
|
|
4280
|
+
|
|
4223
4281
|
recently_played_artists = sp_user_data["sp_user_recently_played_artists"]
|
|
4224
4282
|
|
|
4225
4283
|
if followers_count != followers_old_count:
|
|
@@ -4745,7 +4803,7 @@ def spotify_profile_monitor_uri(user_uri_id, csv_file_name, playlists_to_skip):
|
|
|
4745
4803
|
|
|
4746
4804
|
|
|
4747
4805
|
def main():
|
|
4748
|
-
global CLI_CONFIG_PATH, DOTENV_FILE, LOCAL_TIMEZONE, LIVENESS_CHECK_COUNTER, SP_DC_COOKIE, SP_APP_CLIENT_ID, SP_APP_CLIENT_SECRET, SP_USER_CLIENT_ID, SP_USER_CLIENT_SECRET, LOGIN_REQUEST_BODY_FILE, CLIENTTOKEN_REQUEST_BODY_FILE, REFRESH_TOKEN, LOGIN_URL, USER_AGENT, DEVICE_ID, SYSTEM_ID, USER_URI_ID, CSV_FILE, PLAYLISTS_TO_SKIP_FILE, FILE_SUFFIX, DISABLE_LOGGING, SP_LOGFILE, PROFILE_NOTIFICATION, SPOTIFY_CHECK_INTERVAL, SPOTIFY_ERROR_INTERVAL, FOLLOWERS_FOLLOWINGS_NOTIFICATION, ERROR_NOTIFICATION, DETECT_CHANGED_PROFILE_PIC, DETECT_CHANGES_IN_PLAYLISTS, GET_ALL_PLAYLISTS, imgcat_exe, SMTP_PASSWORD, SP_SHA256, stdout_bck, APP_VERSION, CPU_ARCH, OS_BUILD, PLATFORM, OS_MAJOR, OS_MINOR, CLIENT_MODEL, TOKEN_SOURCE, ALARM_TIMEOUT, pyotp, CLEAN_OUTPUT, USER_AGENT, SP_APP_TOKENS_FILE, SP_USER_TOKENS_FILE
|
|
4806
|
+
global CLI_CONFIG_PATH, DOTENV_FILE, LOCAL_TIMEZONE, LIVENESS_CHECK_COUNTER, SP_DC_COOKIE, SP_APP_CLIENT_ID, SP_APP_CLIENT_SECRET, SP_USER_CLIENT_ID, SP_USER_CLIENT_SECRET, LOGIN_REQUEST_BODY_FILE, CLIENTTOKEN_REQUEST_BODY_FILE, REFRESH_TOKEN, LOGIN_URL, USER_AGENT, DEVICE_ID, SYSTEM_ID, USER_URI_ID, CSV_FILE, PLAYLISTS_TO_SKIP_FILE, FILE_SUFFIX, DISABLE_LOGGING, SP_LOGFILE, PROFILE_NOTIFICATION, SPOTIFY_CHECK_INTERVAL, SPOTIFY_ERROR_INTERVAL, FOLLOWERS_FOLLOWINGS_NOTIFICATION, ERROR_NOTIFICATION, DETECT_CHANGED_PROFILE_PIC, DETECT_CHANGES_IN_PLAYLISTS, GET_ALL_PLAYLISTS, imgcat_exe, SMTP_PASSWORD, SP_SHA256, stdout_bck, APP_VERSION, CPU_ARCH, OS_BUILD, PLATFORM, OS_MAJOR, OS_MINOR, CLIENT_MODEL, TOKEN_SOURCE, ALARM_TIMEOUT, pyotp, CLEAN_OUTPUT, USER_AGENT, SP_APP_TOKENS_FILE, SP_USER_TOKENS_FILE, TRUNCATE_CHARS
|
|
4749
4807
|
|
|
4750
4808
|
if "--generate-config" in sys.argv:
|
|
4751
4809
|
print(CONFIG_BLOCK.strip("\n"))
|
|
@@ -5010,6 +5068,13 @@ def main():
|
|
|
5010
5068
|
default=None,
|
|
5011
5069
|
help="Disable logging to spotify_profile_monitor_<user_uri_id/file_suffix>.log"
|
|
5012
5070
|
)
|
|
5071
|
+
opts.add_argument(
|
|
5072
|
+
"--truncate",
|
|
5073
|
+
dest="truncate",
|
|
5074
|
+
metavar="N",
|
|
5075
|
+
type=int,
|
|
5076
|
+
help="Max characters per screen line (not log), use 999 to auto-detect terminal width, ignored if -d is set"
|
|
5077
|
+
)
|
|
5013
5078
|
|
|
5014
5079
|
args = parser.parse_args()
|
|
5015
5080
|
|
|
@@ -5522,6 +5587,18 @@ def main():
|
|
|
5522
5587
|
if not FILE_SUFFIX:
|
|
5523
5588
|
FILE_SUFFIX = str(args.user_id)
|
|
5524
5589
|
|
|
5590
|
+
if args.truncate:
|
|
5591
|
+
if args.truncate != 999:
|
|
5592
|
+
TRUNCATE_CHARS = args.truncate
|
|
5593
|
+
else:
|
|
5594
|
+
try:
|
|
5595
|
+
terminal_size = shutil.get_terminal_size()
|
|
5596
|
+
print(f"The detected terminal screen width is: {terminal_size.columns} characters\n")
|
|
5597
|
+
TRUNCATE_CHARS = terminal_size.columns
|
|
5598
|
+
except Exception as e:
|
|
5599
|
+
print(f"Error: Cannot determine terminal screen width: {e}")
|
|
5600
|
+
sys.exit(1)
|
|
5601
|
+
|
|
5525
5602
|
if args.disable_logging is True:
|
|
5526
5603
|
DISABLE_LOGGING = True
|
|
5527
5604
|
|
|
@@ -5557,7 +5634,7 @@ def main():
|
|
|
5557
5634
|
ERROR_NOTIFICATION = False
|
|
5558
5635
|
|
|
5559
5636
|
print(f"* Spotify polling intervals:\t[check: {display_time(SPOTIFY_CHECK_INTERVAL)}] [error: {display_time(SPOTIFY_ERROR_INTERVAL)}]")
|
|
5560
|
-
print(f"* Email notifications:\t\t[profile changes = {PROFILE_NOTIFICATION}] [followers/followings = {FOLLOWERS_FOLLOWINGS_NOTIFICATION}]\n
|
|
5637
|
+
print(f"* Email notifications:\t\t[profile changes = {PROFILE_NOTIFICATION}] [followers/followings = {FOLLOWERS_FOLLOWINGS_NOTIFICATION}]\n*\t\t\t\t[errors = {ERROR_NOTIFICATION}]")
|
|
5561
5638
|
print(f"* Token source:\t\t\t{TOKEN_SOURCE}")
|
|
5562
5639
|
print(f"* Profile pic changes:\t\t{DETECT_CHANGED_PROFILE_PIC}")
|
|
5563
5640
|
print(f"* Playlist changes:\t\t{DETECT_CHANGES_IN_PLAYLISTS}")
|
|
@@ -5569,6 +5646,8 @@ def main():
|
|
|
5569
5646
|
print(f"* Ignore listed playlists:\t{bool(PLAYLISTS_TO_SKIP_FILE)}" + (f" ({PLAYLISTS_TO_SKIP_FILE})" if PLAYLISTS_TO_SKIP_FILE else ""))
|
|
5570
5647
|
print(f"* Display profile pics:\t\t{bool(imgcat_exe)}" + (f" (via {imgcat_exe})" if imgcat_exe else ""))
|
|
5571
5648
|
print(f"* Output logging enabled:\t{not DISABLE_LOGGING}" + (f" ({FINAL_LOG_PATH})" if not DISABLE_LOGGING else ""))
|
|
5649
|
+
if not DISABLE_LOGGING and TRUNCATE_CHARS > 0:
|
|
5650
|
+
print(f"* Truncate terminal lines:\t{TRUNCATE_CHARS} chars")
|
|
5572
5651
|
if TOKEN_SOURCE in ('oauth_user', 'oauth_app'):
|
|
5573
5652
|
print(f"* Spotify token cache file:\t{({'oauth_app': SP_APP_TOKENS_FILE, 'oauth_user': SP_USER_TOKENS_FILE}.get(TOKEN_SOURCE) or 'None (memory only)')}")
|
|
5574
5653
|
print(f"* Configuration file:\t\t{cfg_path}")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|