like malachite (atproto-lastfm-importer) but in go and bluer
go spotify tealfm lastfm atproto
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

code quality: rename things

karitham abadd114 9f6aa587

+30 -31
+11 -11
main.go
··· 321 321 if res == nil { 322 322 fmt.Printf("Successfully retried: %s - %s\n", fr.rec.ArtistName(), fr.rec.TrackName) 323 323 if err := a.storage.MarkPublished(did, fr.key); err != nil { 324 - a.log.Error("Failed to mark record as published", sync.Error(err), slog.String("key", fr.key)) 324 + a.log.Error("Failed to mark record as published", sync.ErrorAttr(err), slog.String("key", fr.key)) 325 325 } 326 326 if err := a.storage.RemoveFailed(did, fr.key); err != nil { 327 - a.log.Error("Failed to remove record from failed list", sync.Error(err), slog.String("key", fr.key)) 327 + a.log.Error("Failed to remove record from failed list", sync.ErrorAttr(err), slog.String("key", fr.key)) 328 328 } 329 329 successCount++ 330 330 } else { ··· 508 508 return err 509 509 } 510 510 511 - a.log.Info("Starting import operation", sync.DID(handle)) 511 + a.log.Info("Starting import operation", sync.DIDAttr(handle)) 512 512 513 513 lastfmPath := cmd.String("lastfm") 514 514 spotifyPath := cmd.String("spotify") ··· 521 521 522 522 if clearCache { 523 523 if err := a.storage.ClearAll(); err != nil { 524 - a.log.Error("Failed to clear cache", sync.Error(err)) 524 + a.log.Error("Failed to clear cache", sync.ErrorAttr(err)) 525 525 } else { 526 526 a.log.Info("Cache cleared") 527 527 } ··· 548 548 if err != nil { 549 549 return fmt.Errorf("create auth client: %w", err) 550 550 } 551 - a.log.Info("Authenticated", sync.DID(authClient.DID()), slog.String("pds", authClient.PDS())) 551 + a.log.Info("Authenticated", sync.DIDAttr(authClient.DID()), slog.String("pds", authClient.PDS())) 552 552 553 553 limiter := sync.NewRateLimiter(a.storage, 0.9) 554 554 repoClient := sync.NewRateClient(authClient.APIClient(), authClient.DID(), limiter) ··· 663 663 } 664 664 665 665 fresh := cmd.Bool("fresh") 666 - a.log.Info("Starting sync operation", sync.DID(authClient.DID()), slog.Bool("fresh", fresh)) 666 + a.log.Info("Starting sync operation", sync.DIDAttr(authClient.DID()), slog.Bool("fresh", fresh)) 667 667 668 668 limiter := sync.NewRateLimiter(a.storage, 0.85) 669 669 repoClient := sync.NewRateClient(authClient.APIClient(), authClient.DID(), limiter) 670 670 671 671 if fresh { 672 672 if err := a.storage.Clear(authClient.DID()); err != nil { 673 - a.log.Error("Failed to clear cache", sync.Error(err)) 673 + a.log.Error("Failed to clear cache", sync.ErrorAttr(err)) 674 674 } else { 675 675 a.log.Info("Cache cleared") 676 676 } ··· 696 696 fresh := cmd.Bool("fresh") 697 697 yes := cmd.Bool("yes") 698 698 a.log.Info("Starting dedupe operation", 699 - sync.DID(authClient.DID()), 699 + sync.DIDAttr(authClient.DID()), 700 700 slog.Bool("dry_run", dryRun), 701 701 slog.Bool("fresh", fresh)) 702 702 ··· 705 705 706 706 if fresh { 707 707 if err := a.storage.Clear(authClient.DID()); err != nil { 708 - a.log.Error("Failed to clear cache", sync.Error(err)) 708 + a.log.Error("Failed to clear cache", sync.ErrorAttr(err)) 709 709 } else { 710 710 a.log.Info("Cache cleared") 711 711 } ··· 773 773 OnRetryScheduled(func(e failsafe.ExecutionScheduledEvent[any]) { 774 774 a.log.Warn("Delete failed with transient error, retrying", 775 775 slog.Duration("retryDelay", e.Delay), 776 - sync.Error(e.LastError()), 776 + sync.ErrorAttr(e.LastError()), 777 777 slog.Int("attempt", e.Attempts()), 778 778 slog.String("uri", uri)) 779 779 }). ··· 784 784 }) 785 785 786 786 if err != nil { 787 - a.log.Error("Failed to delete record", sync.Error(err), slog.String("uri", uri)) 787 + a.log.Error("Failed to delete record", sync.ErrorAttr(err), slog.String("uri", uri)) 788 788 } else { 789 789 a.log.Info("Deleted duplicate", slog.String("uri", uri), slog.String("track", rec.Value.TrackName)) 790 790 }
+8 -7
sync/logutil.go
··· 13 13 PlayedAt time.Time 14 14 } 15 15 16 - func Track(name, artist string, playedAt time.Time) slog.Attr { 17 - return slog.Group("track", 18 - slog.String("name", name), 19 - slog.String("artist", artist), 20 - slog.Time("played_at", playedAt), 16 + func trackAttr(rec PlayRecord) slog.Attr { 17 + return slog.Group( 18 + "track", 19 + slog.String("name", rec.TrackName), 20 + slog.String("artist", rec.ArtistName()), 21 + slog.Time("played_at", rec.PlayedTime.Time), 21 22 ) 22 23 } 23 24 24 - func DID(did string) slog.Attr { 25 + func DIDAttr(did string) slog.Attr { 25 26 return slog.String("did", did) 26 27 } 27 28 28 - func Error(err error) slog.Attr { 29 + func ErrorAttr(err error) slog.Attr { 29 30 if err == nil { 30 31 return slog.Attr{} 31 32 }
+8 -10
sync/publish.go
··· 111 111 if opts.DryRun { 112 112 for _, r := range batch { 113 113 tid := syntax.NewTIDFromTime(r.PlayedTime.Time, 0) 114 - slog.Info("would publish record (dry run)", 115 - Track(r.TrackName, r.ArtistName(), r.PlayedTime.Time), 116 - slog.String("rkey", string(tid))) 114 + slog.Info("would publish record (dry run)", trackAttr(r), slog.String("rkey", string(tid))) 117 115 } 118 116 totalSuccess += len(batch) 119 117 tracker.Increment(len(batch)) ··· 141 139 slog.Warn("batch failed with transient error, retrying", 142 140 slog.Int("count", len(batch)), 143 141 slog.Duration("retryDelay", e.Delay), 144 - Error(e.LastError()), 142 + ErrorAttr(e.LastError()), 145 143 slog.Int("attempt", e.Attempts())) 146 144 }). 147 145 Build() ··· 151 149 }) 152 150 if err != nil { 153 151 slog.Error("batch failed after retries", 154 - Error(err), 152 + ErrorAttr(err), 155 153 slog.Int("count", len(batch))) 156 154 157 155 if opts.Storage != nil { 158 156 if markErr := opts.Storage.MarkFailed(did, batchKeys, err.Error()); markErr != nil { 159 - slog.Error("failed to mark records as failed", Error(markErr)) 157 + slog.Error("failed to mark records as failed", ErrorAttr(markErr)) 160 158 } 161 159 } 162 160 ··· 193 191 194 192 var record PlayRecord 195 193 if err := json.Unmarshal(rec, &record); err != nil { 196 - slog.Error("malformed record in storage", slog.String("key", key), Error(err)) 194 + slog.Error("malformed record in storage", slog.String("key", key), ErrorAttr(err)) 197 195 if opts.Storage != nil { 198 196 _ = opts.Storage.MarkFailed(client.DID(), []string{key}, "malformed record") 199 197 } ··· 219 217 220 218 cancelled := false 221 219 if err != nil { 222 - slog.Error("import interrupted", Error(err)) 220 + slog.Error("import interrupted", ErrorAttr(err)) 223 221 cancelled = true 224 222 } 225 223 ··· 287 285 atprotoRecords := prepareRecords(batch, clientAgent) 288 286 err := client.ApplyWrites(ctx, RecordType, atprotoRecords) 289 287 if err != nil { 290 - slog.Error("batch publish failed", Error(err)) 288 + slog.Error("batch publish failed", ErrorAttr(err)) 291 289 return err 292 290 } 293 291 ··· 380 378 OnRetryScheduled(func(e failsafe.ExecutionScheduledEvent[fetchResult]) { 381 379 slog.Warn("fetch failed with transient error, retrying", 382 380 slog.Duration("retryDelay", e.Delay), 383 - Error(e.LastError()), 381 + ErrorAttr(e.LastError()), 384 382 slog.Int("attempt", e.Attempts())) 385 383 }). 386 384 Build()
+2 -2
sync/record.go
··· 91 91 return r.MusicServiceBaseDomain == MusicServiceLastFM 92 92 } 93 93 94 - func (r PlayRecord) BetterThan(other PlayRecord) bool { 94 + func (r PlayRecord) betterThan(other PlayRecord) bool { 95 95 if r.isLastFM() && !other.isLastFM() { 96 96 return true 97 97 } ··· 106 106 } 107 107 108 108 func (r PlayRecord) IsDuplicate(other PlayRecord, tolerance time.Duration) (bool, bool) { 109 - return r.sameAs(other, tolerance), r.BetterThan(other) 109 + return r.sameAs(other, tolerance), r.betterThan(other) 110 110 } 111 111 112 112 func (r PlayRecord) sameAs(other PlayRecord, tolerance time.Duration) bool {
+1 -1
sync/record_test.go
··· 170 170 171 171 for _, tt := range tests { 172 172 t.Run(tt.name, func(t *testing.T) { 173 - result := tt.r1.BetterThan(tt.r2) 173 + result := tt.r1.betterThan(tt.r2) 174 174 var resultService string 175 175 if result { 176 176 resultService = tt.r1.MusicServiceBaseDomain