From cac88f316b43111b9fb6706d92af15784ef85cf0 Mon Sep 17 00:00:00 2001
From: Philipp Wolfer <ph.wolfer@gmail.com>
Date: Wed, 22 Nov 2023 17:53:09 +0100
Subject: [PATCH] Reduced redundancy in model conversions and consistent naming

---
 backends/funkwhale/funkwhale.go            | 76 +++++++++-------------
 backends/funkwhale/funkwhale_test.go       |  8 +--
 backends/jspf/jspf.go                      |  4 +-
 backends/listenbrainz/listenbrainz.go      | 70 ++++++++++----------
 backends/listenbrainz/listenbrainz_test.go | 12 ++--
 backends/listenbrainz/models.go            | 14 +++-
 backends/maloja/maloja.go                  |  4 +-
 backends/maloja/maloja_test.go             |  4 +-
 backends/spotify/spotify.go                | 14 ++--
 backends/spotify/spotify_test.go           |  8 +--
 10 files changed, 107 insertions(+), 107 deletions(-)

diff --git a/backends/funkwhale/funkwhale.go b/backends/funkwhale/funkwhale.go
index 708f2f9..604c697 100644
--- a/backends/funkwhale/funkwhale.go
+++ b/backends/funkwhale/funkwhale.go
@@ -66,7 +66,7 @@ out:
 		}
 
 		for _, fwListen := range result.Results {
-			listen := fwListen.ToListen()
+			listen := fwListen.AsListen()
 			if listen.ListenedAt.Unix() > oldestTimestamp.Unix() {
 				p.Elapsed += 1
 				listens = append(listens, listen)
@@ -118,7 +118,7 @@ out:
 		}
 
 		for _, favorite := range result.Results {
-			love := favorite.ToLove()
+			love := favorite.AsLove()
 			if love.Created.Unix() > oldestTimestamp.Unix() {
 				p.Elapsed += 1
 				loves = append(loves, love)
@@ -142,24 +142,10 @@ out:
 	results <- models.LovesResult{Loves: loves}
 }
 
-func (l Listening) ToListen() models.Listen {
-	track := l.Track
+func (l Listening) AsListen() models.Listen {
 	listen := models.Listen{
 		UserName: l.User.UserName,
-		Track: models.Track{
-			TrackName:     track.Title,
-			ReleaseName:   track.Album.Title,
-			ArtistNames:   []string{track.Artist.Name},
-			TrackNumber:   track.Position,
-			DiscNumber:    track.DiscNumber,
-			RecordingMbid: models.MBID(track.RecordingMbid),
-			ReleaseMbid:   models.MBID(track.Album.ReleaseMbid),
-			ArtistMbids:   []models.MBID{models.MBID(track.Artist.ArtistMbid)},
-			Tags:          track.Tags,
-			AdditionalInfo: map[string]any{
-				"media_player": FunkwhaleClientName,
-			},
-		},
+		Track:    l.Track.AsTrack(),
 	}
 
 	listenedAt, err := time.Parse(time.RFC3339, l.CreationDate)
@@ -167,33 +153,15 @@ func (l Listening) ToListen() models.Listen {
 		listen.ListenedAt = listenedAt
 	}
 
-	if len(track.Uploads) > 0 {
-		listen.Track.Duration = time.Duration(track.Uploads[0].Duration * int(time.Second))
-	}
-
 	return listen
 }
 
-func (f FavoriteTrack) ToLove() models.Love {
-	track := f.Track
-	recordingMbid := models.MBID(track.RecordingMbid)
+func (f FavoriteTrack) AsLove() models.Love {
+	track := f.Track.AsTrack()
 	love := models.Love{
 		UserName:      f.User.UserName,
-		RecordingMbid: recordingMbid,
-		Track: models.Track{
-			TrackName:     track.Title,
-			ReleaseName:   track.Album.Title,
-			ArtistNames:   []string{track.Artist.Name},
-			TrackNumber:   track.Position,
-			DiscNumber:    track.DiscNumber,
-			RecordingMbid: recordingMbid,
-			ReleaseMbid:   models.MBID(track.Album.ReleaseMbid),
-			ArtistMbids:   []models.MBID{models.MBID(track.Artist.ArtistMbid)},
-			Tags:          track.Tags,
-			AdditionalInfo: map[string]any{
-				"media_player": FunkwhaleClientName,
-			},
-		},
+		RecordingMbid: track.RecordingMbid,
+		Track:         track,
 	}
 
 	created, err := time.Parse(time.RFC3339, f.CreationDate)
@@ -201,9 +169,29 @@ func (f FavoriteTrack) ToLove() models.Love {
 		love.Created = created
 	}
 
-	if len(track.Uploads) > 0 {
-		love.Track.Duration = time.Duration(track.Uploads[0].Duration * int(time.Second))
-	}
-
 	return love
 }
+
+func (t Track) AsTrack() models.Track {
+	recordingMbid := models.MBID(t.RecordingMbid)
+	track := models.Track{
+		TrackName:     t.Title,
+		ReleaseName:   t.Album.Title,
+		ArtistNames:   []string{t.Artist.Name},
+		TrackNumber:   t.Position,
+		DiscNumber:    t.DiscNumber,
+		RecordingMbid: recordingMbid,
+		ReleaseMbid:   models.MBID(t.Album.ReleaseMbid),
+		ArtistMbids:   []models.MBID{models.MBID(t.Artist.ArtistMbid)},
+		Tags:          t.Tags,
+		AdditionalInfo: map[string]any{
+			"media_player": FunkwhaleClientName,
+		},
+	}
+
+	if len(t.Uploads) > 0 {
+		track.Duration = time.Duration(t.Uploads[0].Duration * int(time.Second))
+	}
+
+	return track
+}
diff --git a/backends/funkwhale/funkwhale_test.go b/backends/funkwhale/funkwhale_test.go
index a548c3c..3047d43 100644
--- a/backends/funkwhale/funkwhale_test.go
+++ b/backends/funkwhale/funkwhale_test.go
@@ -34,7 +34,7 @@ func TestFromConfig(t *testing.T) {
 	assert.IsType(t, &funkwhale.FunkwhaleApiBackend{}, backend)
 }
 
-func TestFunkwhaleListeningToListen(t *testing.T) {
+func TestFunkwhaleListeningAsListen(t *testing.T) {
 	fwListen := funkwhale.Listening{
 		CreationDate: "2023-11-09T23:59:29.022005Z",
 		User: funkwhale.User{
@@ -61,7 +61,7 @@ func TestFunkwhaleListeningToListen(t *testing.T) {
 			},
 		},
 	}
-	listen := fwListen.ToListen()
+	listen := fwListen.AsListen()
 	assert := assert.New(t)
 	assert.Equal(time.Unix(1699574369, 0).Unix(), listen.ListenedAt.Unix())
 	assert.Equal(fwListen.User.UserName, listen.UserName)
@@ -79,7 +79,7 @@ func TestFunkwhaleListeningToListen(t *testing.T) {
 	assert.Equal(funkwhale.FunkwhaleClientName, listen.AdditionalInfo["media_player"])
 }
 
-func TestFunkwhaleFavoriteTrackToLove(t *testing.T) {
+func TestFunkwhaleFavoriteTrackAsLove(t *testing.T) {
 	favorite := funkwhale.FavoriteTrack{
 		CreationDate: "2023-11-09T23:59:29.022005Z",
 		User: funkwhale.User{
@@ -106,7 +106,7 @@ func TestFunkwhaleFavoriteTrackToLove(t *testing.T) {
 			},
 		},
 	}
-	love := favorite.ToLove()
+	love := favorite.AsLove()
 	assert := assert.New(t)
 	assert.Equal(time.Unix(1699574369, 0).Unix(), love.Created.Unix())
 	assert.Equal(favorite.User.UserName, love.UserName)
diff --git a/backends/jspf/jspf.go b/backends/jspf/jspf.go
index 7296d4b..99cb59a 100644
--- a/backends/jspf/jspf.go
+++ b/backends/jspf/jspf.go
@@ -53,7 +53,7 @@ func (b *JspfBackend) FinishImport() error {
 
 func (b *JspfBackend) ImportLoves(export models.LovesResult, importResult models.ImportResult, progress chan models.Progress) (models.ImportResult, error) {
 	for _, love := range export.Loves {
-		track := loveToTrack(love)
+		track := loveAsTrack(love)
 		b.tracks = append(b.tracks, track)
 		importResult.ImportCount += 1
 		importResult.UpdateTimestamp(love.Created)
@@ -63,7 +63,7 @@ func (b *JspfBackend) ImportLoves(export models.LovesResult, importResult models
 	return importResult, nil
 }
 
-func loveToTrack(love models.Love) Track {
+func loveAsTrack(love models.Love) Track {
 	extension := MusicBrainzTrackExtension{
 		AddedAt:            love.Created,
 		AddedBy:            love.UserName,
diff --git a/backends/listenbrainz/listenbrainz.go b/backends/listenbrainz/listenbrainz.go
index e773929..e4b35b9 100644
--- a/backends/listenbrainz/listenbrainz.go
+++ b/backends/listenbrainz/listenbrainz.go
@@ -77,7 +77,7 @@ out:
 
 		for _, listen := range result.Payload.Listens {
 			if listen.ListenedAt > oldestTimestamp.Unix() {
-				listens = append(listens, listen.ToListen())
+				listens = append(listens, listen.AsListen())
 			} else {
 				// result contains listens older then oldestTimestamp,
 				// we can stop requesting more
@@ -117,7 +117,7 @@ out:
 		}
 
 		for _, feedback := range result.Feedback {
-			love := feedback.ToLove()
+			love := feedback.AsLove()
 			if love.Created.Unix() > oldestTimestamp.Unix() {
 				loves = append(loves, love)
 				p.Elapsed += 1
@@ -196,53 +196,55 @@ func (b *ListenBrainzApiBackend) ImportLoves(export models.LovesResult, importRe
 	return importResult, nil
 }
 
-func (lbListen Listen) ToListen() models.Listen {
-	track := lbListen.TrackMetadata
+func (lbListen Listen) AsListen() models.Listen {
 	listen := models.Listen{
 		ListenedAt: time.Unix(lbListen.ListenedAt, 0),
 		UserName:   lbListen.UserName,
-		Track: models.Track{
-			TrackName:        track.TrackName,
-			ReleaseName:      track.ReleaseName,
-			ArtistNames:      []string{track.ArtistName},
-			Duration:         track.Duration(),
-			TrackNumber:      track.TrackNumber(),
-			DiscNumber:       track.DiscNumber(),
-			RecordingMbid:    models.MBID(track.RecordingMbid()),
-			ReleaseMbid:      models.MBID(track.ReleaseMbid()),
-			ReleaseGroupMbid: models.MBID(track.ReleaseGroupMbid()),
-			Isrc:             track.Isrc(),
-			AdditionalInfo:   track.AdditionalInfo,
-		},
+		Track:      lbListen.TrackMetadata.AsTrack(),
 	}
 	return listen
 }
 
-func (f Feedback) ToLove() models.Love {
-	track := f.TrackMetadata
+func (f Feedback) AsLove() models.Love {
 	recordingMbid := models.MBID(f.RecordingMbid)
+	track := f.TrackMetadata
+	if track == nil {
+		track = &Track{}
+	}
 	love := models.Love{
 		UserName:      f.UserName,
 		RecordingMbid: recordingMbid,
 		Created:       time.Unix(f.Created, 0),
-		Track: models.Track{
-			RecordingMbid: recordingMbid,
-		},
+		Track:         track.AsTrack(),
 	}
 
-	if track != nil {
-		love.Track.TrackName = track.TrackName
-		love.Track.ReleaseName = track.ReleaseName
-		love.ArtistNames = []string{track.ArtistName}
-		love.ReleaseMbid = models.MBID(track.MbidMapping.ReleaseMbid)
-		love.ArtistMbids = make([]models.MBID, 0, len(track.MbidMapping.ArtistMbids))
-
-		if track.MbidMapping != nil {
-			for _, artistMbid := range track.MbidMapping.ArtistMbids {
-				love.Track.ArtistMbids = append(love.Track.ArtistMbids, models.MBID(artistMbid))
-			}
-		}
+	if love.Track.RecordingMbid == "" {
+		love.Track.RecordingMbid = love.RecordingMbid
 	}
 
 	return love
 }
+
+func (t Track) AsTrack() models.Track {
+	track := models.Track{
+		TrackName:        t.TrackName,
+		ReleaseName:      t.ReleaseName,
+		ArtistNames:      []string{t.ArtistName},
+		Duration:         t.Duration(),
+		TrackNumber:      t.TrackNumber(),
+		DiscNumber:       t.DiscNumber(),
+		RecordingMbid:    models.MBID(t.RecordingMbid()),
+		ReleaseMbid:      models.MBID(t.ReleaseMbid()),
+		ReleaseGroupMbid: models.MBID(t.ReleaseGroupMbid()),
+		Isrc:             t.Isrc(),
+		AdditionalInfo:   t.AdditionalInfo,
+	}
+
+	if t.MbidMapping != nil && len(track.ArtistMbids) == 0 {
+		for _, artistMbid := range t.MbidMapping.ArtistMbids {
+			track.ArtistMbids = append(track.ArtistMbids, models.MBID(artistMbid))
+		}
+	}
+
+	return track
+}
diff --git a/backends/listenbrainz/listenbrainz_test.go b/backends/listenbrainz/listenbrainz_test.go
index 507c89f..ae8373c 100644
--- a/backends/listenbrainz/listenbrainz_test.go
+++ b/backends/listenbrainz/listenbrainz_test.go
@@ -34,7 +34,7 @@ func TestFromConfig(t *testing.T) {
 	assert.IsType(t, &listenbrainz.ListenBrainzApiBackend{}, backend)
 }
 
-func TestListenBrainzListenToListen(t *testing.T) {
+func TestListenBrainzListenAsListen(t *testing.T) {
 	lbListen := listenbrainz.Listen{
 		ListenedAt: 1699289873,
 		UserName:   "outsidecontext",
@@ -54,7 +54,7 @@ func TestListenBrainzListenToListen(t *testing.T) {
 			},
 		},
 	}
-	listen := lbListen.ToListen()
+	listen := lbListen.AsListen()
 	assert.Equal(t, time.Unix(1699289873, 0), listen.ListenedAt)
 	assert.Equal(t, lbListen.UserName, listen.UserName)
 	assert.Equal(t, time.Duration(413787*time.Millisecond), listen.Duration)
@@ -70,7 +70,7 @@ func TestListenBrainzListenToListen(t *testing.T) {
 	assert.Equal(t, lbListen.TrackMetadata.AdditionalInfo["foo"], listen.AdditionalInfo["foo"])
 }
 
-func TestListenBrainzFeedbackToLove(t *testing.T) {
+func TestListenBrainzFeedbackAsLove(t *testing.T) {
 	recordingMbid := "c0a1fc94-5f04-4a5f-bc09-e5de0c49cd12"
 	releaseMbid := "d7f22677-9803-4d21-ba42-081b633a6f68"
 	artistMbid := "d7f22677-9803-4d21-ba42-081b633a6f68"
@@ -90,7 +90,7 @@ func TestListenBrainzFeedbackToLove(t *testing.T) {
 			},
 		},
 	}
-	love := feedback.ToLove()
+	love := feedback.AsLove()
 	assert := assert.New(t)
 	assert.Equal(time.Unix(1699859066, 0).Unix(), love.Created.Unix())
 	assert.Equal(feedback.UserName, love.UserName)
@@ -104,14 +104,14 @@ func TestListenBrainzFeedbackToLove(t *testing.T) {
 	assert.Equal(models.MBID(artistMbid), love.Track.ArtistMbids[0])
 }
 
-func TestListenBrainzPartialFeedbackToLove(t *testing.T) {
+func TestListenBrainzPartialFeedbackAsLove(t *testing.T) {
 	recordingMbid := "c0a1fc94-5f04-4a5f-bc09-e5de0c49cd12"
 	feedback := listenbrainz.Feedback{
 		Created:       1699859066,
 		RecordingMbid: recordingMbid,
 		Score:         1,
 	}
-	love := feedback.ToLove()
+	love := feedback.AsLove()
 	assert := assert.New(t)
 	assert.Equal(time.Unix(1699859066, 0).Unix(), love.Created.Unix())
 	assert.Equal(models.MBID(recordingMbid), love.RecordingMbid)
diff --git a/backends/listenbrainz/models.go b/backends/listenbrainz/models.go
index 2af9d72..ee00ad9 100644
--- a/backends/listenbrainz/models.go
+++ b/backends/listenbrainz/models.go
@@ -149,11 +149,21 @@ func (t Track) Isrc() string {
 }
 
 func (t Track) RecordingMbid() string {
-	return tryGetValueOrEmpty[string](t.AdditionalInfo, "recording_mbid")
+	mbid := tryGetValueOrEmpty[string](t.AdditionalInfo, "recording_mbid")
+	if mbid == "" && t.MbidMapping != nil {
+		return t.MbidMapping.RecordingMbid
+	} else {
+		return mbid
+	}
 }
 
 func (t Track) ReleaseMbid() string {
-	return tryGetValueOrEmpty[string](t.AdditionalInfo, "release_mbid")
+	mbid := tryGetValueOrEmpty[string](t.AdditionalInfo, "release_mbid")
+	if mbid == "" && t.MbidMapping != nil {
+		return t.MbidMapping.ReleaseMbid
+	} else {
+		return mbid
+	}
 }
 
 func (t Track) ReleaseGroupMbid() string {
diff --git a/backends/maloja/maloja.go b/backends/maloja/maloja.go
index 8f1a850..9c2c4fe 100644
--- a/backends/maloja/maloja.go
+++ b/backends/maloja/maloja.go
@@ -73,7 +73,7 @@ out:
 		for _, scrobble := range result.List {
 			if scrobble.ListenedAt > oldestTimestamp.Unix() {
 				p.Elapsed += 1
-				listens = append(listens, scrobble.ToListen())
+				listens = append(listens, scrobble.AsListen())
 			} else {
 				break out
 			}
@@ -116,7 +116,7 @@ func (b *MalojaApiBackend) ImportListens(export models.ListensResult, importResu
 	return importResult, nil
 }
 
-func (s Scrobble) ToListen() models.Listen {
+func (s Scrobble) AsListen() models.Listen {
 	track := s.Track
 	listen := models.Listen{
 		ListenedAt:       time.Unix(s.ListenedAt, 0),
diff --git a/backends/maloja/maloja_test.go b/backends/maloja/maloja_test.go
index 809b19a..79dccb0 100644
--- a/backends/maloja/maloja_test.go
+++ b/backends/maloja/maloja_test.go
@@ -32,7 +32,7 @@ func TestFromConfig(t *testing.T) {
 	assert.IsType(t, &maloja.MalojaApiBackend{}, backend)
 }
 
-func TestScrobbleToListen(t *testing.T) {
+func TestScrobbleAsListen(t *testing.T) {
 	scrobble := maloja.Scrobble{
 		ListenedAt: 1699289873,
 		Track: maloja.Track{
@@ -45,7 +45,7 @@ func TestScrobbleToListen(t *testing.T) {
 		},
 		Origin: "client:Funkwhale",
 	}
-	listen := scrobble.ToListen()
+	listen := scrobble.AsListen()
 	assert := assert.New(t)
 	assert.Equal(time.Unix(1699289873, 0), listen.ListenedAt)
 	assert.Equal(time.Duration(414*time.Second), listen.Duration)
diff --git a/backends/spotify/spotify.go b/backends/spotify/spotify.go
index 0362d30..7ce325f 100644
--- a/backends/spotify/spotify.go
+++ b/backends/spotify/spotify.go
@@ -108,7 +108,7 @@ func (b *SpotifyApiBackend) ExportListens(oldestTimestamp time.Time, results cha
 		listens := make(models.ListensList, 0, len(result.Items))
 
 		for _, listen := range result.Items {
-			l := listen.ToListen()
+			l := listen.AsListen()
 			if l.ListenedAt.Unix() > oldestTimestamp.Unix() {
 				listens = append(listens, l)
 			} else {
@@ -168,7 +168,7 @@ out:
 
 		loves := make(models.LovesList, 0, perPage)
 		for _, track := range result.Items {
-			love := track.ToLove()
+			love := track.AsLove()
 			if love.Created.Unix() > oldestTimestamp.Unix() {
 				loves = append(loves, love)
 			} else {
@@ -196,27 +196,27 @@ out:
 	progress <- p.Complete()
 }
 
-func (l Listen) ToListen() models.Listen {
+func (l Listen) AsListen() models.Listen {
 	listenedAt, _ := time.Parse(time.RFC3339, l.PlayedAt)
 	listen := models.Listen{
 		ListenedAt: listenedAt,
-		Track:      l.Track.ToTrack(),
+		Track:      l.Track.AsTrack(),
 	}
 
 	return listen
 }
 
-func (t SavedTrack) ToLove() models.Love {
+func (t SavedTrack) AsLove() models.Love {
 	addedAt, _ := time.Parse(time.RFC3339, t.AddedAt)
 	love := models.Love{
 		Created: addedAt,
-		Track:   t.Track.ToTrack(),
+		Track:   t.Track.AsTrack(),
 	}
 
 	return love
 }
 
-func (t Track) ToTrack() models.Track {
+func (t Track) AsTrack() models.Track {
 	track := models.Track{
 		TrackName:      t.Name,
 		ReleaseName:    t.Album.Name,
diff --git a/backends/spotify/spotify_test.go b/backends/spotify/spotify_test.go
index 8872e83..6ca4eab 100644
--- a/backends/spotify/spotify_test.go
+++ b/backends/spotify/spotify_test.go
@@ -28,13 +28,13 @@ import (
 	"go.uploadedlobster.com/scotty/backends/spotify"
 )
 
-func TestSpotifyListenToListen(t *testing.T) {
+func TestSpotifyListenAsListen(t *testing.T) {
 	data, err := os.ReadFile("testdata/listen.json")
 	require.NoError(t, err)
 	spListen := spotify.Listen{}
 	err = json.Unmarshal(data, &spListen)
 	require.NoError(t, err)
-	listen := spListen.ToListen()
+	listen := spListen.AsListen()
 	listenedAt, _ := time.Parse(time.RFC3339, "2023-11-21T15:24:33.361Z")
 	assert.Equal(t, listenedAt, listen.ListenedAt)
 	assert.Equal(t, time.Duration(413826*time.Millisecond), listen.Duration)
@@ -53,13 +53,13 @@ func TestSpotifyListenToListen(t *testing.T) {
 	assert.Equal(t, []string{"https://open.spotify.com/artist/101HSR6JTJqe3DBh6rb8kz"}, info["spotify_album_artist_ids"])
 }
 
-func TestSavedTrackToLove(t *testing.T) {
+func TestSavedTrackAsLove(t *testing.T) {
 	data, err := os.ReadFile("testdata/track.json")
 	require.NoError(t, err)
 	track := spotify.SavedTrack{}
 	err = json.Unmarshal(data, &track)
 	require.NoError(t, err)
-	love := track.ToLove()
+	love := track.AsLove()
 	created, _ := time.Parse(time.RFC3339, "2022-02-13T21:46:08Z")
 	assert.Equal(t, created, love.Created)
 	assert.Equal(t, time.Duration(187680*time.Millisecond), love.Duration)