Tests for reading / writing scrobbler log

This commit is contained in:
Philipp Wolfer 2023-11-11 17:08:23 +01:00
parent 9d97e324aa
commit cf8a6d2ab6
No known key found for this signature in database
GPG key ID: 8FDF744D4919943B
3 changed files with 344 additions and 161 deletions

View file

@ -22,17 +22,11 @@ THE SOFTWARE.
package backends
import (
"bufio"
"encoding/csv"
"errors"
"fmt"
"io"
"os"
"strconv"
"strings"
"time"
"github.com/spf13/viper"
"go.uploadedlobster.com/scotty/backends/scrobblerlog"
"go.uploadedlobster.com/scotty/models"
)
@ -55,51 +49,12 @@ func (b ScrobblerLogBackend) ExportListens(oldestTimestamp time.Time) ([]models.
defer file.Close()
reader := bufio.NewReader(file)
client, err := readHeader(reader)
result, err := scrobblerlog.Parse(file, b.includeSkipped)
if err != nil {
return nil, err
}
tsvReader := csv.NewReader(reader)
tsvReader.Comma = '\t'
// Row length is often flexible
tsvReader.FieldsPerRecord = -1
listens := make([]models.Listen, 0)
for {
// A row is:
// artistName releaseName trackName trackNumber duration rating timestamp recordingMbid
row, err := tsvReader.Read()
if err == io.EOF {
break
} else if err != nil {
return nil, err
}
// fmt.Printf("row: %v\n", row)
// We consider only the last field (recording MBID) optional
if len(row) < 7 {
line, _ := tsvReader.FieldPos(0)
return nil, errors.New(fmt.Sprintf(
"Invalid record in %s line %v", b.filePath, line))
}
rating := row[5]
if !b.includeSkipped && rating == "S" {
continue
}
listen, err := rowToListen(row, client)
if err != nil {
return nil, err
}
listens = append(listens, listen)
}
return listens, nil
return result.Listens, nil
}
func (b ScrobblerLogBackend) ImportListens(listens []models.Listen, oldestTimestamp time.Time) (ImportResult, error) {
@ -115,122 +70,19 @@ func (b ScrobblerLogBackend) ImportListens(listens []models.Listen, oldestTimest
defer file.Close()
err = writeHeader(file)
log := scrobblerlog.ScrobblerLog{
Timezone: "UNKNOWN",
Client: "Rockbox unknown $Revision$",
Listens: listens,
}
lastTimestamp, err := scrobblerlog.Write(file, &log)
if err != nil {
return result, err
}
tsvWriter := csv.NewWriter(file)
tsvWriter.Comma = '\t'
for _, listen := range listens {
result.Count += 1
if listen.ListenedAt.Unix() > result.LastTimestamp.Unix() {
result.LastTimestamp = listen.ListenedAt
}
// A row is:
// artistName releaseName trackName trackNumber duration rating timestamp recordingMbid
rating, ok := listen.AdditionalInfo["rockbox_rating"].(string)
if !ok || rating == "" {
rating = "L"
}
tsvWriter.Write([]string{
listen.ArtistName(),
listen.ReleaseName,
listen.TrackName,
strconv.Itoa(listen.TrackNumber),
strconv.Itoa(int(listen.Duration.Seconds())),
rating,
strconv.Itoa(int(listen.ListenedAt.Unix())),
string(listen.RecordingMbid),
})
}
tsvWriter.Flush()
result.LastTimestamp = lastTimestamp
result.Count = len(listens)
return result, nil
}
func readHeader(reader *bufio.Reader) (client string, err error) {
// Skip header
for i := 0; i < 3; i++ {
line, _, err := reader.ReadLine()
if err != nil {
return client, err
}
if len(line) == 0 || line[0] != '#' {
err = errors.New(fmt.Sprintf("Unexpected header (line %v)", i))
} else {
text := string(line)
if i == 0 && !strings.HasPrefix(text, "#AUDIOSCROBBLER/1") {
err = errors.New(fmt.Sprintf("Not a scrobbler log file"))
}
after, found := strings.CutPrefix(text, "#CLIENT/")
if found {
client = strings.Split(after, " ")[0]
}
}
if err != nil {
return client, err
}
}
return client, nil
}
func writeHeader(writer io.Writer) error {
headers := []string{
"#AUDIOSCROBBLER/1.1\n",
"#TZ/UNKNOWN\n",
"#CLIENT/Rockbox unknown $Revision$\n",
}
for _, line := range headers {
_, err := writer.Write([]byte(line))
if err != nil {
return err
}
}
return nil
}
func rowToListen(row []string, client string) (models.Listen, error) {
var listen models.Listen
trackNumber, err := strconv.Atoi(row[3])
if err != nil {
return listen, err
}
duration, err := strconv.Atoi(row[4])
if err != nil {
return listen, err
}
timestamp, err := strconv.Atoi(row[6])
if err != nil {
return listen, err
}
listen = models.Listen{
Track: models.Track{
ArtistNames: []string{row[0]},
ReleaseName: row[1],
TrackName: row[2],
TrackNumber: trackNumber,
Duration: time.Duration(duration * int(time.Second)),
AdditionalInfo: models.AdditionalInfo{
"rockbox_rating": row[5],
"media_player": client,
},
},
ListenedAt: time.Unix(int64(timestamp), 0),
}
if len(row) > 7 {
listen.Track.RecordingMbid = models.MBID(row[7])
}
return listen, nil
}