From 1026868e019b1dcd5ba34e8b19d77999b8da4830 Mon Sep 17 00:00:00 2001 From: Superredstone Date: Thu, 19 Feb 2026 13:18:47 +0100 Subject: [PATCH] feat: download implementation --- lib/download.go | 61 ++++++++++++++++++++++++++++++++++++------------- lib/types.go | 48 +++++++++++++++++++++----------------- lib/utils.go | 38 ++++++++++++++++++++++++++++++ main.go | 3 ++- 4 files changed, 112 insertions(+), 38 deletions(-) diff --git a/lib/download.go b/lib/download.go index e070e10..941ed43 100644 --- a/lib/download.go +++ b/lib/download.go @@ -1,5 +1,11 @@ package lib +import ( + "io" + "net/http" + "os" +) + const ( DEFAULT_DOWNLOAD_SERVICE = "tidal" DEFAULT_DOWNLOAD_OUTPUT_FOLDER = "." @@ -17,10 +23,9 @@ type DownloadRequest struct { SpotifyID string } -func (app *App) Download(url string, outputFolder string, serviceString string) error { - if outputFolder == "" { - outputFolder = DEFAULT_DOWNLOAD_OUTPUT_FOLDER - } +func (app *App) Download(url string, outputFile string, serviceString string, quality string) error { + var downloadUrl string + var fileName string if serviceString == "" { serviceString = DEFAULT_DOWNLOAD_SERVICE @@ -33,15 +38,9 @@ func (app *App) Download(url string, outputFolder string, serviceString string) switch urlType { case UrlTypeTrack: - // metadata, err := app.GetTrackMetadata(url) - // if err != nil { - // return err - // } - - // println(metadata.Data.TrackUnion.Id) songlink, err := app.ConvertSongUrl(url) if err != nil { - return err + return err } tidalId, err := app.GetTidalIdFromSonglink(songlink) @@ -49,17 +48,47 @@ func (app *App) Download(url string, outputFolder string, serviceString string) return err } - // err = app.DownloadFromTidal(tidalId) - url, err = app.GetTidalDownloadUrl(tidalId, "LOSSLESS") + downloadUrl, err = app.GetTidalDownloadUrl(tidalId, quality) if err != nil { return err } - println(url) + } + + metadata, err := app.GetTrackMetadata(url) + if err != nil { + return err + } + + outputFile, err = BuildFileOutput(outputFile, fileName, metadata) + if err != nil { + return err + } + + err = app.DownloadFromUrl(downloadUrl, outputFile) + if err != nil { + return err } return nil } -func (app *App) DownloadTrack(dr DownloadRequest) (bool, error) { - return false, nil +func (app *App) DownloadFromUrl(url string, outputFilePath string) error { + outputFile, err := os.Create(outputFilePath) + if err != nil { + return err + } + defer outputFile.Close() + + res, err := http.Get(url) + if err != nil { + return err + } + defer res.Body.Close() + + _, err = io.Copy(outputFile, res.Body) + if err != nil { + return err + } + + return nil } diff --git a/lib/types.go b/lib/types.go index 0fecf31..0045098 100644 --- a/lib/types.go +++ b/lib/types.go @@ -59,7 +59,7 @@ type VideoAssociations struct { TotalCount int64 `json:"totalCount"` } -type AssociationsV3 struct { +type Associations struct { AudioAssociations AudioAssociations `json:"audioAssociations"` VideoAssociations VideoAssociations `json:"videoAssociations"` } @@ -73,12 +73,18 @@ type Duration struct { } type FirstArtist struct { - Items []map[string]interface{} `json:"items"` - TotalCount int64 `json:"totalCount"` + Items []ArtistItems `json:"items"` + TotalCount int64 `json:"totalCount"` +} + +type ArtistItems struct { + Profile struct { + Name string `json:"name"` + } `json:"profile"` } type OtherArtists struct { - Items []interface{} `json:"items"` + Items []ArtistItems `json:"items"` } type Playability struct { @@ -87,23 +93,23 @@ type Playability struct { } type TrackUnion struct { - TypeName string `json:"__typename"` - AlbumOfTrack AlbumOfTrack `json:"albumOfTrack"` - AssociationsV3 AssociationsV3 `json:"associationsV3"` - ContentRating ContentRating `json:"contentRating"` - Duration Duration `json:"duration"` - FirstArtist FirstArtist `json:"firstArtist"` - Id string `json:"id"` - MediaType string `json:"mediaType"` - Name string `json:"name"` - OtherArtists OtherArtists `json:"otherArtists"` - Playability Playability `json:"playability"` - Playcount string `json:"playcount"` - Saved bool `json:"saved"` - SharingInfo interface{} `json:"sharingInfo"` - TrackNumber int64 `json:"trackNumber"` - Uri string `json:"uri"` - VisualIdentity interface{} `json:"visualIdentity"` + TypeName string `json:"__typename"` + AlbumOfTrack AlbumOfTrack `json:"albumOfTrack"` + Associations Associations `json:"associationsV3"` + ContentRating ContentRating `json:"contentRating"` + Duration Duration `json:"duration"` + FirstArtist FirstArtist `json:"firstArtist"` + Id string `json:"id"` + MediaType string `json:"mediaType"` + Name string `json:"name"` + OtherArtists OtherArtists `json:"otherArtists"` + Playability Playability `json:"playability"` + Playcount string `json:"playcount"` + Saved bool `json:"saved"` + SharingInfo interface{} `json:"sharingInfo"` + TrackNumber int64 `json:"trackNumber"` + Uri string `json:"uri"` + VisualIdentity interface{} `json:"visualIdentity"` } type Data struct { diff --git a/lib/utils.go b/lib/utils.go index 1c4006a..56b9cee 100644 --- a/lib/utils.go +++ b/lib/utils.go @@ -2,6 +2,8 @@ package lib import ( "errors" + "fmt" + "path" "strings" ) @@ -38,3 +40,39 @@ func ParseTrackId(url string) (string, error) { return tmp2[0], nil } + +func BuildFileName(metadata TrackMetadata) (string, error) { + var result string + var artists string + + firstArtistLen := len(metadata.Data.TrackUnion.FirstArtist.Items) + if firstArtistLen == 0 { + return result, errors.New("What? This should never happen.") + } + artists = metadata.Data.TrackUnion.FirstArtist.Items[firstArtistLen-1].Profile.Name + + for _, artist := range(metadata.Data.TrackUnion.OtherArtists.Items) { + artists += ", " + artist.Profile.Name + } + + result = fmt.Sprintf("%s - %s", metadata.Data.TrackUnion.Name, artists) + + return result, nil +} + +func BuildFileOutput(outputFile string, fileName string, metadata TrackMetadata) (string, error) { + var result string + + fileName, err := BuildFileName(metadata) + if err != nil { + return result, err + } + + if outputFile == "" { + result = path.Join(DEFAULT_DOWNLOAD_OUTPUT_FOLDER, fileName) + } else { + result = outputFile + } + + return result, nil +} diff --git a/main.go b/main.go index 99e97d9..23e469c 100644 --- a/main.go +++ b/main.go @@ -41,7 +41,8 @@ func main() { }, Action: func(ctx context.Context, cmd *cli.Command) error { song_url := cmd.Args().First() - err := app.Download(song_url, outputFolder, service) + quality := "LOSSLESS" + err := app.Download(song_url, outputFolder, service, quality) return err }, },