Commit 044a4fc9 authored by ale's avatar ale

Update radioctl for the new APIs

parent 10cfbc65
package main package main
import ( import (
"context"
"encoding/json" "encoding/json"
"flag" "flag"
"fmt" "fmt"
...@@ -9,14 +10,21 @@ import ( ...@@ -9,14 +10,21 @@ import (
"os" "os"
"strconv" "strconv"
"strings" "strings"
"time"
"git.autistici.org/ale/autoradio" "git.autistici.org/ale/autoradio"
"git.autistici.org/ale/autoradio/client"
pb "git.autistici.org/ale/autoradio/proto"
"github.com/gonuts/commander" "github.com/gonuts/commander"
gonutsflag "github.com/gonuts/flag" gonutsflag "github.com/gonuts/flag"
"go.etcd.io/etcd/clientv3"
) )
// Format for output of structured data. // Format for output of structured data.
var outputFormat = flag.String("format", "txt", "Output format for structured data (json, txt)") var (
etcdEndpoints = flag.String("etcd", "http://localhost:2379", "Etcd endpoints (comma-separated list of URLs)")
outputFormat = flag.String("format", "txt", "Output format for structured data (json, txt)")
)
type optionalValue struct { type optionalValue struct {
isSet bool isSet bool
...@@ -135,15 +143,27 @@ func (b *BaseCommand) Command() *commander.Command { ...@@ -135,15 +143,27 @@ func (b *BaseCommand) Command() *commander.Command {
func (b *BaseCommand) Run(args []string) { func (b *BaseCommand) Run(args []string) {
} }
func getClient() *autoradio.Client { var auClient *client.Client
return autoradio.NewClient(autoradio.NewEtcdClient(false))
func getClient() *client.Client {
if auClient == nil {
cli, err := clientv3.New(clientv3.Config{
Endpoints: strings.Split(*etcdEndpoints, ","),
DialTimeout: 5 * time.Second,
})
if err != nil {
log.Fatalf("failed to connect to etcd: %v", err)
}
auClient = client.New(cli)
}
return auClient
} }
func setRelay(m *autoradio.Mount, relayUrl string) { func setRelay(m *pb.Mount, relayUrl string) {
if relayUrl == "" { if relayUrl == "" {
// Randomly generate source credentials. // Randomly generate source credentials.
m.Username = autoradio.GenerateUsername(m.Name) m.SourceUsername = autoradio.GenerateUsername(m.Path)
m.Password = autoradio.GeneratePassword() m.SourcePassword = autoradio.GeneratePassword()
} else { } else {
// Validate the given relay URL. // Validate the given relay URL.
u, err := url.Parse(relayUrl) u, err := url.Parse(relayUrl)
...@@ -151,30 +171,30 @@ func setRelay(m *autoradio.Mount, relayUrl string) { ...@@ -151,30 +171,30 @@ func setRelay(m *autoradio.Mount, relayUrl string) {
log.Fatal(err) log.Fatal(err)
} }
m.RelayUrl = u.String() m.RelayUrl = u.String()
m.Username = "" m.SourceUsername = ""
m.Password = "" m.SourcePassword = ""
} }
} }
func printMount(m *autoradio.Mount) { func printMount(m *pb.Mount) {
switch *outputFormat { switch *outputFormat {
case "json": case "json":
s, _ := json.MarshalIndent(m, "", " ") s, _ := json.MarshalIndent(m, "", " ")
os.Stdout.Write(s) os.Stdout.Write(s)
case "txt": case "txt":
fmt.Printf("path=%s\n", m.Name) fmt.Printf("path=%s\n", m.Path)
if m.Username != "" { if m.SourceUsername != "" {
fmt.Printf("username=%s\npassword=%s\n", m.Username, m.Password) fmt.Printf("username=%s\npassword=%s\n", m.SourceUsername, m.SourcePassword)
} }
if m.RelayUrl != "" { if m.RelayUrl != "" {
fmt.Printf("relay_url=%s\n", m.RelayUrl) fmt.Printf("relay_url=%s\n", m.RelayUrl)
} }
if m.Fallback != "" { if m.FallbackPath != "" {
fmt.Printf("fallback_url=%s\n", m.Fallback) fmt.Printf("fallback_url=%s\n", m.FallbackPath)
} }
if t := m.Transcoding; t != nil { if t := m.TranscodeParams; t != nil {
fmt.Printf("transcode_source_url=%s\n", t.SourceName) fmt.Printf("transcode_source_url=%s\n", t.SourcePath)
fmt.Printf("transcode_format=%s\n", t.Format) fmt.Printf("transcode_format=%s\n", t.Format)
fmt.Printf("transcode_bitrate=%d\n", t.BitRate) fmt.Printf("transcode_bitrate=%d\n", t.BitRate)
fmt.Printf("transcode_quality=%f\n", t.Quality) fmt.Printf("transcode_quality=%f\n", t.Quality)
...@@ -186,8 +206,8 @@ func printMount(m *autoradio.Mount) { ...@@ -186,8 +206,8 @@ func printMount(m *autoradio.Mount) {
} }
} }
func mountExists(name string, client *autoradio.Client) bool { func mountExists(name string, c *client.Client) bool {
m, _ := client.GetMount(name) m, _ := c.GetMount(context.Background(), name)
return m != nil return m != nil
} }
...@@ -229,22 +249,22 @@ func (cmd *createMountCommand) Run(args []string) { ...@@ -229,22 +249,22 @@ func (cmd *createMountCommand) Run(args []string) {
} }
// Check if the mount already exists. // Check if the mount already exists.
client := getClient() c := getClient()
if mountExists(path, client) { if mountExists(path, c) {
log.Fatal("ERROR: A mount with that name already exists!") log.Fatal("ERROR: A mount with that name already exists!")
} }
// Create the new mount and set the relevant fields (depending // Create the new mount and set the relevant fields (depending
// on the options passed to the command). // on the options passed to the command).
m := &autoradio.Mount{Name: path} m := &pb.Mount{Path: path}
setRelay(m, cmd.relay) setRelay(m, cmd.relay)
m.Fallback = cmd.fallback m.FallbackPath = cmd.fallback
if err := m.Valid(); err != nil { if err := m.Valid(); err != nil {
log.Fatalf("ERROR: mount configuration is invalid: %v", err) log.Fatalf("ERROR: mount configuration is invalid: %v", err)
} }
if err := client.SetMount(m); err != nil { if err := c.SetMount(context.Background(), m); err != nil {
log.Fatalf("ERROR: creating mount: %v", err) log.Fatalf("ERROR: creating mount: %v", err)
} }
...@@ -254,8 +274,15 @@ func (cmd *createMountCommand) Run(args []string) { ...@@ -254,8 +274,15 @@ func (cmd *createMountCommand) Run(args []string) {
// Create a submount (transcoded stream). // Create a submount (transcoded stream).
type createTranscodingMountCommand struct { type createTranscodingMountCommand struct {
BaseCommand BaseCommand
params *autoradio.EncodingParams
fallback string sourcePath string
format string
quality float64
bitRate int
sampleRate int
channels int
stereoMode string
fallback string
} }
func newCreateTranscodingMountCommand() *createTranscodingMountCommand { func newCreateTranscodingMountCommand() *createTranscodingMountCommand {
...@@ -268,22 +295,17 @@ Create a new stream that will transcode the parent stream with ...@@ -268,22 +295,17 @@ Create a new stream that will transcode the parent stream with
different encoding parameters. different encoding parameters.
`, `,
}, },
params: autoradio.NewEncodingParams(),
} }
} }
func addEncodingFlags(f *gonutsflag.FlagSet, p *autoradio.EncodingParams) {
f.StringVar(&p.SourceName, "source", "", "Source mountpoint")
f.StringVar(&p.Format, "codec", p.Format, "Encoding format")
f.Float64Var(&p.Quality, "quality", p.Quality, "Quality (for VBR encoders)")
f.IntVar(&p.BitRate, "bitrate", p.BitRate, "Bitrate (kbps)")
f.IntVar(&p.SampleRate, "samplerate", p.SampleRate, "Sample rate (Hz)")
f.IntVar(&p.Channels, "channels", p.Channels, "Number of channels")
f.StringVar(&p.StereoMode, "stereo-mode", p.StereoMode, "Stereo mode for mp3 codec (stereo, joint_stereo)")
}
func (cmd *createTranscodingMountCommand) AddFlags(f *gonutsflag.FlagSet) { func (cmd *createTranscodingMountCommand) AddFlags(f *gonutsflag.FlagSet) {
addEncodingFlags(f, cmd.params) f.StringVar(&cmd.sourcePath, "source", "", "Source mountpoint")
f.StringVar(&cmd.format, "codec", "mp3", "Encoding format")
f.Float64Var(&cmd.quality, "quality", 0, "Quality (for VBR encoders)")
f.IntVar(&cmd.bitRate, "bitrate", 32, "Bitrate (kbps)")
f.IntVar(&cmd.sampleRate, "samplerate", 44100, "Sample rate (Hz)")
f.IntVar(&cmd.channels, "channels", 2, "Number of channels")
f.StringVar(&cmd.stereoMode, "stereo-mode", "joint_stereo", "Stereo mode for mp3 codec (stereo, joint_stereo)")
f.StringVar(&cmd.fallback, "fallback", "", "Fallback stream URL") f.StringVar(&cmd.fallback, "fallback", "", "Fallback stream URL")
} }
...@@ -299,28 +321,37 @@ func (cmd *createTranscodingMountCommand) Run(args []string) { ...@@ -299,28 +321,37 @@ func (cmd *createTranscodingMountCommand) Run(args []string) {
} }
// The mount path should not exist. // The mount path should not exist.
client := getClient() c := getClient()
if mountExists(path, client) { if mountExists(path, c) {
log.Fatal("ERROR: a mount with that name already exists!") log.Fatal("ERROR: a mount with that name already exists!")
} }
// The source mount should exist. // The source mount should exist.
if !mountExists(cmd.params.SourceName, client) { if !mountExists(cmd.sourcePath, c) {
log.Fatal("ERROR: the source mount does not exist!") log.Fatal("ERROR: the source mount does not exist!")
} }
// Retrieve the parent mount point and add a TranscodingMount. // Retrieve the parent mount point and add a TranscodingMount.
m := &autoradio.Mount{ m := &pb.Mount{
Name: path, Path: path,
Transcoding: cmd.params, FallbackPath: cmd.fallback,
Transcode: true,
TranscodeParams: &pb.EncodingParams{
SourcePath: cmd.sourcePath,
Format: cmd.format,
BitRate: int32(cmd.bitRate),
SampleRate: int32(cmd.sampleRate),
Channels: int32(cmd.channels),
StereoMode: cmd.stereoMode,
Quality: float32(cmd.quality),
},
} }
setRelay(m, "") setRelay(m, "")
m.Fallback = cmd.fallback
if err := m.Valid(); err != nil { if err := m.Valid(); err != nil {
log.Fatalf("ERROR: mount configuration is invalid: %v", err) log.Fatalf("ERROR: mount configuration is invalid: %v", err)
} }
if err := client.SetMount(m); err != nil { if err := c.SetMount(context.Background(), m); err != nil {
log.Fatalf("ERROR: creating mount: %v", err) log.Fatalf("ERROR: creating mount: %v", err)
} }
...@@ -402,8 +433,8 @@ func (cmd *editMountCommand) Run(args []string) { ...@@ -402,8 +433,8 @@ func (cmd *editMountCommand) Run(args []string) {
log.Fatal("Wrong number of arguments") log.Fatal("Wrong number of arguments")
} }
client := getClient() c := getClient()
m, err := client.GetMount(args[0]) m, err := c.GetMount(context.Background(), args[0])
if err != nil { if err != nil {
log.Fatalf("ERROR: %v", err) log.Fatalf("ERROR: %v", err)
} }
...@@ -413,43 +444,43 @@ func (cmd *editMountCommand) Run(args []string) { ...@@ -413,43 +444,43 @@ func (cmd *editMountCommand) Run(args []string) {
// Only set those fields that were passed on the command line. // Only set those fields that were passed on the command line.
if cmd.fallback.IsSet() { if cmd.fallback.IsSet() {
m.Fallback = cmd.fallback.Value() m.FallbackPath = cmd.fallback.Value()
} }
if cmd.relay.IsSet() { if cmd.relay.IsSet() {
setRelay(m, cmd.relay.Value()) setRelay(m, cmd.relay.Value())
} }
if cmd.transcodingOptionsSet() && m.Transcoding == nil { if cmd.transcodingOptionsSet() && !m.Transcode {
log.Fatal("ERROR: can't set transcoding options on a non-transcoding mount (delete and re-create)") log.Fatal("ERROR: can't set transcoding options on a non-transcoding mount (delete and re-create)")
} }
if cmd.transFormat.IsSet() { if cmd.transFormat.IsSet() {
m.Transcoding.Format = cmd.transFormat.Value() m.TranscodeParams.Format = cmd.transFormat.Value()
} }
if cmd.transSource.IsSet() { if cmd.transSource.IsSet() {
m.Transcoding.SourceName = cmd.transSource.Value() m.TranscodeParams.SourcePath = cmd.transSource.Value()
} }
if cmd.transBitRate.IsSet() { if cmd.transBitRate.IsSet() {
m.Transcoding.BitRate = cmd.transBitRate.Value() m.TranscodeParams.BitRate = int32(cmd.transBitRate.Value())
} }
if cmd.transSampleRate.IsSet() { if cmd.transSampleRate.IsSet() {
m.Transcoding.SampleRate = cmd.transSampleRate.Value() m.TranscodeParams.SampleRate = int32(cmd.transSampleRate.Value())
} }
if cmd.transQuality.IsSet() { if cmd.transQuality.IsSet() {
m.Transcoding.Quality = cmd.transQuality.Value() m.TranscodeParams.Quality = float32(cmd.transQuality.Value())
} }
if cmd.transChannels.IsSet() { if cmd.transChannels.IsSet() {
m.Transcoding.Channels = cmd.transChannels.Value() m.TranscodeParams.Channels = int32(cmd.transChannels.Value())
} }
if cmd.transStereoMode.IsSet() { if cmd.transStereoMode.IsSet() {
m.Transcoding.StereoMode = cmd.transStereoMode.Value() m.TranscodeParams.StereoMode = cmd.transStereoMode.Value()
} }
if err := m.Valid(); err != nil { if err := m.Valid(); err != nil {
log.Fatalf("ERROR: mount configuration is invalid: %v", err) log.Fatalf("ERROR: mount configuration is invalid: %v", err)
} }
if err := client.SetMount(m); err != nil { if err := c.SetMount(context.Background(), m); err != nil {
log.Fatalf("ERROR: updating mount: %v", err) log.Fatalf("ERROR: updating mount: %v", err)
} }
...@@ -478,25 +509,25 @@ func (cmd *deleteMountCommand) Run(args []string) { ...@@ -478,25 +509,25 @@ func (cmd *deleteMountCommand) Run(args []string) {
log.Fatal("Wrong number of arguments") log.Fatal("Wrong number of arguments")
} }
path := args[0] path := args[0]
client := getClient() c := getClient()
if !mountExists(path, client) { if !mountExists(path, c) {
log.Fatal("ERROR: mount not found") log.Fatal("ERROR: mount not found")
} }
if err := client.DelMount(path); err != nil { if err := c.DeleteMount(context.Background(), path); err != nil {
log.Fatalf("ERROR: deleting mount: %v", err) log.Fatalf("ERROR: deleting mount: %v", err)
} }
// Delete all the transcoding mounts that have this as a // Delete all the transcoding mounts that have this as a
// source. // source.
mounts, err := client.ListMounts() mounts, err := c.ListMounts(context.Background())
if err != nil { if err != nil {
log.Fatalf("ERROR: %v", err) log.Fatalf("ERROR: %v", err)
} }
for _, m := range mounts { for _, m := range mounts {
if m.HasTranscoder() && m.Transcoding.SourceName == path { if m.HasTranscoder() && m.TranscodeParams.SourcePath == path {
if err := client.DelMount(m.Name); err != nil { if err := c.DeleteMount(context.Background(), m.Path); err != nil {
log.Printf("ERROR: deleting transcoded mount %s: %v", m.Name, err) log.Printf("ERROR: deleting transcoded mount %s: %v", m.Path, err)
} }
} }
} }
...@@ -526,13 +557,13 @@ func (cmd *listMountsCommand) Run(args []string) { ...@@ -526,13 +557,13 @@ func (cmd *listMountsCommand) Run(args []string) {
log.Fatal("Too many arguments") log.Fatal("Too many arguments")
} }
mounts, err := getClient().ListMounts() mounts, err := getClient().ListMounts(context.Background())
if err != nil { if err != nil {
log.Fatalf("ERROR: %v", err) log.Fatalf("ERROR: %v", err)
} }
var names []string var names []string
for _, m := range mounts { for _, m := range mounts {
names = append(names, m.Name) names = append(names, m.Path)
} }
switch *outputFormat { switch *outputFormat {
...@@ -571,7 +602,7 @@ func (cmd *showMountCommand) Run(args []string) { ...@@ -571,7 +602,7 @@ func (cmd *showMountCommand) Run(args []string) {
log.Fatal("Wrong number of arguments") log.Fatal("Wrong number of arguments")
} }
m, err := getClient().GetMount(args[0]) m, err := getClient().GetMount(context.Background(), args[0])
if err != nil { if err != nil {
log.Fatalf("ERROR: %v", err) log.Fatalf("ERROR: %v", err)
} }
...@@ -605,7 +636,7 @@ func (cmd *backupCommand) Run(args []string) { ...@@ -605,7 +636,7 @@ func (cmd *backupCommand) Run(args []string) {
log.Fatal("Too many arguments") log.Fatal("Too many arguments")
} }
mounts, err := getClient().ListMounts() mounts, err := getClient().ListMounts(context.Background())
if err != nil { if err != nil {
log.Fatalf("ERROR: %v", err) log.Fatalf("ERROR: %v", err)
} }
...@@ -636,15 +667,15 @@ func (cmd *restoreCommand) Run(args []string) { ...@@ -636,15 +667,15 @@ func (cmd *restoreCommand) Run(args []string) {
log.Fatal("Too many arguments") log.Fatal("Too many arguments")
} }
var mounts []*autoradio.Mount var mounts []*pb.Mount
if err := json.NewDecoder(os.Stdin).Decode(&mounts); err != nil { if err := json.NewDecoder(os.Stdin).Decode(&mounts); err != nil {
log.Fatalf("ERROR: %v", err) log.Fatalf("ERROR: %v", err)
} }
client := getClient() c := getClient()
for _, m := range mounts { for _, m := range mounts {
if err := client.SetMount(m); err != nil { if err := c.SetMount(context.Background(), m); err != nil {
log.Printf("ERROR: creating mount %s: %v", m.Name, err) log.Printf("ERROR: creating mount %s: %v", m.Path, err)
} }
} }
} }
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment