@@ -43,10 +43,11 @@ type Scanner struct {
4343 tagReader tags.Reader
4444 excludePattern * regexp.Regexp
4545 scanEmbeddedCover bool
46+ genreTree map [string ][]string
4647 scanning * int32
4748}
4849
49- func New (musicDirs []string , db * db.DB , multiValueSettings map [Tag ]MultiValueSetting , tagReader tags.Reader , excludePattern string , scanEmbeddedCover bool ) * Scanner {
50+ func New (musicDirs []string , db * db.DB , multiValueSettings map [Tag ]MultiValueSetting , tagReader tags.Reader , excludePattern string , scanEmbeddedCover bool , genreTree map [ string ][] string ) * Scanner {
5051 var excludePatternRegExp * regexp.Regexp
5152 if excludePattern != "" {
5253 excludePatternRegExp = regexp .MustCompile (excludePattern )
@@ -59,6 +60,7 @@ func New(musicDirs []string, db *db.DB, multiValueSettings map[Tag]MultiValueSet
5960 tagReader : tagReader ,
6061 excludePattern : excludePatternRegExp ,
6162 scanEmbeddedCover : scanEmbeddedCover ,
63+ genreTree : genreTree ,
6264 scanning : new (int32 ),
6365 }
6466}
@@ -405,6 +407,30 @@ func (s *Scanner) populateTrackAndArtists(tx *db.DB, st *State, i int, album *db
405407 return fmt .Errorf ("populate genres: %w" , err )
406408 }
407409
410+ var inheritedGenreIDs []int
411+ if len (s .genreTree ) > 0 {
412+ direct := map [string ]struct {}{}
413+ for _ , name := range genreNames {
414+ direct [name ] = struct {}{}
415+ }
416+ var inheritedNames []string
417+ for parent , descendants := range s .genreTree {
418+ if _ , ok := direct [parent ]; ok {
419+ continue
420+ }
421+ for _ , desc := range descendants {
422+ if _ , ok := direct [desc ]; ok {
423+ inheritedNames = append (inheritedNames , parent )
424+ break
425+ }
426+ }
427+ }
428+ inheritedGenreIDs , err = populateGenres (tx , inheritedNames )
429+ if err != nil {
430+ return fmt .Errorf ("populate inherited genres: %w" , err )
431+ }
432+ }
433+
408434 // metadata for the album table comes only from the first track's tags
409435 if i == 0 {
410436 if err := tx .Where ("album_id=?" , album .ID ).Delete (db.ArtistAppearances {}).Error ; err != nil {
@@ -436,7 +462,7 @@ func (s *Scanner) populateTrackAndArtists(tx *db.DB, st *State, i int, album *db
436462 return fmt .Errorf ("populate album: %w" , err )
437463 }
438464
439- if err := populateAlbumGenres (tx , album , genreIDs ); err != nil {
465+ if err := populateAlbumGenres (tx , album , genreIDs , inheritedGenreIDs ); err != nil {
440466 return fmt .Errorf ("populate album genres: %w" , err )
441467 }
442468 }
@@ -453,7 +479,7 @@ func (s *Scanner) populateTrackAndArtists(tx *db.DB, st *State, i int, album *db
453479 if err := populateTrack (tx , s .scanEmbeddedCover , album , track , trprops , trags , basename , int (stat .Size ())); err != nil {
454480 return fmt .Errorf ("process %q: %w" , basename , err )
455481 }
456- if err := populateTrackGenres (tx , track , genreIDs ); err != nil {
482+ if err := populateTrackGenres (tx , track , genreIDs , inheritedGenreIDs ); err != nil {
457483 return fmt .Errorf ("populate track genres: %w" , err )
458484 }
459485
@@ -624,28 +650,48 @@ func populateGenres(tx *db.DB, names []string) ([]int, error) {
624650 return ids , nil
625651}
626652
627- func populateTrackGenres (tx * db.DB , track * db.Track , genreIDs []int ) error {
653+ func populateTrackGenres (tx * db.DB , track * db.Track , directIDs , inheritedIDs []int ) error {
628654 if err := tx .Where ("track_id=?" , track .ID ).Delete (db.TrackGenre {}).Error ; err != nil {
629655 return fmt .Errorf ("delete old track genre records: %w" , err )
630656 }
631-
632- if err := tx .InsertBulkLeftMany ("track_genres" , []string {"track_id" , "genre_id" }, track .ID , genreIDs ); err != nil {
633- return fmt .Errorf ("insert bulk track genres: %w" , err )
657+ rows := genreRows ( directIDs , inheritedIDs )
658+ if err := tx .InsertBulkLeftManyRows ("track_genres" , []string {"track_id" , "genre_id" , "inherited" }, track .ID , rows ); err != nil {
659+ return fmt .Errorf ("insert track genres: %w" , err )
634660 }
635661 return nil
636662}
637663
638- func populateAlbumGenres (tx * db.DB , album * db.Album , genreIDs []int ) error {
664+ func populateAlbumGenres (tx * db.DB , album * db.Album , directIDs , inheritedIDs []int ) error {
639665 if err := tx .Where ("album_id=?" , album .ID ).Delete (db.AlbumGenre {}).Error ; err != nil {
640666 return fmt .Errorf ("delete old album genre records: %w" , err )
641667 }
642-
643- if err := tx .InsertBulkLeftMany ("album_genres" , []string {"album_id" , "genre_id" }, album .ID , genreIDs ); err != nil {
644- return fmt .Errorf ("insert bulk album genres: %w" , err )
668+ rows := genreRows ( directIDs , inheritedIDs )
669+ if err := tx .InsertBulkLeftManyRows ("album_genres" , []string {"album_id" , "genre_id" , "inherited" }, album .ID , rows ); err != nil {
670+ return fmt .Errorf ("insert album genres: %w" , err )
645671 }
646672 return nil
647673}
648674
675+ func genreRows (directIDs , inheritedIDs []int ) [][]any {
676+ seen := map [int ]struct {}{}
677+ var rows [][]any
678+ for _ , id := range directIDs {
679+ if _ , ok := seen [id ]; ok {
680+ continue
681+ }
682+ seen [id ] = struct {}{}
683+ rows = append (rows , []any {id , false })
684+ }
685+ for _ , id := range inheritedIDs {
686+ if _ , ok := seen [id ]; ok {
687+ continue
688+ }
689+ seen [id ] = struct {}{}
690+ rows = append (rows , []any {id , true })
691+ }
692+ return rows
693+ }
694+
649695func populateAlbumDiscTitles (tx * db.DB , album * db.Album , discTitles map [int ]string ) error {
650696 if err := tx .Where ("album_id=?" , album .ID ).Delete (db.AlbumDiscTitle {}).Error ; err != nil {
651697 return fmt .Errorf ("delete old album disc titles: %w" , err )
0 commit comments