small improvements; aur package
This commit is contained in:
		
							parent
							
								
									79a6049a91
								
							
						
					
					
						commit
						03677e24be
					
				
					 15 changed files with 386 additions and 287 deletions
				
			
		|  | @ -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?
 |       } | ||||||
|       const moveDown = document.createElement("button"); |       if (parseInt(song.Pos) !== msg.mpd_queue.length - 1) { | ||||||
|       moveDown.classList.add("borderless"); |         const moveDown = document.createElement("button"); | ||||||
|       moveDown.innerHTML = "🔻"; // 🔻 Red Triangle Pointed Up
 |         moveDown.classList.add("borderless"); | ||||||
|       moveDown.addEventListener("click", event => { |         moveDown.innerHTML = "🔻"; // 🔻 Red Triangle Pointed Up
 | ||||||
|         console.log(`DEBUG: move song ${elem.Pos} down`); |         moveDown.addEventListener("click", event => {moveTrackInQueue(event, 1)}); | ||||||
|         // fetch(`${API_URL}/queue_del/${elem.Pos}`).then(r => {
 |         actions.appendChild(moveDown); | ||||||
|         //   console.log(r.text());
 |       } else { | ||||||
|         // });
 |         const spacer = document.createElement("span") | ||||||
|       }); |         spacer.innerHTML = " "; | ||||||
|       // TODO: maybe use a instead of button?
 |         actions.appendChild(spacer); | ||||||
|  |       } | ||||||
|       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…
	
	Add table
		Add a link
		
	
		Reference in a new issue