@@ -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}
@@ -406,6 +408,30 @@ func (s *Scanner) populateTrackAndArtists(tx *db.DB, st *State, i int, album *db
406408 return fmt .Errorf ("populate genres: %w" , err )
407409 }
408410
411+ var inheritedGenreIDs []int
412+ if len (s .genreTree ) > 0 {
413+ direct := map [string ]struct {}{}
414+ for _ , name := range genreNames {
415+ direct [name ] = struct {}{}
416+ }
417+ var inheritedNames []string
418+ for parent , descendants := range s .genreTree {
419+ if _ , ok := direct [parent ]; ok {
420+ continue
421+ }
422+ for _ , desc := range descendants {
423+ if _ , ok := direct [desc ]; ok {
424+ inheritedNames = append (inheritedNames , parent )
425+ break
426+ }
427+ }
428+ }
429+ inheritedGenreIDs , err = populateGenres (tx , inheritedNames )
430+ if err != nil {
431+ return fmt .Errorf ("populate inherited genres: %w" , err )
432+ }
433+ }
434+
409435 // metadata for the album table comes only from the first track's tags
410436 if i == 0 {
411437 if err := tx .Where ("album_id=?" , album .ID ).Delete (db.ArtistAppearances {}).Error ; err != nil {
@@ -437,7 +463,7 @@ func (s *Scanner) populateTrackAndArtists(tx *db.DB, st *State, i int, album *db
437463 return fmt .Errorf ("populate album: %w" , err )
438464 }
439465
440- if err := populateAlbumGenres (tx , album , genreIDs ); err != nil {
466+ if err := populateAlbumGenres (tx , album , genreIDs , inheritedGenreIDs ); err != nil {
441467 return fmt .Errorf ("populate album genres: %w" , err )
442468 }
443469 }
@@ -454,7 +480,7 @@ func (s *Scanner) populateTrackAndArtists(tx *db.DB, st *State, i int, album *db
454480 if err := populateTrack (tx , s .scanEmbeddedCover , album , track , trprops , trags , basename , int (stat .Size ())); err != nil {
455481 return fmt .Errorf ("process %q: %w" , basename , err )
456482 }
457- if err := populateTrackGenres (tx , track , genreIDs ); err != nil {
483+ if err := populateTrackGenres (tx , track , genreIDs , inheritedGenreIDs ); err != nil {
458484 return fmt .Errorf ("populate track genres: %w" , err )
459485 }
460486
@@ -625,28 +651,48 @@ func populateGenres(tx *db.DB, names []string) ([]int, error) {
625651 return ids , nil
626652}
627653
628- func populateTrackGenres (tx * db.DB , track * db.Track , genreIDs []int ) error {
654+ func populateTrackGenres (tx * db.DB , track * db.Track , directIDs , inheritedIDs []int ) error {
629655 if err := tx .Where ("track_id=?" , track .ID ).Delete (db.TrackGenre {}).Error ; err != nil {
630656 return fmt .Errorf ("delete old track genre records: %w" , err )
631657 }
632-
633- if err := tx .InsertBulkLeftMany ("track_genres" , []string {"track_id" , "genre_id" }, track .ID , genreIDs ); err != nil {
634- return fmt .Errorf ("insert bulk track genres: %w" , err )
658+ rows := genreRows ( directIDs , inheritedIDs )
659+ if err := tx .InsertBulkLeftManyRows ("track_genres" , []string {"track_id" , "genre_id" , "inherited" }, track .ID , rows ); err != nil {
660+ return fmt .Errorf ("insert track genres: %w" , err )
635661 }
636662 return nil
637663}
638664
639- func populateAlbumGenres (tx * db.DB , album * db.Album , genreIDs []int ) error {
665+ func populateAlbumGenres (tx * db.DB , album * db.Album , directIDs , inheritedIDs []int ) error {
640666 if err := tx .Where ("album_id=?" , album .ID ).Delete (db.AlbumGenre {}).Error ; err != nil {
641667 return fmt .Errorf ("delete old album genre records: %w" , err )
642668 }
643-
644- if err := tx .InsertBulkLeftMany ("album_genres" , []string {"album_id" , "genre_id" }, album .ID , genreIDs ); err != nil {
645- return fmt .Errorf ("insert bulk album genres: %w" , err )
669+ rows := genreRows ( directIDs , inheritedIDs )
670+ if err := tx .InsertBulkLeftManyRows ("album_genres" , []string {"album_id" , "genre_id" , "inherited" }, album .ID , rows ); err != nil {
671+ return fmt .Errorf ("insert album genres: %w" , err )
646672 }
647673 return nil
648674}
649675
676+ func genreRows (directIDs , inheritedIDs []int ) [][]any {
677+ seen := map [int ]struct {}{}
678+ var rows [][]any
679+ for _ , id := range directIDs {
680+ if _ , ok := seen [id ]; ok {
681+ continue
682+ }
683+ seen [id ] = struct {}{}
684+ rows = append (rows , []any {id , false })
685+ }
686+ for _ , id := range inheritedIDs {
687+ if _ , ok := seen [id ]; ok {
688+ continue
689+ }
690+ seen [id ] = struct {}{}
691+ rows = append (rows , []any {id , true })
692+ }
693+ return rows
694+ }
695+
650696func populateAlbumDiscTitles (tx * db.DB , album * db.Album , discTitles map [int ]string ) error {
651697 if err := tx .Where ("album_id=?" , album .ID ).Delete (db.AlbumDiscTitle {}).Error ; err != nil {
652698 return fmt .Errorf ("delete old album disc titles: %w" , err )
0 commit comments