From bd34f9f36f93a48ac9abf81da292377a70c2b559 Mon Sep 17 00:00:00 2001 From: wgroeneveld Date: Thu, 9 Feb 2023 12:40:56 +0100 Subject: [PATCH] initial commit --- .gitignore | 5 ++++ README.md | 14 ++++++++- examplerequests.txt | 21 +++++++++++++ go.mod | 1 + main.go | 72 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 examplerequests.txt create mode 100644 go.mod create mode 100644 main.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..89591a2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ + +.idea/ +_rest_stream* +.DS_Store + diff --git a/README.md b/README.md index f08ce92..2c726cf 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,15 @@ +<<<<<<< HEAD # subfmproxy -A Subsonic FM Transmitter Proxy: Stream your music to your retro radios! \ No newline at end of file +A Subsonic FM Transmitter Proxy: Stream your music to your retro radios! +======= + +## Inspiration + +- https://github.com/dlsniper/fmradio and talk https://www.youtube.com/watch?v=o5MTRQmhvCk + - (with device https://www.adafruit.com/product/1958) + - with GoBot to interface with Pi/IC: https://gobot.io/ +- fm transmitter C/C++ projects such as + - https://github.com/markondej/fm_transmitter + - https://github.com/MundeepL/PiFM +>>>>>>> 7dbdff8 (initial commit) diff --git a/examplerequests.txt b/examplerequests.txt new file mode 100644 index 0000000..a1b3833 --- /dev/null +++ b/examplerequests.txt @@ -0,0 +1,21 @@ +http://www.subsonic.org/pages/api.jsp + +LOGIN + +2023/02/03 14:14:30 req /rest/ping.view?u=jefklak&t=8c90da505334799b3184e9fb0539814d&s=LEcB8n9&v=1.13.0&c=substreamer&f=json GET + + +2023/02/03 14:14:30 req /rest/ping.view?u=jefklak&t=8c90da505334799b3184e9fb0539814d&s=LEcB8n9&v=1.13.0&c=substreamer&f=json GET +2023/02/03 14:14:31 req /rest/getAlbumList2.view?u=jefklak&t=8c90da505334799b3184e9fb0539814d&s=LEcB8n9&v=1.13.0&c=substreamer&f=json&type=recent&size=20 GET +2023/02/03 14:14:31 req /rest/getAlbumList2.view?u=jefklak&t=8c90da505334799b3184e9fb0539814d&s=LEcB8n9&v=1.13.0&c=substreamer&f=json&type=frequent&size=20 GET +2023/02/03 14:14:31 req /rest/getAlbumList2.view?u=jefklak&t=8c90da505334799b3184e9fb0539814d&s=LEcB8n9&v=1.13.0&c=substreamer&f=json&type=random&size=20 GET +2023/02/03 14:14:31 req /rest/getRandomSongs.view?u=jefklak&t=8c90da505334799b3184e9fb0539814d&s=LEcB8n9&v=1.13.0&c=substreamer&f=json&size=50 GET + +playing music: + +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 +2023/02/03 14:18:07 req /rest/stream.view?u=jefklak&t=8c90da505334799b3184e9fb0539814d&s=LEcB8n9&v=1.13.0&c=substreamer&f=json&id=1bc38cc2c0cfaa302aecdd379abfe938&maxBitRate=320&format=mp3&estimateContentLength=false GET + +doorspoelen naar volgende: + +2023/02/03 14:17:47 req /rest/scrobble.view?u=jefklak&t=8c90da505334799b3184e9fb0539814d&s=LEcB8n9&v=1.13.0&c=substreamer&f=json&id=97394aea4b552463b3c3519777e5d8ca&submission=false GET diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..e34d674 --- /dev/null +++ b/go.mod @@ -0,0 +1 @@ +module subfmproxy diff --git a/main.go b/main.go new file mode 100644 index 0000000..8bd6947 --- /dev/null +++ b/main.go @@ -0,0 +1,72 @@ +package main + +import ( + "bytes" + "io" + "log" + "net/http" + "net/http/httputil" + "net/url" + "os" + "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 + } + + os.WriteFile(strings.ReplaceAll(req.RequestURI, "/", "_")+".txt", body, 0666) + + // 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) +}