mirror of
https://git.sr.ht/~phw/scotty
synced 2025-04-26 06:17:55 +02:00
Tests for reading / writing scrobbler log
This commit is contained in:
parent
9d97e324aa
commit
cf8a6d2ab6
3 changed files with 344 additions and 161 deletions
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue