package main import ( "bytes" "io" "log" "net/http" "net/http/httputil" "net/url" "os" "os/exec" "strings" ) type Proxy struct { proxyTarget *url.URL } func NewProxy(target string) *Proxy { proxy := &Proxy{} urlTarget, err := url.Parse(target) if err != nil { log.Fatal("Target not a valid URL!", target) } proxy.proxyTarget = urlTarget return proxy } // serves as a reverse proxy to http://www.subsonic.org/pages/api.jsp func (pr Proxy) ServeHTTP(w http.ResponseWriter, req *http.Request) { log.Println("req ", req.RequestURI, req.Method) proxy := httputil.NewSingleHostReverseProxy(pr.proxyTarget) // format: 2023/02/03 14:17:16 req /rest/stream.view?u=jefklak&t=8c90da505334799b3184e9fb0539814d&s=LEcB8n9&v=1.13.0&c=substreamer&f=json&id=59d5e314f94f72d30df1a20c537e158c&maxBitRate=320&format=mp3&estimateContentLength=false GET // returns a binary ID3 file ready to be streamed to the FM receiver system. // Warning, this does NOT work with Navidrome's native system that also has a /rest/stream endpoint (without the .view) // TODO wat met "pauzeren"? // zie https://blog.yossarian.net/2022/11/07/Modernizing-my-1980s-sound-system#fnref:model if strings.Contains(req.RequestURI, "/rest/stream.view") { proxy.ModifyResponse = func(resp *http.Response) error { body, err := io.ReadAll(resp.Body) if err != nil { return err } err = resp.Body.Close() if err != nil { return err } go func() { mp3 := strings.ReplaceAll(req.RequestURI, "/", "_") + ".mp3" os.WriteFile(mp3, body, 0666) // on Mac: "sudo ln -s /usr/bin/open /usr/local/bin/play" go exec.Command("play", mp3).Run() }() // pass on the original body into a new body 'cause the original one has been read out already. resp.Body = io.NopCloser(bytes.NewReader(body)) return nil } } // allow for SSL redirection if needed req.URL.Host = pr.proxyTarget.Host req.URL.Scheme = pr.proxyTarget.Scheme req.Header.Set("X-Forwarded-Host", req.Header.Get("Host")) req.Host = pr.proxyTarget.Host proxy.ServeHTTP(w, req) } func main() { proxy := NewProxy("http://cd.xavier/") http.ListenAndServe(":8080", proxy) }