mirror of
https://git.sr.ht/~phw/scotty
synced 2025-04-30 05:37:05 +02:00
scrobblerlog: consider timezone from parsed file
This commit is contained in:
parent
9184d2c3cf
commit
69665bc286
1 changed files with 36 additions and 6 deletions
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright © 2023 Philipp Wolfer <phw@uploadedlobster.com>
|
Copyright © 2023-2025 Philipp Wolfer <phw@uploadedlobster.com>
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -38,6 +38,7 @@ type ScrobblerLog struct {
|
||||||
Timezone string
|
Timezone string
|
||||||
Client string
|
Client string
|
||||||
Listens models.ListensList
|
Listens models.ListensList
|
||||||
|
location *time.Location
|
||||||
}
|
}
|
||||||
|
|
||||||
func Parse(data io.Reader, includeSkipped bool) (ScrobblerLog, error) {
|
func Parse(data io.Reader, includeSkipped bool) (ScrobblerLog, error) {
|
||||||
|
@ -79,8 +80,7 @@ func Parse(data io.Reader, includeSkipped bool) (ScrobblerLog, error) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
client := strings.Split(result.Client, " ")[0]
|
listen, err := result.rowToListen(row)
|
||||||
listen, err := rowToListen(row, client)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
|
@ -138,14 +138,19 @@ func ReadHeader(reader *bufio.Reader, log *ScrobblerLog) error {
|
||||||
err = fmt.Errorf("not a scrobbler log file")
|
err = fmt.Errorf("not a scrobbler log file")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The timezone can be set to "UTC" or "UNKNOWN", if the device writing
|
||||||
|
// the log knows the time, but not the timezone.
|
||||||
timezone, found := strings.CutPrefix(text, "#TZ/")
|
timezone, found := strings.CutPrefix(text, "#TZ/")
|
||||||
if found {
|
if found {
|
||||||
log.Timezone = timezone
|
log.Timezone = timezone
|
||||||
|
log.location = locationFromTimezone(log.Timezone)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
client, found := strings.CutPrefix(text, "#CLIENT/")
|
client, found := strings.CutPrefix(text, "#CLIENT/")
|
||||||
if found {
|
if found {
|
||||||
log.Client = client
|
log.Client = client
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,7 +176,7 @@ func WriteHeader(writer io.Writer, log *ScrobblerLog) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func rowToListen(row []string, client string) (models.Listen, error) {
|
func (l ScrobblerLog) rowToListen(row []string) (models.Listen, error) {
|
||||||
var listen models.Listen
|
var listen models.Listen
|
||||||
trackNumber, err := strconv.Atoi(row[3])
|
trackNumber, err := strconv.Atoi(row[3])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -183,11 +188,12 @@ func rowToListen(row []string, client string) (models.Listen, error) {
|
||||||
return listen, err
|
return listen, err
|
||||||
}
|
}
|
||||||
|
|
||||||
timestamp, err := strconv.Atoi(row[6])
|
timestamp, err := strconv.ParseInt(row[6], 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return listen, err
|
return listen, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
client := strings.Split(l.Client, " ")[0]
|
||||||
listen = models.Listen{
|
listen = models.Listen{
|
||||||
Track: models.Track{
|
Track: models.Track{
|
||||||
ArtistNames: []string{row[0]},
|
ArtistNames: []string{row[0]},
|
||||||
|
@ -200,7 +206,7 @@ func rowToListen(row []string, client string) (models.Listen, error) {
|
||||||
"media_player": client,
|
"media_player": client,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ListenedAt: time.Unix(int64(timestamp), 0),
|
ListenedAt: timeFromLocalTimestamp(timestamp, l.location),
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(row) > 7 {
|
if len(row) > 7 {
|
||||||
|
@ -209,3 +215,27 @@ func rowToListen(row []string, client string) (models.Listen, error) {
|
||||||
|
|
||||||
return listen, nil
|
return listen, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert the timezone string from the header to a time.Location.
|
||||||
|
// Often this is set to "UNKNOWN" in the log file, in which case it defaults
|
||||||
|
// to UTC.
|
||||||
|
func locationFromTimezone(timezone string) *time.Location {
|
||||||
|
location, err := time.LoadLocation(timezone)
|
||||||
|
if err != nil {
|
||||||
|
return time.UTC
|
||||||
|
}
|
||||||
|
return location
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert a Unix timestamp to a time.Time object, but treat the timestamp
|
||||||
|
// as being in the given location's timezone instead of UTC.
|
||||||
|
func timeFromLocalTimestamp(timestamp int64, location *time.Location) time.Time {
|
||||||
|
t := time.Unix(timestamp, 0)
|
||||||
|
|
||||||
|
// The time is now in UTC. Get the offset to the requested timezone.
|
||||||
|
_, offset := t.In(location).Zone()
|
||||||
|
if offset != 0 {
|
||||||
|
t = t.Add(time.Duration(offset) * time.Second)
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue