forked from misiektoja/spotify_monitor
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathspotify_monitor.py
More file actions
executable file
·6239 lines (5333 loc) · 300 KB
/
spotify_monitor.py
File metadata and controls
executable file
·6239 lines (5333 loc) · 300 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# Any DZ song should always get a heart
# Discovery Zone says (custom)(unique) [ignore those on detected playlists]
# some items have ♥ ♥
# alerts on bedroom playlists?
# always add icon if theres a match
# option on whether to count up during search for playlist
# should always cause an asteriks#
# ERROR: track: Umbrella, NOT FOUND in playlist: And, baby, that’s show business for you ❤️🔥 (Umbrella)
# found_playlist['
# count_end
# qty_end
# count_start
# qty_start
# playlist name
# count_shuffle
# count_overridden
# icon_add
# new_playlist
# _detected
# _cleared
# dz_message = ""
# dz_msg_screen = ""
# body_dz = ""
# body_dz_html = ""
# jmk_send = False
# found_playlist = False
# last_found_playlist = False
# active_ever = False
# icon_add = False
# playlist_suffix_string = ""
# hasTrack = False
# sp_playlist_owner = ""
# sp_playlist_image_url = ""
# monitored_playlists_data = {}
# DEBUG_JMK = False
# count_overridden = False
# icon if found in playlist or not?
# notify if sp_dc error
# write song info straight to google sheet
# option to notify for all email types
# generatic notifcation and print strings real time instead of storing strings?
# do this centrally? sp_track = sp_track + found_playlist.get('icon', '')
# switching from DZ to actual playlist doesnt 'cleared'
# don't assume spotify playlist has song - I saw this broken yesterday - Party Mix instead of liked songs
# - hastrack = search playlist not working for Party Mix
# - because it is custom to the person
# - is it viable to still track count_start and if it gets to limit than its good?
#
# improve comments and delete old code
# add # of sessions - need check on profile_monitor if # of songs changes by 100+/- to filter that out
# should DZ count be capped at listening song cap (or at least clarify it never resets)
# rearchitect my playlist monitoring PR tracking/counting/messaging? (it's convoluted)
# variables to indicate in a playlist, which one, first time loading, first time starting?, detected SMS/email, cleared SMS/email state
# centralize processing?
# - song strings for each song to screen
# - detected to screen/email
# - cleared to screen/email
# only show after first? or if at startup retry? * Error, retrying in 3 minutes: Failed to obtain a valid Spotify access token after 3 attempts: refresh_access_token_from_sp_dc(): Unsuccessful token request: 400 Client Error: Bad Request for url: https://open.spotify.com/api/token?reason=init&productType=web-player&totp=577757&totpServer=577757&totpVer=0&sTime=1753718266&cTime=1753718265688&buildDate=2025-07-28&buildVer=web-player_2025-07-28_1753718266000_016bf795
# -* Error: sp_dc may be invalid/expired or Spotify has broken sth again!
# design & try test cases - test transitioning from one playlist to another (given I zero everything out)
# - compare old to new, screen view, emails, etc
#
#?? change NTFY resized image hosting to krontz.nakattack.com to avoid Microsoft filtering
#?? added playlist on 'detected' NTFY messages
#?? add icons to start and stop NTFY essages
#?? clear sp_playlist_image_url if not a playlist
#?? listened_songs was showing -1 on NTFY stream updates
#?? Send full stream via NTFY
#?? Use NTFY for notifications including locally hosted image of the album or song
#?? Dz count shouldnt be 4 after 1st dong
#?? if it's a real playlist and song is in it, that takes priority? (ex: Soft 10s (custom), Rihanna - Love On The Brain) - startup problem?
#?? reset override count when streaming stops permanently (not inactive))
#?? custom/unique tag do it differently
#?? playlists not in email - also incorrect today with two different names
#?? protect new playlist count while expiring old one
#?? fixed active_ever
#?? prioritize playlist exact name match check in find_song_in_playlists
#?? put * on Liked Songs in emails/logs if *
#?? build_dz ONLY if count is high enough to start - errant playlist counts in emails/log (DZ_MESSAGE(4a))
#?? don't show cleared message/sms IFF first boot (never active) AND inactive
#?? fixed missing cleared messages and added more debug to track how code operates
#?? abort loading periodic playlist is length changes by > MAX_PLAYLIST_DIFFERENTIAL
#?? use nonlocal within reset_playlist_counts to adjust variables up-level - every email has Song Count: 1 for liked songs
#?? don't show playlist cleared if user just starting up
#?? removed active user check near SONG NOT IN A MONITORED PLAYLIST (2) since IN A PLAYLIST didn't have that same check
#?? playlist name match mean automatic switch to new playlist? (better if name just changed?)
#?? missing 'detected' after KARA becomes active (first time or every time?)
#?? fixed count messages in log email -> was sometimes this -> # *** Playlist 'Liked Songs' Cleared: Don't - Bryson Tiller (T R A P S O U L) - Song Count: 8
#?? fixed errant case of * playlist assignment (also added a SPECIAL CASE2 debug message)
#?? fixed missing * on emails
#?? only override count if < than qty_start (don't go backwards)
#?? fix duplicate 'cleared' messages
#?? fix incorrect DZ count shown in emails
#?? show song count on END texts
#?? change wording of the message to indicate it's a playlist that was detected -> #*** Liked Songs Detected
#?? change wording of the email 'counts' to indicate it's a playlist that was counted rather than Liked Songs Count: XX
#?? properly handle case of exception song is from a different monitored playlist
#?? and in above case force '*** cleared' message
#?? track smart shuffle count and show it if > 0 when showing DZ song count
#?? removed count on 'detected' texts and fixed count always being 0 for 'cleared' texts
#?? need to allow exception while counting up because otherwise may never get there if '3'
#?? correctly match playlist name in find_song_in_playlists by sending sp_playlist
#?? clearer debug to indicate when PLAYLIST NAME IS ACTUALLY MATCHED
#?? NO printed *** notifications----------------------
#?? made print_to_screen write to log if DEBUG to log is enabled
#?? configurable debug to write to screen/log/none
#?? Configcat support for legacy flags
#?? PR - flag file delete at start
#?? PR - wrong comment (-l) reference
#?? use generic routine of send_notification instead of send_sms
#?? matching playlist means instant detection (and optional notify) rather than counting
#?? don't show 'icon' if in the exception process
#?? error checking for keys 'icon', 'url', 'refresh', 'notify', and 'override'
#?? playlist exception should only apply if actually in a playlist versus building up the count
#?? DEBUG: SONG CHANGE -> print song because it's hard to keep track in log
#?? printing weird -> ICON_SONG_MISSING_FROM_PLAYLIST character spacing weird
#?? if song in two playlists, attribute it the active one first
#?? playlist url sometimes blank
#?? fix case of double-counting song
#?? emails/texts missing -> f"*** {notify_playlist['name']} Detected: {songstr}, Song Count: {notify_playlist['count_start']}" [notify was off for Liked Songs]
#?? fixed COUNT START: 0 next song after an exception granted
#?? Added playlist name to DEBUG: debug statements around finding playlist or not hitting exception limit
#?? Add DEBUG: COUNT_END debug info when playlist not found
#?? When handling exceptions to monitored playlists, need to reset it all back as if it was OK (ex: is_playlist = True)6
#?? When restarting on detected playlist, missing playlist assignment, context: album, and icon appending
#?? No *** detected on screen sometimes
#?? errant 'override' always at user becoming active later (not at restart)
#?? remove spaces before/after playlist and song names
#?? what -> Prints the list of Spotify friends with the last listened track (-l flag)
#?? JMK showing up at end of URLs?
#?? periodic tracks stop if frequency is 0
#?? playlist compare periodically checks entire playlist set and not just length (# of songs)
#?? when printing "monitoring tracks" should I should subdetail (alert? icons?)
#?? wrong playlist (white noise) in email and log
#?? playlist URL blank - add a key?
#?? configurable icon if song not found in playlist
#?? added alert key for each monitored playlist (PLAYLIST_NOTIFICATION equivalent)
#?? initial active jmk email and original emails missing count and playlist (it's hit or miss) - See 2:30pm 7/21
#?? JMK_MODE only to override start count if playlist already in action
#?? playlist detected notes not being shown on screen
#++ does count ever go above 3?
#++ initial active original email missing
#++ initial active getting 3rd text with count info
#++ restored original MONITOR_LIST_FILE functionality
#!/usr/bin/env python3
"""
Author: Michal Szymanski <misiektoja-github@rm-rf.ninja>
v2.9.1
Tool implementing real-time tracking of Spotify friends music activity:
https://github.com/misiektoja/spotify_monitor/
Python pip3 requirements:
requests
python-dateutil
urllib3
pyotp (optional, needed when the token source is set to cookie)
python-dotenv (optional)
wcwidth (optional, needed by TRUNCATE_CHARS feature)
spotipy (required since v2.7 due to new Spotify restrictions introduced on 22 Dec 2025)
"""
VERSION = "2.9.1"
# API 401 error means sp_dc cookie has expired. Lasts one year. 03/15/2025
# spotify-friend-stalker: https://github.com/moritzlauper/spotify-friend-stalker (node.js)
# spotify-buddylist API: https://github.com/valeriangalliat/spotify-buddylist (node.js)
# spotify-api: https://developer.spotify.com/documentation/web-api (official API)
# spotify-monitor: https://github.com/misiektoja/spotify_monitor/
# spotify-api-python: https://github.com/thlucas1/SpotifyWebApiPython (reference only)
# revision history
# 2025/03/28: Finished porting "spotify logger" features of JMK into this app (python, and maintained)
# 2025/03/29: Changed timeout for user playing stopped from 11 mins to 'song length + SPOTIFY_INACTIVITY_CHECK_MARGIN' to match "spotify logger"
# 2025/03/29: Reload any monitored playlists from file every hour
# 2025/03/29: Restored original timeout mechanism (can't use song + margin because song is the last-song-played not the current song)
# 2025/03/29: Added exponentional backoff on retries (**)
# 2025/03/29: Errors are put into log file and not on screen (ex: searchPlaylist error)
# 2025/03/30: Lots of little bug and operational fixes for Discovery Zone
# 2025/03/30: Updated to 1.8.2 from spotify_monitor source
# 2025/03/31: Fixed duplicative DZ logic causing 2 hearts.
# 2025/03/31: Moved remnant calls to _upper logic instead of map() logic
# 2025/03/31: Use songstring() in all instances of send_email
# 2025/03/31: Use upper() on compares in search_playlist
# 2025/03/31: Only check if 'Liked Songs' if NOT 'Discovery Zone'fs
# 2025/04/01: Lots of DZ bug fixes
# 2025/04/01: Periodic load file was loading the same playlist twice instead of different ones
# 2025/04/01: Added notice of duplicate songs removed during periodic load
# 2025/04/01: Combined two periodic load functions into one via ChatGPT
# 2025/04/01: Put midstream Discover Zone detected/cleared messages after song name printed on screen
# 2025/04/04: Added SMS message text to the SMS events in log
# 2025/04/04: Send discovery zone detected SMS after the START SMS
# 2025/04/04: Fix incorrect log info where 'not found in playlist' message was after overriding to 'unknown'
# 2025/04/04: Don't send duplicate emails for song changes (original spotify_monitor emails & JMK_MODE emails)
# 2025/04/04: Updated spotify_profile_monitor to avoid this due to spotify fetch error: *** Load Tracks: Fri Apr 4 06:20:35 2025, 1 songs [was: 302] in dz_songs.txt [0 duplicates removed]
# 2025/04/06: Don't overwrite valid existing playlist name to [liked songs]
# 2025/04/11: Remove 2nd space between timestamp and song names in JMK_MODE emails
# 2025/04/14: Added configcat support
# 2025/04/15: Setup configcat internal error logging to go to log file and not screen
# 2025/04/16: Add a \n in DZ message before "DZ Count & DZ Playlist" to put those on another line
# 2025/04/16: Strip \n from text messages in send_sms
# 2025/04/16: Change formatting of the SUCCESS and ERROR logged events in send_sms
# 2025/04/16: Don't put 2nd DZ MSG after a user goes inactive
# 2025/04/22: WIP: Allow a 1 song exception before exiting Discovery Zone
# 2025/04/28: Added option to truncate output to avoid line wrapping
# 2025/06/10: Migrated to latest code base
# 2025/06/10: Modified look & feel of configuration flags logging at startup
# 2025/06/10: Show stats on monitored playlists (discovery zone & liked songs) on screen during initial startup
# 2025/06/21: Line truncation feature value of 999 now does autodetection of screen width
# 2025/06/21: Don't show songs played on first boot when active
# 2025/06/25: Removed redundant variables for tracks_upper (used) and sp_tracks (unused)
# 2025/06/25: Removed song_count variable, since it's already there (listened_songs)
# 2025/06/27: Configcat can be used to control runtime info instead of SIGUSR/etc that are Linux-only
# 2025/06/28: Hide ConfigCat JSON fetch errors
# 2025/06/28: Show elapsed-session-time after "Songs Played:" on each email update
# 2025/06/28: Fixed truncation by treating tabs as spaces. Removed subtracting two from screen width during truncation auto-calculation
# 2025/06/28: Added ConfigCat feature flag definitions for Jeoff's special items into the .CONF file
# 2025/06/29: Optional flag file to indicate streaming state to other apps
# 2025/06/29: Fix first email not going out for [00] song when user becomes active in JMK_MODE
# 2025/07/02: Pull in latest source changes
# 2025/07/02: Fix message truncation if message is multiple lines via \n
# 2025/07/02: Fix DZ Cleared message when going from DZ Playlist to not DZ Playlist
# 2025/07/03: Added support for setting the FLAG_FILE_PATH
# 2025/07/03: Fixed crashing when logging was disabled
# 2025/07/03: Submitted PR: Missing *
# 2025/07/03: Submitted PR: Song Counts & Time Elapsed
# 2025/07/03: Submitted PR: Line truncation
# 2025/07/03: Submitted PR: Flag file
# 2025/07/07: Fixed truncation to support multi-character-width emojis
# 2025/07/13: Simplified start-text-sent and end-text-sent on-screen messaging
# 2025/07/18: When song not found in playlist, leave the playlist name but indicate with a 'warning' emoji
# 2025/07/20: Completely redid code for DZ and Liked Songs to make it generic for a PR, and more flexible, and to support hysteresis for smart shuffle
# 2025/10/04: If "unknown playlist", don't send that on NTYF alert
# 2025/10/05: NTFY now skips image processing if image_url = ""
# 2025/10/05: Adjusted BE-HUMAN logic to consider # of hours actually monitoring out of 24
# 2025/10/17: Honor tags for send_ntfy topic2 to better differentiate start/stops within stream
# 2025/10/25: Use one NTFY topic for all updates. Use NTFY library code
# 2025/10/25: New priority scheme for send_NTFY (and lower JMK to 1's)
# 2025/10/25: Fix images broken on 10/05/2025 change. Added NTFY_IMAGES as a configuration
# 2025/11/01: New priority scheme for send_ntfy. KEL stopping is now low, only start is high
# 2025/12/27: New spotify API scheme required
# 2025/12/27: New playlist image method required due to spotify API change for spotify-owned editorial playlists
# 2025/12/28: Updated to latest code base
# 2025/01/18: Added missing (custom) to END notifications of playlists
# 2025/01/18: Removed Twilio and send_sms support. Current mechanism is NTFY
# 2025/01/18: Fixed JMK_MODE emails not being sent for stream except START/initial song. Broke after updating code base on 12/25/25
# 2025/02/19: Fixed duplicate emails introduced with catching up to latest code base
# 2025/02/19: Fixed duplicate listened_songs += 1 introduced with catching up to latest code base
# 2025/02/22: Fixed exception crash if error (but not 404) occurs during fetching playlist image URL
# 2025/02/27: Fixed exception crash if error (but not 404) occurs during spotify_get_playlist_owner
# 2025/02/28: Fixed missing 'discovery zone cleared' messages
# 2025/03/14: Check DEBUG_JMK within print_debug, eliminating all those IF statements. Rename JMK_DEBUG to DEBUG_JMK
# 2025/03/14: Rename 'texts' to 'notify'
# bugs and to-dos:
# start/end texts include DZ count if > 0?
# *** PR: identify playlists via song lists and auto-refresh
# *** PR: use configcat to refresh cookie & directly update values (documentation, configuration, boottime data - refresh & emails, error level)
# *** PR: NTFY (a free notification service would be better)
# profile monitor: * Error, retrying in 5 minutes: fetch_server_time() head network request error: HTTPSConnectionPool(host='open.spotify.com', port=443): Read timed out. (read timeout=15)
# *** alternate notification method (pushover, gotify, ntfy.sh )
# *** give discovery zone a +1 song grace after DZ identified [but how to message this, etc] -> started the work, see DZexceptions
# *** detect smart shuffle songs, for JMK at least??
# *** why not write straight to the gdrive spreadsheet instead of indirectly via email?
# command line examples
# *** see .conf file
# ---------------------------
# CONFIGURATION SECTION START
# ---------------------------
CONFIG_BLOCK = """
# Select the method used to obtain the Spotify access token
# Available options:
# cookie - uses the sp_dc cookie to retrieve a token via the Spotify web endpoint (recommended)
# client - uses captured credentials from the Spotify desktop client and a Protobuf-based login flow (for advanced users)
TOKEN_SOURCE = "cookie"
# ---------------------------------------------------------------------
# The section below is used when the token source is set to 'cookie'
# (to configure the alternative 'client' method, see the section at the end of this config block)
#
# - Log in to Spotify web client (https://open.spotify.com/) and retrieve your sp_dc cookie
# (use your web browser's dev console or "Cookie-Editor" by cgagnier to extract it easily: https://cookie-editor.com/)
# - Provide the SP_DC_COOKIE secret using one of the following methods:
# - Pass it at runtime with -u / --spotify-dc-cookie
# - Set it as an environment variable (e.g. export SP_DC_COOKIE=...)
# - Add it to ".env" file (SP_DC_COOKIE=...) for persistent use
# - Fallback: hard-code it in the code or config file
SP_DC_COOKIE = "your_sp_dc_cookie_value"
# ---------------------------------------------------------------------
# The section below is used to get tracks and user info via secondary token (Client Credentials OAuth Flow - 'oauth_app')
#
# To obtain the credentials:
# - Log in to Spotify Developer dashboard: https://developer.spotify.com/dashboard
# - Create a new app
# - For 'Redirect URL', use: http://127.0.0.1:1234
# - Select 'Web API' as the intended API
# - Copy the 'Client ID' and 'Client Secret'
#
# Provide the SP_APP_CLIENT_ID and SP_APP_CLIENT_SECRET secrets using one of the following methods:
# - Pass it at runtime with -r / --oauth-app-creds (use SP_APP_CLIENT_ID:SP_APP_CLIENT_SECRET format - note the colon separator)
# - Set it as an environment variable (e.g. export SP_APP_CLIENT_ID=...; export SP_APP_CLIENT_SECRET=...)
# - Add it to ".env" file (SP_APP_CLIENT_ID=... and SP_APP_CLIENT_SECRET=...) for persistent use
# - Fallback: hard-code it in the code or config file
#
# The tool automatically refreshes the access token, so it remains valid indefinitely
SP_APP_CLIENT_ID = "your_spotify_app_client_id"
SP_APP_CLIENT_SECRET = "your_spotify_app_client_secret"
# Path to cache file used to store OAuth app access tokens across tool restarts
# Set to empty to use in-memory cache only
SP_APP_TOKENS_FILE = ".spotify-monitor-oauth-app.json"
# ---------------------------------------------------------------------
# SMTP settings for sending email notifications
# If left as-is, no notifications will be sent
#
# Provide the SMTP_PASSWORD secret using one of the following methods:
# - Set it as an environment variable (e.g. export SMTP_PASSWORD=...)
# - Add it to ".env" file (SMTP_PASSWORD=...) for persistent use
# Fallback:
# - Hard-code it in the code or config file
SMTP_HOST = "your_smtp_server_ssl"
SMTP_PORT = 587
SMTP_USER = "your_smtp_user"
SMTP_PASSWORD = "your_smtp_password"
SMTP_SSL = True
SENDER_EMAIL = "your_sender_email"
RECEIVER_EMAIL = "your_receiver_email"
# Whether to send an email when user becomes active
# Can also be enabled via the -a flag
ACTIVE_NOTIFICATION = False
# Whether to send an email when user goes inactive
# Can also be enabled via the -i flag
INACTIVE_NOTIFICATION = False
# Whether to send an email when a monitored track/playlist/album plays
# Can also be enabled via the -t flag
TRACK_NOTIFICATION = False
# Whether to send an email on every song change
# Can also be enabled via the -j flag
SONG_NOTIFICATION = False
# Whether to send an email when user plays a song on loop
# Triggered if the same song is played more than SONG_ON_LOOP_VALUE times
# Can also be enabled via the -x flag
SONG_ON_LOOP_NOTIFICATION = False
# Whether to send an email on errors
# Can also be disabled via the -e flag
ERROR_NOTIFICATION = True
# How often to check for user activity; in seconds
# Can also be set using the -c flag
SPOTIFY_CHECK_INTERVAL = 30 # 30 seconds
# Time to wait before retrying after an error; in seconds
SPOTIFY_ERROR_INTERVAL = 180 # 3 mins
# Time after which a user is considered inactive (based on last activity); in seconds
# Can also be set using the -o flag
# Note: If the user listens to songs longer than this value, they may be marked as inactive
SPOTIFY_INACTIVITY_CHECK = 660 # 11 mins
# How many recently listened songs to display in the inactive notification email
# Set to 0 to disable the recently listened songs list
INACTIVE_EMAIL_RECENT_SONGS_COUNT = 5
# Tolerance in seconds for "Played for" display when comparing actual playback time to track duration
# If the difference is within +-PLAYED_FOR_DURATION_TOLERANCE seconds, "Played for" is suppressed
# (treats as if song was played for its full duration to account for timestamp jitter)
PLAYED_FOR_DURATION_TOLERANCE = 1
# Whether to detect and annotate crossfaded songs (songs played with transition timing)
# When enabled, songs played within the crossfade detection thresholds will be marked as
# "(X% - crossfade enabled)" to indicate that the song likely ended early due to crossfade transitions
DETECT_CROSSFADED_SONGS = True
# Thresholds for crossfade detection (as percentage of track duration)
# Songs played between CROSSFADE_DETECTION_MIN and CROSSFADE_DETECTION_MAX will be annotated
# as crossfaded if DETECT_CROSSFADED_SONGS is enabled
CROSSFADE_DETECTION_MIN = 0.96 # 96% - minimum percentage to consider crossfade
CROSSFADE_DETECTION_MAX = 0.99 # 99% - maximum percentage to consider crossfade
# Interval for checking if a user who disappeared from the list of recently active friends has reappeared; in seconds
# Can happen due to:
# - unfollowing the user
# - Spotify service issues
# - private session bugs
# - user inactivity for over a week
# In such a case, the tool will continuously check for the user's reappearance using the time interval specified below
# Can also be set using the -m flag
SPOTIFY_DISAPPEARED_CHECK_INTERVAL = 180 # 3 mins
# Whether to auto-play each listened song in your Spotify client
# Can also be set using the -g flag
TRACK_SONGS = False
# Method used to play the song listened by the tracked user in local Spotify client under macOS
# (i.e. when TRACK_SONGS / -g functionality is enabled)
# Methods:
# "apple-script" (recommended)
# "trigger-url"
SPOTIFY_MACOS_PLAYING_METHOD = "apple-script"
# Method used to play the song listened by the tracked user in local Spotify client under Linux OS
# (i.e. when TRACK_SONGS / -g functionality is enabled)
# Methods:
# "dbus-send" (most common one)
# "qdbus"
# "trigger-url"
SPOTIFY_LINUX_PLAYING_METHOD = "dbus-send"
# Method used to play the song listened by the tracked user in local Spotify client under Windows OS
# (if TRACK_SONGS / -g functionality is enabled)
# Methods:
# "start-uri" (recommended)
# "spotify-cmd"
# "trigger-url"
SPOTIFY_WINDOWS_PLAYING_METHOD = "start-uri"
# Number of consecutive plays of the same song considered to be on loop
SONG_ON_LOOP_VALUE = 3
# Threshold for considering a song as skipped (fraction of duration)
SKIPPED_SONG_THRESHOLD = 0.55 # song is treated as skipped if played for <= 55% of its total length
# Spotify track ID to play when the user goes offline (used when TRACK_SONGS / -g functionality is enabled)
# Leave empty to simply pause
# SP_USER_GOT_OFFLINE_TRACK_ID = "5wCjNjnugSUqGDBrmQhn0e"
SP_USER_GOT_OFFLINE_TRACK_ID = ""
# Delay before pausing the above track after the user goes offline; in seconds
# Set to 0 to keep playing indefinitely until manually paused
SP_USER_GOT_OFFLINE_DELAY_BEFORE_PAUSE = 5 # 5 seconds
# Occasionally, the Spotify API glitches and reports that the user has disappeared from the list of friends
# To avoid false alarms, we delay alerts until this happens REMOVED_DISAPPEARED_COUNTER times in a row
REMOVED_DISAPPEARED_COUNTER = 4
# Optional: specify user agent manually
#
# When the token source is 'cookie' - set it to web browser user agent, some examples:
# Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:139.0) Gecko/20100101 Firefox/139.0
# Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0
#
# When the token source is 'client' - set it to Spotify desktop client user agent, some examples:
# Spotify/126200580 Win32_x86_64/0 (PC desktop)
# Spotify/126400408 OSX_ARM64/OS X 15.5.0 [arm 2]
#
# Leave empty to auto-generate it randomly for specific token source
USER_AGENT = ""
# How often to print a "liveness check" message to the output; in seconds
# Set to 0 to disable
LIVENESS_CHECK_INTERVAL = 43200 # 12 hours
# URL used to verify internet connectivity at startup
CHECK_INTERNET_URL = 'https://api.spotify.com/v1'
# Timeout used when checking initial internet connectivity; in seconds
CHECK_INTERNET_TIMEOUT = 5
# Whether to enable / disable SSL certificate verification while sending https requests
VERIFY_SSL = True
# Threshold for displaying Spotify 50x errors - it is to suppress sporadic issues with Spotify API endpoint
# Adjust the values according to the SPOTIFY_CHECK_INTERVAL timer
# If more than 6 Spotify API related errors in 4 minutes, show an alert
ERROR_500_NUMBER_LIMIT = 6
ERROR_500_TIME_LIMIT = 240 # 4 min
# Threshold for displaying network errors - it is to suppress sporadic issues with internet connectivity
# Adjust the values according to the SPOTIFY_CHECK_INTERVAL timer
# If more than 6 network related errors in 4 minutes, show an alert
ERROR_NETWORK_ISSUES_NUMBER_LIMIT = 6
ERROR_NETWORK_ISSUES_TIME_LIMIT = 240 # 4 min
# CSV file to write every listened track
# Can also be set using the -b flag
CSV_FILE = ""
# Filename with Spotify tracks/playlists/albums to alert on
# Can also be set using the -s flag
MONITOR_LIST_FILE = ""
# Location of the optional dotenv file which can keep secrets
# If not specified it will try to auto-search for .env files
# To disable auto-search, set this to the literal string "none"
# Can also be set using the --env-file flag
DOTENV_FILE = ""
# Suffix to append to the output filenames instead of default user URI ID
# Can also be set using the -y flag
FILE_SUFFIX = ""
# Base name for the log file. Output will be saved to spotify_monitor_<user_uri_id/file_suffix>.log
# Can include a directory path to specify the location, e.g. ~/some_dir/spotify_monitor
SP_LOGFILE = "spotify_monitor"
# Whether to disable logging to spotify_monitor_<user_uri_id/file_suffix>.log
# Can also be disabled via the -d flag
DISABLE_LOGGING = False
# Enable debug mode for technical logging (can also be enabled via --debug flag)
# Shows request flow, selected params and internal state changes (with sensitive values redacted)
DEBUG_MODE = False
# Width of horizontal line
HORIZONTAL_LINE = 113
# Whether to clear the terminal screen after starting the tool
CLEAR_SCREEN = True
# Path to a file that is created when the user is active and deleted when inactive
# Useful for external tools to detect streaming status
# Can also be set via the --flag-file flag
FLAG_FILE = ""
# Max characters per line when printing to screen to avoid line wrapping
# Does not affect log file output
# Set to 999 to auto-detect terminal width
# Applies only when DISABLE_LOGGING is False
# Can also be set via the --truncate flag
TRUNCATE_CHARS = 0
# Value added/subtracted via signal handlers to adjust inactivity timeout (SPOTIFY_INACTIVITY_CHECK); in seconds
SPOTIFY_INACTIVITY_CHECK_SIGNAL_VALUE = 30 # 30 seconds
# Whether to show Apple Music URL in console and emails
ENABLE_APPLE_MUSIC_URL = True
# Whether to show YouTube Music URL in console and emails
ENABLE_YOUTUBE_MUSIC_URL = True
# Whether to show Amazon Music URL in console and emails
ENABLE_AMAZON_MUSIC_URL = False
# Whether to show Deezer URL in console and emails
ENABLE_DEEZER_URL = False
# Whether to show Tidal URL in console and emails
# Note: Tidal requires users to be logged in to their account in the web browser to use the search functionality
ENABLE_TIDAL_URL = False
# Whether to show Genius lyrics URL in console and emails
ENABLE_GENIUS_LYRICS_URL = True
# Whether to show AZLyrics URL in console and emails
ENABLE_AZLYRICS_URL = False
# Whether to show Tekstowo.pl lyrics URL in console and emails
ENABLE_TEKSTOWO_URL = False
# Whether to show Musixmatch lyrics URL in console and emails
# Note: Musixmatch requires users to be logged in to their account in the web browser to use the search functionality
ENABLE_MUSIXMATCH_URL = False
# Whether to show Lyrics.com lyrics URL in console and emails
ENABLE_LYRICS_COM_URL = False
# String to add after playlist name to indicate it's a Spotify public curated and customized playlist
# The distinction may be important because the songs will vary by account due to listening habits.
# This will be used for messages on console and emails
# The string should include all desired characters, including a preceding space and parenthesis, if desired
#
# Example:
# For: 90s Pop (by Spotify), SPOTIFY_SUFFIX = " (by Spotify)"
#
# Leave empty to disable
SPOTIFY_SUFFIX = ""
# The Spotify API sometimes doesn't provide specific public shared playlists for a user.
# This allows you to add one or more playlists to be monitored
#
# Replace {playlist_id} with the ID of the playlist to monitor, and replace {user_id} with the ID for the owner of the playlist
#
# ADD_PLAYLISTS_TO_MONITOR = [
# {'uri': 'spotify:playlist:{playlist_id}', 'owner_name': '{user_id}', 'owner_uri': 'spotify:user:{user_id}'},
# {'uri': 'spotify:playlist:{playlist_id}', 'owner_name': '{user_id}', 'owner_uri': 'spotify:user:{user_id}'}
# ]
#
# example: [ {'uri': 'spotify:playlist:6pYPhRkJMSg1d7j8RHgJK1', 'owner_name': 'teocida', 'owner_uri': 'spotify:user:teocida'} ]
# example: [ {'uri': 'spotify:playlist:0AyBQ5uEhJgdh2NFcMe6wb', 'owner_name': 'uwacwfv5hr23atg1v3dez1sxs', 'owner_uri': 'spotify:user:uwacwfv5hr23atg1v3dez1sxs'} ]
#
LOAD_TRACKS_FREQUENCY = 3600 # 1 hour
ADD_PLAYLISTS_TO_MONITOR = []
# ---------------------------------------------------------------------
# The section below is used when the token source is set to 'cookie'
# Maximum number of attempts to get a valid access token in a single run of the spotify_get_access_token_from_sp_dc() function
TOKEN_MAX_RETRIES = 3
# Interval between access token retry attempts; in seconds
TOKEN_RETRY_TIMEOUT = 0.5 # 0.5 second
# Mapping of TOTP version identifiers to the secrets needed for TOTP generation
# Newest secrets are downloaded automatically from SECRET_CIPHER_DICT_URL (see below)
# Can also be fetched via spotify_monitor_secret_grabber.py utility - see debug dir
SECRET_CIPHER_DICT = {
"61": [44, 55, 47, 42, 70, 40, 34, 114, 76, 74, 50, 111, 120, 97, 75, 76, 94, 102, 43, 69, 49, 120, 118, 80, 64, 78],
}
# Remote or local URL used to fetch updated secrets needed for TOTP generation
# Set to empty string to disable
# If you used "spotify_monitor_secret_grabber.py --secretdict > secretDict.json" specify the file location below
SECRET_CIPHER_DICT_URL = "https://raw.githubusercontent.com/xyloflake/spot-secrets-go/main/secrets/secretDict.json"
# SECRET_CIPHER_DICT_URL = file:///C:/your_path/secretDict.json
# SECRET_CIPHER_DICT_URL = "file:///your_path/secretDict.json"
# Identifier used to select the appropriate secret from SECRET_CIPHER_DICT when generating a TOTP token
# Set to 0 to auto-select the highest available version
TOTP_VER = 0
# ---------------------------------------------------------------------
# The section below is used when the token source is set to 'client'
#
# - Run an intercepting proxy of your choice (like Proxyman)
# - Launch the Spotify desktop client and look for requests to: https://login{n}.spotify.com/v3/login
# (the 'login' part is suffixed with one or more digits)
# - Export the login request body (a binary Protobuf payload) to a file
# (e.g. in Proxyman: right click the request -> Export -> Request Body -> Save File -> <login-request-body-file>)
#
# To automatically extract DEVICE_ID, SYSTEM_ID, USER_URI_ID and REFRESH_TOKEN from the exported binary login
# request Protobuf file:
#
# - Run the tool with the -w flag to indicate an exported file or specify its file name below
LOGIN_REQUEST_BODY_FILE = ""
# Alternatively, you can manually set the DEVICE_ID, SYSTEM_ID, USER_URI_ID and REFRESH_TOKEN options
# (however, using the automated method described above is recommended)
#
# These values can be extracted using one of the following methods:
#
# - Run spotify_profile_monitor with the -w flag without specifying SPOTIFY_USER_URI_ID - it will decode the file and
# print the values to stdout, example:
# spotify_profile_monitor --token-source client -w <path-to-login-request-body-file>
#
# - Use the protoc tool (part of protobuf pip package):
# pip install protobuf
# protoc --decode_raw < <path-to-login-request-body-file>
#
# - Use the built-in Protobuf decoder in your intercepting proxy (if supported)
#
# The Protobuf structure is as follows:
#
# {
# 1: {
# 1: "DEVICE_ID",
# 2: "SYSTEM_ID"
# },
# 100: {
# 1: "USER_URI_ID",
# 2: "REFRESH_TOKEN"
# }
# }
#
# Provide the extracted values below (DEVICE_ID, SYSTEM_ID, USER_URI_ID). The REFRESH_TOKEN secret can be
# supplied using one of the following methods:
# - Set it as an environment variable (e.g. export REFRESH_TOKEN=...)
# - Add it to ".env" file (REFRESH_TOKEN=...) for persistent use
# - Fallback: hard-code it in the code or config file
DEVICE_ID = "your_spotify_app_device_id"
SYSTEM_ID = "your_spotify_app_system_id"
USER_URI_ID = "your_spotify_user_uri_id"
REFRESH_TOKEN = "your_spotify_app_refresh_token"
# ----------------------------------------------
# Advanced options for 'client' token source
# Modifying the values below is NOT recommended!
# ----------------------------------------------
# Spotify login URL
LOGIN_URL = "https://login5.spotify.com/v3/login"
# Spotify client token URL
CLIENTTOKEN_URL = "https://clienttoken.spotify.com/v1/clienttoken"
# Platform-specific values for token generation so the Spotify client token requests match your exact Spotify desktop
# client build (arch, OS build, app version etc.)
#
# - Run an intercepting proxy of your choice (like Proxyman)
# - Launch the Spotify desktop client and look for requests to: https://clienttoken.spotify.com/v1/clienttoken
# (these requests are sent every time client token expires, usually every 2 weeks)
# - Export the client token request body (a binary Protobuf payload) to a file
# (e.g. in Proxyman: right click the request -> Export -> Request Body -> Save File -> <clienttoken-request-body-file>)
#
# To automatically extract APP_VERSION, CPU_ARCH, OS_BUILD, PLATFORM, OS_MAJOR, OS_MINOR and CLIENT_MODEL from the
# exported binary client token request Protobuf file:
#
# - Run the tool with the hidden -z flag to indicate an exported file or specify its file name below
CLIENTTOKEN_REQUEST_BODY_FILE = ""
# Alternatively, you can manually set the APP_VERSION, CPU_ARCH, OS_BUILD, PLATFORM, OS_MAJOR, OS_MINOR and
# CLIENT_MODEL options
#
# These values can be extracted using one of the following methods:
#
# - run spotify_profile_monitor with the hidden -z flag without specifying SPOTIFY_USER_URI_ID - it will decode the file
# and print the values to stdout, example:
# spotify_profile_monitor --token-source client -z <path-to-clienttoken-request-body-file>
#
# - use the protoc tool (part of protobuf pip package):
# pip install protobuf
# protoc --decode_raw < <path-to-clienttoken-request-body-file>
#
# - use the built-in Protobuf decoder in your intercepting proxy (if supported)
#
# The Protobuf structure is as follows:
#
# 1: 1
# 2 {
# 1: "APP_VERSION"
# 2: "DEVICE_ID"
# 3 {
# 1 {
# 4 {
# 1: "CPU_ARCH"
# 3: "OS_BUILD"
# 4: "PLATFORM"
# 5: "OS_MAJOR"
# 6: "OS_MINOR"
# 8: "CLIENT_MODEL"
# }
# }
# 2: "SYSTEM_ID"
# }
# }
#
# Provide the extracted values below (except for DEVICE_ID and SYSTEM_ID as it was already provided via -w)
CPU_ARCH = 10
OS_BUILD = 19045
PLATFORM = 2
OS_MAJOR = 9
OS_MINOR = 9
CLIENT_MODEL = 34404
# App version (e.g. '1.2.62.580.g7e3d9a4f')
# Leave empty to auto-generate from USER_AGENT
APP_VERSION = ""
# ---------------------------------------------------------------------
# The following is for using the ConfigCat feature flag service to control features during run-time.
# The primary way is detailed in the documentation under 'Signal Controls (macOS/Linux/Unix)'
# This method works for any operating system supported by ConfigCat, which has a free tier sufficient for this.
# Replace with the USERNAME (API Key) and PASSWORD (API Secret) generated in the ConfigCat Dashboard via 'My API Credentials'.
# The SDK_KEY is is provided via the 'View SDK Key' button.
CONFIGCAT_USERNAME = ""
CONFIGCAT_PASSWORD = ""
CONFIGCAT_SDK_KEY = ""
# Name of each flag assigned in ConfigCat (it's OK to remove ones you will not use)
CONFIGCAT_RELOAD_SECRETS = ""
CONFIGCAT_EMAIL_USERS = ""
CONFIGCAT_EMAIL_SONGS_ALL = ""
CONFIGCAT_EMAIL_SONGS_TRACKED = ""
CONFIGCAT_EMAIL_SONGS_LOOPED = ""
CONFIGCAT_TIMER_INCREASE = ""
CONFIGCAT_TIMER_DECREASE = ""
"""
# -------------------------
# CONFIGURATION SECTION END
# -------------------------
# Default dummy values so linters shut up
# Do not change values below - modify them in the configuration section or config file instead
TOKEN_SOURCE = ""
SP_DC_COOKIE = ""
SP_APP_CLIENT_ID = ""
SP_APP_CLIENT_SECRET = ""
SP_APP_TOKENS_FILE = ""
LOGIN_REQUEST_BODY_FILE = ""
CLIENTTOKEN_REQUEST_BODY_FILE = ""
LOGIN_URL = ""
DEVICE_ID = ""
SYSTEM_ID = ""
USER_URI_ID = ""
REFRESH_TOKEN = ""
CLIENTTOKEN_URL = ""
APP_VERSION = ""
CPU_ARCH = 0
OS_BUILD = 0
PLATFORM = 0
OS_MAJOR = 0
OS_MINOR = 0
CLIENT_MODEL = 0
SMTP_HOST = ""
SMTP_PORT = 0
SMTP_USER = ""
SMTP_PASSWORD = ""
SMTP_SSL = False
SENDER_EMAIL = ""
RECEIVER_EMAIL = ""
ACTIVE_NOTIFICATION = False
INACTIVE_NOTIFICATION = False
TRACK_NOTIFICATION = False
SONG_NOTIFICATION = False
SONG_ON_LOOP_NOTIFICATION = False
ERROR_NOTIFICATION = False
SPOTIFY_CHECK_INTERVAL = 0
SPOTIFY_ERROR_INTERVAL = 0
SPOTIFY_INACTIVITY_CHECK = 0
INACTIVE_EMAIL_RECENT_SONGS_COUNT = 0
PLAYED_FOR_DURATION_TOLERANCE = 0
DETECT_CROSSFADED_SONGS = False
CROSSFADE_DETECTION_MIN = 0.0
CROSSFADE_DETECTION_MAX = 0.0
SPOTIFY_DISAPPEARED_CHECK_INTERVAL = 0
TRACK_SONGS = False
SPOTIFY_MACOS_PLAYING_METHOD = ""
SPOTIFY_LINUX_PLAYING_METHOD = ""
SPOTIFY_WINDOWS_PLAYING_METHOD = ""
SONG_ON_LOOP_VALUE = 0
SKIPPED_SONG_THRESHOLD = 0
SP_USER_GOT_OFFLINE_TRACK_ID = ""
SP_USER_GOT_OFFLINE_DELAY_BEFORE_PAUSE = 0
REMOVED_DISAPPEARED_COUNTER = 0
USER_AGENT = ""
LIVENESS_CHECK_INTERVAL = 0
CHECK_INTERNET_URL = ""
CHECK_INTERNET_TIMEOUT = 0
VERIFY_SSL = False
ERROR_500_NUMBER_LIMIT = 0
ERROR_500_TIME_LIMIT = 0
ERROR_NETWORK_ISSUES_NUMBER_LIMIT = 0
ERROR_NETWORK_ISSUES_TIME_LIMIT = 0
CSV_FILE = ""
MONITOR_LIST_FILE = ""
DOTENV_FILE = ""
FILE_SUFFIX = ""
SP_LOGFILE = ""
DISABLE_LOGGING = False
DEBUG_MODE = False
HORIZONTAL_LINE = 0
CLEAR_SCREEN = False
SPOTIFY_INACTIVITY_CHECK_SIGNAL_VALUE = 0
ENABLE_GENIUS_LYRICS_URL = False
ENABLE_AZLYRICS_URL = False
ENABLE_TEKSTOWO_URL = False
ENABLE_MUSIXMATCH_URL = False
ENABLE_LYRICS_COM_URL = False
ENABLE_APPLE_MUSIC_URL = False
ENABLE_YOUTUBE_MUSIC_URL = False
ENABLE_AMAZON_MUSIC_URL = False
ENABLE_DEEZER_URL = False
ENABLE_TIDAL_URL = False
TOKEN_MAX_RETRIES = 0
TOKEN_RETRY_TIMEOUT = 0.0
SECRET_CIPHER_DICT = {}
SECRET_CIPHER_DICT_URL = ""
TOTP_VER = 0
FLAG_FILE = ""
TRUNCATE_CHARS = 0
SPOTIFY_SUFFIX = ""
CONFIGCAT_USERNAME = ""
CONFIGCAT_PASSWORD = ""
CONFIGCAT_SDK_KEY = ""
CONFIGCAT_RELOAD_SECRETS = ""
CONFIGCAT_EMAIL_USERS = ""
CONFIGCAT_EMAIL_SONGS_ALL = ""
CONFIGCAT_EMAIL_SONGS_TRACKED = ""
CONFIGCAT_EMAIL_SONGS_LOOPED = ""
CONFIGCAT_TIMER_INCREASE = ""
CONFIGCAT_TIMER_DECREASE = ""
CONFIGCAT_NOTIFY = ""
CONFIGCAT_DZ_ALERTS = ""
CONFIGCAT_ORIG_EMAILS = ""
CONFIGCAT_USERNAME_LEGACY = ""
CONFIGCAT_PASSWORD_LEGACY = ""
CONFIGCAT_SDK_KEY_LEGACY = ""
JMK_MODE = False
ALT_VIEW = False
ALT_COOKIE = False
DISCOVERY_ZONE_FOUND_COUNT = 3
DISCOVERY_ZONE_EXCEPTIONS_ALLOWED = 1
#DZ_PLAYLIST_NAME = "Discovery Zone"
#LIKED_PLAYLIST_NAME = "Liked Songs"
INITIAL_STARTUP = True
#sp_tracks = []
#sp_tracks2 = []
# sp_tracks_upper = [] # Discovery Zone
# sp_tracks2_upper = [] # Liked Songs
USER_ID = ""
GMAIL_TAG = ""
ERR_CODE = ""
SEND_NOTIFY = False
DZ_ALERTS = False
ORIG_EMAILS = False
SHOW_CONFIGCAT_FLAGS = True
SP_DC_COOKIE2 = ""
LOGIN_REQUEST_BODY_FILE2 = ""
LOAD_TRACKS_FREQUENCY = 3600 # 1 hour
monitored_playlists_data = {}
DEBUG_JMK = False
count_overridden = False
NTFY_IMAGES = True
# NTFY configuration
NTFY_TOPIC_KEL = "jeoff_spotify_stream"
NTFY_TOPIC_JMK = "jeoff_spotify_stream_jmk"
from datetime import timezone
import threading
from configcatclient.configcatclient import ConfigCatClient
from configcatclient.configcatoptions import ConfigCatOptions, Hooks
from configcatclient.pollingmode import PollingMode
import logging
from io import StringIO, BytesIO
from PIL import Image
exec(CONFIG_BLOCK, globals())
# Default name for the optional config file
DEFAULT_CONFIG_FILENAME = "spotify_monitor.conf"
# List of secret keys to load from env/config
SECRET_KEYS = ("REFRESH_TOKEN", "SP_DC_COOKIE", "SMTP_PASSWORD", "SP_APP_CLIENT_ID", "SP_APP_CLIENT_SECRET")
SECRET_KEYS+= ("SP_DC_COOKIE2", "CONFIGCAT_USERNAME", "CONFIGCAT_PASSWORD", "CONFIGCAT_SDK_KEY", "CONFIGCAT_USERNAME_LEGACY", "CONFIGCAT_PASSWORD_LEGACY", "CONFIGCAT_SDK_KEY_LEGACY")
# Strings removed from track names for generating proper Genius search URLs
re_search_str = r'remaster|extended|original mix|remix|original soundtrack|radio( |-)edit|\(feat\.|( \(.*version\))|( - .*version)'
re_replace_str = r'( - (\d*)( )*remaster$)|( - (\d*)( )*remastered( version)*( \d*)*.*$)|( \((\d*)( )*remaster\)$)|( - (\d+) - remaster$)|( - extended$)|( - extended mix$)|( - (.*); extended mix$)|( - extended version$)|( - (.*) remix$)|( - remix$)|( - remixed by .*$)|( - original mix$)|( - .*original soundtrack$)|( - .*radio( |-)edit$)|( \(feat\. .*\)$)|( \(\d+.*Remaster.*\)$)|( \(.*Version\))|( - .*version)'
# Default value for network-related timeouts in functions; in seconds
FUNCTION_TIMEOUT = 15
# Default value for alarm signal handler timeout; in seconds
ALARM_TIMEOUT = 15
ALARM_RETRY = 10
# Variables for caching functionality of the Spotify 'cookie' access token and 'client' refresh token to avoid unnecessary refreshing
SP_CACHED_ACCESS_TOKEN = None
SP_CACHED_REFRESH_TOKEN = None
SP_ACCESS_TOKEN_EXPIRES_AT = 0
SP_CACHED_CLIENT_ID = ""
# Variables for caching OAuth app access token (Client Credentials Flow)
SP_CACHED_OAUTH_APP_TOKEN = None
# URL of the Spotify Web Player endpoint to get access token
TOKEN_URL = "https://open.spotify.com/api/token"
# URL of the endpoint to get server time needed to create TOTP object
SERVER_TIME_URL = "https://open.spotify.com/"
# Variables for caching functionality of the Spotify client token to avoid unnecessary refreshing
SP_CACHED_CLIENT_TOKEN = None
SP_CLIENT_TOKEN_EXPIRES_AT = 0
LIVENESS_CHECK_COUNTER = LIVENESS_CHECK_INTERVAL / SPOTIFY_CHECK_INTERVAL
stdout_bck = None
csvfieldnames = ['Date', 'Artist', 'Track', 'Playlist', 'Album', 'Last activity']
CLI_CONFIG_PATH = None
# to solve the issue: 'SyntaxError: f-string expression part cannot include a backslash'
nl_ch = "\n"
import sys
if sys.version_info < (3, 6):
print("* Error: Python version 3.6 or higher required !")