Skip to content

Commit 734e618

Browse files
committed
Merge remote-tracking branch 'upstream/main'
2 parents 13dd9e4 + 57fead0 commit 734e618

6 files changed

Lines changed: 139 additions & 11 deletions

File tree

.github/workflows/gitleaks.yml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
name: Gitleaks
2+
3+
on:
4+
push:
5+
pull_request:
6+
workflow_dispatch:
7+
8+
jobs:
9+
scan:
10+
name: gitleaks
11+
runs-on: ubuntu-latest
12+
permissions:
13+
contents: read
14+
steps:
15+
- name: Checkout repository
16+
uses: actions/checkout@v4
17+
with:
18+
fetch-depth: 0
19+
- name: Run gitleaks scan
20+
uses: gitleaks/gitleaks-action@v2
21+
env:
22+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
23+
GITLEAKS_CONFIG: .gitleaks.toml
24+
GITLEAKS_ENABLE_COMMENTS: "false"
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
name: Build release archives
2+
3+
on:
4+
release:
5+
types: [published]
6+
7+
jobs:
8+
build-and-upload-assets:
9+
name: Build zip and tar.gz and upload to release
10+
runs-on: ubuntu-latest
11+
permissions:
12+
contents: write
13+
14+
steps:
15+
- name: Check out source
16+
uses: actions/checkout@v4
17+
with:
18+
fetch-depth: 0
19+
20+
- name: Build archives from release tag
21+
shell: bash
22+
run: |
23+
set -euo pipefail
24+
TAG="${{ github.event.release.tag_name }}"
25+
PREFIX="spotify_monitor_${TAG}"
26+
27+
git archive --format=zip --output "${PREFIX}.zip" "${TAG}"
28+
git archive --format=tar.gz --output "${PREFIX}.tar.gz" "${TAG}"
29+
30+
ls -lh "${PREFIX}.zip" "${PREFIX}.tar.gz"
31+
32+
- name: Upload archives to GitHub Release
33+
uses: softprops/action-gh-release@v2
34+
with:
35+
files: |
36+
spotify_monitor_${{ github.event.release.tag_name }}.zip
37+
spotify_monitor_${{ github.event.release.tag_name }}.tar.gz
38+
fail_on_unmatched_files: true
39+
overwrite_files: true

.gitleaks.toml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
title = "Shared monitor repos gitleaks config"
2+
3+
[extend]
4+
useDefault = true
5+
6+
[[rules]]
7+
id = "monitor-secret-assignment"
8+
description = "Detects monitor project secret assignments with lower entropy tolerance"
9+
path = '''(?i)(?:^|/)(?:\.env(?:\.[^/]+)?|[^/]+\.(?:py|env|ini|cfg|conf|toml|ya?ml|json|sh))$'''
10+
regex = '''(?i)\b(?:steam_api_key|github_token|lastfm_api_key|lastfm_api_secret|riot_api_key|psn_npsso|ms_app_client_secret|sp_app_client_secret|sp_user_client_secret|sp_client_secret|sp_dc_cookie|refresh_token|session_password|smtp_password|sp_sha256)\b(?:[ \t\w.-]{0,20})[\s'"]{0,3}(?:=|:{1,3}=|:|=>|\?=)[\s=]{0,3}[\x60'"]([^\x60'"\s#]{8,256})[\x60'"](?:[\x60'"\s;]|\\[nr]|$)'''
11+
secretGroup = 1
12+
entropy = 3.1
13+
keywords = [
14+
"steam_api_key",
15+
"github_token",
16+
"lastfm_api_key",
17+
"lastfm_api_secret",
18+
"riot_api_key",
19+
"psn_npsso",
20+
"ms_app_client_secret",
21+
"sp_app_client_secret",
22+
"sp_user_client_secret",
23+
"sp_client_secret",
24+
"sp_dc_cookie",
25+
"refresh_token",
26+
"session_password",
27+
"smtp_password",
28+
"sp_sha256",
29+
]
30+
31+
[[rules.allowlists]]
32+
description = "Skip placeholders and variable references"
33+
regexTarget = "secret"
34+
regexes = [
35+
'''(?i)^your_[a-z0-9_]+$''',
36+
'''(?i)^example(?:_[a-z0-9_]+)?$''',
37+
'''(?i)^dummy(?:_[a-z0-9_]+)?$''',
38+
'''(?i)^changeme(?:_[a-z0-9_]+)?$''',
39+
'''(?i)^replace(?:_[a-z0-9_]+)?$''',
40+
'''(?i)^[a-z_][a-z0-9_]*(?:\.[a-z_][a-z0-9_]*)+$''',
41+
'''(?i)^[a-z_][a-z0-9_]*\([^\)]{0,80}\)$''',
42+
'''(?i)^<[^>]{1,64}>$''',
43+
'''(?i)^\$\{?[A-Z0-9_]{2,64}\}?$''',
44+
'''(?i)^\$\{?[a-z_][a-z0-9_]{1,63}\}?$''',
45+
'''(?i)^x{8,}$''',
46+
'''^_+$''',
47+
]

