Skip to content

Commit 25450ba

Browse files
authored
Merge pull request #41 from mateusmlo/feat/music-services-fts
Add music services feature toggles
2 parents 8006d7c + 164c591 commit 25450ba

12 files changed

Lines changed: 1275 additions & 91 deletions

File tree

.env.template

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ SERVER_HOST=localhost
44

55
SERVER_ROOT_URL=
66

7+
# Enable music services
8+
ENABLE_SPOTIFY=true
9+
ENABLE_LASTFM=true
10+
ENABLE_APPLEMUSIC=true
11+
712
# Spotify OAuth configuration
813
SPOTIFY_CLIENT_ID=
914
SPOTIFY_CLIENT_SECRET=

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,6 @@ jwk*.json
66
.idea
77
AM_AUTHKEY.p8
88
.DS_Store
9+
node_modules/
10+
911
result

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ You now have to bring your own private key to run piper. Can do this via goat `g
3838
- `SERVER_PORT` - The port piper is hosted on
3939
- `SERVER_HOST` - The server host. `localhost` is fine here, or `0.0.0.0` for docker
4040
- `SERVER_ROOT_URL` - This needs to be the pubically accessible url created in [Setup](#setup). Like `https://piper.teal.fm`
41+
42+
- `ENABLE_SPOTIFY` - Enables Spotify integration and validates envs
43+
- `ENABLE_LASTFM` - Enables Last.fm integration and validates envs
44+
- `ENABLE_APPLEMUSIC` - Enables Apple Music integration and validates envs
45+
46+
4147
- `SPOTIFY_CLIENT_ID` - Client Id from setup in [Spotify developer dashboard](https://developer.spotify.com/documentation/web-api/tutorials/getting-started)
4248
- `SPOTIFY_CLIENT_SECRET` - Client Secret from setup in [Spotify developer dashboard](https://developer.spotify.com/documentation/web-api/tutorials/getting-started)
4349
- `SPOTIFY_AUTH_URL` - most likely `https://accounts.spotify.com/authorize`

cmd/handlers.go

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"net/http"
88
"strconv"
99

10+
"github.com/spf13/viper"
1011
"github.com/teal-fm/piper/db"
1112
"github.com/teal-fm/piper/db/apikey"
1213
"github.com/teal-fm/piper/models"
@@ -44,8 +45,11 @@ func home(database *db.DB, pg *pages.Pages) http.HandlerFunc {
4445
}
4546
params := HomeParams{
4647
NavBar: pages.NavBar{
47-
IsLoggedIn: isLoggedIn,
48-
LastFMUsername: lastfmUsername,
48+
IsLoggedIn: isLoggedIn,
49+
LastFMUsername: lastfmUsername,
50+
SpotifyEnabled: viper.GetBool("enable_spotify"),
51+
LastFMEnabled: viper.GetBool("enable_lastfm"),
52+
AppleMusicEnabled: viper.GetBool("enable_applemusic"),
4953
},
5054
}
5155
err := pg.Execute("home", w, params)
@@ -98,8 +102,11 @@ func handleLinkLastfmForm(database *db.DB, pg *pages.Pages) http.HandlerFunc {
98102
CurrentUsername string
99103
}{
100104
NavBar: pages.NavBar{
101-
IsLoggedIn: authenticated,
102-
LastFMUsername: currentUsername,
105+
IsLoggedIn: authenticated,
106+
LastFMUsername: currentUsername,
107+
SpotifyEnabled: viper.GetBool("enable_spotify"),
108+
LastFMEnabled: viper.GetBool("enable_lastfm"),
109+
AppleMusicEnabled: viper.GetBool("enable_applemusic"),
103110
},
104111
CurrentUsername: currentUsername,
105112
}
@@ -150,7 +157,14 @@ func handleAppleMusicLink(pg *pages.Pages, am *applemusic.Service) http.HandlerF
150157
data := struct {
151158
NavBar pages.NavBar
152159
DevToken string
153-
}{DevToken: devToken}
160+
}{
161+
DevToken: devToken,
162+
NavBar: pages.NavBar{
163+
SpotifyEnabled: viper.GetBool("enable_spotify"),
164+
LastFMEnabled: viper.GetBool("enable_lastfm"),
165+
AppleMusicEnabled: viper.GetBool("enable_applemusic"),
166+
},
167+
}
154168
err := pg.Execute("applemusic_link", w, data)
155169
if err != nil {
156170
log.Printf("Error executing template: %v", err)

cmd/main.go

Lines changed: 101 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -98,52 +98,99 @@ func main() {
9898

9999
mbService := musicbrainz.NewMusicBrainzService(database)
100100
playingNowService := playingnow.NewPlayingNowService(database, atprotoService)
101-
spotifyService := spotify.NewSpotifyService(database, atprotoService, mbService, playingNowService)
102-
lastfmService := lastfm.NewLastFMService(database, viper.GetString("lastfm.api_key"), mbService, atprotoService, playingNowService)
103-
// Read Apple Music settings with env fallbacks
104-
teamID := viper.GetString("applemusic.team_id")
105-
if teamID == "" {
106-
teamID = viper.GetString("APPLE_MUSIC_TEAM_ID")
107-
}
108-
keyID := viper.GetString("applemusic.key_id")
109-
if keyID == "" {
110-
keyID = viper.GetString("APPLE_MUSIC_KEY_ID")
101+
102+
// Check feature toggles for music services
103+
enableSpotify := viper.GetBool("enable_spotify")
104+
enableLastFM := viper.GetBool("enable_lastfm")
105+
enableAppleMusic := viper.GetBool("enable_applemusic")
106+
107+
var spotifyService *spotify.Service
108+
var lastfmService *lastfm.Service
109+
var appleMusicService *applemusic.Service
110+
111+
// Initialize Spotify service if enabled and credentials are present
112+
if enableSpotify {
113+
clientID := viper.GetString("spotify.client_id")
114+
clientSecret := viper.GetString("spotify.client_secret")
115+
116+
if clientID != "" && clientSecret != "" {
117+
spotifyService = spotify.NewSpotifyService(database, atprotoService, mbService, playingNowService)
118+
log.Println("Spotify service enabled and configured")
119+
} else {
120+
log.Println("Spotify enabled but credentials missing (client_id or client_secret). Spotify features will be disabled.")
121+
}
122+
} else {
123+
log.Println("Spotify service disabled via ENABLE_SPOTIFY=false")
111124
}
112-
keyPath := viper.GetString("applemusic.private_key_path")
113-
if keyPath == "" {
114-
keyPath = viper.GetString("APPLE_MUSIC_PRIVATE_KEY_PATH")
125+
126+
// Initialize Last.fm service if enabled and API key is present
127+
if enableLastFM {
128+
apiKey := viper.GetString("lastfm.api_key")
129+
130+
if apiKey != "" {
131+
lastfmService = lastfm.NewLastFMService(database, apiKey, mbService, atprotoService, playingNowService)
132+
log.Println("Last.fm service enabled and configured")
133+
} else {
134+
log.Println("Last.fm enabled but API key missing. Last.fm features will be disabled.")
135+
}
136+
} else {
137+
log.Println("Last.fm service disabled via ENABLE_LASTFM=false")
115138
}
116139

117-
var appleMusicService *applemusic.Service
118-
// Only initialize Apple Music service if all required credentials are present
119-
if teamID != "" && keyID != "" && keyPath != "" {
120-
appleMusicService = applemusic.NewService(
121-
teamID,
122-
keyID,
123-
keyPath,
124-
).WithPersistence(
125-
func() (string, time.Time, bool, error) {
126-
return database.GetAppleMusicDeveloperToken()
127-
},
128-
func(token string, exp time.Time) error {
129-
return database.SaveAppleMusicDeveloperToken(token, exp)
130-
},
131-
).WithDeps(database, atprotoService, mbService, playingNowService)
140+
// Initialize Apple Music service if enabled and credentials are present
141+
if enableAppleMusic {
142+
// Read Apple Music settings with env fallbacks
143+
teamID := viper.GetString("applemusic.team_id")
144+
if teamID == "" {
145+
teamID = viper.GetString("APPLE_MUSIC_TEAM_ID")
146+
}
147+
keyID := viper.GetString("applemusic.key_id")
148+
if keyID == "" {
149+
keyID = viper.GetString("APPLE_MUSIC_KEY_ID")
150+
}
151+
keyPath := viper.GetString("applemusic.private_key_path")
152+
if keyPath == "" {
153+
keyPath = viper.GetString("APPLE_MUSIC_PRIVATE_KEY_PATH")
154+
}
155+
156+
// Only initialize if all required credentials are present
157+
if teamID != "" && keyID != "" && keyPath != "" {
158+
appleMusicService = applemusic.NewService(
159+
teamID,
160+
keyID,
161+
keyPath,
162+
).WithPersistence(
163+
func() (string, time.Time, bool, error) {
164+
return database.GetAppleMusicDeveloperToken()
165+
},
166+
func(token string, exp time.Time) error {
167+
return database.SaveAppleMusicDeveloperToken(token, exp)
168+
},
169+
).WithDeps(database, atprotoService, mbService, playingNowService)
170+
log.Println("Apple Music service enabled and configured")
171+
} else {
172+
log.Println("Apple Music enabled but credentials missing (team_id, key_id, or private_key_path). Apple Music features will be disabled.")
173+
}
132174
} else {
133-
log.Println("Apple Music credentials not configured (missing team_id, key_id, or private_key_path). Apple Music features will be disabled.")
175+
log.Println("Apple Music service disabled via ENABLE_APPLEMUSIC=false")
134176
}
135177

136178
oauthManager := oauth.NewOAuthServiceManager()
137179

138-
spotifyOAuth := oauth.NewOAuth2Service(
139-
viper.GetString("spotify.client_id"),
140-
viper.GetString("spotify.client_secret"),
141-
viper.GetString("callback.spotify"),
142-
viper.GetStringSlice("spotify.scopes"),
143-
"spotify",
144-
spotifyService,
145-
)
146-
oauthManager.RegisterService("spotify", spotifyOAuth)
180+
// Register Spotify OAuth service only if Spotify is enabled and configured
181+
if spotifyService != nil {
182+
spotifyOAuth := oauth.NewOAuth2Service(
183+
viper.GetString("spotify.client_id"),
184+
viper.GetString("spotify.client_secret"),
185+
viper.GetString("callback.spotify"),
186+
viper.GetStringSlice("spotify.scopes"),
187+
"spotify",
188+
spotifyService,
189+
)
190+
oauthManager.RegisterService("spotify", spotifyOAuth)
191+
log.Println("Spotify OAuth service registered")
192+
}
193+
147194
oauthManager.RegisterService("atproto", atprotoService)
148195

149196
apiKeyService := apikeyService.NewAPIKeyService(database, sessionManager)
@@ -162,18 +209,27 @@ func main() {
162209
}
163210

164211
trackerInterval := time.Duration(viper.GetInt("tracker.interval")) * time.Second
165-
lastfmInterval := time.Duration(viper.GetInt("lastfm.interval_seconds")) * time.Second // Add config for Last.fm interval
166-
if lastfmInterval <= 0 {
167-
lastfmInterval = 30 * time.Second
212+
213+
// Start Spotify listening tracker if service is configured
214+
if spotifyService != nil {
215+
go spotifyService.StartListeningTracker(trackerInterval)
216+
log.Println("Spotify listening tracker started")
168217
}
169218

170-
go spotifyService.StartListeningTracker(trackerInterval)
219+
// Start Last.fm listening tracker if service is configured
220+
if lastfmService != nil {
221+
lastfmInterval := time.Duration(viper.GetInt("lastfm.interval_seconds")) * time.Second
222+
if lastfmInterval <= 0 {
223+
lastfmInterval = 30 * time.Second
224+
}
225+
go lastfmService.StartListeningTracker(lastfmInterval)
226+
log.Println("Last.fm listening tracker started")
227+
}
171228

172-
go lastfmService.StartListeningTracker(lastfmInterval)
173-
// Apple Music tracker uses same tracker.interval as Spotify for now
174-
// Only start if Apple Music service is configured
229+
// Start Apple Music tracker if service is configured
175230
if appleMusicService != nil {
176231
go appleMusicService.StartListeningTracker(trackerInterval)
232+
log.Println("Apple Music listening tracker started")
177233
}
178234

179235
serverAddr := fmt.Sprintf("%s:%s", viper.GetString("server.host"), viper.GetString("server.port"))

config/config.go

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ func Load() {
2424
viper.SetDefault("tracker.interval", 30)
2525
viper.SetDefault("db.path", "./data/piper.db")
2626

27+
// Feature toggles for music services (default to true for backwards compatibility)
28+
viper.SetDefault("enable_spotify", true)
29+
viper.SetDefault("enable_lastfm", true)
30+
viper.SetDefault("enable_applemusic", true)
31+
2732
// Apple Music defaults
2833
viper.SetDefault("applemusic.team_id", "")
2934
viper.SetDefault("applemusic.key_id", "")
@@ -58,17 +63,6 @@ func Load() {
5863
log.Println("Using config file:", viper.ConfigFileUsed())
5964
}
6065

61-
// check for required settings
62-
requiredVars := []string{"spotify.client_id", "spotify.client_secret"}
63-
var missingVars []string
64-
65-
for _, v := range requiredVars {
66-
if !viper.IsSet(v) {
67-
missingVars = append(missingVars, v)
68-
}
69-
}
70-
71-
if len(missingVars) > 0 {
72-
log.Fatalf("Required configuration variables not set: %s", strings.Join(missingVars, ", "))
73-
}
66+
// No required music service settings - all services are optional
67+
// Services will be enabled based on feature toggles and available credentials
7468
}

0 commit comments

Comments
 (0)