spotify-monitor 2.3.1__tar.gz → 2.5__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.

Potentially problematic release.


This version of spotify-monitor might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: spotify_monitor
3
- Version: 2.3.1
3
+ Version: 2.5
4
4
  Summary: Tool implementing real-time tracking of Spotify friends music activity
5
5
  Author-email: Michal Szymanski <misiektoja-pypi@rm-rf.ninja>
6
6
  License-Expression: GPL-3.0-or-later
@@ -26,7 +26,7 @@ Dynamic: license-file
26
26
 
27
27
  # spotify_monitor
28
28
 
29
- Tool for real-time monitoring of Spotify friends' music activity feed.
29
+ Tool for real-time monitoring of **Spotify friends' music activity feed**.
30
30
 
31
31
  ✨ If you're interested in tracking changes to Spotify users' profiles including their playlists, take a look at another tool I've developed: [spotify_profile_monitor](https://github.com/misiektoja/spotify_profile_monitor).
32
32
 
@@ -77,8 +77,11 @@ Tool for real-time monitoring of Spotify friends' music activity feed.
77
77
  * [Signal Controls (macOS/Linux/Unix)](#signal-controls-macoslinuxunix)
78
78
  * [Coloring Log Output with GRC](#coloring-log-output-with-grc)
79
79
  6. [Debugging Tools](#debugging-tools)
80
+ * [Access Token Retrieval via sp_dc Cookie and TOTP](#access-token-retrieval-via-sp_dc-cookie-and-totp)
81
+ * [Secret Key Extraction from Spotify Web Player Bundles](#secret-key-extraction-from-spotify-web-player-bundles)
80
82
  7. [Change Log](#change-log)
81
- 8. [License](#license)
83
+ 8. [Maintainers](#maintainers)
84
+ 9. [License](#license)
82
85
 
83
86
  <a id="requirements"></a>
84
87
  ## Requirements
@@ -88,8 +91,8 @@ Tool for real-time monitoring of Spotify friends' music activity feed.
88
91
 
89
92
  Tested on:
90
93
 
91
- * **macOS**: Ventura, Sonoma, Sequoia
92
- * **Linux**: Raspberry Pi OS (Bullseye, Bookworm), Ubuntu 24, Rocky Linux 8.x/9.x, Kali Linux 2024/2025
94
+ * **macOS**: Ventura, Sonoma, Sequoia, Tahoe
95
+ * **Linux**: Raspberry Pi OS (Bullseye, Bookworm, Trixie), Ubuntu 24/25, Rocky Linux 8.x/9.x, Kali Linux 2024/2025
93
96
  * **Windows**: 10, 11
94
97
 
95
98
  It should work on other versions of macOS, Linux, Unix and Windows as well.
@@ -112,7 +115,7 @@ Download the *[spotify_monitor.py](https://raw.githubusercontent.com/misiektoja/
112
115
  Install dependencies via pip:
113
116
 
114
117
  ```sh
115
- pip install requests python-dateutil urllib3 pyotp python-dotenv
118
+ pip install requests python-dateutil urllib3 pyotp python-dotenv wcwidth
116
119
  ```
117
120
 
118
121
  Alternatively, from the downloaded *[requirements.txt](https://raw.githubusercontent.com/misiektoja/spotify_monitor/refs/heads/main/requirements.txt)*:
@@ -199,7 +202,7 @@ If your `sp_dc` cookie expires, the tool will notify you via the console and ema
199
202
 
200
203
  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).
201
204
 
202
- `Note`: encrypted byte sequences used for TOTP secret generation tend to expire every now and then; you can either check the [issues](https://github.com/misiektoja/spotify_monitor/issues) section of the project to see if there are any new secrets published or you can run the [spotify_monitor_secret_grabber.py](https://github.com/misiektoja/spotify_monitor/blob/dev/debug/spotify_monitor_secret_grabber.py) and extract it by yourself (see [Debugging Tools](https://github.com/misiektoja/spotify_monitor#debugging-tools) for more info).
205
+ > **NOTE:** secrets used for TOTP generation (`SECRET_CIPHER_DICT`) expire every two days, that's why since v2.4 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).
203
206
 
204
207
  <a id="spotify-desktop-client"></a>
205
208
  #### Spotify Desktop Client
@@ -572,32 +575,165 @@ grc tail -F -n 100 spotify_monitor_<user_uri_id/file_suffix>.log
572
575
  <a id="debugging-tools"></a>
573
576
  ## Debugging Tools
574
577
 
575
- To help with troubleshooting and development, two debug utilities are available in the `debug` directory:
578
+ To help with troubleshooting and development, two debug utilities are available in the [debug](https://github.com/misiektoja/spotify_monitor/tree/dev/debug) directory.
576
579
 
577
- - [spotify_monitor_totp_test.py](https://github.com/misiektoja/spotify_monitor/blob/dev/debug/spotify_monitor_totp_test.py): fetching of Spotify access token based on a Spotify Web Player `sp_dc` cookie value:
580
+ <a id="access-token-retrieval-via-sp_dc-cookie-and-totp"></a>
581
+ ### Access Token Retrieval via sp_dc Cookie and TOTP
582
+
583
+ 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.
584
+
585
+ Download from [here](https://github.com/misiektoja/spotify_monitor/blob/dev/debug/spotify_monitor_totp_test.py) or:
586
+
587
+ ```sh
588
+ wget https://raw.githubusercontent.com/misiektoja/spotify_monitor/refs/heads/dev/debug/spotify_monitor_totp_test.py
589
+ ```
590
+
591
+ Install requirements:
578
592
 
579
593
  ```sh
580
594
  pip install requests python-dateutil pyotp
595
+ ```
596
+
597
+ Run:
598
+
599
+ ```sh
581
600
  python3 spotify_monitor_totp_test.py --sp-dc "your_sp_dc_cookie_value"
582
601
  ```
583
602
 
584
- - [spotify_monitor_secret_grabber.py](https://github.com/misiektoja/spotify_monitor/blob/dev/debug/spotify_monitor_secret_grabber.py): automatic extractor for secret keys used for TOTP generation in Spotify Web Player JavaScript bundles:
603
+ You should get a valid Spotify access token, example output:
604
+
605
+ <p align="center">
606
+ <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%"/>
607
+ </p>
608
+
609
+ > **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).
610
+
611
+ <a id="secret-key-extraction-from-spotify-web-player-bundles"></a>
612
+ ### Secret Key Extraction from Spotify Web Player Bundles
613
+
614
+ 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.
615
+
616
+ > 💡 **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).
617
+
618
+ Download from [here](https://github.com/misiektoja/spotify_monitor/blob/dev/debug/spotify_monitor_secret_grabber.py) or:
619
+
620
+ ```sh
621
+ wget https://raw.githubusercontent.com/misiektoja/spotify_monitor/refs/heads/dev/debug/spotify_monitor_secret_grabber.py
622
+ ```
623
+
624
+ Install requirements:
585
625
 
586
626
  ```sh
587
627
  pip install playwright
588
628
  playwright install
629
+ ```
630
+
631
+ Run interactively (default output mode):
632
+
633
+ ```sh
589
634
  python3 spotify_monitor_secret_grabber.py
590
635
  ```
591
636
 
637
+ You should get output similar to below:
638
+
592
639
  <p align="center">
593
640
  <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%"/>
594
641
  </p>
595
642
 
643
+ Show help:
644
+ ```sh
645
+ python3 spotify_monitor_secret_grabber.py -h
646
+ ```
647
+
648
+ ---
649
+
650
+ <a id="cli-output-modes"></a>
651
+ ### CLI Output Modes
652
+
653
+ The script supports several output modes for different use cases:
654
+
655
+ | Flag | Description | Output |
656
+ |------|--------------|--------|
657
+ | `--secret` | Prints plain JSON array of extracted secrets | `[{"version": X, "secret": "..."}, ...]` |
658
+ | `--secretbytes` | Prints JSON array with ASCII byte values | `[{"version": X, "secret": [..]}, ...]` |
659
+ | `--secretdict` | Prints JSON object/dict mapping version → byte list | `{"X": [..], "Y": [..]}` |
660
+ | `--all` | Extracts secrets and **writes all three outputs** to local files | `secrets.json`, `secretBytes.json`, `secretDict.json` |
661
+
662
+ Print extracted secrets in specific format, for example Python-friendly secret bytes (JSON object/dict) and save to indicated file:
663
+
664
+ ```sh
665
+ python3 spotify_monitor_secret_grabber.py --secretdict > secretDict.json
666
+ ```
667
+
668
+ Or, to generate and save all secret formats to files (`secrets.json`, `secretBytes.json`, `secretDict.json`) at once:
669
+
670
+ ```sh
671
+ python3 spotify_monitor_secret_grabber.py --all
672
+ ```
673
+
674
+ Default file paths and names can be configured directly in the `OUTPUT_FILES` dictionary at the top of the script.
675
+
676
+ ---
677
+
678
+ <a id="-secret-key-extraction-via-docker-recommended-easiest-way"></a>
679
+ ### 🐳 Secret Key Extraction via Docker (Recommended Easiest Way)
680
+
681
+ A prebuilt multi-architecture image is available on Docker Hub: [`misiektoja/spotify-secrets-grabber`](https://hub.docker.com/r/misiektoja/spotify-secrets-grabber)
682
+
683
+ This image works on:
684
+ - macOS (Intel & Apple Silicon)
685
+ - Linux (x86_64 and ARM64)
686
+ - Windows (Docker Desktop / WSL2)
687
+ - Raspberry Pi 4/5 (64-bit OS)
688
+
689
+ Run interactively (default output mode):
690
+
691
+ ```sh
692
+ docker run --rm misiektoja/spotify-secrets-grabber
693
+ ```
694
+
695
+ Show help:
696
+ ```sh
697
+ docker run --rm misiektoja/spotify-secrets-grabber -h
698
+ ```
699
+
700
+ Print extracted secrets in specific format, for example Python-friendly secret bytes (JSON object/dict) and save to indicated file:
701
+ ```sh
702
+ docker run --rm misiektoja/spotify-secrets-grabber --secretdict > secretDict.json
703
+ ```
704
+
705
+ Or, to generate and save all secret formats to files (`secrets.json`, `secretBytes.json`, `secretDict.json`) at once:
706
+
707
+ ```sh
708
+ docker run --rm -v .:/work -w /work misiektoja/spotify-secrets-grabber --all
709
+ ```
710
+
711
+ *For SELinux hosts (Fedora/RHEL), use `-v .:/work:Z`.*
712
+
713
+ <a id="optional-use-docker-compose-one-command-for-all-oss"></a>
714
+ 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):
715
+
716
+ ```sh
717
+ docker compose run --rm spotify-secrets-grabber --all
718
+ ```
719
+
720
+ This will save all files into your current directory on any system (macOS, Linux or Windows).
721
+
722
+ ---
723
+
724
+ 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.
725
+
596
726
  <a id="change-log"></a>
597
727
  ## Change Log
598
728
 
599
729
  See [RELEASE_NOTES.md](https://github.com/misiektoja/spotify_monitor/blob/main/RELEASE_NOTES.md) for details.
600
730
 
731
+ <a id="maintainers"></a>
732
+ ## Maintainers
733
+
734
+ [![Maintainer: misiektoja](https://img.shields.io/badge/maintainer-misiektoja-blue)](https://github.com/misiektoja)
735
+ [![Maintainer: tomballgithub](https://img.shields.io/badge/maintainer-tomballgithub-blue)](https://github.com/tomballgithub)
736
+
601
737
  <a id="license"></a>
602
738
  ## License
603
739
 
@@ -1,6 +1,6 @@
1
1
  # spotify_monitor
2
2
 
3
- Tool for real-time monitoring of Spotify friends' music activity feed.
3
+ Tool for real-time monitoring of **Spotify friends' music activity feed**.
4
4
 
5
5
  ✨ If you're interested in tracking changes to Spotify users' profiles including their playlists, take a look at another tool I've developed: [spotify_profile_monitor](https://github.com/misiektoja/spotify_profile_monitor).
6
6
 
@@ -51,8 +51,11 @@ Tool for real-time monitoring of Spotify friends' music activity feed.
51
51
  * [Signal Controls (macOS/Linux/Unix)](#signal-controls-macoslinuxunix)
52
52
  * [Coloring Log Output with GRC](#coloring-log-output-with-grc)
53
53
  6. [Debugging Tools](#debugging-tools)
54
+ * [Access Token Retrieval via sp_dc Cookie and TOTP](#access-token-retrieval-via-sp_dc-cookie-and-totp)
55
+ * [Secret Key Extraction from Spotify Web Player Bundles](#secret-key-extraction-from-spotify-web-player-bundles)
54
56
  7. [Change Log](#change-log)
55
- 8. [License](#license)
57
+ 8. [Maintainers](#maintainers)
58
+ 9. [License](#license)
56
59
 
57
60
  <a id="requirements"></a>
58
61
  ## Requirements
@@ -62,8 +65,8 @@ Tool for real-time monitoring of Spotify friends' music activity feed.
62
65
 
63
66
  Tested on:
64
67
 
65
- * **macOS**: Ventura, Sonoma, Sequoia
66
- * **Linux**: Raspberry Pi OS (Bullseye, Bookworm), Ubuntu 24, Rocky Linux 8.x/9.x, Kali Linux 2024/2025
68
+ * **macOS**: Ventura, Sonoma, Sequoia, Tahoe
69
+ * **Linux**: Raspberry Pi OS (Bullseye, Bookworm, Trixie), Ubuntu 24/25, Rocky Linux 8.x/9.x, Kali Linux 2024/2025
67
70
  * **Windows**: 10, 11
68
71
 
69
72
  It should work on other versions of macOS, Linux, Unix and Windows as well.
@@ -86,7 +89,7 @@ Download the *[spotify_monitor.py](https://raw.githubusercontent.com/misiektoja/
86
89
  Install dependencies via pip:
87
90
 
88
91
  ```sh
89
- pip install requests python-dateutil urllib3 pyotp python-dotenv
92
+ pip install requests python-dateutil urllib3 pyotp python-dotenv wcwidth
90
93
  ```
91
94
 
92
95
  Alternatively, from the downloaded *[requirements.txt](https://raw.githubusercontent.com/misiektoja/spotify_monitor/refs/heads/main/requirements.txt)*:
@@ -173,7 +176,7 @@ If your `sp_dc` cookie expires, the tool will notify you via the console and ema
173
176
 
174
177
  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).
175
178
 
176
- `Note`: encrypted byte sequences used for TOTP secret generation tend to expire every now and then; you can either check the [issues](https://github.com/misiektoja/spotify_monitor/issues) section of the project to see if there are any new secrets published or you can run the [spotify_monitor_secret_grabber.py](https://github.com/misiektoja/spotify_monitor/blob/dev/debug/spotify_monitor_secret_grabber.py) and extract it by yourself (see [Debugging Tools](https://github.com/misiektoja/spotify_monitor#debugging-tools) for more info).
179
+ > **NOTE:** secrets used for TOTP generation (`SECRET_CIPHER_DICT`) expire every two days, that's why since v2.4 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).
177
180
 
178
181
  <a id="spotify-desktop-client"></a>
179
182
  #### Spotify Desktop Client
@@ -546,32 +549,165 @@ grc tail -F -n 100 spotify_monitor_<user_uri_id/file_suffix>.log
546
549
  <a id="debugging-tools"></a>
547
550
  ## Debugging Tools
548
551
 
549
- To help with troubleshooting and development, two debug utilities are available in the `debug` directory:
552
+ To help with troubleshooting and development, two debug utilities are available in the [debug](https://github.com/misiektoja/spotify_monitor/tree/dev/debug) directory.
550
553
 
551
- - [spotify_monitor_totp_test.py](https://github.com/misiektoja/spotify_monitor/blob/dev/debug/spotify_monitor_totp_test.py): fetching of Spotify access token based on a Spotify Web Player `sp_dc` cookie value:
554
+ <a id="access-token-retrieval-via-sp_dc-cookie-and-totp"></a>
555
+ ### Access Token Retrieval via sp_dc Cookie and TOTP
556
+
557
+ 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.
558
+
559
+ Download from [here](https://github.com/misiektoja/spotify_monitor/blob/dev/debug/spotify_monitor_totp_test.py) or:
560
+
561
+ ```sh
562
+ wget https://raw.githubusercontent.com/misiektoja/spotify_monitor/refs/heads/dev/debug/spotify_monitor_totp_test.py
563
+ ```
564
+
565
+ Install requirements:
552
566
 
553
567
  ```sh
554
568
  pip install requests python-dateutil pyotp
569
+ ```
570
+
571
+ Run:
572
+
573
+ ```sh
555
574
  python3 spotify_monitor_totp_test.py --sp-dc "your_sp_dc_cookie_value"
556
575
  ```
557
576
 
558
- - [spotify_monitor_secret_grabber.py](https://github.com/misiektoja/spotify_monitor/blob/dev/debug/spotify_monitor_secret_grabber.py): automatic extractor for secret keys used for TOTP generation in Spotify Web Player JavaScript bundles:
577
+ You should get a valid Spotify access token, example output:
578
+
579
+ <p align="center">
580
+ <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%"/>
581
+ </p>
582
+
583
+ > **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).
584
+
585
+ <a id="secret-key-extraction-from-spotify-web-player-bundles"></a>
586
+ ### Secret Key Extraction from Spotify Web Player Bundles
587
+
588
+ 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.
589
+
590
+ > 💡 **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).
591
+
592
+ Download from [here](https://github.com/misiektoja/spotify_monitor/blob/dev/debug/spotify_monitor_secret_grabber.py) or:
593
+
594
+ ```sh
595
+ wget https://raw.githubusercontent.com/misiektoja/spotify_monitor/refs/heads/dev/debug/spotify_monitor_secret_grabber.py
596
+ ```
597
+
598
+ Install requirements:
559
599
 
560
600
  ```sh
561
601
  pip install playwright
562
602
  playwright install
603
+ ```
604
+
605
+ Run interactively (default output mode):
606
+
607
+ ```sh
563
608
  python3 spotify_monitor_secret_grabber.py
564
609
  ```
565
610
 
611
+ You should get output similar to below:
612
+
566
613
  <p align="center">
567
614
  <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%"/>
568
615
  </p>
569
616
 
617
+ Show help:
618
+ ```sh
619
+ python3 spotify_monitor_secret_grabber.py -h
620
+ ```
621
+
622
+ ---
623
+
624
+ <a id="cli-output-modes"></a>
625
+ ### CLI Output Modes
626
+
627
+ The script supports several output modes for different use cases:
628
+
629
+ | Flag | Description | Output |
630
+ |------|--------------|--------|
631
+ | `--secret` | Prints plain JSON array of extracted secrets | `[{"version": X, "secret": "..."}, ...]` |
632
+ | `--secretbytes` | Prints JSON array with ASCII byte values | `[{"version": X, "secret": [..]}, ...]` |
633
+ | `--secretdict` | Prints JSON object/dict mapping version → byte list | `{"X": [..], "Y": [..]}` |
634
+ | `--all` | Extracts secrets and **writes all three outputs** to local files | `secrets.json`, `secretBytes.json`, `secretDict.json` |
635
+
636
+ Print extracted secrets in specific format, for example Python-friendly secret bytes (JSON object/dict) and save to indicated file:
637
+
638
+ ```sh
639
+ python3 spotify_monitor_secret_grabber.py --secretdict > secretDict.json
640
+ ```
641
+
642
+ Or, to generate and save all secret formats to files (`secrets.json`, `secretBytes.json`, `secretDict.json`) at once:
643
+
644
+ ```sh
645
+ python3 spotify_monitor_secret_grabber.py --all
646
+ ```
647
+
648
+ Default file paths and names can be configured directly in the `OUTPUT_FILES` dictionary at the top of the script.
649
+
650
+ ---
651
+
652
+ <a id="-secret-key-extraction-via-docker-recommended-easiest-way"></a>
653
+ ### 🐳 Secret Key Extraction via Docker (Recommended Easiest Way)
654
+
655
+ A prebuilt multi-architecture image is available on Docker Hub: [`misiektoja/spotify-secrets-grabber`](https://hub.docker.com/r/misiektoja/spotify-secrets-grabber)
656
+
657
+ This image works on:
658
+ - macOS (Intel & Apple Silicon)
659
+ - Linux (x86_64 and ARM64)
660
+ - Windows (Docker Desktop / WSL2)
661
+ - Raspberry Pi 4/5 (64-bit OS)
662
+
663
+ Run interactively (default output mode):
664
+
665
+ ```sh
666
+ docker run --rm misiektoja/spotify-secrets-grabber
667
+ ```
668
+
669
+ Show help:
670
+ ```sh
671
+ docker run --rm misiektoja/spotify-secrets-grabber -h
672
+ ```
673
+
674
+ Print extracted secrets in specific format, for example Python-friendly secret bytes (JSON object/dict) and save to indicated file:
675
+ ```sh
676
+ docker run --rm misiektoja/spotify-secrets-grabber --secretdict > secretDict.json
677
+ ```
678
+
679
+ Or, to generate and save all secret formats to files (`secrets.json`, `secretBytes.json`, `secretDict.json`) at once:
680
+
681
+ ```sh
682
+ docker run --rm -v .:/work -w /work misiektoja/spotify-secrets-grabber --all
683
+ ```
684
+
685
+ *For SELinux hosts (Fedora/RHEL), use `-v .:/work:Z`.*
686
+
687
+ <a id="optional-use-docker-compose-one-command-for-all-oss"></a>
688
+ 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):
689
+
690
+ ```sh
691
+ docker compose run --rm spotify-secrets-grabber --all
692
+ ```
693
+
694
+ This will save all files into your current directory on any system (macOS, Linux or Windows).
695
+
696
+ ---
697
+
698
+ 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.
699
+
570
700
  <a id="change-log"></a>
571
701
  ## Change Log
572
702
 
573
703
  See [RELEASE_NOTES.md](https://github.com/misiektoja/spotify_monitor/blob/main/RELEASE_NOTES.md) for details.
574
704
 
705
+ <a id="maintainers"></a>
706
+ ## Maintainers
707
+
708
+ [![Maintainer: misiektoja](https://img.shields.io/badge/maintainer-misiektoja-blue)](https://github.com/misiektoja)
709
+ [![Maintainer: tomballgithub](https://img.shields.io/badge/maintainer-tomballgithub-blue)](https://github.com/tomballgithub)
710
+
575
711
  <a id="license"></a>
576
712
  ## License
577
713
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "spotify_monitor"
7
- version = "2.3.1"
7
+ version = "2.5"
8
8
  description = "Tool implementing real-time tracking of Spotify friends music activity"
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_monitor
3
- Version: 2.3.1
3
+ Version: 2.5
4
4
  Summary: Tool implementing real-time tracking of Spotify friends music activity
5
5
  Author-email: Michal Szymanski <misiektoja-pypi@rm-rf.ninja>
6
6
  License-Expression: GPL-3.0-or-later
@@ -26,7 +26,7 @@ Dynamic: license-file
26
26
 
27
27
  # spotify_monitor
28
28
 
29
- Tool for real-time monitoring of Spotify friends' music activity feed.
29
+ Tool for real-time monitoring of **Spotify friends' music activity feed**.
30
30
 
31
31
  ✨ If you're interested in tracking changes to Spotify users' profiles including their playlists, take a look at another tool I've developed: [spotify_profile_monitor](https://github.com/misiektoja/spotify_profile_monitor).
32
32
 
@@ -77,8 +77,11 @@ Tool for real-time monitoring of Spotify friends' music activity feed.
77
77
  * [Signal Controls (macOS/Linux/Unix)](#signal-controls-macoslinuxunix)
78
78
  * [Coloring Log Output with GRC](#coloring-log-output-with-grc)
79
79
  6. [Debugging Tools](#debugging-tools)
80
+ * [Access Token Retrieval via sp_dc Cookie and TOTP](#access-token-retrieval-via-sp_dc-cookie-and-totp)
81
+ * [Secret Key Extraction from Spotify Web Player Bundles](#secret-key-extraction-from-spotify-web-player-bundles)
80
82
  7. [Change Log](#change-log)
81
- 8. [License](#license)
83
+ 8. [Maintainers](#maintainers)
84
+ 9. [License](#license)
82
85
 
83
86
  <a id="requirements"></a>
84
87
  ## Requirements
@@ -88,8 +91,8 @@ Tool for real-time monitoring of Spotify friends' music activity feed.
88
91
 
89
92
  Tested on:
90
93
 
91
- * **macOS**: Ventura, Sonoma, Sequoia
92
- * **Linux**: Raspberry Pi OS (Bullseye, Bookworm), Ubuntu 24, Rocky Linux 8.x/9.x, Kali Linux 2024/2025
94
+ * **macOS**: Ventura, Sonoma, Sequoia, Tahoe
95
+ * **Linux**: Raspberry Pi OS (Bullseye, Bookworm, Trixie), Ubuntu 24/25, Rocky Linux 8.x/9.x, Kali Linux 2024/2025
93
96
  * **Windows**: 10, 11
94
97
 
95
98
  It should work on other versions of macOS, Linux, Unix and Windows as well.
@@ -112,7 +115,7 @@ Download the *[spotify_monitor.py](https://raw.githubusercontent.com/misiektoja/
112
115
  Install dependencies via pip:
113
116
 
114
117
  ```sh
115
- pip install requests python-dateutil urllib3 pyotp python-dotenv
118
+ pip install requests python-dateutil urllib3 pyotp python-dotenv wcwidth
116
119
  ```
117
120
 
118
121
  Alternatively, from the downloaded *[requirements.txt](https://raw.githubusercontent.com/misiektoja/spotify_monitor/refs/heads/main/requirements.txt)*:
@@ -199,7 +202,7 @@ If your `sp_dc` cookie expires, the tool will notify you via the console and ema
199
202
 
200
203
  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).
201
204
 
202
- `Note`: encrypted byte sequences used for TOTP secret generation tend to expire every now and then; you can either check the [issues](https://github.com/misiektoja/spotify_monitor/issues) section of the project to see if there are any new secrets published or you can run the [spotify_monitor_secret_grabber.py](https://github.com/misiektoja/spotify_monitor/blob/dev/debug/spotify_monitor_secret_grabber.py) and extract it by yourself (see [Debugging Tools](https://github.com/misiektoja/spotify_monitor#debugging-tools) for more info).
205
+ > **NOTE:** secrets used for TOTP generation (`SECRET_CIPHER_DICT`) expire every two days, that's why since v2.4 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).
203
206
 
204
207
  <a id="spotify-desktop-client"></a>
205
208
  #### Spotify Desktop Client
@@ -572,32 +575,165 @@ grc tail -F -n 100 spotify_monitor_<user_uri_id/file_suffix>.log
572
575
  <a id="debugging-tools"></a>
573
576
  ## Debugging Tools
574
577
 
575
- To help with troubleshooting and development, two debug utilities are available in the `debug` directory:
578
+ To help with troubleshooting and development, two debug utilities are available in the [debug](https://github.com/misiektoja/spotify_monitor/tree/dev/debug) directory.
576
579
 
577
- - [spotify_monitor_totp_test.py](https://github.com/misiektoja/spotify_monitor/blob/dev/debug/spotify_monitor_totp_test.py): fetching of Spotify access token based on a Spotify Web Player `sp_dc` cookie value:
580
+ <a id="access-token-retrieval-via-sp_dc-cookie-and-totp"></a>
581
+ ### Access Token Retrieval via sp_dc Cookie and TOTP
582
+
583
+ 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.
584
+
585
+ Download from [here](https://github.com/misiektoja/spotify_monitor/blob/dev/debug/spotify_monitor_totp_test.py) or:
586
+
587
+ ```sh
588
+ wget https://raw.githubusercontent.com/misiektoja/spotify_monitor/refs/heads/dev/debug/spotify_monitor_totp_test.py
589
+ ```
590
+
591
+ Install requirements:
578
592
 
579
593
  ```sh
580
594
  pip install requests python-dateutil pyotp
595
+ ```
596
+
597
+ Run:
598
+
599
+ ```sh
581
600
  python3 spotify_monitor_totp_test.py --sp-dc "your_sp_dc_cookie_value"
582
601
  ```
583
602
 
584
- - [spotify_monitor_secret_grabber.py](https://github.com/misiektoja/spotify_monitor/blob/dev/debug/spotify_monitor_secret_grabber.py): automatic extractor for secret keys used for TOTP generation in Spotify Web Player JavaScript bundles:
603
+ You should get a valid Spotify access token, example output:
604
+
605
+ <p align="center">
606
+ <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%"/>
607
+ </p>
608
+
609
+ > **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).
610
+
611
+ <a id="secret-key-extraction-from-spotify-web-player-bundles"></a>
612
+ ### Secret Key Extraction from Spotify Web Player Bundles
613
+
614
+ 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.
615
+
616
+ > 💡 **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).
617
+
618
+ Download from [here](https://github.com/misiektoja/spotify_monitor/blob/dev/debug/spotify_monitor_secret_grabber.py) or:
619
+
620
+ ```sh
621
+ wget https://raw.githubusercontent.com/misiektoja/spotify_monitor/refs/heads/dev/debug/spotify_monitor_secret_grabber.py
622
+ ```
623
+
624
+ Install requirements:
585
625
 
586
626
  ```sh
587
627
  pip install playwright
588
628
  playwright install
629
+ ```
630
+
631
+ Run interactively (default output mode):
632
+
633
+ ```sh
589
634
  python3 spotify_monitor_secret_grabber.py
590
635
  ```
591
636
 
637
+ You should get output similar to below:
638
+
592
639
  <p align="center">
593
640
  <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%"/>
594
641
  </p>
595
642
 
643
+ Show help:
644
+ ```sh
645
+ python3 spotify_monitor_secret_grabber.py -h
646
+ ```
647
+
648
+ ---
649
+
650
+ <a id="cli-output-modes"></a>
651
+ ### CLI Output Modes
652
+
653
+ The script supports several output modes for different use cases:
654
+
655
+ | Flag | Description | Output |
656
+ |------|--------------|--------|
657
+ | `--secret` | Prints plain JSON array of extracted secrets | `[{"version": X, "secret": "..."}, ...]` |
658
+ | `--secretbytes` | Prints JSON array with ASCII byte values | `[{"version": X, "secret": [..]}, ...]` |
659
+ | `--secretdict` | Prints JSON object/dict mapping version → byte list | `{"X": [..], "Y": [..]}` |
660
+ | `--all` | Extracts secrets and **writes all three outputs** to local files | `secrets.json`, `secretBytes.json`, `secretDict.json` |
661
+
662
+ Print extracted secrets in specific format, for example Python-friendly secret bytes (JSON object/dict) and save to indicated file:
663
+
664
+ ```sh
665
+ python3 spotify_monitor_secret_grabber.py --secretdict > secretDict.json
666
+ ```
667
+
668
+ Or, to generate and save all secret formats to files (`secrets.json`, `secretBytes.json`, `secretDict.json`) at once:
669
+
670
+ ```sh
671
+ python3 spotify_monitor_secret_grabber.py --all
672
+ ```
673
+
674
+ Default file paths and names can be configured directly in the `OUTPUT_FILES` dictionary at the top of the script.
675
+
676
+ ---
677
+
678
+ <a id="-secret-key-extraction-via-docker-recommended-easiest-way"></a>
679
+ ### 🐳 Secret Key Extraction via Docker (Recommended Easiest Way)
680
+
681
+ A prebuilt multi-architecture image is available on Docker Hub: [`misiektoja/spotify-secrets-grabber`](https://hub.docker.com/r/misiektoja/spotify-secrets-grabber)
682
+
683
+ This image works on:
684
+ - macOS (Intel & Apple Silicon)
685
+ - Linux (x86_64 and ARM64)
686
+ - Windows (Docker Desktop / WSL2)
687
+ - Raspberry Pi 4/5 (64-bit OS)
688
+
689
+ Run interactively (default output mode):
690
+
691
+ ```sh
692
+ docker run --rm misiektoja/spotify-secrets-grabber
693
+ ```
694
+
695
+ Show help:
696
+ ```sh
697
+ docker run --rm misiektoja/spotify-secrets-grabber -h
698
+ ```
699
+
700
+ Print extracted secrets in specific format, for example Python-friendly secret bytes (JSON object/dict) and save to indicated file:
701
+ ```sh
702
+ docker run --rm misiektoja/spotify-secrets-grabber --secretdict > secretDict.json
703
+ ```
704
+
705
+ Or, to generate and save all secret formats to files (`secrets.json`, `secretBytes.json`, `secretDict.json`) at once:
706
+
707
+ ```sh
708
+ docker run --rm -v .:/work -w /work misiektoja/spotify-secrets-grabber --all
709
+ ```
710
+
711
+ *For SELinux hosts (Fedora/RHEL), use `-v .:/work:Z`.*
712
+
713
+ <a id="optional-use-docker-compose-one-command-for-all-oss"></a>
714
+ 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):
715
+
716
+ ```sh
717
+ docker compose run --rm spotify-secrets-grabber --all
718
+ ```
719
+
720
+ This will save all files into your current directory on any system (macOS, Linux or Windows).
721
+
722
+ ---
723
+
724
+ 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.
725
+
596
726
  <a id="change-log"></a>
597
727
  ## Change Log
598
728
 
599
729
  See [RELEASE_NOTES.md](https://github.com/misiektoja/spotify_monitor/blob/main/RELEASE_NOTES.md) for details.
600
730
 
731
+ <a id="maintainers"></a>
732
+ ## Maintainers
733
+
734
+ [![Maintainer: misiektoja](https://img.shields.io/badge/maintainer-misiektoja-blue)](https://github.com/misiektoja)
735
+ [![Maintainer: tomballgithub](https://img.shields.io/badge/maintainer-tomballgithub-blue)](https://github.com/tomballgithub)
736
+
601
737
  <a id="license"></a>
602
738
  ## License
603
739
 
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env python3
2
2
  """
3
3
  Author: Michal Szymanski <misiektoja-github@rm-rf.ninja>
4
- v2.3.1
4
+ v2.5
5
5
 
6
6
  Tool implementing real-time tracking of Spotify friends music activity:
7
7
  https://github.com/misiektoja/spotify_monitor/
@@ -16,7 +16,7 @@ python-dotenv (optional)
16
16
  wcwidth (optional, needed by TRUNCATE_CHARS feature)
17
17
  """
18
18
 
19
- VERSION = "2.3.1"
19
+ VERSION = "2.5"
20
20
 
21
21
  # ---------------------------
22
22
  # CONFIGURATION SECTION START
@@ -244,25 +244,28 @@ SPOTIFY_INACTIVITY_CHECK_SIGNAL_VALUE = 30 # 30 seconds
244
244
  # The section below is used when the token source is set to 'cookie'
245
245
 
246
246
  # Maximum number of attempts to get a valid access token in a single run of the spotify_get_access_token_from_sp_dc() function
247
- TOKEN_MAX_RETRIES = 10
247
+ TOKEN_MAX_RETRIES = 3
248
248
 
249
249
  # Interval between access token retry attempts; in seconds
250
250
  TOKEN_RETRY_TIMEOUT = 0.5 # 0.5 second
251
251
 
252
- # Mapping of TOTP version identifiers to the encrypted byte sequence for TOTP secret generation
253
- # Newest TOTP secrets can be fetched via spotify_monitor_secret_grabber.py (see debug dir)
252
+ # Mapping of TOTP version identifiers to the secrets needed for TOTP generation
253
+ # Newest secrets are downloaded automatically from SECRET_CIPHER_DICT_URL (see below)
254
+ # Can also be fetched via spotify_monitor_secret_grabber.py utility - see debug dir
254
255
  SECRET_CIPHER_DICT = {
255
- "12": [107, 81, 49, 57, 67, 93, 87, 81, 69, 67, 40, 93, 48, 50, 46, 91, 94, 113, 41, 108, 77, 107, 34],
256
- "11": [111, 45, 40, 73, 95, 74, 35, 85, 105, 107, 60, 110, 55, 72, 69, 70, 114, 83, 63, 88, 91],
257
- "10": [61, 110, 58, 98, 35, 79, 117, 69, 102, 72, 92, 102, 69, 93, 41, 101, 42, 75],
258
- "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],
259
- "8": [37, 84, 32, 76, 87, 90, 87, 47, 13, 75, 48, 54, 44, 28, 19, 21, 22],
260
- "7": [59, 91, 66, 74, 30, 66, 74, 38, 46, 50, 72, 61, 44, 71, 86, 39, 89],
261
256
  "6": [21, 24, 85, 46, 48, 35, 33, 8, 11, 63, 76, 12, 55, 77, 14, 7, 54],
262
257
  "5": [12, 56, 76, 33, 88, 44, 88, 33, 78, 78, 11, 66, 22, 22, 55, 69, 54],
263
258
  }
264
259
 
265
- # Identifier used to select the appropriate encrypted secret from SECRET_CIPHER_DICT when generating a TOTP token
260
+ # Remote or local URL used to fetch updated secrets needed for TOTP generation
261
+ # Set to empty string to disable
262
+ # If you used "spotify_monitor_secret_grabber.py --secretdict > secretDict.json" specify the file location below
263
+ # SECRET_CIPHER_DICT_URL = "https://github.com/Thereallo1026/spotify-secrets/blob/main/secrets/secretDict.json?raw=true"
264
+ SECRET_CIPHER_DICT_URL = "https://github.com/xyloflake/spot-secrets-go/blob/main/secrets/secretDict.json?raw=true"
265
+ # SECRET_CIPHER_DICT_URL = file:///C:/your_path/secretDict.json
266
+ # SECRET_CIPHER_DICT_URL = "file:///your_path/secretDict.json"
267
+
268
+ # Identifier used to select the appropriate secret from SECRET_CIPHER_DICT when generating a TOTP token
266
269
  # Set to 0 to auto-select the highest available version
267
270
  TOTP_VER = 0
268
271
 
@@ -467,6 +470,7 @@ SPOTIFY_INACTIVITY_CHECK_SIGNAL_VALUE = 0
467
470
  TOKEN_MAX_RETRIES = 0
468
471
  TOKEN_RETRY_TIMEOUT = 0.0
469
472
  SECRET_CIPHER_DICT = {}
473
+ SECRET_CIPHER_DICT_URL = ""
470
474
  TOTP_VER = 0
471
475
  FLAG_FILE = ""
472
476
  TRUNCATE_CHARS = 0
@@ -1276,7 +1280,11 @@ def fetch_server_time(session: req.Session, ua: str) -> int:
1276
1280
  def generate_totp():
1277
1281
  import pyotp
1278
1282
 
1279
- secret_cipher_bytes = SECRET_CIPHER_DICT[str((ver := TOTP_VER or max(map(int, SECRET_CIPHER_DICT))))]
1283
+ ver = TOTP_VER or max(map(int, SECRET_CIPHER_DICT))
1284
+ if str(ver) not in SECRET_CIPHER_DICT:
1285
+ raise Exception(f"generate_totp(): Defined TOTP_VER ({ver}) is missing in SECRET_CIPHER_DICT")
1286
+
1287
+ secret_cipher_bytes = SECRET_CIPHER_DICT[str(ver)]
1280
1288
 
1281
1289
  transformed = [e ^ ((t % 33) + 9) for t, e in enumerate(secret_cipher_bytes)]
1282
1290
  joined = "".join(str(num) for num in transformed)
@@ -1286,6 +1294,65 @@ def generate_totp():
1286
1294
  return pyotp.TOTP(secret, digits=6, interval=30)
1287
1295
 
1288
1296
 
1297
+ def fetch_and_update_secrets():
1298
+ global SECRET_CIPHER_DICT
1299
+
1300
+ if not SECRET_CIPHER_DICT_URL:
1301
+ return False
1302
+
1303
+ try:
1304
+ if SECRET_CIPHER_DICT_URL.startswith("file:"):
1305
+ import os
1306
+ from urllib.parse import urlparse, unquote
1307
+
1308
+ parsed = urlparse(SECRET_CIPHER_DICT_URL)
1309
+
1310
+ if parsed.netloc:
1311
+ raw_path = f"/{parsed.netloc}{parsed.path or ''}"
1312
+ else:
1313
+ if SECRET_CIPHER_DICT_URL.startswith("file://"):
1314
+ raw_path = parsed.path or SECRET_CIPHER_DICT_URL[len("file://"):]
1315
+ else:
1316
+ raw_path = parsed.path or SECRET_CIPHER_DICT_URL[len("file:"):]
1317
+
1318
+ raw_path = unquote(raw_path)
1319
+
1320
+ if raw_path.startswith("/~"):
1321
+ raw_path = raw_path[1:]
1322
+
1323
+ if not raw_path.startswith("/") and not raw_path.startswith("~"):
1324
+ raw_path = "/" + raw_path
1325
+
1326
+ path = os.path.expanduser(os.path.expandvars(raw_path))
1327
+
1328
+ print(f"Loading Spotify web-player TOTP secrets from file: {path}")
1329
+ with open(path, "r", encoding="utf-8") as f:
1330
+ secrets = json.load(f)
1331
+ print("─" * HORIZONTAL_LINE)
1332
+ else:
1333
+ print(f"Fetching Spotify web-player TOTP secrets from URL: {SECRET_CIPHER_DICT_URL}")
1334
+ response = req.get(SECRET_CIPHER_DICT_URL, timeout=FUNCTION_TIMEOUT, verify=VERIFY_SSL)
1335
+ response.raise_for_status()
1336
+ secrets = response.json()
1337
+ print("─" * HORIZONTAL_LINE)
1338
+
1339
+ if not isinstance(secrets, dict) or not secrets:
1340
+ raise ValueError("fetch_and_update_secrets(): Fetched payload not a non-empty dict")
1341
+
1342
+ for key, value in secrets.items():
1343
+ if not isinstance(key, str) or not key.isdigit():
1344
+ raise ValueError(f"fetch_and_update_secrets(): Invalid key format: {key}")
1345
+ if not isinstance(value, list) or not all(isinstance(x, int) for x in value):
1346
+ raise ValueError(f"fetch_and_update_secrets(): Invalid value format for key {key}")
1347
+
1348
+ SECRET_CIPHER_DICT = secrets
1349
+ return True
1350
+
1351
+ except Exception as e:
1352
+ print(f"fetch_and_update_secrets(): Failed to get new secrets: {e}")
1353
+ return False
1354
+
1355
+
1289
1356
  # Refreshes the Spotify access token using the sp_dc cookie, tries first with mode "transport" and if needed with "init"
1290
1357
  def refresh_access_token_from_sp_dc(sp_dc: str) -> dict:
1291
1358
  transport = True
@@ -1385,29 +1452,52 @@ def spotify_get_access_token_from_sp_dc(sp_dc: str):
1385
1452
  max_retries = TOKEN_MAX_RETRIES
1386
1453
  retry = 0
1387
1454
 
1388
- while retry < max_retries:
1389
- token_data = refresh_access_token_from_sp_dc(sp_dc)
1390
- token = token_data["access_token"]
1391
- client_id = token_data.get("client_id", "")
1392
- length = token_data["length"]
1455
+ last_error = ""
1393
1456
 
1394
- SP_CACHED_ACCESS_TOKEN = token
1395
- SP_ACCESS_TOKEN_EXPIRES_AT = token_data["expires_at"]
1396
- SP_CACHED_CLIENT_ID = client_id
1397
-
1398
- if SP_CACHED_ACCESS_TOKEN is None or not check_token_validity(SP_CACHED_ACCESS_TOKEN, SP_CACHED_CLIENT_ID, USER_AGENT):
1457
+ while retry < max_retries:
1458
+ try:
1459
+ token_data = refresh_access_token_from_sp_dc(sp_dc)
1460
+ token = token_data["access_token"]
1461
+ client_id = token_data.get("client_id", "")
1462
+ length = token_data["length"]
1463
+
1464
+ SP_CACHED_ACCESS_TOKEN = token
1465
+ SP_ACCESS_TOKEN_EXPIRES_AT = token_data["expires_at"]
1466
+ SP_CACHED_CLIENT_ID = client_id
1467
+
1468
+ if SP_CACHED_ACCESS_TOKEN is None or not check_token_validity(SP_CACHED_ACCESS_TOKEN, SP_CACHED_CLIENT_ID, USER_AGENT):
1469
+ retry += 1
1470
+ time.sleep(TOKEN_RETRY_TIMEOUT)
1471
+ else:
1472
+ break
1473
+ except Exception as e:
1474
+ last_error = str(e)
1399
1475
  retry += 1
1400
- time.sleep(TOKEN_RETRY_TIMEOUT)
1401
- else:
1402
- break
1476
+ if retry < max_retries:
1477
+ time.sleep(TOKEN_RETRY_TIMEOUT)
1403
1478
 
1404
1479
  if retry == max_retries:
1405
- if SP_CACHED_ACCESS_TOKEN is not None:
1406
- print(f"* Token appears to be still invalid after {max_retries} attempts, returning token anyway")
1407
- print_cur_ts("Timestamp:\t\t\t")
1408
- return SP_CACHED_ACCESS_TOKEN
1409
- else:
1410
- raise RuntimeError(f"Failed to obtain a valid Spotify access token after {max_retries} attempts")
1480
+
1481
+ if fetch_and_update_secrets():
1482
+ try:
1483
+ token_data = refresh_access_token_from_sp_dc(sp_dc)
1484
+ token = token_data["access_token"]
1485
+ client_id = token_data.get("client_id", "")
1486
+ length = token_data["length"]
1487
+
1488
+ SP_CACHED_ACCESS_TOKEN = token
1489
+ SP_ACCESS_TOKEN_EXPIRES_AT = token_data["expires_at"]
1490
+ SP_CACHED_CLIENT_ID = client_id
1491
+
1492
+ if SP_CACHED_ACCESS_TOKEN and check_token_validity(SP_CACHED_ACCESS_TOKEN, SP_CACHED_CLIENT_ID, USER_AGENT):
1493
+ return SP_CACHED_ACCESS_TOKEN
1494
+ except Exception as e:
1495
+ last_error = str(e)
1496
+
1497
+ error_msg = f"Failed to obtain a valid Spotify access token after {max_retries} attempts"
1498
+ if last_error:
1499
+ error_msg += f": {last_error}"
1500
+ raise RuntimeError(error_msg)
1411
1501
 
1412
1502
  return SP_CACHED_ACCESS_TOKEN
1413
1503
 
@@ -1985,7 +2075,7 @@ def spotify_convert_uri_to_url(uri):
1985
2075
  return url
1986
2076
 
1987
2077
 
1988
- # Prints the list of Spotify friends with the last listened track (-l flag)
2078
+ # Returns list of Spotify friends
1989
2079
  def spotify_list_friends(friend_activity):
1990
2080
 
1991
2081
  print(f"Number of friends:\t\t{len(friend_activity['friends'])}\n")
@@ -3314,6 +3404,7 @@ def main():
3314
3404
  else:
3315
3405
  if FLAG_FILE:
3316
3406
  FLAG_FILE = os.path.expanduser(FLAG_FILE)
3407
+ flag_file_delete()
3317
3408
 
3318
3409
  if args.send_test_email:
3319
3410
  print("* Sending test email notification ...\n")
File without changes
File without changes