feat(subsonic): expose replaygain tags

This commit is contained in:
sentriz
2024-05-30 11:43:45 +01:00
parent 259be0edde
commit 0e45f5e84c
18 changed files with 307 additions and 178 deletions

View File

@@ -67,7 +67,7 @@ password can then be changed from the web interface
| `GONIC_JUKEBOX_ENABLED` | `-jukebox-enabled` | **optional** whether the subsonic [jukebox api](https://airsonic.github.io/docs/jukebox/) should be enabled | | `GONIC_JUKEBOX_ENABLED` | `-jukebox-enabled` | **optional** whether the subsonic [jukebox api](https://airsonic.github.io/docs/jukebox/) should be enabled |
| `GONIC_JUKEBOX_MPV_EXTRA_ARGS` | `-jukebox-mpv-extra-args` | **optional** extra command line arguments to pass to the jukebox mpv daemon | | `GONIC_JUKEBOX_MPV_EXTRA_ARGS` | `-jukebox-mpv-extra-args` | **optional** extra command line arguments to pass to the jukebox mpv daemon |
| `GONIC_PODCAST_PURGE_AGE` | `-podcast-purge-age` | **optional** age (in days) to purge podcast episodes if not accessed | | `GONIC_PODCAST_PURGE_AGE` | `-podcast-purge-age` | **optional** age (in days) to purge podcast episodes if not accessed |
| `GONIC_EXCLUDE_PATTERN` | `-exclude-pattern` | **optional** files matching this regex pattern will not be imported. Example : <code>@eaDir\|[aA]rtwork\|[cC]overs\|[sS]cans\|[sS]pectrals</code> | | `GONIC_EXCLUDE_PATTERN` | `-exclude-pattern` | **optional** files matching this regex pattern will not be imported. eg <code>@eaDir\|[aA]rtwork\|[cC]overs\|[sS]cans\|[sS]pectrals</code> |
| `GONIC_MULTI_VALUE_GENRE` | `-multi-value-genre` | **optional** setting for multi-valued genre tags when scanning ([see more](#multi-valued-tags-v016)) | | `GONIC_MULTI_VALUE_GENRE` | `-multi-value-genre` | **optional** setting for multi-valued genre tags when scanning ([see more](#multi-valued-tags-v016)) |
| `GONIC_MULTI_VALUE_ARTIST` | `-multi-value-artist` | **optional** setting for multi-valued artist tags when scanning ([see more](#multi-valued-tags-v016)) | | `GONIC_MULTI_VALUE_ARTIST` | `-multi-value-artist` | **optional** setting for multi-valued artist tags when scanning ([see more](#multi-valued-tags-v016)) |
| `GONIC_MULTI_VALUE_ALBUM_ARTIST` | `-multi-value-album-artist` | **optional** setting for multi-valued album artist tags when scanning ([see more](#multi-valued-tags-v016)) | | `GONIC_MULTI_VALUE_ALBUM_ARTIST` | `-multi-value-album-artist` | **optional** setting for multi-valued album artist tags when scanning ([see more](#multi-valued-tags-v016)) |
@@ -97,7 +97,7 @@ the available modes are:
gonic supports multiple music folders. this can be handy if you have your music separated by albums, compilations, singles. or maybe 70s, 80s, 90s. whatever. gonic supports multiple music folders. this can be handy if you have your music separated by albums, compilations, singles. or maybe 70s, 80s, 90s. whatever.
on top of that - if you don't decide your folder names, or simply do not want the same name in your subsonic client, on top of that - if you don't decide your folder names, or simply do not want the same name in your subsonic client,
gonic can parse aliases for the folder names with the optional `ALIAS->PATH` syntax gonic can parse aliases for the folder names with the optional `ALIAS->PATH` syntax
if you're running gonic with the command line, stack the `-music-path` arg if you're running gonic with the command line, stack the `-music-path` arg

View File

@@ -235,9 +235,15 @@ type Track struct {
TagTrackNumber int `sql:"default: null"` TagTrackNumber int `sql:"default: null"`
TagDiscNumber int `sql:"default: null"` TagDiscNumber int `sql:"default: null"`
TagBrainzID string `sql:"default: null"` TagBrainzID string `sql:"default: null"`
TrackStar *TrackStar
TrackRating *TrackRating ReplayGainTrackGain float32
AverageRating float64 `sql:"default: null"` ReplayGainTrackPeak float32
ReplayGainAlbumGain float32
ReplayGainAlbumPeak float32
TrackStar *TrackStar
TrackRating *TrackRating
AverageRating float64 `sql:"default: null"`
} }
func (t *Track) AudioLength() int { return t.Length } func (t *Track) AudioLength() int { return t.Length }

View File

@@ -72,6 +72,7 @@ func (db *DB) Migrate(ctx MigrationContext) error {
construct(ctx, "202311072309", migrateAlbumInfo), construct(ctx, "202311072309", migrateAlbumInfo),
construct(ctx, "202311082304", migrateTemporaryDisplayAlbumArtist), construct(ctx, "202311082304", migrateTemporaryDisplayAlbumArtist),
construct(ctx, "202312110003", migrateAddExtraIndexes), construct(ctx, "202312110003", migrateAddExtraIndexes),
construct(ctx, "202405301140", migrateAddReplayGainFields),
} }
return gormigrate. return gormigrate.
@@ -813,3 +814,7 @@ func migrateAddExtraIndexes(tx *gorm.DB, _ MigrationContext) error {
CREATE INDEX idx_artist_appearances_album_id ON "artist_appearances" (album_id); CREATE INDEX idx_artist_appearances_album_id ON "artist_appearances" (album_id);
`).Error `).Error
} }
func migrateAddReplayGainFields(tx *gorm.DB, _ MigrationContext) error {
return tx.AutoMigrate(Track{}).Error
}

View File

@@ -362,8 +362,14 @@ func (i *TagInfo) Genres() []string { return []string{i.RawGenre} }
func (i *TagInfo) TrackNumber() int { return 1 } func (i *TagInfo) TrackNumber() int { return 1 }
func (i *TagInfo) DiscNumber() int { return 1 } func (i *TagInfo) DiscNumber() int { return 1 }
func (i *TagInfo) Year() int { return 2021 } func (i *TagInfo) Year() int { return 2021 }
func (i *TagInfo) Length() int { return firstInt(100, i.RawLength) }
func (i *TagInfo) Bitrate() int { return firstInt(100, i.RawBitrate) } func (i *TagInfo) ReplayGainTrackGain() float32 { return 0 }
func (i *TagInfo) ReplayGainTrackPeak() float32 { return 0 }
func (i *TagInfo) ReplayGainAlbumGain() float32 { return 0 }
func (i *TagInfo) ReplayGainAlbumPeak() float32 { return 0 }
func (i *TagInfo) Length() int { return firstInt(100, i.RawLength) }
func (i *TagInfo) Bitrate() int { return firstInt(100, i.RawBitrate) }
var _ tagcommon.Reader = (*tagReader)(nil) var _ tagcommon.Reader = (*tagReader)(nil)

View File

@@ -469,8 +469,14 @@ func populateTrack(tx *db.DB, album *db.Album, track *db.Track, trags tagcommon.
track.TagDiscNumber = trags.DiscNumber() track.TagDiscNumber = trags.DiscNumber()
track.TagBrainzID = trags.BrainzID() track.TagBrainzID = trags.BrainzID()
track.Length = trags.Length() // these two should be calculated track.ReplayGainTrackGain = trags.ReplayGainTrackGain()
track.Bitrate = trags.Bitrate() // ...from the file instead of tags track.ReplayGainTrackPeak = trags.ReplayGainTrackPeak()
track.ReplayGainAlbumGain = trags.ReplayGainAlbumGain()
track.ReplayGainAlbumPeak = trags.ReplayGainAlbumPeak()
// these two are calculated from the file instead of tags
track.Length = trags.Length()
track.Bitrate = trags.Bitrate()
if err := tx.Save(&track).Error; err != nil { if err := tx.Save(&track).Error; err != nil {
return fmt.Errorf("saving track: %w", err) return fmt.Errorf("saving track: %w", err)

View File

@@ -102,6 +102,14 @@ func NewTCTrackByFolder(t *db.Track, parent *db.Album) *TrackChild {
for _, a := range t.Artists { for _, a := range t.Artists {
trCh.Artists = append(trCh.Artists, &ArtistRef{ID: a.SID(), Name: a.Name}) trCh.Artists = append(trCh.Artists, &ArtistRef{ID: a.SID(), Name: a.Name})
} }
if t.ReplayGainTrackGain != 0 || t.ReplayGainAlbumGain != 0 {
trCh.ReplayGain = &ReplayGain{
TrackGain: t.ReplayGainTrackGain,
TrackPeak: t.ReplayGainTrackPeak,
AlbumGain: t.ReplayGainAlbumGain,
AlbumPeak: t.ReplayGainAlbumPeak,
}
}
return trCh return trCh
} }

View File

@@ -112,6 +112,14 @@ func NewTrackByTags(t *db.Track, album *db.Album) *TrackChild {
for _, a := range album.Artists { for _, a := range album.Artists {
ret.AlbumArtists = append(ret.AlbumArtists, &ArtistRef{ID: a.SID(), Name: a.Name}) ret.AlbumArtists = append(ret.AlbumArtists, &ArtistRef{ID: a.SID(), Name: a.Name})
} }
if t.ReplayGainTrackGain != 0 || t.ReplayGainAlbumGain != 0 {
ret.ReplayGain = &ReplayGain{
TrackGain: t.ReplayGainTrackGain,
TrackPeak: t.ReplayGainTrackPeak,
AlbumGain: t.ReplayGainAlbumGain,
AlbumPeak: t.ReplayGainAlbumPeak,
}
}
return ret return ret
} }

View File

@@ -169,6 +169,13 @@ type TranscodeMeta struct {
TranscodedSuffix string `xml:"transcodedSuffix,attr,omitempty" json:"transcodedSuffix,omitempty"` TranscodedSuffix string `xml:"transcodedSuffix,attr,omitempty" json:"transcodedSuffix,omitempty"`
} }
type ReplayGain struct {
TrackGain float32 `xml:"trackGain,attr" json:"trackGain"`
TrackPeak float32 `xml:"trackPeak,attr" json:"trackPeak"`
AlbumGain float32 `xml:"albumGain,attr" json:"albumGain"`
AlbumPeak float32 `xml:"albumPeak,attr" json:"albumPeak"`
}
// https://opensubsonic.netlify.app/docs/responses/child/ // https://opensubsonic.netlify.app/docs/responses/child/
type TrackChild struct { type TrackChild struct {
ID *specid.ID `xml:"id,attr,omitempty" json:"id,omitempty"` ID *specid.ID `xml:"id,attr,omitempty" json:"id,omitempty"`
@@ -211,6 +218,8 @@ type TrackChild struct {
UserRating int `xml:"userRating,attr,omitempty" json:"userRating,omitempty"` UserRating int `xml:"userRating,attr,omitempty" json:"userRating,omitempty"`
AverageRating string `xml:"averageRating,attr,omitempty" json:"averageRating,omitempty"` AverageRating string `xml:"averageRating,attr,omitempty" json:"averageRating,omitempty"`
ReplayGain *ReplayGain `xml:"replayGain" json:"replayGain"`
TranscodeMeta TranscodeMeta
} }

View File

@@ -8,16 +8,16 @@
"albumList": { "albumList": {
"album": [ "album": [
{ {
"id": "al-9", "id": "al-5",
"created": "2019-11-30T00:00:00Z", "created": "2019-11-30T00:00:00Z",
"artist": "artist-1", "artist": "artist-0",
"artists": null, "artists": null,
"displayArtist": "", "displayArtist": "",
"title": "album-2", "title": "album-2",
"album": "album-2", "album": "album-2",
"parent": "al-6", "parent": "al-2",
"isDir": true, "isDir": true,
"coverArt": "al-9", "coverArt": "al-5",
"name": "album-2", "name": "album-2",
"songCount": 3, "songCount": 3,
"duration": 300, "duration": 300,
@@ -40,48 +40,16 @@
"playCount": 0 "playCount": 0
}, },
{ {
"id": "al-4", "id": "al-13",
"created": "2019-11-30T00:00:00Z",
"artist": "artist-0",
"artists": null,
"displayArtist": "",
"title": "album-1",
"album": "album-1",
"parent": "al-2",
"isDir": true,
"coverArt": "al-4",
"name": "album-1",
"songCount": 3,
"duration": 300,
"playCount": 0
},
{
"id": "al-12",
"created": "2019-11-30T00:00:00Z", "created": "2019-11-30T00:00:00Z",
"artist": "artist-2", "artist": "artist-2",
"artists": null, "artists": null,
"displayArtist": "", "displayArtist": "",
"title": "album-1",
"album": "album-1",
"parent": "al-10",
"isDir": true,
"coverArt": "al-12",
"name": "album-1",
"songCount": 3,
"duration": 300,
"playCount": 0
},
{
"id": "al-5",
"created": "2019-11-30T00:00:00Z",
"artist": "artist-0",
"artists": null,
"displayArtist": "",
"title": "album-2", "title": "album-2",
"album": "album-2", "album": "album-2",
"parent": "al-2", "parent": "al-10",
"isDir": true, "isDir": true,
"coverArt": "al-5", "coverArt": "al-13",
"name": "album-2", "name": "album-2",
"songCount": 3, "songCount": 3,
"duration": 300, "duration": 300,
@@ -103,6 +71,54 @@
"duration": 300, "duration": 300,
"playCount": 0 "playCount": 0
}, },
{
"id": "al-4",
"created": "2019-11-30T00:00:00Z",
"artist": "artist-0",
"artists": null,
"displayArtist": "",
"title": "album-1",
"album": "album-1",
"parent": "al-2",
"isDir": true,
"coverArt": "al-4",
"name": "album-1",
"songCount": 3,
"duration": 300,
"playCount": 0
},
{
"id": "al-9",
"created": "2019-11-30T00:00:00Z",
"artist": "artist-1",
"artists": null,
"displayArtist": "",
"title": "album-2",
"album": "album-2",
"parent": "al-6",
"isDir": true,
"coverArt": "al-9",
"name": "album-2",
"songCount": 3,
"duration": 300,
"playCount": 0
},
{
"id": "al-12",
"created": "2019-11-30T00:00:00Z",
"artist": "artist-2",
"artists": null,
"displayArtist": "",
"title": "album-1",
"album": "album-1",
"parent": "al-10",
"isDir": true,
"coverArt": "al-12",
"name": "album-1",
"songCount": 3,
"duration": 300,
"playCount": 0
},
{ {
"id": "al-8", "id": "al-8",
"created": "2019-11-30T00:00:00Z", "created": "2019-11-30T00:00:00Z",
@@ -119,22 +135,6 @@
"duration": 300, "duration": 300,
"playCount": 0 "playCount": 0
}, },
{
"id": "al-13",
"created": "2019-11-30T00:00:00Z",
"artist": "artist-2",
"artists": null,
"displayArtist": "",
"title": "album-2",
"album": "album-2",
"parent": "al-10",
"isDir": true,
"coverArt": "al-13",
"name": "album-2",
"songCount": 3,
"duration": 300,
"playCount": 0
},
{ {
"id": "al-11", "id": "al-11",
"created": "2019-11-30T00:00:00Z", "created": "2019-11-30T00:00:00Z",

View File

@@ -24,32 +24,16 @@
"year": 2021 "year": 2021
}, },
{ {
"id": "al-5", "id": "al-7",
"created": "2019-11-30T00:00:00Z",
"artistId": "ar-1",
"artist": "artist-0",
"artists": [{ "id": "ar-1", "name": "artist-0" }],
"displayArtist": "artist-0",
"title": "album-2",
"album": "album-2",
"coverArt": "al-5",
"name": "album-2",
"songCount": 3,
"duration": 300,
"playCount": 0,
"year": 2021
},
{
"id": "al-8",
"created": "2019-11-30T00:00:00Z", "created": "2019-11-30T00:00:00Z",
"artistId": "ar-2", "artistId": "ar-2",
"artist": "artist-1", "artist": "artist-1",
"artists": [{ "id": "ar-2", "name": "artist-1" }], "artists": [{ "id": "ar-2", "name": "artist-1" }],
"displayArtist": "artist-1", "displayArtist": "artist-1",
"title": "album-1", "title": "album-0",
"album": "album-1", "album": "album-0",
"coverArt": "al-8", "coverArt": "al-7",
"name": "album-1", "name": "album-0",
"songCount": 3, "songCount": 3,
"duration": 300, "duration": 300,
"playCount": 0, "playCount": 0,
@@ -71,38 +55,6 @@
"playCount": 0, "playCount": 0,
"year": 2021 "year": 2021
}, },
{
"id": "al-12",
"created": "2019-11-30T00:00:00Z",
"artistId": "ar-3",
"artist": "artist-2",
"artists": [{ "id": "ar-3", "name": "artist-2" }],
"displayArtist": "artist-2",
"title": "album-1",
"album": "album-1",
"coverArt": "al-12",
"name": "album-1",
"songCount": 3,
"duration": 300,
"playCount": 0,
"year": 2021
},
{
"id": "al-7",
"created": "2019-11-30T00:00:00Z",
"artistId": "ar-2",
"artist": "artist-1",
"artists": [{ "id": "ar-2", "name": "artist-1" }],
"displayArtist": "artist-1",
"title": "album-0",
"album": "album-0",
"coverArt": "al-7",
"name": "album-0",
"songCount": 3,
"duration": 300,
"playCount": 0,
"year": 2021
},
{ {
"id": "al-13", "id": "al-13",
"created": "2019-11-30T00:00:00Z", "created": "2019-11-30T00:00:00Z",
@@ -135,6 +87,54 @@
"playCount": 0, "playCount": 0,
"year": 2021 "year": 2021
}, },
{
"id": "al-12",
"created": "2019-11-30T00:00:00Z",
"artistId": "ar-3",
"artist": "artist-2",
"artists": [{ "id": "ar-3", "name": "artist-2" }],
"displayArtist": "artist-2",
"title": "album-1",
"album": "album-1",
"coverArt": "al-12",
"name": "album-1",
"songCount": 3,
"duration": 300,
"playCount": 0,
"year": 2021
},
{
"id": "al-5",
"created": "2019-11-30T00:00:00Z",
"artistId": "ar-1",
"artist": "artist-0",
"artists": [{ "id": "ar-1", "name": "artist-0" }],
"displayArtist": "artist-0",
"title": "album-2",
"album": "album-2",
"coverArt": "al-5",
"name": "album-2",
"songCount": 3,
"duration": 300,
"playCount": 0,
"year": 2021
},
{
"id": "al-8",
"created": "2019-11-30T00:00:00Z",
"artistId": "ar-2",
"artist": "artist-1",
"artists": [{ "id": "ar-2", "name": "artist-1" }],
"displayArtist": "artist-1",
"title": "album-1",
"album": "album-1",
"coverArt": "al-8",
"name": "album-1",
"songCount": 3,
"duration": 300,
"playCount": 0,
"year": 2021
},
{ {
"id": "al-9", "id": "al-9",
"created": "2019-11-30T00:00:00Z", "created": "2019-11-30T00:00:00Z",

View File

@@ -48,7 +48,8 @@
"discNumber": 1, "discNumber": 1,
"type": "music", "type": "music",
"year": 2021, "year": 2021,
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "tr-2", "id": "tr-2",
@@ -75,7 +76,8 @@
"discNumber": 1, "discNumber": 1,
"type": "music", "type": "music",
"year": 2021, "year": 2021,
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "tr-3", "id": "tr-3",
@@ -102,7 +104,8 @@
"discNumber": 1, "discNumber": 1,
"type": "music", "type": "music",
"year": 2021, "year": 2021,
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
} }
] ]
} }

View File

@@ -33,7 +33,8 @@
"discNumber": 1, "discNumber": 1,
"type": "music", "type": "music",
"year": 2021, "year": 2021,
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "tr-2", "id": "tr-2",
@@ -58,7 +59,8 @@
"discNumber": 1, "discNumber": 1,
"type": "music", "type": "music",
"year": 2021, "year": 2021,
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "tr-3", "id": "tr-3",
@@ -83,7 +85,8 @@
"discNumber": 1, "discNumber": 1,
"type": "music", "type": "music",
"year": 2021, "year": 2021,
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
} }
] ]
} }

