diff --git a/internal/api/api.go b/internal/api/api.go index 226239a..8569a0b 100644 --- a/internal/api/api.go +++ b/internal/api/api.go @@ -32,6 +32,10 @@ func Run(ctx *gctx.Context, client *spotify.Client, args []string) error { return commands.Shuffle(ctx, client) case "radio": return commands.Radio(ctx, client) + case "clearradio": + return commands.ClearRadio(ctx, client) + case "refillradio": + return commands.RefillRadio(ctx, client) case "tracks": return tui.DisplayList(ctx, client) case "status": diff --git a/internal/auth/auth.go b/internal/auth/auth.go index bacd474..bd870dc 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -18,9 +18,10 @@ import ( ) var ( - auth *spotifyauth.Authenticator - ch = make(chan *spotify.Client) - state = "abc123" + auth *spotifyauth.Authenticator + ch = make(chan *spotify.Client) + state = "abc123" + configDir, _ = os.UserConfigDir() ) func GetClient(ctx *gctx.Context) (*spotify.Client, error) { @@ -48,9 +49,8 @@ func GetClient(ctx *gctx.Context) (*spotify.Client, error) { spotifyauth.ScopeStreaming, ), ) - homdir, _ := os.UserHomeDir() - if _, err := os.Stat(filepath.Join(homdir, ".config/gospt/auth.json")); err == nil { - authFile, err := os.Open(filepath.Join(homdir, ".config/gospt/auth.json")) + if _, err := os.Stat(filepath.Join(configDir, "gospt/auth.json")); err == nil { + authFile, err := os.Open(filepath.Join(configDir, "gospt/auth.json")) if err != nil { return nil, err } @@ -74,8 +74,7 @@ func GetClient(ctx *gctx.Context) (*spotify.Client, error) { if err != nil { panic(err.Error()) } - homdir, _ := os.UserHomeDir() - err = ioutil.WriteFile(filepath.Join(homdir, ".config/gospt/auth.json"), out, 0o644) + err = ioutil.WriteFile(filepath.Join(configDir, "gospt/auth.json"), out, 0o644) if err != nil { panic("FAILED TO SAVE AUTH") } @@ -84,8 +83,7 @@ func GetClient(ctx *gctx.Context) (*spotify.Client, error) { if err != nil { panic(err.Error()) } - homdir, _ := os.UserHomeDir() - err = ioutil.WriteFile(filepath.Join(homdir, ".config/gospt/auth.json"), out, 0o644) + err = ioutil.WriteFile(filepath.Join(configDir, "gospt/auth.json"), out, 0o644) if err != nil { panic("FAILED TO SAVE AUTH") } @@ -131,8 +129,7 @@ func completeAuth(w http.ResponseWriter, r *http.Request) { if err != nil { panic(err.Error()) } - homdir, _ := os.UserHomeDir() - err = ioutil.WriteFile(filepath.Join(homdir, ".config/gospt/auth.json"), out, 0o644) + err = ioutil.WriteFile(filepath.Join(configDir, "gospt/auth.json"), out, 0o644) if err != nil { panic("FAILED TO SAVE AUTH") } diff --git a/internal/commands/commands.go b/internal/commands/commands.go index bea3927..4937472 100644 --- a/internal/commands/commands.go +++ b/internal/commands/commands.go @@ -79,7 +79,14 @@ func QueueSong(ctx *gctx.Context, client *spotify.Client, id spotify.ID) error { } func Radio(ctx *gctx.Context, client *spotify.Client) error { - rand.Seed(time.Now().Unix()) + err := ClearRadio(ctx, client) + if err != nil { + return err + } + radioPlaylist, err := GetRadioPlaylist(ctx, client) + if err != nil { + return err + } current_song, err := client.PlayerCurrentlyPlaying(ctx) if err != nil { return err @@ -91,27 +98,122 @@ func Radio(ctx *gctx.Context, client *spotify.Client) error { if err != nil { return err } + recomendationIds := []spotify.ID{} for _, song := range recomendations.Tracks { - fmt.Println(song.Name) + recomendationIds = append(recomendationIds, song.ID) } - fmt.Println("QUEUED") + _, err = client.AddTracksToPlaylist(ctx, radioPlaylist.ID, recomendationIds...) + if err != nil { + return err + } + client.PlayOpt(ctx, &spotify.PlayOptions{ + PlaybackContext: &radioPlaylist.URI, + PlaybackOffset: &spotify.PlaybackOffset{ + Position: 0, + }, + }) + err = client.Repeat(ctx, "context") + if err != nil { + return err + } + rand.Seed(time.Now().Unix()) for i := 0; i < 4; i++ { - seed_track := recomendations.Tracks[3] + id := rand.Intn(len(recomendationIds)-2) + 1 seed := spotify.Seeds{ - Tracks: []spotify.ID{seed_track.ID}, + Tracks: []spotify.ID{recomendationIds[id]}, } - recomendations, err = client.GetRecommendations(ctx, seed, &spotify.TrackAttributes{}, spotify.Limit(100)) + additional_recs, err := client.GetRecommendations(ctx, seed, &spotify.TrackAttributes{}, spotify.Limit(100)) if err != nil { return err } - for _, song := range recomendations.Tracks { - fmt.Println(song.Name) + fmt.Println(len(additional_recs.Tracks)) + additionalRecsIds := []spotify.ID{} + for _, song := range additional_recs.Tracks { + additionalRecsIds = append(additionalRecsIds, song.ID) + } + fmt.Println(len(additionalRecsIds)) + _, err = client.AddTracksToPlaylist(ctx, radioPlaylist.ID, additionalRecsIds...) + if err != nil { + return err } - fmt.Println("QUEUED") } return nil } +func RefillRadio(ctx *gctx.Context, client *spotify.Client) error { + status, err := client.PlayerCurrentlyPlaying(ctx) + if err != nil { + return err + } + fmt.Println("PLAYING", status.PlaybackContext.URI) + to_remove := []spotify.ID{} + radioPlaylist, err := GetRadioPlaylist(ctx, client) + found := false + page := 0 + for !found { + tracks, err := client.GetPlaylistItems(ctx, radioPlaylist.ID, spotify.Limit(50), spotify.Offset(page*50)) + if err != nil { + return err + } + for _, track := range tracks.Items { + fmt.Println("CHECKING", track.Track.Track.Name) + if track.Track.Track.ID == status.Item.ID { + found = true + break + } + to_remove = append(to_remove, track.Track.Track.ID) + } + page++ + } + recomendationIds := []spotify.ID{} + if len(to_remove) > 0 { + fmt.Println("REPLENISHING", len(to_remove), "SONGS") + _, err = client.RemoveTracksFromPlaylist(ctx, radioPlaylist.ID, to_remove...) + if err != nil { + return err + } + current_song, err := client.PlayerCurrentlyPlaying(ctx) + if err != nil { + return err + } + seed := spotify.Seeds{ + Tracks: []spotify.ID{current_song.Item.ID}, + } + recomendations, err := client.GetRecommendations(ctx, seed, &spotify.TrackAttributes{}, spotify.Limit(100)) + if err != nil { + return err + } + for idx, song := range recomendations.Tracks { + if idx >= len(to_remove) { + break + } + recomendationIds = append(recomendationIds, song.ID) + } + _, err = client.AddTracksToPlaylist(ctx, radioPlaylist.ID, recomendationIds...) + if err != nil { + return err + } + } + + return nil +} + +func ClearRadio(ctx *gctx.Context, client *spotify.Client) error { + radioPlaylist, err := GetRadioPlaylist(ctx, client) + if err != nil { + return err + } + err = client.UnfollowPlaylist(ctx, radioPlaylist.ID) + if err != nil { + return err + } + configDir, _ := os.UserConfigDir() + os.Remove(filepath.Join(configDir, "gospt/radio.json")) + client.Pause(ctx) + fmt.Println("Radio emptied") + return nil +} + func Devices(ctx *gctx.Context, client *spotify.Client) error { devices, err := client.PlayerDevices(ctx) if err != nil { @@ -266,3 +368,60 @@ func queueWithTransfer(ctx *gctx.Context, client *spotify.Client, track_id spoti ctx.Println("Playing!") return nil } + +func activateDevice(ctx *gctx.Context, client *spotify.Client) error { + configDir, _ := os.UserConfigDir() + deviceFile, err := os.Open(filepath.Join(configDir, "gospt/device.json")) + if err != nil { + return err + } + defer deviceFile.Close() + deviceValue, err := ioutil.ReadAll(deviceFile) + if err != nil { + return err + } + var device *spotify.PlayerDevice + err = json.Unmarshal(deviceValue, &device) + if err != nil { + return err + } + err = client.TransferPlayback(ctx, device.ID, true) + if err != nil { + return err + } + return nil +} + +func GetRadioPlaylist(ctx *gctx.Context, client *spotify.Client) (*spotify.FullPlaylist, error) { + configDir, _ := os.UserConfigDir() + if _, err := os.Stat(filepath.Join(configDir, "gospt/radio.json")); err == nil { + playlistFile, err := os.Open(filepath.Join(configDir, "gospt/radio.json")) + if err != nil { + return nil, err + } + defer playlistFile.Close() + playlistValue, err := ioutil.ReadAll(playlistFile) + if err != nil { + return nil, err + } + var playlist *spotify.FullPlaylist + err = json.Unmarshal(playlistValue, &playlist) + if err != nil { + return nil, err + } + return playlist, nil + } + playlist, err := client.CreatePlaylistForUser(ctx, ctx.UserId, "gosptRADIO", "This is an automanaged playlist for the custom radio of gospt", false, false) + if err != nil { + return nil, err + } + out, err := json.MarshalIndent(playlist, "", " ") + if err != nil { + return nil, err + } + err = ioutil.WriteFile(filepath.Join(configDir, "gospt/radio.json"), out, 0o644) + if err != nil { + return nil, err + } + return playlist, nil +} diff --git a/internal/gctx/context.go b/internal/gctx/context.go index 92c675a..7c44062 100644 --- a/internal/gctx/context.go +++ b/internal/gctx/context.go @@ -9,7 +9,8 @@ import ( type Context struct { context.Context *log.Logger - Debug *log.Logger + Debug *log.Logger + UserId string } func NewContext(ctx context.Context) *Context { @@ -17,6 +18,7 @@ func NewContext(ctx context.Context) *Context { ctx, log.New(os.Stdout, "LOG:", 0), log.New(os.Stdout, "DEBUG:", 1), + "", } return out } diff --git a/main.go b/main.go index 8b180e1..86fecf2 100644 --- a/main.go +++ b/main.go @@ -27,6 +27,11 @@ func main() { if err != nil { panic(err.Error()) } + currentUser, err := client.CurrentUser(ctx) + if err != nil { + panic(err.Error()) + } + ctx.UserId = currentUser.ID err = api.Run(ctx, client, os.Args[1:]) if err != nil { fmt.Println(err)