package criteria import "strings" // FieldInfo contains semantic metadata about a criteria field. type FieldInfo struct { Alias string // If set, this field is a backward-compat alias for another canonical name IsTag bool IsRole bool Numeric bool Boolean bool // Nullable: isMissing/isPresent are supported on this column field. For numeric/boolean // fields, missing means NULL; for string fields it means NULL or empty string. Nullable bool tagAlias string // If set, a tag name from mappings.yaml that resolves to this field name string // Canonical name, populated by LookupField from the map key } // Name returns the canonical field name (the map key used to register this field). func (f FieldInfo) Name() string { return f.name } var fieldMap = map[string]FieldInfo{ "title": {}, "album": {Nullable: true}, "hascoverart": {Boolean: true}, "tracknumber": {}, "discnumber": {}, "year": {}, "date": {tagAlias: "recordingdate"}, "originalyear": {}, "originaldate": {}, "releaseyear": {}, "releasedate": {}, "size": {}, "compilation": {Boolean: true}, "missing": {Boolean: true}, "explicitstatus": {Nullable: true}, "dateadded": {}, "datemodified": {}, "discsubtitle": {Nullable: true}, "comment": {Nullable: true}, "lyrics": {Nullable: true}, "sorttitle": {Nullable: true}, "sortalbum": {Nullable: true}, "sortartist": {Nullable: true}, "sortalbumartist": {Nullable: true}, "albumcomment": {Nullable: true}, "catalognumber": {Nullable: true}, "filepath": {}, "filetype": {}, "codec": {}, "duration": {}, "bitrate": {}, "bitdepth": {Numeric: true, Nullable: true}, "samplerate": {}, "bpm": {Numeric: true, Nullable: true}, "channels": {}, "loved": {Boolean: true}, "dateloved": {}, "lastplayed": {}, "daterated": {}, "playcount": {}, "rating": {}, "averagerating": {Numeric: true}, "albumrating": {}, "albumloved": {Boolean: true}, "albumplaycount": {}, "albumlastplayed": {}, "albumdateloved": {}, "albumdaterated": {}, "artistrating": {}, "artistloved": {Boolean: true}, "artistplaycount": {}, "artistlastplayed": {}, "artistdateloved": {}, "artistdaterated": {}, "mbz_album_id": {Nullable: true}, "mbz_album_artist_id": {Nullable: true}, "mbz_artist_id": {Nullable: true}, "mbz_recording_id": {Nullable: true}, "mbz_release_track_id": {Nullable: true}, "mbz_release_group_id": {Nullable: true}, "rgalbumgain": {Numeric: true, Nullable: true}, "rgalbumpeak": {Numeric: true, Nullable: true}, "rgtrackgain": {Numeric: true, Nullable: true}, "rgtrackpeak": {Numeric: true, Nullable: true}, "library_id": {Numeric: true}, // Backward compatibility: albumtype is an alias for the releasetype tag. "albumtype": {Alias: "releasetype", IsTag: true}, // Backward compatibility: the replaygain_* tag names (as written in metadata and in the // PR #5256 example) are aliases for the canonical rg* column fields. Without these, the tag // names would be registered as empty tags from mappings.yaml and isMissing would always match. "replaygain_album_gain": {Alias: "rgalbumgain", Numeric: true, Nullable: true}, "replaygain_album_peak": {Alias: "rgalbumpeak", Numeric: true, Nullable: true}, "replaygain_track_gain": {Alias: "rgtrackgain", Numeric: true, Nullable: true}, "replaygain_track_peak": {Alias: "rgtrackpeak", Numeric: true, Nullable: true}, // Pseudo-field for random sorting "random": {}, } // AllFieldNames returns the names of all registered criteria fields. func AllFieldNames() []string { names := make([]string, 0, len(fieldMap)) for name := range fieldMap { names = append(names, name) } return names } // LookupField returns semantic metadata for a criteria field name. func LookupField(name string) (FieldInfo, bool) { key := strings.ToLower(name) f, ok := fieldMap[key] if ok { if f.Alias != "" { f.name = f.Alias } else { f.name = key } } return f, ok } // AddRoles adds roles to the field map. This is used to add all artist roles to the field map, so they can be used in // smart playlists. func AddRoles(roles []string) { for _, role := range roles { name := strings.ToLower(role) if _, ok := fieldMap[name]; ok { continue } fieldMap[name] = FieldInfo{IsRole: true} } } // AddTagNames adds tag names to the field map. This is used to add all tags mapped in the `mappings.yaml` // configuration file. func AddTagNames(tagNames []string) { for _, tagName := range tagNames { name := strings.ToLower(tagName) if _, ok := fieldMap[name]; ok { continue } for key, fm := range fieldMap { if fm.tagAlias == name { fm.Alias = key fm.tagAlias = "" fieldMap[name] = fm break } } if _, ok := fieldMap[name]; !ok { fieldMap[name] = FieldInfo{IsTag: true} } } } // AddNumericTags adds tags that should be treated as numbers. func AddNumericTags(tagNames []string) { for _, tagName := range tagNames { name := strings.ToLower(tagName) if fm, ok := fieldMap[name]; ok { fm.Numeric = true fieldMap[name] = fm } else { fieldMap[name] = FieldInfo{IsTag: true, Numeric: true} } } }