-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsession.go
More file actions
137 lines (112 loc) · 4.1 KB
/
Copy pathsession.go
File metadata and controls
137 lines (112 loc) · 4.1 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
package main
import (
"fmt"
"log"
"os"
"time"
"github.com/zmb3/spotify"
)
type ActiveSession struct {
currentSongChannel chan *CurrentSong
client *spotify.Client
sessionCompletedChannel chan bool
}
type CurrentSong struct {
AlbumArtworkUrl string
DetectedColours
Name string
}
type NotRelevantReason int
const (
IncorrectDeviceId NotRelevantReason = iota
NoActiveSession
Null
)
func isRelevantListeningEvent(playerState *spotify.PlayerState) (bool, NotRelevantReason) {
if playerState.CurrentlyPlaying.Playing == true && playerState.Device.Name == os.Getenv("ARTIFY_PLAYER_DEVICE") {
return true, Null
}
if playerState.CurrentlyPlaying.Playing == false {
return false, NoActiveSession
}
return false, IncorrectDeviceId
}
func (a *ActiveSession) emitCurrentSong(currentSong *spotify.FullTrack) {
albumArtColours, err := getAlbumArtworkDominantColours(currentSong.Album.Images[0].URL)
if err != nil {
log.Fatalf("Unable to get dominant colours of image at %s", currentSong.Album.Images[0].URL)
}
// Emit an ActiveSession obj to the channel containing the new song details
a.currentSongChannel <- &CurrentSong{
AlbumArtworkUrl: currentSong.Album.Images[0].URL,
DetectedColours: albumArtColours,
Name: currentSong.Name,
}
}
// Monitors a listening session and emits a CurrentSong via the currentSongChannel
func (a *ActiveSession) observeActiveSession(playerState *spotify.PlayerState) {
playerCooldown := 0
isCurrentlyPlaying := playerState.Playing
a.emitCurrentSong(playerState.Item)
currentSongId := playerState.CurrentlyPlaying.Item.ID
// Continue while a session is continuing, or until 30 seconds after it finishes
for maxPlayerCooldown := 7; playerCooldown < maxPlayerCooldown && isCurrentlyPlaying; {
// Increment the counter if songs have stopped playing
if !isCurrentlyPlaying {
playerCooldown++
} else {
playerCooldown = 0
}
time.Sleep(5 * time.Second)
// Get the current state of the player
currentPlayerState, err := a.client.PlayerState()
if err != nil {
log.Fatalf("Failed to get current player state. Error: %s", err)
}
// If the now playing song is different from the previous
if currentPlayerState.Item.ID != currentSongId {
// Emit the current song to the activeSessionChannel
a.emitCurrentSong(currentPlayerState.Item)
// Update currentSongId with the ID of the current song
currentSongId = currentPlayerState.Item.ID
}
isCurrentlyPlaying, _ = isRelevantListeningEvent(currentPlayerState)
}
a.sessionCompletedChannel <- true
close(a.currentSongChannel)
}
// Used so we can log errors out directly rather than handling them in the
func getPlayerState(client *spotify.Client) *spotify.PlayerState {
playerState, err := client.PlayerState()
if err != nil {
log.Fatalf("Unable to get player state for user %s. Error: %s", user.ID, err)
}
return playerState
}
func monitorForActiveSession(client *spotify.Client) {
for {
playerState, err := client.PlayerState()
if err != nil {
log.Fatalf("Unable to get player state for user %s. Error: %s", user.ID, err)
}
// Check for relevant sessions every 30 seconds, and break once one is found
for isRelevant, _ := isRelevantListeningEvent(playerState); !isRelevant; isRelevant, _ = isRelevantListeningEvent(getPlayerState(client)) {
fmt.Println("No active session found. Waiting...")
time.Sleep(10 * time.Second) // TODO: Check less often overnight/low usage times
}
activeSession := &ActiveSession{
currentSongChannel: make(chan *CurrentSong),
client: client,
sessionCompletedChannel: make(chan bool, 1),
}
// Execute the observe routine, and the routine that updates the lights. These routines communicate via the currentSongChannel
go activeSession.observeActiveSession(playerState)
go activeSession.updateLightColours()
// Once the routines are started, wait for a completion message from the sessionCompletedChannel. Once this is received, break so the whole monitoring loop restarts
for activeSessionComplete := range activeSession.sessionCompletedChannel {
if activeSessionComplete {
break
}
}
}
}