RELEASE_NOTES.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22

33
This is a high-level summary of the most important changes.
44

5+
# Changes in 2.9.2 (27 Apr 2026)
6+
7+
**Bug fixes**:
8+
9+
- **BUGFIX:** Bounded the retry loop in `spotify_get_access_token_from_sp_dc` to prevent an infinite loop when TOTP secrets are repeatedly fetched but the resulting token refresh keeps failing (thanks [@tomballgithub](https://github.com/tomballgithub), fixes [#37](https://github.com/misiektoja/spotify_monitor/issues/37))
10+
- **BUGFIX:** Auto-fallback to the highest available TOTP version when the configured `TOTP_VER` is missing from `SECRET_CIPHER_DICT` (with a warning) so the script self-heals instead of failing every retry
11+
512
# Changes in 2.9.1 (09 Mar 2026)
613

714
**Features and Improvements**:

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "spotify_monitor"
7-
version = "2.9.1"
7+
version = "2.9.2"
88
description = "Track Spotify friends' music activity in real time with auto-playback, skipped tracks detection and instant notifications"
99
readme = "README.md"
1010
license = "GPL-3.0-or-later"

spotify_monitor.py

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@
159159
#!/usr/bin/env python3
160160
"""
161161
Author: Michal Szymanski <misiektoja-github@rm-rf.ninja>
162-
v2.9.1
162+
v2.9.2
163163
164164
Tool implementing real-time tracking of Spotify friends music activity:
165165
https://github.com/misiektoja/spotify_monitor/
@@ -175,7 +175,7 @@
175175
spotipy (required since v2.7 due to new Spotify restrictions introduced on 22 Dec 2025)
176176
"""
177177

178-
VERSION = "2.9.1"
178+
VERSION = "2.9.2"
179179

180180
# API 401 error means sp_dc cookie has expired. Lasts one year. 03/15/2025
181181

@@ -2602,16 +2602,24 @@ def fetch_server_time(session: req.Session, ua: str) -> int:
26022602
return int(parsedate_to_datetime(date_hdr).timestamp())
26032603

26042604

2605+
# Resolves the effective TOTP version, falling back to the highest available when the configured TOTP_VER is missing from SECRET_CIPHER_DICT
2606+
def resolve_totp_ver() -> int:
2607+
if not SECRET_CIPHER_DICT:
2608+
raise SecretsUnavailableError("resolve_totp_ver(): SECRET_CIPHER_DICT is empty")
2609+
if TOTP_VER and str(TOTP_VER) in SECRET_CIPHER_DICT:
2610+
return TOTP_VER
2611+
available = sorted(map(int, SECRET_CIPHER_DICT))
2612+
fallback = available[-1]
2613+
if TOTP_VER:
2614+
print(f"Warning: configured TOTP_VER ({TOTP_VER}) is missing from SECRET_CIPHER_DICT (available: {available}); falling back to auto-selected version {fallback}")
2615+
return fallback
2616+
2617+
26052618
# Creates a TOTP object using a secret derived from transformed cipher bytes
26062619
def generate_totp():
26072620
import pyotp
26082621

2609-
if not SECRET_CIPHER_DICT:
2610-
raise SecretsUnavailableError("generate_totp(): SECRET_CIPHER_DICT is empty")
2611-
2612-
ver = TOTP_VER or max(map(int, SECRET_CIPHER_DICT))
2613-
if str(ver) not in SECRET_CIPHER_DICT:
2614-
raise SecretsUnavailableError(f"generate_totp(): Defined TOTP_VER ({ver}) is missing in SECRET_CIPHER_DICT")
2622+
ver = resolve_totp_ver()
26152623

26162624
secret_cipher_bytes = SECRET_CIPHER_DICT[str(ver)]
26172625

@@ -2704,7 +2712,7 @@ def refresh_access_token_from_sp_dc(sp_dc: str) -> dict:
27042712
client_time = int(time_ns() / 1000 / 1000)
27052713
otp_value = totp_obj.at(server_time)
27062714

2707-
totp_ver = TOTP_VER or max(map(int, SECRET_CIPHER_DICT))
2715+
totp_ver = resolve_totp_ver()
27082716

27092717
params = {
27102718
"reason": "transport",
@@ -2829,7 +2837,10 @@ def spotify_get_access_token_from_sp_dc(sp_dc: str):
28292837
last_error = str(e)
28302838
debug_print(f"TOTP secrets unavailable: {e}")
28312839
if fetch_and_update_secrets():
2832-
debug_print("TOTP secrets updated, retrying token refresh immediately")
2840+
debug_print("TOTP secrets updated, retrying token refresh")
2841+
retry += 1
2842+
if retry < max_retries:
2843+
time.sleep(TOKEN_RETRY_TIMEOUT)
28332844
continue
28342845
raise RuntimeError(f"Failed to obtain TOTP secrets for token refresh: {e}")
28352846
except Exception as e:

0 commit comments

Comments
 (0)