View File

@@ -23,7 +23,8 @@
"isVideo": false, "isVideo": false,
"parent": "al-2", "parent": "al-2",
"title": "album-0", "title": "album-0",
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "al-4", "id": "al-4",
@@ -38,7 +39,8 @@
"isVideo": false, "isVideo": false,
"parent": "al-2", "parent": "al-2",
"title": "album-1", "title": "album-1",
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "al-5", "id": "al-5",
@@ -53,7 +55,8 @@
"isVideo": false, "isVideo": false,
"parent": "al-2", "parent": "al-2",
"title": "album-2", "title": "album-2",
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
} }
] ]
} }

View File

@@ -34,7 +34,8 @@
"discNumber": 1, "discNumber": 1,
"type": "music", "type": "music",
"year": 2021, "year": 2021,
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "tr-2", "id": "tr-2",
@@ -63,7 +64,8 @@
"discNumber": 1, "discNumber": 1,
"type": "music", "type": "music",
"year": 2021, "year": 2021,
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "tr-3", "id": "tr-3",
@@ -92,7 +94,8 @@
"discNumber": 1, "discNumber": 1,
"type": "music", "type": "music",
"year": 2021, "year": 2021,
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "tr-4", "id": "tr-4",
@@ -121,7 +124,8 @@
"discNumber": 1, "discNumber": 1,
"type": "music", "type": "music",
"year": 2021, "year": 2021,
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "tr-5", "id": "tr-5",
@@ -150,7 +154,8 @@
"discNumber": 1, "discNumber": 1,
"type": "music", "type": "music",
"year": 2021, "year": 2021,
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "tr-6", "id": "tr-6",
@@ -179,7 +184,8 @@
"discNumber": 1, "discNumber": 1,
"type": "music", "type": "music",
"year": 2021, "year": 2021,
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "tr-7", "id": "tr-7",
@@ -208,7 +214,8 @@
"discNumber": 1, "discNumber": 1,
"type": "music", "type": "music",
"year": 2021, "year": 2021,
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "tr-8", "id": "tr-8",
@@ -237,7 +244,8 @@
"discNumber": 1, "discNumber": 1,
"type": "music", "type": "music",
"year": 2021, "year": 2021,
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "tr-9", "id": "tr-9",
@@ -266,7 +274,8 @@
"discNumber": 1, "discNumber": 1,
"type": "music", "type": "music",
"year": 2021, "year": 2021,
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "tr-10", "id": "tr-10",
@@ -295,7 +304,8 @@
"discNumber": 1, "discNumber": 1,
"type": "music", "type": "music",
"year": 2021, "year": 2021,
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "tr-11", "id": "tr-11",
@@ -324,7 +334,8 @@
"discNumber": 1, "discNumber": 1,
"type": "music", "type": "music",
"year": 2021, "year": 2021,
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "tr-12", "id": "tr-12",
@@ -353,7 +364,8 @@
"discNumber": 1, "discNumber": 1,
"type": "music", "type": "music",
"year": 2021, "year": 2021,
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "tr-13", "id": "tr-13",
@@ -382,7 +394,8 @@
"discNumber": 1, "discNumber": 1,
"type": "music", "type": "music",
"year": 2021, "year": 2021,
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "tr-14", "id": "tr-14",
@@ -411,7 +424,8 @@
"discNumber": 1, "discNumber": 1,
"type": "music", "type": "music",
"year": 2021, "year": 2021,
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "tr-15", "id": "tr-15",
@@ -440,7 +454,8 @@
"discNumber": 1, "discNumber": 1,
"type": "music", "type": "music",
"year": 2021, "year": 2021,
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "tr-16", "id": "tr-16",
@@ -469,7 +484,8 @@
"discNumber": 1, "discNumber": 1,
"type": "music", "type": "music",
"year": 2021, "year": 2021,
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "tr-17", "id": "tr-17",
@@ -498,7 +514,8 @@
"discNumber": 1, "discNumber": 1,
"type": "music", "type": "music",
"year": 2021, "year": 2021,
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "tr-18", "id": "tr-18",
@@ -527,7 +544,8 @@
"discNumber": 1, "discNumber": 1,
"type": "music", "type": "music",
"year": 2021, "year": 2021,
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "tr-19", "id": "tr-19",
@@ -556,7 +574,8 @@
"discNumber": 1, "discNumber": 1,
"type": "music", "type": "music",
"year": 2021, "year": 2021,
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "tr-20", "id": "tr-20",
@@ -585,7 +604,8 @@
"discNumber": 1, "discNumber": 1,
"type": "music", "type": "music",
"year": 2021, "year": 2021,
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
} }
] ]
} }

