scrobblerlog: Use specific Record type

This makes the interface more generic and easier to reuse in other
projects.
This commit is contained in:
Philipp Wolfer 2025-04-29 09:18:57 +02:00
parent aeb3a56982
commit aad542850a
No known key found for this signature in database
GPG key ID: 8FDF744D4919943B
3 changed files with 132 additions and 76 deletions

View file

@ -37,7 +37,6 @@ import (
"time"
"go.uploadedlobster.com/mbtypes"
"go.uploadedlobster.com/scotty/internal/models"
)
// TZInfo is the timezone information in the header of the scrobbler log file.
@ -50,16 +49,36 @@ const (
TZ_UTC TZInfo = "UTC"
)
// L if listened at least 50% or S if skipped
type Rating string
const (
RATING_LISTENED Rating = "L"
RATING_SKIPPED Rating = "S"
)
// A single entry of a track in the scrobbler log file.
type Record struct {
ArtistName string
AlbumName string
TrackName string
TrackNumber int
Duration time.Duration
Rating Rating
Timestamp time.Time
MusicBrainzRecordingID mbtypes.MBID
}
// Represents a scrobbler log file.
type ScrobblerLog struct {
TZ TZInfo
Client string
Listens models.ListensList
Records []Record
location *time.Location
}
func (l *ScrobblerLog) Parse(data io.Reader, includeSkipped bool) error {
l.Listens = make(models.ListensList, 0)
l.Records = make([]Record, 0)
reader := bufio.NewReader(data)
err := l.ReadHeader(reader)
@ -95,41 +114,37 @@ func (l *ScrobblerLog) Parse(data io.Reader, includeSkipped bool) error {
continue
}
listen, err := l.rowToListen(row)
record, err := l.rowToRecord(row)
if err != nil {
return err
}
l.Listens = append(l.Listens, listen)
l.Records = append(l.Records, record)
}
return nil
}
func (l *ScrobblerLog) Append(data io.Writer, listens models.ListensList) (lastTimestamp time.Time, err error) {
func (l *ScrobblerLog) Append(data io.Writer, records []Record) (lastTimestamp time.Time, err error) {
tsvWriter := csv.NewWriter(data)
tsvWriter.Comma = '\t'
for _, listen := range listens {
if listen.ListenedAt.Unix() > lastTimestamp.Unix() {
lastTimestamp = listen.ListenedAt
for _, record := range records {
if record.Timestamp.After(lastTimestamp) {
lastTimestamp = record.Timestamp
}
// A row is:
// artistName releaseName trackName trackNumber duration rating timestamp recordingMBID
rating, ok := listen.AdditionalInfo["rockbox_rating"].(string)
if !ok || rating == "" {
rating = "L"
}
err = 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),
record.ArtistName,
record.AlbumName,
record.TrackName,
strconv.Itoa(record.TrackNumber),
strconv.Itoa(int(record.Duration.Seconds())),
string(record.Rating),
strconv.FormatInt(record.Timestamp.Unix(), 10),
string(record.MusicBrainzRecordingID),
})
}
@ -191,44 +206,38 @@ func (l *ScrobblerLog) WriteHeader(writer io.Writer) error {
return nil
}
func (l ScrobblerLog) rowToListen(row []string) (models.Listen, error) {
var listen models.Listen
func (l ScrobblerLog) rowToRecord(row []string) (Record, error) {
var record Record
trackNumber, err := strconv.Atoi(row[3])
if err != nil {
return listen, err
return record, err
}
duration, err := strconv.Atoi(row[4])
if err != nil {
return listen, err
return record, err
}
timestamp, err := strconv.ParseInt(row[6], 10, 64)
if err != nil {
return listen, err
return record, err
}
client := strings.Split(l.Client, " ")[0]
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: timeFromLocalTimestamp(timestamp, l.location),
record = Record{
ArtistName: row[0],
AlbumName: row[1],
TrackName: row[2],
TrackNumber: trackNumber,
Duration: time.Duration(duration) * time.Second,
Rating: Rating(row[5]),
Timestamp: timeFromLocalTimestamp(timestamp, l.location),
}
if len(row) > 7 {
listen.Track.RecordingMBID = mbtypes.MBID(row[7])
record.MusicBrainzRecordingID = mbtypes.MBID(row[7])
}
return listen, nil
return record, nil
}
// Convert the timezone string from the header to a time.Location.