switch from websockets to sse
This commit is contained in:
parent
67335c97b4
commit
534077bad3
|
@ -1,43 +0,0 @@
|
||||||
name: Run tests
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
pull_request:
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
nix:
|
|
||||||
runs-on: docker
|
|
||||||
container:
|
|
||||||
image: nix
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
- name: Nix doctor
|
|
||||||
run: nix doctor
|
|
||||||
- name: Check flake
|
|
||||||
run: nix flake check --all-systems
|
|
||||||
- name: Build flake
|
|
||||||
run: nix build --print-out-paths
|
|
||||||
- name: Flake info
|
|
||||||
run: nix flake info
|
|
||||||
tests:
|
|
||||||
runs-on: docker
|
|
||||||
container:
|
|
||||||
image: debian:stable
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
- name: Setup Go
|
|
||||||
uses: actions/setup-go@v4
|
|
||||||
with:
|
|
||||||
#go-version: "stable"
|
|
||||||
go-version-file: "go.mod"
|
|
||||||
check-latest: true
|
|
||||||
- name: Run Linter
|
|
||||||
uses: dominikh/staticcheck-action@v1
|
|
||||||
with:
|
|
||||||
version: "latest"
|
|
||||||
install-go: false
|
|
||||||
- name: Run tests
|
|
||||||
run: make test
|
|
73
.gitlab-ci.yml
Normal file
73
.gitlab-ci.yml
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
include:
|
||||||
|
- template: Security/Dependency-Scanning.gitlab-ci.yml
|
||||||
|
- template: Security/SAST.gitlab-ci.yml
|
||||||
|
- template: Security/Secret-Detection.gitlab-ci.yml
|
||||||
|
- template: Security/Container-Scanning.gitlab-ci.yml
|
||||||
|
|
||||||
|
variables:
|
||||||
|
# Use TLS https://docs.gitlab.com/ee/ci/docker/using_docker_build.html#tls-enabled
|
||||||
|
DOCKER_HOST: tcp://docker:2376
|
||||||
|
DOCKER_TLS_CERTDIR: "/certs"
|
||||||
|
CONTAINER_TEST_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
|
||||||
|
CONTAINER_RELEASE_IMAGE: $CI_REGISTRY_IMAGE:latest
|
||||||
|
|
||||||
|
container_scanning:
|
||||||
|
variables:
|
||||||
|
CS_DEFAULT_BRANCH_IMAGE: $CI_REGISTRY_IMAGE/$CI_DEFAULT_BRANCH:$CI_COMMIT_SHA
|
||||||
|
|
||||||
|
image: golang:latest
|
||||||
|
|
||||||
|
stages:
|
||||||
|
- test
|
||||||
|
- build
|
||||||
|
- release
|
||||||
|
|
||||||
|
format:
|
||||||
|
stage: test
|
||||||
|
script:
|
||||||
|
- go fmt $(go list ./...)
|
||||||
|
|
||||||
|
vet:
|
||||||
|
stage: test
|
||||||
|
script:
|
||||||
|
- go vet $(go list ./...)
|
||||||
|
|
||||||
|
test:
|
||||||
|
stage: test
|
||||||
|
script:
|
||||||
|
- go test -race $(go list ./...)
|
||||||
|
|
||||||
|
sast:
|
||||||
|
stage: test
|
||||||
|
|
||||||
|
compile:
|
||||||
|
stage: build
|
||||||
|
script:
|
||||||
|
- mkdir -p bin
|
||||||
|
- go build -v -o bin ./...
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- bin
|
||||||
|
|
||||||
|
build:
|
||||||
|
stage: build
|
||||||
|
image: docker:stable
|
||||||
|
services:
|
||||||
|
- docker:stable-dind
|
||||||
|
script:
|
||||||
|
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
|
||||||
|
- docker build --pull -t $CONTAINER_TEST_IMAGE .
|
||||||
|
- docker push $CONTAINER_TEST_IMAGE
|
||||||
|
|
||||||
|
release:
|
||||||
|
stage: release
|
||||||
|
image: docker:stable
|
||||||
|
services:
|
||||||
|
- docker:stable-dind
|
||||||
|
script:
|
||||||
|
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
|
||||||
|
- docker pull $CONTAINER_TEST_IMAGE
|
||||||
|
- docker tag $CONTAINER_TEST_IMAGE $CONTAINER_RELEASE_IMAGE
|
||||||
|
- docker push $CONTAINER_RELEASE_IMAGE
|
||||||
|
only:
|
||||||
|
- main
|
3
Makefile
3
Makefile
|
@ -6,6 +6,7 @@ PROJECT := sanic
|
||||||
|
|
||||||
mpd: ## Run mpd test instance
|
mpd: ## Run mpd test instance
|
||||||
mkdir -p /tmp/${PROJECT}/{music,playlists}
|
mkdir -p /tmp/${PROJECT}/{music,playlists}
|
||||||
|
cp *.mp3 /tmp/${PROJECT}/music/
|
||||||
touch /tmp/${PROJECT}/mpd_db
|
touch /tmp/${PROJECT}/mpd_db
|
||||||
mpd --no-daemon ./mpd.conf
|
mpd --no-daemon ./mpd.conf
|
||||||
|
|
||||||
|
@ -17,7 +18,7 @@ build: ## Compile project
|
||||||
|
|
||||||
update: ## Update go dependencies
|
update: ## Update go dependencies
|
||||||
go get -u
|
go get -u
|
||||||
which gomod2nix && gomod2nix
|
which gomod2nix && gomod2nix # sync go deps with nix
|
||||||
|
|
||||||
tidy: ## Add missing and remove unused modules
|
tidy: ## Add missing and remove unused modules
|
||||||
go mod tidy
|
go mod tidy
|
||||||
|
|
12
README.md
12
README.md
|
@ -28,7 +28,7 @@ Example flake setup (untested):
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||||
sanic = {
|
sanic = {
|
||||||
url = "git.berlin.ccc.de/cccb/sanic";
|
url = "gitlab.com/XenGi/sanic";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -52,10 +52,18 @@ Example flake setup (untested):
|
||||||
|
|
||||||
Install from the AUR:
|
Install from the AUR:
|
||||||
|
|
||||||
```
|
```shell
|
||||||
yay -S sanic
|
yay -S sanic
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Podman
|
||||||
|
|
||||||
|
Run as daemon:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
podman run -d -v ./config.ini:/config.ini -p 8443:8443 registry.gitlab.com/XenGi/sanic:latest
|
||||||
|
```
|
||||||
|
|
||||||
## 🛠️ Development
|
## 🛠️ Development
|
||||||
|
|
||||||
sanic is developed using [Nix][nix], but you can also just use the usual Golang tooling.
|
sanic is developed using [Nix][nix], but you can also just use the usual Golang tooling.
|
||||||
|
|
|
@ -6,7 +6,7 @@ port = 6600
|
||||||
|
|
||||||
[ui]
|
[ui]
|
||||||
hostname = [::1]
|
hostname = [::1]
|
||||||
port = 8080
|
port = 8443
|
||||||
tls = no
|
tls = yes
|
||||||
cert = cert.pem
|
cert = cert.pem
|
||||||
key = key.pem
|
key = key.pem
|
||||||
|
|
12
flake.lock
12
flake.lock
|
@ -28,11 +28,11 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1717050755,
|
"lastModified": 1722589758,
|
||||||
"narHash": "sha256-C9IEHABulv2zEDFA+Bf0E1nmfN4y6MIUe5eM2RCrDC0=",
|
"narHash": "sha256-sbbA8b6Q2vB/t/r1znHawoXLysCyD4L/6n6/RykiSnA=",
|
||||||
"owner": "tweag",
|
"owner": "tweag",
|
||||||
"repo": "gomod2nix",
|
"repo": "gomod2nix",
|
||||||
"rev": "31b6d2e40b36456e792cd6cf50d5a8ddd2fa59a1",
|
"rev": "4e08ca09253ef996bd4c03afa383b23e35fe28a1",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -43,11 +43,11 @@
|
||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1720955038,
|
"lastModified": 1722957468,
|
||||||
"narHash": "sha256-GaliJqfFwyYxReFywxAa8orCO+EnDq2NK2F+5aSc8vo=",
|
"narHash": "sha256-SQ0TCC4aklOhN/OzcztrKqDLY8SjpIZcyvTulzhDXs0=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "aa247c0c90ecf4ae7a032c54fdc21b91ca274062",
|
"rev": "2a13929e1f191b3690dd2f2db13098b04adb9043",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
21
go.mod
21
go.mod
|
@ -4,9 +4,9 @@ go 1.22
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/fhs/gompd/v2 v2.3.0
|
github.com/fhs/gompd/v2 v2.3.0
|
||||||
github.com/labstack/echo-contrib v0.17.0
|
github.com/labstack/echo-contrib v0.17.1
|
||||||
github.com/labstack/echo/v4 v4.12.0
|
github.com/labstack/echo/v4 v4.12.0
|
||||||
golang.org/x/net v0.24.0
|
golang.org/x/net v0.28.0
|
||||||
gopkg.in/ini.v1 v1.67.0
|
gopkg.in/ini.v1 v1.67.0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -17,15 +17,16 @@ require (
|
||||||
github.com/labstack/gommon v0.4.2 // indirect
|
github.com/labstack/gommon v0.4.2 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/prometheus/client_golang v1.19.0 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
|
github.com/prometheus/client_golang v1.19.1 // indirect
|
||||||
github.com/prometheus/client_model v0.6.1 // indirect
|
github.com/prometheus/client_model v0.6.1 // indirect
|
||||||
github.com/prometheus/common v0.52.3 // indirect
|
github.com/prometheus/common v0.55.0 // indirect
|
||||||
github.com/prometheus/procfs v0.13.0 // indirect
|
github.com/prometheus/procfs v0.15.1 // indirect
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||||
golang.org/x/crypto v0.22.0 // indirect
|
golang.org/x/crypto v0.26.0 // indirect
|
||||||
golang.org/x/sys v0.19.0 // indirect
|
golang.org/x/sys v0.23.0 // indirect
|
||||||
golang.org/x/text v0.14.0 // indirect
|
golang.org/x/text v0.17.0 // indirect
|
||||||
golang.org/x/time v0.5.0 // indirect
|
golang.org/x/time v0.6.0 // indirect
|
||||||
google.golang.org/protobuf v1.33.0 // indirect
|
google.golang.org/protobuf v1.34.2 // indirect
|
||||||
)
|
)
|
||||||
|
|
22
go.sum
22
go.sum
|
@ -12,6 +12,8 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/labstack/echo-contrib v0.17.0 h1:xam8wakZOsiQYM14Z0og1xF3w/heWNeDF5AtC5PlX8E=
|
github.com/labstack/echo-contrib v0.17.0 h1:xam8wakZOsiQYM14Z0og1xF3w/heWNeDF5AtC5PlX8E=
|
||||||
github.com/labstack/echo-contrib v0.17.0/go.mod h1:mjX5VB3OqJcroIEycptBOY9Hr7rK+unq79W8QFKGNV0=
|
github.com/labstack/echo-contrib v0.17.0/go.mod h1:mjX5VB3OqJcroIEycptBOY9Hr7rK+unq79W8QFKGNV0=
|
||||||
|
github.com/labstack/echo-contrib v0.17.1 h1:7I/he7ylVKsDUieaGRZ9XxxTYOjfQwVzHzUYrNykfCU=
|
||||||
|
github.com/labstack/echo-contrib v0.17.1/go.mod h1:SnsCZtwHBAZm5uBSAtQtXQHI3wqEA73hvTn0bYMKnZA=
|
||||||
github.com/labstack/echo/v4 v4.12.0 h1:IKpw49IMryVB2p1a4dzwlhP1O2Tf2E0Ir/450lH+kI0=
|
github.com/labstack/echo/v4 v4.12.0 h1:IKpw49IMryVB2p1a4dzwlhP1O2Tf2E0Ir/450lH+kI0=
|
||||||
github.com/labstack/echo/v4 v4.12.0/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM=
|
github.com/labstack/echo/v4 v4.12.0/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM=
|
||||||
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
|
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
|
||||||
|
@ -21,16 +23,24 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
|
||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU=
|
github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU=
|
||||||
github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
|
github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
|
||||||
|
github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
|
||||||
|
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
|
||||||
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||||
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||||
github.com/prometheus/common v0.52.3 h1:5f8uj6ZwHSscOGNdIQg6OiZv/ybiK2CO2q2drVZAQSA=
|
github.com/prometheus/common v0.52.3 h1:5f8uj6ZwHSscOGNdIQg6OiZv/ybiK2CO2q2drVZAQSA=
|
||||||
github.com/prometheus/common v0.52.3/go.mod h1:BrxBKv3FWBIGXw89Mg1AeBq7FSyRzXWI3l3e7W3RN5U=
|
github.com/prometheus/common v0.52.3/go.mod h1:BrxBKv3FWBIGXw89Mg1AeBq7FSyRzXWI3l3e7W3RN5U=
|
||||||
|
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
|
||||||
|
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
|
||||||
github.com/prometheus/procfs v0.13.0 h1:GqzLlQyfsPbaEHaQkO7tbDlriv/4o5Hudv6OXHGKX7o=
|
github.com/prometheus/procfs v0.13.0 h1:GqzLlQyfsPbaEHaQkO7tbDlriv/4o5Hudv6OXHGKX7o=
|
||||||
github.com/prometheus/procfs v0.13.0/go.mod h1:cd4PFCR54QLnGKPaKGA6l+cfuNXtht43ZKY6tow0Y1g=
|
github.com/prometheus/procfs v0.13.0/go.mod h1:cd4PFCR54QLnGKPaKGA6l+cfuNXtht43ZKY6tow0Y1g=
|
||||||
|
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
||||||
|
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
|
@ -39,18 +49,30 @@ github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQ
|
||||||
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||||
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
|
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
|
||||||
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
||||||
|
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
|
||||||
|
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
|
||||||
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
||||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
||||||
|
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
|
||||||
|
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
||||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM=
|
||||||
|
golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
|
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
|
||||||
|
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
|
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
|
||||||
|
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||||
|
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||||
|
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
|
|
@ -14,8 +14,8 @@ schema = 3
|
||||||
version = "v3.2.2+incompatible"
|
version = "v3.2.2+incompatible"
|
||||||
hash = "sha256-LOkpuXhWrFayvVf1GOaOmZI5YKEsgqVSb22aF8LnCEM="
|
hash = "sha256-LOkpuXhWrFayvVf1GOaOmZI5YKEsgqVSb22aF8LnCEM="
|
||||||
[mod."github.com/labstack/echo-contrib"]
|
[mod."github.com/labstack/echo-contrib"]
|
||||||
version = "v0.17.0"
|
version = "v0.17.1"
|
||||||
hash = "sha256-J5S8vO8Zg9uV9A3zbILswn/oPdwLKRXncsXdEjDOmU8="
|
hash = "sha256-OkO1gWz1xL8Rw2wy4RMJjsLGhABAqAsjpVkQ3b6G+L4="
|
||||||
[mod."github.com/labstack/echo/v4"]
|
[mod."github.com/labstack/echo/v4"]
|
||||||
version = "v4.12.0"
|
version = "v4.12.0"
|
||||||
hash = "sha256-TPXJv/6C53bnmcEYxa9g5Mft8u/rLT96q64tQ9+RtKU="
|
hash = "sha256-TPXJv/6C53bnmcEYxa9g5Mft8u/rLT96q64tQ9+RtKU="
|
||||||
|
@ -28,18 +28,21 @@ schema = 3
|
||||||
[mod."github.com/mattn/go-isatty"]
|
[mod."github.com/mattn/go-isatty"]
|
||||||
version = "v0.0.20"
|
version = "v0.0.20"
|
||||||
hash = "sha256-qhw9hWtU5wnyFyuMbKx+7RB8ckQaFQ8D+8GKPkN3HHQ="
|
hash = "sha256-qhw9hWtU5wnyFyuMbKx+7RB8ckQaFQ8D+8GKPkN3HHQ="
|
||||||
|
[mod."github.com/munnerz/goautoneg"]
|
||||||
|
version = "v0.0.0-20191010083416-a7dc8b61c822"
|
||||||
|
hash = "sha256-79URDDFenmGc9JZu+5AXHToMrtTREHb3BC84b/gym9Q="
|
||||||
[mod."github.com/prometheus/client_golang"]
|
[mod."github.com/prometheus/client_golang"]
|
||||||
version = "v1.19.0"
|
version = "v1.19.1"
|
||||||
hash = "sha256-YV8sxMPR+xorTUCriTfcFsaV2b7PZfPJDQmOgUYOZJo="
|
hash = "sha256-MSLsMDi89uQc7Pa2fhqeamyfPJpenGj3r+eB/UotK7w="
|
||||||
[mod."github.com/prometheus/client_model"]
|
[mod."github.com/prometheus/client_model"]
|
||||||
version = "v0.6.1"
|
version = "v0.6.1"
|
||||||
hash = "sha256-rIDyUzNfxRA934PIoySR0EhuBbZVRK/25Jlc/r8WODw="
|
hash = "sha256-rIDyUzNfxRA934PIoySR0EhuBbZVRK/25Jlc/r8WODw="
|
||||||
[mod."github.com/prometheus/common"]
|
[mod."github.com/prometheus/common"]
|
||||||
version = "v0.52.3"
|
version = "v0.55.0"
|
||||||
hash = "sha256-JzNAt7pimXZMnPxv8droAHJq+5OKWi9BYkGtMvqpyd0="
|
hash = "sha256-qzvCnc+hnAB5dq2MYy8GlPxgyNnyn9kFVlN2CXZe9T0="
|
||||||
[mod."github.com/prometheus/procfs"]
|
[mod."github.com/prometheus/procfs"]
|
||||||
version = "v0.13.0"
|
version = "v0.15.1"
|
||||||
hash = "sha256-J31K36TkIiQU2EGOcmqDa+dkoKXiVuxafPVT4rKbEsg="
|
hash = "sha256-H+WXJemFFwdoglmD6p7JRjrJJZmIVAmJwYmLbZ8Q9sw="
|
||||||
[mod."github.com/valyala/bytebufferpool"]
|
[mod."github.com/valyala/bytebufferpool"]
|
||||||
version = "v1.0.0"
|
version = "v1.0.0"
|
||||||
hash = "sha256-I9FPZ3kCNRB+o0dpMwBnwZ35Fj9+ThvITn8a3Jr8mAY="
|
hash = "sha256-I9FPZ3kCNRB+o0dpMwBnwZ35Fj9+ThvITn8a3Jr8mAY="
|
||||||
|
@ -47,23 +50,23 @@ schema = 3
|
||||||
version = "v1.2.2"
|
version = "v1.2.2"
|
||||||
hash = "sha256-gp+lNXE8zjO+qJDM/YbS6V43HFsYP6PKn4ux1qa5lZ0="
|
hash = "sha256-gp+lNXE8zjO+qJDM/YbS6V43HFsYP6PKn4ux1qa5lZ0="
|
||||||
[mod."golang.org/x/crypto"]
|
[mod."golang.org/x/crypto"]
|
||||||
version = "v0.22.0"
|
version = "v0.26.0"
|
||||||
hash = "sha256-2+u9nd32+Bi7EEv7QFc12CRTbfV7DApNv+yKIr7+lTw="
|
hash = "sha256-Iicrsb65fCmjfPILKoSLyBZMwe2VUcoTF5SpYTCQEuk="
|
||||||
[mod."golang.org/x/net"]
|
[mod."golang.org/x/net"]
|
||||||
version = "v0.24.0"
|
version = "v0.28.0"
|
||||||
hash = "sha256-w1c21ljta5wNIyel9CSIn/crPzwOCRofNKhqmfs4aEQ="
|
hash = "sha256-WdH/mgsX/CB+CiYtXEwJAXHN8FgtW2YhFcWwrrHNBLo="
|
||||||
[mod."golang.org/x/sys"]
|
[mod."golang.org/x/sys"]
|
||||||
version = "v0.19.0"
|
version = "v0.23.0"
|
||||||
hash = "sha256-cmuL31TYLJmDm/fDnI2Sn0wB88cpdOHV1+urorsJWx4="
|
hash = "sha256-tC6QVLu72bADgINz26FUGdmYqKgsU45bHPg7sa0ZV7w="
|
||||||
[mod."golang.org/x/text"]
|
[mod."golang.org/x/text"]
|
||||||
version = "v0.14.0"
|
version = "v0.17.0"
|
||||||
hash = "sha256-yh3B0tom1RfzQBf1RNmfdNWF1PtiqxV41jW1GVS6JAg="
|
hash = "sha256-R8JbsP7KX+KFTHH7SjRnUGCdvtagylVOfngWEnVSqBc="
|
||||||
[mod."golang.org/x/time"]
|
[mod."golang.org/x/time"]
|
||||||
version = "v0.5.0"
|
version = "v0.6.0"
|
||||||
hash = "sha256-W6RgwgdYTO3byIPOFxrP2IpAZdgaGowAaVfYby7AULU="
|
hash = "sha256-gW9TVK9HjLk52lzfo5rBzSunc01gS0+SG2nk0X1w55M="
|
||||||
[mod."google.golang.org/protobuf"]
|
[mod."google.golang.org/protobuf"]
|
||||||
version = "v1.33.0"
|
version = "v1.34.2"
|
||||||
hash = "sha256-cWwQjtUwSIEkAlAadrlxK1PYZXTRrV4NKzt7xDpJgIU="
|
hash = "sha256-nMTlrDEE2dbpWz50eQMPBQXCyQh4IdjrTIccaU0F3m0="
|
||||||
[mod."gopkg.in/ini.v1"]
|
[mod."gopkg.in/ini.v1"]
|
||||||
version = "v1.67.0"
|
version = "v1.67.0"
|
||||||
hash = "sha256-V10ahGNGT+NLRdKUyRg1dos5RxLBXBk1xutcnquc/+4="
|
hash = "sha256-V10ahGNGT+NLRdKUyRg1dos5RxLBXBk1xutcnquc/+4="
|
||||||
|
|
106
server.go
106
server.go
|
@ -1,19 +1,14 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/fhs/gompd/v2/mpd"
|
|
||||||
"github.com/labstack/echo-contrib/echoprometheus"
|
"github.com/labstack/echo-contrib/echoprometheus"
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
"github.com/labstack/echo/v4/middleware"
|
"github.com/labstack/echo/v4/middleware"
|
||||||
"golang.org/x/net/websocket"
|
|
||||||
"gopkg.in/ini.v1"
|
"gopkg.in/ini.v1"
|
||||||
"log"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config holds the configuration for the mpd connection and for the web server.
|
// Config holds the configuration for the mpd connection and for the web server.
|
||||||
|
@ -78,6 +73,21 @@ func main() {
|
||||||
return c.File("index.html")
|
return c.File("index.html")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// echo back request to check if HTTP/2 works etc
|
||||||
|
e.GET("/echo", func(c echo.Context) error {
|
||||||
|
req := c.Request()
|
||||||
|
format := `
|
||||||
|
<code>
|
||||||
|
Protocol: %s<br>
|
||||||
|
Host: %s<br>
|
||||||
|
Remote Address: %s<br>
|
||||||
|
Method: %s<br>
|
||||||
|
Path: %s<br>
|
||||||
|
</code>
|
||||||
|
`
|
||||||
|
return c.HTML(http.StatusOK, fmt.Sprintf(format, req.Proto, req.Host, req.RemoteAddr, req.Method, req.URL.Path))
|
||||||
|
})
|
||||||
|
|
||||||
g := e.Group("/api")
|
g := e.Group("/api")
|
||||||
g.GET("/update_db", updateDb)
|
g.GET("/update_db", updateDb)
|
||||||
g.GET("/previous_track", previousTrack)
|
g.GET("/previous_track", previousTrack)
|
||||||
|
@ -104,7 +114,7 @@ func main() {
|
||||||
|
|
||||||
g.GET("/download", downloadTrack)
|
g.GET("/download", downloadTrack)
|
||||||
|
|
||||||
e.GET("/ws", wsServe)
|
e.GET("/sse", serveSSE)
|
||||||
|
|
||||||
if config.UI.Tls {
|
if config.UI.Tls {
|
||||||
e.Logger.Fatal(e.StartTLS(fmt.Sprintf("%s:%d", config.UI.Hostname, config.UI.Port), config.UI.Certificate, config.UI.Key))
|
e.Logger.Fatal(e.StartTLS(fmt.Sprintf("%s:%d", config.UI.Hostname, config.UI.Port), config.UI.Certificate, config.UI.Key))
|
||||||
|
@ -113,90 +123,8 @@ func main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// wsServe handles websocket connections.
|
|
||||||
func wsServe(c echo.Context) error {
|
|
||||||
websocket.Handler(func(ws *websocket.Conn) {
|
|
||||||
defer ws.Close()
|
|
||||||
|
|
||||||
// Connect to MPD server
|
|
||||||
mpdConn, err := mpd.Dial("tcp", "localhost:6600")
|
|
||||||
if err != nil {
|
|
||||||
//log.Fatalln(err)
|
|
||||||
c.Logger().Error(err)
|
|
||||||
err = websocket.Message.Send(ws, fmt.Sprintf("{\"mpd_error\":\"%s\"}", err.Error()))
|
|
||||||
if err != nil {
|
|
||||||
c.Logger().Error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
defer mpdConn.Close()
|
|
||||||
|
|
||||||
for {
|
|
||||||
// Read
|
|
||||||
msg := ""
|
|
||||||
err := websocket.Message.Receive(ws, &msg)
|
|
||||||
if err != nil {
|
|
||||||
c.Logger().Error(err)
|
|
||||||
break
|
|
||||||
} else {
|
|
||||||
// log.Print(msg)
|
|
||||||
if strings.ToLower(msg) == "#status" {
|
|
||||||
status, err := mpdConn.Status()
|
|
||||||
if err != nil {
|
|
||||||
c.Logger().Error(err)
|
|
||||||
}
|
|
||||||
currentsong, err := mpdConn.CurrentSong()
|
|
||||||
if err != nil {
|
|
||||||
c.Logger().Error(err)
|
|
||||||
}
|
|
||||||
queue, err := mpdConn.PlaylistInfo(-1, -1)
|
|
||||||
if err != nil {
|
|
||||||
c.Logger().Error(err)
|
|
||||||
}
|
|
||||||
jsonStatus, err := json.Marshal(status)
|
|
||||||
if err != nil {
|
|
||||||
c.Logger().Error(err)
|
|
||||||
}
|
|
||||||
jsonCurrentSong, err := json.Marshal(currentsong)
|
|
||||||
if err != nil {
|
|
||||||
c.Logger().Error(err)
|
|
||||||
}
|
|
||||||
jsonQueue, err := json.Marshal(queue)
|
|
||||||
if err != nil {
|
|
||||||
c.Logger().Error(err)
|
|
||||||
}
|
|
||||||
err = websocket.Message.Send(ws, fmt.Sprintf("{\"mpd_status\":%s,\"mpd_current_song\":%s,\"mpd_queue\":%s}", string(jsonStatus), string(jsonCurrentSong), string(jsonQueue)))
|
|
||||||
if err != nil {
|
|
||||||
c.Logger().Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if strings.HasPrefix(strings.ToLower(msg), "#download ") {
|
|
||||||
// Download video link as audio file
|
|
||||||
uri := strings.SplitN(msg, " ", 2)[1]
|
|
||||||
// TODO: implement yt-dlp integration
|
|
||||||
err := websocket.Message.Send(ws, fmt.Sprintf("Downloading %s", uri))
|
|
||||||
if err != nil {
|
|
||||||
c.Logger().Error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).ServeHTTP(c.Response(), c.Request())
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// downloadTrack tries to download a given URL and saves the song to the database.
|
// downloadTrack tries to download a given URL and saves the song to the database.
|
||||||
func downloadTrack(c echo.Context) error {
|
func downloadTrack(c echo.Context) error {
|
||||||
// yt-dlp \
|
|
||||||
// --no-wait-for-video \
|
|
||||||
// --no-playlist \
|
|
||||||
// --windows-filenames \
|
|
||||||
// --newline \
|
|
||||||
// --extract-audio \
|
|
||||||
// --audio-format mp3 \
|
|
||||||
// --audio-quality 0 \
|
|
||||||
// -f bestaudio/best \
|
|
||||||
// ${video_url}
|
|
||||||
|
|
||||||
cmd := exec.Command(
|
cmd := exec.Command(
|
||||||
"yt-dlp",
|
"yt-dlp",
|
||||||
"--no-wait-for-video",
|
"--no-wait-for-video",
|
||||||
|
@ -212,7 +140,7 @@ func downloadTrack(c echo.Context) error {
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
if err := cmd.Run(); err != nil {
|
if err := cmd.Run(); err != nil {
|
||||||
log.Fatalln(err)
|
c.Logger().Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.String(http.StatusAccepted, "")
|
return c.String(http.StatusAccepted, "")
|
||||||
|
|
191
sse.go
Normal file
191
sse.go
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"github.com/fhs/gompd/v2/mpd"
|
||||||
|
"github.com/labstack/echo/v4"
|
||||||
|
"io"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Event represents Server-Sent Event.
|
||||||
|
// SSE explanation: https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#event_stream_format
|
||||||
|
type Event struct {
|
||||||
|
// ID is used to set the EventSource object's last event ID value.
|
||||||
|
ID []byte
|
||||||
|
// Data field is for the message. When the EventSource receives multiple consecutive lines
|
||||||
|
// that begin with data:, it concatenates them, inserting a newline character between each one.
|
||||||
|
// Trailing newlines are removed.
|
||||||
|
Data []byte
|
||||||
|
// Event is a string identifying the type of event described. If this is specified, an event
|
||||||
|
// will be dispatched on the browser to the listener for the specified event name; the website
|
||||||
|
// source code should use addEventListener() to listen for named events. The onmessage handler
|
||||||
|
// is called if no event name is specified for a message.
|
||||||
|
Event []byte
|
||||||
|
// Retry is the reconnection time. If the connection to the server is lost, the browser will
|
||||||
|
// wait for the specified time before attempting to reconnect. This must be an integer, specifying
|
||||||
|
// the reconnection time in milliseconds. If a non-integer value is specified, the field is ignored.
|
||||||
|
Retry []byte
|
||||||
|
// Comment line can be used to prevent connections from timing out; a server can send a comment
|
||||||
|
// periodically to keep the connection alive.
|
||||||
|
Comment []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalTo marshals Event to given Writer
|
||||||
|
func (ev *Event) MarshalTo(w io.Writer) error {
|
||||||
|
// Marshalling part is taken from: https://github.com/r3labs/sse/blob/c6d5381ee3ca63828b321c16baa008fd6c0b4564/http.go#L16
|
||||||
|
if len(ev.Data) == 0 && len(ev.Comment) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ev.Data) > 0 {
|
||||||
|
if _, err := fmt.Fprintf(w, "id: %s\n", ev.ID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
sd := bytes.Split(ev.Data, []byte("\n"))
|
||||||
|
for i := range sd {
|
||||||
|
if _, err := fmt.Fprintf(w, "data: %s\n", sd[i]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ev.Event) > 0 {
|
||||||
|
if _, err := fmt.Fprintf(w, "event: %s\n", ev.Event); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ev.Retry) > 0 {
|
||||||
|
if _, err := fmt.Fprintf(w, "retry: %s\n", ev.Retry); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ev.Comment) > 0 {
|
||||||
|
if _, err := fmt.Fprintf(w, ": %s\n", ev.Comment); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := fmt.Fprint(w, "\n"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// serveSSE handles sending Server-Sent-Events.
|
||||||
|
func serveSSE(c echo.Context) error {
|
||||||
|
// TODO: figure out how to retrieve IP from Forwarded header behind proxy: https://echo.labstack.com/docs/ip-address
|
||||||
|
c.Logger().Printf("SSE client connected, ip: %v", c.RealIP())
|
||||||
|
|
||||||
|
w := c.Response()
|
||||||
|
w.Header().Set("Content-Type", "text/event-stream")
|
||||||
|
w.Header().Set("Cache-Control", "no-cache")
|
||||||
|
w.Header().Set("Connection", "keep-alive")
|
||||||
|
|
||||||
|
// Connect to MPD server
|
||||||
|
mpdConn, err := mpd.Dial("tcp", "localhost:6600")
|
||||||
|
if err != nil {
|
||||||
|
c.Logger().Error(err)
|
||||||
|
event := Event{
|
||||||
|
Event: []byte("mpd"),
|
||||||
|
Data: []byte(fmt.Sprintf("connection error: %s", err.Error())),
|
||||||
|
}
|
||||||
|
if err := event.MarshalTo(w); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
w.Flush()
|
||||||
|
}
|
||||||
|
defer mpdConn.Close()
|
||||||
|
|
||||||
|
ticker := time.NewTicker(1 * time.Second)
|
||||||
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
var lastJsonStatus []byte
|
||||||
|
var lastJsonCurrentSong []byte
|
||||||
|
var lastJsonQueue []byte
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-c.Request().Context().Done():
|
||||||
|
c.Logger().Printf("SSE client disconnected, ip: %v", c.RealIP())
|
||||||
|
return nil
|
||||||
|
case <-ticker.C:
|
||||||
|
c.Logger().Printf("Getting MPD status for %v", c.RealIP())
|
||||||
|
|
||||||
|
status, err := mpdConn.Status()
|
||||||
|
if err != nil {
|
||||||
|
c.Logger().Error(err)
|
||||||
|
}
|
||||||
|
jsonStatus, err := json.Marshal(status)
|
||||||
|
if err != nil {
|
||||||
|
c.Logger().Error(err)
|
||||||
|
}
|
||||||
|
// Only send new event if different from last time
|
||||||
|
if !bytes.Equal(jsonStatus, lastJsonStatus) {
|
||||||
|
statusEvent := Event{
|
||||||
|
Event: []byte("status"),
|
||||||
|
Data: []byte(string(jsonStatus)),
|
||||||
|
}
|
||||||
|
if err := statusEvent.MarshalTo(w); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
lastJsonStatus = jsonStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
currentsong, err := mpdConn.CurrentSong()
|
||||||
|
if err != nil {
|
||||||
|
c.Logger().Error(err)
|
||||||
|
}
|
||||||
|
jsonCurrentSong, err := json.Marshal(currentsong)
|
||||||
|
if err != nil {
|
||||||
|
c.Logger().Error(err)
|
||||||
|
}
|
||||||
|
// Only send new event if different from last time
|
||||||
|
if !bytes.Equal(jsonCurrentSong, lastJsonCurrentSong) {
|
||||||
|
currentSongEvent := Event{
|
||||||
|
Event: []byte("status"),
|
||||||
|
Data: []byte(string(jsonCurrentSong)),
|
||||||
|
}
|
||||||
|
if err := currentSongEvent.MarshalTo(w); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
lastJsonCurrentSong = jsonCurrentSong
|
||||||
|
}
|
||||||
|
|
||||||
|
queue, err := mpdConn.PlaylistInfo(-1, -1)
|
||||||
|
if err != nil {
|
||||||
|
c.Logger().Error(err)
|
||||||
|
}
|
||||||
|
jsonQueue, err := json.Marshal(queue)
|
||||||
|
if err != nil {
|
||||||
|
c.Logger().Error(err)
|
||||||
|
}
|
||||||
|
// Only send new event if different from last time
|
||||||
|
if !bytes.Equal(jsonQueue, lastJsonQueue) {
|
||||||
|
queueEvent := Event{
|
||||||
|
Event: []byte("status"),
|
||||||
|
Data: []byte(string(jsonQueue)),
|
||||||
|
}
|
||||||
|
if err := queueEvent.MarshalTo(w); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
lastJsonQueue = jsonQueue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ping to prevent timeout
|
||||||
|
pingEvent := Event{
|
||||||
|
Comment: []byte("ping"),
|
||||||
|
}
|
||||||
|
if err := pingEvent.MarshalTo(w); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -197,7 +197,6 @@
|
||||||
<div id="playlist-browser" style="display: none">
|
<div id="playlist-browser" style="display: none">
|
||||||
<label for="control-playlist-list"></label>
|
<label for="control-playlist-list"></label>
|
||||||
<select id="control-playlist-list" size="15">
|
<select id="control-playlist-list" size="15">
|
||||||
<option value="1">basedrive</option><!-- TODO: Remove this line -->
|
|
||||||
</select><!--/#control-playlist-list-->
|
</select><!--/#control-playlist-list-->
|
||||||
<div>
|
<div>
|
||||||
<button id="control-refresh-playlists">🔄 Refresh</button><!-- 🔄 Counterclockwise Arrows Button -->
|
<button id="control-refresh-playlists">🔄 Refresh</button><!-- 🔄 Counterclockwise Arrows Button -->
|
||||||
|
@ -223,62 +222,9 @@
|
||||||
</table>
|
</table>
|
||||||
</div><!--/#result-->
|
</div><!--/#result-->
|
||||||
<footer>
|
<footer>
|
||||||
<a href="https://git.berlin.ccc.de/cccb/sanic"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 92 92"><defs><clipPath id="a"><path d="M0 .113h91.887V92H0Zm0 0"/></clipPath></defs><g clip-path="url(#a)"><path style="stroke:none;fill-rule:nonzero;fill:#ffffff;fill-opacity:1" d="M90.156 41.965 50.036 1.848a5.913 5.913 0 0 0-8.368 0l-8.332 8.332 10.566 10.566a7.03 7.03 0 0 1 7.23 1.684 7.043 7.043 0 0 1 1.673 7.277l10.183 10.184a7.026 7.026 0 0 1 7.278 1.672 7.04 7.04 0 0 1 0 9.957 7.045 7.045 0 0 1-9.961 0 7.038 7.038 0 0 1-1.532-7.66l-9.5-9.497V59.36a7.04 7.04 0 0 1 1.86 11.29 7.04 7.04 0 0 1-9.957 0 7.04 7.04 0 0 1 0-9.958 7.034 7.034 0 0 1 2.308-1.539V33.926a7.001 7.001 0 0 1-2.308-1.535 7.049 7.049 0 0 1-1.516-7.7L29.242 14.273 1.734 41.777a5.918 5.918 0 0 0 0 8.371L41.855 90.27a5.92 5.92 0 0 0 8.368 0l39.933-39.934a5.925 5.925 0 0 0 0-8.371"/></g></svg></a> Sanic MPD Web UI 0.1.0 - by XenGi and coon © 2023
|
<a href="https://gitlab.com/XenGi/sanic"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 92 92"><defs><clipPath id="a"><path d="M0 .113h91.887V92H0Zm0 0"/></clipPath></defs><g clip-path="url(#a)"><path style="stroke:none;fill-rule:nonzero;fill:#ffffff;fill-opacity:1" d="M90.156 41.965 50.036 1.848a5.913 5.913 0 0 0-8.368 0l-8.332 8.332 10.566 10.566a7.03 7.03 0 0 1 7.23 1.684 7.043 7.043 0 0 1 1.673 7.277l10.183 10.184a7.026 7.026 0 0 1 7.278 1.672 7.04 7.04 0 0 1 0 9.957 7.045 7.045 0 0 1-9.961 0 7.038 7.038 0 0 1-1.532-7.66l-9.5-9.497V59.36a7.04 7.04 0 0 1 1.86 11.29 7.04 7.04 0 0 1-9.957 0 7.04 7.04 0 0 1 0-9.958 7.034 7.034 0 0 1 2.308-1.539V33.926a7.001 7.001 0 0 1-2.308-1.535 7.049 7.049 0 0 1-1.516-7.7L29.242 14.273 1.734 41.777a5.918 5.918 0 0 0 0 8.371L41.855 90.27a5.92 5.92 0 0 0 8.368 0l39.933-39.934a5.925 5.925 0 0 0 0-8.371"/></g></svg></a> Sanic MPD Web UI 0.1.0 - by XenGi and coon © 2023
|
||||||
</footer>
|
</footer>
|
||||||
</main>
|
</main>
|
||||||
<script src="index.js"></script>
|
<script src="index.js"></script>
|
||||||
<script>
|
|
||||||
const table = document.querySelector("#queue > table > tbody");
|
|
||||||
for (let i = 1; i <= 100; i++) {
|
|
||||||
const tr = document.createElement("tr");
|
|
||||||
if (i === 1) {
|
|
||||||
tr.classList.add("playing");
|
|
||||||
tr.dataset.song_id = i;
|
|
||||||
}
|
|
||||||
const pos = document.createElement("td");
|
|
||||||
pos.innerText = i.toString();
|
|
||||||
const artist = document.createElement("td");
|
|
||||||
artist.innerText = `Artist ${i.toString()} with a super long name that should finally bring the length to a maximum`;
|
|
||||||
const track = document.createElement("td");
|
|
||||||
track.innerText = `Track ${i.toString()} with super long name that could potentially expand the table by a lot!`;
|
|
||||||
const album = document.createElement("td");
|
|
||||||
album.innerText = `Album ${i.toString()}`;
|
|
||||||
const length = document.createElement("td");
|
|
||||||
length.innerText = "01:00:00";
|
|
||||||
const actions = document.createElement("td");
|
|
||||||
actions.classList.add("actions");
|
|
||||||
// TODO: maybe use a instead of button?
|
|
||||||
const moveUp = document.createElement("button");
|
|
||||||
moveUp.classList.add("borderless");
|
|
||||||
moveUp.innerHTML = "🔺"; // 🔺 Red Triangle Pointed Down
|
|
||||||
moveUp.addEventListener("click", event => {
|
|
||||||
console.log(`DEBUG: move song ${i} up`);
|
|
||||||
});
|
|
||||||
// TODO: maybe use a instead of button?
|
|
||||||
const moveDown = document.createElement("button");
|
|
||||||
moveDown.classList.add("borderless");
|
|
||||||
moveDown.innerHTML = "🔻"; // 🔻 Red Triangle Pointed Up
|
|
||||||
moveDown.addEventListener("click", event => {
|
|
||||||
console.log(`DEBUG: move song ${i} down`);
|
|
||||||
});
|
|
||||||
// TODO: maybe use a instead of button?
|
|
||||||
const remove = document.createElement("button");
|
|
||||||
remove.classList.add("borderless");
|
|
||||||
remove.innerHTML = "❌"; // ❌ Cross mark
|
|
||||||
remove.addEventListener("click", event => {
|
|
||||||
console.log(`DEBUG: remove song ${i} from queue`);
|
|
||||||
});
|
|
||||||
actions.appendChild(moveUp);
|
|
||||||
actions.appendChild(moveDown);
|
|
||||||
actions.appendChild(remove);
|
|
||||||
tr.appendChild(pos);
|
|
||||||
tr.appendChild(artist);
|
|
||||||
tr.appendChild(track);
|
|
||||||
tr.appendChild(album);
|
|
||||||
tr.appendChild(length);
|
|
||||||
tr.appendChild(actions);
|
|
||||||
table.appendChild(tr);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -392,8 +392,28 @@ control_volume.addEventListener("change", event => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Server-Sent-Events
|
||||||
|
|
||||||
|
if (typeof (EventSource) !== "undefined") {
|
||||||
|
const sse = new EventSource("/sse");
|
||||||
|
sse.addEventListener("status", (event) => {
|
||||||
|
const status = JSON.parse(event.data);
|
||||||
|
console.log("test: " + event.data);
|
||||||
|
|
||||||
|
connection_state.innerHTML = "❌ Disconnected"; // ✅ Check Mark Button
|
||||||
|
});
|
||||||
|
|
||||||
|
sse.onmessage = function (event) {
|
||||||
|
console.log("sse message: " + event.data);
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
console.log("Sorry, your browser does not support server-sent events...");
|
||||||
|
}
|
||||||
|
|
||||||
// Websocket logic
|
// Websocket logic
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
// Create WebSocket connection.
|
// Create WebSocket connection.
|
||||||
const socket = new WebSocket(`${document.location.protocol === "https:" ? "wss" : "ws"}://${document.location.host}/ws`);
|
const socket = new WebSocket(`${document.location.protocol === "https:" ? "wss" : "ws"}://${document.location.host}/ws`);
|
||||||
|
|
||||||
|
@ -409,9 +429,9 @@ socket.addEventListener("message", event => {
|
||||||
|
|
||||||
const msg = JSON.parse(event.data);
|
const msg = JSON.parse(event.data);
|
||||||
|
|
||||||
if ("mpd_status" in msg) {
|
if ("status" in msg) {
|
||||||
if (msg.mpd_status == null) {
|
if (msg.mpd_status == null) {
|
||||||
connection_state.innerHTML = "❌ Disconnected"; // ✅ Check Mark Button
|
|
||||||
} else {
|
} else {
|
||||||
// print error if present
|
// print error if present
|
||||||
if ("error" in msg.mpd_status) {
|
if ("error" in msg.mpd_status) {
|
||||||
|
@ -480,7 +500,7 @@ socket.addEventListener("message", event => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// update song info
|
// update song info
|
||||||
if ("mpd_current_song" in msg && msg.mpd_current_song != null) {
|
if ("current_song" in msg && msg.mpd_current_song != null) {
|
||||||
let track;
|
let track;
|
||||||
if ("Artist" in msg.mpd_current_song && "Title" in msg.mpd_current_song) {
|
if ("Artist" in msg.mpd_current_song && "Title" in msg.mpd_current_song) {
|
||||||
track = `${msg.mpd_current_song.Artist} - ${msg.mpd_current_song.Title}`
|
track = `${msg.mpd_current_song.Artist} - ${msg.mpd_current_song.Title}`
|
||||||
|
@ -493,7 +513,7 @@ socket.addEventListener("message", event => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// update queue
|
// update queue
|
||||||
if ("mpd_queue" in msg && msg.mpd_queue != null) {
|
if ("queue" in msg && msg.mpd_queue != null) {
|
||||||
const tbody = document.createElement("tbody");
|
const tbody = document.createElement("tbody");
|
||||||
msg.mpd_queue.forEach(song => {
|
msg.mpd_queue.forEach(song => {
|
||||||
const tr = document.createElement("tr");
|
const tr = document.createElement("tr");
|
||||||
|
@ -557,7 +577,7 @@ socket.addEventListener("message", event => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("mpd_error" in msg) {
|
if ("error" in msg) {
|
||||||
console.error(`MPD Error: ${msg.mpd_error}`)
|
console.error(`MPD Error: ${msg.mpd_error}`)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -572,3 +592,4 @@ window.setInterval(() => {
|
||||||
}
|
}
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
Loading…
Reference in a new issue