This commit is contained in:
afkarxyz
2026-01-27 06:16:05 +07:00
parent e04f6e4fdd
commit 25233349b9
38 changed files with 2492 additions and 1682 deletions
+122 -72
View File
@@ -47,6 +47,7 @@ type TrackMetadata struct {
Publisher string `json:"publisher,omitempty"`
Plays string `json:"plays,omitempty"`
PreviewURL string `json:"preview_url,omitempty"`
IsExplicit bool `json:"is_explicit,omitempty"`
}
type ArtistSimple struct {
@@ -79,6 +80,7 @@ type AlbumTrackMetadata struct {
Plays string `json:"plays,omitempty"`
Status string `json:"status,omitempty"`
PreviewURL string `json:"preview_url,omitempty"`
IsExplicit bool `json:"is_explicit,omitempty"`
}
type TrackResponse struct {
@@ -198,6 +200,7 @@ type apiTrackResponse struct {
Medium string `json:"medium"`
Large string `json:"large"`
} `json:"cover"`
IsExplicit bool `json:"is_explicit"`
}
type apiAlbumResponse struct {
@@ -208,12 +211,13 @@ type apiAlbumResponse struct {
ReleaseDate string `json:"releaseDate"`
Count int `json:"count"`
Tracks []struct {
ID string `json:"id"`
Name string `json:"name"`
Artists string `json:"artists"`
ArtistIds []string `json:"artistIds"`
Duration string `json:"duration"`
Plays string `json:"plays"`
ID string `json:"id"`
Name string `json:"name"`
Artists string `json:"artists"`
ArtistIds []string `json:"artistIds"`
Duration string `json:"duration"`
Plays string `json:"plays"`
IsExplicit bool `json:"is_explicit"`
} `json:"tracks"`
}
@@ -240,6 +244,7 @@ type apiPlaylistResponse struct {
AlbumArtist string `json:"albumArtist"`
AlbumID string `json:"albumId"`
Duration string `json:"duration"`
IsExplicit bool `json:"is_explicit"`
} `json:"tracks"`
}
@@ -261,11 +266,13 @@ type apiArtistResponse struct {
Gallery []string `json:"gallery"`
Discography struct {
All []struct {
ID string `json:"id"`
Name string `json:"name"`
Cover string `json:"cover"`
Date string `json:"date"`
Year int `json:"year"`
ID string `json:"id"`
Name string `json:"name"`
Cover string `json:"cover"`
Date string `json:"date"`
Year int `json:"year"`
TotalTracks int `json:"total_tracks"`
Type string `json:"type"`
} `json:"all"`
Total int `json:"total"`
} `json:"discography"`
@@ -274,12 +281,13 @@ type apiArtistResponse struct {
type apiSearchResponse struct {
Results struct {
Tracks []struct {
ID string `json:"id"`
Name string `json:"name"`
Artists string `json:"artists"`
Album string `json:"album"`
Duration string `json:"duration"`
Cover string `json:"cover"`
ID string `json:"id"`
Name string `json:"name"`
Artists string `json:"artists"`
Album string `json:"album"`
Duration string `json:"duration"`
Cover string `json:"cover"`
IsExplicit bool `json:"is_explicit"`
} `json:"tracks"`
Albums []struct {
ID string `json:"id"`
@@ -320,6 +328,7 @@ type SearchResult struct {
Duration int `json:"duration_ms,omitempty"`
TotalTracks int `json:"total_tracks,omitempty"`
Owner string `json:"owner,omitempty"`
IsExplicit bool `json:"is_explicit,omitempty"`
}
type SearchResponse struct {
@@ -464,6 +473,10 @@ func (c *SpotifyMetadataClient) fetchAlbum(ctx context.Context, albumID string)
if err := client.Initialize(); err != nil {
return nil, fmt.Errorf("failed to initialize spotify client: %w", err)
}
return c.fetchAlbumWithClient(ctx, client, albumID)
}
func (c *SpotifyMetadataClient) fetchAlbumWithClient(ctx context.Context, client *SpotifyClient, albumID string) (*apiAlbumResponse, error) {
allItems := []interface{}{}
offset := 0
@@ -727,6 +740,12 @@ func (c *SpotifyMetadataClient) fetchArtistDiscography(ctx context.Context, pars
}
offset += limit
select {
case <-ctx.Done():
return nil, ctx.Err()
default:
}
}
albumsItems := []interface{}{}
@@ -843,6 +862,7 @@ func (c *SpotifyMetadataClient) formatTrackData(raw *apiTrackResponse) TrackResp
Copyright: raw.Copyright,
Publisher: raw.Album.Label,
Plays: raw.Plays,
IsExplicit: raw.IsExplicit,
}
return TrackResponse{
@@ -904,6 +924,7 @@ func (c *SpotifyMetadataClient) formatAlbumData(raw *apiAlbumResponse) (*AlbumRe
ArtistURL: artistURL,
ArtistsData: artistsData,
Plays: item.Plays,
IsExplicit: item.IsExplicit,
})
}
@@ -964,6 +985,7 @@ func (c *SpotifyMetadataClient) formatPlaylistData(raw *apiPlaylistResponse) Pla
ArtistsData: artistsData,
Plays: item.Plays,
Status: item.Status,
IsExplicit: item.IsExplicit,
})
}
@@ -995,79 +1017,105 @@ func (c *SpotifyMetadataClient) formatArtistDiscographyData(ctx context.Context,
albumList := make([]DiscographyAlbumMetadata, 0, len(raw.Discography.All))
allTracks := make([]AlbumTrackMetadata, 0)
type fetchResult struct {
tracks []AlbumTrackMetadata
err error
}
resultsChan := make(chan fetchResult, len(raw.Discography.All))
sem := make(chan struct{}, 5)
sharedClient := NewSpotifyClient()
if err := sharedClient.Initialize(); err != nil {
return nil, fmt.Errorf("failed to initialize shared spotify client: %w", err)
}
for _, alb := range raw.Discography.All {
select {
case <-ctx.Done():
return &ArtistDiscographyPayload{
ArtistInfo: info,
AlbumList: albumList,
TrackList: allTracks,
}, ctx.Err()
default:
}
albumList = append(albumList, DiscographyAlbumMetadata{
ID: alb.ID,
Name: alb.Name,
AlbumType: "album",
AlbumType: alb.Type,
ReleaseDate: alb.Date,
TotalTracks: 0,
TotalTracks: alb.TotalTracks,
Artists: raw.Name,
Images: alb.Cover,
ExternalURL: fmt.Sprintf("https://open.spotify.com/album/%s", alb.ID),
})
albumData, err := c.fetchAlbum(ctx, alb.ID)
if err != nil {
fmt.Printf("Error getting tracks for album %s: %v\n", alb.Name, err)
continue
}
go func(albumID string, albumName string) {
sem <- struct{}{}
for idx, tr := range albumData.Tracks {
durationMS := parseDuration(tr.Duration)
trackNumber := idx + 1
time.Sleep(100 * time.Millisecond)
defer func() { <-sem }()
var artistID, artistURL string
if len(tr.ArtistIds) > 0 {
artistID = tr.ArtistIds[0]
artistURL = fmt.Sprintf("https://open.spotify.com/artist/%s", artistID)
select {
case <-ctx.Done():
resultsChan <- fetchResult{err: ctx.Err()}
return
default:
}
artistsData := make([]ArtistSimple, 0, len(tr.ArtistIds))
for _, id := range tr.ArtistIds {
artistsData = append(artistsData, ArtistSimple{
ID: id,
Name: "",
ExternalURL: fmt.Sprintf("https://open.spotify.com/artist/%s", id),
albumData, err := c.fetchAlbumWithClient(ctx, sharedClient, albumID)
if err != nil {
fmt.Printf("Error getting tracks for album %s: %v\n", albumName, err)
resultsChan <- fetchResult{tracks: []AlbumTrackMetadata{}}
return
}
tracks := make([]AlbumTrackMetadata, 0, len(albumData.Tracks))
for idx, tr := range albumData.Tracks {
durationMS := parseDuration(tr.Duration)
trackNumber := idx + 1
var artistID, artistURL string
if len(tr.ArtistIds) > 0 {
artistID = tr.ArtistIds[0]
artistURL = fmt.Sprintf("https://open.spotify.com/artist/%s", artistID)
}
artistsData := make([]ArtistSimple, 0, len(tr.ArtistIds))
for _, id := range tr.ArtistIds {
artistsData = append(artistsData, ArtistSimple{
ID: id,
Name: "",
ExternalURL: fmt.Sprintf("https://open.spotify.com/artist/%s", id),
})
}
tracks = append(tracks, AlbumTrackMetadata{
SpotifyID: tr.ID,
Artists: tr.Artists,
Name: tr.Name,
AlbumName: albumData.Name,
AlbumArtist: raw.Name,
AlbumType: "album",
DurationMS: durationMS,
Images: albumData.Cover,
ReleaseDate: albumData.ReleaseDate,
TrackNumber: trackNumber,
TotalTracks: albumData.Count,
DiscNumber: 1,
ExternalURL: fmt.Sprintf("https://open.spotify.com/track/%s", tr.ID),
ISRC: tr.ID,
AlbumID: albumID,
AlbumURL: fmt.Sprintf("https://open.spotify.com/album/%s", albumID),
ArtistID: artistID,
ArtistURL: artistURL,
ArtistsData: artistsData,
Plays: tr.Plays,
IsExplicit: tr.IsExplicit,
})
}
resultsChan <- fetchResult{tracks: tracks}
}(alb.ID, alb.Name)
}
allTracks = append(allTracks, AlbumTrackMetadata{
SpotifyID: tr.ID,
Artists: tr.Artists,
Name: tr.Name,
AlbumName: albumData.Name,
AlbumArtist: albumData.Artists,
AlbumType: "album",
DurationMS: durationMS,
Images: albumData.Cover,
ReleaseDate: albumData.ReleaseDate,
TrackNumber: trackNumber,
TotalTracks: albumData.Count,
DiscNumber: 1,
ExternalURL: fmt.Sprintf("https://open.spotify.com/track/%s", tr.ID),
ISRC: tr.ID,
AlbumID: alb.ID,
AlbumURL: fmt.Sprintf("https://open.spotify.com/album/%s", alb.ID),
ArtistID: artistID,
ArtistURL: artistURL,
ArtistsData: artistsData,
Plays: tr.Plays,
})
for i := 0; i < len(raw.Discography.All); i++ {
res := <-resultsChan
if res.err != nil {
return nil, res.err
}
allTracks = append(allTracks, res.tracks...)
}
return &ArtistDiscographyPayload{
@@ -1246,6 +1294,7 @@ func (c *SpotifyMetadataClient) Search(ctx context.Context, query string, limit
Images: item.Cover,
ExternalURL: fmt.Sprintf("https://open.spotify.com/track/%s", item.ID),
Duration: parseDuration(item.Duration),
IsExplicit: item.IsExplicit,
})
}
@@ -1359,6 +1408,7 @@ func (c *SpotifyMetadataClient) SearchByType(ctx context.Context, query string,
Images: item.Cover,
ExternalURL: fmt.Sprintf("https://open.spotify.com/track/%s", item.ID),
Duration: parseDuration(item.Duration),
IsExplicit: item.IsExplicit,
})
}
case "album":