From 5881f3d538a9a4770330489b18fb126068d798c7 Mon Sep 17 00:00:00 2001 From: Dan Jones Date: Sat, 11 Nov 2023 17:01:01 -0600 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Get=20tags=20from=20mb?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/print.go | 121 ++++++++++++++++++++++++++++++++++++----- app/run.go | 6 +- media/brainz/brainz.go | 79 +++++++++++++++++++++++---- media/fingerprint.go | 3 +- media/tags/tags.go | 13 +++-- 5 files changed, 190 insertions(+), 32 deletions(-) diff --git a/app/print.go b/app/print.go index 25c7195..ac8b44f 100644 --- a/app/print.go +++ b/app/print.go @@ -2,17 +2,19 @@ package app import ( "fmt" - "strconv" + "codeberg.org/danjones000/strip-beats/input/boolean" "codeberg.org/danjones000/strip-beats/input/list" "codeberg.org/danjones000/strip-beats/media" "codeberg.org/danjones000/strip-beats/media/brainz" "codeberg.org/danjones000/strip-beats/utils" + "github.com/akrennmair/slice" ) type recOpt struct { - rec brainz.Recording - num int + rec brainz.Recording + r rune + score float64 } func (o recOpt) Title() string { @@ -21,7 +23,8 @@ func (o recOpt) Title() string { func (o recOpt) Text() string { return fmt.Sprintf( - "By %s - First Released %s - %s %s", + "(Score %.2f) By %s - First Released %s - %s %s", + 100*o.score, o.rec.FirstArtist().Name, o.rec.FirstReleaseDate, o.rec.FirstGenre().Name, @@ -29,11 +32,7 @@ func (o recOpt) Text() string { } func (o recOpt) Rune() rune { - s := strconv.Itoa(o.num) - for _, c := range s { - return c - } - return 0 + return o.r } func (o recOpt) Selected() func() { @@ -57,27 +56,123 @@ func print() { fmt.Println(rec.Title) tags.Title = rec.Title + tags.MusicbrainzRecordingId = rec.Id tags.Date = rec.FirstReleaseDate + tags.Genre = rec.FirstGenre().Name + tags.Artist = rec.FirstArtist().Name + tags.ArtistSort = rec.FirstArtist().SortName + tags.AcoustidId = rec.AcousticId - fmt.Printf("%+v\n", tags) + rel := findFirstRelease(rec) + if !boolean.Choose(fmt.Sprintf("Is %s album (%s %s) correct?", rel.Title, rel.Country, rel.Date)) { + rel = chooseRel(rec) + } + media := rel.Media[0] + track := media.Tracks[0] + full, err := brainz.GetReleaseWithMedia(rel.Id) + if err != nil { + panic(err) + } + albumArtist := full.ArtistCredit[0] + + tags.MusicbrainzReleaseGroupId = full.ReleaseGroup.Id + tags.MusicbrainzAlbumId = rel.Id + tags.MusicbrainzAlbumArtistId = albumArtist.Artist.Id + tags.AlbumArtist = albumArtist.Name + tags.Album = rel.Title + if rel.Date != "" { + tags.Date = rel.Date + } + tags.AlbumArtistSort = albumArtist.Artist.SortName + tags.ReleaseCountry = rel.Country + if len(full.LabelInfo) > 0 { + tags.Label = full.LabelInfo[0].Label.Name + tags.MusicBrainzLabelId = full.LabelInfo[0].Label.Id + } + if len(rel.Genres) > 0 && rel.Genres[0].Name != "" { + tags.Genre = rel.Genres[0].Name + } + tags.Disc = media.Position + tags.DiscCount = len(full.Media) + tags.Track = track.Position + tags.TrackCount = media.TrackCount } func chooseRec(ids media.IdResults) brainz.Recording { var recs []list.Option var rec brainz.Recording var err error - i := 1 + i := 'a' for _, res := range ids.Results { for _, rec = range res.Recordings { err = brainz.FillRecording(&rec) if err != nil { panic(err) } - recs = append(recs, recOpt{rec, i}) - i = (i + 1) % 10 + rec.AcousticId = res.Id + recs = append(recs, recOpt{rec, i, res.Score}) + if rec.Title != "" { + // Empty titles will be filtered out, so we don't need to increment them + i = i + 1 + } } } + recs = slice.Filter(recs, func(opt list.Option) bool { + return opt.Title() != "" + }) return list.List("Which recording is the correct one?", recs, nil).(recOpt).rec } + +func findFirstRelease(rec brainz.Recording) brainz.Release { + var rel brainz.Release + for _, rel = range rec.Releases { + if rel.Date == rec.FirstReleaseDate { + return rel + } + } + if len(rec.Releases) > 0 { + return rec.Releases[0] + } + return brainz.Release{} +} + +func chooseRel(rec brainz.Recording) brainz.Release { + var rels []list.Option + var rel brainz.Release + i := 'a' + for _, rel = range rec.Releases { + rels = append(rels, relOpt{rel, i}) + if rel.Title != "" { + // Empty titles will be filtered out, so we don't need to increment them + i = i + 1 + } + } + rels = slice.Filter(rels, func(opt list.Option) bool { + return opt.Title() != "" + }) + + return list.List("Which releases is the correct one?", rels, nil).(relOpt).rel +} + +type relOpt struct { + rel brainz.Release + r rune +} + +func (o relOpt) Title() string { + return o.rel.Title +} + +func (o relOpt) Text() string { + return fmt.Sprintf("%s %s", o.rel.Country, o.rel.Date) +} + +func (o relOpt) Rune() rune { + return o.r +} + +func (o relOpt) Selected() func() { + return nil +} diff --git a/app/run.go b/app/run.go index 409d974..4e62b52 100644 --- a/app/run.go +++ b/app/run.go @@ -52,14 +52,16 @@ func testMb() { func testPrint() { // SetFile("/home/drj/MyFiles/Videos/WebShows/YouTube/Dolly_Parton_-_Topic/Just_Because_I_m_a_Woman.Dolly_Parton_-_Topic.Fmv-XQerVkM.webm") - SetFile("/home/drj/MyFiles/Videos/WebShows/YouTube/Whitney_Houston/I_Will_Always_Love_You_Ultimate_Collection_Edit.Whitney_Houston.rB7z_l8mBxw.mp4") + // SetFile("/home/drj/MyFiles/Videos/WebShows/YouTube/Whitney_Houston/I_Will_Always_Love_You_Ultimate_Collection_Edit.Whitney_Houston.rB7z_l8mBxw.mp4") + SetFile("/home/drj/MyFiles/Music/Original_Broadway_Cast_of_Hamilton/Hamilton/d02t12-We_Know.m4a") + // SetFile("/home/drj/MyFiles/Videos/WebShows/YouTube/KaceyMusgravesVEVO/Kacey_Musgraves_-_Biscuits-nGIUtLO_x8g.mp4") + // SetFile("/home/drj/MyFiles/Videos/WebShows/YouTube/Willie_Nelson_-_Topic/Too_Sick_To_Pray.Willie_Nelson_-_Topic.8QgBXo41j2E.webm") print() quit() } func Run(step AppStep) { - testPrint() for step < Quit { switch step { case Pick: diff --git a/media/brainz/brainz.go b/media/brainz/brainz.go index 30553d7..47bd4c9 100644 --- a/media/brainz/brainz.go +++ b/media/brainz/brainz.go @@ -12,6 +12,7 @@ import ( type Recording struct { Id uuid.UUID + AcousticId uuid.UUID Isrcs []string FirstReleaseDate string `json:"first-release-date"` Length int @@ -66,21 +67,57 @@ type Genre struct { } type Release struct { - Id uuid.UUID - Country string - Date string - Media []Media - Status string - StatusId uuid.UUID `json:"status-id"` - ArtistCredit []ArtistCredit `json:"artist-credit"` - Title string - Genres []Genre + Id uuid.UUID + Asin string + Barcode string + Country string + Date string + Disambiguation string + Media []Media + Packaging string + PackagingId uuid.UUID `json:"packaging-id"` + Quality string + Status string + StatusId uuid.UUID `json:"status-id"` + ArtistCredit []ArtistCredit `json:"artist-credit"` + Title string + Genres []Genre + ReleaseGroup ReleaseGroup `json:"release-group"` + LabelInfo []LabelInfo `json:"label-info"` // ReleaseEvents []ReleaseEvent `json:"release-events"` } +type LabelInfo struct { + CatalogNumber string `json:"catalog-number"` + Label Label +} + +type Label struct { + Id uuid.UUID + Name string + SortName string `json:"sort-name"` + Disambiguation string + TypeId string `json:"type-id"` + Type string + LabelCode int `json:"label-code"` +} + +type ReleaseGroup struct { + Id uuid.UUID + Title string + ArtistCredit []ArtistCredit `json:"artist-credit"` + Disambiguation string + FirstReleaseDate string `json:"first-release-date"` + PrimaryType string `json:"primary-type"` + PrimaryTypeId uuid.UUID `json:"primary-type-id"` + SecondaryTypes []string `json:"secondary-types"` + SecondaryTypeIds []uuid.UUID `json:"secondary-type-ids"` +} + type ArtistCredit struct { - Name string - Artist Artist + Name string + Artist Artist + JoinPhrase string } type Artist struct { @@ -95,6 +132,7 @@ type Artist struct { type Media struct { FormatId uuid.UUID `json:"format-id"` Position int + Title string TrackOffset int `json:"track-offset"` Format string TrackCount int `json:"track-count"` @@ -109,6 +147,25 @@ type Track struct { Length int } +func GetReleaseWithMedia(id uuid.UUID) (Release, error) { + rel := Release{Id: id} + url := fmt.Sprintf("https://musicbrainz.org/ws/2/release/%s", id) + resp, err := h.GetWithQuery(url, u.Values{ + "fmt": []string{"json"}, + "inc": []string{"artist-credits+discids+labels+release-groups"}}) + if err != nil { + return rel, err + } + defer resp.Body.Close() + body, err := io.ReadAll(resp.Body) + if err != nil { + return rel, err + } + err = json.Unmarshal(body, &rel) + + return rel, err +} + func GetRecording(id string) (Recording, error) { u, err := uuid.Parse(id) rec := Recording{Id: u} diff --git a/media/fingerprint.go b/media/fingerprint.go index 2b4d1ad..5952bad 100644 --- a/media/fingerprint.go +++ b/media/fingerprint.go @@ -12,6 +12,7 @@ import ( "codeberg.org/danjones000/strip-beats/config" "codeberg.org/danjones000/strip-beats/media/brainz" h "codeberg.org/danjones000/strip-beats/utils/http" + "github.com/google/uuid" ) type FPrint struct { @@ -45,7 +46,7 @@ type IdResults struct { } type IdResult struct { - Id string + Id uuid.UUID Score float64 Recordings []brainz.Recording } diff --git a/media/tags/tags.go b/media/tags/tags.go index fc0f020..eeb13fd 100644 --- a/media/tags/tags.go +++ b/media/tags/tags.go @@ -5,13 +5,11 @@ import ( ) type Tags struct { - MajorBrand string `json:"major_brand"` Title string Artist string AlbumArtist string `json:"album_artist"` Album string Date string - Encoder string Comment string Description string Synopsis string @@ -21,12 +19,17 @@ type Tags struct { MusicbrainzAlbumId uuid.UUID `json:"MUSICBRAINZ_ALBUMID"` MusicbrainzAlbumArtistId uuid.UUID `json:"MUSICBRAINZ_ALBUMARTISTID"` MusicbrainzRecordingId uuid.UUID + MusicBrainzLabelId uuid.UUID + ArtistSort string AlbumArtistSort string ReleaseCountry string Label string - ReleaseType string Composer string Genre string - Disc string - Track string + Disc int `json:"-"` + DiscCount int + Track int `json:"-"` + TrackCount int + TrackStr string `json:"track"` + DiscStr string `json:"disc"` }