mirror of
https://github.com/navidrome/navidrome.git
synced 2025-04-03 20:47:35 +03:00
Support for Original Date, Release Date & splitting/grouping of album editions (#2162)
* Update AlbumGridView.js * Update AlbumDetails.js * Update AlbumDetails.js * Create DoubleRangeField.js * Update and rename DoubleRangeField.js to RangeFieldDouble.js * Update RangeFieldDouble.js * Update AlbumGridView.js * Update AlbumDetails.js * Update RangeFieldDouble.js * Update index.js * Update RangeFieldDouble.js * Update RangeFieldDouble.js * Update RangeFieldDouble.js * Update RangeFieldDouble.js * Update RangeFieldDouble.js * Update AlbumDetails.js * Update RangeFieldDouble.js * Update AlbumDetails.js * Update RangeFieldDouble.js * Update AlbumDetails.js * Update RangeFieldDouble.js * Update RangeFieldDouble.js * Update AlbumDetails.js * Update AlbumDetails.js * Update RangeFieldDouble.js * Update RangeFieldDouble.js * Update AlbumDetails.js * Update RangeFieldDouble.js * Update AlbumDetails.js * Update en.json * Update en.json * Update AlbumDetails.js * Update RangeFieldDouble.js * Update AlbumGridView.js * Update AlbumDetails.js * Update AlbumSongs.js * Update ContextMenus.js * Update SongDatagrid.js * Update AlbumSongs.js * Update SongDatagrid.js * Update SongDatagrid.js * Update SongDatagrid.js * Update AlbumSongs.js * Update SongList.js * Update playlist_track_repository.go * Update 20230113000000_release_year.go * Update PlayButton.js * Update mediafile_repository.go * Update album.go * Update playlist_track_repository.go * Update playlist_track_repository.go * Update SongDatagrid.js * Update 20230113000000_release_year.go * Update SongDatagrid.js * Update AlbumSongs.js * Update SongDatagrid.js * Update SongDatagrid.js * Update SongDatagrid.js * Update SongDatagrid.js * Update AlbumDetails.js * Update AlbumSongs.js * Update AlbumSongs.js * Update RangeFieldDouble.js * Update SongDatagrid.js * Update 20230113000000_release_year.go * Update 20230113000000_release_year.go * Update 20230113000000_release_year.go * Update 20230113000000_release_year.go * Update AlbumSongs.js * Update AlbumSongs.js * Update mapping.go * Update RangeFieldDouble.js * Update AlbumGridView.js * Update AlbumSongs.js * Update en.json * Update SongDatagrid.js * Update SongDatagrid.js * Update metadata.go * Update mapping.go * Update AlbumDetails.js * Update AlbumGridView.js * Update RangeFieldDouble.js * Update mapping.go * Update metadata.go * Update mapping.go * Update AlbumDetails.js * Update 20230113000000_release_year.go * Update AlbumDetails.js * Update en.json * Update configuration.go * Update mapping.go * Update configuration.go * Update mediafile.go * Update metadata.go * Update RangeFieldDouble.js * Update 20230113000000_release_year.go * Update configuration.go * Update mapping.go * Update mediafile.go * Update mapping.go * Update RangeFieldDouble.js * Update RangeFieldDouble.js * Update RangeFieldDouble.js * Update RangeFieldDouble.js * Update RangeFieldDouble.js * Update 20230113000000_release_year.go * Update AlbumDetails.js * Update RangeFieldDouble.js * Update mapping.go * Update metadata.go * Update album.go * Update mediafile.go * Update mediafile.go * Update album.go * Update fields.go * Update mediafile_repository.go * Update playlist_track_repository.go * Update AlbumSongs.js * Update SongDatagrid.js * Update PlayButton.js * Update SongList.js * Update ContextMenus.js * Update SongDatagrid.js * Update metadata.go * Update ArtistShow.js * Update mapping.go * Update configuration.go * Update mapping.go * Update metadata.go * Update metadata.go * Update mapping.go * Update metadata.go * Update metadata.go * Update mapping.go * Update 20230113000000_release_year.go * Update 20230113000000_release_year.go * Update mapping.go * Update metadata.go * Update metadata.go * Update album.go * Update mediafile.go * Update AlbumDetails.js * Update AlbumSongs.js * Update album.go * Update mediafile.go * Update metadata.go * Update mediafile.go * Update 20230113000000_release_year.go * Update 20230113000000_release_year.go * Update album.go * Update mediafile.go * Update RangeFieldDouble.js * Update AlbumDetails.js * Update AlbumGridView.js * Update en.json * Update AlbumGridView.js * Update RangeFieldDouble.js * Update and rename 20230113000000_release_year.go to 20230113000000_release_date.go * Update album.go * Update mediafile.go * Update fields.go * Update playlist_track_repository.go * Update mediafile_repository.go * Update mapping.go * Update metadata.go * Update mapping.go * Update SongDatagrid.js * Update RangeFieldDouble.js * Update index.js * Update ContextMenus.js * Update PlayButton.js * Create FormatDate.js * Update SongList.js * Update AlbumDetails.js * Update AlbumSongs.js * Update AlbumSongs.js * Update en.json * Update AlbumDetails.js * Update album.go fixed conflict I think? * Update mediafile.go fixed conflict * Format with goimports * Update SongDatagrid.js only show Cat # in desktop view * Update metadata_internal_test.go * Update metadata_test.go * Delete test.mp3 * Add files via upload mp3 test file with Date, Original Date and Release Date * Update metadata_test.go * Update metadata_test.go * Update metadata_test.go * Update metadata_test.go * Update taglib_test.go * Delete test.mp3 * Add files via upload file with replaygain & dates * Update AlbumGridView.js * Update AlbumDetails.js * Update AlbumSongs.js * Update ContextMenus.js * Update FormatDate.js * Update PlayButton.js * Update RangeFieldDouble.js * Update SongDatagrid.js * Update AlbumSongs.js * Update SongDatagrid.js * Update AlbumSongs.js * Fix formatting * Update mapping.go * Update AlbumSongs.js * Update SongDatagrid.js * Update SongDatagrid.js prettier * Create RangeDoubleField.js rename of RangeFieldDouble.js * Update AlbumGridView.js RangeFieldDouble -> RangeDoubleField * Update mediafile.go AllOrNothing() -> allOrNothing() * Update metadata_internal_test.go getYear -> getDate * Update AlbumDetails.js wrote suggested changes * Update en.json Editions -> Releases & fixed the field name * Update configuration.go Rename Editions -> Releases * Update 20230113000000_release_date.go Editions -> Releases * Update album.go Editions -> Releases * Update mediafile.go Editions -> Releases * Update AlbumDetails.js Editions -> Releases * Update AlbumSongs.js Editions -> Releases * Update RangeDoubleField.js Editions -> Releases * Update SongDatagrid.js Editions -> Releases * Update index.js FormatFullDate and RangeDoubleField * Rename FormatDate.js to FormatFullDate.js * Delete RangeFieldDouble.js * Update mediafile.go AllOrNothing -> allOrNothing * Update mapping.go Editions -> Releases * Update AlbumDetails.js prettier * Update SongDatagrid.js showReleaseRow -> showReleaseDivider * Update AlbumSongs.js showReleaseRow -> showReleaseDivider for clarity * Update and rename 20230113000000_release_date.go to 20230515184510_add_release_date.go - rename the migration file - fixed the import to goose/v3 - additional db fields for original date & year * Update 20230515184510_add_release_date.go * Update fields.go * Update album.go * Update mediafile.go * Update mapping.go * Update AlbumDetails.js * Update en.json * Update AlbumDetails.js * Update AlbumDetails.js now hopefully prettier * Update mapping.go --------- Co-authored-by: Deluan <deluan@navidrome.org>
This commit is contained in:
parent
010ba0d15c
commit
52b77e4194
24 changed files with 511 additions and 78 deletions
|
@ -32,9 +32,10 @@ func newMediaFileMapper(rootFolder string, genres model.GenreRepository) *mediaF
|
|||
func (s mediaFileMapper) toMediaFile(md metadata.Tags) model.MediaFile {
|
||||
mf := &model.MediaFile{}
|
||||
mf.ID = s.trackID(md)
|
||||
mf.Year, mf.Date, mf.OriginalYear, mf.OriginalDate, mf.ReleaseYear, mf.ReleaseDate = s.mapDates(md)
|
||||
mf.Title = s.mapTrackTitle(md)
|
||||
mf.Album = md.Album()
|
||||
mf.AlbumID = s.albumID(md)
|
||||
mf.AlbumID = s.albumID(md, mf.ReleaseDate)
|
||||
mf.Album = s.mapAlbumName(md)
|
||||
mf.ArtistID = s.artistID(md)
|
||||
mf.Artist = s.mapArtistName(md)
|
||||
|
@ -42,7 +43,6 @@ func (s mediaFileMapper) toMediaFile(md metadata.Tags) model.MediaFile {
|
|||
mf.AlbumArtist = s.mapAlbumArtistName(md)
|
||||
mf.Genre, mf.Genres = s.mapGenres(md.Genres())
|
||||
mf.Compilation = md.Compilation()
|
||||
mf.Year = md.Year()
|
||||
mf.TrackNumber, _ = md.TrackNumber()
|
||||
mf.DiscNumber, _ = md.DiscNumber()
|
||||
mf.DiscSubtitle = md.DiscSubtitle()
|
||||
|
@ -128,8 +128,13 @@ func (s mediaFileMapper) trackID(md metadata.Tags) string {
|
|||
return fmt.Sprintf("%x", md5.Sum([]byte(md.FilePath())))
|
||||
}
|
||||
|
||||
func (s mediaFileMapper) albumID(md metadata.Tags) string {
|
||||
func (s mediaFileMapper) albumID(md metadata.Tags, releaseDate string) string {
|
||||
albumPath := strings.ToLower(fmt.Sprintf("%s\\%s", s.mapAlbumArtistName(md), s.mapAlbumName(md)))
|
||||
if !conf.Server.Scanner.GroupAlbumReleases {
|
||||
if len(releaseDate) != 0 {
|
||||
albumPath = fmt.Sprintf("%s\\%s", albumPath, releaseDate)
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("%x", md5.Sum([]byte(albumPath)))
|
||||
}
|
||||
|
||||
|
@ -169,3 +174,18 @@ func (s mediaFileMapper) mapGenres(genres []string) (string, model.Genres) {
|
|||
}
|
||||
return result[0].Name, result
|
||||
}
|
||||
|
||||
func (s mediaFileMapper) mapDates(md metadata.Tags) (int, string, int, string, int, string) {
|
||||
year, date := md.Date()
|
||||
originalYear, originalDate := md.OriginalDate()
|
||||
releaseYear, releaseDate := md.ReleaseDate()
|
||||
|
||||
// MusicBrainz Picard writes the Release Date of an album to the Date tag, and leaves the Release Date tag empty
|
||||
taggedLikePicard := (originalYear != 0) &&
|
||||
(releaseYear == 0) &&
|
||||
(year >= originalYear)
|
||||
if taggedLikePicard {
|
||||
return originalYear, originalDate, originalYear, originalDate, year, date
|
||||
}
|
||||
return year, date, originalYear, originalDate, releaseYear, releaseDate
|
||||
}
|
||||
|
|
|
@ -94,17 +94,19 @@ func (t Tags) Artist() string { return t.getFirstTagValue("artist", "sort_artist
|
|||
func (t Tags) AlbumArtist() string {
|
||||
return t.getFirstTagValue("album_artist", "album artist", "albumartist")
|
||||
}
|
||||
func (t Tags) SortTitle() string { return t.getSortTag("", "title", "name") }
|
||||
func (t Tags) SortAlbum() string { return t.getSortTag("", "album") }
|
||||
func (t Tags) SortArtist() string { return t.getSortTag("", "artist") }
|
||||
func (t Tags) SortAlbumArtist() string { return t.getSortTag("tso2", "albumartist", "album_artist") }
|
||||
func (t Tags) Genres() []string { return t.getAllTagValues("genre") }
|
||||
func (t Tags) Year() int { return t.getYear("date") }
|
||||
func (t Tags) Comment() string { return t.getFirstTagValue("comment") }
|
||||
func (t Tags) Lyrics() string { return t.getFirstTagValue("lyrics", "lyrics-eng") }
|
||||
func (t Tags) Compilation() bool { return t.getBool("tcmp", "compilation") }
|
||||
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) SortTitle() string { return t.getSortTag("", "title", "name") }
|
||||
func (t Tags) SortAlbum() string { return t.getSortTag("", "album") }
|
||||
func (t Tags) SortArtist() string { return t.getSortTag("", "artist") }
|
||||
func (t Tags) SortAlbumArtist() string { return t.getSortTag("tso2", "albumartist", "album_artist") }
|
||||
func (t Tags) Genres() []string { return t.getAllTagValues("genre") }
|
||||
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) Lyrics() string { return t.getFirstTagValue("lyrics", "lyrics-eng") }
|
||||
func (t Tags) Compilation() bool { return t.getBool("tcmp", "compilation") }
|
||||
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")
|
||||
}
|
||||
|
@ -217,18 +219,38 @@ func (t Tags) getSortTag(originalTag string, tagNames ...string) string {
|
|||
|
||||
var dateRegex = regexp.MustCompile(`([12]\d\d\d)`)
|
||||
|
||||
func (t Tags) getYear(tagNames ...string) int {
|
||||
func (t Tags) getDate(tagNames ...string) (int, string) {
|
||||
tag := t.getFirstTagValue(tagNames...)
|
||||
if tag == "" {
|
||||
return 0
|
||||
if len(tag) < 4 {
|
||||
return 0, ""
|
||||
}
|
||||
// first get just the year
|
||||
match := dateRegex.FindStringSubmatch(tag)
|
||||
if len(match) == 0 {
|
||||
log.Warn("Error parsing year date field", "file", t.filePath, "date", tag)
|
||||
return 0
|
||||
log.Warn("Error parsing "+tagNames[0]+" field for year", "file", t.filePath, "date", tag)
|
||||
return 0, ""
|
||||
}
|
||||
year, _ := strconv.Atoi(match[1])
|
||||
return year
|
||||
|
||||
if len(tag) < 5 {
|
||||
return year, match[1]
|
||||
}
|
||||
|
||||
//then try YYYY-MM-DD
|
||||
if len(tag) > 10 {
|
||||
tag = tag[:10]
|
||||
}
|
||||
layout := "2006-01-02"
|
||||
_, err := time.Parse(layout, tag)
|
||||
if err != nil {
|
||||
layout = "2006-01"
|
||||
_, err = time.Parse(layout, tag)
|
||||
if err != nil {
|
||||
log.Warn("Error parsing "+tagNames[0]+" field for month + day", "file", t.filePath, "date", tag)
|
||||
return year, match[1]
|
||||
}
|
||||
}
|
||||
return year, tag
|
||||
}
|
||||
|
||||
func (t Tags) getBool(tagNames ...string) bool {
|
||||
|
|
|
@ -6,29 +6,51 @@ import (
|
|||
)
|
||||
|
||||
var _ = Describe("Tags", func() {
|
||||
Describe("getYear", func() {
|
||||
Describe("getDate", func() {
|
||||
It("parses the year correctly", func() {
|
||||
var examples = map[string]int{
|
||||
var examplesYear = map[string]int{
|
||||
"1985": 1985,
|
||||
"2002-01": 2002,
|
||||
"1969.06": 1969,
|
||||
"1980.07.25": 1980,
|
||||
"2004-00-00": 2004,
|
||||
"2016-12-31": 2016,
|
||||
"2013-May-12": 2013,
|
||||
"May 12, 2016": 2016,
|
||||
"01/10/1990": 1990,
|
||||
}
|
||||
for tag, expected := range examples {
|
||||
for tag, expected := range examplesYear {
|
||||
md := &Tags{}
|
||||
md.tags = map[string][]string{"date": {tag}}
|
||||
Expect(md.Year()).To(Equal(expected))
|
||||
testYear, _ := md.Date()
|
||||
Expect(testYear).To(Equal(expected))
|
||||
}
|
||||
})
|
||||
|
||||
It("parses the date correctly", func() {
|
||||
var examplesDate = map[string]string{
|
||||
"1985": "1985",
|
||||
"2002-01": "2002-01",
|
||||
"1969.06": "1969",
|
||||
"1980.07.25": "1980",
|
||||
"2004-00-00": "2004",
|
||||
"2016-12-31": "2016-12-31",
|
||||
"2013-May-12": "2013",
|
||||
"May 12, 2016": "2016",
|
||||
"01/10/1990": "1990",
|
||||
}
|
||||
for tag, expected := range examplesDate {
|
||||
md := &Tags{}
|
||||
md.tags = map[string][]string{"date": {tag}}
|
||||
_, testDate := md.Date()
|
||||
Expect(testDate).To(Equal(expected))
|
||||
}
|
||||
})
|
||||
It("returns 0 if year is invalid", func() {
|
||||
md := &Tags{}
|
||||
md.tags = map[string][]string{"date": {"invalid"}}
|
||||
Expect(md.Year()).To(Equal(0))
|
||||
testYear, _ := md.Date()
|
||||
Expect(testYear).To(Equal(0))
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
@ -27,7 +27,15 @@ var _ = Describe("Tags", func() {
|
|||
Expect(m.AlbumArtist()).To(Equal("Album Artist"))
|
||||
Expect(m.Compilation()).To(BeTrue())
|
||||
Expect(m.Genres()).To(Equal([]string{"Rock"}))
|
||||
Expect(m.Year()).To(Equal(2014))
|
||||
y, d := m.Date()
|
||||
Expect(y).To(Equal(2014))
|
||||
Expect(d).To(Equal("2014-05-21"))
|
||||
y, d = m.OriginalDate()
|
||||
Expect(y).To(Equal(1996))
|
||||
Expect(d).To(Equal("1996-11-21"))
|
||||
y, d = m.ReleaseDate()
|
||||
Expect(y).To(Equal(2020))
|
||||
Expect(d).To(Equal("2020-12-31"))
|
||||
n, t := m.TrackNumber()
|
||||
Expect(n).To(Equal(2))
|
||||
Expect(t).To(Equal(10))
|
||||
|
|
|
@ -42,6 +42,8 @@ var _ = Describe("Extractor", func() {
|
|||
Expect(m).To(HaveKeyWithValue("tcmp", []string{"1"})) // Compilation
|
||||
Expect(m).To(HaveKeyWithValue("genre", []string{"Rock"}))
|
||||
Expect(m).To(HaveKeyWithValue("date", []string{"2014-05-21", "2014"}))
|
||||
Expect(m).To(HaveKeyWithValue("originaldate", []string{"1996-11-21"}))
|
||||
Expect(m).To(HaveKeyWithValue("releasedate", []string{"2020-12-31"}))
|
||||
Expect(m).To(HaveKeyWithValue("discnumber", []string{"1/2"}))
|
||||
Expect(m).To(HaveKeyWithValue("has_picture", []string{"true"}))
|
||||
Expect(m).To(HaveKeyWithValue("duration", []string{"1.02"}))
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue