.refine artist fetch all tracks
This commit is contained in:
@@ -142,12 +142,27 @@ func (a *App) GetSpotifyMetadata(req SpotifyMetadataRequest) (string, error) {
|
|||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
settings, err := a.LoadSettings()
|
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 err == nil && settings != nil {
|
||||||
if useAPI, ok := settings["useSpotFetchAPI"].(bool); ok && useAPI {
|
if useAPI, ok := settings["useSpotFetchAPI"].(bool); ok && useAPI {
|
||||||
if apiURL, ok := settings["spotFetchAPIUrl"].(string); ok && apiURL != "" {
|
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 {
|
if err != nil {
|
||||||
return "", fmt.Errorf("failed to fetch metadata from API: %v", err)
|
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 {
|
if err != nil {
|
||||||
return "", fmt.Errorf("failed to fetch metadata: %v", err)
|
return "", fmt.Errorf("failed to fetch metadata: %v", err)
|
||||||
}
|
}
|
||||||
@@ -283,7 +300,21 @@ func (a *App) DownloadTrack(req DownloadRequest) (DownloadResponse, error) {
|
|||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
trackURL := fmt.Sprintf("https://open.spotify.com/track/%s", req.SpotifyID)
|
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 {
|
if err == nil {
|
||||||
|
|
||||||
var trackResp struct {
|
var trackResp struct {
|
||||||
|
|||||||
+14
-14
@@ -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")
|
dataMap := getMap(data, "data")
|
||||||
trackData := getMap(dataMap, "trackUnion")
|
trackData := getMap(dataMap, "trackUnion")
|
||||||
if len(trackData) == 0 {
|
if len(trackData) == 0 {
|
||||||
@@ -665,7 +665,7 @@ func FilterTrack(data map[string]interface{}, albumFetchData ...map[string]inter
|
|||||||
for _, artist := range albumArtists {
|
for _, artist := range albumArtists {
|
||||||
albumArtistNames = append(albumArtistNames, getString(artist, "name"))
|
albumArtistNames = append(albumArtistNames, getString(artist, "name"))
|
||||||
}
|
}
|
||||||
albumArtistsString = strings.Join(albumArtistNames, GetSeparator())
|
albumArtistsString = strings.Join(albumArtistNames, separator)
|
||||||
}
|
}
|
||||||
if albumArtistsString == "" {
|
if albumArtistsString == "" {
|
||||||
albumArtistsString = getString(albumUnionData, "artists")
|
albumArtistsString = getString(albumUnionData, "artists")
|
||||||
@@ -681,7 +681,7 @@ func FilterTrack(data map[string]interface{}, albumFetchData ...map[string]inter
|
|||||||
for _, artist := range albumArtists {
|
for _, artist := range albumArtists {
|
||||||
albumArtistNames = append(albumArtistNames, getString(artist, "name"))
|
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 {
|
for _, artist := range artists {
|
||||||
artistNames = append(artistNames, getString(artist, "name"))
|
artistNames = append(artistNames, getString(artist, "name"))
|
||||||
}
|
}
|
||||||
artistsString := strings.Join(artistNames, GetSeparator())
|
artistsString := strings.Join(artistNames, separator)
|
||||||
|
|
||||||
copyrightTexts := []string{}
|
copyrightTexts := []string{}
|
||||||
for _, item := range copyrightInfo {
|
for _, item := range copyrightInfo {
|
||||||
@@ -802,7 +802,7 @@ func FilterTrack(data map[string]interface{}, albumFetchData ...map[string]inter
|
|||||||
return filtered
|
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")
|
dataMap := getMap(data, "data")
|
||||||
albumData := getMap(dataMap, "albumUnion")
|
albumData := getMap(dataMap, "albumUnion")
|
||||||
if len(albumData) == 0 {
|
if len(albumData) == 0 {
|
||||||
@@ -814,7 +814,7 @@ func FilterAlbum(data map[string]interface{}) map[string]interface{} {
|
|||||||
for _, artist := range artists {
|
for _, artist := range artists {
|
||||||
artistNames = append(artistNames, getString(artist, "name"))
|
artistNames = append(artistNames, getString(artist, "name"))
|
||||||
}
|
}
|
||||||
albumArtistsString := strings.Join(artistNames, GetSeparator())
|
albumArtistsString := strings.Join(artistNames, separator)
|
||||||
|
|
||||||
coverObj := extractCoverImage(getMap(albumData, "coverArt"))
|
coverObj := extractCoverImage(getMap(albumData, "coverArt"))
|
||||||
var cover interface{}
|
var cover interface{}
|
||||||
@@ -875,7 +875,7 @@ func FilterAlbum(data map[string]interface{}) map[string]interface{} {
|
|||||||
for _, artist := range trackArtists {
|
for _, artist := range trackArtists {
|
||||||
trackArtistNames = append(trackArtistNames, getString(artist, "name"))
|
trackArtistNames = append(trackArtistNames, getString(artist, "name"))
|
||||||
}
|
}
|
||||||
trackArtistsString := strings.Join(trackArtistNames, GetSeparator())
|
trackArtistsString := strings.Join(trackArtistNames, separator)
|
||||||
|
|
||||||
trackURI := getString(track, "uri")
|
trackURI := getString(track, "uri")
|
||||||
trackID := ""
|
trackID := ""
|
||||||
@@ -943,7 +943,7 @@ func FilterAlbum(data map[string]interface{}) map[string]interface{} {
|
|||||||
return filtered
|
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")
|
dataMap := getMap(data, "data")
|
||||||
playlistData := getMap(dataMap, "playlistV2")
|
playlistData := getMap(dataMap, "playlistV2")
|
||||||
if len(playlistData) == 0 {
|
if len(playlistData) == 0 {
|
||||||
@@ -1075,7 +1075,7 @@ func FilterPlaylist(data map[string]interface{}) map[string]interface{} {
|
|||||||
for _, artist := range trackArtists {
|
for _, artist := range trackArtists {
|
||||||
trackArtistNames = append(trackArtistNames, getString(artist, "name"))
|
trackArtistNames = append(trackArtistNames, getString(artist, "name"))
|
||||||
}
|
}
|
||||||
artistsString := strings.Join(trackArtistNames, GetSeparator())
|
artistsString := strings.Join(trackArtistNames, separator)
|
||||||
|
|
||||||
trackDurationMs := getFloat64(getMap(trackData, "trackDuration"), "totalMilliseconds")
|
trackDurationMs := getFloat64(getMap(trackData, "trackDuration"), "totalMilliseconds")
|
||||||
durationObj := extractDuration(trackDurationMs)
|
durationObj := extractDuration(trackDurationMs)
|
||||||
@@ -1121,7 +1121,7 @@ func FilterPlaylist(data map[string]interface{}) map[string]interface{} {
|
|||||||
for _, artist := range albumArtists {
|
for _, artist := range albumArtists {
|
||||||
albumArtistNames = append(albumArtistNames, getString(artist, "name"))
|
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, "")
|
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")
|
dataMap := getMap(data, "data")
|
||||||
artistData := getMap(dataMap, "artistUnion")
|
artistData := getMap(dataMap, "artistUnion")
|
||||||
if len(artistData) == 0 {
|
if len(artistData) == 0 {
|
||||||
@@ -1424,7 +1424,7 @@ func FilterArtist(data map[string]interface{}) map[string]interface{} {
|
|||||||
return filtered
|
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")
|
dataMap := getMap(data, "data")
|
||||||
searchData := getMap(dataMap, "searchV2")
|
searchData := getMap(dataMap, "searchV2")
|
||||||
if len(searchData) == 0 {
|
if len(searchData) == 0 {
|
||||||
@@ -1514,7 +1514,7 @@ func FilterSearch(data map[string]interface{}) map[string]interface{} {
|
|||||||
for _, artist := range trackArtists {
|
for _, artist := range trackArtists {
|
||||||
trackArtistNames = append(trackArtistNames, getString(artist, "name"))
|
trackArtistNames = append(trackArtistNames, getString(artist, "name"))
|
||||||
}
|
}
|
||||||
trackArtistsString := strings.Join(trackArtistNames, GetSeparator())
|
trackArtistsString := strings.Join(trackArtistNames, separator)
|
||||||
|
|
||||||
durationString := getString(trackDuration, "formatted")
|
durationString := getString(trackDuration, "formatted")
|
||||||
|
|
||||||
@@ -1586,7 +1586,7 @@ func FilterSearch(data map[string]interface{}) map[string]interface{} {
|
|||||||
for _, artist := range albumArtists {
|
for _, artist := range albumArtists {
|
||||||
albumArtistNames = append(albumArtistNames, getString(artist, "name"))
|
albumArtistNames = append(albumArtistNames, getString(artist, "name"))
|
||||||
}
|
}
|
||||||
albumArtistsString := strings.Join(albumArtistNames, GetSeparator())
|
albumArtistsString := strings.Join(albumArtistNames, separator)
|
||||||
|
|
||||||
dateInfo := getMap(album, "date")
|
dateInfo := getMap(album, "date")
|
||||||
var year interface{}
|
var year interface{}
|
||||||
|
|||||||
@@ -11,10 +11,9 @@ import (
|
|||||||
"time"
|
"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 == "" {
|
if !useAPI || apiBaseURL == "" {
|
||||||
|
return GetFilteredSpotifyData(ctx, spotifyURL, batch, delay, separator, callback)
|
||||||
return GetFilteredSpotifyData(ctx, spotifyURL, batch, delay)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
spotifyType, id := parseSpotifyURLToTypeAndID(spotifyURL)
|
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)
|
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
|
return data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+100
-30
@@ -18,13 +18,17 @@ var (
|
|||||||
errInvalidSpotifyURL = errors.New("invalid or unsupported Spotify URL")
|
errInvalidSpotifyURL = errors.New("invalid or unsupported Spotify URL")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type MetadataCallback func(data interface{})
|
||||||
|
|
||||||
type SpotifyMetadataClient struct {
|
type SpotifyMetadataClient struct {
|
||||||
httpClient *http.Client
|
httpClient *http.Client
|
||||||
|
Separator string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSpotifyMetadataClient() *SpotifyMetadataClient {
|
func NewSpotifyMetadataClient() *SpotifyMetadataClient {
|
||||||
return &SpotifyMetadataClient{
|
return &SpotifyMetadataClient{
|
||||||
httpClient: &http.Client{Timeout: 30 * time.Second},
|
httpClient: &http.Client{Timeout: 30 * time.Second},
|
||||||
|
Separator: ", ",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -342,54 +346,57 @@ type SearchResponse struct {
|
|||||||
Playlists []SearchResult `json:"playlists"`
|
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()
|
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)
|
parsed, err := parseSpotifyURI(spotifyURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
raw, err := c.getRawSpotifyData(ctx, parsed, batch, delay)
|
raw, err := c.getRawSpotifyData(ctx, parsed, batch, delay, callback)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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 {
|
switch parsed.Type {
|
||||||
case "playlist":
|
case "playlist":
|
||||||
return c.fetchPlaylist(ctx, parsed.ID)
|
return c.fetchPlaylist(ctx, parsed.ID, callback)
|
||||||
case "album":
|
case "album":
|
||||||
return c.fetchAlbum(ctx, parsed.ID)
|
return c.fetchAlbum(ctx, parsed.ID, callback)
|
||||||
case "track":
|
case "track":
|
||||||
return c.fetchTrack(ctx, parsed.ID)
|
return c.fetchTrack(ctx, parsed.ID)
|
||||||
case "artist_discography":
|
case "artist_discography":
|
||||||
return c.fetchArtistDiscography(ctx, parsed)
|
return c.fetchArtistDiscography(ctx, parsed, callback)
|
||||||
case "artist":
|
case "artist":
|
||||||
|
|
||||||
discographyParsed := spotifyURI{Type: "artist_discography", ID: parsed.ID, DiscographyGroup: "all"}
|
discographyParsed := spotifyURI{Type: "artist_discography", ID: parsed.ID, DiscographyGroup: "all"}
|
||||||
return c.fetchArtistDiscography(ctx, discographyParsed)
|
return c.fetchArtistDiscography(ctx, discographyParsed, callback)
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unsupported Spotify type: %s", parsed.Type)
|
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) {
|
switch payload := raw.(type) {
|
||||||
case *apiPlaylistResponse:
|
case *apiPlaylistResponse:
|
||||||
return c.formatPlaylistData(payload), nil
|
return c.formatPlaylistData(payload, callback), nil
|
||||||
case *apiAlbumResponse:
|
case *apiAlbumResponse:
|
||||||
return c.formatAlbumData(payload)
|
return c.formatAlbumData(payload, callback)
|
||||||
case *apiTrackResponse:
|
case *apiTrackResponse:
|
||||||
return c.formatTrackData(payload), nil
|
return c.formatTrackData(payload), nil
|
||||||
case *apiArtistResponse:
|
case *apiArtistResponse:
|
||||||
return c.formatArtistDiscographyData(ctx, payload)
|
return c.formatArtistDiscographyData(ctx, payload, callback)
|
||||||
default:
|
default:
|
||||||
return nil, errors.New("unknown raw payload type")
|
return nil, errors.New("unknown raw payload type")
|
||||||
}
|
}
|
||||||
@@ -437,7 +444,7 @@ func (c *SpotifyMetadataClient) fetchTrack(ctx context.Context, trackID string)
|
|||||||
|
|
||||||
if albumID != "" {
|
if albumID != "" {
|
||||||
|
|
||||||
albumResponse, err := c.fetchAlbumWithClient(ctx, client, albumID)
|
albumResponse, err := c.fetchAlbumWithClient(ctx, client, albumID, nil)
|
||||||
if err == nil && albumResponse != nil {
|
if err == nil && albumResponse != nil {
|
||||||
|
|
||||||
albumJSON, _ := json.Marshal(albumResponse)
|
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)
|
jsonData, err := json.Marshal(filteredData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -497,15 +504,15 @@ func (c *SpotifyMetadataClient) fetchTrack(ctx context.Context, trackID string)
|
|||||||
return &result, nil
|
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()
|
client := NewSpotifyClient()
|
||||||
if err := client.Initialize(); err != nil {
|
if err := client.Initialize(); err != nil {
|
||||||
return nil, fmt.Errorf("failed to initialize spotify client: %w", err)
|
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{}{}
|
allItems := []interface{}{}
|
||||||
offset := 0
|
offset := 0
|
||||||
@@ -537,6 +544,15 @@ func (c *SpotifyMetadataClient) fetchAlbumWithClient(ctx context.Context, client
|
|||||||
|
|
||||||
if data == nil {
|
if data == nil {
|
||||||
data = response
|
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")
|
albumData := getMap(getMap(response, "data"), "albumUnion")
|
||||||
@@ -579,7 +595,7 @@ func (c *SpotifyMetadataClient) fetchAlbumWithClient(ctx context.Context, client
|
|||||||
tracksV2["totalCount"] = len(allItems)
|
tracksV2["totalCount"] = len(allItems)
|
||||||
}
|
}
|
||||||
|
|
||||||
filteredData := FilterAlbum(data)
|
filteredData := FilterAlbum(data, c.Separator)
|
||||||
|
|
||||||
jsonData, err := json.Marshal(filteredData)
|
jsonData, err := json.Marshal(filteredData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -594,7 +610,7 @@ func (c *SpotifyMetadataClient) fetchAlbumWithClient(ctx context.Context, client
|
|||||||
return &result, nil
|
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()
|
client := NewSpotifyClient()
|
||||||
if err := client.Initialize(); err != nil {
|
if err := client.Initialize(); err != nil {
|
||||||
return nil, fmt.Errorf("failed to initialize spotify client: %w", err)
|
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 {
|
if data == nil {
|
||||||
data = response
|
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")
|
playlistData := getMap(getMap(response, "data"), "playlistV2")
|
||||||
@@ -672,7 +697,7 @@ func (c *SpotifyMetadataClient) fetchPlaylist(ctx context.Context, playlistID st
|
|||||||
content["totalCount"] = len(allItems)
|
content["totalCount"] = len(allItems)
|
||||||
}
|
}
|
||||||
|
|
||||||
filteredData := FilterPlaylist(data)
|
filteredData := FilterPlaylist(data, c.Separator)
|
||||||
|
|
||||||
jsonData, err := json.Marshal(filteredData)
|
jsonData, err := json.Marshal(filteredData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -687,7 +712,7 @@ func (c *SpotifyMetadataClient) fetchPlaylist(ctx context.Context, playlistID st
|
|||||||
return &result, nil
|
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()
|
client := NewSpotifyClient()
|
||||||
if err := client.Initialize(); err != nil {
|
if err := client.Initialize(); err != nil {
|
||||||
return nil, fmt.Errorf("failed to initialize spotify client: %w", err)
|
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)
|
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{}{}
|
allDiscographyItems := []interface{}{}
|
||||||
offset := 0
|
offset := 0
|
||||||
limit := 50
|
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)
|
jsonData, err := json.Marshal(filteredData)
|
||||||
if err != nil {
|
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
|
var artistID, artistURL string
|
||||||
|
|
||||||
info := AlbumInfoMetadata{
|
info := AlbumInfoMetadata{
|
||||||
@@ -911,6 +946,13 @@ func (c *SpotifyMetadataClient) formatAlbumData(raw *apiAlbumResponse) (*AlbumRe
|
|||||||
ArtistURL: artistURL,
|
ArtistURL: artistURL,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if callback != nil {
|
||||||
|
callback(AlbumResponsePayload{
|
||||||
|
AlbumInfo: info,
|
||||||
|
TrackList: []AlbumTrackMetadata{},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
tracks := make([]AlbumTrackMetadata, 0, len(raw.Tracks))
|
tracks := make([]AlbumTrackMetadata, 0, len(raw.Tracks))
|
||||||
for idx, item := range raw.Tracks {
|
for idx, item := range raw.Tracks {
|
||||||
durationMS := parseDuration(item.Duration)
|
durationMS := parseDuration(item.Duration)
|
||||||
@@ -955,13 +997,17 @@ func (c *SpotifyMetadataClient) formatAlbumData(raw *apiAlbumResponse) (*AlbumRe
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if callback != nil {
|
||||||
|
callback(tracks)
|
||||||
|
}
|
||||||
|
|
||||||
return &AlbumResponsePayload{
|
return &AlbumResponsePayload{
|
||||||
AlbumInfo: info,
|
AlbumInfo: info,
|
||||||
TrackList: tracks,
|
TrackList: tracks,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *SpotifyMetadataClient) formatPlaylistData(raw *apiPlaylistResponse) PlaylistResponsePayload {
|
func (c *SpotifyMetadataClient) formatPlaylistData(raw *apiPlaylistResponse, callback MetadataCallback) PlaylistResponsePayload {
|
||||||
var info PlaylistInfoMetadata
|
var info PlaylistInfoMetadata
|
||||||
info.Tracks.Total = raw.Count
|
info.Tracks.Total = raw.Count
|
||||||
info.Followers.Total = raw.Followers
|
info.Followers.Total = raw.Followers
|
||||||
@@ -971,6 +1017,13 @@ func (c *SpotifyMetadataClient) formatPlaylistData(raw *apiPlaylistResponse) Pla
|
|||||||
info.Cover = raw.Cover
|
info.Cover = raw.Cover
|
||||||
info.Description = raw.Description
|
info.Description = raw.Description
|
||||||
|
|
||||||
|
if callback != nil {
|
||||||
|
callback(PlaylistResponsePayload{
|
||||||
|
PlaylistInfo: info,
|
||||||
|
TrackList: []AlbumTrackMetadata{},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
tracks := make([]AlbumTrackMetadata, 0, len(raw.Tracks))
|
tracks := make([]AlbumTrackMetadata, 0, len(raw.Tracks))
|
||||||
for _, item := range raw.Tracks {
|
for _, item := range raw.Tracks {
|
||||||
durationMS := parseDuration(item.Duration)
|
durationMS := parseDuration(item.Duration)
|
||||||
@@ -1015,13 +1068,17 @@ func (c *SpotifyMetadataClient) formatPlaylistData(raw *apiPlaylistResponse) Pla
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if callback != nil {
|
||||||
|
callback(tracks)
|
||||||
|
}
|
||||||
|
|
||||||
return PlaylistResponsePayload{
|
return PlaylistResponsePayload{
|
||||||
PlaylistInfo: info,
|
PlaylistInfo: info,
|
||||||
TrackList: tracks,
|
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"
|
discType := "all"
|
||||||
|
|
||||||
info := ArtistInfoMetadata{
|
info := ArtistInfoMetadata{
|
||||||
@@ -1067,7 +1124,17 @@ func (c *SpotifyMetadataClient) formatArtistDiscographyData(ctx context.Context,
|
|||||||
Images: alb.Cover,
|
Images: alb.Cover,
|
||||||
ExternalURL: fmt.Sprintf("https://open.spotify.com/album/%s", alb.ID),
|
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) {
|
go func(albumID string, albumName string) {
|
||||||
sem <- struct{}{}
|
sem <- struct{}{}
|
||||||
|
|
||||||
@@ -1081,7 +1148,7 @@ func (c *SpotifyMetadataClient) formatArtistDiscographyData(ctx context.Context,
|
|||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
albumData, err := c.fetchAlbumWithClient(ctx, sharedClient, albumID)
|
albumData, err := c.fetchAlbumWithClient(ctx, sharedClient, albumID, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Error getting tracks for album %s: %v\n", albumName, err)
|
fmt.Printf("Error getting tracks for album %s: %v\n", albumName, err)
|
||||||
resultsChan <- fetchResult{tracks: []AlbumTrackMetadata{}}
|
resultsChan <- fetchResult{tracks: []AlbumTrackMetadata{}}
|
||||||
@@ -1131,6 +1198,9 @@ func (c *SpotifyMetadataClient) formatArtistDiscographyData(ctx context.Context,
|
|||||||
IsExplicit: tr.IsExplicit,
|
IsExplicit: tr.IsExplicit,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
if callback != nil {
|
||||||
|
callback(tracks)
|
||||||
|
}
|
||||||
resultsChan <- fetchResult{tracks: tracks}
|
resultsChan <- fetchResult{tracks: tracks}
|
||||||
}(alb.ID, alb.Name)
|
}(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)
|
return nil, fmt.Errorf("failed to query search: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
filteredData := FilterSearch(data)
|
filteredData := FilterSearch(data, c.Separator)
|
||||||
|
|
||||||
jsonData, err := json.Marshal(filteredData)
|
jsonData, err := json.Marshal(filteredData)
|
||||||
if err != nil {
|
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)
|
return nil, fmt.Errorf("failed to query search: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
filteredData := FilterSearch(data)
|
filteredData := FilterSearch(data, c.Separator)
|
||||||
|
|
||||||
jsonData, err := json.Marshal(filteredData)
|
jsonData, err := json.Marshal(filteredData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ interface ArtistInfoProps {
|
|||||||
header?: string;
|
header?: string;
|
||||||
gallery?: string[];
|
gallery?: string[];
|
||||||
followers: number;
|
followers: number;
|
||||||
|
total_albums?: number;
|
||||||
genres: string[];
|
genres: string[];
|
||||||
biography?: string;
|
biography?: string;
|
||||||
verified?: boolean;
|
verified?: boolean;
|
||||||
@@ -99,6 +100,7 @@ export function ArtistInfo({ artistInfo, albumList, trackList, searchQuery, sort
|
|||||||
const [downloadingGalleryIndex, setDownloadingGalleryIndex] = useState<number | null>(null);
|
const [downloadingGalleryIndex, setDownloadingGalleryIndex] = useState<number | null>(null);
|
||||||
const [downloadingAllGallery, setDownloadingAllGallery] = useState(false);
|
const [downloadingAllGallery, setDownloadingAllGallery] = useState(false);
|
||||||
const [activeTab, setActiveTab] = useState<"albums" | "tracks" | "gallery">("albums");
|
const [activeTab, setActiveTab] = useState<"albums" | "tracks" | "gallery">("albums");
|
||||||
|
const displayedAlbumCount = artistInfo.total_albums || albumList.length;
|
||||||
const filteredAlbumGroups = useMemo(() => {
|
const filteredAlbumGroups = useMemo(() => {
|
||||||
const albumTypeMap = new Map(albumList.map(a => [a.name, a.album_type]));
|
const albumTypeMap = new Map(albumList.map(a => [a.name, a.album_type]));
|
||||||
const albumGroups = trackList.reduce((acc, track) => {
|
const albumGroups = trackList.reduce((acc, track) => {
|
||||||
@@ -330,9 +332,9 @@ export function ArtistInfo({ artistInfo, albumList, trackList, searchQuery, sort
|
|||||||
</>)}
|
</>)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2 text-sm flex-wrap text-white/90">
|
<div className="flex items-center gap-2 text-sm flex-wrap text-white/90">
|
||||||
<span>{albumList.length} {albumList.length === 1 ? "album" : "albums"}</span>
|
<span>{displayedAlbumCount.toLocaleString()} {displayedAlbumCount === 1 ? "album" : "albums"}</span>
|
||||||
<span>•</span>
|
<span>•</span>
|
||||||
<span>{trackList.length} {trackList.length === 1 ? "track" : "tracks"}</span>
|
<span>{trackList.length.toLocaleString()} {trackList.length === 1 ? "track" : "tracks"}</span>
|
||||||
{artistInfo.genres.length > 0 && (<>
|
{artistInfo.genres.length > 0 && (<>
|
||||||
<span>•</span>
|
<span>•</span>
|
||||||
<span>{artistInfo.genres.join(", ")}</span>
|
<span>{artistInfo.genres.join(", ")}</span>
|
||||||
@@ -383,9 +385,9 @@ export function ArtistInfo({ artistInfo, albumList, trackList, searchQuery, sort
|
|||||||
</>)}
|
</>)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2 text-sm flex-wrap">
|
<div className="flex items-center gap-2 text-sm flex-wrap">
|
||||||
<span>{albumList.length} {albumList.length === 1 ? "album" : "albums"}</span>
|
<span>{displayedAlbumCount.toLocaleString()} {displayedAlbumCount === 1 ? "album" : "albums"}</span>
|
||||||
<span>•</span>
|
<span>•</span>
|
||||||
<span>{trackList.length} {trackList.length === 1 ? "track" : "tracks"}</span>
|
<span>{trackList.length.toLocaleString()} {trackList.length === 1 ? "track" : "tracks"}</span>
|
||||||
{artistInfo.genres.length > 0 && (<>
|
{artistInfo.genres.length > 0 && (<>
|
||||||
<span>•</span>
|
<span>•</span>
|
||||||
<span>{artistInfo.genres.join(", ")}</span>
|
<span>{artistInfo.genres.join(", ")}</span>
|
||||||
@@ -412,7 +414,7 @@ export function ArtistInfo({ artistInfo, albumList, trackList, searchQuery, sort
|
|||||||
|
|
||||||
{activeTab === "gallery" && hasGallery && (<div className="space-y-4">
|
{activeTab === "gallery" && hasGallery && (<div className="space-y-4">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<h3 className="text-2xl font-bold">Gallery ({artistInfo.gallery!.length})</h3>
|
<h3 className="text-2xl font-bold">Gallery ({artistInfo.gallery!.length.toLocaleString()})</h3>
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<Button onClick={handleDownloadAllGallery} size="sm" variant="outline" disabled={downloadingAllGallery}>
|
<Button onClick={handleDownloadAllGallery} size="sm" variant="outline" disabled={downloadingAllGallery}>
|
||||||
|
|||||||
@@ -1,13 +1,17 @@
|
|||||||
import { useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { getSettings } from "@/lib/settings";
|
import { getSettings } from "@/lib/settings";
|
||||||
import { fetchSpotifyMetadata } from "@/lib/api";
|
import { fetchSpotifyMetadata } from "@/lib/api";
|
||||||
import { toastWithSound as toast } from "@/lib/toast-with-sound";
|
import { toastWithSound as toast } from "@/lib/toast-with-sound";
|
||||||
import { logger } from "@/lib/logger";
|
import { logger } from "@/lib/logger";
|
||||||
import { AddFetchHistory } from "../../wailsjs/go/main/App";
|
import { AddFetchHistory } from "../../wailsjs/go/main/App";
|
||||||
|
import { EventsOff, EventsOn } from "../../wailsjs/runtime/runtime";
|
||||||
import type { SpotifyMetadataResponse } from "@/types/api";
|
import type { SpotifyMetadataResponse } from "@/types/api";
|
||||||
export function useMetadata() {
|
export function useMetadata() {
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [metadata, setMetadata] = useState<SpotifyMetadataResponse | null>(null);
|
const [metadata, setMetadata] = useState<SpotifyMetadataResponse | null>(null);
|
||||||
|
const loadingToastId = useRef<string | number | null>(null);
|
||||||
|
const fetchedCount = useRef(0);
|
||||||
|
const currentName = useRef("");
|
||||||
const [showApiModal, setShowApiModal] = useState(false);
|
const [showApiModal, setShowApiModal] = useState(false);
|
||||||
const [showAlbumDialog, setShowAlbumDialog] = useState(false);
|
const [showAlbumDialog, setShowAlbumDialog] = useState(false);
|
||||||
const [selectedAlbum, setSelectedAlbum] = useState<{
|
const [selectedAlbum, setSelectedAlbum] = useState<{
|
||||||
@@ -16,6 +20,73 @@ export function useMetadata() {
|
|||||||
external_urls: string;
|
external_urls: string;
|
||||||
} | null>(null);
|
} | null>(null);
|
||||||
const [pendingArtistName, setPendingArtistName] = useState<string | null>(null);
|
const [pendingArtistName, setPendingArtistName] = useState<string | null>(null);
|
||||||
|
useEffect(() => {
|
||||||
|
if (loading) {
|
||||||
|
fetchedCount.current = 0;
|
||||||
|
currentName.current = "";
|
||||||
|
loadingToastId.current = toast.silentInfo("fetching metadata...", {
|
||||||
|
duration: Infinity,
|
||||||
|
description: "please wait while we retrieve the information"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (loadingToastId.current) {
|
||||||
|
toast.dismiss(loadingToastId.current);
|
||||||
|
loadingToastId.current = null;
|
||||||
|
}
|
||||||
|
}, [loading]);
|
||||||
|
useEffect(() => {
|
||||||
|
const handler = (data: any) => {
|
||||||
|
if (!data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Array.isArray(data)) {
|
||||||
|
fetchedCount.current += data.length;
|
||||||
|
if (loadingToastId.current && currentName.current) {
|
||||||
|
toast.silentInfo(`fetching tracks for ${currentName.current.toLowerCase()}...`, {
|
||||||
|
id: loadingToastId.current,
|
||||||
|
description: `${fetchedCount.current.toLocaleString()} tracks fetched`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const baseInfo = data;
|
||||||
|
const name = "artist_info" in baseInfo ? baseInfo.artist_info.name :
|
||||||
|
"album_info" in baseInfo ? baseInfo.album_info.name :
|
||||||
|
"playlist_info" in baseInfo ? (baseInfo.playlist_info.name || baseInfo.playlist_info.owner.name) : "";
|
||||||
|
if (name) {
|
||||||
|
currentName.current = name;
|
||||||
|
if (loadingToastId.current) {
|
||||||
|
toast.silentInfo(`fetching tracks for ${name.toLowerCase()}...`, {
|
||||||
|
id: loadingToastId.current,
|
||||||
|
description: `${fetchedCount.current.toLocaleString()} tracks fetched`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setMetadata(prev => {
|
||||||
|
if (Array.isArray(data)) {
|
||||||
|
if (!prev || !("track_list" in prev)) {
|
||||||
|
return prev;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
track_list: [...prev.track_list, ...data]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (prev && "track_list" in prev && prev.track_list.length > 0) {
|
||||||
|
return prev;
|
||||||
|
}
|
||||||
|
const baseInfo = data;
|
||||||
|
if (!("track_list" in baseInfo)) {
|
||||||
|
baseInfo.track_list = [];
|
||||||
|
}
|
||||||
|
return baseInfo;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
EventsOn("metadata-stream", handler);
|
||||||
|
return () => EventsOff("metadata-stream");
|
||||||
|
}, []);
|
||||||
const getUrlType = (url: string): string => {
|
const getUrlType = (url: string): string => {
|
||||||
if (url.includes("/track/"))
|
if (url.includes("/track/"))
|
||||||
return "track";
|
return "track";
|
||||||
|
|||||||
@@ -5,41 +5,49 @@ import { getSettings } from "./settings";
|
|||||||
const toastStyle = {
|
const toastStyle = {
|
||||||
className: "font-mono lowercase",
|
className: "font-mono lowercase",
|
||||||
};
|
};
|
||||||
|
type ToastData = Parameters<typeof toast.success>[1];
|
||||||
const isSfxEnabled = () => getSettings().sfxEnabled;
|
const isSfxEnabled = () => getSettings().sfxEnabled;
|
||||||
export const toastWithSound = {
|
export const toastWithSound = {
|
||||||
success: (message: string, data?: any) => {
|
success: (message: string, data?: ToastData) => {
|
||||||
const msg = message.toLowerCase();
|
const msg = message.toLowerCase();
|
||||||
logger.success(msg);
|
logger.success(msg);
|
||||||
if (isSfxEnabled())
|
if (isSfxEnabled())
|
||||||
playSuccessSound();
|
playSuccessSound();
|
||||||
return toast.success(msg, { ...toastStyle, ...data });
|
return toast.success(msg, { ...toastStyle, ...data });
|
||||||
},
|
},
|
||||||
error: (message: string, data?: any) => {
|
error: (message: string, data?: ToastData) => {
|
||||||
const msg = message.toLowerCase();
|
const msg = message.toLowerCase();
|
||||||
logger.error(msg);
|
logger.error(msg);
|
||||||
if (isSfxEnabled())
|
if (isSfxEnabled())
|
||||||
playErrorSound();
|
playErrorSound();
|
||||||
return toast.error(msg, { ...toastStyle, ...data });
|
return toast.error(msg, { ...toastStyle, ...data });
|
||||||
},
|
},
|
||||||
warning: (message: string, data?: any) => {
|
warning: (message: string, data?: ToastData) => {
|
||||||
const msg = message.toLowerCase();
|
const msg = message.toLowerCase();
|
||||||
logger.warning(msg);
|
logger.warning(msg);
|
||||||
if (isSfxEnabled())
|
if (isSfxEnabled())
|
||||||
playWarningSound();
|
playWarningSound();
|
||||||
return toast.warning(msg, { ...toastStyle, ...data });
|
return toast.warning(msg, { ...toastStyle, ...data });
|
||||||
},
|
},
|
||||||
info: (message: string, data?: any) => {
|
info: (message: string, data?: ToastData) => {
|
||||||
const msg = message.toLowerCase();
|
const msg = message.toLowerCase();
|
||||||
logger.info(msg);
|
logger.info(msg);
|
||||||
if (isSfxEnabled())
|
if (isSfxEnabled())
|
||||||
playInfoSound();
|
playInfoSound();
|
||||||
return toast.info(msg, { ...toastStyle, ...data });
|
return toast.info(msg, { ...toastStyle, ...data });
|
||||||
},
|
},
|
||||||
message: (message: string, data?: any) => {
|
message: (message: string, data?: ToastData) => {
|
||||||
const msg = message.toLowerCase();
|
const msg = message.toLowerCase();
|
||||||
logger.info(msg);
|
logger.info(msg);
|
||||||
if (isSfxEnabled())
|
if (isSfxEnabled())
|
||||||
playInfoSound();
|
playInfoSound();
|
||||||
return toast(msg, { ...toastStyle, ...data });
|
return toast(msg, { ...toastStyle, ...data });
|
||||||
},
|
},
|
||||||
|
silentInfo: (message: string, data?: ToastData) => {
|
||||||
|
const msg = message.toLowerCase();
|
||||||
|
logger.info(msg);
|
||||||
|
return toast.info(msg, { ...toastStyle, ...data });
|
||||||
|
},
|
||||||
|
dismiss: (id?: string | number) => toast.dismiss(id),
|
||||||
|
toast: toast,
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user