Skip to content

Commit

Permalink
Merge pull request #9 from ugjka/blast-srciptable
Browse files Browse the repository at this point in the history
make blast scriptable
  • Loading branch information
ugjka committed Feb 13, 2024
2 parents 67e4a45 + 93cb802 commit 75a0568
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 32 deletions.
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,29 @@ Select the lan IP address for the stream:

There's also a `-debug` and `-headers` flags if you want to inspect your DLNA device

### Flags for scripting

```
[ugjka@ugjka blast]$ blast -h
Usage of blast:
-bitrate int
mp3 bitrate (default 320)
-chunk int
chunk size in seconds (default 1)
-debug
print debug info
-device string
dlna friendly name
-headers
print request headers
-ip string
ip address
-port int
stream port (default 9000)
-source string
audio source (pactl list sources short | cut -f2)
```

## Building

You need the `go` and `go-tools` toolchain, also `git`
Expand Down
6 changes: 3 additions & 3 deletions audio_serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func (s source) ServeHTTP(w http.ResponseWriter, r *http.Request) {
chunked := ok && r.Proto == "HTTP/1.1"

if !chunked {
var yearBytes = yearSeconds * (MP3BITRATE / 8) * 1000
var yearBytes = yearSeconds * (*bitrate / 8) * 1000
w.Header().Add("Content-Length", fmt.Sprint(yearBytes))
}

Expand All @@ -92,7 +92,7 @@ func (s source) ServeHTTP(w http.ResponseWriter, r *http.Request) {
"-f", "s16le",
"-ac", "2",
"-i", "-",
"-b:a", fmt.Sprintf("%dk", MP3BITRATE),
"-b:a", fmt.Sprintf("%dk", *bitrate),
"-f", "mp3", "-",
)
parecReader, parecWriter := io.Pipe()
Expand All @@ -109,7 +109,7 @@ func (s source) ServeHTTP(w http.ResponseWriter, r *http.Request) {
err error
n int
)
buf := make([]byte, (MP3BITRATE/8)*1000*CHUNK_SECONDS)
buf := make([]byte, (*bitrate/8)*1000**chunk)
for {
n, err = ffmpegReader.Read(buf)
if err != nil {
Expand Down
12 changes: 10 additions & 2 deletions audio_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import (
"os/exec"
)

func chooseAudioSource() (source, error) {
func chooseAudioSource(lookup string) (source, error) {
srcCMD := exec.Command("pactl", "-f", "json", "list", "sources", "short")
srcData, err := srcCMD.Output()
if err != nil {
Expand All @@ -45,10 +45,18 @@ func chooseAudioSource() (source, error) {
if len(srcJSON) == 0 {
return "", fmt.Errorf("no audio sources found")
}
srcJSON = append(srcJSON, struct{ Name string }{BLASTMONITOR})
if lookup != "" {
for _, v := range srcJSON {
if v.Name == lookup {
return source(lookup), nil
}
}
return "", fmt.Errorf("%s: not found", lookup)
}

fmt.Println("Audio sources")
// append for on-demand loading of blast sink
srcJSON = append(srcJSON, struct{ Name string }{BLASTMONITOR})
for i, v := range srcJSON {
fmt.Printf("%d: %s\n", i, v.Name)
}
Expand Down
22 changes: 18 additions & 4 deletions dlna_device.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,31 @@ import (
"github.com/huin/goupnp/dcps/av1"
)

func chooseUPNPDevice() (*goupnp.MaybeRootDevice, error) {
fmt.Println("Loading...")
func chooseUPNPDevice(lookup string) (*goupnp.MaybeRootDevice, error) {
if lookup == "" {
fmt.Println("Loading...")
}

roots, err := goupnp.DiscoverDevices(av1.URN_AVTransport_1)

fmt.Print("\033[1A\033[K")
fmt.Println("----------")
if lookup == "" {
fmt.Print("\033[1A\033[K")
fmt.Println("----------")
}

if err != nil {
return nil, fmt.Errorf("discover: %v", err)
}
if lookup != "" {
for _, v := range roots {
if v.Root != nil {
if v.Root.Device.FriendlyName == lookup {
return &v, nil
}
}
}
return nil, fmt.Errorf("%s: not found", lookup)
}

if len(roots) == 0 {
return nil, fmt.Errorf("no dlna devices on the network found")
Expand Down
15 changes: 13 additions & 2 deletions lan_ip.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import (
"net"
)

func chooseStreamIP() (net.IP, error) {
func chooseStreamIP(lookup string) (net.IP, error) {
addrs, err := net.InterfaceAddrs()
if err != nil {
return nil, err
Expand All @@ -47,7 +47,18 @@ func chooseStreamIP() (net.IP, error) {
if len(ips) == 0 {
return nil, fmt.Errorf("no usable lan ip addresses found")
}

if lookup != "" {
lookupIp := net.ParseIP(lookup)
if lookupIp == nil {
return nil, fmt.Errorf("%s: not found", lookup)
}
for _, ip := range ips {
if ip.Equal(lookupIp) {
return lookupIp, nil
}
}
return nil, fmt.Errorf("%s: not found", lookup)
}
fmt.Println("Your LAN ip addresses")
for i, ip := range ips {
fmt.Printf("%d: %s\n", i, ip)
Expand Down
54 changes: 33 additions & 21 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,18 +46,17 @@ import (
//go:embed logo.png
var logobytes []byte

var headers = new(bool)
var (
headers = new(bool)
bitrate = new(int)
port = new(int)
chunk = new(int)
)

const (
BLASTMONITOR = "blast.monitor"
STREAM_NAME = "stream.mp3"
LOGO_NAME = "logo.png"
// open in firewall
STREAMPORT = 9000
// lame encoder bitrate
MP3BITRATE = 320
// chunk size when using chunked tranfer encoding
CHUNK_SECONDS = 1
)

func main() {
Expand All @@ -77,6 +76,14 @@ func main() {
}
debug := flag.Bool("debug", false, "print debug info")
headers = flag.Bool("headers", false, "print request headers")
// script flags
device := flag.String("device", "", "dlna friendly name")
source := flag.String("source", "", "audio source (pactl list sources short | cut -f2)")
ip := flag.String("ip", "", "ip address")
bitrate = flag.Int("bitrate", 320, "mp3 bitrate")
port = flag.Int("port", 9000, "stream port")
chunk = flag.Int("chunk", 1, "chunk size in seconds")

flag.Parse()

var (
Expand Down Expand Up @@ -108,7 +115,7 @@ func main() {
fmt.Println("terminated...")
os.Exit(0)
}()
DLNADevice, err = chooseUPNPDevice()
DLNADevice, err = chooseUPNPDevice(*device)
if err != nil {
fmt.Fprintln(os.Stderr, "upnp:", err)
os.Exit(1)
Expand All @@ -135,10 +142,11 @@ func main() {
os.Exit(0)
}
}
if *device == "" {
fmt.Println("----------")
}

fmt.Println("----------")

audioSource, err := chooseAudioSource()
audioSource, err := chooseAudioSource(*source)
if err != nil {
fmt.Fprintln(os.Stderr, "audio:", err)
os.Exit(1)
Expand All @@ -157,26 +165,30 @@ func main() {
blastSinkID = bytes.TrimSpace(blastSinkID)
}

fmt.Println("----------")
streamAddress, err := chooseStreamIP()
if *source == "" {
fmt.Println("----------")
}
streamAddress, err := chooseStreamIP(*ip)
if err != nil {
fmt.Fprintln(os.Stderr, "network:", err)
cleanup()
os.Exit(1)
}
fmt.Println("----------")
if *ip == "" {
fmt.Println("----------")
}

log.Printf(
"starting the stream on port %d (configure your firewall if necessary)",
STREAMPORT,
*port,
)

mux := http.NewServeMux()
mux.Handle("/"+STREAM_NAME, audioSource)
var logoHandler logo = logobytes
mux.Handle("/"+LOGO_NAME, logoHandler)
httpServer := &http.Server{
Addr: fmt.Sprintf(":%d", STREAMPORT),
Addr: fmt.Sprintf(":%d", *port),
ReadTimeout: -1,
WriteTimeout: -1,
Handler: mux,
Expand All @@ -191,7 +203,7 @@ func main() {
}()
// detect when the stream server is up
for {
_, err := net.Dial("tcp", fmt.Sprintf(":%d", STREAMPORT))
_, err := net.Dial("tcp", fmt.Sprintf(":%d", *port))
if err == nil {
break
}
Expand All @@ -208,9 +220,9 @@ func main() {
}
if streamAddress.To4() != nil {
streamURL = fmt.Sprintf("%s://%s:%d/%s",
protocol, streamAddress, STREAMPORT, STREAM_NAME)
protocol, streamAddress, *port, STREAM_NAME)
albumArtURL = fmt.Sprintf("http://%s:%d/%s",
streamAddress, STREAMPORT, LOGO_NAME)
streamAddress, *port, LOGO_NAME)
} else {
var zone string
if streamAddress.IsLinkLocalUnicast() {
Expand All @@ -220,9 +232,9 @@ func main() {
}
}
streamURL = fmt.Sprintf("%s://[%s%s]:%d/%s",
protocol, streamAddress, zone, STREAMPORT, STREAM_NAME)
protocol, streamAddress, zone, *port, STREAM_NAME)
albumArtURL = fmt.Sprintf("http://[%s%s]:%d/%s",
streamAddress, zone, STREAMPORT, LOGO_NAME)
streamAddress, zone, *port, LOGO_NAME)
}
log.Printf("stream URI: %s\n", streamURL)
log.Println("setting av1transport URI and playing")
Expand Down

0 comments on commit 75a0568

Please sign in to comment.