+3
-3
cmd/main.go
+3
-3
cmd/main.go
···
108
108
lastfmInterval = 30 * time.Second
109
109
}
110
110
111
-
if err := spotifyService.LoadAllUsers(); err != nil {
112
-
log.Printf("Warning: Failed to preload Spotify users: %v", err)
113
-
}
111
+
//if err := spotifyService.LoadAllUsers(); err != nil {
112
+
// log.Printf("Warning: Failed to preload Spotify users: %v", err)
113
+
//}
114
114
go spotifyService.StartListeningTracker(trackerInterval)
115
115
116
116
go lastfmService.StartListeningTracker(lastfmInterval)
+9
-7
oauth/oauth_manager.go
+9
-7
oauth/oauth_manager.go
···
15
15
services map[string]AuthService
16
16
sessionManager *session.SessionManager
17
17
mu sync.RWMutex
18
+
logger *log.Logger
18
19
}
19
20
20
21
func NewOAuthServiceManager(sessionManager *session.SessionManager) *OAuthServiceManager {
21
22
return &OAuthServiceManager{
22
23
services: make(map[string]AuthService),
23
24
sessionManager: sessionManager,
25
+
logger: log.New(log.Writer(), "oauth: ", log.LstdFlags|log.Lmsgprefix),
24
26
}
25
27
}
26
28
···
29
31
m.mu.Lock()
30
32
defer m.mu.Unlock()
31
33
m.services[name] = service
32
-
log.Printf("Registered auth service: %s", name)
34
+
m.logger.Printf("Registered auth service: %s", name)
33
35
}
34
36
35
37
// get an AuthService by registered name
···
51
53
return
52
54
}
53
55
54
-
log.Printf("Auth service '%s' not found for login request", serviceName)
56
+
m.logger.Printf("Auth service '%s' not found for login request", serviceName)
55
57
http.Error(w, fmt.Sprintf("Auth service '%s' not found", serviceName), http.StatusNotFound)
56
58
}
57
59
}
···
62
64
service, exists := m.services[serviceName]
63
65
m.mu.RUnlock()
64
66
65
-
log.Printf("Logging in with service %s", serviceName)
67
+
m.logger.Printf("Logging in with service %s", serviceName)
66
68
67
69
if !exists {
68
-
log.Printf("Auth service '%s' not found for callback request", serviceName)
70
+
m.logger.Printf("Auth service '%s' not found for callback request", serviceName)
69
71
http.Error(w, fmt.Sprintf("OAuth service '%s' not found", serviceName), http.StatusNotFound)
70
72
return
71
73
}
···
73
75
userID, err := service.HandleCallback(w, r)
74
76
75
77
if err != nil {
76
-
log.Printf("Error handling callback for service '%s': %v", serviceName, err)
78
+
m.logger.Printf("Error handling callback for service '%s': %v", serviceName, err)
77
79
http.Error(w, fmt.Sprintf("Error handling callback for service '%s'", serviceName), http.StatusInternalServerError)
78
80
return
79
81
}
···
83
85
84
86
m.sessionManager.SetSessionCookie(w, session)
85
87
86
-
log.Printf("Created session for user %d via service %s", userID, serviceName)
88
+
m.logger.Printf("Created session for user %d via service %s", userID, serviceName)
87
89
88
90
http.Redirect(w, r, "/", http.StatusSeeOther)
89
91
} else {
90
-
log.Printf("Callback for service '%s' did not result in a valid user ID.", serviceName)
92
+
m.logger.Printf("Callback for service '%s' did not result in a valid user ID.", serviceName)
91
93
// todo: redirect to an error page
92
94
// right now this just redirects home but we don't want this behaviour ideally
93
95
http.Redirect(w, r, "/", http.StatusSeeOther)
+45
-40
service/lastfm/lastfm.go
+45
-40
service/lastfm/lastfm.go
···
9
9
"log"
10
10
"net/http"
11
11
"net/url"
12
+
"os"
12
13
"strconv"
13
14
"sync"
14
15
"time"
···
40
41
atprotoService *atprotoauth.ATprotoAuthService
41
42
lastSeenNowPlaying map[string]Track
42
43
mu sync.Mutex
44
+
logger *log.Logger
43
45
}
44
46
45
47
func NewLastFMService(db *db.DB, apiKey string, musicBrainzService *musicbrainz.MusicBrainzService, atprotoService *atprotoauth.ATprotoAuthService) *LastFMService {
48
+
logger := log.New(os.Stdout, "lastfm: ", log.LstdFlags|log.Lmsgprefix)
49
+
46
50
return &LastFMService{
47
51
db: db,
48
52
httpClient: &http.Client{
···
56
60
musicBrainzService: musicBrainzService,
57
61
lastSeenNowPlaying: make(map[string]Track),
58
62
mu: sync.Mutex{},
63
+
logger: logger,
59
64
}
60
65
}
61
66
62
67
func (l *LastFMService) loadUsernames() error {
63
68
u, err := l.db.GetAllUsersWithLastFM()
64
69
if err != nil {
65
-
log.Printf("Error loading users with Last.fm from DB: %v", err)
70
+
l.logger.Printf("Error loading users with Last.fm from DB: %v", err)
66
71
return fmt.Errorf("failed to load users from database: %w", err)
67
72
}
68
73
usernames := make([]string, len(u))
···
71
76
if user.LastFMUsername != nil { // Check if the username is set
72
77
usernames[i] = *user.LastFMUsername
73
78
} else {
74
-
log.Printf("User ID %d has Last.fm enabled but no username set", user.ID)
79
+
l.logger.Printf("User ID %d has Last.fm enabled but no username set", user.ID)
75
80
}
76
81
}
77
82
···
84
89
}
85
90
86
91
l.Usernames = filteredUsernames
87
-
log.Printf("Loaded %d Last.fm usernames", len(l.Usernames))
92
+
l.logger.Printf("Loaded %d Last.fm usernames", len(l.Usernames))
88
93
89
94
return nil
90
95
}
···
113
118
return nil, fmt.Errorf("failed to create request for %s: %w", username, err)
114
119
}
115
120
116
-
log.Printf("Fetching recent tracks for user: %s", username)
121
+
l.logger.Printf("Fetching recent tracks for user: %s", username)
117
122
resp, err := l.httpClient.Do(req)
118
123
if err != nil {
119
124
return nil, fmt.Errorf("failed to fetch recent tracks for %s: %w", username, err)
···
134
139
}
135
140
if err := json.Unmarshal(bodyBytes, &recentTracksResp); err != nil {
136
141
// Log the body content that failed to decode
137
-
log.Printf("Failed to decode response body for %s: %s", username, string(bodyBytes))
142
+
l.logger.Printf("Failed to decode response body for %s: %s", username, string(bodyBytes))
138
143
return nil, fmt.Errorf("failed to decode response for %s: %w", username, err)
139
144
}
140
145
141
146
if len(recentTracksResp.RecentTracks.Tracks) > 0 {
142
-
log.Printf("Fetched %d tracks for %s. Most recent: %s - %s",
147
+
l.logger.Printf("Fetched %d tracks for %s. Most recent: %s - %s",
143
148
len(recentTracksResp.RecentTracks.Tracks),
144
149
username,
145
150
recentTracksResp.RecentTracks.Tracks[0].Artist.Text,
146
151
recentTracksResp.RecentTracks.Tracks[0].Name)
147
152
} else {
148
-
log.Printf("No recent tracks found for %s", username)
153
+
l.logger.Printf("No recent tracks found for %s", username)
149
154
}
150
155
151
156
return &recentTracksResp, nil
···
153
158
154
159
func (l *LastFMService) StartListeningTracker(interval time.Duration) {
155
160
if err := l.loadUsernames(); err != nil {
156
-
log.Printf("Failed to perform initial username load: %v", err)
161
+
l.logger.Printf("Failed to perform initial username load: %v", err)
157
162
// Decide if we should proceed without initial load or return error
158
163
}
159
164
160
165
if len(l.Usernames) == 0 {
161
-
log.Println("No Last.fm users configured. Tracker will run but fetch cycles will be skipped until users are added.")
166
+
l.logger.Println("No Last.fm users configured. Tracker will run but fetch cycles will be skipped until users are added.")
162
167
} else {
163
-
log.Printf("Found %d Last.fm users.", len(l.Usernames))
168
+
l.logger.Printf("Found %d Last.fm users.", len(l.Usernames))
164
169
}
165
170
166
171
ticker := time.NewTicker(interval)
···
169
174
if len(l.Usernames) > 0 {
170
175
l.fetchAllUserTracks(context.Background())
171
176
} else {
172
-
log.Println("Skipping initial fetch cycle as no users are configured.")
177
+
l.logger.Println("Skipping initial fetch cycle as no users are configured.")
173
178
}
174
179
175
180
for {
···
177
182
case <-ticker.C:
178
183
// refresh usernames periodically from db
179
184
if err := l.loadUsernames(); err != nil {
180
-
log.Printf("Error reloading usernames in ticker: %v", err)
185
+
l.logger.Printf("Error reloading usernames in ticker: %v", err)
181
186
// Continue ticker loop even if reload fails? Or log and potentially stop?
182
187
continue // Continue for now
183
188
}
184
189
if len(l.Usernames) > 0 {
185
190
l.fetchAllUserTracks(context.Background())
186
191
} else {
187
-
log.Println("No Last.fm users configured. Skipping fetch cycle.")
192
+
l.logger.Println("No Last.fm users configured. Skipping fetch cycle.")
188
193
}
189
194
// TODO: Implement graceful shutdown using context cancellation
190
195
// case <-ctx.Done():
191
-
// log.Println("Stopping Last.fm listening tracker.")
196
+
// l.logger.Println("Stopping Last.fm listening tracker.")
192
197
// ticker.Stop()
193
198
// return
194
199
}
195
200
}
196
201
}()
197
202
198
-
log.Printf("Last.fm Listening Tracker started with interval %v", interval)
203
+
l.logger.Printf("Last.fm Listening Tracker started with interval %v", interval)
199
204
}
200
205
201
206
// fetchAllUserTracks iterates through users and fetches their tracks.
202
207
func (l *LastFMService) fetchAllUserTracks(ctx context.Context) {
203
-
log.Printf("Starting fetch cycle for %d users...", len(l.Usernames))
208
+
l.logger.Printf("Starting fetch cycle for %d users...", len(l.Usernames))
204
209
var wg sync.WaitGroup // Use WaitGroup to fetch concurrently (optional)
205
210
fetchErrors := make(chan error, len(l.Usernames)) // Channel for errors
206
211
207
212
for _, username := range l.Usernames {
208
213
if ctx.Err() != nil {
209
-
log.Printf("Context cancelled before starting fetch for user %s.", username)
214
+
l.logger.Printf("Context cancelled before starting fetch for user %s.", username)
210
215
break // Exit loop if context is cancelled
211
216
}
212
217
···
214
219
go func(uname string) { // Launch fetch and process in a goroutine per user
215
220
defer wg.Done()
216
221
if ctx.Err() != nil {
217
-
log.Printf("Context cancelled during fetch cycle for user %s.", uname)
222
+
l.logger.Printf("Context cancelled during fetch cycle for user %s.", uname)
218
223
return // Exit goroutine if context is cancelled
219
224
}
220
225
···
223
228
const fetchLimit = 5
224
229
recentTracks, err := l.getRecentTracks(ctx, uname, fetchLimit)
225
230
if err != nil {
226
-
log.Printf("Error fetching tracks for %s: %v", uname, err)
231
+
l.logger.Printf("Error fetching tracks for %s: %v", uname, err)
227
232
fetchErrors <- fmt.Errorf("fetch failed for %s: %w", uname, err) // Report error
228
233
return
229
234
}
230
235
231
236
if recentTracks == nil || len(recentTracks.RecentTracks.Tracks) == 0 {
232
-
log.Printf("No tracks returned for user %s", uname)
237
+
l.logger.Printf("No tracks returned for user %s", uname)
233
238
return
234
239
}
235
240
236
241
// Process the fetched tracks
237
242
if err := l.processTracks(ctx, uname, recentTracks.RecentTracks.Tracks); err != nil {
238
-
log.Printf("Error processing tracks for %s: %v", uname, err)
243
+
l.logger.Printf("Error processing tracks for %s: %v", uname, err)
239
244
fetchErrors <- fmt.Errorf("process failed for %s: %w", uname, err) // Report error
240
245
}
241
246
}(username)
···
247
252
// Log any errors that occurred during the fetch cycle
248
253
errorCount := 0
249
254
for err := range fetchErrors {
250
-
log.Printf("Fetch cycle error: %v", err)
255
+
l.logger.Printf("Fetch cycle error: %v", err)
251
256
errorCount++
252
257
}
253
258
254
259
if errorCount > 0 {
255
-
log.Printf("Finished fetch cycle with %d errors.", errorCount)
260
+
l.logger.Printf("Finished fetch cycle with %d errors.", errorCount)
256
261
} else {
257
-
log.Println("Finished fetch cycle successfully.")
262
+
l.logger.Println("Finished fetch cycle successfully.")
258
263
}
259
264
}
260
265
···
274
279
}
275
280
276
281
if lastKnownTimestamp == nil {
277
-
log.Printf("no previous scrobble timestamp found for user %s. processing latest track.", username)
282
+
l.logger.Printf("no previous scrobble timestamp found for user %s. processing latest track.", username)
278
283
} else {
279
-
log.Printf("last known scrobble for %s was at %s", username, lastKnownTimestamp.Format(time.RFC3339))
284
+
l.logger.Printf("last known scrobble for %s was at %s", username, lastKnownTimestamp.Format(time.RFC3339))
280
285
}
281
286
282
287
var (
···
287
292
// handle now playing track separately
288
293
if len(tracks) > 0 && tracks[0].Attr != nil && tracks[0].Attr.NowPlaying == "true" {
289
294
nowPlayingTrack := tracks[0]
290
-
log.Printf("now playing track for %s: %s - %s", username, nowPlayingTrack.Artist.Text, nowPlayingTrack.Name)
295
+
l.logger.Printf("now playing track for %s: %s - %s", username, nowPlayingTrack.Artist.Text, nowPlayingTrack.Name)
291
296
l.mu.Lock()
292
297
lastSeen, existed := l.lastSeenNowPlaying[username]
293
298
// if our current track matches with last seen
294
299
// just compare artist/album/name for now
295
300
if existed && lastSeen.Album == nowPlayingTrack.Album && lastSeen.Name == nowPlayingTrack.Name && lastSeen.Artist == nowPlayingTrack.Artist {
296
-
log.Printf("current track matches last seen track for %s", username)
301
+
l.logger.Printf("current track matches last seen track for %s", username)
297
302
} else {
298
-
log.Printf("current track does not match last seen track for %s", username)
303
+
l.logger.Printf("current track does not match last seen track for %s", username)
299
304
// aha! we record this!
300
305
l.lastSeenNowPlaying[username] = nowPlayingTrack
301
306
}
···
312
317
}
313
318
314
319
if lastNonNowPlaying == nil {
315
-
log.Printf("no non-now-playing tracks found for user %s.", username)
320
+
l.logger.Printf("no non-now-playing tracks found for user %s.", username)
316
321
return nil
317
322
}
318
323
319
324
latestTrackTime := lastNonNowPlaying.Date
320
325
321
326
// print both
322
-
fmt.Printf("latestTrackTime: %s\n", latestTrackTime)
323
-
fmt.Printf("lastKnownTimestamp: %s\n", lastKnownTimestamp)
327
+
l.logger.Printf("latestTrackTime: %s\n", latestTrackTime)
328
+
l.logger.Printf("lastKnownTimestamp: %s\n", lastKnownTimestamp)
324
329
325
330
if lastKnownTimestamp != nil && lastKnownTimestamp.Equal(latestTrackTime.Time) {
326
-
log.Printf("no new tracks to process for user %s.", username)
331
+
l.logger.Printf("no new tracks to process for user %s.", username)
327
332
return nil
328
333
}
329
334
330
335
for _, track := range tracks {
331
336
if track.Date == nil {
332
-
log.Printf("skipping track without timestamp for %s: %s - %s", username, track.Artist.Text, track.Name)
337
+
l.logger.Printf("skipping track without timestamp for %s: %s - %s", username, track.Artist.Text, track.Name)
333
338
continue
334
339
}
335
340
···
337
342
// before or at last known
338
343
if lastKnownTimestamp != nil && (trackTime.Before(*lastKnownTimestamp) || trackTime.Equal(*lastKnownTimestamp)) {
339
344
if processedCount == 0 {
340
-
log.Printf("reached already known scrobbles for user %s (track time: %s, last known: %s).",
345
+
l.logger.Printf("reached already known scrobbles for user %s (track time: %s, last known: %s).",
341
346
username, trackTime.Format(time.RFC3339), lastKnownTimestamp.Format(time.RFC3339))
342
347
}
343
348
break
···
360
365
361
366
hydratedTrack, err := musicbrainz.HydrateTrack(l.musicBrainzService, baseTrack)
362
367
if err != nil {
363
-
log.Printf("error hydrating track for user %s: %s - %s: %v", username, track.Artist.Text, track.Name, err)
368
+
l.logger.Printf("error hydrating track for user %s: %s - %s: %v", username, track.Artist.Text, track.Name, err)
364
369
// we can use the track without MBIDs, it's still valid
365
370
hydratedTrack = &baseTrack
366
371
}
367
372
l.db.SaveTrack(user.ID, hydratedTrack)
368
-
log.Printf("Submitting track")
373
+
l.logger.Printf("Submitting track")
369
374
err = l.SubmitTrackToPDS(*user.ATProtoDID, hydratedTrack, ctx)
370
375
if err != nil {
371
-
log.Printf("error submitting track for user %s: %s - %s: %v", username, track.Artist.Text, track.Name, err)
376
+
l.logger.Printf("error submitting track for user %s: %s - %s: %v", username, track.Artist.Text, track.Name, err)
372
377
}
373
378
processedCount++
374
379
···
382
387
}
383
388
384
389
if processedCount > 0 {
385
-
log.Printf("processed %d new track(s) for user %s. latest timestamp: %s",
390
+
l.logger.Printf("processed %d new track(s) for user %s. latest timestamp: %s",
386
391
processedCount, username, latestProcessedTime.Format(time.RFC3339))
387
392
}
388
393
···
407
412
}
408
413
409
414
// printout the session details
410
-
fmt.Printf("Submitting track for the did: %+v\n", sess.DID)
415
+
l.logger.Printf("Submitting track for the did: %+v\n", sess.DID)
411
416
412
417
artists := make([]*teal.AlphaFeedDefs_Artist, 0, len(track.Artist))
413
418
for _, a := range track.Artist {
+11
-8
service/musicbrainz/musicbrainz.go
+11
-8
service/musicbrainz/musicbrainz.go
···
8
8
"log"
9
9
"net/http"
10
10
"net/url"
11
+
"os"
11
12
"sort"
12
13
"strings"
13
14
"sync" // Added for mutex
···
75
76
cacheMutex sync.RWMutex // Mutex to protect the cache
76
77
cacheTTL time.Duration // Time-to-live for cache entries
77
78
cleaner MetadataCleaner // Cleaner for cleaning up expired cache entries
79
+
logger *log.Logger // Logger for logging
78
80
}
79
81
80
82
// NewMusicBrainzService creates a new service instance with rate limiting and caching.
···
83
85
limiter := rate.NewLimiter(rate.Every(time.Second), 1)
84
86
// Set a default cache TTL (e.g., 1 hour)
85
87
defaultCacheTTL := 1 * time.Hour
86
-
88
+
logger := log.New(os.Stdout, "musicbrainz: ", log.LstdFlags|log.Lmsgprefix)
87
89
return &MusicBrainzService{
88
90
db: db,
89
91
httpClient: &http.Client{
···
94
96
cacheTTL: defaultCacheTTL, // Set the cache TTL
95
97
cleaner: *NewMetadataCleaner("Latin"), // Initialize the cleaner
96
98
// cacheMutex is zero-value ready
99
+
logger: logger,
97
100
}
98
101
}
99
102
···
127
130
s.cacheMutex.RUnlock()
128
131
129
132
if found && now.Before(entry.expiresAt) {
130
-
log.Printf("Cache hit for MusicBrainz search: key=%s", cacheKey)
133
+
s.logger.Printf("Cache hit for MusicBrainz search: key=%s", cacheKey)
131
134
// Return the cached data directly. Consider if a deep copy is needed if callers modify results.
132
135
return entry.recordings, nil
133
136
}
134
137
// --- Cache Miss or Expired ---
135
138
if found {
136
-
log.Printf("Cache expired for MusicBrainz search: key=%s", cacheKey)
139
+
s.logger.Printf("Cache expired for MusicBrainz search: key=%s", cacheKey)
137
140
} else {
138
-
log.Printf("Cache miss for MusicBrainz search: key=%s", cacheKey)
141
+
s.logger.Printf("Cache miss for MusicBrainz search: key=%s", cacheKey)
139
142
}
140
143
141
144
// --- Proceed with API call ---
···
191
194
expiresAt: time.Now().UTC().Add(s.cacheTTL),
192
195
}
193
196
s.cacheMutex.Unlock()
194
-
log.Printf("Cached MusicBrainz search result for key=%s, TTL=%s", cacheKey, s.cacheTTL)
197
+
s.logger.Printf("Cached MusicBrainz search result for key=%s, TTL=%s", cacheKey, s.cacheTTL)
195
198
196
199
// Return the newly fetched results
197
200
return result.Recordings, nil
198
201
}
199
202
200
203
// GetBestRelease selects the 'best' release from a list based on specific criteria.
201
-
func GetBestRelease(releases []MusicBrainzRelease, trackTitle string) *MusicBrainzRelease {
204
+
func (s *MusicBrainzService) GetBestRelease(releases []MusicBrainzRelease, trackTitle string) *MusicBrainzRelease {
202
205
if len(releases) == 0 {
203
206
return nil
204
207
}
···
251
254
}
252
255
253
256
// 3. If none found, return the oldest release overall (which is the first one after sorting)
254
-
log.Printf("Could not find a suitable release for '%s', picking oldest: '%s' (%s)", trackTitle, releases[0].Title, releases[0].ID)
257
+
s.logger.Printf("Could not find a suitable release for '%s', picking oldest: '%s' (%s)", trackTitle, releases[0].Title, releases[0].ID)
255
258
r := releases[0]
256
259
return &r
257
260
}
···
279
282
}
280
283
281
284
firstResult := res[0]
282
-
firstResultAlbum := GetBestRelease(firstResult.Releases, firstResult.Title)
285
+
firstResultAlbum := mb.GetBestRelease(firstResult.Releases, firstResult.Title)
283
286
284
287
// woof. we Might not have any ISRCs!
285
288
var bestISRC string
+47
-42
service/spotify/spotify.go
+47
-42
service/spotify/spotify.go
···
9
9
"log"
10
10
"net/http"
11
11
"net/url"
12
+
"os"
12
13
"strings"
13
14
"sync"
14
15
"time"
···
34
35
userTracks map[int64]*models.Track
35
36
userTokens map[int64]string
36
37
mu sync.RWMutex
38
+
logger *log.Logger
37
39
}
38
40
39
41
func NewSpotifyService(database *db.DB, atprotoService *atprotoauth.ATprotoAuthService, musicBrainzService *musicbrainz.MusicBrainzService) *SpotifyService {
42
+
logger := log.New(os.Stdout, "spotify: ", log.LstdFlags|log.Lmsgprefix)
43
+
40
44
return &SpotifyService{
41
45
DB: database,
42
46
atprotoService: atprotoService,
43
47
mb: musicBrainzService,
44
48
userTracks: make(map[int64]*models.Track),
45
49
userTokens: make(map[int64]string),
50
+
logger: logger,
46
51
}
47
52
}
48
53
49
54
func (s *SpotifyService) SubmitTrackToPDS(did string, track *models.Track, ctx context.Context) error {
50
55
client, err := s.atprotoService.GetATProtoClient()
51
56
if err != nil || client == nil {
52
-
log.Printf("Error getting ATProto client: %v", err)
57
+
s.logger.Printf("Error getting ATProto client: %v", err)
53
58
return fmt.Errorf("failed to get ATProto client: %w", err)
54
59
}
55
60
···
110
115
111
116
var out atproto.RepoCreateRecord_Output
112
117
if err := xrpcClient.Do(ctx, authArgs, xrpc.Procedure, "application/json", "com.atproto.repo.createRecord", nil, input, &out); err != nil {
113
-
log.Printf("Error creating record for DID %s: %v. Input: %+v", did, err, input)
118
+
s.logger.Printf("Error creating record for DID %s: %v. Input: %+v", did, err, input)
114
119
return fmt.Errorf("failed to create record on PDS for DID %s: %w", did, err)
115
120
}
116
121
117
-
log.Printf("Successfully submitted track '%s' to PDS for DID %s. Record URI: %s", track.Name, did, out.Uri)
122
+
s.logger.Printf("Successfully submitted track '%s' to PDS for DID %s. Record URI: %s", track.Name, did, out.Uri)
118
123
return nil
119
124
}
120
125
121
126
func (s *SpotifyService) SetAccessToken(token string, refreshToken string, userId int64, hasSession bool) (int64, error) {
122
127
userID, err := s.identifyAndStoreUser(token, refreshToken, userId, hasSession)
123
128
if err != nil {
124
-
log.Printf("Error identifying and storing user: %v", err)
129
+
s.logger.Printf("Error identifying and storing user: %v", err)
125
130
return 0, err
126
131
}
127
132
return userID, nil
···
130
135
func (s *SpotifyService) identifyAndStoreUser(token string, refreshToken string, userId int64, hasSession bool) (int64, error) {
131
136
userProfile, err := s.fetchSpotifyProfile(token)
132
137
if err != nil {
133
-
log.Printf("Error fetching Spotify profile: %v", err)
138
+
s.logger.Printf("Error fetching Spotify profile: %v", err)
134
139
return 0, err
135
140
}
136
141
137
-
fmt.Printf("uid: %d hasSession: %t", userId, hasSession)
142
+
s.logger.Printf("uid: %d hasSession: %t", userId, hasSession)
138
143
139
144
user, err := s.DB.GetUserBySpotifyID(userProfile.ID)
140
145
if err != nil {
141
146
// This error might mean DB connection issue, not just user not found.
142
-
log.Printf("Error checking for user by Spotify ID %s: %v", userProfile.ID, err)
147
+
s.logger.Printf("Error checking for user by Spotify ID %s: %v", userProfile.ID, err)
143
148
return 0, err
144
149
}
145
150
···
148
153
// We don't intend users to log in via spotify!
149
154
if user == nil {
150
155
if !hasSession {
151
-
log.Printf("User does not seem to exist")
156
+
s.logger.Printf("User does not seem to exist")
152
157
return 0, fmt.Errorf("user does not seem to exist")
153
158
} else {
154
159
// overwrite prev user
155
160
user, err = s.DB.AddSpotifySession(userId, userProfile.DisplayName, userProfile.Email, userProfile.ID, token, refreshToken, tokenExpiryTime)
156
161
if err != nil {
157
-
log.Printf("Error adding Spotify session for user ID %d: %v", userId, err)
162
+
s.logger.Printf("Error adding Spotify session for user ID %d: %v", userId, err)
158
163
return 0, err
159
164
}
160
165
}
···
162
167
err = s.DB.UpdateUserToken(user.ID, token, refreshToken, tokenExpiryTime)
163
168
if err != nil {
164
169
// for now log and continue
165
-
log.Printf("Error updating user token for user ID %d: %v", user.ID, err)
170
+
s.logger.Printf("Error updating user token for user ID %d: %v", user.ID, err)
166
171
} else {
167
-
log.Printf("Updated token for existing user: %s (ID: %d)", *user.Username, user.ID)
172
+
s.logger.Printf("Updated token for existing user: %s (ID: %d)", *user.Username, user.ID)
168
173
}
169
174
}
170
175
user.AccessToken = &token
···
174
179
s.userTokens[user.ID] = token
175
180
s.mu.Unlock()
176
181
177
-
log.Printf("User authenticated via Spotify: %s (ID: %d)", *user.Username, user.ID)
182
+
s.logger.Printf("User authenticated via Spotify: %s (ID: %d)", *user.Username, user.ID)
178
183
return user.ID, nil
179
184
}
180
185
···
203
208
token, err := s.refreshTokenInner(user.ID)
204
209
if err != nil {
205
210
//Probably should remove the access token and refresh in long run?
206
-
log.Printf("Error refreshing token for user %d: %v", user.ID, err)
211
+
s.logger.Printf("Error refreshing token for user %d: %v", user.ID, err)
207
212
continue
208
213
}
209
214
s.userTokens[user.ID] = token
210
215
}
211
216
}
212
217
213
-
log.Printf("Loaded %d active users with valid tokens", count)
218
+
s.logger.Printf("Loaded %d active users with valid tokens", count)
214
219
return nil
215
220
}
216
221
···
280
285
// Also clear the bad refresh token from the DB
281
286
updateErr := s.DB.UpdateUserToken(userID, "", "", time.Now().UTC()) // Clear tokens
282
287
if updateErr != nil {
283
-
log.Printf("Failed to clear bad refresh token for user %d: %v", userID, updateErr)
288
+
s.logger.Printf("Failed to clear bad refresh token for user %d: %v", userID, updateErr)
284
289
}
285
290
return "", fmt.Errorf("spotify token refresh failed (%d): %s", resp.StatusCode, string(body))
286
291
}
···
306
311
// Update DB
307
312
if err := s.DB.UpdateUserToken(userID, tokenResponse.AccessToken, newRefreshToken, newExpiry); err != nil {
308
313
// Log error but continue, as we have the token in memory
309
-
log.Printf("Error updating user token in DB for user %d after refresh: %v", userID, err)
314
+
s.logger.Printf("Error updating user token in DB for user %d after refresh: %v", userID, err)
310
315
}
311
316
312
317
// Update in-memory cache
···
314
319
s.userTokens[userID] = tokenResponse.AccessToken
315
320
s.mu.Unlock()
316
321
317
-
log.Printf("Successfully refreshed token for user %d", userID)
322
+
s.logger.Printf("Successfully refreshed token for user %d", userID)
318
323
return tokenResponse.AccessToken, nil
319
324
}
320
325
···
329
334
func (s *SpotifyService) RefreshExpiredTokens() {
330
335
users, err := s.DB.GetUsersWithExpiredTokens()
331
336
if err != nil {
332
-
log.Printf("Error fetching users with expired tokens: %v", err)
337
+
s.logger.Printf("Error fetching users with expired tokens: %v", err)
333
338
return
334
339
}
335
340
···
344
349
345
350
if err != nil {
346
351
// just print out errors here for now
347
-
log.Printf("Error from service/spotify/spotify.go when refreshing tokens: %s", err.Error())
352
+
s.logger.Printf("Error from service/spotify/spotify.go when refreshing tokens: %s", err.Error())
348
353
}
349
354
350
355
refreshed++
351
356
}
352
357
353
358
if refreshed > 0 {
354
-
log.Printf("Refreshed tokens for %d users", refreshed)
359
+
s.logger.Printf("Refreshed tokens for %d users", refreshed)
355
360
}
356
361
}
357
362
···
412
417
tracks, err := s.DB.GetRecentTracks(userID, 20)
413
418
if err != nil {
414
419
http.Error(w, "Error retrieving track history", http.StatusInternalServerError)
415
-
log.Printf("Error retrieving track history: %v", err)
420
+
s.logger.Printf("Error retrieving track history: %v", err)
416
421
return
417
422
}
418
423
···
454
459
455
460
// oops, token expired or other client error
456
461
if resp.StatusCode == 401 && attempt == 0 { // Only refresh on 401 on the first attempt
457
-
log.Printf("Spotify token potentially expired for user %d, attempting refresh...", userID)
462
+
s.logger.Printf("Spotify token potentially expired for user %d, attempting refresh...", userID)
458
463
newAccessToken, refreshErr := s.refreshTokenInner(userID)
459
464
if refreshErr != nil {
460
-
log.Printf("Token refresh failed for user %d: %v", userID, refreshErr)
465
+
s.logger.Printf("Token refresh failed for user %d: %v", userID, refreshErr)
461
466
// No point retrying if refresh failed
462
467
return nil, fmt.Errorf("spotify token expired or invalid for user %d and refresh failed: %w", userID, refreshErr)
463
468
}
464
-
log.Printf("Token refreshed for user %d, retrying request...", userID)
469
+
s.logger.Printf("Token refreshed for user %d, retrying request...", userID)
465
470
token = newAccessToken // Update token for the next attempt
466
471
req.Header.Set("Authorization", "Bearer "+token) // Update header for retry
467
472
continue // Go to next attempt in the loop
···
555
560
for range ticker.C {
556
561
err := s.LoadAllUsers()
557
562
if err != nil {
558
-
log.Printf("Error loading spotify users: %v", err)
563
+
s.logger.Printf("Error loading spotify users: %v", err)
559
564
continue
560
565
}
561
566
// copy userIDs to avoid holding the lock too long
···
569
574
for _, userID := range userIDs {
570
575
track, err := s.FetchCurrentTrack(userID)
571
576
if err != nil {
572
-
log.Printf("Error fetching track for user %d: %v", userID, err)
577
+
s.logger.Printf("Error fetching track for user %d: %v", userID, err)
573
578
continue
574
579
}
575
580
···
603
608
604
609
// just log when we stamp tracks
605
610
if isNewTrack && isLastTrackStamped && !currentTrack.HasStamped {
606
-
log.Printf("User %d stamped (previous) track: %s by %s", userID, currentTrack.Name, currentTrack.Artist)
611
+
s.logger.Printf("User %d stamped (previous) track: %s by %s", userID, currentTrack.Name, currentTrack.Artist)
607
612
currentTrack.HasStamped = true
608
613
if currentTrack.PlayID != 0 {
609
614
s.DB.UpdateTrack(currentTrack.PlayID, currentTrack)
610
615
611
-
log.Printf("Updated!")
616
+
s.logger.Printf("Updated!")
612
617
}
613
618
}
614
619
615
620
if isStamped && !currentTrack.HasStamped {
616
-
log.Printf("User %d stamped track: %s by %s", userID, track.Name, track.Artist)
621
+
s.logger.Printf("User %d stamped track: %s by %s", userID, track.Name, track.Artist)
617
622
track.HasStamped = true
618
623
// if currenttrack has a playid and the last track is the same as the current track
619
624
if !isNewTrack && currentTrack.PlayID != 0 {
···
624
629
s.userTracks[userID] = track
625
630
s.mu.Unlock()
626
631
627
-
log.Printf("Updated!")
632
+
s.logger.Printf("Updated!")
628
633
}
629
634
}
630
635
631
636
if isNewTrack {
632
637
id, err := s.DB.SaveTrack(userID, track)
633
638
if err != nil {
634
-
log.Printf("Error saving track for user %d: %v", userID, err)
639
+
s.logger.Printf("Error saving track for user %d: %v", userID, err)
635
640
continue
636
641
}
637
642
···
645
650
// The 'track' variable is *models.Track and has been saved to DB, PlayID is populated.
646
651
dbUser, errUser := s.DB.GetUserByID(userID) // Fetch user by their internal ID
647
652
if errUser != nil {
648
-
log.Printf("User %d: Error fetching user details for PDS submission: %v", userID, errUser)
653
+
s.logger.Printf("User %d: Error fetching user details for PDS submission: %v", userID, errUser)
649
654
} else if dbUser == nil {
650
-
log.Printf("User %d: User not found in DB. Skipping PDS submission.", userID)
655
+
s.logger.Printf("User %d: User not found in DB. Skipping PDS submission.", userID)
651
656
} else if dbUser.ATProtoDID == nil || *dbUser.ATProtoDID == "" {
652
-
log.Printf("User %d (%d): ATProto DID not set. Skipping PDS submission for track '%s'.", userID, dbUser.ATProtoDID, track.Name)
657
+
s.logger.Printf("User %d (%d): ATProto DID not set. Skipping PDS submission for track '%s'.", userID, dbUser.ATProtoDID, track.Name)
653
658
} else {
654
659
// User has a DID, proceed with hydration and submission
655
660
var trackToSubmitToPDS *models.Track = track // Default to the original track (already *models.Track)
···
658
663
// and it returns *models.Track
659
664
hydratedTrack, errHydrate := musicbrainz.HydrateTrack(s.mb, *track)
660
665
if errHydrate != nil {
661
-
log.Printf("User %d (%d): Error hydrating track '%s' with MusicBrainz: %v. Proceeding with original track data for PDS.", userID, dbUser.ATProtoDID, track.Name, errHydrate)
666
+
s.logger.Printf("User %d (%d): Error hydrating track '%s' with MusicBrainz: %v. Proceeding with original track data for PDS.", userID, dbUser.ATProtoDID, track.Name, errHydrate)
662
667
} else {
663
-
log.Printf("User %d (%d): Successfully hydrated track '%s' with MusicBrainz.", userID, dbUser.ATProtoDID, track.Name)
668
+
s.logger.Printf("User %d (%d): Successfully hydrated track '%s' with MusicBrainz.", userID, dbUser.ATProtoDID, track.Name)
664
669
trackToSubmitToPDS = hydratedTrack // hydratedTrack is *models.Track
665
670
}
666
671
} else {
667
-
log.Printf("User %d (%d): MusicBrainz service not configured. Proceeding with original track data for PDS.", userID, dbUser.ATProtoDID)
672
+
s.logger.Printf("User %d (%d): MusicBrainz service not configured. Proceeding with original track data for PDS.", userID, dbUser.ATProtoDID)
668
673
}
669
674
670
675
artistName := "Unknown Artist"
···
672
677
artistName = trackToSubmitToPDS.Artist[0].Name
673
678
}
674
679
675
-
log.Printf("User %d (%d): Attempting to submit track '%s' by %s to PDS (DID: %s)", userID, dbUser.ATProtoDID, trackToSubmitToPDS.Name, artistName, *dbUser.ATProtoDID)
680
+
s.logger.Printf("User %d (%d): Attempting to submit track '%s' by %s to PDS (DID: %s)", userID, dbUser.ATProtoDID, trackToSubmitToPDS.Name, artistName, *dbUser.ATProtoDID)
676
681
// Use context.Background() for now, or pass down a context if available
677
682
if errPDS := s.SubmitTrackToPDS(*dbUser.ATProtoDID, trackToSubmitToPDS, context.Background()); errPDS != nil {
678
-
log.Printf("User %d (%d): Error submitting track '%s' to PDS: %v", userID, dbUser.ATProtoDID, trackToSubmitToPDS.Name, errPDS)
683
+
s.logger.Printf("User %d (%d): Error submitting track '%s' to PDS: %v", userID, dbUser.ATProtoDID, trackToSubmitToPDS.Name, errPDS)
679
684
} else {
680
-
log.Printf("User %d (%d): Successfully submitted track '%s' to PDS.", userID, dbUser.ATProtoDID, trackToSubmitToPDS.Name)
685
+
s.logger.Printf("User %d (%d): Successfully submitted track '%s' to PDS.", userID, dbUser.ATProtoDID, trackToSubmitToPDS.Name)
681
686
}
682
687
}
683
688
// End of PDS submission block
684
689
685
-
log.Printf("User %d is listening to: %s by %s", userID, track.Name, track.Artist)
690
+
s.logger.Printf("User %d is listening to: %s by %s", userID, track.Name, track.Artist)
686
691
}
687
692
}
688
693
689
694
//unloading users to save memory and make sure we get new signups
690
695
err = s.LoadAllUsers()
691
696
if err != nil {
692
-
log.Printf("Error loading spotify users: %v", err)
697
+
s.logger.Printf("Error loading spotify users: %v", err)
693
698
}
694
699
}
695
700
}