mirror of
https://git.sr.ht/~phw/scotty
synced 2025-06-05 12:58:33 +02:00
Moved general LB related code to separate package
This commit is contained in:
parent
34b6bb9aa3
commit
5c56e480f1
10 changed files with 54 additions and 49 deletions
244
pkg/listenbrainz/models.go
Normal file
244
pkg/listenbrainz/models.go
Normal file
|
@ -0,0 +1,244 @@
|
|||
/*
|
||||
Copyright © 2023-2025 Philipp Wolfer <phw@uploadedlobster.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
package listenbrainz
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"go.uploadedlobster.com/mbtypes"
|
||||
"golang.org/x/exp/constraints"
|
||||
)
|
||||
|
||||
type GetListensResult struct {
|
||||
Payload GetListenPayload `json:"payload"`
|
||||
}
|
||||
|
||||
type GetListenPayload struct {
|
||||
Count int `json:"count"`
|
||||
UserName string `json:"user_id"`
|
||||
LatestListenTimestamp int64 `json:"latest_listen_ts"`
|
||||
OldestListenTimestamp int64 `json:"oldest_listen_ts"`
|
||||
Listens []Listen `json:"listens"`
|
||||
}
|
||||
|
||||
type listenType string
|
||||
|
||||
const (
|
||||
PlayingNow listenType = "playing_now"
|
||||
Single listenType = "single"
|
||||
Import listenType = "import"
|
||||
)
|
||||
|
||||
type ListenSubmission struct {
|
||||
ListenType listenType `json:"listen_type"`
|
||||
Payload []Listen `json:"payload"`
|
||||
}
|
||||
|
||||
type Listen struct {
|
||||
InsertedAt int64 `json:"inserted_at,omitempty"`
|
||||
ListenedAt int64 `json:"listened_at"`
|
||||
RecordingMSID string `json:"recording_msid,omitempty"`
|
||||
UserName string `json:"user_name,omitempty"`
|
||||
TrackMetadata Track `json:"track_metadata"`
|
||||
}
|
||||
|
||||
type Track struct {
|
||||
TrackName string `json:"track_name,omitempty"`
|
||||
ArtistName string `json:"artist_name,omitempty"`
|
||||
ReleaseName string `json:"release_name,omitempty"`
|
||||
RecordingMSID string `json:"recording_msid,omitempty"`
|
||||
AdditionalInfo map[string]any `json:"additional_info,omitempty"`
|
||||
MBIDMapping *MBIDMapping `json:"mbid_mapping,omitempty"`
|
||||
}
|
||||
|
||||
type MBIDMapping struct {
|
||||
ArtistMBIDs []mbtypes.MBID `json:"artist_mbids,omitempty"`
|
||||
Artists []Artist `json:"artists,omitempty"`
|
||||
RecordingMBID mbtypes.MBID `json:"recording_mbid,omitempty"`
|
||||
RecordingName string `json:"recording_name,omitempty"`
|
||||
ReleaseMBID mbtypes.MBID `json:"release_mbid,omitempty"`
|
||||
CAAID int `json:"caa_id,omitempty"`
|
||||
CAAReleaseMBID mbtypes.MBID `json:"caa_release_mbid,omitempty"`
|
||||
}
|
||||
|
||||
type Artist struct {
|
||||
ArtistCreditName string `json:"artist_credit_name,omitempty"`
|
||||
ArtistMBID string `json:"artist_mbid,omitempty"`
|
||||
JoinPhrase string `json:"join_phrase,omitempty"`
|
||||
}
|
||||
|
||||
type GetFeedbackResult struct {
|
||||
Count int `json:"count"`
|
||||
TotalCount int `json:"total_count"`
|
||||
Offset int `json:"offset"`
|
||||
Feedback []Feedback `json:"feedback"`
|
||||
}
|
||||
|
||||
type Feedback struct {
|
||||
Created int64 `json:"created,omitempty"`
|
||||
RecordingMBID mbtypes.MBID `json:"recording_mbid,omitempty"`
|
||||
RecordingMSID mbtypes.MBID `json:"recording_msid,omitempty"`
|
||||
Score int `json:"score,omitempty"`
|
||||
TrackMetadata *Track `json:"track_metadata,omitempty"`
|
||||
UserName string `json:"user_id,omitempty"`
|
||||
}
|
||||
|
||||
type LookupResult struct {
|
||||
ArtistCreditName string `json:"artist_credit_name"`
|
||||
ReleaseName string `json:"release_name"`
|
||||
RecordingName string `json:"recording_name"`
|
||||
RecordingMBID mbtypes.MBID `json:"recording_mbid"`
|
||||
ReleaseMBID mbtypes.MBID `json:"release_mbid"`
|
||||
ArtistMBIDs []mbtypes.MBID `json:"artist_mbids"`
|
||||
}
|
||||
|
||||
type StatusResult struct {
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
type ErrorResult struct {
|
||||
Code int `json:"code"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
func (t Track) Duration() time.Duration {
|
||||
info := t.AdditionalInfo
|
||||
millisecondsF, ok := tryGetFloat[float64](info, "duration_ms")
|
||||
if ok {
|
||||
return time.Duration(int64(millisecondsF * float64(time.Millisecond)))
|
||||
}
|
||||
|
||||
millisecondsI, ok := tryGetInteger[int64](info, "duration_ms")
|
||||
if ok {
|
||||
return time.Duration(millisecondsI * int64(time.Millisecond))
|
||||
}
|
||||
|
||||
secondsF, ok := tryGetFloat[float64](info, "duration")
|
||||
if ok {
|
||||
return time.Duration(int64(secondsF * float64(time.Second)))
|
||||
}
|
||||
|
||||
secondsI, ok := tryGetInteger[int64](info, "duration")
|
||||
if ok {
|
||||
return time.Duration(secondsI * int64(time.Second))
|
||||
}
|
||||
|
||||
return time.Duration(0)
|
||||
}
|
||||
|
||||
func (t Track) TrackNumber() int {
|
||||
value, ok := tryGetInteger[int](t.AdditionalInfo, "tracknumber")
|
||||
if ok {
|
||||
return value
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (t Track) DiscNumber() int {
|
||||
value, ok := tryGetInteger[int](t.AdditionalInfo, "discnumber")
|
||||
if ok {
|
||||
return value
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (t Track) ISRC() mbtypes.ISRC {
|
||||
return mbtypes.ISRC(tryGetValueOrEmpty[string](t.AdditionalInfo, "isrc"))
|
||||
}
|
||||
|
||||
func (t Track) RecordingMBID() mbtypes.MBID {
|
||||
mbid := mbtypes.MBID(tryGetValueOrEmpty[string](t.AdditionalInfo, "recording_mbid"))
|
||||
if mbid == "" && t.MBIDMapping != nil {
|
||||
return t.MBIDMapping.RecordingMBID
|
||||
} else {
|
||||
return mbid
|
||||
}
|
||||
}
|
||||
|
||||
func (t Track) ReleaseMBID() mbtypes.MBID {
|
||||
mbid := mbtypes.MBID(tryGetValueOrEmpty[string](t.AdditionalInfo, "release_mbid"))
|
||||
if mbid == "" && t.MBIDMapping != nil {
|
||||
return t.MBIDMapping.ReleaseMBID
|
||||
} else {
|
||||
return mbid
|
||||
}
|
||||
}
|
||||
|
||||
func (t Track) ReleaseGroupMBID() mbtypes.MBID {
|
||||
return mbtypes.MBID(tryGetValueOrEmpty[string](t.AdditionalInfo, "release_group_mbid"))
|
||||
}
|
||||
|
||||
func tryGetValueOrEmpty[T any](dict map[string]any, key string) T {
|
||||
var result T
|
||||
value, ok := dict[key].(T)
|
||||
if ok {
|
||||
result = value
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func tryGetFloat[T constraints.Float](dict map[string]any, key string) (T, bool) {
|
||||
valueFloat64, ok := dict[key].(float64)
|
||||
if ok {
|
||||
return T(valueFloat64), ok
|
||||
}
|
||||
|
||||
valueFloat32, ok := dict[key].(float32)
|
||||
if ok {
|
||||
return T(valueFloat32), ok
|
||||
}
|
||||
|
||||
valueStr, ok := dict[key].(string)
|
||||
if ok {
|
||||
valueFloat64, err := strconv.ParseFloat(valueStr, 64)
|
||||
if err == nil {
|
||||
return T(valueFloat64), ok
|
||||
}
|
||||
}
|
||||
|
||||
return 0, false
|
||||
}
|
||||
|
||||
func tryGetInteger[T constraints.Integer](dict map[string]any, key string) (T, bool) {
|
||||
valueInt64, ok := dict[key].(int64)
|
||||
if ok {
|
||||
return T(valueInt64), ok
|
||||
}
|
||||
|
||||
valueInt32, ok := dict[key].(int32)
|
||||
if ok {
|
||||
return T(valueInt32), ok
|
||||
}
|
||||
|
||||
valueInt, ok := dict[key].(int)
|
||||
if ok {
|
||||
return T(valueInt), ok
|
||||
}
|
||||
|
||||
valueFloat, ok := tryGetFloat[float64](dict, key)
|
||||
if ok {
|
||||
return T(valueFloat), ok
|
||||
}
|
||||
|
||||
return 0, false
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue