From b7f5ed4e5fcb3b51636a9dad79e7e80f7ebbb102 Mon Sep 17 00:00:00 2001 From: Florian Fischer Date: Fri, 23 Dec 2022 08:51:37 +0100 Subject: implement local storage backend The 'local' backend is able to serve the music from a path specified by '-server-music-dir'. Add command line options to select the server port as well as a list of backends. --- TODO | 14 ++++++++ backend.go | 29 ++++++++++++++++ config.go | 7 ++++ dummyBackend.go | 24 ------------- go.mod | 18 ++++++---- go.sum | 58 ++++++++++++++++++++++++------- llog/llog.go | 2 +- localBackend.go | 103 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ main.go | 18 +++++++--- player.go | 38 ++++++++++++--------- playlist.go | 2 +- web/client.html | 15 ++++----- wrms.go | 9 +++-- youtube.go | 6 ++-- 14 files changed, 265 insertions(+), 78 deletions(-) create mode 100644 TODO create mode 100644 backend.go create mode 100644 config.go delete mode 100644 dummyBackend.go create mode 100644 localBackend.go diff --git a/TODO b/TODO new file mode 100644 index 0000000..4bd1d60 --- /dev/null +++ b/TODO @@ -0,0 +1,14 @@ +## Fronted + +## Web Backend + +- [ ] Implement a event endpoint + +## Backends + +- [X] Implement local storage backend +- [ ] Fix youtube search + +## Configuration +- [ ] Implement command line flags +- [ ] Implement config file support diff --git a/backend.go b/backend.go new file mode 100644 index 0000000..9cb42f8 --- /dev/null +++ b/backend.go @@ -0,0 +1,29 @@ +package main + +import ( + "crypto/sha1" + "encoding/base64" +) + +type Backend interface { + Play(song *Song, player *Player) + Search(keyword string) []Song +} + +type DummyBackend struct{} + +func (dummy *DummyBackend) Play(song *Song, player *Player) { +} + +func (dummy *DummyBackend) Search(keyword string) []Song { + + s := NewDummySong("Dummy Mc Crashtest", "exactly "+keyword) + return []Song{s} +} + +func NewDummySong(title, artist string) Song { + h := sha1.New() + h.Write([]byte(title)) + h.Write([]byte(artist)) + return NewSong(title, artist, "dummy", base64.URLEncoding.EncodeToString(h.Sum(nil))) +} diff --git a/config.go b/config.go new file mode 100644 index 0000000..ff56936 --- /dev/null +++ b/config.go @@ -0,0 +1,7 @@ +package main + +type Config struct { + port int + backends string + localMusicDir string +} diff --git a/dummyBackend.go b/dummyBackend.go deleted file mode 100644 index 460dd5e..0000000 --- a/dummyBackend.go +++ /dev/null @@ -1,24 +0,0 @@ -package main - -import ( - "crypto/sha1" - "encoding/base64" -) - -type DummyBackend struct{} - -func (dummy *DummyBackend) Play(song *Song, player *Player) { -} - -func (dummy *DummyBackend) Search(keyword string) []Song { - - s := NewDummySong("Dummy Mc Crashtest", "exactly "+keyword) - return []Song{s} -} - -func NewDummySong(title, artist string) Song { - h := sha1.New() - h.Write([]byte(title)) - h.Write([]byte(artist)) - return NewSong(title, artist, "dummy", base64.URLEncoding.EncodeToString(h.Sum(nil))) -} diff --git a/go.mod b/go.mod index 5840c4d..c2d9525 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.18 require ( github.com/AnjanaMadu/YTSearch v1.0.0 + github.com/dhowden/tag v0.0.0-20220618230019-adf36e896086 github.com/google/uuid v1.3.0 github.com/librespot-org/librespot-golang v0.0.0-20220325184705-31669e5a889f nhooyr.io/websocket v1.8.7 @@ -12,12 +13,15 @@ require ( require ( github.com/badfortrains/mdns v0.0.0-20160325001438-447166384f51 // indirect github.com/buger/jsonparser v1.1.1 // indirect - github.com/golang/protobuf v1.5.0 // indirect - github.com/klauspost/compress v1.10.4 // indirect - github.com/miekg/dns v1.1.8 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/klauspost/compress v1.15.13 // indirect + github.com/miekg/dns v1.1.50 // indirect github.com/stretchr/testify v1.5.1 // indirect - golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480 // indirect - golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e // indirect - golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd // indirect - google.golang.org/protobuf v1.27.1 // indirect + golang.org/x/crypto v0.4.0 // indirect + golang.org/x/mod v0.7.0 // indirect + golang.org/x/net v0.4.0 // indirect + golang.org/x/sys v0.3.0 // indirect + golang.org/x/tools v0.4.0 // indirect + golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect + google.golang.org/protobuf v1.28.1 // indirect ) diff --git a/go.sum b/go.sum index 2e5ce48..8a7deb3 100644 --- a/go.sum +++ b/go.sum @@ -7,6 +7,8 @@ github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx2 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dhowden/tag v0.0.0-20220618230019-adf36e896086 h1:ORubSQoKnncsBnR4zD9CuYFJCPOCuSNEpWEZrDdBXkc= +github.com/dhowden/tag v0.0.0-20220618230019-adf36e896086/go.mod h1:Z3Lomva4pyMWYezjMAU5QWRh0p1VvO4199OHlFnyKkM= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14= @@ -26,8 +28,9 @@ github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo= github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= @@ -39,16 +42,17 @@ github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.10.4 h1:jFzIFaf586tquEB5EhzQG0HwGNSlgAJpG53G6Ss11wc= -github.com/klauspost/compress v1.10.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.15.13 h1:NFn1Wr8cfnenSJSA46lLq4wHCcBzKTSjnBIexDMMOV0= +github.com/klauspost/compress v1.15.13/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/librespot-org/librespot-golang v0.0.0-20220325184705-31669e5a889f h1:tTMPsyVClxxV+CnlLqzgxTTTFM6J7i//rSejN1Md0b0= github.com/librespot-org/librespot-golang v0.0.0-20220325184705-31669e5a889f/go.mod h1:LeHPXRci2Bg6RBmw8BDEFH5nfb8oGU6mll+qTH8UIzw= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/miekg/dns v1.1.8 h1:1QYRAKU3lN5cRfLCkPU08hwvLJFhvjP6MqNMmQz6ZVI= github.com/miekg/dns v1.1.8/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= +github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= @@ -66,28 +70,58 @@ github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/xlab/portaudio-go v0.0.0-20170905165025-132d041879db/go.mod h1:r57mRacDQMS6Fz8ubv1nE8zZ0DbQ/sY0NkCmdxeUXmY= github.com/xlab/vorbis-go v0.0.0-20190125051917-087364aef51d/go.mod h1:AMqfx3jFwPqem3u8mF2lsRodZs30jG/Mag5HZ3mB3sA= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480 h1:O5YqonU5IWby+w98jVUG9h7zlCWCcH4RHyPVReBmhzk= golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8= +golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190420063019-afa5a82059c6/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU= +golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.4.0 h1:7mTAgkunk3fr4GAloyyCasadO6h9zSsQZbwvcaIciV4= +golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= diff --git a/llog/llog.go b/llog/llog.go index e7fb924..52ffc40 100644 --- a/llog/llog.go +++ b/llog/llog.go @@ -16,7 +16,7 @@ const ( l_DDebug ) -var Level LogLevel = l_Warning +var Level LogLevel = l_DDebug func getLevelNames() []string { return []string{"Fatal", "Error", "Warning", "Info", "Debug", "DDebug"} diff --git a/localBackend.go b/localBackend.go new file mode 100644 index 0000000..a09d9a1 --- /dev/null +++ b/localBackend.go @@ -0,0 +1,103 @@ +package main + +import ( + "fmt" + "github.com/dhowden/tag" + "muhq.space/go/wrms/llog" + "os" + "path" + "path/filepath" + "strings" + "sync" +) + +type LocalBackend struct { + musicDir string + lock sync.RWMutex + songs map[string]*Song +} + +func NewLocalBackend(musicDir string) *LocalBackend { + b := LocalBackend{musicDir, sync.RWMutex{}, map[string]*Song{}} + + go b.findSongs() + return &b +} + +func (b *LocalBackend) insert(song *Song) { + b.lock.Lock() + defer b.lock.Unlock() + b.songs[song.Uri] = song +} + +func (b *LocalBackend) findSongs() { + llog.Debug(fmt.Sprintf("Starting song search under: %s", b.musicDir)) + + // Insert some Songs + song := NewSong("No!", "Bukahara", "local", "Bukahara/Phantasma/01-No!.ogg") + + for _, s := range []*Song{&song} { + b.insert(s) + } + + err := filepath.Walk(b.musicDir, func(p string, finfo os.FileInfo, err error) error { + if err != nil { + llog.Warning(fmt.Sprintf("error %v at path %s\n", err, p)) + return err + } + + if !finfo.Mode().IsRegular() { + return nil + } + + ext := strings.ToLower(path.Ext(p)) + for _, excluded := range []string{".png", ".jpg", ".txt", ".pdf", ".m3u"} { + if ext == excluded { + return nil + } + } + + f, err := os.Open(p) + defer f.Close() + if err != nil { + llog.Warning(fmt.Sprintf("error %v opening file %s", err, p)) + return err + } + + m, err := tag.ReadFrom(f) + if err != nil { + llog.Warning(fmt.Sprintf("error reading tags from %s: %v", p, err)) + // Contiune walking + return nil + } + + s := NewSong(m.Title(), m.Artist(), "local", p) + b.insert(&s) + return nil + }) + + if err != nil { + llog.Error(fmt.Sprintf("error walking the path %q: %v", b.musicDir, err)) + } +} + +func (b *LocalBackend) Play(song *Song, player *Player) { + player.PlayUri("file://" + song.Uri) +} + +func (b *LocalBackend) Search(keyword string) []Song { + pattern := strings.ToLower(keyword) + results := []Song{} + + b.lock.RLock() + defer b.lock.RUnlock() + + for _, s := range b.songs { + if strings.Contains(strings.ToLower(s.Title), pattern) || + strings.Contains(strings.ToLower(s.Artist), pattern) { + results = append(results, *s) + } + } + + return results +} diff --git a/main.go b/main.go index 4067f63..9eefc83 100644 --- a/main.go +++ b/main.go @@ -3,20 +3,21 @@ package main import ( "context" "encoding/json" + "flag" "fmt" "html/template" "io/ioutil" "log" "net/http" - "muhq.space/wrms/llog" + "muhq.space/go/wrms/llog" "github.com/google/uuid" "nhooyr.io/websocket" ) -var wrms = NewWrms() +var wrms *Wrms func landingPage(w http.ResponseWriter, r *http.Request) { if _, err := r.Cookie("UUID"); err != nil { @@ -212,12 +213,21 @@ func setupRoutes() { } func main() { + config := Config{} + flag.IntVar(&config.port, "port", 8080, "port to listen to") + flag.StringVar(&config.backends, "backends", "dummy youtube spotify", "music backend to use") + flag.StringVar(&config.localMusicDir, "serve-music-dir", "", "local music directory to serve") + flag.Parse() + + wrms = NewWrms(config) + defer wrms.Close() + setupRoutes() // wrms.AddSong(NewDummySong("Lala", "SNFMT")) // wrms.AddSong(NewDummySong("Hobelbank", "MC Wankwichtel")) log.Println(wrms) - defer wrms.Close() - log.Fatal(http.ListenAndServe(":8080", nil)) + + log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", config.port), nil)) } diff --git a/player.go b/player.go index c7a9390..94efa27 100644 --- a/player.go +++ b/player.go @@ -6,32 +6,40 @@ import ( "os/exec" "syscall" - "muhq.space/wrms/llog" + "muhq.space/go/wrms/llog" ) -type Backend interface { - Play(song *Song, player *Player) - Search(keyword string) []Song -} - type Player struct { Backends map[string]Backend mpv *exec.Cmd wrms *Wrms } -func NewPlayer(wrms *Wrms) Player { +func NewPlayer(wrms *Wrms, backends []string) Player { available_backends := map[string]Backend{} - spotify, err := NewSpotify() - if err != nil { - // log.Fatalln("Error during initialization of the spotify backend:", err.Error()) - llog.Error(fmt.Sprintf("Error during initialization of the spotify backend: %s", err.Error())) - } - available_backends["spotify"] = spotify - available_backends["youtube"] = &YoutubeBackend{} + var b Backend + var err error + for _, backend := range backends { + switch backend { + case "spotify": + b, err = NewSpotify() + case "youtube": + b = &YoutubeBackend{} + case "dummy": + b = &DummyBackend{} + case "local": + b = NewLocalBackend(wrms.Config.localMusicDir) + default: + llog.Error(fmt.Sprintf("Not supported backend %s", backend)) + } - available_backends["dummy"] = &DummyBackend{} + if err != nil { + llog.Error(fmt.Sprintf("Error during initialization of the %s backend: %s", backend, err.Error())) + } else { + available_backends[backend] = b + } + } return Player{available_backends, nil, wrms} } diff --git a/playlist.go b/playlist.go index 1c177e0..c5fa9e1 100644 --- a/playlist.go +++ b/playlist.go @@ -3,7 +3,7 @@ package main import ( "container/heap" "fmt" - "muhq.space/wrms/llog" + "muhq.space/go/wrms/llog" ) type Playlist []*Song diff --git a/web/client.html b/web/client.html index 20a53ed..87ca51a 100644 --- a/web/client.html +++ b/web/client.html @@ -122,10 +122,10 @@ votes.set(song.uri, "down") } - url = "/" + (isUpvoteBtn ? "up" : "down") + "?song=" + song.uri; + url = "/" + (isUpvoteBtn ? "up" : "down") + "?song=" + encodeURIComponent(song.uri); } else { votes.delete(song.uri); - url = "/unvote?song=" + song.uri; + url = "/unvote?song=" + encodeURIComponent(song.uri); } console.log("Get " + url); @@ -137,8 +137,10 @@ listItem.appendChild(upvoteBtn); listItem.appendChild(downvoteBtn); - const itemValue = document.createTextNode(song.weight + ' ' + formatSong(song)); - listItem.appendChild(itemValue); + listItem.appendChild(document.createTextNode(song.weight + ' ' + formatSong(song))); + const sourceLabel = document.createElement("SMALL"); + sourceLabel.innerHTML = " (" + song.source + ")"; + listItem.appendChild(sourceLabel); playlist.appendChild(listItem); } } @@ -257,10 +259,7 @@ }); listItem.classList.add("searchResult") - - let searchResultLabel = document.createTextNode(formatSong(song)); - listItem.appendChild(searchResultLabel); - + listItem.innerHTML = formatSong(song) + " (" + song.source + ")"; searchResults.appendChild(listItem); } diff --git a/wrms.go b/wrms.go index 252c14e..ee3274c 100644 --- a/wrms.go +++ b/wrms.go @@ -3,8 +3,9 @@ package main import ( "encoding/json" "fmt" + "strings" - "muhq.space/wrms/llog" + "muhq.space/go/wrms/llog" "github.com/google/uuid" @@ -63,11 +64,13 @@ type Wrms struct { CurrentSong Song Player Player Playing bool + Config Config } -func NewWrms() *Wrms { +func NewWrms(config Config) *Wrms { wrms := Wrms{} - wrms.Player = NewPlayer(&wrms) + wrms.Config = config + wrms.Player = NewPlayer(&wrms, strings.Split(config.backends, " ")) return &wrms } diff --git a/youtube.go b/youtube.go index a3f92d7..ad715ce 100644 --- a/youtube.go +++ b/youtube.go @@ -3,7 +3,7 @@ package main import ( "fmt" "github.com/AnjanaMadu/YTSearch" - "log" + "muhq.space/go/wrms/llog" ) type YoutubeBackend struct{} @@ -15,7 +15,7 @@ func (youtube *YoutubeBackend) Play(song *Song, player *Player) { func (youtube *YoutubeBackend) Search(pattern string) []Song { results, err := ytsearch.Search(pattern) if err != nil { - log.Fatal("Youtube search failed with", err) + llog.Error(fmt.Sprintf("Youtube search failed with", err)) } songs := []Song{} @@ -31,6 +31,6 @@ func (youtube *YoutubeBackend) Search(pattern string) []Song { songs = append(songs, NewSong(result.Title, "", "youtube", result.VideoId)) } - log.Println(fmt.Sprintf("youtube found %d matching videos", len(songs))) + llog.Debug(fmt.Sprintf("youtube found %d matching videos", len(songs))) return songs } -- cgit v1.2.3