View File

@@ -20,7 +20,8 @@
"isVideo": false, "isVideo": false,
"parent": "al-2", "parent": "al-2",
"title": "album-0", "title": "album-0",
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "al-4", "id": "al-4",
@@ -35,7 +36,8 @@
"isVideo": false, "isVideo": false,
"parent": "al-2", "parent": "al-2",
"title": "album-1", "title": "album-1",
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "al-5", "id": "al-5",
@@ -50,7 +52,8 @@
"isVideo": false, "isVideo": false,
"parent": "al-2", "parent": "al-2",
"title": "album-2", "title": "album-2",
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "al-7", "id": "al-7",
@@ -65,7 +68,8 @@
"isVideo": false, "isVideo": false,
"parent": "al-6", "parent": "al-6",
"title": "album-0", "title": "album-0",
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "al-8", "id": "al-8",
@@ -80,7 +84,8 @@
"isVideo": false, "isVideo": false,
"parent": "al-6", "parent": "al-6",
"title": "album-1", "title": "album-1",
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "al-9", "id": "al-9",
@@ -95,7 +100,8 @@
"isVideo": false, "isVideo": false,
"parent": "al-6", "parent": "al-6",
"title": "album-2", "title": "album-2",
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "al-11", "id": "al-11",
@@ -110,7 +116,8 @@
"isVideo": false, "isVideo": false,
"parent": "al-10", "parent": "al-10",
"title": "album-0", "title": "album-0",
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "al-12", "id": "al-12",
@@ -125,7 +132,8 @@
"isVideo": false, "isVideo": false,
"parent": "al-10", "parent": "al-10",
"title": "album-1", "title": "album-1",
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "al-13", "id": "al-13",
@@ -140,7 +148,8 @@
"isVideo": false, "isVideo": false,
"parent": "al-10", "parent": "al-10",
"title": "album-2", "title": "album-2",
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
} }
] ]
} }

