small improvements; aur package
This commit is contained in:
parent
79a6049a91
commit
03677e24be
|
@ -35,10 +35,9 @@ jobs:
|
||||||
go-version-file: "go.mod"
|
go-version-file: "go.mod"
|
||||||
check-latest: true
|
check-latest: true
|
||||||
- name: Run Linter
|
- name: Run Linter
|
||||||
uses: dominikh/staticcheck-action@v1.3.0
|
uses: dominikh/staticcheck-action@v1
|
||||||
with:
|
with:
|
||||||
version: "latest"
|
version: "latest"
|
||||||
install-go: false
|
install-go: false
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: make test
|
run: make test
|
||||||
|
|
||||||
|
|
1
Makefile
1
Makefile
|
@ -17,6 +17,7 @@ build: ## Compile project
|
||||||
|
|
||||||
update: ## Update go dependencies
|
update: ## Update go dependencies
|
||||||
go get -u
|
go get -u
|
||||||
|
which gomod2nix && gomod2nix
|
||||||
|
|
||||||
tidy: ## Add missing and remove unused modules
|
tidy: ## Add missing and remove unused modules
|
||||||
go mod tidy
|
go mod tidy
|
||||||
|
|
28
README.md
28
README.md
|
@ -24,10 +24,11 @@ Example flake setup (untested):
|
||||||
|
|
||||||
```nix
|
```nix
|
||||||
{
|
{
|
||||||
|
description = "Example Flake to install sanic on your host";
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||||
sanic = {
|
sanic = {
|
||||||
url = "git+https://git.berlin.ccc.de/cccb/sanic?ref=main";
|
url = "git.berlin.ccc.de/cccb/sanic";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -37,16 +38,17 @@ Example flake setup (untested):
|
||||||
pkgs = import nixpkgs { inherit system; };
|
pkgs = import nixpkgs { inherit system; };
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
devShell.${system} = pkgs.mkShell {
|
nixosConfigurations."myhostname".nixpkgs.lib.nixosSystem = {
|
||||||
packages = [
|
inherit system;
|
||||||
sanic.packages.${system}.default
|
modules = [
|
||||||
|
{ environment.systemPackages = [ sanic.packages.${system}.default ]; }
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Arch Linux (Manjaro, etc)
|
### Arch Linux
|
||||||
|
|
||||||
Install from the AUR:
|
Install from the AUR:
|
||||||
|
|
||||||
|
@ -54,18 +56,6 @@ Install from the AUR:
|
||||||
yay -S sanic
|
yay -S sanic
|
||||||
```
|
```
|
||||||
|
|
||||||
### Debian (Ubuntu, Mint, etc)
|
|
||||||
|
|
||||||
_tba_
|
|
||||||
|
|
||||||
### Red Hat (Fedora, Rocky Linux, etc)
|
|
||||||
|
|
||||||
_tba_
|
|
||||||
|
|
||||||
### Windows / macOS
|
|
||||||
|
|
||||||
lol! 🤣
|
|
||||||
|
|
||||||
## 🛠️ 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.
|
||||||
|
@ -77,7 +67,7 @@ Update go depdendencies like this:
|
||||||
```shell
|
```shell
|
||||||
go get -u # or `make update`
|
go get -u # or `make update`
|
||||||
go mod tidy # or `make tidy`
|
go mod tidy # or `make tidy`
|
||||||
gomod2nix
|
gomod2nix # sync go deps with nix
|
||||||
```
|
```
|
||||||
|
|
||||||
### ❄️ w/ Nix
|
### ❄️ w/ Nix
|
||||||
|
@ -88,7 +78,7 @@ Enter development shell (also has [mpc][mpc] client installed for testing):
|
||||||
nix develop
|
nix develop
|
||||||
```
|
```
|
||||||
|
|
||||||
Build nix flake:
|
Build sanic:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
nix build
|
nix build
|
||||||
|
|
53
aur/PKGBUILD
Normal file
53
aur/PKGBUILD
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
# Maintainer: Ricardo Band <email@ricardo.band>
|
||||||
|
|
||||||
|
pkgname=sanic
|
||||||
|
pkgver=0.0.1
|
||||||
|
pkgrel=1
|
||||||
|
pkgdesc="chaos music control inspired by relaxx player"
|
||||||
|
arch=('any')
|
||||||
|
url=https://git.berlin.ccc.de/cccb/sanic
|
||||||
|
license=('custom:MIT')
|
||||||
|
makedepends=('go')
|
||||||
|
source=("$pkgname.service"
|
||||||
|
"$pkgname.sysusers"
|
||||||
|
"$pkgname.tmpfiles"
|
||||||
|
"$url/archive/v$pkgver.tar.gz")
|
||||||
|
sha256sums=("1337deadbeef"
|
||||||
|
"1337deadbeef"
|
||||||
|
"1337deadbeef"
|
||||||
|
"1337deadbeef")
|
||||||
|
|
||||||
|
prepare() {
|
||||||
|
cd "$pkgname-$pkgver"
|
||||||
|
|
||||||
|
mkdir -p build/
|
||||||
|
}
|
||||||
|
|
||||||
|
build() {
|
||||||
|
cd "$pkgname-$pkgver"
|
||||||
|
|
||||||
|
export CGO_CPPFLAGS="$CPPFLAGS"
|
||||||
|
export CGO_CFLAGS="$CFLAGS"
|
||||||
|
export CGO_CXXFLAGS="$CXXFLAGS"
|
||||||
|
export CGO_LDFLAGS="$LDFLAGS"
|
||||||
|
export GOFLAGS="-buildmode=pie -trimpath -ldflags=-linkmode=external -mod=readonly -modcacherw"
|
||||||
|
|
||||||
|
go build -o build/ .
|
||||||
|
}
|
||||||
|
|
||||||
|
check() {
|
||||||
|
cd "$pkgname-$pkgver"
|
||||||
|
|
||||||
|
go test ./...
|
||||||
|
}
|
||||||
|
|
||||||
|
package() {
|
||||||
|
cd "$pkgname-$pkgver"
|
||||||
|
|
||||||
|
install -Dm644 "LICENSE" "$pkgdir/usr/share/licenses/$pkgname/LICENSE"
|
||||||
|
install -Dm755 build/$pkgname "$pkgdir"/usr/bin/$pkgname
|
||||||
|
install -Dm644 "../$pkgname.service" "$pkgdir/usr/lib/systemd/system/$pkgname.service"
|
||||||
|
install -Dm644 "../$pkgname.sysusers" "$pkgdir/usr/lib/sysusers.d/$pkgname.conf"
|
||||||
|
install -Dm644 "../$pkgname.tmpfiles" "$pkgdir/usr/lib/tmpfiles.d/$pkgname.conf"
|
||||||
|
}
|
||||||
|
|
28
aur/sanic.service
Normal file
28
aur/sanic.service
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
[Unit]
|
||||||
|
Description=chaos music control
|
||||||
|
After=network-online.target
|
||||||
|
Wants=network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=sanic
|
||||||
|
Group=sanic
|
||||||
|
ExecStart=/usr/bin/sanic
|
||||||
|
Restart=always
|
||||||
|
# security
|
||||||
|
NoNewPrivileges=true
|
||||||
|
ProtectSystem=strict
|
||||||
|
ProtectHome=yes
|
||||||
|
StateDirectory=sanic
|
||||||
|
StateDirectoryMode=0750
|
||||||
|
ConfigurationDirectory=sanic
|
||||||
|
ConfigurationDirectoryMode=0750
|
||||||
|
PrivateTmp=true
|
||||||
|
ProtectKernelTunables=true
|
||||||
|
ProtectKernelModules=true
|
||||||
|
ProtectKernelLogs=true
|
||||||
|
ProtectControlGroups=true
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
|
3
aur/sanic.sysusers
Normal file
3
aur/sanic.sysusers
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
u sanic - "chaos music control" /run/sanic /usr/bin/nologin
|
||||||
|
g sanic - -
|
||||||
|
|
3
aur/sanic.tmpfiles
Normal file
3
aur/sanic.tmpfiles
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
d /etc/sanic 0750 sanic sanic
|
||||||
|
d /run/sanic 0750 sanic sanic
|
||||||
|
|
22
go.mod
22
go.mod
|
@ -1,31 +1,31 @@
|
||||||
module github.com/cccb/sanic
|
module git.berlin.ccc.de/cccb/sanic
|
||||||
|
|
||||||
go 1.21
|
go 1.21
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/fhs/gompd/v2 v2.3.0
|
github.com/fhs/gompd/v2 v2.3.0
|
||||||
github.com/labstack/echo-contrib v0.15.0
|
github.com/labstack/echo-contrib v0.16.0
|
||||||
github.com/labstack/echo/v4 v4.11.4
|
github.com/labstack/echo/v4 v4.11.4
|
||||||
golang.org/x/net v0.20.0
|
golang.org/x/net v0.24.0
|
||||||
gopkg.in/ini.v1 v1.67.0
|
gopkg.in/ini.v1 v1.67.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
||||||
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.18.0 // indirect
|
github.com/prometheus/client_golang v1.19.0 // indirect
|
||||||
github.com/prometheus/client_model v0.5.0 // indirect
|
github.com/prometheus/client_model v0.6.1 // indirect
|
||||||
github.com/prometheus/common v0.46.0 // indirect
|
github.com/prometheus/common v0.52.2 // indirect
|
||||||
github.com/prometheus/procfs v0.12.0 // indirect
|
github.com/prometheus/procfs v0.13.0 // 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.18.0 // indirect
|
golang.org/x/crypto v0.22.0 // indirect
|
||||||
golang.org/x/sys v0.16.0 // indirect
|
golang.org/x/sys v0.19.0 // indirect
|
||||||
golang.org/x/text v0.14.0 // indirect
|
golang.org/x/text v0.14.0 // indirect
|
||||||
golang.org/x/time v0.5.0 // indirect
|
golang.org/x/time v0.5.0 // indirect
|
||||||
google.golang.org/protobuf v1.32.0 // indirect
|
google.golang.org/protobuf v1.33.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
48
go.sum
48
go.sum
|
@ -1,17 +1,17 @@
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
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/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/fhs/gompd/v2 v2.3.0 h1:wuruUjmOODRlJhrYx73rJnzS7vTSXSU7pWmZtM3VPE0=
|
github.com/fhs/gompd/v2 v2.3.0 h1:wuruUjmOODRlJhrYx73rJnzS7vTSXSU7pWmZtM3VPE0=
|
||||||
github.com/fhs/gompd/v2 v2.3.0/go.mod h1:nNdZtcpD5VpmzZbRl5rV6RhxeMmAWTxEsSIMBkmMIy4=
|
github.com/fhs/gompd/v2 v2.3.0/go.mod h1:nNdZtcpD5VpmzZbRl5rV6RhxeMmAWTxEsSIMBkmMIy4=
|
||||||
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
||||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/labstack/echo-contrib v0.15.0 h1:9K+oRU265y4Mu9zpRDv3X+DGTqUALY6oRHCSZZKCRVU=
|
github.com/labstack/echo-contrib v0.16.0 h1:vk5Kd+egpTOJxD3l+3IvZzQWPbrXiYxhkkgkJL99j/w=
|
||||||
github.com/labstack/echo-contrib v0.15.0/go.mod h1:lei+qt5CLB4oa7VHTE0yEfQSEB9XTJI1LUqko9UWvo4=
|
github.com/labstack/echo-contrib v0.16.0/go.mod h1:mjX5VB3OqJcroIEycptBOY9Hr7rK+unq79W8QFKGNV0=
|
||||||
github.com/labstack/echo/v4 v4.11.4 h1:vDZmA+qNeh1pd/cCkEicDMrjtrnMGQ1QFI9gWN1zGq8=
|
github.com/labstack/echo/v4 v4.11.4 h1:vDZmA+qNeh1pd/cCkEicDMrjtrnMGQ1QFI9gWN1zGq8=
|
||||||
github.com/labstack/echo/v4 v4.11.4/go.mod h1:noh7EvLwqDsmh/X/HWKPUl1AjzJrhyptRyEbQJfxen8=
|
github.com/labstack/echo/v4 v4.11.4/go.mod h1:noh7EvLwqDsmh/X/HWKPUl1AjzJrhyptRyEbQJfxen8=
|
||||||
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
|
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
|
||||||
|
@ -23,34 +23,34 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
|
||||||
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/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.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk=
|
github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU=
|
||||||
github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA=
|
github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
|
||||||
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
|
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||||
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
|
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||||
github.com/prometheus/common v0.46.0 h1:doXzt5ybi1HBKpsZOL0sSkaNHJJqkyfEWZGGqqScV0Y=
|
github.com/prometheus/common v0.52.2 h1:LW8Vk7BccEdONfrJBDffQGRtpSzi5CQaRZGtboOO2ck=
|
||||||
github.com/prometheus/common v0.46.0/go.mod h1:Tp0qkxpb9Jsg54QMe+EAmqXkSV7Evdy1BTn+g2pa/hQ=
|
github.com/prometheus/common v0.52.2/go.mod h1:lrWtQx+iDfn2mbH5GUzlH9TSHyfZpHkSiG1W7y3sF2Q=
|
||||||
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
|
github.com/prometheus/procfs v0.13.0 h1:GqzLlQyfsPbaEHaQkO7tbDlriv/4o5Hudv6OXHGKX7o=
|
||||||
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
|
github.com/prometheus/procfs v0.13.0/go.mod h1:cd4PFCR54QLnGKPaKGA6l+cfuNXtht43ZKY6tow0Y1g=
|
||||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
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=
|
||||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
|
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
|
||||||
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.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
|
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
|
||||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
||||||
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
|
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
||||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
||||||
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.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
||||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.19.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/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=
|
||||||
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
|
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||||
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||||
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=
|
||||||
|
|
|
@ -5,8 +5,8 @@ schema = 3
|
||||||
version = "v1.0.1"
|
version = "v1.0.1"
|
||||||
hash = "sha256-h75GUqfwJKngCJQVE5Ao5wnO3cfKD9lSIteoLp/3xJ4="
|
hash = "sha256-h75GUqfwJKngCJQVE5Ao5wnO3cfKD9lSIteoLp/3xJ4="
|
||||||
[mod."github.com/cespare/xxhash/v2"]
|
[mod."github.com/cespare/xxhash/v2"]
|
||||||
version = "v2.2.0"
|
version = "v2.3.0"
|
||||||
hash = "sha256-nPufwYQfTkyrEkbBrpqM3C2vnMxfIz6tAaBmiUP7vd4="
|
hash = "sha256-7hRlwSR+fos1kx4VZmJ/7snR7zHh8ZFKX+qqqqGcQpY="
|
||||||
[mod."github.com/fhs/gompd/v2"]
|
[mod."github.com/fhs/gompd/v2"]
|
||||||
version = "v2.3.0"
|
version = "v2.3.0"
|
||||||
hash = "sha256-JBb7BvLu1wlUAbMt/g5JmJtA3fxqr6dKWeeLwfGsB08="
|
hash = "sha256-JBb7BvLu1wlUAbMt/g5JmJtA3fxqr6dKWeeLwfGsB08="
|
||||||
|
@ -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.15.0"
|
version = "v0.16.0"
|
||||||
hash = "sha256-bDjEAJc5gPs+G5M8fbTSBFgb0t4dTYqdECyvHvuf3gY="
|
hash = "sha256-YnO4Ngu+gb/upIo856FDCtcTev36Vs/xUvP2qMiSGnA="
|
||||||
[mod."github.com/labstack/echo/v4"]
|
[mod."github.com/labstack/echo/v4"]
|
||||||
version = "v4.11.4"
|
version = "v4.11.4"
|
||||||
hash = "sha256-pVKfkZtxi5e/1MTK2RcKWSgNpEbRDo3lKUVKo01WYO0="
|
hash = "sha256-pVKfkZtxi5e/1MTK2RcKWSgNpEbRDo3lKUVKo01WYO0="
|
||||||
|
@ -29,17 +29,17 @@ schema = 3
|
||||||
version = "v0.0.20"
|
version = "v0.0.20"
|
||||||
hash = "sha256-qhw9hWtU5wnyFyuMbKx+7RB8ckQaFQ8D+8GKPkN3HHQ="
|
hash = "sha256-qhw9hWtU5wnyFyuMbKx+7RB8ckQaFQ8D+8GKPkN3HHQ="
|
||||||
[mod."github.com/prometheus/client_golang"]
|
[mod."github.com/prometheus/client_golang"]
|
||||||
version = "v1.18.0"
|
version = "v1.19.0"
|
||||||
hash = "sha256-kuC6WUg2j7A+9qnSp5VZSYo+oltgLvj/70TpqlCJIdE="
|
hash = "sha256-YV8sxMPR+xorTUCriTfcFsaV2b7PZfPJDQmOgUYOZJo="
|
||||||
[mod."github.com/prometheus/client_model"]
|
[mod."github.com/prometheus/client_model"]
|
||||||
version = "v0.5.0"
|
version = "v0.6.1"
|
||||||
hash = "sha256-/sXlngf8AoEIeLIiaLg6Y7uYPVq7tI0qnLt0mUyKid4="
|
hash = "sha256-rIDyUzNfxRA934PIoySR0EhuBbZVRK/25Jlc/r8WODw="
|
||||||
[mod."github.com/prometheus/common"]
|
[mod."github.com/prometheus/common"]
|
||||||
version = "v0.46.0"
|
version = "v0.52.2"
|
||||||
hash = "sha256-Q303suNDzc+DbIYhiqURNhymXeheWEshwm7XasKnX+Y="
|
hash = "sha256-XQUvk9/Kwf9NDlDUVl7mOWRD7z7z9QEbLH/rNU4D2nI="
|
||||||
[mod."github.com/prometheus/procfs"]
|
[mod."github.com/prometheus/procfs"]
|
||||||
version = "v0.12.0"
|
version = "v0.13.0"
|
||||||
hash = "sha256-Y4ZZmxIpVCO67zN3pGwSk2TcI88zvmGJkgwq9DRTwFw="
|
hash = "sha256-J31K36TkIiQU2EGOcmqDa+dkoKXiVuxafPVT4rKbEsg="
|
||||||
[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,14 +47,14 @@ 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.18.0"
|
version = "v0.22.0"
|
||||||
hash = "sha256-BuMVUxOIyfLo8MOhqYt+uQ8NDN6P2KdblKyfPxINzQ4="
|
hash = "sha256-2+u9nd32+Bi7EEv7QFc12CRTbfV7DApNv+yKIr7+lTw="
|
||||||
[mod."golang.org/x/net"]
|
[mod."golang.org/x/net"]
|
||||||
version = "v0.20.0"
|
version = "v0.24.0"
|
||||||
hash = "sha256-PCttIsWSBQd6fDXL49jepszUAMLnAGAKR//5EDO3XDk="
|
hash = "sha256-w1c21ljta5wNIyel9CSIn/crPzwOCRofNKhqmfs4aEQ="
|
||||||
[mod."golang.org/x/sys"]
|
[mod."golang.org/x/sys"]
|
||||||
version = "v0.16.0"
|
version = "v0.19.0"
|
||||||
hash = "sha256-ZkGclbp2S7NQYhbuGji6XokCn2Qi1BJy8dwyAOTV8sY="
|
hash = "sha256-cmuL31TYLJmDm/fDnI2Sn0wB88cpdOHV1+urorsJWx4="
|
||||||
[mod."golang.org/x/text"]
|
[mod."golang.org/x/text"]
|
||||||
version = "v0.14.0"
|
version = "v0.14.0"
|
||||||
hash = "sha256-yh3B0tom1RfzQBf1RNmfdNWF1PtiqxV41jW1GVS6JAg="
|
hash = "sha256-yh3B0tom1RfzQBf1RNmfdNWF1PtiqxV41jW1GVS6JAg="
|
||||||
|
@ -62,8 +62,8 @@ schema = 3
|
||||||
version = "v0.5.0"
|
version = "v0.5.0"
|
||||||
hash = "sha256-W6RgwgdYTO3byIPOFxrP2IpAZdgaGowAaVfYby7AULU="
|
hash = "sha256-W6RgwgdYTO3byIPOFxrP2IpAZdgaGowAaVfYby7AULU="
|
||||||
[mod."google.golang.org/protobuf"]
|
[mod."google.golang.org/protobuf"]
|
||||||
version = "v1.32.0"
|
version = "v1.33.0"
|
||||||
hash = "sha256-GJuTkMGHCzHbyK4yD5kY4oMn8wQWqgkeBK//yVDqHJk="
|
hash = "sha256-cWwQjtUwSIEkAlAadrlxK1PYZXTRrV4NKzt7xDpJgIU="
|
||||||
[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="
|
||||||
|
|
57
mpd.go
57
mpd.go
|
@ -1,6 +1,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"github.com/fhs/gompd/v2/mpd"
|
"github.com/fhs/gompd/v2/mpd"
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -23,7 +24,7 @@ func updateDb(c echo.Context) error {
|
||||||
c.Logger().Error(err)
|
c.Logger().Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.String(http.StatusOK, strconv.Itoa(jobId))
|
return c.String(http.StatusOK, fmt.Sprintf("Database update started with job id %d", jobId))
|
||||||
}
|
}
|
||||||
|
|
||||||
func previousTrack(c echo.Context) error {
|
func previousTrack(c echo.Context) error {
|
||||||
|
@ -39,7 +40,7 @@ func previousTrack(c echo.Context) error {
|
||||||
c.Logger().Error(err)
|
c.Logger().Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.String(http.StatusOK, "")
|
return c.String(http.StatusOK, "Playing previous track in queue")
|
||||||
}
|
}
|
||||||
|
|
||||||
func nextTrack(c echo.Context) error {
|
func nextTrack(c echo.Context) error {
|
||||||
|
@ -55,7 +56,7 @@ func nextTrack(c echo.Context) error {
|
||||||
c.Logger().Error(err)
|
c.Logger().Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.String(http.StatusOK, "")
|
return c.String(http.StatusOK, "PLaying next track in queue")
|
||||||
}
|
}
|
||||||
|
|
||||||
func stopPlayback(c echo.Context) error {
|
func stopPlayback(c echo.Context) error {
|
||||||
|
@ -71,7 +72,7 @@ func stopPlayback(c echo.Context) error {
|
||||||
c.Logger().Error(err)
|
c.Logger().Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.String(http.StatusOK, "")
|
return c.String(http.StatusOK, "Playback stopped")
|
||||||
}
|
}
|
||||||
|
|
||||||
func resumePlayback(c echo.Context) error {
|
func resumePlayback(c echo.Context) error {
|
||||||
|
@ -98,7 +99,7 @@ func resumePlayback(c echo.Context) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.String(http.StatusOK, "")
|
return c.String(http.StatusOK, "Playback resumed")
|
||||||
}
|
}
|
||||||
|
|
||||||
func pausePlayback(c echo.Context) error {
|
func pausePlayback(c echo.Context) error {
|
||||||
|
@ -114,7 +115,7 @@ func pausePlayback(c echo.Context) error {
|
||||||
c.Logger().Error(err)
|
c.Logger().Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.String(http.StatusOK, "")
|
return c.String(http.StatusOK, "Playback paused")
|
||||||
}
|
}
|
||||||
|
|
||||||
func seek(c echo.Context) error {
|
func seek(c echo.Context) error {
|
||||||
|
@ -134,12 +135,13 @@ func seek(c echo.Context) error {
|
||||||
return c.String(http.StatusBadRequest, "seconds must be positive integer")
|
return c.String(http.StatusBadRequest, "seconds must be positive integer")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Duration type seems to be used incorrectly
|
||||||
err = conn.SeekCur(time.Duration(seconds)*time.Second, false)
|
err = conn.SeekCur(time.Duration(seconds)*time.Second, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Logger().Error(err)
|
c.Logger().Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.String(http.StatusOK, "")
|
return c.String(http.StatusOK, fmt.Sprintf("Seeked current track to %d seconds", seconds))
|
||||||
}
|
}
|
||||||
|
|
||||||
func toggleRepeat(c echo.Context) error {
|
func toggleRepeat(c echo.Context) error {
|
||||||
|
@ -154,16 +156,19 @@ func toggleRepeat(c echo.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Logger().Error(err)
|
c.Logger().Error(err)
|
||||||
}
|
}
|
||||||
|
var msg string
|
||||||
if status["repeat"] == "1" {
|
if status["repeat"] == "1" {
|
||||||
err = conn.Repeat(false)
|
err = conn.Repeat(false)
|
||||||
|
msg = "Toggled Repeat mode to off"
|
||||||
} else {
|
} else {
|
||||||
err = conn.Repeat(true)
|
err = conn.Repeat(true)
|
||||||
|
msg = "Toggled Repeat mode to on"
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Logger().Error(err)
|
c.Logger().Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.String(http.StatusOK, "")
|
return c.String(http.StatusOK, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func toggleRandom(c echo.Context) error {
|
func toggleRandom(c echo.Context) error {
|
||||||
|
@ -178,16 +183,19 @@ func toggleRandom(c echo.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Logger().Error(err)
|
c.Logger().Error(err)
|
||||||
}
|
}
|
||||||
|
var msg string
|
||||||
if status["random"] == "1" {
|
if status["random"] == "1" {
|
||||||
err = conn.Random(false)
|
err = conn.Random(false)
|
||||||
|
msg = "Toggled Random mode to off"
|
||||||
} else {
|
} else {
|
||||||
err = conn.Random(true)
|
err = conn.Random(true)
|
||||||
|
msg = "Toggled Random mode to on"
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Logger().Error(err)
|
c.Logger().Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.String(http.StatusOK, "")
|
return c.String(http.StatusOK, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func setVolume(c echo.Context) error {
|
func setVolume(c echo.Context) error {
|
||||||
|
@ -212,7 +220,7 @@ func setVolume(c echo.Context) error {
|
||||||
c.Logger().Error(err)
|
c.Logger().Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.String(http.StatusOK, "")
|
return c.String(http.StatusOK, fmt.Sprintf("Set volume to %d", level))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Queue
|
// Queue
|
||||||
|
@ -236,5 +244,32 @@ func deleteTrackFromQueue(c echo.Context) error {
|
||||||
return c.String(http.StatusBadRequest, err.Error())
|
return c.String(http.StatusBadRequest, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.String(http.StatusOK, "")
|
return c.String(http.StatusOK, fmt.Sprintf("Removed song %d from queue", songId))
|
||||||
|
}
|
||||||
|
|
||||||
|
func moveTrackInQueue(c echo.Context) error {
|
||||||
|
// Connect to MPD server
|
||||||
|
conn, err := mpd.Dial("tcp", "localhost:6600")
|
||||||
|
if err != nil {
|
||||||
|
c.Logger().Error(err)
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
songId, err := strconv.Atoi(c.Param("song_id"))
|
||||||
|
if err != nil {
|
||||||
|
c.Logger().Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
position, err := strconv.Atoi(c.Param("position"))
|
||||||
|
if err != nil {
|
||||||
|
c.Logger().Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = conn.MoveID(songId, position)
|
||||||
|
if err != nil {
|
||||||
|
c.Logger().Error(err)
|
||||||
|
return c.String(http.StatusBadRequest, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.String(http.StatusOK, fmt.Sprintf("Moved song %d to position %d", songId, position))
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,7 +89,8 @@ func main() {
|
||||||
g.GET("/random", toggleRandom)
|
g.GET("/random", toggleRandom)
|
||||||
g.GET("/volume/:level", setVolume)
|
g.GET("/volume/:level", setVolume)
|
||||||
|
|
||||||
g.GET("/queue/delete/:song_id", deleteTrackFromQueue)
|
g.GET("/queue/:song_id/delete", deleteTrackFromQueue)
|
||||||
|
g.GET("/queue/:song_id/move/:position", moveTrackInQueue)
|
||||||
|
|
||||||
g.GET("/download", downloadTrack)
|
g.GET("/download", downloadTrack)
|
||||||
|
|
||||||
|
|
|
@ -62,7 +62,7 @@
|
||||||
<label for="control-track">Now playing:</label>
|
<label for="control-track">Now playing:</label>
|
||||||
<!--<input type="text" id="control-track" name="track" disabled="disabled" />-->
|
<!--<input type="text" id="control-track" name="track" disabled="disabled" />-->
|
||||||
<div class="marquee" id="control-track">
|
<div class="marquee" id="control-track">
|
||||||
<span>Fall On Your Sword - Shatner Of The Mount by Fall On Your Sword</span>
|
<span></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
@ -226,7 +226,7 @@
|
||||||
<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://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
|
||||||
</footer>
|
</footer>
|
||||||
</main>
|
</main>
|
||||||
<script src="controls.js"></script>
|
<script src="index.js"></script>
|
||||||
<script>
|
<script>
|
||||||
const table = document.querySelector("#queue > table > tbody");
|
const table = document.querySelector("#queue > table > tbody");
|
||||||
for (let i = 1; i <= 100; i++) {
|
for (let i = 1; i <= 100; i++) {
|
||||||
|
|
|
@ -38,8 +38,85 @@ const control_attach_playlist = document.getElementById("control-attach-playlist
|
||||||
const control_save_playlist = document.getElementById("control-save-playlist");
|
const control_save_playlist = document.getElementById("control-save-playlist");
|
||||||
const control_delete_playlist = document.getElementById("control-delete-playlist");
|
const control_delete_playlist = document.getElementById("control-delete-playlist");
|
||||||
|
|
||||||
|
// Utility functions
|
||||||
|
|
||||||
|
secondsToTrackTime = (t) => {
|
||||||
|
const hours = Math.floor(t / 3600);
|
||||||
|
const minutes = Math.floor((t - hours * 3600) / 60);
|
||||||
|
const seconds = Math.floor(t - hours * 3600 - minutes * 60);
|
||||||
|
|
||||||
|
return `${hours}:${minutes.toString().padStart(2, "0")}:${seconds.toString().padStart(2, "0")}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
removeTrackFromQueue = (event) => {
|
||||||
|
const song_id = event.target.parentElement.parentElement.dataset.song_id;
|
||||||
|
|
||||||
|
console.log(`DEBUG: remove song id ${song_id} from queue`);
|
||||||
|
fetch(`${API_URL}/queue/${song_id}/delete`).then(r => {
|
||||||
|
console.log(r.text());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
moveTrackInQueue = (event, direction) => {
|
||||||
|
const song_id = event.target.parentElement.parentElement.dataset.song_id;
|
||||||
|
// TODO: figure out position in queue by counting HTML elements?
|
||||||
|
const position = parseInt(event.target.parentElement.parentElement.firstChild.innerText);
|
||||||
|
|
||||||
|
console.log(`DEBUG: move song ${song_id} down in queue to position ${position + direction}`);
|
||||||
|
fetch(`${API_URL}/queue/${song_id}/move/${position + direction}`).then(r => {
|
||||||
|
console.log(r.text());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// UI controls
|
// UI controls
|
||||||
|
|
||||||
|
tab_browser.addEventListener("click", () => {
|
||||||
|
if (!tab_browser.classList.contains("active")) {
|
||||||
|
tab_browser.classList.add("active");
|
||||||
|
tab_search.classList.remove("active")
|
||||||
|
tab_playlists.classList.remove("active")
|
||||||
|
document.getElementById("file-browser").style.display = "block";
|
||||||
|
document.getElementById("search").style.display = "none";
|
||||||
|
document.getElementById("playlist-browser").style.display = "none";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
tab_search.addEventListener("click", () => {
|
||||||
|
if (!tab_search.classList.contains("active")) {
|
||||||
|
tab_browser.classList.remove("active");
|
||||||
|
tab_search.classList.add("active")
|
||||||
|
tab_playlists.classList.remove("active")
|
||||||
|
document.getElementById("file-browser").style.display = "none";
|
||||||
|
document.getElementById("search").style.display = "block";
|
||||||
|
document.getElementById("playlist-browser").style.display = "none";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
tab_playlists.addEventListener("click", () => {
|
||||||
|
if (!tab_playlists.classList.contains("active")) {
|
||||||
|
tab_browser.classList.remove("active");
|
||||||
|
tab_search.classList.remove("active")
|
||||||
|
tab_playlists.classList.add("active")
|
||||||
|
document.getElementById("file-browser").style.display = "none";
|
||||||
|
document.getElementById("search").style.display = "none";
|
||||||
|
document.getElementById("playlist-browser").style.display = "block";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
control_save_playlist.addEventListener("click", () => {
|
||||||
|
dialog_save_playlist.showModal()
|
||||||
|
});
|
||||||
|
|
||||||
|
dialog_save_playlist_close.addEventListener("click", () => {
|
||||||
|
dialog_save_playlist.close()
|
||||||
|
});
|
||||||
|
|
||||||
|
// control_progress.addEventListener("change", event => {
|
||||||
|
// control_time.value = `${secondsToTrackTime(event.target.value)}/${secondsToTrackTime(event.target.max)}`;
|
||||||
|
// });
|
||||||
|
|
||||||
|
// Add API calls to controls
|
||||||
|
|
||||||
control_replace_playlist.addEventListener("click", e => {
|
control_replace_playlist.addEventListener("click", e => {
|
||||||
fetch(`${API_URL}/`).then(async r => {
|
fetch(`${API_URL}/`).then(async r => {
|
||||||
if (r.status !== 200) {
|
if (r.status !== 200) {
|
||||||
|
@ -56,15 +133,7 @@ control_attach_playlist.addEventListener("click", e => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
control_save_playlist.addEventListener("click", e => {
|
dialog_save_playlist_submit.addEventListener("click", () => {
|
||||||
dialog_save_playlist.showModal()
|
|
||||||
});
|
|
||||||
|
|
||||||
dialog_save_playlist_close.addEventListener("click", e => {
|
|
||||||
dialog_save_playlist.close()
|
|
||||||
});
|
|
||||||
|
|
||||||
dialog_save_playlist_submit.addEventListener("click", e => {
|
|
||||||
fetch(`${API_URL}/playlists`, {method: "PUT"}).then(async r => {
|
fetch(`${API_URL}/playlists`, {method: "PUT"}).then(async r => {
|
||||||
if (r.status === 201) {
|
if (r.status === 201) {
|
||||||
console.log(`Playlist "${control_playlist_name.value}" saved`)
|
console.log(`Playlist "${control_playlist_name.value}" saved`)
|
||||||
|
@ -72,7 +141,7 @@ dialog_save_playlist_submit.addEventListener("click", e => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
control_delete_playlist.addEventListener("click", e => {
|
control_delete_playlist.addEventListener("click", () => {
|
||||||
const playlist_id = control_playlist_list.value;
|
const playlist_id = control_playlist_list.value;
|
||||||
fetch(`${API_URL}/playlists/${playlist_id}`, {method: "DELETE"}).then(r => {
|
fetch(`${API_URL}/playlists/${playlist_id}`, {method: "DELETE"}).then(r => {
|
||||||
if (r.status === 204) {
|
if (r.status === 204) {
|
||||||
|
@ -83,68 +152,34 @@ control_delete_playlist.addEventListener("click", e => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
tab_browser.addEventListener("click", e => {
|
control_update_db.addEventListener("click", () => {
|
||||||
if (!tab_browser.classList.contains("active")) {
|
|
||||||
tab_browser.classList.add("active");
|
|
||||||
tab_search.classList.remove("active")
|
|
||||||
tab_playlists.classList.remove("active")
|
|
||||||
document.getElementById("file-browser").style.display = "block";
|
|
||||||
document.getElementById("search").style.display = "none";
|
|
||||||
document.getElementById("playlist-browser").style.display = "none";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
tab_search.addEventListener("click", e => {
|
|
||||||
if (!tab_search.classList.contains("active")) {
|
|
||||||
tab_browser.classList.remove("active");
|
|
||||||
tab_search.classList.add("active")
|
|
||||||
tab_playlists.classList.remove("active")
|
|
||||||
document.getElementById("file-browser").style.display = "none";
|
|
||||||
document.getElementById("search").style.display = "block";
|
|
||||||
document.getElementById("playlist-browser").style.display = "none";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
tab_playlists.addEventListener("click", e => {
|
|
||||||
if (!tab_playlists.classList.contains("active")) {
|
|
||||||
tab_browser.classList.remove("active");
|
|
||||||
tab_search.classList.remove("active")
|
|
||||||
tab_playlists.classList.add("active")
|
|
||||||
document.getElementById("file-browser").style.display = "none";
|
|
||||||
document.getElementById("search").style.display = "none";
|
|
||||||
document.getElementById("playlist-browser").style.display = "block";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add API calls to controls
|
|
||||||
|
|
||||||
control_update_db.addEventListener("click", e => {
|
|
||||||
console.log("Issuing database update")
|
console.log("Issuing database update")
|
||||||
fetch(`${API_URL}/update_db`).then(async r => {
|
fetch(`${API_URL}/update_db`).then(async r => {
|
||||||
if (r.status === 200) {
|
if (r.status === 200) {
|
||||||
const job_id = await r.text();
|
console.log(await r.text());
|
||||||
console.log(`Update started (Job ID: ${job_id})`);
|
|
||||||
e.target.disabled = true;
|
e.target.disabled = true;
|
||||||
} else {
|
} else {
|
||||||
console.error(`API returned ${r.status}: ${r.statusText}`);
|
console.error(`API returned ${r.status}: ${r.statusText}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
control_previous.addEventListener("click", e => {
|
|
||||||
|
control_previous.addEventListener("click", () => {
|
||||||
fetch(`${API_URL}/previous_track`).then(async r => {
|
fetch(`${API_URL}/previous_track`).then(async r => {
|
||||||
if (r.status >= 400) {
|
if (r.status >= 400) {
|
||||||
console.error(`API returned ${r.status}: ${r.statusText}`);
|
console.error(`API returned ${r.status}: ${r.statusText}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
control_play_pause.addEventListener("click", e => {
|
|
||||||
if (e.target.innerHTML === "⏸︎") {
|
control_play_pause.addEventListener("click", event => {
|
||||||
|
if (event.target.innerHTML === "⏸︎") { // Resume playback
|
||||||
fetch(`${API_URL}/pause`).then(async r => {
|
fetch(`${API_URL}/pause`).then(async r => {
|
||||||
if (r.status >= 400) {
|
if (r.status >= 400) {
|
||||||
console.error(`API returned ${r.status}: ${r.statusText}`);
|
console.error(`API returned ${r.status}: ${r.statusText}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else { // Pause
|
} else { // Pause playback
|
||||||
fetch(`${API_URL}/play`).then(async r => {
|
fetch(`${API_URL}/play`).then(async r => {
|
||||||
if (r.status >= 400) {
|
if (r.status >= 400) {
|
||||||
console.error(`API returned ${r.status}: ${r.statusText}`);
|
console.error(`API returned ${r.status}: ${r.statusText}`);
|
||||||
|
@ -152,34 +187,42 @@ control_play_pause.addEventListener("click", e => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
control_stop.addEventListener("click", e => {
|
|
||||||
|
control_stop.addEventListener("click", () => {
|
||||||
fetch(`${API_URL}/stop`).then(async r => {
|
fetch(`${API_URL}/stop`).then(async r => {
|
||||||
if (r.status >= 400) {
|
if (r.status >= 400) {
|
||||||
console.error(`API returned ${r.status}: ${r.statusText}`);
|
console.error(`API returned ${r.status}: ${r.statusText}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
control_next.addEventListener("click", e => {
|
|
||||||
|
control_next.addEventListener("click", () => {
|
||||||
fetch(`${API_URL}/next_track`).then(async r => {
|
fetch(`${API_URL}/next_track`).then(async r => {
|
||||||
if (r.status >= 400) {
|
if (r.status >= 400) {
|
||||||
console.error(`API returned ${r.status}: ${r.statusText}`);
|
console.error(`API returned ${r.status}: ${r.statusText}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
control_progress.addEventListener("change", e => {
|
|
||||||
fetch(`${API_URL}/seek/${e.target.value}`).then(async r => {
|
control_progress.addEventListener("change", event => {
|
||||||
|
fetch(`${API_URL}/seek/${event.target.value}`).then(async r => {
|
||||||
if (r.status >= 400) {
|
if (r.status >= 400) {
|
||||||
console.error(`API returned ${r.status}: ${r.statusText}`);
|
console.error(`API returned ${r.status}: ${r.statusText}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
control_repeat.addEventListener("click", e => {
|
|
||||||
if (e.target.dataset.state === "on") { // TODO: check is never true
|
control_progress.addEventListener("input", event => {
|
||||||
e.target.innerHTML = "🔘 repeat";
|
control_time.value = `${secondsToTrackTime(event.target.value)}/${secondsToTrackTime(event.target.max)}`;
|
||||||
e.target.dataset.state = "off";
|
});
|
||||||
|
|
||||||
|
control_repeat.addEventListener("click", event => {
|
||||||
|
if (event.target.dataset.state === "on") { // TODO: check is never true
|
||||||
|
event.target.innerHTML = "🔘 repeat";
|
||||||
|
event.target.dataset.state = "off";
|
||||||
} else {
|
} else {
|
||||||
e.target.innerHTML = "🔴 repeat";
|
event.target.innerHTML = "🔴 repeat";
|
||||||
e.target.dataset.state = "on";
|
event.target.dataset.state = "on";
|
||||||
}
|
}
|
||||||
fetch(`${API_URL}/repeat`).then(async r => {
|
fetch(`${API_URL}/repeat`).then(async r => {
|
||||||
if (r.status >= 400) {
|
if (r.status >= 400) {
|
||||||
|
@ -187,13 +230,14 @@ control_repeat.addEventListener("click", e => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
control_shuffle.addEventListener("click", e => {
|
|
||||||
if (e.target.dataset.state === "on") { // TODO: check is never true
|
control_shuffle.addEventListener("click", event => {
|
||||||
e.target.innerHTML = "🔘 shuffle";
|
if (event.target.dataset.state === "on") { // TODO: check is never true
|
||||||
e.target.dataset.state = "off";
|
event.target.innerHTML = "🔘 shuffle";
|
||||||
|
event.target.dataset.state = "off";
|
||||||
} else {
|
} else {
|
||||||
e.target.innerHTML = "🔴 shuffle";
|
event.target.innerHTML = "🔴 shuffle";
|
||||||
e.target.dataset.state = "on";
|
event.target.dataset.state = "on";
|
||||||
}
|
}
|
||||||
fetch(`${API_URL}/random`).then(async r => {
|
fetch(`${API_URL}/random`).then(async r => {
|
||||||
if (r.status >= 400) {
|
if (r.status >= 400) {
|
||||||
|
@ -201,7 +245,8 @@ control_shuffle.addEventListener("click", e => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
control_xfade_minus.addEventListener("click", e => {
|
|
||||||
|
control_xfade_minus.addEventListener("click", () => {
|
||||||
// TODO: not yet implemented
|
// TODO: not yet implemented
|
||||||
fetch(`${API_URL}/xfade`).then(async r => {
|
fetch(`${API_URL}/xfade`).then(async r => {
|
||||||
if (r.status >= 400) {
|
if (r.status >= 400) {
|
||||||
|
@ -209,7 +254,8 @@ control_xfade_minus.addEventListener("click", e => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
control_xfade_plus.addEventListener("click", e => {
|
|
||||||
|
control_xfade_plus.addEventListener("click", () => {
|
||||||
// TODO: not yet implemented
|
// TODO: not yet implemented
|
||||||
fetch(`${API_URL}/xfade`).then(async r => {
|
fetch(`${API_URL}/xfade`).then(async r => {
|
||||||
if (r.status >= 400) {
|
if (r.status >= 400) {
|
||||||
|
@ -217,47 +263,52 @@ control_xfade_plus.addEventListener("click", e => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
control_volume_up.addEventListener("click", e => {
|
|
||||||
const v = Math.min(parseInt(control_volume.value) + VOLUME_STEP, 100);
|
control_volume_up.addEventListener("click", () => {
|
||||||
fetch(`${API_URL}/volume/${v}`).then(async r => {
|
const volume = Math.min(parseInt(control_volume.value) + VOLUME_STEP, 100);
|
||||||
|
fetch(`${API_URL}/volume/${volume}`).then(async r => {
|
||||||
if (r.status >= 400) {
|
if (r.status >= 400) {
|
||||||
console.error(`API returned ${r.status}: ${r.statusText}`);
|
console.error(`API returned ${r.status}: ${r.statusText}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
control_volume.value = v;
|
control_volume.value = volume;
|
||||||
|
|
||||||
});
|
});
|
||||||
control_volume_down.addEventListener("click", e => {
|
|
||||||
const v = Math.max(parseInt(control_volume.value) - VOLUME_STEP, 0);
|
control_volume_down.addEventListener("click", () => {
|
||||||
fetch(`${API_URL}/volume/${v}`).then(async r => {
|
const volume = Math.max(parseInt(control_volume.value) - VOLUME_STEP, 0);
|
||||||
|
fetch(`${API_URL}/volume/${volume}`).then(async r => {
|
||||||
if (r.status >= 400) {
|
if (r.status >= 400) {
|
||||||
console.error(`API returned ${r.status}: ${r.statusText}`);
|
console.error(`API returned ${r.status}: ${r.statusText}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
control_volume.value = v;
|
control_volume.value = volume;
|
||||||
});
|
});
|
||||||
control_volume.addEventListener("change", e => {
|
|
||||||
fetch(`${API_URL}/volume/${e.target.value}`).then(async r => {
|
control_volume.addEventListener("change", event => {
|
||||||
|
fetch(`${API_URL}/volume/${event.target.value}`).then(async r => {
|
||||||
if (r.status >= 400) {
|
if (r.status >= 400) {
|
||||||
console.error(`API returned ${r.status}: ${r.statusText}`);
|
console.error(`API returned ${r.status}: ${r.statusText}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 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`);
|
||||||
|
|
||||||
// Connection opened
|
// Connection opened
|
||||||
socket.addEventListener("open", (e) => {
|
socket.addEventListener("open", () => {
|
||||||
socket.send("Hello Server!");
|
socket.send("Hello Server!");
|
||||||
});
|
});
|
||||||
|
|
||||||
// Listen for messages and update UI state
|
// Listen for messages and update UI state
|
||||||
socket.addEventListener("message", (e) => {
|
socket.addEventListener("message", event => {
|
||||||
// Print out mpd response
|
// Print out mpd response
|
||||||
console.log(`DEBUG: ${e.data}`); // DEBUG
|
console.log(`DEBUG: ${event.data}`); // DEBUG
|
||||||
|
|
||||||
const msg = JSON.parse(e.data);
|
const msg = JSON.parse(event.data);
|
||||||
|
|
||||||
if ("mpd_status" in msg) {
|
if ("mpd_status" in msg) {
|
||||||
if (msg.mpd_status == null) {
|
if (msg.mpd_status == null) {
|
||||||
|
@ -286,20 +337,13 @@ socket.addEventListener("message", (e) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// update playback time
|
// update playback time
|
||||||
if ("elapsed" in msg.mpd_status && "duration" in msg.mpd_status) {
|
if ("time" in msg.mpd_status) {
|
||||||
const elapsed_hours = Math.floor(msg.mpd_status.elapsed / 3600);
|
const [elapsed, duration] = msg.mpd_status.time.split(":", 2)
|
||||||
const elapsed_minutes = Math.floor((msg.mpd_status.elapsed - elapsed_hours * 3600) / 60);
|
control_progress.value = elapsed;
|
||||||
const elapsed_seconds = Math.floor(msg.mpd_status.elapsed - elapsed_hours * 3600 - elapsed_minutes * 60);
|
control_progress.max = duration;
|
||||||
const duration_hours = Math.floor(msg.mpd_status.duration / 3600);
|
// triggers the update of control_time element
|
||||||
const duration_minutes = Math.floor((msg.mpd_status.duration - duration_hours * 3600) / 60);
|
const e = new Event("input");
|
||||||
const duration_seconds = Math.floor(msg.mpd_status.duration - duration_hours * 3600 - duration_minutes * 60);
|
control_progress.dispatchEvent(e);
|
||||||
control_time.value = `${elapsed_hours}:${elapsed_minutes.toString().padStart(2, '0')}:${elapsed_seconds.toString().padStart(2, '0')}/${duration_hours}:${duration_minutes.toString().padStart(2, '0')}:${duration_seconds.toString().padStart(2, '0')}`;
|
|
||||||
}
|
|
||||||
if ("elapsed" in msg.mpd_status) {
|
|
||||||
control_progress.value = msg.mpd_status.elapsed;
|
|
||||||
}
|
|
||||||
if ("duration" in msg.mpd_status) {
|
|
||||||
control_progress.max = msg.mpd_status.duration;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// update repeat state
|
// update repeat state
|
||||||
|
@ -340,79 +384,68 @@ socket.addEventListener("message", (e) => {
|
||||||
if ("mpd_current_song" in msg && msg.mpd_current_song != null) {
|
if ("mpd_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 = `<span>${msg.mpd_current_song.Artist} - ${msg.mpd_current_song.Title}</span>`
|
track = `${msg.mpd_current_song.Artist} - ${msg.mpd_current_song.Title}`
|
||||||
} else {
|
} else {
|
||||||
track = `<span>${msg.mpd_current_song.file}</span>`;
|
track = msg.mpd_current_song.file;
|
||||||
}
|
}
|
||||||
if (control_track.innerHTML.toString() !== track) {
|
if (control_track.innerHTML !== `<span>${track}</span>`) {
|
||||||
control_track.innerHTML = track;
|
control_track.innerHTML = `<span>${track}</span>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// update queue
|
// update queue
|
||||||
if ("mpd_queue" in msg && msg.mpd_queue != null) {
|
if ("mpd_queue" in msg && msg.mpd_queue != null) {
|
||||||
const tbody = document.createElement("tbody");
|
const tbody = document.createElement("tbody");
|
||||||
msg.mpd_queue.forEach(elem => {
|
msg.mpd_queue.forEach(song => {
|
||||||
const tr = document.createElement("tr");
|
const tr = document.createElement("tr");
|
||||||
tr.dataset.song_id = elem.Id;
|
tr.dataset.song_id = song.Id;
|
||||||
if ("songid" in msg.mpd_status && msg.mpd_status.songid === elem.Id) {
|
if ("songid" in msg.mpd_status && msg.mpd_status.songid === song.Id) {
|
||||||
tr.classList.add("playing");
|
tr.classList.add("playing");
|
||||||
} else {
|
|
||||||
tr.classList.remove("playing");
|
|
||||||
}
|
}
|
||||||
// TODO: check if current row is currently playing track
|
|
||||||
const pos = document.createElement("td");
|
const pos = document.createElement("td");
|
||||||
pos.innerText = elem.Pos;
|
pos.innerText = song.Pos;
|
||||||
const artist = document.createElement("td");
|
const artist = document.createElement("td");
|
||||||
if ("Artist" in elem) {
|
if ("Artist" in song) {
|
||||||
artist.innerText = elem.Artist;
|
artist.innerText = song.Artist;
|
||||||
}
|
}
|
||||||
const track = document.createElement("td");
|
const track = document.createElement("td");
|
||||||
if ("Title" in elem) {
|
if ("Title" in song) {
|
||||||
track.innerText = elem.Title;
|
track.innerText = song.Title;
|
||||||
} else {
|
} else {
|
||||||
track.innerText = elem.file;
|
track.innerText = song.file;
|
||||||
}
|
}
|
||||||
const album = document.createElement("td");
|
const album = document.createElement("td");
|
||||||
// album.innerText = "";
|
// TODO: Do songs have album info attached to them?
|
||||||
|
album.innerText = "";
|
||||||
const length = document.createElement("td");
|
const length = document.createElement("td");
|
||||||
const duration_hours = Math.floor(elem.duration / 3600);
|
length.innerText = secondsToTrackTime(song.duration);
|
||||||
const duration_minutes = Math.floor((elem.duration - duration_hours * 3600) / 60);
|
|
||||||
const duration_seconds = Math.floor(elem.duration - duration_hours * 3600 - duration_minutes * 60);
|
|
||||||
length.innerText = `${duration_hours}:${duration_minutes.toString().padStart(2, '0')}:${duration_seconds.toString().padStart(2, '0')}`;
|
|
||||||
const actions = document.createElement("td");
|
const actions = document.createElement("td");
|
||||||
// TODO: maybe use a instead of button?
|
if (parseInt(song.Pos) !== 0) {
|
||||||
const moveUp = document.createElement("button");
|
const moveUp = document.createElement("button");
|
||||||
moveUp.classList.add("borderless");
|
moveUp.classList.add("borderless");
|
||||||
moveUp.innerHTML = "🔺"; // 🔺 Red Triangle Pointed Down
|
moveUp.innerHTML = "🔺"; // 🔺 Red Triangle Pointed Down
|
||||||
moveUp.addEventListener("click", event => {
|
moveUp.addEventListener("click", event => { moveTrackInQueue(event, -1) });
|
||||||
console.log(`DEBUG: move song ${elem.Pos} up`);
|
actions.appendChild(moveUp);
|
||||||
// fetch(`${API_URL}/queue_del/${elem.Pos}`).then(r => {
|
} else {
|
||||||
// console.log(r.text());
|
const spacer = document.createElement("span")
|
||||||
// });
|
spacer.innerHTML = " ";
|
||||||
});
|
actions.appendChild(spacer);
|
||||||
// TODO: maybe use a instead of button?
|
}
|
||||||
|
if (parseInt(song.Pos) !== msg.mpd_queue.length - 1) {
|
||||||
const moveDown = document.createElement("button");
|
const moveDown = document.createElement("button");
|
||||||
moveDown.classList.add("borderless");
|
moveDown.classList.add("borderless");
|
||||||
moveDown.innerHTML = "🔻"; // 🔻 Red Triangle Pointed Up
|
moveDown.innerHTML = "🔻"; // 🔻 Red Triangle Pointed Up
|
||||||
moveDown.addEventListener("click", event => {
|
moveDown.addEventListener("click", event => {moveTrackInQueue(event, 1)});
|
||||||
console.log(`DEBUG: move song ${elem.Pos} down`);
|
actions.appendChild(moveDown);
|
||||||
// fetch(`${API_URL}/queue_del/${elem.Pos}`).then(r => {
|
} else {
|
||||||
// console.log(r.text());
|
const spacer = document.createElement("span")
|
||||||
// });
|
spacer.innerHTML = " ";
|
||||||
});
|
actions.appendChild(spacer);
|
||||||
// TODO: maybe use a instead of button?
|
}
|
||||||
const remove = document.createElement("button");
|
const remove = document.createElement("button");
|
||||||
remove.classList.add("borderless");
|
remove.classList.add("borderless");
|
||||||
remove.innerHTML = "❌"; // ❌ Cross mark
|
remove.innerHTML = "❌"; // ❌ Cross mark
|
||||||
remove.addEventListener("click", event => {
|
remove.addEventListener("click", removeTrackFromQueue);
|
||||||
console.log(`DEBUG: remove song id ${elem.Id} from queue`);
|
|
||||||
fetch(`${API_URL}/queue/delete/${elem.Id}`).then(r => {
|
|
||||||
console.log(r.text());
|
|
||||||
});
|
|
||||||
});
|
|
||||||
actions.appendChild(moveUp);
|
|
||||||
actions.appendChild(moveDown);
|
|
||||||
actions.appendChild(remove);
|
actions.appendChild(remove);
|
||||||
tr.appendChild(pos);
|
tr.appendChild(pos);
|
||||||
tr.appendChild(artist);
|
tr.appendChild(artist);
|
||||||
|
@ -438,8 +471,8 @@ socket.addEventListener("message", (e) => {
|
||||||
window.setInterval(() => {
|
window.setInterval(() => {
|
||||||
if (socket.readyState === socket.OPEN) {
|
if (socket.readyState === socket.OPEN) {
|
||||||
socket.send("#status");
|
socket.send("#status");
|
||||||
connection_state.innerHTML = "✅ Connected"; // ❌ Cross Mark
|
connection_state.innerHTML = "✅ Connected"; // ✅ Check Mark Button
|
||||||
} else {
|
} else {
|
||||||
connection_state.innerHTML = "❌ Disconnected"; // ✅ Check Mark Button
|
connection_state.innerHTML = "❌ Disconnected"; // ❌ Cross Mark
|
||||||
}
|
}
|
||||||
}, 1000);
|
}, 1000);
|
|
@ -1,47 +0,0 @@
|
||||||
function addSongToQueue(song) {
|
|
||||||
const table = document.querySelector("#queue tbody");
|
|
||||||
const tr = document.createElement("tr");
|
|
||||||
const pos = document.createElement("td");
|
|
||||||
pos.innerText = "?"; // TODO: figure out queue length +1
|
|
||||||
const artist = document.createElement("td");
|
|
||||||
artist.innerText = song.artist;
|
|
||||||
const track = document.createElement("td");
|
|
||||||
track.innerText = song.track;
|
|
||||||
const album = document.createElement("td");
|
|
||||||
album.innerText = song.album;
|
|
||||||
const length = document.createElement("td");
|
|
||||||
length.innerText = song.length;
|
|
||||||
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 ${song.id} up`);
|
|
||||||
});
|
|
||||||
// TODO: maybe use `a` instead of `button`?
|
|
||||||
const moveDown = document.createElement("button");
|
|
||||||
moveDown.classList.add("borderless");
|
|
||||||
moveDown.innerHTML = "$#x1F53B;"; // 🔻 Red Triangle Pointed Up
|
|
||||||
moveDown.addEventListener("click", event => {
|
|
||||||
console.log(`DEBUG: move song ${song.id} down`);
|
|
||||||
});
|
|
||||||
// TODO: maybe use `a` instead of `button`?
|
|
||||||
const remove = document.createElement("button");
|
|
||||||
remove.classList.add("borderless");
|
|
||||||
remove.innerHTML = "$#x274C;"; // ❌ Cross mark; 🗑️ Wastebasket
|
|
||||||
remove.addEventListener("click", event => {
|
|
||||||
console.log(`DEBUG: remove song ${song.id} 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);
|
|
||||||
}
|
|
Loading…
Reference in a new issue