This commit is contained in:
parent
e4f23c6805
commit
9a34c4b05b
1
go.mod
1
go.mod
@ -8,6 +8,7 @@ require (
|
|||||||
github.com/charmbracelet/bubbles v0.20.0
|
github.com/charmbracelet/bubbles v0.20.0
|
||||||
github.com/charmbracelet/bubbletea v1.1.2
|
github.com/charmbracelet/bubbletea v1.1.2
|
||||||
github.com/charmbracelet/lipgloss v0.13.1
|
github.com/charmbracelet/lipgloss v0.13.1
|
||||||
|
github.com/go-resty/resty/v2 v2.16.2
|
||||||
github.com/lmittmann/tint v1.0.5
|
github.com/lmittmann/tint v1.0.5
|
||||||
github.com/rivo/tview v0.0.0-20241016194538-c5e4fb24af13
|
github.com/rivo/tview v0.0.0-20241016194538-c5e4fb24af13
|
||||||
github.com/urfave/cli/v3 v3.0.0-alpha9.1
|
github.com/urfave/cli/v3 v3.0.0-alpha9.1
|
||||||
|
4
go.sum
4
go.sum
@ -94,6 +94,8 @@ github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
|||||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
|
github.com/go-resty/resty/v2 v2.16.2 h1:CpRqTjIzq/rweXUt9+GxzzQdlkqMdt8Lm/fuK/CAbAg=
|
||||||
|
github.com/go-resty/resty/v2 v2.16.2/go.mod h1:0fHAoK7JoBy/Ch36N8VFeMsK7xQOHhvWaC3iOktwmIU=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
@ -418,6 +420,8 @@ golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
|||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
|
||||||
|
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
|
@ -18,18 +18,18 @@ import (
|
|||||||
func (c *Commander) Radio() error {
|
func (c *Commander) Radio() error {
|
||||||
currentSong, err := c.Client().PlayerCurrentlyPlaying(c.Context)
|
currentSong, err := c.Client().PlayerCurrentlyPlaying(c.Context)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("failed to get current song: %w", err)
|
||||||
}
|
}
|
||||||
if currentSong.Item != nil {
|
if currentSong.Item != nil {
|
||||||
return c.RadioGivenSong(currentSong.Item.SimpleTrack, currentSong.Progress)
|
return c.RadioGivenSong(currentSong.Item.SimpleTrack, currentSong.Progress)
|
||||||
}
|
}
|
||||||
_, err = c.activateDevice()
|
_, err = c.activateDevice()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("failed to activate device: %w", err)
|
||||||
}
|
}
|
||||||
tracks, err := c.Client().CurrentUsersTracks(c.Context, spotify.Limit(10))
|
tracks, err := c.Client().CurrentUsersTracks(c.Context, spotify.Limit(10))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("failed to get current users tracks: %w", err)
|
||||||
}
|
}
|
||||||
return c.RadioGivenSong(tracks.Tracks[rand.Intn(len(tracks.Tracks))].SimpleTrack, 0)
|
return c.RadioGivenSong(tracks.Tracks[rand.Intn(len(tracks.Tracks))].SimpleTrack, 0)
|
||||||
}
|
}
|
||||||
@ -178,7 +178,7 @@ func (c *Commander) RadioGivenSong(song spotify.SimpleTrack, pos spotify.Numeric
|
|||||||
}
|
}
|
||||||
recomendations, err := c.Client().GetRecommendations(c.Context, seed, &spotify.TrackAttributes{}, spotify.Limit(99))
|
recomendations, err := c.Client().GetRecommendations(c.Context, seed, &spotify.TrackAttributes{}, spotify.Limit(99))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("failed to get recommendations: %w", err)
|
||||||
}
|
}
|
||||||
recomendationIds := []spotify.ID{}
|
recomendationIds := []spotify.ID{}
|
||||||
for _, song := range recomendations.Tracks {
|
for _, song := range recomendations.Tracks {
|
||||||
@ -218,25 +218,9 @@ func (c *Commander) RadioGivenSong(song spotify.SimpleTrack, pos spotify.Numeric
|
|||||||
if pos != 0 {
|
if pos != 0 {
|
||||||
pos = pos + spotify.Numeric(delay)
|
pos = pos + spotify.Numeric(delay)
|
||||||
}
|
}
|
||||||
err = c.Client().PlayOpt(c.Context, &spotify.PlayOptions{
|
err = c.PlayRadio(radioPlaylist, int(pos))
|
||||||
PlaybackContext: &radioPlaylist.URI,
|
|
||||||
PositionMs: pos,
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if isNoActiveError(err) {
|
return err
|
||||||
deviceID, err := c.activateDevice()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = c.Client().PlayOpt(c.Context, &spotify.PlayOptions{
|
|
||||||
PlaybackContext: &radioPlaylist.URI,
|
|
||||||
DeviceID: &deviceID,
|
|
||||||
PositionMs: pos,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
err = c.Client().Repeat(c.Context, "context")
|
err = c.Client().Repeat(c.Context, "context")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -274,6 +258,30 @@ func (c *Commander) RadioGivenSong(song spotify.SimpleTrack, pos spotify.Numeric
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Commander) PlayRadio(radioPlaylist *spotify.FullPlaylist, pos int) error {
|
||||||
|
err := c.Client().PlayOpt(c.Context, &spotify.PlayOptions{
|
||||||
|
PlaybackContext: &radioPlaylist.URI,
|
||||||
|
PositionMs: spotify.Numeric(pos),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
if isNoActiveError(err) {
|
||||||
|
deviceID, err := c.activateDevice()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = c.Client().PlayOpt(c.Context, &spotify.PlayOptions{
|
||||||
|
PlaybackContext: &radioPlaylist.URI,
|
||||||
|
DeviceID: &deviceID,
|
||||||
|
PositionMs: spotify.Numeric(pos),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Commander) ClearRadio() error {
|
func (c *Commander) ClearRadio() error {
|
||||||
radioPlaylist, db, err := c.GetRadioPlaylist("")
|
radioPlaylist, db, err := c.GetRadioPlaylist("")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -581,23 +589,9 @@ func (c *Commander) RadioGivenList(songs []spotify.ID, name string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = c.Client().PlayOpt(c.Context, &spotify.PlayOptions{
|
err = c.PlayRadio(radioPlaylist, 0)
|
||||||
PlaybackContext: &radioPlaylist.URI,
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if isNoActiveError(err) {
|
return err
|
||||||
deviceID, err := c.activateDevice()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = c.Client().PlayOpt(c.Context, &spotify.PlayOptions{
|
|
||||||
PlaybackContext: &radioPlaylist.URI,
|
|
||||||
DeviceID: &deviceID,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
for i := 0; i < 4; i++ {
|
for i := 0; i < 4; i++ {
|
||||||
id := rand.Intn(len(recomendationIds)-2) + 1
|
id := rand.Intn(len(recomendationIds)-2) + 1
|
||||||
|
180
src/listenbrainz/listenbrainz.go
Normal file
180
src/listenbrainz/listenbrainz.go
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
package listenbrainz
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ListenBrainz struct {
|
||||||
|
client *resty.Client
|
||||||
|
|
||||||
|
labs *resty.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewListenBrainz(
|
||||||
|
Endpoint string,
|
||||||
|
ApiKey string,
|
||||||
|
) *ListenBrainz {
|
||||||
|
c := resty.New().SetBaseURL(Endpoint)
|
||||||
|
if ApiKey != "" {
|
||||||
|
c = c.SetHeader("Authorization", "Token "+ApiKey)
|
||||||
|
}
|
||||||
|
return &ListenBrainz{
|
||||||
|
client: c,
|
||||||
|
labs: resty.New().SetBaseURL("https://labs.api.listenbrainz.org"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type RadioApiResponse struct {
|
||||||
|
Payload struct {
|
||||||
|
Feedback []string `json:"feedback"`
|
||||||
|
Jspf struct {
|
||||||
|
Playlist struct {
|
||||||
|
Annotation string `json:"annotation"`
|
||||||
|
Creator string `json:"creator"`
|
||||||
|
Extension struct {
|
||||||
|
HTTPSMusicbrainzOrgDocJspfPlaylist struct {
|
||||||
|
Public bool `json:"public"`
|
||||||
|
} `json:"https://musicbrainz.org/doc/jspf#playlist"`
|
||||||
|
} `json:"extension"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Track []ApiTrack `json:"track"`
|
||||||
|
} `json:"playlist"`
|
||||||
|
} `json:"jspf"`
|
||||||
|
} `json:"payload"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ApiTrack struct {
|
||||||
|
Album string `json:"album"`
|
||||||
|
Creator string `json:"creator"`
|
||||||
|
Duration int `json:"duration,omitempty"`
|
||||||
|
Extension struct {
|
||||||
|
HTTPSMusicbrainzOrgDocJspfTrack struct {
|
||||||
|
ArtistIdentifiers []string `json:"artist_identifiers"`
|
||||||
|
ReleaseIdentifier string `json:"release_identifier"`
|
||||||
|
} `json:"https://musicbrainz.org/doc/jspf#track"`
|
||||||
|
} `json:"extension"`
|
||||||
|
Identifier []string `json:"identifier"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *ListenBrainz) RequestRadio(ctx context.Context, req *RadioRequest) (*RadioTracksResponse, error) {
|
||||||
|
var res RadioApiResponse
|
||||||
|
resp, err := o.client.R().
|
||||||
|
SetResult(&res).
|
||||||
|
SetQueryParam("prompt", req.Prompt).
|
||||||
|
SetQueryParam("mode", req.Prompt).
|
||||||
|
Get("/1/explore/lb-radio")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case resp.StatusCode() == 200:
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("radio request code %d: %s", resp.StatusCode(), resp.Status())
|
||||||
|
}
|
||||||
|
tracks := res.Payload.Jspf.Playlist.Track
|
||||||
|
return &RadioTracksResponse{
|
||||||
|
Tracks: tracks,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type RadioTracksResponse struct {
|
||||||
|
Tracks []ApiTrack
|
||||||
|
}
|
||||||
|
|
||||||
|
type RadioRequest struct {
|
||||||
|
Prompt string
|
||||||
|
Mode string `json:"mode"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MatchTracksParams struct {
|
||||||
|
Tracks []ApiTrack
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *ListenBrainz) MatchTracks(ctx context.Context, params *MatchTracksParams) error {
|
||||||
|
// first try to get the mbid from the recording id
|
||||||
|
o.labs.R().Get("/")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type MatchedTrack struct {
|
||||||
|
Mbid string
|
||||||
|
SpotifyId string
|
||||||
|
Strategy string
|
||||||
|
}
|
||||||
|
|
||||||
|
type TrackMatch struct {
|
||||||
|
RecordingMbid string `json:"recording_mbid"`
|
||||||
|
ArtistName string `json:"artist_name"`
|
||||||
|
ReleaseName string `json:"release_name"`
|
||||||
|
TrackName string `json:"track_name"`
|
||||||
|
SpotifyTrackIds []string `json:"spotify_track_ids"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var ErrNoMatch = fmt.Errorf("no match")
|
||||||
|
|
||||||
|
func (o *ListenBrainz) MatchTrack(ctx context.Context, track *ApiTrack) (*MatchedTrack, error) {
|
||||||
|
// refuse to match a track with no identifiers
|
||||||
|
if len(track.Identifier) == 0 {
|
||||||
|
return nil, fmt.Errorf("%w: no identifier", ErrNoMatch)
|
||||||
|
}
|
||||||
|
var matches []TrackMatch
|
||||||
|
// there are mbids, so try to get the first one that is valid
|
||||||
|
resp, err := o.labs.R().
|
||||||
|
SetResult(&matches).
|
||||||
|
SetQueryParam("recording_mbid", track.Identifier[0]).
|
||||||
|
Get("/spotify-id-from-mbid/json")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if resp.StatusCode() != 200 {
|
||||||
|
return nil, fmt.Errorf("labs request code %d: %s", resp.StatusCode(), resp.Status())
|
||||||
|
}
|
||||||
|
if len(matches) == 0 {
|
||||||
|
return nil, fmt.Errorf("%w: no mbid", ErrNoMatch)
|
||||||
|
}
|
||||||
|
// for each match, see if ther eis a spotify id, and if there is, we are done!
|
||||||
|
for _, match := range matches {
|
||||||
|
if len(match.SpotifyTrackIds) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return &MatchedTrack{
|
||||||
|
Mbid: match.RecordingMbid,
|
||||||
|
SpotifyId: match.SpotifyTrackIds[0],
|
||||||
|
Strategy: "exact-match",
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
for _, match := range matches {
|
||||||
|
var submatch []TrackMatch
|
||||||
|
resp, err := o.labs.R().
|
||||||
|
SetResult(&submatch).
|
||||||
|
SetQueryParam("artist_name", match.ArtistName).
|
||||||
|
SetQueryParam("release_name", match.ReleaseName).
|
||||||
|
SetQueryParam("track_name", match.TrackName).
|
||||||
|
Get("/spotify-id-from-track/json")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if resp.StatusCode() != 200 {
|
||||||
|
return nil, fmt.Errorf("labs request code %d: %s", resp.StatusCode(), resp.Status())
|
||||||
|
}
|
||||||
|
if len(submatch) == 0 {
|
||||||
|
return nil, fmt.Errorf("%w: no tracks found", ErrNoMatch)
|
||||||
|
}
|
||||||
|
for _, submatch := range submatch {
|
||||||
|
if len(submatch.SpotifyTrackIds) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return &MatchedTrack{
|
||||||
|
Mbid: match.RecordingMbid,
|
||||||
|
SpotifyId: submatch.SpotifyTrackIds[0],
|
||||||
|
Strategy: "track-match",
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("%w: no tracks found", ErrNoMatch)
|
||||||
|
}
|
79
src/listenbrainz/radio.go
Normal file
79
src/listenbrainz/radio.go
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
package listenbrainz
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RadioPromptBuilder struct {
|
||||||
|
px RadioParameters
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *RadioPromptBuilder) Add(name string, values ...string) *RadioPromptBuilder {
|
||||||
|
o.px = append(o.px, RadioParameter{
|
||||||
|
Name: name,
|
||||||
|
Values: values,
|
||||||
|
})
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *RadioPromptBuilder) AddParameter(p RadioParameter) *RadioPromptBuilder {
|
||||||
|
o.px = append(o.px, p)
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *RadioPromptBuilder) AddWithCount(name string, count int, values ...string) *RadioPromptBuilder {
|
||||||
|
o.px = append(o.px, RadioParameter{
|
||||||
|
Name: name,
|
||||||
|
Count: count,
|
||||||
|
Values: values,
|
||||||
|
})
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
func (o *RadioPromptBuilder) AddWithOption(name string, option string, values ...string) *RadioPromptBuilder {
|
||||||
|
o.px = append(o.px, RadioParameter{
|
||||||
|
Name: name,
|
||||||
|
Option: option,
|
||||||
|
Values: values,
|
||||||
|
})
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *RadioPromptBuilder) String() string {
|
||||||
|
val, _ := o.px.MarshalText()
|
||||||
|
return string(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
type RadioParameters []RadioParameter
|
||||||
|
|
||||||
|
func (r RadioParameters) MarshalText() ([]byte, error) {
|
||||||
|
o := &bytes.Buffer{}
|
||||||
|
for pidx, v := range r {
|
||||||
|
o.WriteString(v.Name)
|
||||||
|
o.WriteString(":(")
|
||||||
|
for idx, vv := range v.Values {
|
||||||
|
o.WriteString(vv)
|
||||||
|
if len(v.Values) > 1 && idx != len(v.Values)-1 {
|
||||||
|
o.WriteString(",")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
o.WriteString(")")
|
||||||
|
if v.Count > 0 {
|
||||||
|
o.WriteString(":" + strconv.Itoa(v.Count))
|
||||||
|
}
|
||||||
|
if v.Option != "" {
|
||||||
|
o.WriteString(":" + v.Option)
|
||||||
|
}
|
||||||
|
if len(r) > 1 && pidx != len(r)-1 {
|
||||||
|
o.WriteString(" ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return o.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type RadioParameter struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Values []string `json:"value"`
|
||||||
|
Count int `json:"count"`
|
||||||
|
Option string `json:"options"`
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user