View File

@@ -30,7 +30,8 @@
"discNumber": 1, "discNumber": 1,
"type": "music", "type": "music",
"year": 2021, "year": 2021,
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "tr-2", "id": "tr-2",
@@ -55,7 +56,8 @@
"discNumber": 1, "discNumber": 1,
"type": "music", "type": "music",
"year": 2021, "year": 2021,
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "tr-3", "id": "tr-3",
@@ -80,7 +82,8 @@
"discNumber": 1, "discNumber": 1,
"type": "music", "type": "music",
"year": 2021, "year": 2021,
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "tr-4", "id": "tr-4",
@@ -105,7 +108,8 @@
"discNumber": 1, "discNumber": 1,
"type": "music", "type": "music",
"year": 2021, "year": 2021,
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "tr-5", "id": "tr-5",
@@ -130,7 +134,8 @@
"discNumber": 1, "discNumber": 1,
"type": "music", "type": "music",
"year": 2021, "year": 2021,
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "tr-6", "id": "tr-6",
@@ -155,7 +160,8 @@
"discNumber": 1, "discNumber": 1,
"type": "music", "type": "music",
"year": 2021, "year": 2021,
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "tr-7", "id": "tr-7",
@@ -180,7 +186,8 @@
"discNumber": 1, "discNumber": 1,
"type": "music", "type": "music",
"year": 2021, "year": 2021,
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "tr-8", "id": "tr-8",
@@ -205,7 +212,8 @@
"discNumber": 1, "discNumber": 1,
"type": "music", "type": "music",
"year": 2021, "year": 2021,
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "tr-9", "id": "tr-9",
@@ -230,7 +238,8 @@
"discNumber": 1, "discNumber": 1,
"type": "music", "type": "music",
"year": 2021, "year": 2021,
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "tr-10", "id": "tr-10",
@@ -255,7 +264,8 @@
"discNumber": 1, "discNumber": 1,
"type": "music", "type": "music",
"year": 2021, "year": 2021,
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "tr-11", "id": "tr-11",
@@ -280,7 +290,8 @@
"discNumber": 1, "discNumber": 1,
"type": "music", "type": "music",
"year": 2021, "year": 2021,
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "tr-12", "id": "tr-12",
@@ -305,7 +316,8 @@
"discNumber": 1, "discNumber": 1,
"type": "music", "type": "music",
"year": 2021, "year": 2021,
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "tr-13", "id": "tr-13",
@@ -330,7 +342,8 @@
"discNumber": 1, "discNumber": 1,
"type": "music", "type": "music",
"year": 2021, "year": 2021,
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "tr-14", "id": "tr-14",
@@ -355,7 +368,8 @@
"discNumber": 1, "discNumber": 1,
"type": "music", "type": "music",
"year": 2021, "year": 2021,
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "tr-15", "id": "tr-15",
@@ -380,7 +394,8 @@
"discNumber": 1, "discNumber": 1,
"type": "music", "type": "music",
"year": 2021, "year": 2021,
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "tr-16", "id": "tr-16",
@@ -405,7 +420,8 @@
"discNumber": 1, "discNumber": 1,
"type": "music", "type": "music",
"year": 2021, "year": 2021,
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "tr-17", "id": "tr-17",
@@ -430,7 +446,8 @@
"discNumber": 1, "discNumber": 1,
"type": "music", "type": "music",
"year": 2021, "year": 2021,
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "tr-18", "id": "tr-18",
@@ -455,7 +472,8 @@
"discNumber": 1, "discNumber": 1,
"type": "music", "type": "music",
"year": 2021, "year": 2021,
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "tr-19", "id": "tr-19",
@@ -480,7 +498,8 @@
"discNumber": 1, "discNumber": 1,
"type": "music", "type": "music",
"year": 2021, "year": 2021,
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
}, },
{ {
"id": "tr-20", "id": "tr-20",
@@ -505,7 +524,8 @@
"discNumber": 1, "discNumber": 1,
"type": "music", "type": "music",
"year": 2021, "year": 2021,
"musicBrainzId": "" "musicBrainzId": "",
"replayGain": null
} }
] ]
} }

