From 9b8ad87eca80a7962013149fc7aae36935dc063f Mon Sep 17 00:00:00 2001 From: Superredstone Date: Thu, 12 Feb 2026 18:30:18 +0100 Subject: [PATCH] feat: first working version --- .gitignore | 1 + go.mod | 4 +++ go.sum | 13 +++++++ main.go | 41 ++++++++++++++++++++-- pkg/download.go | 92 +++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 149 insertions(+), 2 deletions(-) create mode 100644 pkg/download.go diff --git a/.gitignore b/.gitignore index 59c6a6a..54a5a0b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ lib/ app/ +downloads/ diff --git a/go.mod b/go.mod index 81e55b6..2b06d36 100644 --- a/go.mod +++ b/go.mod @@ -9,13 +9,17 @@ require ( github.com/go-flac/flacvorbis v0.2.0 // indirect github.com/go-flac/go-flac v1.0.0 // indirect github.com/icza/bitio v1.1.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/leaanthony/slicer v1.6.0 // indirect github.com/leaanthony/u v1.1.1 // indirect github.com/mewkiz/flac v1.0.13 // indirect github.com/mewkiz/pkg v0.0.0-20250417130911-3f050ff8c56d // indirect github.com/mewpkg/term v0.0.0-20241026122259-37a80af23985 // indirect github.com/pquerna/otp v1.5.0 // indirect + github.com/spf13/cobra v1.10.2 // indirect + github.com/spf13/pflag v1.0.10 // indirect github.com/ulikunitz/xz v0.5.15 // indirect + github.com/urfave/cli/v3 v3.6.2 // indirect github.com/wailsapp/wails/v2 v2.11.0 // indirect go.etcd.io/bbolt v1.4.3 // indirect golang.org/x/sys v0.30.0 // indirect diff --git a/go.sum b/go.sum index de08ceb..f444d55 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,7 @@ github.com/bogem/id3v2/v2 v2.1.4 h1:CEwe+lS2p6dd9UZRlPc1zbFNIha2mb2qzT1cCEoNWoI= github.com/bogem/id3v2/v2 v2.1.4/go.mod h1:l+gR8MZ6rc9ryPTPkX77smS5Me/36gxkMgDayZ9G1vY= github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI= github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-flac/flacpicture v0.3.0 h1:LkmTxzFLIynwfhHiZsX0s8xcr3/u33MzvV89u+zOT8I= github.com/go-flac/flacpicture v0.3.0/go.mod h1:DPbrzVYQ3fJcvSgLFp9HXIrEQEdfdk/+m0nQCzwodZI= @@ -12,6 +13,8 @@ github.com/go-flac/go-flac v1.0.0/go.mod h1:WnZhcpmq4u1UdZMNn9LYSoASpWOCMOoxXxcW github.com/icza/bitio v1.1.0 h1:ysX4vtldjdi3Ygai5m1cWy4oLkhWTAi+SyO6HC8L9T0= github.com/icza/bitio v1.1.0/go.mod h1:0jGnlLAx8MKMr9VGnn/4YrvZiprkvBelsVIbA9Jjr9A= github.com/icza/mighty v0.0.0-20180919140131-cfd07d671de6/go.mod h1:xQig96I1VNBDIWGCdTt54nHt6EeI639SmHycLYL7FkA= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/leaanthony/slicer v1.6.0 h1:1RFP5uiPJvT93TAHi+ipd3NACobkW53yUiBqZheE/Js= github.com/leaanthony/slicer v1.6.0/go.mod h1:o/Iz29g7LN0GqH3aMjWAe90381nyZlDNquK+mtH2Fj8= github.com/leaanthony/u v1.1.1 h1:TUFjwDGlNX+WuwVEzDqQwC2lOv0P4uhTQw7CMFdiK7M= @@ -26,15 +29,24 @@ github.com/mewpkg/term v0.0.0-20241026122259-37a80af23985/go.mod h1:uiPmbdUbdt1N github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pquerna/otp v1.5.0 h1:NMMR+WrmaqXU4EzdGJEE1aUUI0AMRzsp96fFFWNPwxs= github.com/pquerna/otp v1.5.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= +github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY= github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/urfave/cli/v3 v3.6.2 h1:lQuqiPrZ1cIz8hz+HcrG0TNZFxU70dPZ3Yl+pSrH9A8= +github.com/urfave/cli/v3 v3.6.2/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso= github.com/wailsapp/wails/v2 v2.11.0 h1:seLacV8pqupq32IjS4Y7V8ucab0WZwtK6VvUVxSBtqQ= github.com/wailsapp/wails/v2 v2.11.0/go.mod h1:jrf0ZaM6+GBc1wRmXsM8cIvzlg0karYin3erahI4+0k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo= go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -65,3 +77,4 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/main.go b/main.go index cdb89ee..c9d0d05 100644 --- a/main.go +++ b/main.go @@ -1,13 +1,50 @@ package main import ( - "fmt" + "context" + "log" + "os" "github.com/Superredstone/spotiflac-cli/app" + "github.com/Superredstone/spotiflac-cli/lib" + "github.com/Superredstone/spotiflac-cli/pkg" + "github.com/urfave/cli/v3" ) func main() { + var song_url string application := app.NewApp() + startup() - + cmd := &cli.Command{ + Name: "spotiflac-cli", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "download", + Usage: "Download a song/playlist", + Destination: &song_url, + }, + }, + Action: func(ctx context.Context, cmd *cli.Command) error { + pkg.Download(application, song_url) + + return nil + }, + } + if err := cmd.Run(context.Background(), os.Args); err != nil { + log.Fatal(err) + shutdown() + } + + shutdown() +} + +func startup() { + if err := lib.InitHistoryDB("SpotiFLAC"); err != nil { + log.Fatal("Failed to init history DB: %v\n", err) + } +} + +func shutdown() { + lib.CloseHistoryDB() } diff --git a/pkg/download.go b/pkg/download.go new file mode 100644 index 0000000..9a6e008 --- /dev/null +++ b/pkg/download.go @@ -0,0 +1,92 @@ +package pkg + +import ( + "encoding/json" + "fmt" + "strconv" + + "github.com/Superredstone/spotiflac-cli/app" +) + +type MetadataSong struct { + Track MetadataTrack `json:"track"` +} + +type MetadataTrack struct { + SpotifyID string `json:"spotify_id"` + Artists string `json:"artists"` + Name string `json:"name"` + AlbumName string `json:"album_name"` + AlbumArtist string `json:"album_artist"` + DurationMS int `json:"duration_ms"` + Images string `json:"images"` + ReleaseDate string `json:"release_date"` + TrackNumber int `json:"track_number"` + TotalTracks int `json:"total_tracks"` + DiscNumber int `json:"disc_number"` + TotalDiscs int `json:"total_discs"` + ExternalURLs string `json:"external_urls"` + Copyright string `json:"copyright"` + Publisher string `json:"publisher"` + Plays string `json:"plays"` + IsExplicit bool `json:"is_explicit"` +} + +type MetadataPlaylist struct { + TrackList []MetadataTrack `json:"track_list"` +} + +func Download(application *app.App, url string) { + metadata, err := GetMetadata[MetadataPlaylist](application, url) + if err != nil { + fmt.Println("Unable to fetch metadata for song " + url) + } + + trackListSize := strconv.Itoa(len(metadata.TrackList)) + for idx, track := range metadata.TrackList { + fmt.Println("[" + strconv.Itoa(idx+1) + "/" + trackListSize + "] " + track.Name + " - " + track.Artists) + + downloadRequest := app.DownloadRequest{ + Service: "tidal", + TrackName: track.Name, + ArtistName: track.Artists, + AlbumName: track.AlbumName, + AlbumArtist: track.AlbumArtist, + ReleaseDate: track.ReleaseDate, + OutputDir: "downloads/", + SpotifyID: track.SpotifyID, + } + application.DownloadTrack(downloadRequest) + } +} + +func GetMetadata[T MetadataPlaylist | MetadataSong](application *app.App, url string) (T, error) { + var result T + + metadata, err := GetGenericMetadata(application, url) + if err != nil { + return result, nil + } + + err = json.Unmarshal([]byte(metadata), &result) + if err != nil { + return result, nil + } + + return result, nil +} + +func GetGenericMetadata(application *app.App, url string) (string, error) { + metadataRequest := app.SpotifyMetadataRequest{ + URL: url, + Delay: 0, + Timeout: 5, + } + + metadata, err := application.GetSpotifyMetadata(metadataRequest) + if err != nil { + return metadata, err + } + + return metadata, nil +}