1
0
Fork 0
mirror of https://git.sr.ht/~phw/scotty synced 2025-04-18 11:09:29 +02:00

Updated all import/export interfaces

This commit is contained in:
Philipp Wolfer 2023-11-15 19:24:12 +01:00
parent 729a3d0ed0
commit ab04eb1123
No known key found for this signature in database
GPG key ID: 8FDF744D4919943B
12 changed files with 247 additions and 167 deletions

View file

@ -37,8 +37,6 @@ func (b DumpBackend) FromConfig(config *viper.Viper) models.Backend {
func (b DumpBackend) ImportListens(results chan models.ListensResult, oldestTimestamp time.Time) (models.ImportResult, error) { func (b DumpBackend) ImportListens(results chan models.ListensResult, oldestTimestamp time.Time) (models.ImportResult, error) {
importResult := models.ImportResult{ importResult := models.ImportResult{
TotalCount: 0,
ImportCount: 0,
LastTimestamp: oldestTimestamp, LastTimestamp: oldestTimestamp,
} }
for result := range results { for result := range results {
@ -59,8 +57,6 @@ func (b DumpBackend) ImportListens(results chan models.ListensResult, oldestTime
func (b DumpBackend) ImportLoves(results chan models.LovesResult, oldestTimestamp time.Time) (models.ImportResult, error) { func (b DumpBackend) ImportLoves(results chan models.LovesResult, oldestTimestamp time.Time) (models.ImportResult, error) {
importResult := models.ImportResult{ importResult := models.ImportResult{
TotalCount: 0,
ImportCount: 0,
LastTimestamp: oldestTimestamp, LastTimestamp: oldestTimestamp,
} }
for result := range results { for result := range results {

View file

@ -22,7 +22,7 @@ THE SOFTWARE.
package funkwhale package funkwhale
import ( import (
"slices" "sort"
"time" "time"
"github.com/spf13/viper" "github.com/spf13/viper"
@ -45,17 +45,19 @@ func (b FunkwhaleApiBackend) FromConfig(config *viper.Viper) models.Backend {
return b return b
} }
func (b FunkwhaleApiBackend) ExportListens(oldestTimestamp time.Time) ([]models.Listen, error) { func (b FunkwhaleApiBackend) ExportListens(oldestTimestamp time.Time, results chan models.ListensResult) {
page := 1 page := 1
perPage := MaxItemsPerGet perPage := MaxItemsPerGet
listens := make([]models.Listen, 0, 2*MaxItemsPerGet) // We need to gather the full list of listens in order to sort them
listens := make(models.ListensList, 0, 2*MaxItemsPerGet)
out: out:
for { for {
result, err := b.client.GetHistoryListenings(b.username, page, perPage) result, err := b.client.GetHistoryListenings(b.username, page, perPage)
if err != nil { if err != nil {
return nil, err results <- models.ListensResult{Error: err}
close(results)
} }
count := len(result.Results) count := len(result.Results)
@ -80,21 +82,26 @@ out:
page += 1 page += 1
} }
slices.Reverse(listens) sort.Sort(listens)
return listens, nil results <- models.ListensResult{Listens: listens}
close(results)
} }
func (b FunkwhaleApiBackend) ExportLoves(oldestTimestamp time.Time) ([]models.Love, error) { func (b FunkwhaleApiBackend) ExportLoves(oldestTimestamp time.Time, results chan models.LovesResult) {
page := 1 page := 1
perPage := MaxItemsPerGet perPage := MaxItemsPerGet
loves := make([]models.Love, 0, 2*MaxItemsPerGet) defer close(results)
// We need to gather the full list of listens in order to sort them
loves := make(models.LovesList, 0, 2*MaxItemsPerGet)
out: out:
for { for {
result, err := b.client.GetFavoriteTracks(page, perPage) result, err := b.client.GetFavoriteTracks(page, perPage)
if err != nil { if err != nil {
return nil, err results <- models.LovesResult{Error: err}
return
} }
count := len(result.Results) count := len(result.Results)
@ -119,8 +126,8 @@ out:
page += 1 page += 1
} }
slices.Reverse(loves) sort.Sort(loves)
return loves, nil results <- models.LovesResult{Loves: loves}
} }
func (l Listening) ToListen() models.Listen { func (l Listening) ToListen() models.Listen {

View file

@ -46,52 +46,65 @@ func (b JspfBackend) FromConfig(config *viper.Viper) models.Backend {
return b return b
} }
func (b JspfBackend) ImportLoves(loves []models.Love, oldestTimestamp time.Time) (models.ImportResult, error) { func (b JspfBackend) ImportLoves(results chan models.LovesResult, oldestTimestamp time.Time) (models.ImportResult, error) {
result := models.ImportResult{ importResult := models.ImportResult{
TotalCount: len(loves),
ImportCount: 0,
LastTimestamp: oldestTimestamp, LastTimestamp: oldestTimestamp,
} }
tracks := make([]Track, 0, result.TotalCount) tracks := make([]Track, 0, importResult.TotalCount)
for _, love := range loves { for result := range results {
extension := MusicBrainzTrackExtension{ if result.Error != nil {
AddedAt: love.Created, return importResult, result.Error
AddedBy: love.UserName,
AdditionalMetadata: love.AdditionalInfo,
ArtistIdentifiers: make([]string, len(love.ArtistMbids)),
} }
for i, mbid := range love.ArtistMbids { importResult.TotalCount += len(result.Loves)
extension.ArtistIdentifiers[i] = "https://musicbrainz.org/artist/" + string(mbid) for _, love := range result.Loves {
track := loveToTrack(love)
tracks = append(tracks, track)
oldestTimestamp = love.Created
importResult.ImportCount += 1
} }
if love.ReleaseMbid != "" {
extension.ReleaseIdentifier = "https://musicbrainz.org/release/" + string(love.ReleaseMbid)
}
track := Track{
Title: love.TrackName,
Album: love.ReleaseName,
Creator: love.ArtistName(),
TrackNum: love.TrackNumber,
Extension: map[string]any{
"https://musicbrainz.org/doc/jspf#track": extension,
},
}
if love.RecordingMbid != "" {
track.Identifier = append(track.Identifier, "https://musicbrainz.org/recording/"+string(love.RecordingMbid))
}
tracks = append(tracks, track)
result.UpdateTimestamp(love.Created)
result.ImportCount += 1
} }
err := b.writeJspf(tracks) err := b.writeJspf(tracks)
return result, err if err != nil {
importResult.UpdateTimestamp(oldestTimestamp)
importResult.ImportCount = len(tracks)
}
return importResult, err
}
func loveToTrack(love models.Love) Track {
extension := MusicBrainzTrackExtension{
AddedAt: love.Created,
AddedBy: love.UserName,
AdditionalMetadata: love.AdditionalInfo,
ArtistIdentifiers: make([]string, len(love.ArtistMbids)),
}
for i, mbid := range love.ArtistMbids {
extension.ArtistIdentifiers[i] = "https://musicbrainz.org/artist/" + string(mbid)
}
if love.ReleaseMbid != "" {
extension.ReleaseIdentifier = "https://musicbrainz.org/release/" + string(love.ReleaseMbid)
}
track := Track{
Title: love.TrackName,
Album: love.ReleaseName,
Creator: love.ArtistName(),
TrackNum: love.TrackNumber,
Extension: map[string]any{
"https://musicbrainz.org/doc/jspf#track": extension,
},
}
if love.RecordingMbid != "" {
track.Identifier = append(track.Identifier, "https://musicbrainz.org/recording/"+string(love.RecordingMbid))
}
return track
} }
func (b JspfBackend) writeJspf(tracks []Track) error { func (b JspfBackend) writeJspf(tracks []Track) error {

View file

@ -23,7 +23,7 @@ package listenbrainz
import ( import (
"fmt" "fmt"
"slices" "sort"
"time" "time"
"github.com/spf13/viper" "github.com/spf13/viper"
@ -42,16 +42,21 @@ func (b ListenBrainzApiBackend) FromConfig(config *viper.Viper) models.Backend {
return b return b
} }
func (b ListenBrainzApiBackend) ExportListens(oldestTimestamp time.Time) ([]models.Listen, error) { func (b ListenBrainzApiBackend) ExportListens(oldestTimestamp time.Time, results chan models.ListensResult) {
maxTime := time.Now() maxTime := time.Now()
minTime := time.Unix(0, 0) minTime := time.Unix(0, 0)
listens := make([]models.Listen, 0, 2*MaxItemsPerGet)
defer close(results)
// FIXME: Optimize by fetching the listens in reverse listen time order
listens := make(models.ListensList, 0, 2*MaxItemsPerGet)
out: out:
for { for {
result, err := b.client.GetListens(b.username, maxTime, minTime) result, err := b.client.GetListens(b.username, maxTime, minTime)
if err != nil { if err != nil {
return nil, err results <- models.ListensResult{Error: err}
return
} }
count := len(result.Payload.Listens) count := len(result.Payload.Listens)
@ -73,19 +78,21 @@ out:
} }
} }
slices.Reverse(listens) sort.Sort(listens)
return listens, nil results <- models.ListensResult{Listens: listens}
} }
func (b ListenBrainzApiBackend) ExportLoves(oldestTimestamp time.Time) ([]models.Love, error) { func (b ListenBrainzApiBackend) ExportLoves(oldestTimestamp time.Time, results chan models.LovesResult) {
offset := 0 offset := 0
loves := make([]models.Love, 0, 2*MaxItemsPerGet) defer close(results)
loves := make(models.LovesList, 0, 2*MaxItemsPerGet)
out: out:
for { for {
result, err := b.client.GetFeedback(b.username, 1, offset) result, err := b.client.GetFeedback(b.username, 1, offset)
if err != nil { if err != nil {
return nil, err results <- models.LovesResult{Error: err}
return
} }
count := len(result.Feedback) count := len(result.Feedback)
@ -105,69 +112,79 @@ out:
offset += MaxItemsPerGet offset += MaxItemsPerGet
} }
slices.Reverse(loves) sort.Sort(loves)
return loves, nil results <- models.LovesResult{Loves: loves}
} }
func (b ListenBrainzApiBackend) ImportLoves(loves []models.Love, oldestTimestamp time.Time) (models.ImportResult, error) { func (b ListenBrainzApiBackend) ImportLoves(results chan models.LovesResult, oldestTimestamp time.Time) (models.ImportResult, error) {
result := models.ImportResult{ importResult := models.ImportResult{
TotalCount: len(loves),
ImportCount: 0,
LastTimestamp: oldestTimestamp, LastTimestamp: oldestTimestamp,
ImportErrors: make([]string, 0), ImportErrors: make([]string, 0),
} }
existingLoves, err := b.ExportLoves(time.Unix(0, 0)) existingLovesChan := make(chan models.LovesResult)
if err != nil { go b.ExportLoves(time.Unix(0, 0), existingLovesChan)
return result, err existingLoves := <-existingLovesChan
if existingLoves.Error != nil {
results <- models.LovesResult{Error: existingLoves.Error}
close(results)
} }
existingMbids := make(map[string]bool, len(existingLoves)) existingMbids := make(map[string]bool, len(existingLoves.Loves))
for _, love := range existingLoves { for _, love := range existingLoves.Loves {
existingMbids[string(love.RecordingMbid)] = true existingMbids[string(love.RecordingMbid)] = true
} }
for _, love := range loves { for result := range results {
if love.Created.Unix() <= oldestTimestamp.Unix() { if result.Error != nil {
continue return importResult, result.Error
} }
recordingMbid := string(love.RecordingMbid) importResult.TotalCount += len(result.Loves)
if recordingMbid == "" { for _, love := range result.Loves {
lookup, err := b.client.Lookup(love.TrackName, love.ArtistName()) if love.Created.Unix() <= oldestTimestamp.Unix() {
if err == nil { continue
recordingMbid = lookup.RecordingMbid
} }
}
if recordingMbid != "" { recordingMbid := string(love.RecordingMbid)
ok := false
errMsg := "" if recordingMbid == "" {
if existingMbids[recordingMbid] { lookup, err := b.client.Lookup(love.TrackName, love.ArtistName())
ok = true if err == nil {
} else { recordingMbid = lookup.RecordingMbid
resp, err := b.client.SendFeedback(Feedback{
RecordingMbid: recordingMbid,
Score: 1,
})
ok = err == nil && resp.Status == "ok"
if err != nil {
errMsg = err.Error()
} }
} }
if ok { if recordingMbid != "" {
result.UpdateTimestamp(love.Created) ok := false
result.ImportCount += 1 errMsg := ""
} else { if existingMbids[recordingMbid] {
msg := fmt.Sprintf("Failed import of \"%s\" by %s: %v", ok = true
love.TrackName, love.ArtistName(), errMsg) } else {
result.ImportErrors = append(result.ImportErrors, msg) resp, err := b.client.SendFeedback(Feedback{
RecordingMbid: recordingMbid,
Score: 1,
})
ok = err == nil && resp.Status == "ok"
if err != nil {
errMsg = err.Error()
}
}
if ok {
importResult.UpdateTimestamp(love.Created)
importResult.ImportCount += 1
} else {
msg := fmt.Sprintf("Failed import of \"%s\" by %s: %v",
love.TrackName, love.ArtistName(), errMsg)
importResult.ImportErrors = append(importResult.ImportErrors, msg)
}
} }
} }
} }
return result, nil
return importResult, nil
} }
func (lbListen Listen) ToListen() models.Listen { func (lbListen Listen) ToListen() models.Listen {

View file

@ -28,6 +28,8 @@ import (
"github.com/go-resty/resty/v2" "github.com/go-resty/resty/v2"
) )
const MaxItemsPerGet = 1000
type Client struct { type Client struct {
HttpClient *resty.Client HttpClient *resty.Client
token string token string

View file

@ -23,7 +23,7 @@ package maloja
import ( import (
"errors" "errors"
"slices" "sort"
"strings" "strings"
"time" "time"
@ -45,17 +45,21 @@ func (b MalojaApiBackend) FromConfig(config *viper.Viper) models.Backend {
return b return b
} }
func (b MalojaApiBackend) ExportListens(oldestTimestamp time.Time) ([]models.Listen, error) { func (b MalojaApiBackend) ExportListens(oldestTimestamp time.Time, results chan models.ListensResult) {
page := 0 page := 0
perPage := 1000 perPage := MaxItemsPerGet
listens := make([]models.Listen, 0) defer close(results)
// We need to gather the full list of listens in order to sort them
listens := make(models.ListensList, 0, 2*perPage)
out: out:
for { for {
result, err := b.client.GetScrobbles(page, perPage) result, err := b.client.GetScrobbles(page, perPage)
if err != nil { if err != nil {
return nil, err results <- models.ListensResult{Error: err}
return
} }
count := len(result.List) count := len(result.List)
@ -74,44 +78,49 @@ out:
page += 1 page += 1
} }
slices.Reverse(listens) sort.Sort(listens)
return listens, nil results <- models.ListensResult{Listens: listens}
} }
func (b MalojaApiBackend) ImportListens(listens []models.Listen, oldestTimestamp time.Time) (models.ImportResult, error) { func (b MalojaApiBackend) ImportListens(results chan models.ListensResult, oldestTimestamp time.Time) (models.ImportResult, error) {
result := models.ImportResult{ importResult := models.ImportResult{
TotalCount: len(listens),
ImportCount: 0,
LastTimestamp: oldestTimestamp, LastTimestamp: oldestTimestamp,
} }
for _, listen := range listens {
if listen.ListenedAt.Unix() <= oldestTimestamp.Unix() { for result := range results {
continue if result.Error != nil {
return importResult, result.Error
} }
scrobble := NewScrobble{ importResult.TotalCount += len(result.Listens)
Title: listen.TrackName, for _, listen := range result.Listens {
Artists: listen.ArtistNames, if listen.ListenedAt.Unix() <= oldestTimestamp.Unix() {
Album: listen.ReleaseName, break
Duration: int64(listen.PlaybackDuration.Seconds()), }
Length: int64(listen.Duration.Seconds()),
Time: listen.ListenedAt.Unix(),
Nofix: b.nofix,
}
resp, err := b.client.NewScrobble(scrobble) scrobble := NewScrobble{
if err != nil { Title: listen.TrackName,
return result, err Artists: listen.ArtistNames,
} else if resp.Status != "success" { Album: listen.ReleaseName,
return result, errors.New(resp.Error.Description) Duration: int64(listen.PlaybackDuration.Seconds()),
} Length: int64(listen.Duration.Seconds()),
Time: listen.ListenedAt.Unix(),
Nofix: b.nofix,
}
if listen.ListenedAt.Unix() > result.LastTimestamp.Unix() { resp, err := b.client.NewScrobble(scrobble)
result.LastTimestamp = listen.ListenedAt if err != nil {
return importResult, err
} else if resp.Status != "success" {
return importResult, errors.New(resp.Error.Description)
}
importResult.UpdateTimestamp(listen.ListenedAt)
importResult.ImportCount += 1
} }
result.ImportCount += 1
} }
return result, nil
return importResult, nil
} }
func (s Scrobble) ToListen() models.Listen { func (s Scrobble) ToListen() models.Listen {

View file

@ -37,12 +37,12 @@ import (
type ScrobblerLog struct { type ScrobblerLog struct {
Timezone string Timezone string
Client string Client string
Listens []models.Listen Listens models.ListensList
} }
func Parse(data io.Reader, includeSkipped bool) (ScrobblerLog, error) { func Parse(data io.Reader, includeSkipped bool) (ScrobblerLog, error) {
result := ScrobblerLog{ result := ScrobblerLog{
Listens: make([]models.Listen, 0), Listens: make(models.ListensList, 0),
} }
reader := bufio.NewReader(data) reader := bufio.NewReader(data)
@ -92,16 +92,11 @@ func Parse(data io.Reader, includeSkipped bool) (ScrobblerLog, error) {
return result, nil return result, nil
} }
func Write(data io.Writer, log *ScrobblerLog) (lastTimestamp time.Time, err error) { func Write(data io.Writer, listens models.ListensList) (lastTimestamp time.Time, err error) {
err = writeHeader(data, log)
if err != nil {
return
}
tsvWriter := csv.NewWriter(data) tsvWriter := csv.NewWriter(data)
tsvWriter.Comma = '\t' tsvWriter.Comma = '\t'
for _, listen := range log.Listens { for _, listen := range listens {
if listen.ListenedAt.Unix() > lastTimestamp.Unix() { if listen.ListenedAt.Unix() > lastTimestamp.Unix() {
lastTimestamp = listen.ListenedAt lastTimestamp = listen.ListenedAt
} }
@ -162,7 +157,7 @@ func readHeader(reader *bufio.Reader, log *ScrobblerLog) error {
return nil return nil
} }
func writeHeader(writer io.Writer, log *ScrobblerLog) error { func WriteHeader(writer io.Writer, log *ScrobblerLog) error {
headers := []string{ headers := []string{
"#AUDIOSCROBBLER/1.1\n", "#AUDIOSCROBBLER/1.1\n",
"#TZ/" + log.Timezone + "\n", "#TZ/" + log.Timezone + "\n",

View file

@ -98,7 +98,9 @@ func TestWrite(t *testing.T) {
}, },
}, },
} }
lastTimestamp, err := scrobblerlog.Write(buffer, &log) err := scrobblerlog.WriteHeader(buffer, &log)
require.NoError(t, err)
lastTimestamp, err := scrobblerlog.Write(buffer, log.Listens)
require.NoError(t, err) require.NoError(t, err)
result := string(buffer.Bytes()) result := string(buffer.Bytes())
lines := strings.Split(result, "\n") lines := strings.Split(result, "\n")

View file

@ -23,6 +23,7 @@ package scrobblerlog
import ( import (
"os" "os"
"sort"
"time" "time"
"github.com/spf13/viper" "github.com/spf13/viper"
@ -40,31 +41,36 @@ func (b ScrobblerLogBackend) FromConfig(config *viper.Viper) models.Backend {
return b return b
} }
func (b ScrobblerLogBackend) ExportListens(oldestTimestamp time.Time) ([]models.Listen, error) { func (b ScrobblerLogBackend) ExportListens(oldestTimestamp time.Time, results chan models.ListensResult) {
defer close(results)
file, err := os.Open(b.filePath) file, err := os.Open(b.filePath)
if err != nil { if err != nil {
return nil, err results <- models.ListensResult{Error: err}
return
} }
defer file.Close() defer file.Close()
result, err := Parse(file, b.includeSkipped) log, err := Parse(file, b.includeSkipped)
if err != nil { if err != nil {
return nil, err results <- models.ListensResult{Error: err}
close(results)
return
} }
return result.Listens, nil listens := log.Listens.NewerThan(oldestTimestamp)
sort.Sort(listens)
results <- models.ListensResult{Listens: listens}
} }
func (b ScrobblerLogBackend) ImportListens(listens []models.Listen, oldestTimestamp time.Time) (models.ImportResult, error) { func (b ScrobblerLogBackend) ImportListens(results chan models.ListensResult, oldestTimestamp time.Time) (models.ImportResult, error) {
result := models.ImportResult{ importResult := models.ImportResult{
TotalCount: len(listens),
LastTimestamp: oldestTimestamp, LastTimestamp: oldestTimestamp,
} }
file, err := os.Create(b.filePath) file, err := os.Create(b.filePath)
if err != nil { if err != nil {
return result, err return importResult, err
} }
defer file.Close() defer file.Close()
@ -72,16 +78,27 @@ func (b ScrobblerLogBackend) ImportListens(listens []models.Listen, oldestTimest
log := ScrobblerLog{ log := ScrobblerLog{
Timezone: "UNKNOWN", Timezone: "UNKNOWN",
Client: "Rockbox unknown $Revision$", Client: "Rockbox unknown $Revision$",
Listens: listens,
} }
lastTimestamp, err := Write(file, &log) err = WriteHeader(file, &log)
if err != nil { if err != nil {
return result, err return importResult, err
} }
result.LastTimestamp = lastTimestamp for result := range results {
result.ImportCount = len(listens) if result.Error != nil {
return result, nil return importResult, result.Error
}
importResult.TotalCount += len(result.Listens)
lastTimestamp, err := Write(file, result.Listens)
if err != nil {
return importResult, err
}
importResult.UpdateTimestamp(lastTimestamp)
importResult.ImportCount += len(result.Listens)
}
return importResult, nil
} }

View file

@ -48,23 +48,20 @@ func (b SubsonicApiBackend) FromConfig(config *viper.Viper) models.Backend {
} }
func (b SubsonicApiBackend) ExportLoves(oldestTimestamp time.Time, results chan models.LovesResult) { func (b SubsonicApiBackend) ExportLoves(oldestTimestamp time.Time, results chan models.LovesResult) {
defer close(results)
err := b.client.Authenticate(b.password) err := b.client.Authenticate(b.password)
if err != nil { if err != nil {
results <- models.LovesResult{Error: err} results <- models.LovesResult{Error: err}
close(results)
return return
} }
starred, err := b.client.GetStarred2(map[string]string{}) starred, err := b.client.GetStarred2(map[string]string{})
if err != nil { if err != nil {
results <- models.LovesResult{Error: err} results <- models.LovesResult{Error: err}
close(results)
return return
} }
results <- models.LovesResult{Loves: b.filterSongs(starred.Song, oldestTimestamp)} results <- models.LovesResult{Loves: b.filterSongs(starred.Song, oldestTimestamp)}
close(results)
return
} }
func (b SubsonicApiBackend) filterSongs(songs []*subsonic.Child, oldestTimestamp time.Time) models.LovesList { func (b SubsonicApiBackend) filterSongs(songs []*subsonic.Child, oldestTimestamp time.Time) models.LovesList {

View file

@ -67,6 +67,17 @@ type Love struct {
type ListensList []Listen type ListensList []Listen
// Returns a new ListensList with only elements that are newer than t.
func (l ListensList) NewerThan(t time.Time) ListensList {
result := make(ListensList, 0, len(l))
for _, item := range l {
if item.ListenedAt.Unix() > t.Unix() {
result = append(result, item)
}
}
return result
}
func (l ListensList) Len() int { func (l ListensList) Len() int {
return len(l) return len(l)
} }

View file

@ -27,6 +27,7 @@ import (
"time" "time"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uploadedlobster.com/scotty/models" "go.uploadedlobster.com/scotty/models"
) )
@ -41,7 +42,7 @@ func TestTrackArtistName(t *testing.T) {
assert.Equal(t, "Foo, Bar, Baz", track.ArtistName()) assert.Equal(t, "Foo, Bar, Baz", track.ArtistName())
} }
func TestListensListSort(t *testing.T) { func TestListensListNewerThan(t *testing.T) {
listen1 := models.Listen{ListenedAt: time.Unix(3, 0)} listen1 := models.Listen{ListenedAt: time.Unix(3, 0)}
listen2 := models.Listen{ListenedAt: time.Unix(0, 0)} listen2 := models.Listen{ListenedAt: time.Unix(0, 0)}
listen3 := models.Listen{ListenedAt: time.Unix(2, 0)} listen3 := models.Listen{ListenedAt: time.Unix(2, 0)}
@ -52,6 +53,19 @@ func TestListensListSort(t *testing.T) {
assert.Equal(t, listen3, list[1]) assert.Equal(t, listen3, list[1])
} }
func TestListensListSort(t *testing.T) {
now := time.Now()
listen1 := models.Listen{UserName: "l1", ListenedAt: now.Add(-1 * time.Hour)}
listen2 := models.Listen{UserName: "l2", ListenedAt: now}
listen3 := models.Listen{UserName: "l3", ListenedAt: now.Add(1 * time.Hour)}
listen4 := models.Listen{UserName: "l4", ListenedAt: now.Add(2 * time.Hour)}
list := models.ListensList{listen1, listen2, listen3, listen4}
newList := list.NewerThan(now)
require.Len(t, newList, 2)
assert.Equal(t, listen3, newList[0])
assert.Equal(t, listen4, newList[1])
}
func TestLovesListSort(t *testing.T) { func TestLovesListSort(t *testing.T) {
love1 := models.Love{Created: time.Unix(3, 0)} love1 := models.Love{Created: time.Unix(3, 0)}
love2 := models.Love{Created: time.Unix(0, 0)} love2 := models.Love{Created: time.Unix(0, 0)}