package vlc import ( "errors" "net/url" "path/filepath" "strconv" "strings" "github.com/goccy/go-json" ) // Status contains information related to the VLC instance status. Use parseStatus to parse the response from a // status.go function. type Status struct { // TODO: The Status structure is still a work in progress Fullscreen bool `json:"fullscreen"` Stats Stats `json:"stats"` AspectRatio string `json:"aspectratio"` AudioDelay float64 `json:"audiodelay"` APIVersion uint `json:"apiversion"` CurrentPlID uint `json:"currentplid"` Time uint `json:"time"` Volume uint `json:"volume"` Length uint `json:"length"` Random bool `json:"random"` AudioFilters map[string]string `json:"audiofilters"` Rate float64 `json:"rate"` VideoEffects VideoEffects `json:"videoeffects"` State string `json:"state"` Loop bool `json:"loop"` Version string `json:"version"` Position float64 `json:"position"` Information Information `json:"information"` Repeat bool `json:"repeat"` SubtitleDelay float64 `json:"subtitledelay"` Equalizer []Equalizer `json:"equalizer"` } // Stats contains certain statistics of a VLC instance. A Stats variable is included in Status type Stats struct { InputBitRate float64 `json:"inputbitrate"` SentBytes uint `json:"sentbytes"` LosABuffers uint `json:"lostabuffers"` AveragedEMuxBitrate float64 `json:"averagedemuxbitrate"` ReadPackets uint `json:"readpackets"` DemuxReadPackets uint `json:"demuxreadpackets"` LostPictures uint `json:"lostpictures"` DisplayedPictures uint `json:"displayedpictures"` SentPackets uint `json:"sentpackets"` DemuxReadBytes uint `json:"demuxreadbytes"` DemuxBitRate float64 `json:"demuxbitrate"` PlayedABuffers uint `json:"playedabuffers"` DemuxDiscontinuity uint `json:"demuxdiscontinuity"` DecodeAudio uint `json:"decodedaudio"` SendBitRate float64 `json:"sendbitrate"` ReadBytes uint `json:"readbytes"` AverageInputBitRate float64 `json:"averageinputbitrate"` DemuxCorrupted uint `json:"demuxcorrupted"` DecodedVideo uint `json:"decodedvideo"` } // VideoEffects contains the current video effects configuration. A VideoEffects variable is included in Status type VideoEffects struct { Hue int `json:"hue"` Saturation int `json:"saturation"` Contrast int `json:"contrast"` Brightness int `json:"brightness"` Gamma int `json:"gamma"` } // Information contains information related to the item currently being played. It is also part of Status type Information struct { Chapter int `json:"chapter"` // TODO: Chapters definition might need to be changed Chapters []interface{} `json:"chapters"` Title int `json:"title"` // TODO: Category definition might need to be updated/modified Category map[string]struct { Filename string `json:"filename"` Codec string `json:"Codec"` Channels string `json:"Channels"` BitsPerSample string `json:"Bits_per_sample"` Type string `json:"Type"` SampleRate string `json:"Sample_rate"` } `json:"category"` Titles []interface{} `json:"titles"` } // Equalizer contains information related to the equalizer configuration. An Equalizer variable is included in Status type Equalizer struct { Presets map[string]string `json:"presets"` Bands map[string]string `json:"bands"` Preamp int `json:"preamp"` } // parseStatus parses GetStatus() responses to Status struct. func parseStatus(statusResponse string) (status *Status, err error) { err = json.Unmarshal([]byte(statusResponse), &status) if err != nil { return } return status, nil } // GetStatus returns a Status object containing information of the instances' status func (vlc *VLC) GetStatus() (status *Status, err error) { // Make request var response string response, err = vlc.RequestMaker("/requests/status.json") // Error handling if err != nil { return } // Parse response to Status status, err = parseStatus(response) return } // Play playlist item with given id. If id is omitted, play last active item func (vlc *VLC) Play(itemID ...int) (err error) { // Check variadic arguments and form urlSegment if len(itemID) > 1 { err = errors.New("please provide only up to one ID") return } urlSegment := "/requests/status.json?command=pl_play" if len(itemID) == 1 { urlSegment = urlSegment + "&id=" + strconv.Itoa(itemID[0]) } _, err = vlc.RequestMaker(urlSegment) return } // Pause toggles pause: If current state was 'stop', play item with given id, if no id specified, play current item. // If no current item, play the first item in the playlist. func (vlc *VLC) Pause(itemID ...int) (err error) { // Check variadic arguments and form urlSegment if len(itemID) > 1 { err = errors.New("please provide only up to one ID") return } urlSegment := "/requests/status.json?command=pl_pause" if len(itemID) == 1 { urlSegment = urlSegment + "&id=" + strconv.Itoa(itemID[0]) } _, err = vlc.RequestMaker(urlSegment) return } // Stop stops playback func (vlc *VLC) Stop() (err error) { _, err = vlc.RequestMaker("/requests/status.json?command=pl_stop") return } // Next skips to the next playlist item func (vlc *VLC) Next() (err error) { _, err = vlc.RequestMaker("/requests/status.json?command=pl_next") return } // Previous goes back to the previous playlist item func (vlc *VLC) Previous() (err error) { _, err = vlc.RequestMaker("/requests/status.json?command=pl_previous") return } // EmptyPlaylist empties the playlist func (vlc *VLC) EmptyPlaylist() (err error) { _, err = vlc.RequestMaker("/requests/status.json?command=pl_empty") return } // ToggleLoop toggles Random Playback func (vlc *VLC) ToggleLoop() (err error) { _, err = vlc.RequestMaker("/requests/status.json?command=pl_random") return } // ToggleRepeat toggles Playback Looping func (vlc *VLC) ToggleRepeat() (err error) { _, err = vlc.RequestMaker("/requests/status.json?command=pl_loop") return } // ToggleRandom toggles Repeat func (vlc *VLC) ToggleRandom() (err error) { _, err = vlc.RequestMaker("/requests/status.json?command=pl_repeat") return } // ToggleFullscreen toggles Fullscreen mode func (vlc *VLC) ToggleFullscreen() (err error) { _, err = vlc.RequestMaker("/requests/status.json?command=fullscreen") return } func escapeInput(input string) string { if strings.HasPrefix(input, "http") { return url.QueryEscape(input) } else { input = filepath.FromSlash(input) return strings.ReplaceAll(url.QueryEscape(input), "+", "%20") } } // AddAndPlay adds a URI to the playlist and starts playback. // The option field is optional and can have the values: noaudio, novideo func (vlc *VLC) AddAndPlay(uri string, option ...string) error { // Check variadic arguments and form urlSegment if len(option) > 1 { return errors.New("please provide only one option") } urlSegment := "/requests/status.json?command=in_play&input=" + escapeInput(uri) if len(option) == 1 { if (option[0] != "noaudio") && (option[0] != "novideo") { return errors.New("invalid option") } urlSegment = urlSegment + "&option=" + option[0] } _, err := vlc.RequestMaker(urlSegment) return err } // Add adds a URI to the playlist func (vlc *VLC) Add(uri string) (err error) { _, err = vlc.RequestMaker("/requests/status.json?command=in_enqueue&input=" + escapeInput(uri)) return } // AddSubtitle adds a subtitle from URI to currently playing file func (vlc *VLC) AddSubtitle(uri string) (err error) { _, err = vlc.RequestMaker("/requests/status.json?command=addsubtitle&val=" + escapeInput(uri)) return } // Resume resumes playback if paused, else does nothing func (vlc *VLC) Resume() (err error) { _, err = vlc.RequestMaker("/requests/status.json?command=pl_forceresume") return } // ForcePause pauses playback, does nothing if already paused func (vlc *VLC) ForcePause() (err error) { _, err = vlc.RequestMaker("/requests/status.json?command=pl_forcepause") return } // Delete deletes an item with given id from playlist func (vlc *VLC) Delete(id int) (err error) { _, err = vlc.RequestMaker("/requests/status.json?command=pl_delete&id=" + strconv.Itoa(id)) return } // AudioDelay sets Audio Delay in seconds func (vlc *VLC) AudioDelay(delay float64) (err error) { _, err = vlc.RequestMaker("/requests/status.json?command=audiodelay&val=" + strconv.FormatFloat(delay, 'f', -1, 64)) return } // SubDelay sets Subtitle Delay in seconds func (vlc *VLC) SubDelay(delay float64) (err error) { _, err = vlc.RequestMaker("/requests/status.json?command=subdelay&val=" + strconv.FormatFloat(delay, 'f', -1, 64)) return } // PlaybackRate sets Playback Rate. Must be > 0 func (vlc *VLC) PlaybackRate(rate float64) (err error) { if rate <= 0 { err = errors.New("rate must be greater than 0") return } _, err = vlc.RequestMaker("/requests/status.json?command=rate&val=" + strconv.FormatFloat(rate, 'f', -1, 64)) return } // AspectRatio sets aspect ratio. Must be one of the following values. Any other value will reset aspect ratio to default. // Valid aspect ratio values: 1:1 , 4:3 , 5:4 , 16:9 , 16:10 , 221:100 , 235:100 , 239:100 func (vlc *VLC) AspectRatio(ratio string) (err error) { _, err = vlc.RequestMaker("/requests/status.json?command=aspectratio&val=" + ratio) return } // Sort sorts playlist using sort mode and order . // If id=0 then items will be sorted in normal order, if id=1 they will be sorted in reverse order. // A non exhaustive list of sort modes: 0 Id, 1 Name, 3 Author, 5 Random, 7 Track number. func (vlc *VLC) Sort(id int, val int) (err error) { if (id != 0) && (id != 1) { err = errors.New("sorting order must be 0 or 1") return } _, err = vlc.RequestMaker("/requests/status.json?command=pl_sort&id=" + strconv.Itoa(id) + "&val=" + strconv.Itoa(val)) return } // ToggleSD toggle-enables service discovery module . // Typical values are: sap shoutcast, podcast, hal func (vlc *VLC) ToggleSD(val string) (err error) { _, err = vlc.RequestMaker("/requests/status.json?command=pl_sd&val=" + val) return } // Volume sets Volume level (can be absolute integer, or +/- relative value). // Percentage isn't working at the moment. Allowed values are of the form: +, -, or % func (vlc *VLC) Volume(val string) (err error) { _, err = vlc.RequestMaker("/requests/status.json?command=volume&val=" + val) return } // Seek seeks to // // Allowed values are of the form: // [+ or -][:][:][] // or [+ or -]% // (value between [ ] are optional, value between < > are mandatory) // examples: // 1000 -> seek to the 1000th second // +1H:2M -> seek 1 hour and 2 minutes forward // -10% -> seek 10% back func (vlc *VLC) Seek(val string) (err error) { _, err = vlc.RequestMaker("/requests/status.json?command=seek&val=" + val) return } // Preamp sets the preamp gain value, must be >=-20 and <=20 func (vlc *VLC) Preamp(gain int) (err error) { if (gain < -20) || (gain > 20) { err = errors.New("preamp must be between -20 and 20") return } _, err = vlc.RequestMaker("/requests/status.json?command=preamp&val=" + strconv.Itoa(gain)) return } // SetEQ sets the gain for a specific Equalizer band func (vlc *VLC) SetEQ(band int, gain int) (err error) { if (gain < -20) || (gain > 20) { err = errors.New("gain must be between -20 and 20") return } _, err = vlc.RequestMaker("/requests/status.json?command=equalizer&band=" + strconv.Itoa(band) + "&val=" + strconv.Itoa(gain)) return } // ToggleEQ toggles the EQ (true to enable, false to disable) func (vlc *VLC) ToggleEQ(enable bool) (err error) { enableStr := "0" if enable == true { enableStr = "1" } _, err = vlc.RequestMaker("/requests/status.json?command=enableeq&val=" + enableStr) return } // SetEQPreset sets the equalizer preset as per the id specified func (vlc *VLC) SetEQPreset(id int) (err error) { _, err = vlc.RequestMaker("/requests/status.json?command=setpreset&id=" + strconv.Itoa(id)) return } // SelectTitle selects the title using the title number func (vlc *VLC) SelectTitle(id int) (err error) { _, err = vlc.RequestMaker("/requests/status.json?command=title&val=" + strconv.Itoa(id)) return } // SelectChapter selects the chapter using the chapter number func (vlc *VLC) SelectChapter(id int) (err error) { _, err = vlc.RequestMaker("/requests/status.json?command=chapter&val=" + strconv.Itoa(id)) return } // SelectAudioTrack selects the audio track (use the number from the stream) func (vlc *VLC) SelectAudioTrack(id int) (err error) { _, err = vlc.RequestMaker("/requests/status.json?command=audio_track&val=" + strconv.Itoa(id)) return } // SelectVideoTrack selects the video track (use the number from the stream) func (vlc *VLC) SelectVideoTrack(id int) (err error) { _, err = vlc.RequestMaker("/requests/status.json?command=video_track&val=" + strconv.Itoa(id)) return } // SelectSubtitleTrack selects the subtitle track (use the number from the stream) func (vlc *VLC) SelectSubtitleTrack(id int) (err error) { _, err = vlc.RequestMaker("/requests/status.json?command=subtitle_track&val=" + strconv.Itoa(id)) return }