initial commit
This commit is contained in:
parent
b7797b30f6
commit
bd34f9f36f
|
@ -0,0 +1,5 @@
|
||||||
|
|
||||||
|
.idea/
|
||||||
|
_rest_stream*
|
||||||
|
.DS_Store
|
||||||
|
|
12
README.md
12
README.md
|
@ -1,3 +1,15 @@
|
||||||
|
<<<<<<< HEAD
|
||||||
# subfmproxy
|
# subfmproxy
|
||||||
|
|
||||||
A Subsonic FM Transmitter Proxy: Stream your music to your retro radios!
|
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)
|
||||||
|
|
|
@ -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
|
|
@ -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)
|
||||||
|
}
|
Loading…
Reference in New Issue