@@ -16,7 +16,9 @@ import (
1616 "net/http"
1717 "net/url"
1818 "os"
19+ "strconv"
1920 "strings"
21+ "time"
2022)
2123
2224type App struct {
@@ -133,6 +135,7 @@ func Handler() error {
133135
134136 if oauthToken != "" {
135137 app .RepairTerminatedThumbnails (oauthToken )
138+ app .RepairDiscogsLabelThumbnails (oauthToken )
136139 app .ArchiveTerminatedPlaylists (oauthToken )
137140 }
138141
@@ -251,6 +254,41 @@ func (client *App) RepairTerminatedThumbnails(accessToken string) {
251254 }
252255}
253256
257+ func (client * App ) RepairDiscogsLabelThumbnails (accessToken string ) {
258+ rows , err := client .SQLDriver .Query (`
259+ SELECT l.label_id, p.spotify_playlist FROM dg_labels l
260+ JOIN dg_playlists p ON l.label_id = p.label_id AND p.num = 1
261+ WHERE l.thumbnail_medium IS NULL
262+ OR l.thumbnail_medium = ''
263+ OR l.thumbnail_medium NOT LIKE '%spotify%'
264+ LIMIT 20` )
265+ if err != nil {
266+ fmt .Println ("RepairDiscogsLabelThumbnails query error:" , err )
267+ return
268+ }
269+ defer rows .Close ()
270+
271+ for rows .Next () {
272+ var labelID , playlistID string
273+ if err := rows .Scan (& labelID , & playlistID ); err != nil {
274+ continue
275+ }
276+ imageURL , err := getSpotifyPlaylistImage (accessToken , playlistID )
277+ if err != nil || imageURL == "" {
278+ fmt .Printf ("[DG] No Spotify image for label %s: %v\n " , labelID , err )
279+ continue
280+ }
281+ _ , err = client .SQLDriver .Exec (
282+ "UPDATE dg_labels SET thumbnail_medium = ? WHERE label_id = ?" ,
283+ imageURL , labelID )
284+ if err != nil {
285+ fmt .Printf ("[DG] Failed to update thumbnail for label %s: %v\n " , labelID , err )
286+ continue
287+ }
288+ fmt .Printf ("[DG] Repaired thumbnail for label %s\n " , labelID )
289+ }
290+ }
291+
254292func (client * App ) ArchiveTerminatedPlaylists (accessToken string ) {
255293 rows , err := client .SQLDriver .Query (`
256294 SELECT e.channel_name, p.spotify_playlist FROM yt_channels e
@@ -286,11 +324,34 @@ func (client *App) ArchiveTerminatedPlaylists(accessToken string) {
286324 }
287325}
288326
327+ // spotifyDo executes an HTTP request with Spotify 429 rate-limit retry.
328+ func spotifyDo (req * http.Request ) (* http.Response , error ) {
329+ for i := 0 ; i < 3 ; i ++ {
330+ resp , err := http .DefaultClient .Do (req )
331+ if err != nil {
332+ return nil , err
333+ }
334+ if resp .StatusCode != 429 {
335+ return resp , nil
336+ }
337+ resp .Body .Close ()
338+ wait := 5
339+ if s := resp .Header .Get ("Retry-After" ); s != "" {
340+ if v , err := strconv .Atoi (s ); err == nil {
341+ wait = v
342+ }
343+ }
344+ fmt .Printf ("[Rate limited] waiting %ds\n " , wait )
345+ time .Sleep (time .Duration (wait ) * time .Second )
346+ }
347+ return nil , fmt .Errorf ("rate limited after 3 retries" )
348+ }
349+
289350func getSpotifyPlaylistName (accessToken , playlistID string ) (string , error ) {
290351 req , _ := http .NewRequest ("GET" ,
291352 "https://api.spotify.com/v1/playlists/" + playlistID + "?fields=name" , nil )
292353 req .Header .Set ("Authorization" , "Bearer " + accessToken )
293- resp , err := http . DefaultClient . Do (req )
354+ resp , err := spotifyDo (req )
294355 if err != nil {
295356 return "" , err
296357 }
@@ -312,7 +373,7 @@ func renameSpotifyPlaylist(accessToken, playlistID, newName string) error {
312373 strings .NewReader (string (payload )))
313374 req .Header .Set ("Authorization" , "Bearer " + accessToken )
314375 req .Header .Set ("Content-Type" , "application/json" )
315- resp , err := http . DefaultClient . Do (req )
376+ resp , err := spotifyDo (req )
316377 if err != nil {
317378 return err
318379 }
@@ -328,7 +389,7 @@ func getSpotifyPlaylistImage(accessToken, playlistID string) (string, error) {
328389 req , _ := http .NewRequest ("GET" ,
329390 "https://api.spotify.com/v1/playlists/" + playlistID + "?fields=images" , nil )
330391 req .Header .Set ("Authorization" , "Bearer " + accessToken )
331- resp , err := http . DefaultClient . Do (req )
392+ resp , err := spotifyDo (req )
332393 if err != nil {
333394 return "" , err
334395 }
0 commit comments