From f3bb022238e7d94968f45cf49ecc340551f316af Mon Sep 17 00:00:00 2001 From: Deluan Date: Sat, 11 May 2024 17:44:05 -0400 Subject: [PATCH] Add sampleRate to the DB --- .../20240511210036_add_sample_rate.go | 29 +++++ model/mediafile.go | 1 + scanner/mapping.go | 1 + scanner/metadata/metadata.go | 113 +++++++++--------- scanner/metadata/metadata_test.go | 3 + 5 files changed, 91 insertions(+), 56 deletions(-) create mode 100644 db/migrations/20240511210036_add_sample_rate.go diff --git a/db/migrations/20240511210036_add_sample_rate.go b/db/migrations/20240511210036_add_sample_rate.go new file mode 100644 index 00000000..619cdcff --- /dev/null +++ b/db/migrations/20240511210036_add_sample_rate.go @@ -0,0 +1,29 @@ +package migrations + +import ( + "context" + "database/sql" + + "github.com/pressly/goose/v3" +) + +func init() { + goose.AddMigrationContext(upAddSampleRate, downAddSampleRate) +} + +func upAddSampleRate(ctx context.Context, tx *sql.Tx) error { + _, err := tx.ExecContext(ctx, ` +alter table media_file + add sample_rate integer not null default 0; + +create index if not exists media_file_sample_rate + on media_file (sample_rate); +`) + notice(tx, "A full rescan should be performed to pick up additional tags") + return err +} + +func downAddSampleRate(ctx context.Context, tx *sql.Tx) error { + _, err := tx.ExecContext(ctx, `alter table media_file drop sample_rate;`) + return err +} diff --git a/model/mediafile.go b/model/mediafile.go index fc8793ca..79a3bc0d 100644 --- a/model/mediafile.go +++ b/model/mediafile.go @@ -43,6 +43,7 @@ type MediaFile struct { Suffix string `structs:"suffix" json:"suffix"` Duration float32 `structs:"duration" json:"duration"` BitRate int `structs:"bit_rate" json:"bitRate"` + SampleRate int `structs:"sample_rate" json:"sampleRate"` Channels int `structs:"channels" json:"channels"` Genre string `structs:"genre" json:"genre"` Genres Genres `structs:"-" json:"genres"` diff --git a/scanner/mapping.go b/scanner/mapping.go index 3e56f3bc..a67823a9 100644 --- a/scanner/mapping.go +++ b/scanner/mapping.go @@ -46,6 +46,7 @@ func (s MediaFileMapper) ToMediaFile(md metadata.Tags) model.MediaFile { mf.DiscSubtitle = md.DiscSubtitle() mf.Duration = md.Duration() mf.BitRate = md.BitRate() + mf.SampleRate = md.SampleRate() mf.Channels = md.Channels() mf.Path = md.FilePath() mf.Suffix = md.Suffix() diff --git a/scanner/metadata/metadata.go b/scanner/metadata/metadata.go index 568ee044..2d4315f9 100644 --- a/scanner/metadata/metadata.go +++ b/scanner/metadata/metadata.go @@ -139,6 +139,63 @@ func (t Tags) Date() (int, string) { return t.getDate("date") } func (t Tags) OriginalDate() (int, string) { return t.getDate("originaldate") } func (t Tags) ReleaseDate() (int, string) { return t.getDate("releasedate") } func (t Tags) Comment() string { return t.getFirstTagValue("comment") } +func (t Tags) Compilation() bool { return t.getBool("tcmp", "compilation", "wm/iscompilation") } +func (t Tags) TrackNumber() (int, int) { return t.getTuple("track", "tracknumber") } +func (t Tags) DiscNumber() (int, int) { return t.getTuple("disc", "discnumber") } +func (t Tags) DiscSubtitle() string { + return t.getFirstTagValue("tsst", "discsubtitle", "setsubtitle") +} +func (t Tags) CatalogNum() string { return t.getFirstTagValue("catalognumber") } +func (t Tags) Bpm() int { return (int)(math.Round(t.getFloat("tbpm", "bpm", "fbpm"))) } +func (t Tags) HasPicture() bool { return t.getFirstTagValue("has_picture") != "" } + +// MusicBrainz Identifiers + +func (t Tags) MbzReleaseTrackID() string { + return t.getMbzID("musicbrainz_releasetrackid", "musicbrainz release track id") +} + +func (t Tags) MbzRecordingID() string { + return t.getMbzID("musicbrainz_trackid", "musicbrainz track id") +} +func (t Tags) MbzAlbumID() string { return t.getMbzID("musicbrainz_albumid", "musicbrainz album id") } +func (t Tags) MbzArtistID() string { + return t.getMbzID("musicbrainz_artistid", "musicbrainz artist id") +} +func (t Tags) MbzAlbumArtistID() string { + return t.getMbzID("musicbrainz_albumartistid", "musicbrainz album artist id") +} +func (t Tags) MbzAlbumType() string { + return t.getFirstTagValue("musicbrainz_albumtype", "musicbrainz album type") +} +func (t Tags) MbzAlbumComment() string { + return t.getFirstTagValue("musicbrainz_albumcomment", "musicbrainz album comment") +} + +// ReplayGain Properties + +func (t Tags) RGAlbumGain() float64 { return t.getGainValue("replaygain_album_gain") } +func (t Tags) RGAlbumPeak() float64 { return t.getPeakValue("replaygain_album_peak") } +func (t Tags) RGTrackGain() float64 { return t.getGainValue("replaygain_track_gain") } +func (t Tags) RGTrackPeak() float64 { return t.getPeakValue("replaygain_track_peak") } + +// File properties + +func (t Tags) Duration() float32 { return float32(t.getFloat("duration")) } +func (t Tags) SampleRate() int { return t.getInt("samplerate") } +func (t Tags) BitRate() int { return t.getInt("bitrate") } +func (t Tags) Channels() int { return t.getInt("channels") } +func (t Tags) ModificationTime() time.Time { return t.fileInfo.ModTime() } +func (t Tags) Size() int64 { return t.fileInfo.Size() } +func (t Tags) FilePath() string { return t.filePath } +func (t Tags) Suffix() string { return strings.ToLower(strings.TrimPrefix(path.Ext(t.filePath), ".")) } +func (t Tags) BirthTime() time.Time { + if ts := times.Get(t.fileInfo); ts.HasBirthTime() { + return ts.BirthTime() + } + return time.Now() +} + func (t Tags) Lyrics() string { lyricList := model.LyricList{} basicLyrics := t.getAllTagValues("lyrics", "unsynced_lyrics", "unsynced lyrics", "unsyncedlyrics") @@ -181,62 +238,6 @@ func (t Tags) Lyrics() string { return string(res) } -func (t Tags) Compilation() bool { return t.getBool("tcmp", "compilation", "wm/iscompilation") } -func (t Tags) TrackNumber() (int, int) { return t.getTuple("track", "tracknumber") } -func (t Tags) DiscNumber() (int, int) { return t.getTuple("disc", "discnumber") } -func (t Tags) DiscSubtitle() string { - return t.getFirstTagValue("tsst", "discsubtitle", "setsubtitle") -} -func (t Tags) CatalogNum() string { return t.getFirstTagValue("catalognumber") } -func (t Tags) Bpm() int { return (int)(math.Round(t.getFloat("tbpm", "bpm", "fbpm"))) } -func (t Tags) HasPicture() bool { return t.getFirstTagValue("has_picture") != "" } - -// MusicBrainz Identifiers - -func (t Tags) MbzReleaseTrackID() string { - return t.getMbzID("musicbrainz_releasetrackid", "musicbrainz release track id") -} - -func (t Tags) MbzRecordingID() string { - return t.getMbzID("musicbrainz_trackid", "musicbrainz track id") -} -func (t Tags) MbzAlbumID() string { return t.getMbzID("musicbrainz_albumid", "musicbrainz album id") } -func (t Tags) MbzArtistID() string { - return t.getMbzID("musicbrainz_artistid", "musicbrainz artist id") -} -func (t Tags) MbzAlbumArtistID() string { - return t.getMbzID("musicbrainz_albumartistid", "musicbrainz album artist id") -} -func (t Tags) MbzAlbumType() string { - return t.getFirstTagValue("musicbrainz_albumtype", "musicbrainz album type") -} -func (t Tags) MbzAlbumComment() string { - return t.getFirstTagValue("musicbrainz_albumcomment", "musicbrainz album comment") -} - -// File properties - -func (t Tags) Duration() float32 { return float32(t.getFloat("duration")) } -func (t Tags) BitRate() int { return t.getInt("bitrate") } -func (t Tags) Channels() int { return t.getInt("channels") } -func (t Tags) ModificationTime() time.Time { return t.fileInfo.ModTime() } -func (t Tags) Size() int64 { return t.fileInfo.Size() } -func (t Tags) FilePath() string { return t.filePath } -func (t Tags) Suffix() string { return strings.ToLower(strings.TrimPrefix(path.Ext(t.filePath), ".")) } -func (t Tags) BirthTime() time.Time { - if ts := times.Get(t.fileInfo); ts.HasBirthTime() { - return ts.BirthTime() - } - return time.Now() -} - -// ReplayGain Properties - -func (t Tags) RGAlbumGain() float64 { return t.getGainValue("replaygain_album_gain") } -func (t Tags) RGAlbumPeak() float64 { return t.getPeakValue("replaygain_album_peak") } -func (t Tags) RGTrackGain() float64 { return t.getGainValue("replaygain_track_gain") } -func (t Tags) RGTrackPeak() float64 { return t.getPeakValue("replaygain_track_peak") } - func (t Tags) getGainValue(tagName string) float64 { // Gain is in the form [-]a.bb dB var tag = t.getFirstTagValue(tagName) diff --git a/scanner/metadata/metadata_test.go b/scanner/metadata/metadata_test.go index 3c0279e1..bc1e572c 100644 --- a/scanner/metadata/metadata_test.go +++ b/scanner/metadata/metadata_test.go @@ -94,6 +94,7 @@ var _ = Describe("Tags", func() { Expect(m.Duration()).To(BeNumerically("~", 1.02, 0.01)) Expect(m.BitRate()).To(Equal(192)) Expect(m.Channels()).To(Equal(2)) + Expect(m.SampleRate()).To(Equal(44100)) Expect(m.FilePath()).To(Equal("tests/fixtures/test.mp3")) Expect(m.Suffix()).To(Equal("mp3")) Expect(m.Size()).To(Equal(int64(51876))) @@ -113,6 +114,7 @@ var _ = Describe("Tags", func() { // TabLib 1.12 returns 18, previous versions return 39. // See https://github.com/taglib/taglib/commit/2f238921824741b2cfe6fbfbfc9701d9827ab06b Expect(m.BitRate()).To(BeElementOf(18, 39, 40, 43, 49)) + Expect(m.SampleRate()).To(Equal(8000)) m = mds["tests/fixtures/test.wma"] Expect(err).To(BeNil()) @@ -124,6 +126,7 @@ var _ = Describe("Tags", func() { Expect(m.FilePath()).To(Equal("tests/fixtures/test.wma")) Expect(m.Size()).To(Equal(int64(21581))) Expect(m.BitRate()).To(BeElementOf(128)) + Expect(m.SampleRate()).To(Equal(44100)) }) DescribeTable("Lyrics test",