View File

@@ -24,9 +24,15 @@ type Info interface {
Genres() []string Genres() []string
TrackNumber() int TrackNumber() int
DiscNumber() int DiscNumber() int
Year() int
ReplayGainTrackGain() float32
ReplayGainTrackPeak() float32
ReplayGainAlbumGain() float32
ReplayGainAlbumPeak() float32
Length() int Length() int
Bitrate() int Bitrate() int
Year() int
} }
const ( const (

View File

@@ -51,8 +51,14 @@ func (i *info) Genres() []string { return find(i.raw, "genres") }
func (i *info) TrackNumber() int { return intSep("/", first(find(i.raw, "tracknumber"))) } // eg. 5/12 func (i *info) TrackNumber() int { return intSep("/", first(find(i.raw, "tracknumber"))) } // eg. 5/12
func (i *info) DiscNumber() int { return intSep("/", first(find(i.raw, "discnumber"))) } // eg. 1/2 func (i *info) DiscNumber() int { return intSep("/", first(find(i.raw, "discnumber"))) } // eg. 1/2
func (i *info) Year() int { return intSep("-", first(find(i.raw, "originaldate", "date", "year"))) } // eg. 2023-12-01 func (i *info) Year() int { return intSep("-", first(find(i.raw, "originaldate", "date", "year"))) } // eg. 2023-12-01
func (i *info) Length() int { return i.props.Length }
func (i *info) Bitrate() int { return i.props.Bitrate } func (i *info) ReplayGainTrackGain() float32 { return dB(first(find(i.raw, "replaygain_track_gain"))) }
func (i *info) ReplayGainTrackPeak() float32 { return flt(first(find(i.raw, "replaygain_track_peak"))) }
func (i *info) ReplayGainAlbumGain() float32 { return dB(first(find(i.raw, "replaygain_album_gain"))) }
func (i *info) ReplayGainAlbumPeak() float32 { return flt(first(find(i.raw, "replaygain_album_peak"))) }
func (i *info) Length() int { return i.props.Length }
func (i *info) Bitrate() int { return i.props.Bitrate }
func first[T comparable](is []T) T { func first[T comparable](is []T) T {
var z T var z T
@@ -83,6 +89,17 @@ func filterStr(ss []string) []string {
return r return r
} }
func flt(in string) float32 {
f, _ := strconv.ParseFloat(in, 32)
return float32(f)
}
func dB(in string) float32 {
in = strings.ToLower(in)
in = strings.TrimSuffix(in, " db")
in = strings.TrimSuffix(in, "db")
return flt(in)
}
func intSep(sep, in string) int { func intSep(sep, in string) int {
start, _, _ := strings.Cut(in, sep) start, _, _ := strings.Cut(in, sep)
out, _ := strconv.Atoi(start) out, _ := strconv.Atoi(start)