diff --git a/app.go b/app.go index a488c70..2e31cb3 100644 --- a/app.go +++ b/app.go @@ -142,12 +142,27 @@ func (a *App) GetSpotifyMetadata(req SpotifyMetadataRequest) (string, error) { defer cancel() settings, err := a.LoadSettings() + separator := req.Separator + if separator == "" { + separator = ", " + if err == nil && settings != nil { + if sep, ok := settings["separator"].(string); ok { + if sep == "semicolon" { + separator = "; " + } else if sep == "comma" { + separator = ", " + } + } + } + } if err == nil && settings != nil { if useAPI, ok := settings["useSpotFetchAPI"].(bool); ok && useAPI { if apiURL, ok := settings["spotFetchAPIUrl"].(string); ok && apiURL != "" { - data, err := backend.GetSpotifyDataWithAPI(ctx, req.URL, true, apiURL, req.Batch, time.Duration(req.Delay*float64(time.Second))) + data, err := backend.GetSpotifyDataWithAPI(ctx, req.URL, true, apiURL, req.Batch, time.Duration(req.Delay*float64(time.Second)), separator, func(tracks interface{}) { + runtime.EventsEmit(a.ctx, "metadata-stream", tracks) + }) if err != nil { return "", fmt.Errorf("failed to fetch metadata from API: %v", err) } @@ -162,7 +177,9 @@ func (a *App) GetSpotifyMetadata(req SpotifyMetadataRequest) (string, error) { } } - data, err := backend.GetFilteredSpotifyData(ctx, req.URL, req.Batch, time.Duration(req.Delay*float64(time.Second))) + data, err := backend.GetFilteredSpotifyData(ctx, req.URL, req.Batch, time.Duration(req.Delay*float64(time.Second)), separator, func(tracks interface{}) { + runtime.EventsEmit(a.ctx, "metadata-stream", tracks) + }) if err != nil { return "", fmt.Errorf("failed to fetch metadata: %v", err) } @@ -283,7 +300,21 @@ func (a *App) DownloadTrack(req DownloadRequest) (DownloadResponse, error) { defer cancel() trackURL := fmt.Sprintf("https://open.spotify.com/track/%s", req.SpotifyID) - trackData, err := backend.GetFilteredSpotifyData(ctx, trackURL, false, 0) + metadataSeparator := req.Separator + if metadataSeparator == "" { + metadataSeparator = ", " + metadataSettings, _ := a.LoadSettings() + if metadataSettings != nil { + if sep, ok := metadataSettings["separator"].(string); ok { + if sep == "semicolon" { + metadataSeparator = "; " + } else if sep == "comma" { + metadataSeparator = ", " + } + } + } + } + trackData, err := backend.GetFilteredSpotifyData(ctx, trackURL, false, 0, metadataSeparator, nil) if err == nil { var trackResp struct { diff --git a/backend/spotfetch.go b/backend/spotfetch.go index 440154d..c584d94 100644 --- a/backend/spotfetch.go +++ b/backend/spotfetch.go @@ -485,7 +485,7 @@ func extractDuration(ms float64) map[string]interface{} { } } -func FilterTrack(data map[string]interface{}, albumFetchData ...map[string]interface{}) map[string]interface{} { +func FilterTrack(data map[string]interface{}, separator string, albumFetchData ...map[string]interface{}) map[string]interface{} { dataMap := getMap(data, "data") trackData := getMap(dataMap, "trackUnion") if len(trackData) == 0 { @@ -665,7 +665,7 @@ func FilterTrack(data map[string]interface{}, albumFetchData ...map[string]inter for _, artist := range albumArtists { albumArtistNames = append(albumArtistNames, getString(artist, "name")) } - albumArtistsString = strings.Join(albumArtistNames, GetSeparator()) + albumArtistsString = strings.Join(albumArtistNames, separator) } if albumArtistsString == "" { albumArtistsString = getString(albumUnionData, "artists") @@ -681,7 +681,7 @@ func FilterTrack(data map[string]interface{}, albumFetchData ...map[string]inter for _, artist := range albumArtists { albumArtistNames = append(albumArtistNames, getString(artist, "name")) } - albumArtistsString = strings.Join(albumArtistNames, GetSeparator()) + albumArtistsString = strings.Join(albumArtistNames, separator) } } @@ -715,7 +715,7 @@ func FilterTrack(data map[string]interface{}, albumFetchData ...map[string]inter for _, artist := range artists { artistNames = append(artistNames, getString(artist, "name")) } - artistsString := strings.Join(artistNames, GetSeparator()) + artistsString := strings.Join(artistNames, separator) copyrightTexts := []string{} for _, item := range copyrightInfo { @@ -802,7 +802,7 @@ func FilterTrack(data map[string]interface{}, albumFetchData ...map[string]inter return filtered } -func FilterAlbum(data map[string]interface{}) map[string]interface{} { +func FilterAlbum(data map[string]interface{}, separator string) map[string]interface{} { dataMap := getMap(data, "data") albumData := getMap(dataMap, "albumUnion") if len(albumData) == 0 { @@ -814,7 +814,7 @@ func FilterAlbum(data map[string]interface{}) map[string]interface{} { for _, artist := range artists { artistNames = append(artistNames, getString(artist, "name")) } - albumArtistsString := strings.Join(artistNames, GetSeparator()) + albumArtistsString := strings.Join(artistNames, separator) coverObj := extractCoverImage(getMap(albumData, "coverArt")) var cover interface{} @@ -875,7 +875,7 @@ func FilterAlbum(data map[string]interface{}) map[string]interface{} { for _, artist := range trackArtists { trackArtistNames = append(trackArtistNames, getString(artist, "name")) } - trackArtistsString := strings.Join(trackArtistNames, GetSeparator()) + trackArtistsString := strings.Join(trackArtistNames, separator) trackURI := getString(track, "uri") trackID := "" @@ -943,7 +943,7 @@ func FilterAlbum(data map[string]interface{}) map[string]interface{} { return filtered } -func FilterPlaylist(data map[string]interface{}) map[string]interface{} { +func FilterPlaylist(data map[string]interface{}, separator string) map[string]interface{} { dataMap := getMap(data, "data") playlistData := getMap(dataMap, "playlistV2") if len(playlistData) == 0 { @@ -1075,7 +1075,7 @@ func FilterPlaylist(data map[string]interface{}) map[string]interface{} { for _, artist := range trackArtists { trackArtistNames = append(trackArtistNames, getString(artist, "name")) } - artistsString := strings.Join(trackArtistNames, GetSeparator()) + artistsString := strings.Join(trackArtistNames, separator) trackDurationMs := getFloat64(getMap(trackData, "trackDuration"), "totalMilliseconds") durationObj := extractDuration(trackDurationMs) @@ -1121,7 +1121,7 @@ func FilterPlaylist(data map[string]interface{}) map[string]interface{} { for _, artist := range albumArtists { albumArtistNames = append(albumArtistNames, getString(artist, "name")) } - albumArtistsString = strings.Join(albumArtistNames, GetSeparator()) + albumArtistsString = strings.Join(albumArtistNames, separator) } } @@ -1295,7 +1295,7 @@ func stripHTMLTags(s string) string { return re.ReplaceAllString(s, "") } -func FilterArtist(data map[string]interface{}) map[string]interface{} { +func FilterArtist(data map[string]interface{}, separator string) map[string]interface{} { dataMap := getMap(data, "data") artistData := getMap(dataMap, "artistUnion") if len(artistData) == 0 { @@ -1424,7 +1424,7 @@ func FilterArtist(data map[string]interface{}) map[string]interface{} { return filtered } -func FilterSearch(data map[string]interface{}) map[string]interface{} { +func FilterSearch(data map[string]interface{}, separator string) map[string]interface{} { dataMap := getMap(data, "data") searchData := getMap(dataMap, "searchV2") if len(searchData) == 0 { @@ -1514,7 +1514,7 @@ func FilterSearch(data map[string]interface{}) map[string]interface{} { for _, artist := range trackArtists { trackArtistNames = append(trackArtistNames, getString(artist, "name")) } - trackArtistsString := strings.Join(trackArtistNames, GetSeparator()) + trackArtistsString := strings.Join(trackArtistNames, separator) durationString := getString(trackDuration, "formatted") @@ -1586,7 +1586,7 @@ func FilterSearch(data map[string]interface{}) map[string]interface{} { for _, artist := range albumArtists { albumArtistNames = append(albumArtistNames, getString(artist, "name")) } - albumArtistsString := strings.Join(albumArtistNames, GetSeparator()) + albumArtistsString := strings.Join(albumArtistNames, separator) dateInfo := getMap(album, "date") var year interface{} diff --git a/backend/spotfetch_api.go b/backend/spotfetch_api.go index cce1a93..9943aaa 100644 --- a/backend/spotfetch_api.go +++ b/backend/spotfetch_api.go @@ -11,10 +11,9 @@ import ( "time" ) -func GetSpotifyDataWithAPI(ctx context.Context, spotifyURL string, useAPI bool, apiBaseURL string, batch bool, delay time.Duration) (interface{}, error) { +func GetSpotifyDataWithAPI(ctx context.Context, spotifyURL string, useAPI bool, apiBaseURL string, batch bool, delay time.Duration, separator string, callback MetadataCallback) (interface{}, error) { if !useAPI || apiBaseURL == "" { - - return GetFilteredSpotifyData(ctx, spotifyURL, batch, delay) + return GetFilteredSpotifyData(ctx, spotifyURL, batch, delay, separator, callback) } spotifyType, id := parseSpotifyURLToTypeAndID(spotifyURL) @@ -79,6 +78,43 @@ func GetSpotifyDataWithAPI(ctx context.Context, spotifyURL string, useAPI bool, return nil, fmt.Errorf("unsupported Spotify type: %s", spotifyType) } + if callback != nil { + switch payload := data.(type) { + case *AlbumResponsePayload: + if len(payload.TrackList) > 0 { + callback(payload.TrackList) + } + case PlaylistResponsePayload: + if len(payload.TrackList) > 0 { + callback(payload.TrackList) + } + case *ArtistDiscographyPayload: + if len(payload.TrackList) > 0 { + callback(payload.TrackList) + } + case TrackResponse: + t := payload.Track + callback([]AlbumTrackMetadata{{ + SpotifyID: t.SpotifyID, + Artists: t.Artists, + Name: t.Name, + AlbumName: t.AlbumName, + AlbumArtist: t.AlbumArtist, + DurationMS: t.DurationMS, + Images: t.Images, + ReleaseDate: t.ReleaseDate, + TrackNumber: t.TrackNumber, + TotalTracks: t.TotalTracks, + DiscNumber: t.DiscNumber, + TotalDiscs: t.TotalDiscs, + ExternalURL: t.ExternalURL, + Plays: t.Plays, + PreviewURL: t.PreviewURL, + IsExplicit: t.IsExplicit, + }}) + } + } + return data, nil } diff --git a/backend/spotify_metadata.go b/backend/spotify_metadata.go index ef296d8..3d28633 100644 --- a/backend/spotify_metadata.go +++ b/backend/spotify_metadata.go @@ -18,13 +18,17 @@ var ( errInvalidSpotifyURL = errors.New("invalid or unsupported Spotify URL") ) +type MetadataCallback func(data interface{}) + type SpotifyMetadataClient struct { httpClient *http.Client + Separator string } func NewSpotifyMetadataClient() *SpotifyMetadataClient { return &SpotifyMetadataClient{ httpClient: &http.Client{Timeout: 30 * time.Second}, + Separator: ", ", } } @@ -342,54 +346,57 @@ type SearchResponse struct { Playlists []SearchResult `json:"playlists"` } -func GetFilteredSpotifyData(ctx context.Context, spotifyURL string, batch bool, delay time.Duration) (interface{}, error) { +func GetFilteredSpotifyData(ctx context.Context, spotifyURL string, batch bool, delay time.Duration, separator string, callback MetadataCallback) (interface{}, error) { client := NewSpotifyMetadataClient() - return client.GetFilteredData(ctx, spotifyURL, batch, delay) + if separator != "" { + client.Separator = separator + } + return client.GetFilteredData(ctx, spotifyURL, batch, delay, callback) } -func (c *SpotifyMetadataClient) GetFilteredData(ctx context.Context, spotifyURL string, batch bool, delay time.Duration) (interface{}, error) { +func (c *SpotifyMetadataClient) GetFilteredData(ctx context.Context, spotifyURL string, batch bool, delay time.Duration, callback MetadataCallback) (interface{}, error) { parsed, err := parseSpotifyURI(spotifyURL) if err != nil { return nil, err } - raw, err := c.getRawSpotifyData(ctx, parsed, batch, delay) + raw, err := c.getRawSpotifyData(ctx, parsed, batch, delay, callback) if err != nil { return nil, err } - return c.processSpotifyData(ctx, raw) + return c.processSpotifyData(ctx, raw, callback) } -func (c *SpotifyMetadataClient) getRawSpotifyData(ctx context.Context, parsed spotifyURI, batch bool, delay time.Duration) (interface{}, error) { +func (c *SpotifyMetadataClient) getRawSpotifyData(ctx context.Context, parsed spotifyURI, batch bool, delay time.Duration, callback MetadataCallback) (interface{}, error) { switch parsed.Type { case "playlist": - return c.fetchPlaylist(ctx, parsed.ID) + return c.fetchPlaylist(ctx, parsed.ID, callback) case "album": - return c.fetchAlbum(ctx, parsed.ID) + return c.fetchAlbum(ctx, parsed.ID, callback) case "track": return c.fetchTrack(ctx, parsed.ID) case "artist_discography": - return c.fetchArtistDiscography(ctx, parsed) + return c.fetchArtistDiscography(ctx, parsed, callback) case "artist": discographyParsed := spotifyURI{Type: "artist_discography", ID: parsed.ID, DiscographyGroup: "all"} - return c.fetchArtistDiscography(ctx, discographyParsed) + return c.fetchArtistDiscography(ctx, discographyParsed, callback) default: return nil, fmt.Errorf("unsupported Spotify type: %s", parsed.Type) } } -func (c *SpotifyMetadataClient) processSpotifyData(ctx context.Context, raw interface{}) (interface{}, error) { +func (c *SpotifyMetadataClient) processSpotifyData(ctx context.Context, raw interface{}, callback MetadataCallback) (interface{}, error) { switch payload := raw.(type) { case *apiPlaylistResponse: - return c.formatPlaylistData(payload), nil + return c.formatPlaylistData(payload, callback), nil case *apiAlbumResponse: - return c.formatAlbumData(payload) + return c.formatAlbumData(payload, callback) case *apiTrackResponse: return c.formatTrackData(payload), nil case *apiArtistResponse: - return c.formatArtistDiscographyData(ctx, payload) + return c.formatArtistDiscographyData(ctx, payload, callback) default: return nil, errors.New("unknown raw payload type") } @@ -437,7 +444,7 @@ func (c *SpotifyMetadataClient) fetchTrack(ctx context.Context, trackID string) if albumID != "" { - albumResponse, err := c.fetchAlbumWithClient(ctx, client, albumID) + albumResponse, err := c.fetchAlbumWithClient(ctx, client, albumID, nil) if err == nil && albumResponse != nil { albumJSON, _ := json.Marshal(albumResponse) @@ -482,7 +489,7 @@ func (c *SpotifyMetadataClient) fetchTrack(ctx context.Context, trackID string) } } - filteredData := FilterTrack(data, albumFetchData) + filteredData := FilterTrack(data, c.Separator, albumFetchData) jsonData, err := json.Marshal(filteredData) if err != nil { @@ -497,15 +504,15 @@ func (c *SpotifyMetadataClient) fetchTrack(ctx context.Context, trackID string) return &result, nil } -func (c *SpotifyMetadataClient) fetchAlbum(ctx context.Context, albumID string) (*apiAlbumResponse, error) { +func (c *SpotifyMetadataClient) fetchAlbum(ctx context.Context, albumID string, callback MetadataCallback) (*apiAlbumResponse, error) { client := NewSpotifyClient() if err := client.Initialize(); err != nil { return nil, fmt.Errorf("failed to initialize spotify client: %w", err) } - return c.fetchAlbumWithClient(ctx, client, albumID) + return c.fetchAlbumWithClient(ctx, client, albumID, callback) } -func (c *SpotifyMetadataClient) fetchAlbumWithClient(ctx context.Context, client *SpotifyClient, albumID string) (*apiAlbumResponse, error) { +func (c *SpotifyMetadataClient) fetchAlbumWithClient(ctx context.Context, client *SpotifyClient, albumID string, callback MetadataCallback) (*apiAlbumResponse, error) { allItems := []interface{}{} offset := 0 @@ -537,6 +544,15 @@ func (c *SpotifyMetadataClient) fetchAlbumWithClient(ctx context.Context, client if data == nil { data = response + if callback != nil { + filtered := FilterAlbum(data, c.Separator) + jsonData, _ := json.Marshal(filtered) + var result apiAlbumResponse + if json.Unmarshal(jsonData, &result) == nil { + formatted, _ := c.formatAlbumData(&result, nil) + callback(formatted) + } + } } albumData := getMap(getMap(response, "data"), "albumUnion") @@ -579,7 +595,7 @@ func (c *SpotifyMetadataClient) fetchAlbumWithClient(ctx context.Context, client tracksV2["totalCount"] = len(allItems) } - filteredData := FilterAlbum(data) + filteredData := FilterAlbum(data, c.Separator) jsonData, err := json.Marshal(filteredData) if err != nil { @@ -594,7 +610,7 @@ func (c *SpotifyMetadataClient) fetchAlbumWithClient(ctx context.Context, client return &result, nil } -func (c *SpotifyMetadataClient) fetchPlaylist(ctx context.Context, playlistID string) (*apiPlaylistResponse, error) { +func (c *SpotifyMetadataClient) fetchPlaylist(ctx context.Context, playlistID string, callback MetadataCallback) (*apiPlaylistResponse, error) { client := NewSpotifyClient() if err := client.Initialize(); err != nil { return nil, fmt.Errorf("failed to initialize spotify client: %w", err) @@ -630,6 +646,15 @@ func (c *SpotifyMetadataClient) fetchPlaylist(ctx context.Context, playlistID st if data == nil { data = response + if callback != nil { + filtered := FilterPlaylist(data, c.Separator) + jsonData, _ := json.Marshal(filtered) + var result apiPlaylistResponse + if json.Unmarshal(jsonData, &result) == nil { + formatted := c.formatPlaylistData(&result, nil) + callback(formatted) + } + } } playlistData := getMap(getMap(response, "data"), "playlistV2") @@ -672,7 +697,7 @@ func (c *SpotifyMetadataClient) fetchPlaylist(ctx context.Context, playlistID st content["totalCount"] = len(allItems) } - filteredData := FilterPlaylist(data) + filteredData := FilterPlaylist(data, c.Separator) jsonData, err := json.Marshal(filteredData) if err != nil { @@ -687,7 +712,7 @@ func (c *SpotifyMetadataClient) fetchPlaylist(ctx context.Context, playlistID st return &result, nil } -func (c *SpotifyMetadataClient) fetchArtistDiscography(ctx context.Context, parsed spotifyURI) (*apiArtistResponse, error) { +func (c *SpotifyMetadataClient) fetchArtistDiscography(ctx context.Context, parsed spotifyURI, callback MetadataCallback) (*apiArtistResponse, error) { client := NewSpotifyClient() if err := client.Initialize(); err != nil { return nil, fmt.Errorf("failed to initialize spotify client: %w", err) @@ -712,6 +737,16 @@ func (c *SpotifyMetadataClient) fetchArtistDiscography(ctx context.Context, pars return nil, fmt.Errorf("failed to query artist overview: %w", err) } + if callback != nil { + filtered := FilterArtist(data, c.Separator) + jsonData, _ := json.Marshal(filtered) + var result apiArtistResponse + if json.Unmarshal(jsonData, &result) == nil { + formatted, _ := c.formatArtistDiscographyData(ctx, &result, nil) + callback(formatted) + } + } + allDiscographyItems := []interface{}{} offset := 0 limit := 50 @@ -841,7 +876,7 @@ func (c *SpotifyMetadataClient) fetchArtistDiscography(ctx context.Context, pars } } - filteredData := FilterArtist(data) + filteredData := FilterArtist(data, c.Separator) jsonData, err := json.Marshal(filteredData) if err != nil { @@ -898,7 +933,7 @@ func (c *SpotifyMetadataClient) formatTrackData(raw *apiTrackResponse) TrackResp } } -func (c *SpotifyMetadataClient) formatAlbumData(raw *apiAlbumResponse) (*AlbumResponsePayload, error) { +func (c *SpotifyMetadataClient) formatAlbumData(raw *apiAlbumResponse, callback MetadataCallback) (*AlbumResponsePayload, error) { var artistID, artistURL string info := AlbumInfoMetadata{ @@ -911,6 +946,13 @@ func (c *SpotifyMetadataClient) formatAlbumData(raw *apiAlbumResponse) (*AlbumRe ArtistURL: artistURL, } + if callback != nil { + callback(AlbumResponsePayload{ + AlbumInfo: info, + TrackList: []AlbumTrackMetadata{}, + }) + } + tracks := make([]AlbumTrackMetadata, 0, len(raw.Tracks)) for idx, item := range raw.Tracks { durationMS := parseDuration(item.Duration) @@ -955,13 +997,17 @@ func (c *SpotifyMetadataClient) formatAlbumData(raw *apiAlbumResponse) (*AlbumRe }) } + if callback != nil { + callback(tracks) + } + return &AlbumResponsePayload{ AlbumInfo: info, TrackList: tracks, }, nil } -func (c *SpotifyMetadataClient) formatPlaylistData(raw *apiPlaylistResponse) PlaylistResponsePayload { +func (c *SpotifyMetadataClient) formatPlaylistData(raw *apiPlaylistResponse, callback MetadataCallback) PlaylistResponsePayload { var info PlaylistInfoMetadata info.Tracks.Total = raw.Count info.Followers.Total = raw.Followers @@ -971,6 +1017,13 @@ func (c *SpotifyMetadataClient) formatPlaylistData(raw *apiPlaylistResponse) Pla info.Cover = raw.Cover info.Description = raw.Description + if callback != nil { + callback(PlaylistResponsePayload{ + PlaylistInfo: info, + TrackList: []AlbumTrackMetadata{}, + }) + } + tracks := make([]AlbumTrackMetadata, 0, len(raw.Tracks)) for _, item := range raw.Tracks { durationMS := parseDuration(item.Duration) @@ -1015,13 +1068,17 @@ func (c *SpotifyMetadataClient) formatPlaylistData(raw *apiPlaylistResponse) Pla }) } + if callback != nil { + callback(tracks) + } + return PlaylistResponsePayload{ PlaylistInfo: info, TrackList: tracks, } } -func (c *SpotifyMetadataClient) formatArtistDiscographyData(ctx context.Context, raw *apiArtistResponse) (*ArtistDiscographyPayload, error) { +func (c *SpotifyMetadataClient) formatArtistDiscographyData(ctx context.Context, raw *apiArtistResponse, callback MetadataCallback) (*ArtistDiscographyPayload, error) { discType := "all" info := ArtistInfoMetadata{ @@ -1067,7 +1124,17 @@ func (c *SpotifyMetadataClient) formatArtistDiscographyData(ctx context.Context, Images: alb.Cover, ExternalURL: fmt.Sprintf("https://open.spotify.com/album/%s", alb.ID), }) + } + if callback != nil { + callback(ArtistDiscographyPayload{ + ArtistInfo: info, + AlbumList: albumList, + TrackList: []AlbumTrackMetadata{}, + }) + } + + for _, alb := range raw.Discography.All { go func(albumID string, albumName string) { sem <- struct{}{} @@ -1081,7 +1148,7 @@ func (c *SpotifyMetadataClient) formatArtistDiscographyData(ctx context.Context, default: } - albumData, err := c.fetchAlbumWithClient(ctx, sharedClient, albumID) + albumData, err := c.fetchAlbumWithClient(ctx, sharedClient, albumID, nil) if err != nil { fmt.Printf("Error getting tracks for album %s: %v\n", albumName, err) resultsChan <- fetchResult{tracks: []AlbumTrackMetadata{}} @@ -1131,6 +1198,9 @@ func (c *SpotifyMetadataClient) formatArtistDiscographyData(ctx context.Context, IsExplicit: tr.IsExplicit, }) } + if callback != nil { + callback(tracks) + } resultsChan <- fetchResult{tracks: tracks} }(alb.ID, alb.Name) } @@ -1290,7 +1360,7 @@ func (c *SpotifyMetadataClient) Search(ctx context.Context, query string, limit return nil, fmt.Errorf("failed to query search: %w", err) } - filteredData := FilterSearch(data) + filteredData := FilterSearch(data, c.Separator) jsonData, err := json.Marshal(filteredData) if err != nil { @@ -1407,7 +1477,7 @@ func (c *SpotifyMetadataClient) SearchByType(ctx context.Context, query string, return nil, fmt.Errorf("failed to query search: %w", err) } - filteredData := FilterSearch(data) + filteredData := FilterSearch(data, c.Separator) jsonData, err := json.Marshal(filteredData) if err != nil { diff --git a/frontend/src/components/ArtistInfo.tsx b/frontend/src/components/ArtistInfo.tsx index 5abcf8c..f075cce 100644 --- a/frontend/src/components/ArtistInfo.tsx +++ b/frontend/src/components/ArtistInfo.tsx @@ -21,6 +21,7 @@ interface ArtistInfoProps { header?: string; gallery?: string[]; followers: number; + total_albums?: number; genres: string[]; biography?: string; verified?: boolean; @@ -99,6 +100,7 @@ export function ArtistInfo({ artistInfo, albumList, trackList, searchQuery, sort const [downloadingGalleryIndex, setDownloadingGalleryIndex] = useState(null); const [downloadingAllGallery, setDownloadingAllGallery] = useState(false); const [activeTab, setActiveTab] = useState<"albums" | "tracks" | "gallery">("albums"); + const displayedAlbumCount = artistInfo.total_albums || albumList.length; const filteredAlbumGroups = useMemo(() => { const albumTypeMap = new Map(albumList.map(a => [a.name, a.album_type])); const albumGroups = trackList.reduce((acc, track) => { @@ -330,9 +332,9 @@ export function ArtistInfo({ artistInfo, albumList, trackList, searchQuery, sort )}
- {albumList.length} {albumList.length === 1 ? "album" : "albums"} + {displayedAlbumCount.toLocaleString()} {displayedAlbumCount === 1 ? "album" : "albums"} - {trackList.length} {trackList.length === 1 ? "track" : "tracks"} + {trackList.length.toLocaleString()} {trackList.length === 1 ? "track" : "tracks"} {artistInfo.genres.length > 0 && (<> {artistInfo.genres.join(", ")} @@ -383,9 +385,9 @@ export function ArtistInfo({ artistInfo, albumList, trackList, searchQuery, sort )}
- {albumList.length} {albumList.length === 1 ? "album" : "albums"} + {displayedAlbumCount.toLocaleString()} {displayedAlbumCount === 1 ? "album" : "albums"} - {trackList.length} {trackList.length === 1 ? "track" : "tracks"} + {trackList.length.toLocaleString()} {trackList.length === 1 ? "track" : "tracks"} {artistInfo.genres.length > 0 && (<> {artistInfo.genres.join(", ")} @@ -412,7 +414,7 @@ export function ArtistInfo({ artistInfo, albumList, trackList, searchQuery, sort {activeTab === "gallery" && hasGallery && (
-

Gallery ({artistInfo.gallery!.length})

+

Gallery ({artistInfo.gallery!.length.toLocaleString()})