diff --git a/lib/download.go b/lib/download.go index bc6921a..a6b74b2 100644 --- a/lib/download.go +++ b/lib/download.go @@ -2,9 +2,13 @@ package lib import ( "errors" + "fmt" "io" "net/http" "os" + "path" + "strconv" + "time" ) const ( @@ -36,14 +40,13 @@ func (app *App) Download(url string, outputFile string, service string, quality switch urlType { case UrlTypeTrack: - if err := app.DownloadTrack(url, outputFile, service, quality); err != nil { + if err := app.DownloadTrack(url, outputFile, service, quality, false); err != nil { return err } return nil case UrlTypePlaylist: - _, err := app.GetPlaylistMetadata(url) - if err != nil { + if err := app.DownloadPlaylist(url, outputFile, service, quality); err != nil { return err } @@ -53,7 +56,48 @@ func (app *App) Download(url string, outputFile string, service string, quality return errors.New("Invalid URL type.") } -func (app *App) DownloadTrack(url string, outputFile, service string, quality string) error { +func (app *App) DownloadPlaylist(url string, outputFile string, service string, quality string) error { + playlist, err := app.GetPlaylistMetadata(url) + if err != nil { + return err + } + + var urls []string + for _, item := range playlist.Data.Playlist.Content.Items { + url, err := SpotifyUriToLink(item.Item.Data.Uri) + if err != nil { + return err + } + + urls = append(urls, url) + } + + trackListSize := len(urls) + for idx, url := range urls { + metadata, err := app.GetTrackMetadata(url) + if err != nil { + return err + } + + artists, err := GetArtists(metadata) + if err != nil { + return err + } + + fmt.Println("[" + strconv.Itoa(idx+1) + "/" + strconv.Itoa(trackListSize) + "] " + metadata.Data.TrackUnion.Name + " - " + artists) + + if err := app.DownloadTrack(url, outputFile+"/", service, quality, true); err != nil { + return err + } + + // Avoid getting rate limited + time.Sleep(800 * time.Millisecond) + } + + return nil +} + +func (app *App) DownloadTrack(url string, outputFile string, service string, quality string, downloadFromPlaylist bool) error { songlink, err := app.ConvertSongUrl(url) if err != nil { return err @@ -79,9 +123,17 @@ func (app *App) DownloadTrack(url string, outputFile, service string, quality st return err } - outputFile, err = BuildFileOutput(outputFile, extension, metadata) - if err != nil { - return err + if downloadFromPlaylist { + fileName, err := BuildFileName(metadata, extension) + if err != nil { + return err + } + outputFile = path.Join(outputFile, fileName) + } else { + outputFile, err = BuildFileOutput(outputFile, extension, metadata) + if err != nil { + return err + } } fileExists, err := FileExists(outputFile) diff --git a/lib/types.go b/lib/types.go index 9715208..5acc756 100644 --- a/lib/types.go +++ b/lib/types.go @@ -127,7 +127,19 @@ type TrackMetadata struct { type PlaylistMetadata struct { Data struct { Playlist struct { - Name string `json:"name"` + Name string `json:"name"` + Content struct { + Items []struct { + Item struct { + Data struct { + IdentityTrait struct { + Name string `json:"name"` + } `json:"identityTrait"` + Uri string `json:"uri"` + } `json:"data"` + } `json:"itemV3"` + } `json:"items"` + } `json:"content"` } `json:"playlistV2"` } `json:"data"` } diff --git a/lib/utils.go b/lib/utils.go index d47b497..63cc5eb 100644 --- a/lib/utils.go +++ b/lib/utils.go @@ -15,6 +15,10 @@ const ( UrlTypePlaylist ) +const ( + BASE_SPOTIFY_TRACK_URL = "https://open.spotify.com/track/" +) + func ParseUrlType(url string) (UrlType, error) { if strings.Contains(url, "https://open.spotify.com/track") { return UrlTypeTrack, nil @@ -131,3 +135,13 @@ func (app *App) InitSpotifyClient() error { return nil } + +func SpotifyUriToLink(uri string) (string, error) { + spotifyId := strings.Split(uri, ":") + + if len(spotifyId) != 3 { + return "", errors.New("Invalid URI parsed.") + } + + return BASE_SPOTIFY_TRACK_URL + spotifyId[2], nil +}