Compare commits

..

2750 Commits

Author SHA1 Message Date
Deluan
af4b2bb4c9 go mod tidy 2023-05-13 21:30:05 -04:00
Deluan
4773adba00 Add aliases for playlists and service commands 2023-05-13 21:28:13 -04:00
Deluan
7bbf4cbaea Fix lint errors 2023-05-13 21:28:13 -04:00
Deluan
cff19445ba Add service management 2023-05-13 21:28:11 -04:00
dependabot[bot]
0d920c7832 Bump github.com/prometheus/client_golang from 1.14.0 to 1.15.1 (#2342)
Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.14.0 to 1.15.1.
- [Release notes](https://github.com/prometheus/client_golang/releases)
- [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prometheus/client_golang/compare/v1.14.0...v1.15.1)

---
updated-dependencies:
- dependency-name: github.com/prometheus/client_golang
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-13 14:34:11 -04:00
dependabot[bot]
957a73e052 Bump github.com/mileusna/useragent from 1.2.1 to 1.3.2 (#2319)
Bumps [github.com/mileusna/useragent](https://github.com/mileusna/useragent) from 1.2.1 to 1.3.2.
- [Release notes](https://github.com/mileusna/useragent/releases)
- [Commits](https://github.com/mileusna/useragent/compare/v1.2.1...v1.3.2)

---
updated-dependencies:
- dependency-name: github.com/mileusna/useragent
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-13 14:32:01 -04:00
dependabot[bot]
abc418eaa2 Bump github.com/onsi/ginkgo/v2 from 2.9.2 to 2.9.4 (#2343)
Bumps [github.com/onsi/ginkgo/v2](https://github.com/onsi/ginkgo) from 2.9.2 to 2.9.4.
- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v2.9.2...v2.9.4)

---
updated-dependencies:
- dependency-name: github.com/onsi/ginkgo/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-13 14:28:27 -04:00
dependabot[bot]
1128322011 Bump golang.org/x/tools from 0.8.0 to 0.9.1 (#2350)
Bumps [golang.org/x/tools](https://github.com/golang/tools) from 0.8.0 to 0.9.1.
- [Release notes](https://github.com/golang/tools/releases)
- [Commits](https://github.com/golang/tools/compare/v0.8.0...v0.9.1)

---
updated-dependencies:
- dependency-name: golang.org/x/tools
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-13 14:28:05 -04:00
dependabot[bot]
2e479defd5 Bump github.com/go-chi/httprate from 0.7.1 to 0.7.4 (#2320)
Bumps [github.com/go-chi/httprate](https://github.com/go-chi/httprate) from 0.7.1 to 0.7.4.
- [Release notes](https://github.com/go-chi/httprate/releases)
- [Commits](https://github.com/go-chi/httprate/compare/v0.7.1...v0.7.4)

---
updated-dependencies:
- dependency-name: github.com/go-chi/httprate
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-13 14:24:37 -04:00
dependabot[bot]
8311a7f215 Bump golang.org/x/sync from 0.1.0 to 0.2.0 (#2344)
Bumps [golang.org/x/sync](https://github.com/golang/sync) from 0.1.0 to 0.2.0.
- [Commits](https://github.com/golang/sync/compare/v0.1.0...v0.2.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sync
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-13 14:23:40 -04:00
dependabot[bot]
6ec8f78076 Bump github.com/pressly/goose/v3 from 3.10.0 to 3.11.2 (#2341)
Bumps [github.com/pressly/goose/v3](https://github.com/pressly/goose) from 3.10.0 to 3.11.2.
- [Release notes](https://github.com/pressly/goose/releases)
- [Changelog](https://github.com/pressly/goose/blob/master/.goreleaser.yml)
- [Commits](https://github.com/pressly/goose/compare/v3.10.0...v3.11.2)

---
updated-dependencies:
- dependency-name: github.com/pressly/goose/v3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-13 14:23:17 -04:00
Logan Marchione
3e879d2a8c Add K8s manifest (#2330)
* Add K8s manifest

* Update README.md
2023-04-29 16:14:44 -04:00
Jeff Henson
6d3d005fca Allow the setrlimit syscall - #1961 (#2333)
This appears to be used by newer go versions and navidrome fails to
start unless it's allowed.

Signed-off-by: Jeff Henson <jeff@henson.io>
2023-04-27 21:30:43 -04:00
Deluan
c12510d6e2 Update README 2023-04-11 14:00:44 -04:00
Deluan
0bd73bd3f4 Better GH Action names 2023-04-11 09:16:25 -04:00
Deluan
8c120ee3c9 Better GH Action names 2023-04-11 09:15:08 -04:00
Deluan
9590b3c25d Use the highest resolution artist image from Spotify 2023-04-10 15:34:22 -04:00
Deluan
4887c33053 Bump golang.org/x packages 2023-04-10 14:07:12 -04:00
Subhajit Ghosh
da21acba92 Give page the right lang attribute (#2299)
* Fixed issue no #2174

Signed-off-by: Subhajit Ghosh <subhajitstd07@gmail.com>

* Fixed issue no #2174

---------

Signed-off-by: Subhajit Ghosh <subhajitstd07@gmail.com>
2023-04-08 13:39:59 -04:00
Deluan Quintão
9154e44eb4 Add initial support for OpenSubsonic. (#2302) 2023-04-08 13:25:37 -04:00
Deluan Quintão
2e01063429 Update translations (#2198)
Co-authored-by: deluan <deluan@users.noreply.github.com>
2023-04-06 22:09:49 -04:00
Deluan
597e5abed6 Fix push develop to Docker Hub 2023-04-06 20:11:35 -04:00
Deluan Quintão
92994efe48 Publish docker images to ghcr.io (#2298)
* Publish all images (including PRs) to GHCR, only releases and `develop` to Docker Hub
2023-04-06 19:53:31 -04:00
Deluan
9628b1389d Add help msg for JS formatting errors 2023-04-06 11:45:32 -04:00
Deluan
347424009d Show Player name, not client, in mobile view. Fix #1659. 2023-04-05 22:48:33 -04:00
Deluan
ecac74c2bd Fix getSongsByGenre pagination. Fix #1640 2023-04-05 22:39:32 -04:00
Deluan
ddfde7bfc8 Run lint on latest Go 1.20.x 2023-04-04 19:13:24 -04:00
Deluan
96c50d369a Upgrade to Go 1.20.3 and GoRelease 1.16.1 2023-04-04 19:10:03 -04:00
Deluan
310c816cdd Use Go 1.20 for local cross-compilation 2023-04-04 15:33:42 -04:00
Deluan
bd402fb2a8 Fix IntelliJ warning 2023-04-04 13:01:32 -04:00
dependabot[bot]
8bb141b730 Bump github.com/spf13/cobra from 1.6.1 to 1.7.0 (#2293)
Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.6.1 to 1.7.0.
- [Release notes](https://github.com/spf13/cobra/releases)
- [Commits](https://github.com/spf13/cobra/compare/v1.6.1...v1.7.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/cobra
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-04 11:04:18 -04:00
Deluan
f25b91b4d8 Remove any previous UNIX socket file 2023-04-04 11:03:37 -04:00
dependabot[bot]
f959701d9d Bump github.com/onsi/gomega from 1.27.5 to 1.27.6 (#2292)
Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.27.5 to 1.27.6.
- [Release notes](https://github.com/onsi/gomega/releases)
- [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/gomega/compare/v1.27.5...v1.27.6)

---
updated-dependencies:
- dependency-name: github.com/onsi/gomega
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-04 10:55:36 -04:00
Deluan
61dd8d55ca Fix data race in scanner 2023-04-04 10:51:43 -04:00
Deluan
bbb9461000 Increase max Server-Sent Events' ID 2023-04-04 10:46:57 -04:00
Deluan
95016f687e Fix SQL migrations 2023-04-04 10:45:55 -04:00
Deluan
c3cc7dee01 Enable SQL migrations 2023-04-04 10:30:28 -04:00
Deluan
7847f19c9d Upgrade goose 2023-04-04 10:05:31 -04:00
dependabot[bot]
7a0df4429e Bump github.com/onsi/gomega from 1.27.5 to 1.27.6 (#2288)
Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.27.5 to 1.27.6.
- [Release notes](https://github.com/onsi/gomega/releases)
- [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/gomega/compare/v1.27.5...v1.27.6)

---
updated-dependencies:
- dependency-name: github.com/onsi/gomega
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-03 21:01:39 -04:00
Deluan
6a8d2dc87d Only use valid images for artist.* artwork 2023-04-03 18:07:15 -04:00
Deluan
de816e8e5d Fix lint error 2023-04-03 11:15:46 -04:00
Deluan
b22d0366d5 Use channels for EventStream instead of diodes 2023-04-03 10:51:24 -04:00
Deluan
fea2de8f90 Add Galician translation. 2023-04-02 18:58:44 -04:00
Deluan
d6dd0aaae7 Close SSE connection on write error 2023-04-02 18:40:58 -04:00
Fadeeeeeeee
458017b112 Update Chinese translations (#2260)
* Update Chinese translations

* Update Chinese translations

* Update Chinese translations
2023-04-02 18:40:48 -04:00
Deluan
e6bfa2bb0b Convert our usage of go-diodes into a simplified, generic version 2023-04-01 21:53:45 -04:00
Deluan
1c7fb74a1d Fix writeEvents race condition.
This required removing the compress middleware from the /events route.
2023-04-01 20:54:15 -04:00
Deluan
83ae2ba3e6 Fix race condition 2023-04-01 18:40:37 -04:00
Joakim Repomaa
2ccc5bc941 Implement artist art priority (#2266)
* implement artist art priority

* add tests
2023-03-30 18:28:05 -04:00
Deluan
406554f1c4 Remove some tools from dependencies, reducing the modules dependencies 2023-03-30 15:33:47 -04:00
Deluan
e89cdf6199 Fix flaky tests 2023-03-30 09:25:18 -04:00
Deluan
cf804a52ef Add support for listening on Unix socket.
For that to work, specify the config option `Address` with `unix:/path/to/socket/file`.

Closes #1477
2023-03-29 16:05:59 -04:00
Deluan
628fd69d3d Fix race condition 2023-03-29 15:17:34 -04:00
Deluan
1d00d1e986 Fix writeEvent function.
It would not send anything if the `ResponseWriter` was not a `http.Flusher`, and it was leaking channels with `time.After`
2023-03-29 15:04:40 -04:00
Deluan
607c4067b8 Show translation changes on pipeline 2023-03-29 13:03:37 -04:00
Deluan
e3079d81ea More tests 2023-03-27 20:36:23 -04:00
Deluan
3bedd89c17 Bump dependencies 2023-03-27 14:48:20 -04:00
dependabot[bot]
57829bfa4c Bump github.com/lestrrat-go/jwx/v2 from 2.0.8 to 2.0.9 (#2282)
Bumps [github.com/lestrrat-go/jwx/v2](https://github.com/lestrrat-go/jwx) from 2.0.8 to 2.0.9.
- [Release notes](https://github.com/lestrrat-go/jwx/releases)
- [Changelog](https://github.com/lestrrat-go/jwx/blob/develop/v2/Changes)
- [Commits](https://github.com/lestrrat-go/jwx/compare/v2.0.8...v2.0.9)

---
updated-dependencies:
- dependency-name: github.com/lestrrat-go/jwx/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-27 14:47:32 -04:00
Deluan
b998c05ca0 Some refactorings 2023-03-26 21:28:37 -04:00
Deluan
05d381c26f Add more middleware tests 2023-03-26 21:28:36 -04:00
zayedalsaidi
59a9c056b4 Add Arabic translation (#2277) 2023-03-26 19:56:59 -04:00
Deluan
0de81b8352 Bump caniuse-lite 2023-03-26 19:38:09 -04:00
Deluan
91785ecf36 Add tests for core.Archiver 2023-03-26 19:34:12 -04:00
Deluan
65eeb5ec1a Add tests for serverAddressMiddleware 2023-03-26 13:29:57 -04:00
Julien Voisin
17e0cd5504 Shuffle the tests, just in case (#2272) 2023-03-22 20:12:12 -04:00
Deluan
3a6d2dcd49 More log redaction 2023-03-21 11:16:00 -04:00
Deluan
183b462fed Fix zip comments in Share downloads. 2023-03-21 10:34:04 -04:00
Deluan
16fc4eb792 Fix missing extensions in Share downloads.
See https://github.com/navidrome/navidrome/pull/2246#issuecomment-1476996397
2023-03-21 10:31:00 -04:00
dependabot[bot]
6fee744d99 Bump github.com/onsi/gomega from 1.27.3 to 1.27.4 (#2268)
Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.27.3 to 1.27.4.
- [Release notes](https://github.com/onsi/gomega/releases)
- [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/gomega/compare/v1.27.3...v1.27.4)

---
updated-dependencies:
- dependency-name: github.com/onsi/gomega
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-20 14:15:32 -04:00
dependabot[bot]
74d5c7bc82 Bump github.com/golangci/golangci-lint from 1.51.2 to 1.52.0 (#2270)
Bumps [github.com/golangci/golangci-lint](https://github.com/golangci/golangci-lint) from 1.51.2 to 1.52.0.
- [Release notes](https://github.com/golangci/golangci-lint/releases)
- [Changelog](https://github.com/golangci/golangci-lint/blob/master/CHANGELOG.md)
- [Commits](https://github.com/golangci/golangci-lint/compare/v1.51.2...v1.52.0)

---
updated-dependencies:
- dependency-name: github.com/golangci/golangci-lint
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-20 14:15:18 -04:00
dependabot[bot]
880fc9e195 Bump github.com/Masterminds/squirrel from 1.5.3 to 1.5.4 (#2269)
Bumps [github.com/Masterminds/squirrel](https://github.com/Masterminds/squirrel) from 1.5.3 to 1.5.4.
- [Release notes](https://github.com/Masterminds/squirrel/releases)
- [Commits](https://github.com/Masterminds/squirrel/compare/v1.5.3...v1.5.4)

---
updated-dependencies:
- dependency-name: github.com/Masterminds/squirrel
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-20 14:15:01 -04:00
Xidorn Quan
1430aa108d Update play_date on scrobble only when newer - #2262 (#2263)
* fix(persistence): Update play_date on scrobble only when newer - #2262

Signed-off-by: Xidorn Quan <me@upsuper.org>

* expand iff

---------

Signed-off-by: Xidorn Quan <me@upsuper.org>
2023-03-18 18:28:01 -04:00
Deluan
673880d661 Add option to load TLS cert/key, and use HTTPS 2023-03-17 16:32:13 -04:00
Deluan
7ea111322b Don't pump the volume up to 100% if it is not in a mobile device. Fix #2255
This detection method is not bullet-proof, but should work for now.

Ref: https://stackoverflow.com/a/3540295
2023-03-16 17:25:07 -04:00
Deluan
377e7ebd52 Disable share downloading when EnableDownloads is false.
Fixes https://github.com/navidrome/navidrome/pull/2246#issuecomment-1472341635
2023-03-16 13:11:26 -04:00
Deluan
23c483da10 Only freezes issues/prs after 120 days 2023-03-15 17:53:54 -04:00
Deluan
c380139606 Fix lint 2023-03-15 13:10:14 -04:00
Deluan
63fbccf5a9 Enable memory profiling 2023-03-15 12:43:25 -04:00
Deluan
1f6ec1d9f5 Add pprof endpoint, disabled by default 2023-03-15 10:56:16 -04:00
dependabot[bot]
cad8156353 Bump webpack from 5.74.0 to 5.76.1 in /ui (#2256)
Bumps [webpack](https://github.com/webpack/webpack) from 5.74.0 to 5.76.1.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.74.0...v5.76.1)

---
updated-dependencies:
- dependency-name: webpack
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-15 09:13:22 -04:00
Deluan Quintão
f7d4fcdcc1 Convert all Subsonic API ints to int32 as per specification (#2252)
* Fix Genre

* Fix ArtistID3

* Fix AlbumID3

* Fix Child

* Fix NowPlayingEntry

* Fix Playlist

* Fix Share

* Fix User

* Fix Artist

* Fix Directory

* Fix Error
2023-03-14 09:48:52 -04:00
Deluan Quintão
002cb4ed71 Update README.md 2023-03-13 19:34:47 -04:00
Deluan Quintão
e13eaebbde Update README.md 2023-03-13 19:32:13 -04:00
dependabot[bot]
539c0faedb Bump github.com/onsi/ginkgo/v2 from 2.9.0 to 2.9.1 (#2251)
Bumps [github.com/onsi/ginkgo/v2](https://github.com/onsi/ginkgo) from 2.9.0 to 2.9.1.
- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v2.9.0...v2.9.1)

---
updated-dependencies:
- dependency-name: github.com/onsi/ginkgo/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-13 14:42:40 -04:00
Moink
4ccb6ccb09 Update Chinese translations (#2250) 2023-03-12 20:24:31 -04:00
Deluan
ec0eb2866b Hide Love button on Artist Page when EnableFavourites=false. Fix #2245 2023-03-10 23:34:02 -05:00
Deluan
b520d8827a Add download button in the SharePlayer 2023-03-10 23:33:29 -05:00
Deluan
a7d3e6e1f1 Add option to allow share to be downloaded 2023-03-10 23:33:29 -05:00
Deluan
a22eef39f7 Add share download endpoint 2023-03-10 23:33:29 -05:00
Torsten Curdt
50d9838652 Add docker compose examples, with traefik or caddy and without, fixes #476 (#2240)
* add docker compose examples, with traefik or caddy and without, fixes #476

* ignore the docker-compose in root, but not the one in contrib
2023-03-10 18:57:09 -05:00
Deluan
016454c217 Bump golangci-lint version 2023-03-10 17:46:05 -05:00
Deluan
41a5db72e7 Update more dependencies 2023-03-10 17:31:13 -05:00
Deluan
6e6ec58429 Update sanitize and golang.org/x dependencies 2023-03-10 17:21:08 -05:00
Deluan
c88e1baa7c Make playlist tracks match case-insensitive. Fix #1720 2023-03-10 12:29:38 -05:00
Deluan
e16e3d2e7b Fix pipeline. 2023-03-09 22:25:56 -05:00
Deluan
339a6239fd Ignore Recycle Bins in Windows. Fix #1074 2023-03-09 22:14:58 -05:00
Deluan
47f15ccbc3 Make AlbumArtists clickable in AlbumSongs view. Fixes #1627 2023-03-09 18:04:07 -05:00
Deluan
9667f3cd48 Add file path to toggleable columns in SongList view. Fix #1719 2023-03-09 17:47:20 -05:00
Deluan
5773fa0349 Fix discussions links 2023-03-08 14:14:42 -05:00
Deluan
527c378c41 Add feature request link to About dialog 2023-03-08 12:41:51 -05:00
Deluan
caa0788853 Fine tune issue templates 2023-03-08 12:27:28 -05:00
Deluan
40b14e6d81 Add log-output to lock-threads bot 2023-03-06 20:12:46 -05:00
Deluan
becd50eb68 Remove debug-only option from stale bot 2023-03-06 20:08:02 -05:00
Deluan
15b5aa9143 Add stale/lock-threads bot 2023-03-06 20:01:42 -05:00
Deluan
7987d982cf Fix pipeline's lint error message 2023-03-06 19:38:20 -05:00
Deluan
1dd074bbb4 Add new issue templates 2023-03-06 17:15:36 -05:00
Deluan
7eac9d2bbe Bump dependencies 2023-03-05 21:09:45 -05:00
dependabot[bot]
362d8c50fe Bump github.com/onsi/gomega from 1.26.0 to 1.27.1 (#2204)
Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.26.0 to 1.27.1.
- [Release notes](https://github.com/onsi/gomega/releases)
- [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/gomega/compare/v1.26.0...v1.27.1)

---
updated-dependencies:
- dependency-name: github.com/onsi/gomega
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-05 20:25:16 -05:00
dependabot[bot]
01c604ba7b Bump github.com/stretchr/testify from 1.8.1 to 1.8.2 (#2216)
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.8.1 to 1.8.2.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.8.1...v1.8.2)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-05 20:23:36 -05:00
dependabot[bot]
2c129a2890 Bump golang.org/x/image from 0.0.0-20191009234506-e7c1f5e7dbb8 to 0.5.0 (#2217)
Bumps [golang.org/x/image](https://github.com/golang/image) from 0.0.0-20191009234506-e7c1f5e7dbb8 to 0.5.0.
- [Release notes](https://github.com/golang/image/releases)
- [Commits](https://github.com/golang/image/commits/v0.5.0)

---
updated-dependencies:
- dependency-name: golang.org/x/image
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-05 20:23:04 -05:00
Deluan
5fc4076aec Fix translation key 2023-02-16 21:05:11 -05:00
Deluan
d303ad2676 Bump dependencies 2023-02-15 22:46:56 -05:00
Deluan
c4a68c8a0a Fix build pipeline 2023-02-15 22:27:16 -05:00
Deluan
ad9ce98cc2 Use GoLang 1.20.1 in pipeline 2023-02-15 22:21:50 -05:00
Deluan
a134b1b608 Use sync/atomic package, now that we are at Go 1.19 2023-02-15 21:21:59 -05:00
Deluan
6dce4b2478 Remove custom atomic.Bool, we are now at Go 1.19 2023-02-15 21:18:24 -05:00
Deluan
10108c63c9 Allow BaseURL to contain full server url, including scheme and host. Fix #2183 2023-02-15 21:13:38 -05:00
Deluan
aac6e2cb07 Add path to cookies. Fix #1580 2023-02-15 20:23:32 -05:00
Deluan
0ffdb2eee0 Bump minimum Go version to 1.19 2023-02-15 20:20:08 -05:00
Kendall Garner
8b93962fad Limit share size while handling theme properly (#2171)
* limit player to 768 px

Signed-off-by: Kendall Garner <17521368+kgarner7@users.noreply.github.com>

* fix size limitation

---------

Signed-off-by: Kendall Garner <17521368+kgarner7@users.noreply.github.com>
2023-02-13 20:00:39 -05:00
Kendall Garner
b129cae0d8 Only create context if gain mode active (#2173) 2023-02-13 19:57:23 -05:00
Deluan
2400e4f60d Fix DB migration. Fix #2168 2023-02-12 14:58:33 -05:00
Deluan Quintão
3cd934abd7 Update translations (#2159)
Co-authored-by: deluan <deluan@users.noreply.github.com>
2023-02-11 20:25:01 -05:00
Deluan
727632b616 Refactor play tracking 2023-02-11 18:52:28 -05:00
Kendall Garner
9e268678f2 Limit Share player to 768 px (#2164)
Signed-off-by: Kendall Garner <17521368+kgarner7@users.noreply.github.com>
2023-02-11 12:38:35 -05:00
RTapeLoadingError
bb29ad3b12 Update Spanish translation (#2165)
Updated some empty fields.
2023-02-11 12:33:59 -05:00
Deluan
b68ed2e4f9 Fix album's image_files 2023-02-09 18:29:08 -05:00
Deluan
0c3ac906b8 Enable ReplayGain by default and always import RG tags 2023-02-09 17:45:38 -05:00
Deluan
b0e58cb885 Use Navidrome's own public images endpoint for getAlbumInfo's imageURLs 2023-02-08 20:03:31 -05:00
Deluan
806713719f Add lastUpdated to coverArt ids. Helps with invalidating art cache client-side. 2023-02-08 20:03:31 -05:00
Deluan
a3b8682d44 Fix polling of buffered scrobbles 2023-02-07 19:18:26 -05:00
Deluan
0bbb54934b Use Go 1.20 in pipeline, drop support for 1.18 2023-02-07 14:28:02 -05:00
Deluan
759ff844e2 Make ffmpeg path configurable, also finds it automatically in current folder. Fixes #1932 2023-02-07 13:46:09 -05:00
Deluan
b8c5e49dd3 Close stream when downloading files, fix fd leak 2023-02-07 09:58:50 -05:00
Deluan
05c6cdea1a Don't cancel transcoding session if context is canceled 2023-02-07 09:58:50 -05:00
Daniel Hammer
fc8462dc8a "Spell-Jacking" mitigation ~ prevent sensitive data leak from spell checker. (#2091)
@see https://www.otto-js.com/news/article/chrome-and-edge-enhanced-spellcheck-features-expose-pii-even-your-passwords

Co-authored-by: Daniel Hammer <daniel.hammer+oss@gmail.com>
2023-02-06 16:29:28 -05:00
Deluan
9d459fbd0a Abort start-up if config file is invalid 2023-02-06 13:00:07 -05:00
Deluan
9b2dd1bb06 Fix playlist delete and reorder actions 2023-02-06 10:41:33 -05:00
Deluan
bfaf4a3388 Add logs to cache hunter 2023-02-06 10:41:33 -05:00
Deluan
a7f15facf9 Bump github.com/golangci/golangci-lint to 1.51.1 2023-02-06 10:41:33 -05:00
Deluan
ee8f6447eb Add option to disable Cache Warmer. Related to #2142 2023-02-06 10:41:33 -05:00
Deluan
dad4949a6d Refactor Subsonic search to make it a bit more readable 2023-02-05 00:58:34 -05:00
Deluan
3ce3185118 Don't retrieve Various Artists and Unknown Artist info from Last.fm 2023-02-04 21:18:51 -05:00
Deluan
a50d9c8b67 Use the latest sanitize, to fix some diacritics 2023-02-04 19:09:14 -05:00
Kendall Garner
f8dfb3ad86 Clearer lyrics in Nord theme (#2146) 2023-02-04 13:02:15 -05:00
Deluan
255f8e4a76 Update react-player, fix #2117 2023-02-04 12:49:47 -05:00
Deluan
eba70ab826 Change throttling log messages 2023-02-04 12:37:47 -05:00
Deluan
ee6b10db72 Replace custom code with errgroup 2023-02-04 12:37:47 -05:00
Deluan
797cc87141 Enqueue external metadata refreshes 2023-02-04 12:37:47 -05:00
dependabot[bot]
bfbe980637 Bump http-cache-semantics from 4.1.0 to 4.1.1 in /ui (#2139)
Bumps [http-cache-semantics](https://github.com/kornelski/http-cache-semantics) from 4.1.0 to 4.1.1.
- [Release notes](https://github.com/kornelski/http-cache-semantics/releases)
- [Commits](https://github.com/kornelski/http-cache-semantics/commits)

---
updated-dependencies:
- dependency-name: http-cache-semantics
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-03 16:33:50 -05:00
Deluan
d9d0a97674 Better log message 2023-02-03 11:35:10 -05:00
Deluan
c031167bb1 Don't retrieve all artist external metadata if we just want artist images 2023-02-03 11:06:53 -05:00
Deluan
4a25e6d3d8 Fix Mapped Similar Artists log 2023-02-03 09:57:29 -05:00
Deluan
ad2ad514b3 Add dev option to increase external metadata cache expiration. More logs 2023-02-02 16:55:12 -05:00
Deluan
588ee94f7c Discard request for image canceled by the client before any further processing 2023-02-02 14:55:07 -05:00
Deluan
3c5032a3e8 Add migration to rebuild albums paths 2023-02-02 14:42:01 -05:00
Deluan
bcab3cc0f9 Add throttling to /share/img endpoint.
See: https://github.com/navidrome/navidrome/issues/2130#issuecomment-1414152343
2023-02-02 13:59:04 -05:00
Deluan
9b81aa4403 Fix artwork resolution when paths contains :. Fix #2137 2023-02-02 12:18:55 -05:00
Deluan
f904784e67 Bump dependencies 2023-02-02 11:20:52 -05:00
Deluan
0ce750d469 Update golangci-lint and fix lint errors 2023-02-02 11:10:28 -05:00
Deluan
cf04db7a98 Don't try to connect to external services if artist is Unknown 2023-02-02 10:57:37 -05:00
Deluan
f4b50c493c When retrieving images from external sources, avoid calling it again if data is already cached locally.
Relates to https://github.com/navidrome/navidrome/issues/2130#issuecomment-1412742918
2023-02-02 10:38:17 -05:00
Deluan
4a7e86e989 Fix file descriptor leaking. 2023-02-02 10:36:49 -05:00
vlfldr
a1a5b2fc30 Fix invisible checkboxes in Gruvbox theme (#2135)
* Added Gruvbox Dark color theme

* Correct formatting by running prettier

* Fixed invisible checkboxes and tweaked colors in Gruvbox theme
2023-02-01 13:33:55 -05:00
Deluan
f00e6117ff Invalidate artist cache (by changing cache key format) 2023-02-01 10:34:55 -05:00
Deluan
d8e794317f Return 404 when artwork is not available in /share/img endpoint 2023-02-01 10:34:02 -05:00
Deluan
128b626ec9 Add option to change max playlists shown in UI's sidebar, MaxSidebarPlaylists. Fix #2077 2023-02-01 10:25:25 -05:00
Deluan
d683297fa7 Better behaviour of Prev/Next buttons when share has only one song:
- Allow Prev to restart the song
- Disable Next
2023-01-31 21:27:47 -05:00
Deluan
aaf58bbd32 Handle nil pointer dereference. Fix #2133 2023-01-31 20:54:15 -05:00
deluan
58c46827cd Update translations 2023-01-31 10:05:55 -05:00
Deluan
712d8f9fcc Add trace logs to calls to external services 2023-01-31 09:37:09 -05:00
Deluan
b6fcfa9fc8 Add a fallback when the browser does not support copying the share link to clipboard (not a secure origin)
See: https://stackoverflow.com/a/51823007
2023-01-30 12:09:01 -05:00
Deluan
762a1ba998 Fix downloading and sharing from a playlist. Fix #2123 2023-01-30 11:20:22 -05:00
deluan
25374b3bbe Update translations 2023-01-30 08:42:01 -05:00
Deluan
68e6115789 Rename DevEnableShare to EnableSharing 2023-01-29 20:33:10 -05:00
Deluan
a651d65a5b Add a comment to the generated zip 2023-01-29 17:08:18 -05:00
Deluan
dc56c52557 Refactor zip archiver.
Add `disc` to path when downloading albums. Fix #2121
2023-01-29 15:25:20 -05:00
Deluan
5163df6531 Rollback changes to Chinese translations
Were not updated in POEditor
2023-01-27 11:09:42 -05:00
deluan
fc693e5601 Update translations 2023-01-27 11:00:43 -05:00
Deluan
731bd7ee73 Fix update translations job 2023-01-27 10:26:03 -05:00
Deluan
9f684e5a69 Add job to create translations PRs 2023-01-27 10:15:04 -05:00
Deluan
e2ea5eba8c Disable creation of shares when feature is disabled.
Fix https://github.com/navidrome/navidrome/pull/2106#issuecomment-1404731388
2023-01-26 10:12:52 -05:00
Deluan Quintão
b825d3cfac Fix versioning releases in the pipeline (#2101)
* Revert "Disable buildvcs flag"

This reverts commit 1374dab087.

* Config /github/workspace folder as trusted
2023-01-25 15:35:01 -05:00
Deluan
1950c07b1d Disable external links when EnableExternalServices is false. Fix #2022 2023-01-25 10:28:03 -05:00
Deluan
e0fc997adb Fix Share dialog titles for Album and Playlist 2023-01-25 10:20:28 -05:00
Deluan
5eefb265e5 Simplify radio CRUD code 2023-01-25 10:03:55 -05:00
paradajz
39161fdf47 Playlist view: optionally show comment column (#2073)
* playlist view: optionally show genre and comment columns

* Remove genre from Playlist columns, as it is not a valid attribute of playlist

Co-authored-by: Deluan <deluan@navidrome.org>
2023-01-24 21:15:41 -05:00
selfhoster1312
1e24809ed6 Create accounts automatically when authenticating from HTTP header (#2087)
* Create accounts automatically when authenticating from HTTP header

* Disable password check when header auth is enabled

* Formatting

* Password change is valid when no password (old or new) is provided

* Test suite runs with header auth disabled (mock config)
Prevents nil pointer access (panic) while testing password validating logic

* Use a constant prefix for autogenerated passwords (header auth case)

* Add tests

* Add context to log messages

Co-authored-by: Deluan <deluan@navidrome.org>
2023-01-24 20:18:10 -05:00
Deluan
9721ef8974 Fix download translation key 2023-01-24 20:14:51 -05:00
Deluan
16850a9be0 Revert "Replace the LoveButton with ArtistContextMenu in the artist page - #1979"
see https://github.com/navidrome/navidrome/issues/1979#issuecomment-1402904870
2023-01-24 20:14:51 -05:00
Aleksey Lobanov
457e1fc97b Base SQL metrics in MetricsWorker (#2002)
* feat: Add metrics worker

* refactor: Add todos for useful for metrics methods

* feat: Run MetricsWorker is Prometheus is Enabled

* refactor: Unused low-level variable was removed in metrics

* feat: No worker for metrics, add more

* refactor: Unnecessary todo removed

* refactor: Remove dead unused constant

* Reduce metrics public interface

Co-authored-by: Deluan <deluan@navidrome.org>
2023-01-24 19:26:07 -05:00
Deluan
d31faf5249 Bump github.com/onsi/gomega from 1.25.0 to 1.26.0 2023-01-24 19:04:33 -05:00
Deluan
2082948144 Fix downloadOriginalFormat term in English translation 2023-01-24 18:41:43 -05:00
Deluan
39dc9c4310 Disable Subsonic Share endpoints if feature is disabled 2023-01-24 18:36:47 -05:00
Deluan
0c263cf234 Make AlbumSongs BulkActionsToolbar more responsive 2023-01-24 18:36:47 -05:00
Deluan
85084cda57 Add button to share selected songs 2023-01-24 18:36:47 -05:00
Deluan
69b36c75a5 Add meta tags to show cover and share description in social platforms 2023-01-24 18:36:47 -05:00
Deluan
cab43c89e6 Mark Share.LastVisited optional in Subsonic API 2023-01-24 18:36:47 -05:00
Deluan
433da37982 Add Share to Context menus, also share artist 2023-01-24 18:36:47 -05:00
Deluan
051e9c556d Use redux for ShareDialog 2023-01-24 18:36:47 -05:00
Deluan
17d9573f4d Refactor dialogs, make it simple to add a new dialog to all views 2023-01-24 18:36:47 -05:00
Deluan
26be5b8396 Keep order of shared mediafiles 2023-01-24 18:36:47 -05:00
Deluan
c770229154 Add Share capability to Subsonic user's info 2023-01-24 18:36:47 -05:00
Deluan
ef4765c768 Fix getShares sort order 2023-01-24 18:36:47 -05:00
Deluan
6c05fcb699 Create contents label for group of shared mediafiles 2023-01-24 18:36:47 -05:00
Deluan
63e67bd502 Make Share list responsive 2023-01-24 18:36:47 -05:00
Deluan
230f2fdc02 Reduce spacing between album buttons, to avoid breaking the toolbar in two 2023-01-24 18:36:47 -05:00
Deluan
d639da9eb5 Enable sharing only selected songs with the Subsonic API 2023-01-24 18:36:47 -05:00
Deluan
e34f26588e Fix empty entry collection in Shares 2023-01-24 18:36:47 -05:00
Deluan
c994ed70ea Fix expireAt update error 2023-01-24 18:36:46 -05:00
Deluan
40cac5c367 Fix JS console warning 2023-01-24 18:36:46 -05:00
Deluan
34277f238c Make Share icon dynamic 2023-01-24 18:36:46 -05:00
Deluan
dbf80d8592 Change public/share path to /share - DSub does not use the URL from the API response... :( 2023-01-24 18:36:46 -05:00
Deluan
d5df102f9f Implement updateShare and deleteShare Subsonic endpoints 2023-01-24 18:36:46 -05:00
Deluan
20271df4fb Workaround to detect empty dates in some Subsonic clients 2023-01-24 18:36:46 -05:00
Deluan
d4c1d2ece4 Handle expired shares 2023-01-24 18:36:46 -05:00
Deluan
d0dceae094 Add getShares and createShare Subsonic endpoints 2023-01-24 18:36:46 -05:00
Deluan
94cc2b2ac5 Fix tests and lint errors, plus a bit of refactor 2023-01-24 18:36:46 -05:00
Deluan
72a12e344e More share translations 2023-01-24 18:36:46 -05:00
Deluan
12bb6c3847 Don't expose empty dates in share info 2023-01-24 18:36:46 -05:00
Deluan
58fc271864 Share playlists 2023-01-24 18:36:46 -05:00
Deluan
65174d3fb2 Refactor DownloadMenuDialog to use useTranscodingOptions hook 2023-01-24 18:36:46 -05:00
Deluan
c8293fcdd8 Extract transcoding options to its own hook 2023-01-24 18:36:46 -05:00
Deluan
d9c42b3183 Add share's contents and description to the DB 2023-01-24 18:36:46 -05:00
Deluan
364fdfbd8d Use defaultDownsamplingFormat in share options 2023-01-24 18:36:45 -05:00
Deluan
63b4a12a93 Fine tune SharePlayer 2023-01-24 18:36:45 -05:00
Deluan
357c0e1e19 Refactor URL builders in UI 2023-01-24 18:36:45 -05:00
Deluan
84aa094e56 More work on Shares 2023-01-24 18:36:45 -05:00
Deluan
ab04e33da6 Initial work on Shares 2023-01-24 18:36:45 -05:00
Kendall Garner
5331de17c2 Fixes the slide bar clickable area (#2113) 2023-01-24 11:15:14 -05:00
dependabot[bot]
199f66b8de Bump @testing-library/react from 12.1.2 to 12.1.5 in /ui (#2109)
Bumps [@testing-library/react](https://github.com/testing-library/react-testing-library) from 12.1.2 to 12.1.5.
- [Release notes](https://github.com/testing-library/react-testing-library/releases)
- [Changelog](https://github.com/testing-library/react-testing-library/blob/main/CHANGELOG.md)
- [Commits](https://github.com/testing-library/react-testing-library/compare/v12.1.2...v12.1.5)

---
updated-dependencies:
- dependency-name: "@testing-library/react"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-23 12:19:47 -05:00
dependabot[bot]
535171faf8 Bump github.com/onsi/gomega from 1.24.2 to 1.25.0 (#2111)
Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.24.2 to 1.25.0.
- [Release notes](https://github.com/onsi/gomega/releases)
- [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/gomega/compare/v1.24.2...v1.25.0)

---
updated-dependencies:
- dependency-name: github.com/onsi/gomega
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-23 12:19:21 -05:00
dependabot[bot]
bee39ad28e Bump github.com/spf13/viper from 1.14.0 to 1.15.0 (#2110)
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.14.0 to 1.15.0.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.14.0...v1.15.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/viper
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-23 12:18:51 -05:00
Kendall Garner
2de570fe72 Fix order of gain menu options (#2105) 2023-01-22 11:08:54 -05:00
Deluan
33f033beba Fix artist image not caching on browser 2023-01-20 21:28:44 -05:00
Deluan
b9934799ec Increase size of artist image 2023-01-20 20:55:17 -05:00
Deluan
adea15ab93 Use constant 2023-01-20 16:01:16 -05:00
Corrado Primier
0c27e7a43b Fix Illumos build - #2067 (#2069)
Build currently fails on Illumos with error `Undefined symbol sendfile`. Fix it by linking `sendfile` explicitly.
2023-01-19 12:52:01 -05:00
Deluan
8956f5e7fd Fix Album.MaxYear calculation 2023-01-19 09:34:58 -05:00
Deluan
7073d18b54 Make private methods unpublished 2023-01-19 09:34:39 -05:00
Deluan
7fc964aec5 Don't wake CacheWarmer every 10 seconds, let it sleep :) 2023-01-18 19:31:15 -05:00
Deluan
136d5f9a83 Add config option to show album participations under artists in Subsonic clients 2023-01-18 14:20:06 -05:00
vlfldr
8ae0bcb459 Add Gruvbox Dark color theme (#2092)
* Added Gruvbox Dark color theme

* Correct formatting by running prettier
2023-01-18 13:23:36 -05:00
Deluan
127c75e34b Don't try to downsample if requested bitrate is equal or greater than original. Fix #2066 2023-01-18 13:20:51 -05:00
Deluan
d5c9cf07bd Fix Playlist show 2023-01-18 09:43:07 -05:00
Deluan
701e301d48 Increase timeout for obtaining login background image list 2023-01-17 22:57:14 -05:00
Deluan
580e9ae4bd Fix timer going awry 2023-01-17 22:04:09 -05:00
Zane van Iperen
feb774a149 Change genre.Put() to upsert. Fix #1918 and #1564 (#1920)
* persistence/genre: change Put() to upsert

Absolutely disgusting hack to work around [1]. Try to insert the genre,
but if it conflicts, ignore it and update the genre with the existing
ID.

[1]: https://github.com/navidrome/navidrome/issues/1918.

* scanner: remove cached genre repository

Not needed anytmore. And remember:

  "Many Small Queries Are Efficient In SQLite" [1].

[1]: https://www.sqlite.org/np1queryprob.html

* Revert "scanner: remove cached genre repository"

This reverts commit c5d900aa43.

* Use squirrel to build SQL, to reduce risk of SQL injection

Co-authored-by: Deluan <deluan@navidrome.org>
2023-01-17 21:04:18 -05:00
Deluan
17eab6a88d Fix resized image cache key 2023-01-17 20:58:38 -05:00
Deluan
bedd2b2074 Implement better artwork cache keys 2023-01-17 20:37:10 -05:00
Kendall Garner
93adda66d9 Get album info (when available) from Last.fm, add getAlbumInfo endpoint (#2061)
* lastfm album.getInfo, getAlbuminfo(2) endpoints

* ... for description and reduce not found log level

* address first comments

* return all images

* Update migration timestamp

* Handle a few edge cases

* Add CoverArtPriority option to retrieve albumart from external sources

* Make agents methods more descriptive

* Use Last.fm name consistently

Co-authored-by: Deluan <deluan@navidrome.org>
2023-01-17 20:22:54 -05:00
Deluan
5564f00838 Some refactor, log message changes 2023-01-17 17:26:48 -05:00
Kendall Garner
1324a16fc5 ReplayGain support + audio normalization (web player) (#1988)
* ReplayGain support

- extract ReplayGain tags from files, expose via native api
- use metadata to normalize audio in web player

* make pre-push happy

* remove unnecessary prints

* remove another unnecessary print

* add tooltips, see metadata

* address comments, use settings instead

* remove console.log

* use better language for gain modes
2023-01-17 15:57:19 -05:00
Deluan
9ae156dd82 Remove unused prop 2023-01-17 14:31:17 -05:00
Deluan
438d45c176 Change Internet Radio UX 2023-01-17 14:22:10 -05:00
Deluan
e76080809d Fix pipeline lint error help message 2023-01-17 11:02:07 -05:00
Deluan
0a65bf171b Change Players icon, to distinguish it from Internet Radios 2023-01-16 20:51:18 -05:00
Deluan
e40da183bb Move artwork id encoding to public package 2023-01-16 15:24:25 -05:00
Deluan
13ba08157a Add Size column to Album Songs view 2023-01-16 15:13:05 -05:00
Deluan
7682fddec0 Add Size column to Artist and Album views 2023-01-16 15:00:50 -05:00
Deluan
4a054de3d5 Hide togglable columns when in Album Grid view mode. Fixes #2064 2023-01-16 15:00:33 -05:00
dependabot[bot]
b6233e57b3 Bump @material-ui/styles from 4.11.4 to 4.11.5 in /ui (#2093)
Bumps [@material-ui/styles](https://github.com/mui-org/material-ui/tree/HEAD/packages/material-ui-styles) from 4.11.4 to 4.11.5.
- [Release notes](https://github.com/mui-org/material-ui/releases)
- [Commits](https://github.com/mui-org/material-ui/commits/HEAD/packages/material-ui-styles)

---
updated-dependencies:
- dependency-name: "@material-ui/styles"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-16 12:17:12 -05:00
dependabot[bot]
c00040d94e Bump github.com/dustin/go-humanize from 1.0.0 to 1.0.1 (#2094)
Bumps [github.com/dustin/go-humanize](https://github.com/dustin/go-humanize) from 1.0.0 to 1.0.1.
- [Release notes](https://github.com/dustin/go-humanize/releases)
- [Commits](https://github.com/dustin/go-humanize/compare/v1.0.0...v1.0.1)

---
updated-dependencies:
- dependency-name: github.com/dustin/go-humanize
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-16 12:16:39 -05:00
Deluan
c748d669d6 Sort radio stations by name 2023-01-15 16:12:22 -05:00
Deluan
d319b66ff3 Make Radio Create and Edit forms consistent 2023-01-15 15:43:46 -05:00
Deluan
a8478ca74c Fix Subsonic XML Internet Radio response 2023-01-15 15:38:38 -05:00
Kendall Garner
8877b1695a Add Internet Radio support (#2063)
* add internet radio support

* Add dynamic sidebar icon to Radios

* Fix typos

* Make URL suffix consistent

* Fix typo

* address feedback

* Don't need to preload when playing Internet Radios

* Reorder migration, or else it won't be applied

* Make Radio list view responsive

Also added filter by name, removed RadioActions and RadioContextMenu, and added a default radio icon, in case of favicon is not available.

* Simplify StreamField usage

* fix button, hide progress on mobile

* use js styles over index.css

Co-authored-by: Deluan <deluan@navidrome.org>
2023-01-15 15:11:37 -05:00
Gil Desmarais
aa21a2a305 Respect prefers-reduced-motion browser configuration (#2090)
Signed-off-by: Gil Desmarais <git@desmarais.de>

Signed-off-by: Gil Desmarais <git@desmarais.de>
2023-01-14 18:42:23 -05:00
Deluan
e3496c7eea Fix artist folder detection. Now works when the artist has only one album. 2023-01-14 14:36:27 -05:00
Deluan
d3e4a5287d "Touch" playlists to force some clients to reload cover art 2023-01-14 12:21:31 -05:00
Deluan
12dd219e16 Don't refresh artistInfo when setting artist's love/rating 2023-01-14 10:52:03 -05:00
bornav
1d6b04e3ad Replace the LoveButton with ArtistContextMenu in the artist page - #1979 2023-01-14 10:52:03 -05:00
Deluan
dfbf86c577 Allow any HTTP methods for public images endpoint. Fix artist covers in Subtracks 2023-01-14 10:17:21 -05:00
Deluan
16c869ec86 Optimize playlist cover generation 2023-01-13 22:18:34 -05:00
Deluan
c46a2a5f5f New dev options to control getCoverArt throttling 2023-01-13 22:18:34 -05:00
Deluan
ab7668f562 Use a custom artist image cache key.
Invalidate when `Agents` config changes. This should solve https://github.com/navidrome/navidrome/issues/1601#issuecomment-1241702797
2023-01-13 22:18:34 -05:00
Deluan
94c6d47181 More descriptive error when artist.jpg not found 2023-01-13 22:18:34 -05:00
Deluan
0ffef05cc3 Remove "Biography not available" when agents are not available 2023-01-13 22:18:34 -05:00
Deluan
3f2d24695e PreCache artist images 2023-01-13 22:18:34 -05:00
Deluan
cbe3adf987 Don't show error when it is nil 2023-01-13 22:18:34 -05:00
Deluan
c90468b895 Find artist.* image in Artist folder 2023-01-13 22:18:34 -05:00
Deluan
69e0a266f4 Remove size from public image ID JWT 2023-01-13 22:18:34 -05:00
Deluan
8f0d002922 Add local TopSongs 2023-01-13 22:18:34 -05:00
Deluan
77a99a735b Always access artist images through Navidrome (proxy calls to external URLs) 2023-01-13 22:18:34 -05:00
Deluan
918fee3ea3 Artwork reader for Artist 2023-01-13 22:18:34 -05:00
Deluan
bf461473ef Add local agent, only for images 2023-01-13 22:18:34 -05:00
Deluan
387acc5f63 Add public endpoint to expose images 2023-01-13 22:18:34 -05:00
Deluan
7fbcb2904a Add function number.RandomInt64 2023-01-13 21:40:24 -05:00
Deluan
7a617d3a1d Remove unused "embed" build tag 2023-01-13 21:35:54 -05:00
Deluan
769e8bedba Rename WeightedChooser's method Put to Add, a better name 2023-01-13 19:43:27 -05:00
Deluan
291455f0b7 Fix Download Dialog not showing in Artist page 2023-01-13 19:40:43 -05:00
Deluan
b1b081e3d8 Move react-scripts to devDependencies 2023-01-13 09:33:10 -05:00
dependabot[bot]
9ea9b48891 Bump golang.org/x/tools from 0.4.0 to 0.5.0
Bumps [golang.org/x/tools](https://github.com/golang/tools) from 0.4.0 to 0.5.0.
- [Release notes](https://github.com/golang/tools/releases)
- [Commits](https://github.com/golang/tools/compare/v0.4.0...v0.5.0)

---
updated-dependencies:
- dependency-name: golang.org/x/tools
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-13 09:15:58 -05:00
dependabot[bot]
e6e9260648 Bump decode-uri-component from 0.2.0 to 0.2.2 in /ui
Bumps [decode-uri-component](https://github.com/SamVerschueren/decode-uri-component) from 0.2.0 to 0.2.2.
- [Release notes](https://github.com/SamVerschueren/decode-uri-component/releases)
- [Commits](https://github.com/SamVerschueren/decode-uri-component/compare/v0.2.0...v0.2.2)

---
updated-dependencies:
- dependency-name: decode-uri-component
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-13 09:15:30 -05:00
dependabot[bot]
224e3b3089 Bump json5 from 1.0.1 to 1.0.2 in /ui
Bumps [json5](https://github.com/json5/json5) from 1.0.1 to 1.0.2.
- [Release notes](https://github.com/json5/json5/releases)
- [Changelog](https://github.com/json5/json5/blob/main/CHANGELOG.md)
- [Commits](https://github.com/json5/json5/compare/v1.0.1...v1.0.2)

---
updated-dependencies:
- dependency-name: json5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-13 09:14:55 -05:00
dependabot[bot]
023e103720 Bump prettier from 2.4.1 to 2.8.2 in /ui
Bumps [prettier](https://github.com/prettier/prettier) from 2.4.1 to 2.8.2.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/2.4.1...2.8.2)

---
updated-dependencies:
- dependency-name: prettier
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-13 09:14:10 -05:00
dependabot[bot]
53ef50d980 Bump golang.org/x/text from 0.5.0 to 0.6.0
Bumps [golang.org/x/text](https://github.com/golang/text) from 0.5.0 to 0.6.0.
- [Release notes](https://github.com/golang/text/releases)
- [Commits](https://github.com/golang/text/compare/v0.5.0...v0.6.0)

---
updated-dependencies:
- dependency-name: golang.org/x/text
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-13 09:11:48 -05:00
Deluan
feabcdfe9f Show help message when goimports/go mod tidy breaks the build 2023-01-13 08:58:41 -05:00
Deluan
1374dab087 Disable buildvcs flag 2023-01-12 22:18:50 -05:00
dependabot[bot]
18aac7c729 Bump github.com/onsi/ginkgo/v2 from 2.6.1 to 2.7.0
Bumps [github.com/onsi/ginkgo/v2](https://github.com/onsi/ginkgo) from 2.6.1 to 2.7.0.
- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v2.6.1...v2.7.0)

---
updated-dependencies:
- dependency-name: github.com/onsi/ginkgo/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-12 21:33:06 -05:00
dependabot[bot]
c8ecf3b495 Bump github.com/go-chi/httprate from 0.7.0 to 0.7.1
Bumps [github.com/go-chi/httprate](https://github.com/go-chi/httprate) from 0.7.0 to 0.7.1.
- [Release notes](https://github.com/go-chi/httprate/releases)
- [Commits](https://github.com/go-chi/httprate/compare/v0.7.0...v0.7.1)

---
updated-dependencies:
- dependency-name: github.com/go-chi/httprate
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-12 21:32:34 -05:00
Deluan
7e03f8ca82 Upgrade to Go 1.19.5 2023-01-12 21:20:45 -05:00
Deluan
fdbece5c92 Use custom sanitize package, fix #2070 2023-01-12 13:39:05 -05:00
Deluan
df0f140f9f Don't refresh smart playlists when generating covers 2023-01-01 20:28:03 -05:00
Deluan
950cc28e67 Add coverArt to Subsonic playlist response 2023-01-01 19:35:19 -05:00
Deluan
6260927074 Serve artist placeholder directly, instead of using LastFM's CDN 2022-12-30 20:14:03 -05:00
Celyn Walters
b8c171d3d4 Hide LastFM icons if config.lastFMEnabled is false (#1935)
Hide LastFM icons if `config.lastFMEnabled` is false
2022-12-30 17:15:14 -05:00
Deluan
80ded63d35 Add test for mapTrackTitle 2022-12-30 15:13:04 -05:00
Deluan
cc14485194 When trying to PreCache, wait for ImageCache to be available 2022-12-28 23:26:39 -05:00
Deluan
0c7c6ba020 PreCache Playlists CoverArt 2022-12-28 15:31:56 -05:00
Deluan
14032a524b Reduce retention in CacheWarmer 2022-12-28 15:31:56 -05:00
Deluan
61e5523457 Handle "naked" CoverArtIDs (IDs of album, mediafiles and playlists) 2022-12-28 15:31:56 -05:00
Deluan
bc09de6640 Better error handling 2022-12-28 15:31:56 -05:00
Deluan
949331ed24 GetCoverArt generates a tiled (2x2) image for playlists 2022-12-28 15:31:56 -05:00
Deluan
501386b11f Parse correctly playlist CoverArt ids 2022-12-28 15:31:56 -05:00
Deluan
8f3387a894 Fix tests and clean up code a bit 2022-12-28 15:31:56 -05:00
Deluan
332900774d Rename DevFastAccessCoverArt to EnableMediaFileCoverArt 2022-12-28 15:31:56 -05:00
Deluan
722a00cacf Fix artwork caching 2022-12-28 15:31:56 -05:00
Deluan
92ddae4a65 Created dedicated artwork readers 2022-12-28 15:31:56 -05:00
Deluan
c1c4645501 Move artwork handling to its own package 2022-12-28 15:31:56 -05:00
Deluan
8cf78efb9c Add timeout for artwork extraction 2022-12-28 15:31:56 -05:00
Deluan
52a4721c91 Remove empty (invalid) entries from the cache 2022-12-28 15:31:56 -05:00
Deluan
e89d99aee0 Also caches resized images 2022-12-28 15:31:56 -05:00
Deluan
dc16ccdb93 Make tests compatible with GoLang 1.18 2022-12-28 15:31:56 -05:00
Deluan
b6eb60f019 Add new Artwork Cache Warmer 2022-12-28 15:31:56 -05:00
Deluan
8c1cd9c273 Refactor file type functions 2022-12-28 15:31:56 -05:00
Deluan
9ec349dce0 Make sure album is updated if external cover changes 2022-12-28 15:31:56 -05:00
Deluan
f5719a7571 Fix spaces in CoverArtPriority, more trace logs in artwork resolution 2022-12-28 15:31:56 -05:00
Deluan
3dbd5c8d31 Remove unnecessary cache invalidator, as ID nows contains the updatedAt value 2022-12-28 15:31:56 -05:00
Deluan
73bb0104f0 Cache original images 2022-12-28 15:31:56 -05:00
Deluan
26a7adae5f Change Image cache key format 2022-12-28 15:31:56 -05:00
Deluan
04eab5666a Add back CoverArtPriority 2022-12-28 15:31:56 -05:00
Deluan
045b023b35 Fix DevFastAccessCoverArt flag 2022-12-28 15:31:56 -05:00
Deluan
57c3334ea0 Remove unused DevPreCacheAlbumArtwork config option 2022-12-28 15:31:56 -05:00
Deluan
847a0432ea If resize fails, send the artwork as is. Closes #1102 2022-12-28 15:31:56 -05:00
Deluan
8e640bb858 Implement new Artist refresh 2022-12-28 15:31:56 -05:00
Deluan
bce7b163ba Skip trying to read cover art from mediafile if it does not have one 2022-12-28 15:31:56 -05:00
Deluan
2923f01cd9 Fix UI artwork id creation 2022-12-28 15:31:56 -05:00
Deluan
a087f57d2d Handle request (context) cancellation 2022-12-28 15:31:56 -05:00
Deluan
9fcd1c9354 Make internal method unexported 2022-12-28 15:31:56 -05:00
Deluan
2814c818bd go mod tidy 2022-12-28 15:31:56 -05:00
Deluan
73719c3abd Fix cover detection on M4A containers 2022-12-28 15:31:56 -05:00
Deluan
e0da1d1589 Log artwork origin (tag, file, etc...) 2022-12-28 15:31:56 -05:00
Deluan
92b42b35b3 Fallback extracting tags using ffmpeg 2022-12-28 15:31:56 -05:00
Deluan
abd3274250 Handle empty cover art ID in subsonic API 2022-12-28 15:31:56 -05:00
Deluan
0da27e8a3f Add image cache back 2022-12-28 15:31:56 -05:00
Deluan
40bb211b39 Small test refactor 2022-12-28 15:31:56 -05:00
Deluan
87d4db7638 Handle mediafile covers 2022-12-28 15:31:56 -05:00
Deluan
213ceeca78 Resize if requested 2022-12-28 15:31:56 -05:00
Deluan
7b87386089 Load artwork from embedded 2022-12-28 15:31:56 -05:00
Deluan
c36e77d41f Remove CoverArtID, fix tests 2022-12-28 15:31:56 -05:00
Deluan
38bde0ddba Remove current Image Cache implementation 2022-12-28 15:31:56 -05:00
Deluan
c430401ea9 Remove current artwork implementation 2022-12-28 15:31:56 -05:00
Deluan
0130c6dc13 Add all images found for each album in the database 2022-12-28 15:31:56 -05:00
Deluan
2f90fc9bd4 Move album refresh to scanner 2022-12-28 15:31:56 -05:00
Deluan
566ae93950 Remove old refresh code 2022-12-28 15:31:56 -05:00
Deluan
83ff44f5f4 Move cover art discovery (temporarily) to model 2022-12-28 15:31:56 -05:00
Deluan
28e7371d93 Moved logic of collapsing songs into albums to model package
(it should really be called domain.... maybe will rename it later)
2022-12-28 15:31:56 -05:00
Deluan
e03ccb3166 Replace MinInt/MaxInt with generic versions 2022-12-28 15:31:56 -05:00
Deluan
6f5aaa1ec4 Move alternative tag names mapping to metadata 2022-12-28 15:31:56 -05:00
Deluan
0c22af3585 Invert dependency of metadata and extractors 2022-12-28 15:31:56 -05:00
Kendall Garner
55b0227494 Add Date Added column in Album and Song lists (#2055) 2022-12-22 22:44:07 -05:00
Deluan
db6e8e45b7 Fix build badge: https://github.com/badges/shields/issues/8671 2022-12-21 18:41:22 -05:00
Deluan
5943e8f953 Rename log.LevelCritical to log.LevelFatal 2022-12-21 14:53:36 -05:00
Deluan
28389fb05e Add command line M3U exporter. Closes #1914 2022-12-21 14:39:40 -05:00
Deluan
5d8318f7b3 Change "Go to current song" hotkey.
It was blocking Cmd-C (copy on macOS)
2022-12-18 20:58:01 -05:00
dependabot[bot]
75596a6b64 Bump github.com/onsi/gomega from 1.24.1 to 1.24.2 (#2048)
Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.24.1 to 1.24.2.
- [Release notes](https://github.com/onsi/gomega/releases)
- [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/gomega/compare/v1.24.1...v1.24.2)

---
updated-dependencies:
- dependency-name: github.com/onsi/gomega
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-18 12:41:42 -05:00
dependabot[bot]
a9ddb2db6b Bump github.com/beego/beego/v2 from 2.0.6 to 2.0.7 (#2047)
Bumps [github.com/beego/beego/v2](https://github.com/beego/beego) from 2.0.6 to 2.0.7.
- [Release notes](https://github.com/beego/beego/releases)
- [Changelog](https://github.com/beego/beego/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/beego/beego/compare/v2.0.6...v2.0.7)

---
updated-dependencies:
- dependency-name: github.com/beego/beego/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-18 12:38:55 -05:00
dependabot[bot]
fe1a6a7dd5 Bump github.com/onsi/ginkgo/v2 from 2.5.1 to 2.6.1 (#2046)
Bumps [github.com/onsi/ginkgo/v2](https://github.com/onsi/ginkgo) from 2.5.1 to 2.6.1.
- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v2.5.1...v2.6.1)

---
updated-dependencies:
- dependency-name: github.com/onsi/ginkgo/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-18 12:38:33 -05:00
dependabot[bot]
9cb1fc4fa1 Bump github.com/go-chi/chi/v5 from 5.0.7 to 5.0.8 (#2040)
Bumps [github.com/go-chi/chi/v5](https://github.com/go-chi/chi) from 5.0.7 to 5.0.8.
- [Release notes](https://github.com/go-chi/chi/releases)
- [Changelog](https://github.com/go-chi/chi/blob/master/CHANGELOG.md)
- [Commits](https://github.com/go-chi/chi/compare/v5.0.7...v5.0.8)

---
updated-dependencies:
- dependency-name: github.com/go-chi/chi/v5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-18 12:38:20 -05:00
Deluan Quintão
24d520882e Don't cache transcoded files if the request was cancelled (#2041)
* Don't cache transcoded files if the request was cancelled (or there was a transcoding error)

* Add context to logs

* Simplify Wait error handling

* Fix flaky test

* Change log level for "populating cache" error message

* Small cleanups
2022-12-18 12:22:12 -05:00
Kendall Garner
54395e7e6a Enable transcoding of downlods (#1667)
* feat(download): Enable transcoding of downlods - #573

Signed-off-by: Kendall Garner <17521368+kgarner7@users.noreply.github.com>

* feat(download): Make automatic transcoding of downloads optional

Signed-off-by: Kendall Garner <17521368+kgarner7@users.noreply.github.com>

* Fix spelling

* address changes

* prettier

* fix config

* use previous name

Signed-off-by: Kendall Garner <17521368+kgarner7@users.noreply.github.com>
2022-12-18 12:12:37 -05:00
Deluan
6489dd4478 Fix overriding previous logger in context 2022-12-14 11:50:16 -05:00
Deluan
6c4a0be6ff Add endpoints in Subsonic API logs 2022-12-14 10:52:46 -05:00
Deluan
982b604500 Add username to authenticated log messages 2022-12-14 09:35:30 -05:00
Deluan
f206d81afd Some cleanup, fixes typos and grammar errors 2022-12-06 20:09:03 -05:00
Deluan
c5f7cf97f4 Some cleanup, adding missing context handling 2022-12-06 19:57:47 -05:00
gauth-fr
55ba39cb79 Add global Downsampling feature (#1575)
* Add global downsampling feature

* Default to Opus &  consider player transcoder

* Add a test case for DefaultDownsamplingFormat

Co-authored-by: Deluan <deluan@navidrome.org>
2022-12-06 19:41:16 -05:00
Deluan
0cc1db54d4 Bump github.com/bradleyjkemp/cupaloy to v2.8.0 2022-12-05 22:45:02 -05:00
Deluan
879992eb33 Change "current song" hotkey English label 2022-12-05 13:50:19 -05:00
Robert Sammelson
b5b01f78db Keyboard shortcut to go to current song (#2029)
* feat(hotkeys): keyboard-shortcut-for-current-song - #1336

Signed-off-by: Pavithra Nair <pmpavithranair@gmail.com>

* Fix previously mentioned bugs

Signed-off-by: Pavithra Nair <pmpavithranair@gmail.com>
Co-authored-by: Pavithra Nair <pmpavithranair@gmail.com>
2022-12-05 13:37:49 -05:00
dependabot[bot]
cdddd4ce30 Bump golang.org/x/text from 0.4.0 to 0.5.0 (#2030)
Bumps [golang.org/x/text](https://github.com/golang/text) from 0.4.0 to 0.5.0.
- [Release notes](https://github.com/golang/text/releases)
- [Commits](https://github.com/golang/text/compare/v0.4.0...v0.5.0)

---
updated-dependencies:
- dependency-name: golang.org/x/text
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-05 13:24:42 -05:00
Reo
4489c34757 Fix Misleading Error Message on unreadable Media due to Permission (#1873)
* fix(taglib): Fix misleading error message on unreadable media - #1576

Signed-off-by: reo <reo_999@proton.me>

* fix(taglib): Add unit test and exclude scan for only unreadable file - #1576

Signed-off-by: reo <reo_999@proton.me>

* fix(taglib): Add unit test and exclude scan for only unreadable file - #1576

Signed-off-by: reo <reo_999@proton.me>

* fix(taglib): Add unit test and exclude scan for only unreadable file - #1576

Signed-off-by: reo <reo_999@proton.me>

* fix(taglib): Add unit test and exclude scan for only unreadable file - #1576

Signed-off-by: reo <reo_999@proton.me>

* fix(taglib): Add unit test and exclude scan for only unreadable file - #1576

Signed-off-by: reo <reo_999@proton.me>

* Fix test and simplify code a bit

We don't need to expose the type of error: `taglib.Parse()` always return nil

* Fix comment

Signed-off-by: reo <reo_999@proton.me>
Co-authored-by: Deluan <deluan@navidrome.org>
2022-12-04 12:48:21 -05:00
Deluan
51b67d18d3 Increase number of "Shuffle All" songs 2022-12-03 20:54:23 -05:00
Robert Sammelson
c4d1569441 Fix bug in duration format logic (#2026) 2022-12-03 20:31:02 -05:00
Deluan
68ceeb9ea1 Fix build for non-unix 2022-12-03 10:42:36 -05:00
Deluan
4549b91ae0 Fix build for non-unix 2022-12-02 20:39:44 -05:00
Deluan
9ffd145e82 Add log for signal received 2022-12-02 20:30:30 -05:00
dependabot[bot]
5713010984 Bump github.com/spf13/viper from 1.13.0 to 1.14.0 (#2019)
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.13.0 to 1.14.0.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.13.0...v1.14.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/viper
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-02 18:28:43 -05:00
Deluan
00c6545cb1 Bump github.com/go-chi/jwtauth/v5 from 5.0.2 to 5.1.0 2022-12-02 17:58:53 -05:00
dependabot[bot]
3f45a4ed98 Bump github.com/beego/beego/v2 from 2.0.5 to 2.0.6 (#2016)
Bumps [github.com/beego/beego/v2](https://github.com/beego/beego) from 2.0.5 to 2.0.6.
- [Release notes](https://github.com/beego/beego/releases)
- [Changelog](https://github.com/beego/beego/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/beego/beego/compare/v2.0.5...v2.0.6)

---
updated-dependencies:
- dependency-name: github.com/beego/beego/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-02 17:40:08 -05:00
dependabot[bot]
46c09e4b11 Bump github.com/prometheus/client_golang from 1.13.0 to 1.14.0 (#2018)
Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.13.0 to 1.14.0.
- [Release notes](https://github.com/prometheus/client_golang/releases)
- [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prometheus/client_golang/compare/v1.13.0...v1.14.0)

---
updated-dependencies:
- dependency-name: github.com/prometheus/client_golang
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-02 17:39:52 -05:00
Deluan
40395f47f0 Use forked react-player. May fix issue #1472 2022-12-02 17:20:16 -05:00
Deluan
2c214154dc Add nakedret linter 2022-11-30 14:16:30 -05:00
Deluan
03640ca93d Fix background images when BaseURL is specified 2022-11-29 14:48:05 -05:00
Deluan
d8c5944ef1 Fix race condition in scanner 2022-11-29 11:08:47 -05:00
Deluan
10cd3152ba Remove misplaced import 2022-11-27 22:01:07 -05:00
Deluan
950b5dc1ce Remove math/rand and only use crypto/rand 2022-11-27 21:53:13 -05:00
Deluan
195f39182d Host default login background images in Navidrome's own website 2022-11-27 21:37:33 -05:00
Deluan Quintão
334ccac643 Spotify-ish Improvement (#2012)
* spotify-improvement

* fixing the issue of applying styles to filter fields too

* Remove scrollbar styling.

Maybe we should simulate macOS's scrollbar behaviour, with something like this: https://gist.github.com/spemer/a0e218bbb45433bd611e68446523a00b

Co-authored-by: Rishabh Malhotra <rishabhmalhotraa01@gmail.com>
2022-11-27 12:13:00 -05:00
Garvit Galgat
676de79fb3 Don't abort scan if all audio files are in the MediaFolder's root. Fix #868 (#893)
* fixed #868

* Make sure we only abort scanning if it is not a fullScan

Co-authored-by: Deluan <deluan@navidrome.org>
2022-11-27 11:45:37 -05:00
Raghd Hamzeh
d5fe0f214c fix: send content type header in listenbrainz requests - #1944 (#1994)
fixes #1944

Signed-off-by: Raghd Hamzeh <raghd@rhamzeh.com>

Signed-off-by: Raghd Hamzeh <raghd@rhamzeh.com>
2022-11-27 09:47:13 -05:00
Deluan
6ae6e023ea Bump some NPM dependencies 2022-11-27 09:28:47 -05:00
Deluan
7bafbce816 Reduce number of goroutines in test, to avoid hitting the hard limit of 8128 2022-11-26 15:28:30 -05:00
Deluan
a69a31a3bf Use custom atomic.Bool, as it is not supported in Go 1.18 2022-11-26 15:14:19 -05:00
Deluan
88823fca76 Fix race conditions in tests 2022-11-26 15:07:53 -05:00
Deluan
0bb133a6ac Kill ffmpeg if context is cancelled 2022-11-26 15:06:59 -05:00
Deluan Quintão
76a94ecb70 Update GH actions
* Update GH actions

* Fix

* Fix "Cannot open: File exists" messages
2022-11-26 14:11:39 -05:00
Deluan
1b5f855bff Compress more http content-types.
Also, some minor refactoring
2022-11-26 13:13:05 -05:00
Zane van Iperen
472f99b2b5 Add AAC default transcoding (#2010) 2022-11-23 10:20:40 -05:00
dependabot[bot]
4d660a2ba7 Bump github.com/golangci/golangci-lint from 1.49.0 to 1.50.1 (#1954)
Bumps [github.com/golangci/golangci-lint](https://github.com/golangci/golangci-lint) from 1.49.0 to 1.50.1.
- [Release notes](https://github.com/golangci/golangci-lint/releases)
- [Changelog](https://github.com/golangci/golangci-lint/blob/master/CHANGELOG.md)
- [Commits](https://github.com/golangci/golangci-lint/compare/v1.49.0...v1.50.1)

---
updated-dependencies:
- dependency-name: github.com/golangci/golangci-lint
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-22 10:57:27 -05:00
dependabot[bot]
398101896f Bump golang.org/x/tools from 0.1.12 to 0.3.0 (#1991)
Bumps [golang.org/x/tools](https://github.com/golang/tools) from 0.1.12 to 0.3.0.
- [Release notes](https://github.com/golang/tools/releases)
- [Commits](https://github.com/golang/tools/compare/v0.1.12...v0.3.0)

---
updated-dependencies:
- dependency-name: golang.org/x/tools
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-22 10:04:30 -05:00
dependabot[bot]
d76985e3f7 Bump github.com/kr/pretty from 0.3.0 to 0.3.1 (#1924)
Bumps [github.com/kr/pretty](https://github.com/kr/pretty) from 0.3.0 to 0.3.1.
- [Release notes](https://github.com/kr/pretty/releases)
- [Commits](https://github.com/kr/pretty/compare/v0.3.0...v0.3.1)

---
updated-dependencies:
- dependency-name: github.com/kr/pretty
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-22 10:03:06 -05:00
dependabot[bot]
e17e4ef146 Bump github.com/microcosm-cc/bluemonday from 1.0.20 to 1.0.21 (#1905)
Bumps [github.com/microcosm-cc/bluemonday](https://github.com/microcosm-cc/bluemonday) from 1.0.20 to 1.0.21.
- [Release notes](https://github.com/microcosm-cc/bluemonday/releases)
- [Commits](https://github.com/microcosm-cc/bluemonday/compare/v1.0.20...v1.0.21)

---
updated-dependencies:
- dependency-name: github.com/microcosm-cc/bluemonday
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-22 10:02:42 -05:00
dependabot[bot]
0a4a9d485e Bump github.com/mattn/go-sqlite3 from 1.14.15 to 1.14.16 (#1965)
Bumps [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3) from 1.14.15 to 1.14.16.
- [Release notes](https://github.com/mattn/go-sqlite3/releases)
- [Commits](https://github.com/mattn/go-sqlite3/compare/v1.14.15...v1.14.16)

---
updated-dependencies:
- dependency-name: github.com/mattn/go-sqlite3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-22 09:43:05 -05:00
dependabot[bot]
ce2c579235 Bump github.com/spf13/cobra from 1.5.0 to 1.6.1 (#1966)
Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.5.0 to 1.6.1.
- [Release notes](https://github.com/spf13/cobra/releases)
- [Commits](https://github.com/spf13/cobra/compare/v1.5.0...v1.6.1)

---
updated-dependencies:
- dependency-name: github.com/spf13/cobra
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-22 09:42:48 -05:00
dependabot[bot]
4e19c5e078 Bump github.com/stretchr/testify from 1.8.0 to 1.8.1 (#1951)
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.8.0 to 1.8.1.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.8.0...v1.8.1)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-22 09:42:26 -05:00
jan666
ab6be8d2dc Listenbrainz Scrobble (#2009)
- send SubmissionClient and SubmissionClientVersion
2022-11-22 09:32:46 -05:00
dependabot[bot]
586f5c413d Bump github.com/onsi/ginkgo/v2 from 2.2.0 to 2.5.1 (#2007)
Bumps [github.com/onsi/ginkgo/v2](https://github.com/onsi/ginkgo) from 2.2.0 to 2.5.1.
- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v2.2.0...v2.5.1)

---
updated-dependencies:
- dependency-name: github.com/onsi/ginkgo/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-21 22:57:34 -05:00
dependabot[bot]
e6a93da75f Bump github.com/onsi/gomega from 1.20.2 to 1.24.1 (#1990)
Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.20.2 to 1.24.1.
- [Release notes](https://github.com/onsi/gomega/releases)
- [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/gomega/compare/v1.20.2...v1.24.1)

---
updated-dependencies:
- dependency-name: github.com/onsi/gomega
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-21 21:08:07 -05:00
Deluan
fcb891e704 Add an id attribute to Search boxes. Should fix #1998 2022-11-21 13:44:16 -05:00
Deluan
19af11efbe Simplify Subsonic API handler implementation 2022-11-21 12:57:56 -05:00
Deluan
cd41d9a419 Shutdown gracefully, close DB connection 2022-11-21 12:28:09 -05:00
Deluan
5f3f7afb90 Add note about unstable state of master branch 2022-11-11 21:23:07 -05:00
Deluan
1467036efd Add DefaultUIVolume option. Closes #1679 2022-11-11 16:31:28 -05:00
dependabot[bot]
ff6c8f7e9d Bump loader-utils from 2.0.0 to 2.0.3 in /ui (#1978)
Bumps [loader-utils](https://github.com/webpack/loader-utils) from 2.0.0 to 2.0.3.
- [Release notes](https://github.com/webpack/loader-utils/releases)
- [Changelog](https://github.com/webpack/loader-utils/blob/v2.0.3/CHANGELOG.md)
- [Commits](https://github.com/webpack/loader-utils/compare/v2.0.0...v2.0.3)

---
updated-dependencies:
- dependency-name: loader-utils
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-07 19:28:02 -05:00
Deluan
3a462c7f07 Fix ARM v5 and v6 builds, by going back to armel.
Also upgrades Go to 1.19.3. Closes #1968
2022-11-07 17:16:30 -05:00
Deluan
9c433b5d68 Add missing context to logger calls 2022-11-04 11:30:12 -04:00
YaoFeng Ruan
daa428ede7 Update Chinese translations (#1945)
* Corrected some Simplified Chinese translations

* Fix wrong expression symbols in Traditional Chinese translation

* Modify punctuation to Chinese punctuation in Chinese translation
Add spaces between Chinese and English words in Chinese translation

* Added missing Traditional Chinese translation

* Improve some Chinese translations

* Remove redundant punctuation in Traditional Chinese translation

* Adjust the order of fields in `zh-Hans` and `zh-Hant` to be consistent with `en`
2022-11-04 10:44:32 -04:00
Deluan
76517cab12 Fix potential nil pointer dereference 2022-11-04 10:39:25 -04:00
Deluan
8f02daf337 Reduce spurious error/warn messages, if loglevel != debug 2022-11-03 12:38:05 -04:00
Deluan
80b7311453 Add TrackNumber to "fake" generated filenames. Fixes #1912 2022-11-02 12:11:01 -04:00
Deluan
ca2cb26d8e Add played field to Subsonic API responses. Fix #1971
This is not an "official" field in the specification, but I guess it does not hurt to expose this ;)
2022-11-02 11:20:51 -04:00
Deluan
081cfe5a9f Fix build badge 2022-10-31 10:35:07 -04:00
Deluan
5f38d9dca2 Fix 60 seconds (again). Fixes #1956 2022-10-26 09:10:01 -04:00
Aleksey Lobanov
64e2a0bcd4 Optimize static images (#1941)
.png files were processed with `optipng -o7` command
2022-10-20 10:51:31 -04:00
Deluan
aab4925dfc Restore DefaultLanguage case-sensitiveness by reverting commit bfeb8ef6b3.
Language code should be case-sensitive. Fix #1946. Supersedes #1947.
2022-10-19 09:14:02 -04:00
Deluan
af5c2b5a42 Round song duration (instead of truncating it). Relates to #1926 2022-10-10 21:33:00 -04:00
Deluan
62e7492357 Add Linkify test 2022-10-07 17:44:16 -04:00
Deluan
53a4ea673b Linkify urls in playlist comments 2022-10-07 16:12:07 -04:00
Deluan
c530ccf138 Linkify urls in album comments. Fixes #1053, supersedes #1570 and #1169
Simple approach, may be extended/enhanced in the future.
2022-10-06 23:46:30 -04:00
Deluan
fa5dc5af10 Fix adding songs to plain playlists 2022-10-06 19:45:31 -04:00
Deluan
bbd3882a75 Some clean-up in criteria package 2022-10-04 15:24:29 -04:00
Deluan
12b4a48842 Fix get info dialog in artist page. Closes #1909 2022-10-04 12:30:04 -04:00
dependabot[bot]
37f7625c7d Bump github.com/prometheus/client_golang from 1.12.1 to 1.13.0 (#1902)
Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.12.1 to 1.13.0.
- [Release notes](https://github.com/prometheus/client_golang/releases)
- [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prometheus/client_golang/compare/v1.12.1...v1.13.0)

---
updated-dependencies:
- dependency-name: github.com/prometheus/client_golang
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-02 20:34:29 -04:00
dependabot[bot]
7612a55859 Bump github.com/mileusna/useragent from 1.2.0 to 1.2.1 (#1901)
Bumps [github.com/mileusna/useragent](https://github.com/mileusna/useragent) from 1.2.0 to 1.2.1.
- [Release notes](https://github.com/mileusna/useragent/releases)
- [Commits](https://github.com/mileusna/useragent/compare/v1.2.0...v1.2.1)

---
updated-dependencies:
- dependency-name: github.com/mileusna/useragent
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-02 20:34:01 -04:00
Deluan
3d5a1cef92 Don't allow adding songs to smart playlists 2022-10-02 20:14:15 -04:00
Aleksey Lobanov
552989a05b Add basic Prometheus metrics handler (#1830)
* feat: Add Prometheus configuration options

* feat: Add Prometheus metrics handler

* build: prometheus became direct dependency

* docs: change description for prometheus metrics path
2022-10-02 19:59:53 -04:00
Renere
6a6fa3e3b5 Nord Theme - Make links have a different colour (#1900) 2022-10-01 22:23:33 -04:00
Zane van Iperen
c7ef4bd803 Capture "musicbrainz_releasetrackid" tag (#1827)
* db/migration: typo fix

* model: add MbzReleaseTrackID field

* scanner: capture the musicbrainz_releasetrackid tag
2022-10-01 12:13:47 -04:00
Renere
22507c9789 Add Nord Theme. Closes #1158 and supersedes #1159 (#1899).
* Re-add tpbnick's Nord theme

* Run Prettier formatter on Nord theme

* Update themes index

* Fix button margins

* Modernise the look of switches

* Adjust margins and padding

* Fix sidebar's background colour not applying to all of sidebar when scrolling down

* Adjust App Bar box shadow

* Adjust roundedness

* Adjust shadows

* Adjust outlined inputs

* Add transitions to items in sidebar when hovered / losing hover

* Adjust border radiuses

* Adjust pagination buttons

* Add big play button from Spotify theme

* Remove playlist background gradient

* Adjust colour of MuiChip elelments

* Adjust table borders

* Remove duplicate MuiTableRow key

* Attempt to make switches in both the playlist section and settings section visable against background & the toggle. Not ideal.

* Style the player

* Format CSS to Prettier standards

* Fix mobile player style

* Make play button in album grid view blue

* Make main view background lighter
2022-10-01 12:01:21 -04:00
Deluan
87feac041b Add make target to download some music for development purposes. Closes #1703 2022-09-30 23:10:33 -04:00
Deluan
f82df70302 Add nilerr linter 2022-09-30 20:18:14 -04:00
Deluan
364e699ac1 Add asciicheck, bidichk, and durationcheck linters 2022-09-30 20:17:59 -04:00
Deluan
0798959be8 Add asasalint linter 2022-09-30 19:55:44 -04:00
William Lohan
4209e14208 Add theme Electric Purple (#1889)
* add theme file

add theme file electricPurple.js

* import theme file 

import theme file  electricPurple

* add electricPurple.css.js
2022-09-30 19:54:00 -04:00
Deluan
77dbafff0f Add errorlint linter 2022-09-30 19:33:39 -04:00
Deluan
db67c1277e Fix error comparisons 2022-09-30 18:54:25 -04:00
Deluan
7b0a8f47de Add exportloopref linter 2022-09-30 18:23:47 -04:00
William Lohan
16865f0fca remove deprecated linters (#1898) 2022-09-30 18:11:44 -04:00
Deluan
5965459bb9 Update browserlist db 2022-09-30 13:33:42 -04:00
Steve Richter
66818b25ec Allow ExternalLink icons to be styled (#1503)
* Allow ArtistExternalLink icons to be styled

* Allow AlbumExternalLink icons to be styled

* Standardize external links' classes to kebab-case

Co-authored-by: Deluan <deluan@navidrome.org>
2022-09-30 13:33:35 -04:00
Deluan
e7fab8bb7b Show AlbumArtist in Album table view. Fixes #1626 2022-09-29 16:47:44 -04:00
joaomqc
8befe10ee6 fix(UI): Warn if track is already present when adding to playlist - 1604 (#1897)
* fix(UI): Warn if track is already present when adding to playlist - 1604

Signed-off-by: joaomqc <joaomqc@hotmail.com>

* fix tests

Signed-off-by: joaomqc <joaomqc@hotmail.com>

Signed-off-by: joaomqc <joaomqc@hotmail.com>
Co-authored-by: João Coelho <1120458@isep.ipp.pt>
2022-09-29 13:19:14 -04:00
Deluan
218d14727a Bump redux and react-redux versions 2022-09-29 11:05:05 -04:00
Evan.Shu
50a4ce6ba2 Fix add playlist dialog (#1758) 2022-09-28 22:15:39 -04:00
henning mueller
8130c05ccc Mount devcontainer workspace SELinux compatible (#1816) 2022-09-28 22:10:06 -04:00
Deluan
15952a3c7f npm audit fix 2022-09-28 22:01:13 -04:00
Nemo Xiong
9a99a2bd49 Update Chinese (simplified) translations (#1633)
* add new translations

* translation: fix improper full width character usage in zh-Hans translation

Full width % messed up with format strings.

* translation: fix two machine translations in zh-Hans

* translation: fix one mistranslation in zh-Hans

* translation: fix format in zh-Hans

* translation: fix format and two translations in zh-Hans

* translation: fix format in zh-Hans
2022-09-28 21:47:48 -04:00
dependabot[bot]
c7b65509ae Bump @testing-library/jest-dom from 5.15.0 to 5.16.5 in /ui (#1836)
Bumps [@testing-library/jest-dom](https://github.com/testing-library/jest-dom) from 5.15.0 to 5.16.5.
- [Release notes](https://github.com/testing-library/jest-dom/releases)
- [Changelog](https://github.com/testing-library/jest-dom/blob/main/CHANGELOG.md)
- [Commits](https://github.com/testing-library/jest-dom/compare/v5.15.0...v5.16.5)

---
updated-dependencies:
- dependency-name: "@testing-library/jest-dom"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-28 21:43:26 -04:00
Deluan
6b09dc7198 Fix new test-library eslint errors 2022-09-28 21:30:20 -04:00
Deluan
86ab35069d Upgrade react-scripts to 5.0.1
This also upgrades WebPack to v5, which should fix the issue #1768
2022-09-28 21:03:22 -04:00
Deluan
413292da6b Reduce go mod download verbosity 2022-09-28 20:27:53 -04:00
Deluan
694968c607 Bump dependencies 2022-09-28 13:25:08 -04:00
Deluan
6dc70d6810 Don't reset language to default after logout 2022-09-28 13:06:32 -04:00
Deluan
bfeb8ef6b3 DefaultLanguage is now case-insensitive 2022-09-28 11:30:22 -04:00
Deluan Quintão
ba28e9a109 Update README. Fixes #1834 2022-09-27 21:32:23 -04:00
Andy Klimczak
2f7a3c5eda feat: Add listenbrainz base url configuration (#1774)
* feat: Add listenbrainz base url configuration

- ListenBrainz.BaseURL config value

* Don't need to store baseUrl

* Use `url.JoinPath` to concatenate url paths

* Replace url.JoinPath (Go 1.19 only) with custom function

Co-authored-by: Deluan <deluan@navidrome.org>
2022-09-27 21:06:28 -04:00
Deluan
cb3ba23fce New config DefaultLanguage. Closes #1561 2022-09-27 19:31:09 -04:00
Manuel
72cde6dfde fix:(middlewares.go) - Set Cookie SameSite mode to Strict - 1776 (#1777)
* None is deprecated and will fallback to Lax in the future.
* Using Strict is future proof and provides additional CSR protection

Signed-off-by: Manuel Kroeber <manuel.kroeber@gmail.com>

Signed-off-by: Manuel Kroeber <manuel.kroeber@gmail.com>
2022-09-27 17:58:47 -04:00
Kendall Garner
751e42c705 Fix creating server (#1894) 2022-09-27 16:53:40 -04:00
Deluan
ded9ab53e5 Use armhf for ARM builds 2022-09-27 16:47:47 -04:00
Deluan
416b5c7d13 Fix Linux 32 bits build 2022-09-26 23:54:03 -04:00
Deluan
afb31c3eae Fix invalid option in pipeline 2022-09-26 22:56:17 -04:00
Deluan
dd57278ba2 Upgrade to GoLang 1.19 and bump golangci-lint version 2022-09-26 22:44:54 -04:00
Deluan
2a3cd08f20 Fix GO-S2114 security issue
See https://deepsource.io/directory/analyzers/go/issues/GO-S2114
2022-09-26 22:33:42 -04:00
Deluan
a7a0e23956 Fix formatting 2022-09-26 21:28:10 -04:00
Deluan
4cf43ed735 Only compute version once 2022-09-14 21:09:39 -04:00
Deluan
ebad96b8a4 Fix warning about mixing value and pointer receivers 2022-08-21 14:42:17 -04:00
Deluan
e981ee27c0 Add test for WithTx 2022-07-30 13:07:38 -04:00
Deluan
965dbccd48 Upgrade to latest go-sqlite3 (it's v1.14, not v2!) 2022-07-30 12:46:20 -04:00
Deluan
695f82a1a0 Upgrade to Beego 2's orm 2022-07-30 12:43:48 -04:00
Deluan
16afd3a490 Remove //+build tags, as the code does not compile on older versions of Go anymore 2022-07-29 08:41:28 -04:00
Deluan
67f2a89d89 Fix tracks never "loved" to be selected in Smart Playlists. Refer to https://github.com/navidrome/navidrome/issues/1417#issuecomment-1163423575 2022-07-27 21:09:39 -04:00
dependabot[bot]
bf1f93ef1a Bump github.com/go-chi/httprate from 0.5.2 to 0.6.0 (#1828)
Bumps [github.com/go-chi/httprate](https://github.com/go-chi/httprate) from 0.5.2 to 0.6.0.
- [Release notes](https://github.com/go-chi/httprate/releases)
- [Commits](https://github.com/go-chi/httprate/compare/v0.5.2...v0.6.0)

---
updated-dependencies:
- dependency-name: github.com/go-chi/httprate
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-07-27 15:29:30 -04:00
Deluan
ebf7354df4 Add more info in search log message 2022-07-27 14:59:01 -04:00
Deluan
c0066ebd85 Add log warn when request is cancelled/interrupted 2022-07-27 14:27:18 -04:00
Deluan
cd5bce7b16 Speed up /search subsonic endpoints by parallelizing the queries 2022-07-27 13:56:04 -04:00
Deluan
d613b19306 Simplify Singleton usage by leveraging Go 1.18's generics 2022-07-27 12:15:05 -04:00
Deluan
a2d9aaeff8 Fix Quality translation in Spanish 2022-07-27 10:42:04 -04:00
Deluan
49392e06a7 Update caniuse-lite 2022-07-26 17:48:29 -04:00
Deluan
181cb8a2b7 Remove interfacer linter, as it does not work with Go 1.18 and will not be updated (it is deprecated) 2022-07-26 16:59:52 -04:00
Deluan
31882abf6f Upgrade Ginkgo to V2 2022-07-26 16:53:17 -04:00
Deluan
0d8eaa2878 Remove experimental version of context package 2022-07-26 16:41:10 -04:00
Deluan
f4bffb1676 Update @djherbis's packages 2022-07-26 15:16:56 -04:00
Deluan
f21847308c Remove hardcoded github.com/dhowden/tag branch. Fix #1764 2022-07-26 15:10:16 -04:00
Deluan
9c3b14c5c4 Return 501 for "not implemented". Fixes #1785 2022-07-26 13:18:08 -04:00
Deluan
8cd405d15e Add IP to Subsonic API's invalid login log messages. Closes #1814 2022-07-25 23:54:49 -04:00
Deluan
35bec14d4d Add missing test case for #1778 2022-07-25 23:34:09 -04:00
Deluan
321b3c5a64 Fix fscache key mapping. Closes #1778 2022-07-25 23:01:19 -04:00
Deluan
b7e50f7731 Fix docker build in pipeline 2022-07-25 10:54:19 -04:00
dependabot[bot]
2e9c81c3de Bump github.com/mileusna/useragent from 1.0.2 to 1.1.0 (#1819)
Bumps [github.com/mileusna/useragent](https://github.com/mileusna/useragent) from 1.0.2 to 1.1.0.
- [Release notes](https://github.com/mileusna/useragent/releases)
- [Commits](https://github.com/mileusna/useragent/compare/v1.0.2...v1.1.0)

---
updated-dependencies:
- dependency-name: github.com/mileusna/useragent
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-07-25 10:43:45 -04:00
dependabot[bot]
49647423aa Bump github.com/sirupsen/logrus from 1.8.1 to 1.9.0 (#1821)
Bumps [github.com/sirupsen/logrus](https://github.com/sirupsen/logrus) from 1.8.1 to 1.9.0.
- [Release notes](https://github.com/sirupsen/logrus/releases)
- [Changelog](https://github.com/sirupsen/logrus/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sirupsen/logrus/compare/v1.8.1...v1.9.0)

---
updated-dependencies:
- dependency-name: github.com/sirupsen/logrus
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-07-25 10:42:51 -04:00
dependabot[bot]
9f62533bb0 Bump github.com/go-chi/cors from 1.2.0 to 1.2.1 (#1822)
Bumps [github.com/go-chi/cors](https://github.com/go-chi/cors) from 1.2.0 to 1.2.1.
- [Release notes](https://github.com/go-chi/cors/releases)
- [Commits](https://github.com/go-chi/cors/compare/v1.2.0...v1.2.1)

---
updated-dependencies:
- dependency-name: github.com/go-chi/cors
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-07-25 10:08:48 -04:00
dependabot[bot]
7d58f4469a Bump github.com/lestrrat-go/jwx from 1.2.17 to 1.2.25 (#1742)
Bumps [github.com/lestrrat-go/jwx](https://github.com/lestrrat-go/jwx) from 1.2.17 to 1.2.25.
- [Release notes](https://github.com/lestrrat-go/jwx/releases)
- [Changelog](https://github.com/lestrrat-go/jwx/blob/v1.2.25/Changes)
- [Commits](https://github.com/lestrrat-go/jwx/compare/v1.2.17...v1.2.25)

---
updated-dependencies:
- dependency-name: github.com/lestrrat-go/jwx
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-07-25 10:08:06 -04:00
dependabot[bot]
974816f0a2 Bump github.com/onsi/gomega from 1.18.1 to 1.20.0 (#1817)
Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.18.1 to 1.20.0.
- [Release notes](https://github.com/onsi/gomega/releases)
- [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/gomega/compare/v1.18.1...v1.20.0)

---
updated-dependencies:
- dependency-name: github.com/onsi/gomega
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-07-24 20:35:38 -04:00
Deluan
7665478a52 Upgrade golangci-lint and fix new lint error 2022-07-24 19:30:23 -04:00
Deluan
bde5be347b Build with GoLang 1.18.4 2022-07-24 19:02:09 -04:00
Deluan
aae79b4561 Upgrade to GoLang 1.18 2022-07-24 15:31:22 -04:00
Ian Kerins
ce0db8344b Fix signaler not exiting on cancel (#1638)
* fix: make signaler exit on cancel

`break` is incorrect here, as it just breaks out of the select.
`return` to exit the function instead.

Fixes #1636.

Signed-off-by: Ian Kerins <ianskerins@gmail.com>

* fix: exit non-zero on fatal error

Signed-off-by: Ian Kerins <ianskerins@gmail.com>
2022-03-30 10:04:17 -04:00
Matt Doyle
5987cd0c08 Fixes a coloring glitch with the Monokai theme "unauthorized" popup (#1670)
* Fixes the coloring on the Monokai theme auth popup

* Indentation fix
2022-03-26 22:41:29 -04:00
Matt Doyle
e7cf74d863 Adds a Monokai theme (#1669)
* Adds a new Monokai theme

* Deletes a commented-out line
2022-03-26 21:14:13 -04:00
Deluan
2ddd3acba6 Fix translatable label 2022-02-10 18:18:03 -05:00
Deluan
028723f721 Fix loading overridden translations from ${DataFolder}/resources/i18n 2022-02-10 14:56:39 -05:00
Deluan
50ff8bcce7 Add "random" sort option for Smart Playlists 2022-02-09 09:39:42 -05:00
Deluan
e966d94c0b Force correct mime-type for JS and CSS files 2022-02-08 15:17:35 -05:00
dependabot[bot]
86fe1e3b2c Bump github.com/ReneKroon/ttlcache/v2 from 2.9.0 to 2.11.0 (#1529)
Bumps [github.com/ReneKroon/ttlcache/v2](https://github.com/ReneKroon/ttlcache) from 2.9.0 to 2.11.0.
- [Release notes](https://github.com/ReneKroon/ttlcache/releases)
- [Changelog](https://github.com/ReneKroon/ttlcache/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ReneKroon/ttlcache/compare/v2.9.0...v2.11.0)

---
updated-dependencies:
- dependency-name: github.com/ReneKroon/ttlcache/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-01-21 19:35:19 -05:00
dependabot[bot]
eed54d7e10 Bump github.com/lestrrat-go/jwx from 1.2.11 to 1.2.17 (#1574)
Bumps [github.com/lestrrat-go/jwx](https://github.com/lestrrat-go/jwx) from 1.2.11 to 1.2.17.
- [Release notes](https://github.com/lestrrat-go/jwx/releases)
- [Changelog](https://github.com/lestrrat-go/jwx/blob/main/Changes)
- [Commits](https://github.com/lestrrat-go/jwx/compare/v1.2.11...v1.2.17)

---
updated-dependencies:
- dependency-name: github.com/lestrrat-go/jwx
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-01-21 19:26:28 -05:00
dependabot[bot]
ab36344d76 Bump github.com/microcosm-cc/bluemonday from 1.0.16 to 1.0.17 (#1560)
Bumps [github.com/microcosm-cc/bluemonday](https://github.com/microcosm-cc/bluemonday) from 1.0.16 to 1.0.17.
- [Release notes](https://github.com/microcosm-cc/bluemonday/releases)
- [Commits](https://github.com/microcosm-cc/bluemonday/compare/v1.0.16...v1.0.17)

---
updated-dependencies:
- dependency-name: github.com/microcosm-cc/bluemonday
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-01-21 19:26:11 -05:00
dependabot[bot]
e5d03a3bdb Bump github.com/Masterminds/squirrel from 1.5.1 to 1.5.2 (#1501)
Bumps [github.com/Masterminds/squirrel](https://github.com/Masterminds/squirrel) from 1.5.1 to 1.5.2.
- [Release notes](https://github.com/Masterminds/squirrel/releases)
- [Commits](https://github.com/Masterminds/squirrel/compare/v1.5.1...v1.5.2)

---
updated-dependencies:
- dependency-name: github.com/Masterminds/squirrel
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-01-21 19:25:38 -05:00
Deluan Quintão
30813cd34b Update translations (#1578)
* Add Catalan translation

* Move Bulgarian translation to correct path

* Update zh-Hans.json (POEditor.com)

* Update zh-Hant.json (POEditor.com)

* Update cs.json (POEditor.com)

* Update da.json (POEditor.com)

* Update nl.json (POEditor.com)

* Update eo.json (POEditor.com)

* Update fi.json (POEditor.com)

* Update fr.json (POEditor.com)

* Update de.json (POEditor.com)

* Update it.json (POEditor.com)

* Update ja.json (POEditor.com)

* Update fa.json (POEditor.com)

* Update pl.json (POEditor.com)

* Update pt.json (POEditor.com)

* Update ru.json (POEditor.com)

* Update sl.json (POEditor.com)

* Update es.json (POEditor.com)

* Update th.json (POEditor.com)

* Update tr.json (POEditor.com)

* Update uk.json (POEditor.com)

* Update bg.json (POEditor.com)

* Update ca.json (POEditor.com)
2022-01-21 19:21:19 -05:00
MrEddX
6164f37c9e Added Bulgarian Translation (#1577)
Initial Release
2022-01-21 19:09:48 -05:00
Deluan
9e79b5cbf2 Fix potential SQL injection in Smart Playlists 2022-01-18 21:36:29 -05:00
Steve Richter
8c707b4e0c Handle invalid theme in ui state (#1504) 2022-01-05 18:47:14 -05:00
Deluan
910091f1f1 Fix playCount casing 2021-12-14 09:33:34 -05:00
Deluan
2e1b985d30 Revert "Direct link to dev build"
This reverts commit a99b9b4d44.
2021-12-10 12:41:19 -05:00
Deluan Quintão
100b80528e Update README.md 2021-12-09 12:24:43 -05:00
Deluan
bde9d5f954 Fix TypeError: Cannot read property 'id' of undefined 2021-12-03 17:15:39 -05:00
Deluan
69615f1aa1 Trying to fix multiple EventStream connections, one more time 2021-12-02 10:49:32 -05:00
Deluan
a99b9b4d44 Direct link to dev build 2021-11-29 22:57:26 -05:00
Deluan
9892524ab8 Connect eventStream after login 2021-11-29 18:49:29 -05:00
Deluan
9fe978953c Try to avoid creating multiple eventStreams 2021-11-29 17:47:34 -05:00
Deluan Quintão
5425c1a4d7 Update translations (#1489)
* Update fi.json (POEditor.com)

* Update fr.json (POEditor.com)

* Update de.json (POEditor.com)

* Update ja.json (POEditor.com)

* Update sl.json (POEditor.com)

* Update es.json (POEditor.com)
2021-11-26 12:34:22 -05:00
Deluan
afe1e4cfcd Fix lint for public credentials 2021-11-25 15:55:53 -05:00
Deluan
20cdd38fc4 Better logging for agents configuration 2021-11-25 15:48:32 -05:00
dependabot[bot]
913a4cf546 Bump github.com/onsi/gomega from 1.16.0 to 1.17.0 (#1459)
Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.16.0 to 1.17.0.
- [Release notes](https://github.com/onsi/gomega/releases)
- [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/gomega/compare/v1.16.0...v1.17.0)

---
updated-dependencies:
- dependency-name: github.com/onsi/gomega
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-23 23:43:32 -05:00
dependabot[bot]
121ada5acd Bump @testing-library/jest-dom from 5.14.1 to 5.15.0 in /ui (#1456)
Bumps [@testing-library/jest-dom](https://github.com/testing-library/jest-dom) from 5.14.1 to 5.15.0.
- [Release notes](https://github.com/testing-library/jest-dom/releases)
- [Changelog](https://github.com/testing-library/jest-dom/blob/main/CHANGELOG.md)
- [Commits](https://github.com/testing-library/jest-dom/compare/v5.14.1...v5.15.0)

---
updated-dependencies:
- dependency-name: "@testing-library/jest-dom"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-23 23:13:22 -05:00
dependabot[bot]
e59a95c9eb Bump github.com/golangci/golangci-lint from 1.42.1 to 1.43.0 (#1460)
Bumps [github.com/golangci/golangci-lint](https://github.com/golangci/golangci-lint) from 1.42.1 to 1.43.0.
- [Release notes](https://github.com/golangci/golangci-lint/releases)
- [Changelog](https://github.com/golangci/golangci-lint/blob/master/CHANGELOG.md)
- [Commits](https://github.com/golangci/golangci-lint/compare/v1.42.1...v1.43.0)

---
updated-dependencies:
- dependency-name: github.com/golangci/golangci-lint
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-23 23:12:27 -05:00
Mahoo Huang
d75f286ae8 Update zh-Hans.json (#1478) 2021-11-23 23:11:49 -05:00
Deluan
30d3f1eda0 Add userRating to Subsonic Album/Artist responses. Closes #1486 2021-11-23 21:50:57 -05:00
dependabot[bot]
6a1f9678b1 Bump github.com/ReneKroon/ttlcache/v2 from 2.8.1 to 2.9.0 (#1414)
Bumps [github.com/ReneKroon/ttlcache/v2](https://github.com/ReneKroon/ttlcache) from 2.8.1 to 2.9.0.
- [Release notes](https://github.com/ReneKroon/ttlcache/releases)
- [Changelog](https://github.com/ReneKroon/ttlcache/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ReneKroon/ttlcache/compare/v2.8.1...v2.9.0)

---
updated-dependencies:
- dependency-name: github.com/ReneKroon/ttlcache/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-22 12:50:42 -05:00
dependabot[bot]
a0977ce48a Bump github.com/go-chi/chi/v5 from 5.0.4 to 5.0.7 (#1484)
Bumps [github.com/go-chi/chi/v5](https://github.com/go-chi/chi) from 5.0.4 to 5.0.7.
- [Release notes](https://github.com/go-chi/chi/releases)
- [Changelog](https://github.com/go-chi/chi/blob/master/CHANGELOG.md)
- [Commits](https://github.com/go-chi/chi/compare/v5.0.4...v5.0.7)

---
updated-dependencies:
- dependency-name: github.com/go-chi/chi/v5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-22 12:29:10 -05:00
dependabot[bot]
b3d8038686 Bump github.com/lestrrat-go/jwx from 1.2.7 to 1.2.11 (#1485)
Bumps [github.com/lestrrat-go/jwx](https://github.com/lestrrat-go/jwx) from 1.2.7 to 1.2.11.
- [Release notes](https://github.com/lestrrat-go/jwx/releases)
- [Changelog](https://github.com/lestrrat-go/jwx/blob/main/Changes)
- [Commits](https://github.com/lestrrat-go/jwx/compare/v1.2.7...v1.2.11)

---
updated-dependencies:
- dependency-name: github.com/lestrrat-go/jwx
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-22 12:28:23 -05:00
Deluan
0714f08274 Recover from SIGSEGVs in taglib's code 2021-11-20 12:33:06 -05:00
Deluan
cbeaadf8e2 Fix logging smart playlist's song count 2021-11-20 12:29:09 -05:00
Deluan
3e282df639 Set volume to 100% when web player is in mobile mode.
Fix #1429
2021-11-19 19:45:18 -05:00
Deluan
ce7940bbfc Allow overriding name and comment when importing NSP playlists 2021-11-19 19:14:38 -05:00
Deluan
92c31c961d Fix values from annotation table cannot be compared to 0
Solves this issue: https://github.com/navidrome/navidrome/issues/1417#issuecomment-974052454
2021-11-19 18:22:33 -05:00
Deluan
4bf4765442 Bot that adds a download link on pull requests 2021-11-19 13:07:55 -05:00
Brice Johnson
6d947f6f7e Allowing 3rd party UIs to access x-total-count http header (#1470)
* Adding 'x-content-duratin' and 'x-total-count' to CORS exposed headers

* Moving cors setup to middlewares.go

* adding x-nd-authorization to exposed headers
2021-11-19 10:07:54 -05:00
Deluan Quintão
8c7d95c135 Update Translations (#1471)
* Update zh-Hans.json (POEditor.com)

* Update zh-Hant.json (POEditor.com)

* Update cs.json (POEditor.com)

* Update da.json (POEditor.com)

* Update nl.json (POEditor.com)

* Update eo.json (POEditor.com)

* Update fr.json (POEditor.com)

* Update de.json (POEditor.com)

* Update it.json (POEditor.com)

* Update ja.json (POEditor.com)

* Update fa.json (POEditor.com)

* Update pl.json (POEditor.com)

* Update pt.json (POEditor.com)

* Update ru.json (POEditor.com)

* Update sl.json (POEditor.com)

* Update es.json (POEditor.com)

* Update th.json (POEditor.com)

* Update tr.json (POEditor.com)

* Update uk.json (POEditor.com)

* Update fi.json (POEditor.com)
2021-11-18 17:52:12 -05:00
Deluan
d4447e373f Fix sorting albums by year (should use name as secondary sort field).
Relates to https://github.com/navidrome/navidrome/issues/961#issuecomment-967624681
2021-11-17 21:47:14 -05:00
Steve Richter
3bd6f82c80 Rename ListenBrainz config flag and enable by default (#1443) 2021-11-17 21:11:53 -05:00
BIKI DAS
da26c5cfe0 Combined multiple appends into a single one (#1464) 2021-11-17 19:51:44 -05:00
Deluan
023d7bfa8a Remove link from songs to artist (when artist has no albums) 2021-11-17 18:47:54 -05:00
Deluan
48a627885c Simplify prototype definition for taglib_read 2021-11-13 12:18:42 -05:00
Deluan
91b470c93b Show artist link in Songs lists 2021-11-05 20:25:12 -04:00
Deluan
1c82bf5179 Remove non-album artist_ids from the DB 2021-11-05 20:24:50 -04:00
Deluan
0d9dcebf32 Fix playlist cannot be empty via Subsonic API 2021-11-05 10:23:45 -04:00
Deluan
5994c31f4c Fix migration to support null values 2021-11-04 21:23:41 -04:00
Deluan
804fb716db Show in the logs how long it took to startup 2021-11-04 13:49:05 -04:00
Deluan
d3a2f769b7 Better logging of GetSimilar call 2021-11-03 15:59:16 -04:00
Deluan
68a84ec832 Smarter cache of external info calls (last.fm / spotify) 2021-11-03 14:13:50 -04:00
Deluan
9e48d87f84 Add a new index for album, to optimize the getAlbumList?type=alphabeticalByArtist Subsonic query 2021-11-02 22:08:21 -04:00
Deluan
9712a5b1c6 Fix error codes for required parameters in getAlbumList 2021-11-02 21:38:08 -04:00
Deluan
9422373be0 Optimize AlbumRepository.GetAll and add a GetAllWithoutGenres method specifically for Subsonic API, where multiple-genres are not required 2021-11-02 21:19:49 -04:00
dependabot[bot]
bc8132ef1f Bump @testing-library/user-event from 13.2.1 to 13.5.0 in /ui
Bumps [@testing-library/user-event](https://github.com/testing-library/user-event) from 13.2.1 to 13.5.0.
- [Release notes](https://github.com/testing-library/user-event/releases)
- [Changelog](https://github.com/testing-library/user-event/blob/main/CHANGELOG.md)
- [Commits](https://github.com/testing-library/user-event/compare/v13.2.1...v13.5.0)

---
updated-dependencies:
- dependency-name: "@testing-library/user-event"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-02 15:54:18 -04:00
dependabot[bot]
82bc8cd0aa Bump github.com/go-chi/httprate from 0.5.1 to 0.5.2
Bumps [github.com/go-chi/httprate](https://github.com/go-chi/httprate) from 0.5.1 to 0.5.2.
- [Release notes](https://github.com/go-chi/httprate/releases)
- [Commits](https://github.com/go-chi/httprate/compare/v0.5.1...v0.5.2)

---
updated-dependencies:
- dependency-name: github.com/go-chi/httprate
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-02 15:53:53 -04:00
dependabot[bot]
1e5ab59df8 Bump github.com/onsi/ginkgo from 1.16.4 to 1.16.5
Bumps [github.com/onsi/ginkgo](https://github.com/onsi/ginkgo) from 1.16.4 to 1.16.5.
- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v1.16.4...v1.16.5)

---
updated-dependencies:
- dependency-name: github.com/onsi/ginkgo
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-02 15:52:39 -04:00
dependabot[bot]
28ad91a9d6 Bump github.com/Masterminds/squirrel from 1.5.0 to 1.5.1
Bumps [github.com/Masterminds/squirrel](https://github.com/Masterminds/squirrel) from 1.5.0 to 1.5.1.
- [Release notes](https://github.com/Masterminds/squirrel/releases)
- [Commits](https://github.com/Masterminds/squirrel/compare/v1.5.0...v1.5.1)

---
updated-dependencies:
- dependency-name: github.com/Masterminds/squirrel
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-02 15:52:15 -04:00
dependabot[bot]
e40e86590c Bump github.com/microcosm-cc/bluemonday from 1.0.15 to 1.0.16
Bumps [github.com/microcosm-cc/bluemonday](https://github.com/microcosm-cc/bluemonday) from 1.0.15 to 1.0.16.
- [Release notes](https://github.com/microcosm-cc/bluemonday/releases)
- [Commits](https://github.com/microcosm-cc/bluemonday/compare/v1.0.15...v1.0.16)

---
updated-dependencies:
- dependency-name: github.com/microcosm-cc/bluemonday
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-02 15:49:33 -04:00
Deluan
12818fb590 Make song/album/artist endpoints read-only 2021-11-02 14:44:50 -04:00
Deluan
aaeaa3c590 Fix playlist filter 2021-11-02 12:56:43 -04:00
Deluan
053909196c More info in scrobbling logs 2021-11-02 12:25:29 -04:00
Deluan
6a87fc88f7 Ignores invalid timestamps in requests (use current time)
Fix this issue: https://www.reddit.com/r/navidrome/comments/ql3imf/scrobbling_fails_when_using_substreamer/
2021-11-02 10:33:40 -04:00
Deluan
975986ab16 Add bulk action to make playlists private/public
Better responsiveness
2021-11-01 21:27:36 -04:00
Deluan
778f474d26 Use new rest lib (Update receives all columns that need to be updated) 2021-11-01 21:27:36 -04:00
Deluan
b2acec0a09 When externals services are disabled, only disable UILoginBackgroundURL if it is not set by the user 2021-11-01 09:11:32 -04:00
Deluan
e7202339af Ignore empty lines in M3U files 2021-10-31 20:43:30 -04:00
Deluan
8c2e4da396 Fix dateLoved in criteria. Now log invalid field names in criteria 2021-10-31 20:31:11 -04:00
Deluan
a4d3bf42a7 Remove some duplicated code 2021-10-31 15:08:06 -04:00
Deluan
765557d739 Remove "Show" button from PlaylistEdit view 2021-10-31 15:08:06 -04:00
Deluan
86afd16cc8 Allow changing playlist's owner. Relates to #698 2021-10-31 15:08:06 -04:00
Deluan
133fed344f Add owner_id to playlist 2021-10-31 15:08:06 -04:00
Deluan
84bbcdbfc2 Add artist image lightbox 2021-10-30 20:05:01 -04:00
Deluan
1823159b25 New config to disable all external integrations. Closes #102 2021-10-30 18:03:46 -04:00
Deluan
0b5ed9eb80 Update ListenBrainz Portuguese translations 2021-10-30 16:19:39 -04:00
Steve Richter
a56d5bc850 Listenbrainz scrobbling (#1424)
* Refactor session_keys to its own package

* Adjust play_tracker

- Don't send external NowPlaying/Scrobble for tracks with unknown artist
- Continue to the next agent on error

* Implement ListenBrainz Agent and Auth Router

* Implement frontend for ListenBrainz linking

* Update listenBrainzRequest

- Don't marshal Player to json
- Rename Track to Title

* Return ErrRetryLater on ListenBrainz server errors

* Add tests for listenBrainzAgent

* Add tests for ListenBrainz Client

* Adjust ListenBrainzTokenDialog to handle errors better

* Refactor listenbrainz.formatListen and listenBrainzRequest structs

* Refactor agent auth_routers

* Refactor session_keys to agents package

* Add test for listenBrainzResponse

* Add tests for ListenBrainz auth_router

* Update ListenBrainzTokenDialog and auth_router

* Adjust player scrobble toggle
2021-10-30 12:17:42 -04:00
Steve Richter
ccc871d1f7 Only reset player scrobbled state on track change or end (#1432)
* Only reset player scrobbled state on track change or end

* Only reset player start time on track change or end
2021-10-30 12:09:40 -04:00
Deluan
d3e142233b Fix TypeError: Cannot read properties of undefined (reading 'length') 2021-10-29 18:10:17 -04:00
Deluan
a42aeff88d Optimize queries by path, should speed up the scanner a bit 2021-10-29 13:11:51 -04:00
Deluan
7cdbc04c5e Update caniuse-lite 2021-10-29 11:49:10 -04:00
Deluan
f3fae7e233 Optimize basic media_file query, avoiding adding "group by" or joining with genres if not required 2021-10-29 09:50:22 -04:00
Deluan
074732b1dc Filter playlists by names and comments 2021-10-28 13:58:06 -04:00
Deluan
66a9cbb7d9 Remove temp folders after tests 2021-10-28 10:40:31 -04:00
Deluan
fa3471f527 Simplify resources code, enabling any resource to be overridden (not just translations) 2021-10-28 10:25:25 -04:00
Deluan
9072412812 Fix translations on Windows 2021-10-28 09:41:37 -04:00
Deluan
cca32360db Use refetch when changing the playlist (as opposed to a full refresh) 2021-10-27 20:53:58 -04:00
Deluan
85d48478e8 Add .mka file format. Only works with ffmpeg extractor 2021-10-27 15:00:32 -04:00
Deluan
2183eb6498 Should not allow changing sort order in Album songs view 2021-10-27 14:35:58 -04:00
Deluan
ea435d0f60 Fix error on empty playlists. Simplify code for some operations 2021-10-27 09:50:24 -04:00
Deluan
f645c4769c Fix double escaped lyrics and comments 2021-10-26 19:33:21 -04:00
Deluan
5e87280750 Load playlist track genres 2021-10-26 18:46:08 -04:00
Deluan
526b6597c8 Remove duplication for loading tracks 2021-10-26 18:34:21 -04:00
Deluan
5dce499d6d Fix/Optimized Playlist tracks deletion 2021-10-26 14:05:28 -04:00
Deluan
fbd87ba577 Fix console error "Cannot convert undefined or null to object PlaylistsSubMenu" 2021-10-26 14:05:05 -04:00
Deluan
63b5191ea7 Fix lint 2021-10-26 10:57:59 -04:00
Deluan
af00503b77 Optimize playlist updates 2021-10-26 10:45:14 -04:00
Steve Richter
85185e3b98 Misc small changes (#1433)
* Fix React key warning in HelpDialog

* Change "lyric" to "lyrics" in en.json
2021-10-26 08:57:20 -04:00
Deluan
83eaafcbfb Add dateLoved Criteria field 2021-10-25 16:44:59 -04:00
Deluan
93ce0b5683 Fix Genre field and Contains/NotContains/StartsWith/EndsWith in Criteria (Smart Playlists) 2021-10-25 16:17:03 -04:00
Deluan
47549ecfc1 Increase updatePlaylist chunk to 100 tracks 2021-10-25 13:00:46 -04:00
Deluan
ed1ca65ad5 Show hotkeys as chips, for easier reading 2021-10-25 11:14:43 -04:00
Deluan
8d6b5f9d02 Speed up Subsonic GetPlaylist (by optimizing loadTracks) 2021-10-25 11:14:20 -04:00
Deluan
76fdcd112b Tweak SimilarSongs algorithm to prioritize the requested main artist 2021-10-24 18:04:40 -04:00
Deluan
18e1c169f9 Don't read the whole smart playlist file in memory 2021-10-24 14:41:08 -04:00
whorfin
4bc4daa68f Improve git-vs-tarball detection (#1423)
* Extract version from directory name if .git dir is missing

* Avoid using shell

* Remove .gitinfo build from pipeline

* Fix git-detecting rule to be robust in presence of setup-git
2021-10-23 21:27:19 -04:00
Deluan
cc1659aa73 Better way to match top songs from external sources (Last.fm) 2021-10-23 20:26:30 -04:00
Deluan
31c598de07 Fix drag-n-drop from a playlist, also fix useDrag memoization 2021-10-23 20:25:28 -04:00
Deluan
2e2a647e67 Make SmartPlaylists read-only 2021-10-23 20:25:28 -04:00
Deluan
d169f54e7d Rename hasCoverArt field in criteria 2021-10-23 20:25:28 -04:00
Deluan
1494be9aaa Add playCount and playDate columns to album songs list 2021-10-23 20:25:28 -04:00
Deluan
c73f64ee3a Removed unused code 2021-10-23 20:25:28 -04:00
Deluan
806b13cf42 Update stats of Smart Playlist when it is created
Also fix loadTracks
2021-10-23 20:25:28 -04:00
Deluan
2c860edeb5 Don't import invalid .nsp files 2021-10-23 20:25:28 -04:00
Deluan
6a550dab77 Use new Criteria and remove SmartPlaylist struct 2021-10-23 20:25:28 -04:00
Deluan
3972616585 New Criteria API 2021-10-23 20:25:28 -04:00
Deluan
d0ce030386 Add PlayCount and PlayDate columns to PlaylistSongs 2021-10-23 20:25:28 -04:00
Deluan
947353610c Include never played songs in the "not in the last" operator 2021-10-23 20:25:28 -04:00
Deluan
2b57b98a4b Fix smart playlist refreshing only after the tracks were loaded 2021-10-23 20:25:28 -04:00
Deluan
1a96e9fe65 Import smart playlists (extension .nsp) 2021-10-23 20:25:28 -04:00
Deluan
21da1df4ea Cache smart playlist refreshes for 5 seconds 2021-10-23 20:25:28 -04:00
Deluan
d21932bd1b First version of SmartPlaylists being generated on demand 2021-10-23 20:25:28 -04:00
Deluan
c72add516a Add methods to Playlist model
Also, don't load genres for Playlists tracks (not necessary for now)
2021-10-23 20:25:28 -04:00
Deluan
d200933b68 Reduce number of queries for some playlists operations.
Also allow admins to update/delete playlists from other users in the Subsonic API. Closes #1366
2021-10-23 20:25:28 -04:00
Deluan
943082ef4e Fix time-based tests (again) 2021-10-23 20:25:28 -04:00
Deluan
c3fb4e1282 Fix rules serialization 2021-10-23 20:25:28 -04:00
Deluan
9c8f779f42 Fix time-based tests 2021-10-23 20:25:28 -04:00
Deluan
815623715e Load SmartPlaylists rules from DB 2021-10-23 20:25:28 -04:00
Deluan
7221b49b98 More tests 2021-10-23 20:25:28 -04:00
Deluan
cf8d08ec26 Initial drafts for Smart Playlists 2021-10-23 20:25:28 -04:00
Deluan
2a756eab88 Show external links on all resolutions but mobile 2021-10-21 10:30:53 -04:00
Deluan
104679ca6e Guard against record being undefined. Fix error Cannot read properties of undefined (reading 'id') 2021-10-19 20:22:56 -04:00
Dheeraj Lalwani
5621551dd0 Adds Lyrics Support to Subsonic API (#1379)
* Add function 'isSynced' that identifies if lyrics are synced or not and add tests for the same

* implement 'getLyrics' which returns lyrics if they exist

Signed-off-by: Dheeraj Lalwani <lalwanidheeraj1234@gmail.com>

* remove timestamps frorom the the lyrics if they are synced, fix filters & clean up code

Signed-off-by: Dheeraj Lalwani <lalwanidheeraj1234@gmail.com>

* add snapshot tests for the 'Lyrics' response & add some clean up

Signed-off-by: Dheeraj Lalwani <lalwanidheeraj1234@gmail.com>

* add tests for 'GetLyrics' function

Signed-off-by: Dheeraj Lalwani <lalwanidheeraj1234@gmail.com>

* update the snapshot test & the test for 'GetLyrics' function

Signed-off-by: Dheeraj Lalwani <lalwanidheeraj1234@gmail.com>
2021-10-19 16:33:06 -04:00
Deluan
3214783ce9 Remove double-retching playlist's tracks 2021-10-19 12:54:21 -04:00
Chirag Lulla
34b01c2cbf Display lyrics on UI if synced lyrics present in metadata (#1406)
Signed-off-by: Chirag Lulla <lullachirag239@gmail.com>
2021-10-19 10:21:20 -04:00
Deluan
0d2a8f5338 Enable new Artist Page by default 2021-10-17 11:07:59 -04:00
Deluan
b7fedddfd8 Guard against record being undefined. Fix error Cannot read properties of undefined (reading 'albumId') 2021-10-16 20:29:31 -04:00
Dnouv
1d742cf8c7 Artist page improvements (#1391)
* Seperate mobile desktop components

* Fix err

* Rename classes and fix some styles

* Add lastFM button and remove console log

* Add Mbiz Icon

* render bio as dangerouslySetInnerHTML and remove unused css classes

* Add Fav and Stars

* Remove unstandardised class selector

* Remove ext link from m view

* Fix naming and simplify rounded styling

* Refactor ArtistShow:

- Extracted DesktopArtistDetails to its own file
- Removed album count as it was incorrect, it is not considering compilations
- Show bio and image from Native API, if it is available, before calling `getArtistInfo`

Co-authored-by: Deluan <deluan@navidrome.org>
2021-10-15 21:02:11 -04:00
Deluan
7505b5c554 Bump GoLang to 1.17.2 2021-10-13 09:53:44 -04:00
Deluan
174ad9e9da Fix ffmpeg bitrate parsing for flac files 2021-10-12 22:02:24 -04:00
certuna
ba0ee6aba4 Rename manifest.json to manifest.webmanifest (#1399)
* Rename manifest.json to manifest.webmanifest

browser consoles keep complaining that the manifest doesn't have the `.webmanifest` extension.

* FIx manifest.webmanifest references

Co-authored-by: Deluan <deluan@navidrome.org>
2021-10-12 20:06:09 -04:00
Deluan Quintão
6b38acad49 Update README.md 2021-10-08 10:51:09 -04:00
Deluan
ee8943f338 Fix semantic classes for currently playing song
Fix #1364
2021-10-07 19:49:27 -04:00
Serguey Parkhomovsky
86a87b4bb1 Fix default volume (#1395)
With the update in #1378, the default volume is now erroneously set to 25% instead of 50%. Remove the Math.pow and set it to 50% instead.
2021-10-07 17:21:08 -04:00
Deluan
8bbb878bb3 Add Finnish translation 2021-10-06 18:11:02 -04:00
Deluan Quintão
8591a9acdf Update translations (#1383)
* Add Persian translation

* Update cs.json (POEditor.com)

* Update fr.json (POEditor.com)

* Update ja.json (POEditor.com)

* Update pt.json (POEditor.com)

* Update ru.json (POEditor.com)

* Update sl.json (POEditor.com)

* Fix "Shared Playlists" pt translation

* Update de.json (POEditor.com)

* Update it.json (POEditor.com)

* Update sl.json (POEditor.com)

* Update es.json (POEditor.com)

* Update zh-Hant.json (POEditor.com)

* Update th.json (POEditor.com)
2021-10-06 18:08:29 -04:00
Deluan
b6e30cd01f Return playlists sorted in getPlaylists Subsonic endpoint 2021-10-05 14:47:57 -04:00
Deluan Quintão
058e7e4374 Add logo to README 2021-10-05 12:15:44 -04:00
Deluan
152836a01c Bump @testing-library/react from 12.1.1 to 12.1.2 in /ui 2021-10-04 17:30:01 -04:00
Deluan
6139338e80 Bump react-icons from 4.2.0 to 4.3.1 in /ui 2021-10-04 17:29:27 -04:00
Deluan
f0c11916ca Revert: Small optimization in genre mapping 2021-10-04 17:28:45 -04:00
Dnouv
a6311259fd Fix layout error in ArtistShow (#1387) 2021-10-04 17:14:38 -04:00
Deluan
dbde0ffa0c Bump github.com/djherbis/atime to v1.1.0 2021-10-03 22:50:25 -04:00
Deluan
fba733708c Sort songs by artist/album/disc/track_number before adding to playlist 2021-10-02 21:55:45 -04:00
Deluan
e673360087 Limit number of playlists displayed in the sidebar, to avoid UI freezes 2021-10-02 21:39:33 -04:00
Deluan
2b105ca77b Enable DevSidebarPlaylists by default.
Closes #771
2021-10-02 13:56:42 -04:00
Deluan
9c29ee3651 Check permissions before adding songs to playlists 2021-10-02 13:23:17 -04:00
Deluan
6c3e45de41 Add songs to playlists with drag and drop 2021-10-02 13:14:33 -04:00
dependabot[bot]
2ab4647420 Bump golang.org/x/tools from 0.1.6 to 0.1.7 (#1382)
Bumps [golang.org/x/tools](https://github.com/golang/tools) from 0.1.6 to 0.1.7.
- [Release notes](https://github.com/golang/tools/releases)
- [Commits](https://github.com/golang/tools/compare/v0.1.6...v0.1.7)

---
updated-dependencies:
- dependency-name: golang.org/x/tools
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-01 16:38:04 -04:00
Deluan Quintão
91e36a2c18 Check goimports in the pipeline (#1381)
* Check goimports in the pipeline

* Check goimports in the pipeline

* Check goimports in the pipeline

* go mod tidy

* wip

* wip

* Fix goimports and go:build tags

* Run golangci-lint before goimports
2021-10-01 15:32:24 -04:00
dependabot[bot]
0cbba80284 Bump react-router-dom from 5.2.0 to 5.3.0 in /ui (#1330)
Bumps [react-router-dom](https://github.com/ReactTraining/react-router) from 5.2.0 to 5.3.0.
- [Release notes](https://github.com/ReactTraining/react-router/releases)
- [Changelog](https://github.com/remix-run/react-router/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ReactTraining/react-router/compare/v5.2.0...v5.3.0)

---
updated-dependencies:
- dependency-name: react-router-dom
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-01 10:34:37 -04:00
Deluan
f9d910473f Bump react-admin to 3.18.3 2021-10-01 09:45:28 -04:00
Igor Rzegocki
be3a6dc7a3 Use local copy of workbox service worker scripts (#1358)
* Use local copy of workbox service worker scripts

* Refactor workbox integration:

- Only add prod js, without maps. Reduces the size from 170k to 24k
- Removed it from build. As it is small now, we can add it to source, and have a script to just update it whenever it is required
- Fixed relative paths in navidrome-service-worker.js, should now work with BaseUrl != ''

Co-authored-by: Deluan <deluan@navidrome.org>
2021-10-01 09:14:15 -04:00
Deluan
b1e7760996 Preload next song 2021-10-01 08:43:59 -04:00
Deluan
ad45ab4a04 Fix genre update chunking 2021-10-01 08:43:59 -04:00
Serguey Parkhomovsky
24fef584ad Bump react-jinke-music-player from 4.24.0 to 4.24.2 (#1378)
This should fix #1367.
2021-10-01 08:42:02 -04:00
Deluan
e17d436902 Do not attach Genres to the "Various Artists" artist 2021-09-27 21:55:33 -04:00
Deluan
73659e5669 Change "Build" link to point to the latest build artifacts from master 2021-09-27 18:17:53 -04:00
Deluan
71b1e7f870 Bump github.com/lestrrat-go/jwx from 1.2.6 to 1.2.7 2021-09-27 13:29:31 -04:00
Deluan
694056010c Bump @testing-library/react from 12.1.0 to 12.1.1 in /ui 2021-09-27 13:27:59 -04:00
Deluan
4fda895a64 Bump blueimp-md5 from 2.18.0 to 2.19.0 in /ui 2021-09-27 13:26:31 -04:00
Deluan
f664af5559 Bump react-admin from 3.18.1 to 3.18.2 in /ui 2021-09-27 13:26:31 -04:00
Deluan
c6868ff8a0 Don't show Artist Page for "Various Artists" 2021-09-27 11:52:23 -04:00
Deluan
0b65a4e34e Fix comment word wrapping 2021-09-27 09:48:31 -04:00
Deluan
24872e6c2a Fix biography word wrapping and requests for undefined resource 2021-09-27 09:47:16 -04:00
Deluan
b4e5c662dc Fix JS console warning 2021-09-26 17:36:30 -04:00
Deluan
6752e0a17d Fix harmless error message in logs when ScanSchedule set was "0"
Message:
`ERRO[0000] Error scheduling periodic scan                error="expected exactly 5 fields, found 1: [0]"`
2021-09-26 15:57:27 -04:00
Deluan
5680e53949 Update genres in chunks. Should fix #1368 2021-09-26 15:55:52 -04:00
Dnouv
482c2dec0c Artist Detail Page (first cut) (#1287)
* Configure fetching from API and route

* pretty

* Remove errors

* Remove errors

* Remove errors

* Complete page for Desktop view

* Fix error

* Add xs Artist page

* Remove unused import

* Add styles for theme

* Change route path

* Remove artId useEffect array

* Remove array

* Fix cover load err

* Add redirect on err

* Remove route

* What's in a name? consistency :)

* Fix err

* Fix UI changes

* Fetch album from resource

* Renaming done

* Review changes

* Some touch-up

* Small refactor, to make naming and structure more consistent with AlbumShow

* Make artist's album list similar to original implementation

* Reuse AlbumGridView, to avoid duplication

* Add feature flag to enable new Artist Page, default false

* Better biography styling. Small refactorings,

* Don't encode quotes and other symbols

* Moved AlbumShow to correct folder

Co-authored-by: Deluan <deluan@navidrome.org>
2021-09-26 15:32:40 -04:00
caiocotts
210dc6b12e Add x-total-count to Subsonic API getAlbumList (#1360)
* Add x-total-count to Subsonic API getAlbumList

* Rename variable

Co-authored-by: Deluan <deluan@navidrome.org>
2021-09-21 20:40:39 -04:00
Deluan
73a2271cdd Small optimization in genre mapping 2021-09-21 13:37:44 -04:00
Samarjeet
0c0bd2967d Replace expanded with a dialog (#1258)
* Replace expanded with a dialog

* Change `info` label to "Get Info"

* Rename things for consistency

Co-authored-by: Deluan <deluan@navidrome.org>
2021-09-20 20:30:43 -04:00
Deluan
15ae3d47cf Only apply audioStreamRx once 2021-09-20 19:33:50 -04:00
Deluan
1365adbb39 Support 7.1 (8) channels 2021-09-20 19:33:50 -04:00
Miguel A. Arroyo
e12a14a87d feat: Adds Audio Channel Metadata - #1036 2021-09-20 19:33:50 -04:00
Deluan
0079a9b938 Close Sidebar when going to Playlists list 2021-09-20 19:00:37 -04:00
Deluan
892c2bfd58 Revert "Disable mini-player (bubble) dragging. Should fix #1217"
This reverts commit abf6318a8b.
2021-09-20 18:24:18 -04:00
dependabot[bot]
5fb7328965 Bump golang.org/x/tools from 0.1.5 to 0.1.6
Bumps [golang.org/x/tools](https://github.com/golang/tools) from 0.1.5 to 0.1.6.
- [Release notes](https://github.com/golang/tools/releases)
- [Commits](https://github.com/golang/tools/compare/v0.1.5...v0.1.6)

---
updated-dependencies:
- dependency-name: golang.org/x/tools
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-09-20 18:18:45 -04:00
Deluan
86479a6d06 More info when recovering from panic 2021-09-20 18:16:22 -04:00
Deluan
76bd20e8ff Recover from any possible taglib panics. Fixes #1343 2021-09-20 18:09:39 -04:00
dependabot[bot]
7a15ed0740 Bump prettier from 2.4.0 to 2.4.1 in /ui
Bumps [prettier](https://github.com/prettier/prettier) from 2.4.0 to 2.4.1.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/2.4.0...2.4.1)

---
updated-dependencies:
- dependency-name: prettier
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-09-20 17:45:50 -04:00
dependabot[bot]
d574df5fd7 Bump github.com/go-chi/jwtauth/v5 from 5.0.1 to 5.0.2
Bumps [github.com/go-chi/jwtauth/v5](https://github.com/go-chi/jwtauth) from 5.0.1 to 5.0.2.
- [Release notes](https://github.com/go-chi/jwtauth/releases)
- [Commits](https://github.com/go-chi/jwtauth/compare/v5.0.1...v5.0.2)

---
updated-dependencies:
- dependency-name: github.com/go-chi/jwtauth/v5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-09-20 17:45:25 -04:00
dependabot[bot]
a7ace48efb Bump github.com/ReneKroon/ttlcache/v2 from 2.8.0 to 2.8.1
Bumps [github.com/ReneKroon/ttlcache/v2](https://github.com/ReneKroon/ttlcache) from 2.8.0 to 2.8.1.
- [Release notes](https://github.com/ReneKroon/ttlcache/releases)
- [Changelog](https://github.com/ReneKroon/ttlcache/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ReneKroon/ttlcache/compare/v2.8.0...v2.8.1)

---
updated-dependencies:
- dependency-name: github.com/ReneKroon/ttlcache/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-09-20 17:44:52 -04:00
dependabot[bot]
84d98010d3 Bump github.com/spf13/viper from 1.8.1 to 1.9.0
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.8.1 to 1.9.0.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.8.1...v1.9.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/viper
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-09-20 17:44:02 -04:00
Deluan
e63804fbdd Use newer versions of node in the pipeline 2021-09-18 22:38:55 -04:00
Deluan
a5101fa9b4 Use npm dependencies cache from setup-node@v2 2021-09-18 22:38:55 -04:00
dependabot[bot]
f2ed3f2d86 Bump prettier from 2.3.2 to 2.4.0 in /ui (#1341)
Bumps [prettier](https://github.com/prettier/prettier) from 2.3.2 to 2.4.0.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/2.3.2...2.4.0)

---
updated-dependencies:
- dependency-name: prettier
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-09-13 16:12:29 -04:00
dependabot[bot]
b23ab1ccec Bump @testing-library/react from 12.0.0 to 12.1.0 in /ui (#1342)
Bumps [@testing-library/react](https://github.com/testing-library/react-testing-library) from 12.0.0 to 12.1.0.
- [Release notes](https://github.com/testing-library/react-testing-library/releases)
- [Changelog](https://github.com/testing-library/react-testing-library/blob/main/CHANGELOG.md)
- [Commits](https://github.com/testing-library/react-testing-library/compare/v12.0.0...v12.1.0)

---
updated-dependencies:
- dependency-name: "@testing-library/react"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-09-13 16:10:07 -04:00
Deluan
abf6318a8b Disable mini-player (bubble) dragging. Should fix #1217 2021-09-13 11:17:06 -04:00
Deluan
b55f3a6946 Add paddingBottom to the whole sidebar menu, to avoid playlists to be covered by the player 2021-09-12 21:24:07 -04:00
Deluan
ab2912b4fa Only import playlists from configured paths in option PlaylistsPath. Closes #1181
Syntax is Ant-style Globs, with support for '**' (any subfolder). Default: '.:**' (or '.;**' in Windows`, meaning all folders and subfolders under `MusicFolder`
2021-09-12 21:07:51 -04:00
Deluan
9f00aad216 Upgrade to GoLang 1.17.1 2021-09-12 14:14:33 -04:00
Deluan Quintão
79363d6c07 Move Playlists to the sidebar menu (#1339)
* Show playlists in sidebar menu

* Fix menu

* Refresh playlist submenu when adding new playlist

* Group shared playlists below user's playlists

* Fix text overflow in menu options

* Add button in playlist menu to go to Playlists list

* Add config option `DevSidebarPlaylists` to enable this feature (default false)
2021-09-11 13:11:15 -04:00
Deluan
a7017e4bb0 Fix JS console warning 2021-09-10 18:04:38 -04:00
Deluan
dc0ec32dbf Fix menu items highlight 2021-09-10 16:17:08 -04:00
Salman Inayat
06b1a1a25c Album size overflow fixed (#1071)
* Added back button

* Added back button

* Added back button

* Fixed Album size overflow

* Fixed Album size overflow

* Fixed album size overflowing

* Fixed album size overflowing

* Fixed album size overflowing

* Fixed album size overflow on small screen

* Changes reverted in PlayerEdit.js

* prettier formatting issue resolved

Co-authored-by: Deluan <deluan@navidrome.org>
2021-09-09 11:56:48 -04:00
Deluan
6ac2fefaf3 Make AppBar stick on scroll 2021-09-09 11:21:16 -04:00
Samarjeet
2e921cd793 Fix sidebar scroll height (#1338)
* Fix sidebar scroll height

* Prettier

Co-authored-by: Deluan <deluan@navidrome.org>
2021-09-09 09:33:38 -04:00
Deluan Quintão
c2251e6ddc Update GoLang to 1.17 (#1295)
* Update GoLang to 1.17

* Rename pipeline jobs
2021-09-09 00:26:56 -04:00
Deluan
25aab8bcb5 go mod tidy 2021-09-09 00:17:43 -04:00
Deluan
94083f85d2 Bump @testing-library/react-hooks version 2021-09-09 00:16:44 -04:00
Deluan
f0ef5187b2 Bump react-redux version 2021-09-09 00:16:13 -04:00
Deluan
79e79b6366 Bump github.com/go-chi/chi 2021-09-09 00:15:16 -04:00
Deluan
a961ffe2e1 Bump github.com/golangci/golangci-lint 2021-09-09 00:14:18 -04:00
Deluan
402aaaaf45 Bump github.com/lestrrat-go/jwx version 2021-09-09 00:13:39 -04:00
Deluan
5c7784f842 Bump github.com/cespare/reflex version 2021-09-09 00:13:06 -04:00
Deluan
a4e96d25a8 Pin Node to 16.8 as a workaround to https://github.com/nodejs/node/issues/40030 2021-09-08 23:28:28 -04:00
Deluan
8444c28bed Upgrade react-admin to 3.18.1
This makes the sidebar fixed when users scroll vertically. This supersedes #1024, even though it also hides the hamburger menu...
2021-09-08 22:50:03 -04:00
Tucker Kern
fb11080545 Improve performance of placeholder images (#1325)
* Don't include updatedAt field when fetching album art placeholder. This will allow browers to cache the place holder

* Apply resizing to placeholder image

* Fix issues discovered by CI linter and prettier

* Updates from PR review
2021-09-06 22:34:37 -04:00
whorfin
173b30cd59 Extract version from directory name if .git dir is missing (#1327)
* Extract version from directory name if .git dir is missing

* Avoid using shell

* Remove .gitinfo build from pipeline
2021-09-06 21:26:04 -04:00
Deluan
af7c87dd7b Give a warning on commands that do not build the frontend.
This is to avoid confusions like this:
https://github.com/navidrome/navidrome/issues/1297#issuecomment-913007331
2021-09-05 21:39:19 -04:00
Artyom
8df056b797 ru.json update (#1320) 2021-09-05 10:56:54 -04:00
caiocotts
54f98497c2 Use wchar_t for TagLib filenames on Windows (#1310)
* Use wchar_t for tagLib filenames on Windows

* Make TagLib default extractor for all platforms.

* Organize imports

Co-authored-by: Deluan <deluan@navidrome.org>
2021-08-28 19:35:54 -04:00
dependabot[bot]
c55e65902b Bump github.com/onsi/gomega from 1.15.0 to 1.16.0 (#1300)
Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.15.0 to 1.16.0.
- [Release notes](https://github.com/onsi/gomega/releases)
- [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/gomega/compare/v1.15.0...v1.16.0)

---
updated-dependencies:
- dependency-name: github.com/onsi/gomega
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-08-23 20:50:46 -04:00
dependabot[bot]
d37231f319 Bump github.com/ReneKroon/ttlcache/v2 from 2.7.0 to 2.8.0 (#1298)
Bumps [github.com/ReneKroon/ttlcache/v2](https://github.com/ReneKroon/ttlcache) from 2.7.0 to 2.8.0.
- [Release notes](https://github.com/ReneKroon/ttlcache/releases)
- [Changelog](https://github.com/ReneKroon/ttlcache/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ReneKroon/ttlcache/compare/v2.7.0...v2.8.0)

---
updated-dependencies:
- dependency-name: github.com/ReneKroon/ttlcache/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-08-23 20:47:36 -04:00
dependabot[bot]
47fa32ec93 Bump github.com/golangci/golangci-lint from 1.41.1 to 1.42.0 (#1299)
Bumps [github.com/golangci/golangci-lint](https://github.com/golangci/golangci-lint) from 1.41.1 to 1.42.0.
- [Release notes](https://github.com/golangci/golangci-lint/releases)
- [Changelog](https://github.com/golangci/golangci-lint/blob/master/CHANGELOG.md)
- [Commits](https://github.com/golangci/golangci-lint/compare/v1.41.1...v1.42.0)

---
updated-dependencies:
- dependency-name: github.com/golangci/golangci-lint
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-08-23 17:41:55 -04:00
Deluan
cf042ed83d Fix random volume changes 2021-08-22 12:18:26 -04:00
Deluan
d481864035 Some small refactorings 2021-08-22 12:16:49 -04:00
Deluan
c2927e105b Fix loadSimilar method, was causing "internal error". Fix #1293 2021-08-21 19:37:00 -04:00
Deluan
6983390ca3 Setup git hooks when running make setup 2021-08-21 13:45:34 -04:00
Deluan
143f5ba9d5 Import song duration with hundredths when using TagLib
This is how ffmpeg extractor currently works, and it makes album durations more precise.
2021-08-20 21:42:01 -04:00
Deluan
05e27095b2 Fix getTopSongs endpoint 2021-08-19 08:17:22 -04:00
Tucker Kern
aa72d3d41b Add missing song information to players and apply EnableCoverAnimation to mobile player. (#1268)
* Disable mobile player cover animation when EnableCoverAnimation is set to false. Also increase cover art size and remove rounded borders.

* Display song album and year in mobile player view

* Remove default singer element from mobile player and reduce vertical white space

* Only add song year if it exists

* Add song year to desktop player when present

* Increase non-animated cover size to 85% and set a limit on the width of 600px.

* Explain what what the styles impact

* Remove unused style for songArtist

* Apply prettier

* Adjust player styles to handle nonsquare album art better. Should probably push this upstream too

* Also fix desktop player's handling of non square cover art.
2021-08-17 13:57:48 -04:00
ToadCast
a20bd5fe05 Correct some french translations (#1278) 2021-08-13 20:27:37 -04:00
Deluan Quintão
99aeaabf7d Update translations (#1285)
* Update es.json (POEditor.com)

* Update zh-Hans.json (POEditor.com)

* Update zh-Hant.json (POEditor.com)

* Update cs.json (POEditor.com)

* Update nl.json (POEditor.com)

* Update fr.json (POEditor.com)

* Update de.json (POEditor.com)

* Update it.json (POEditor.com)

* Update ja.json (POEditor.com)

* Update pl.json (POEditor.com)

* Update pt.json (POEditor.com)

* Update sl.json (POEditor.com)

* Update es.json (POEditor.com)

* Update th.json (POEditor.com)

* Update uk.json (POEditor.com)
2021-08-13 20:27:18 -04:00
dependabot[bot]
0a5f966047 Bump redux from 4.1.0 to 4.1.1 in /ui
Bumps [redux](https://github.com/reduxjs/redux) from 4.1.0 to 4.1.1.
- [Release notes](https://github.com/reduxjs/redux/releases)
- [Changelog](https://github.com/reduxjs/redux/blob/master/CHANGELOG.md)
- [Commits](https://github.com/reduxjs/redux/compare/v4.1.0...v4.1.1)

---
updated-dependencies:
- dependency-name: redux
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-13 20:11:05 -04:00
dependabot[bot]
5338567346 Bump github.com/onsi/gomega from 1.14.0 to 1.15.0
Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.14.0 to 1.15.0.
- [Release notes](https://github.com/onsi/gomega/releases)
- [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/gomega/compare/v1.14.0...v1.15.0)

---
updated-dependencies:
- dependency-name: github.com/onsi/gomega
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-13 19:57:12 -04:00
dependabot[bot]
50526024b2 Bump github.com/lestrrat-go/jwx from 1.2.4 to 1.2.5
Bumps [github.com/lestrrat-go/jwx](https://github.com/lestrrat-go/jwx) from 1.2.4 to 1.2.5.
- [Release notes](https://github.com/lestrrat-go/jwx/releases)
- [Changelog](https://github.com/lestrrat-go/jwx/blob/main/Changes)
- [Commits](https://github.com/lestrrat-go/jwx/compare/v1.2.4...v1.2.5)

---
updated-dependencies:
- dependency-name: github.com/lestrrat-go/jwx
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-13 19:56:57 -04:00
Deluan
db4165f27c Bum react-admin to 3.17.2. Fix SearchInput border 2021-08-13 19:45:18 -04:00
Deluan
a8cf887e87 Adjust icon size 2021-08-03 22:01:57 -04:00
Deluan Quintão
63000ab5ae Update en.json (POEditor.com) 2021-08-03 21:41:32 -04:00
Deluan
b44ef81c6f Allow 0 value to disable ScanSchedule.
Seems that Viper does not recognize empty environment vars as valid values

Closes #1274
2021-08-03 19:37:36 -04:00
caio
e9d0abe0bc Support local paths as urls in playlists. 2021-08-02 23:53:47 -04:00
Deluan
bcafe88ef9 Don't autoplay when reloading play queue from Redux store 2021-08-01 19:49:17 -04:00
dependabot[bot]
1710730b45 Bump github.com/kr/pretty from 0.2.1 to 0.3.0 (#1267)
Bumps [github.com/kr/pretty](https://github.com/kr/pretty) from 0.2.1 to 0.3.0.
- [Release notes](https://github.com/kr/pretty/releases)
- [Commits](https://github.com/kr/pretty/compare/v0.2.1...v0.3.0)

---
updated-dependencies:
- dependency-name: github.com/kr/pretty
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-08-01 12:56:55 -04:00
Deluan
aa1571e074 Remove unused AnnotatedModel interface 2021-08-01 12:04:45 -04:00
Deluan
c831dc4cdf Use structs lib to map models to DB. Fix #1266 2021-08-01 12:04:45 -04:00
Deluan
344d7a4392 Inject DB into DataStore, instead of hardcode the dependency 2021-07-31 20:15:20 -04:00
Deluan
c0fc36da63 Make album genres clickable 2021-07-27 13:22:35 -04:00
Deluan
e68b22ea5d Don't send invalid scrobbles when clearing the player's queue 2021-07-26 16:50:50 -04:00
Sitansh Rajput
fb4eefced5 "Add to Playlist" on AlbumList actions (#1257)
* added a dependency npm was complaining about

added playlist to album actions

* removed chokidar dependency

Co-authored-by: Skrtansh Rajput <srajput@alienvault.com>
2021-07-26 15:00:38 -04:00
Deluan
615cac2ec4 Extract ExternalLinks into its own component 2021-07-26 13:25:31 -04:00
dependabot[bot]
72f9e3e80a Bump @testing-library/user-event from 13.2.0 to 13.2.1 in /ui
Bumps [@testing-library/user-event](https://github.com/testing-library/user-event) from 13.2.0 to 13.2.1.
- [Release notes](https://github.com/testing-library/user-event/releases)
- [Changelog](https://github.com/testing-library/user-event/blob/main/CHANGELOG.md)
- [Commits](https://github.com/testing-library/user-event/compare/v13.2.0...v13.2.1)

---
updated-dependencies:
- dependency-name: "@testing-library/user-event"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-26 13:25:19 -04:00
Deluan
763bcafdac Use Tooltip in links to external sites 2021-07-24 19:52:15 -04:00
Deluan
5b81f7a73a Fix get song by id 2021-07-24 18:54:22 -04:00
Deluan
7bd506accc Retrieve all options for Genre filters 2021-07-24 16:00:27 -04:00
Deluan
b66c39451f Fix build 2021-07-24 11:45:46 -04:00
Deluan
4ed01bad86 Use ffmpeg extractor by default on Windows
This is to avoid issue with unicode chars in filenames. See #810
2021-07-24 11:35:49 -04:00
Deluan
d3975d206a Reorganize metadata extractors code 2021-07-24 11:10:19 -04:00
Deluan Quintão
6175629bb4 Build and release Docker image for Linux 32 bits platform (#1260) 2021-07-24 07:51:12 -04:00
Deluan
6c550819fd Use TagLib to detect whether a media file has embedded cover or not 2021-07-24 01:59:53 -04:00
Deluan
91325071a6 Change fallback extractor to taglib, the default option 2021-07-22 23:08:47 -04:00
Deluan
876dda83f2 Reduce number of calls to lstat.
Should make the scanner a bit faster, specially in networked filesystems
2021-07-21 22:17:37 -04:00
Deluan
86c0b422f6 Small refactorings 2021-07-21 12:46:30 -04:00
Deluan
1cef44a543 Show in the logs which mbid will be used if multiple mbids are found for album/artist 2021-07-21 11:12:03 -04:00
Deluan
4fcb238295 Fix "too many SQL variables" error in GetStarred endpoint 2021-07-21 10:45:52 -04:00
Deluan
4f9d546da4 Abort startup if config file is invalid 2021-07-20 22:35:22 -04:00
Deluan
eeb14f0243 Removed unused function 2021-07-20 20:50:59 -04:00
Deluan
a89bdfbb8d Fix build 2021-07-20 20:22:15 -04:00
Deluan
8afa2cd833 Remove dependency of deprecated ioutil package 2021-07-20 20:12:28 -04:00
Deluan
774ad65155 Use fs.FS in MergeFS implementation 2021-07-20 19:54:44 -04:00
Deluan
7540881695 Small refactorings 2021-07-20 19:18:29 -04:00
Deluan
08840f6170 Simplify cover detection in roll-up code by left-joining synthesized table 2021-07-20 17:45:08 -04:00
Deluan
cddd1b3f6b Simplify genre roll-up code by left-joining synthesized tables 2021-07-20 17:45:08 -04:00
Deluan
bc6b175414 Make getGenre Subsonic endpoint returns genres sorted by counts 2021-07-20 17:45:08 -04:00
Deluan
b6e9ec4db4 Optimize GetAll genres query 2021-07-20 17:45:08 -04:00
Deluan
1471e1240d Show songs' genres as text instead of Chips 2021-07-20 17:45:08 -04:00
Deluan
95181d748d Fix rollup of track genres to albums and artists.
See: https://github.com/navidrome/navidrome/pull/1251#issuecomment-882343022
2021-07-20 17:45:08 -04:00
Deluan
254e5673e1 Fix log message about artist with Various Artists' mbid 2021-07-20 17:45:08 -04:00
Deluan
00e418cb2a Fix log message about multiple MBIDs 2021-07-20 17:45:08 -04:00
Deluan
2742977c63 Fix multiple id3v2.4 genres appearing as one big concatenated genre 2021-07-20 17:45:08 -04:00
Deluan
69f71be98a Add more tests 2021-07-20 17:45:08 -04:00
Deluan
58ee4c60ca Add Links to external sites 2021-07-20 17:45:08 -04:00
Deluan
21cd50d81c Fix aggregated values (count, size, duration) in roll-up queries 2021-07-20 17:45:08 -04:00
Deluan
054b5eafdb Add Genres as "Chips" in Album details and Song details 2021-07-20 17:45:08 -04:00
Deluan
e2233779f1 Force full rescan when adding multi-genres 2021-07-20 17:45:08 -04:00
Deluan
3a356499ae Fix lint error 2021-07-20 17:45:08 -04:00
Deluan
a0cd585401 Fix Count methods 2021-07-20 17:45:08 -04:00
Deluan
20b7e5c49b Add Genre filters to UI 2021-07-20 17:45:08 -04:00
Deluan
c56c7c865e Purge unused genres at the end of the scan 2021-07-20 17:45:08 -04:00
Deluan
b56e034ce3 Add multiple genres to Artists 2021-07-20 17:45:08 -04:00
Deluan
1d8607ef6a Remove unnecessary repositories methods 2021-07-20 17:45:08 -04:00
Deluan
5e54925520 Add multiple genres to Albums 2021-07-20 17:45:08 -04:00
Deluan
39da741a80 Add multiple genres to MediaFile 2021-07-20 17:45:08 -04:00
Deluan
7cd3a8ba67 Add genre tables, read multiple-genres from tags 2021-07-20 17:45:08 -04:00
Deluan
1f0314021e Change initial scan message log level 2021-07-19 16:53:58 -04:00
Deluan
19c2ef3803 Enable buffered scrobbles by default 2021-07-19 16:10:37 -04:00
dependabot[bot]
d886c63122 Bump react-image-lightbox from 5.1.1 to 5.1.4 in /ui (#1252)
Bumps [react-image-lightbox](https://github.com/frontend-collective/react-image-lightbox) from 5.1.1 to 5.1.4.
- [Release notes](https://github.com/frontend-collective/react-image-lightbox/releases)
- [Changelog](https://github.com/frontend-collective/react-image-lightbox/blob/master/CHANGELOG.md)
- [Commits](https://github.com/frontend-collective/react-image-lightbox/compare/v5.1.1...v5.1.4)

---
updated-dependencies:
- dependency-name: react-image-lightbox
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-07-19 13:20:44 -04:00
dependabot[bot]
5b828cd7ef Bump @testing-library/user-event from 13.1.9 to 13.2.0 in /ui (#1253)
Bumps [@testing-library/user-event](https://github.com/testing-library/user-event) from 13.1.9 to 13.2.0.
- [Release notes](https://github.com/testing-library/user-event/releases)
- [Changelog](https://github.com/testing-library/user-event/blob/main/CHANGELOG.md)
- [Commits](https://github.com/testing-library/user-event/compare/v13.1.9...v13.2.0)

---
updated-dependencies:
- dependency-name: "@testing-library/user-event"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-07-19 13:20:28 -04:00
Deluan
ef60db3a5f Bump github.com/lestrrat-go/jwx from 1.2.2 to 1.2.4 2021-07-19 13:17:55 -04:00
Deluan
882b02c747 Fix forceRescan not re-importing all tracks 2021-07-19 10:32:33 -04:00
Deluan
44e7502aef Log warning when artist has a MBID of Various Artists 2021-07-18 18:28:51 -04:00
Deluan
e61cf3217d Reapply the fix from #1054, but without getting into an infinite look in case of SMB fs errors. See #1164 2021-07-17 21:06:53 -04:00
Deluan
03ad6e972a Removed unused attributes in Last.fm responses 2021-07-16 21:06:47 -04:00
Deluan
eb8ffc6f76 Fix infinite loop when the fs fails. Closes #1164 2021-07-16 09:38:58 -04:00
Deluan
b0fc684cb6 Fix small lint errors found by gocritic 2021-07-15 13:43:03 -04:00
Deluan
8d56ec898e Use AlbumArtist tag even for compilations, when it is specified.
If the tracks' AlbumArtists are different, then use "Various Artists"
2021-07-15 11:53:08 -04:00
Deluan Quintão
5064cb2a46 Add git version info to release source (#1250) 2021-07-15 09:49:34 -04:00
Deluan
f78257235e Add option to have different loglevels per source folder/file 2021-07-14 18:44:14 -04:00
dependabot[bot]
1a6a284bc1 Bump github.com/google/uuid from 1.2.0 to 1.3.0 (#1249)
Bumps [github.com/google/uuid](https://github.com/google/uuid) from 1.2.0 to 1.3.0.
- [Release notes](https://github.com/google/uuid/releases)
- [Commits](https://github.com/google/uuid/compare/v1.2.0...v1.3.0)

---
updated-dependencies:
- dependency-name: github.com/google/uuid
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-07-14 11:09:23 -04:00
dependabot[bot]
1d948beb1f Bump github.com/go-chi/httprate from 0.5.0 to 0.5.1 (#1248)
Bumps [github.com/go-chi/httprate](https://github.com/go-chi/httprate) from 0.5.0 to 0.5.1.
- [Release notes](https://github.com/go-chi/httprate/releases)
- [Commits](https://github.com/go-chi/httprate/compare/v0.5.0...v0.5.1)

---
updated-dependencies:
- dependency-name: github.com/go-chi/httprate
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-07-14 11:07:11 -04:00
dependabot[bot]
deefd7a2fd Bump github.com/lestrrat-go/jwx from 1.2.1 to 1.2.2 (#1247)
Bumps [github.com/lestrrat-go/jwx](https://github.com/lestrrat-go/jwx) from 1.2.1 to 1.2.2.
- [Release notes](https://github.com/lestrrat-go/jwx/releases)
- [Changelog](https://github.com/lestrrat-go/jwx/blob/main/Changes)
- [Commits](https://github.com/lestrrat-go/jwx/compare/v1.2.1...v1.2.2)

---
updated-dependencies:
- dependency-name: github.com/lestrrat-go/jwx
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-07-14 11:06:51 -04:00
Deluan
97f87416a4 Bump @testing-library dependencies 2021-07-14 11:02:11 -04:00
Deluan
5d8b90b168 Bump Go dependencies 2021-07-14 10:55:40 -04:00
Deluan
8396b51a9c Upgrade React-Admin to 3.17.0 2021-07-14 10:39:48 -04:00
Deluan
4a25fa0920 Make the default volume 50% (compensate for logarithmic volume).
Closes #1052
2021-07-14 09:58:50 -04:00
dependabot[bot]
8e71f308c2 Bump prettier from 2.3.1 to 2.3.2 in /ui (#1210)
Bumps [prettier](https://github.com/prettier/prettier) from 2.3.1 to 2.3.2.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/2.3.1...2.3.2)

---
updated-dependencies:
- dependency-name: prettier
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-07-09 21:29:51 -04:00
Deluan
53fe2e98c5 Remove eject script, hopefully it will never be used 2021-07-08 21:09:38 -04:00
Deluan
334068c8bf Refactor mime-types configuration 2021-07-08 11:38:29 -04:00
Deluan
b34d77f85a Don't show "playing/paused" icon on the first song when calling "Play Now" 2021-07-05 21:45:20 -04:00
Deluan
24d4c81b34 Change default volume to 50%
Should fix #1052
2021-07-04 21:36:41 -04:00
Deluan
189d0c0ab3 Restore volume when playing a song...
... or continuing to play a paused one
2021-07-04 21:36:41 -04:00
Deluan
1922eaaab2 Make cover rectangular in player when cover animation is disabled 2021-07-04 12:52:59 -04:00
Deluan
5d9bea5087 Fix Album grid responsiveness on small screens.
Potentially fixes #647
2021-07-04 12:09:30 -04:00
Deluan
69afb69959 Fix Disc context menu not visible in mobile 2021-07-04 11:44:41 -04:00
Deluan
27ba267b38 Fix play single song action 2021-07-03 22:29:59 -04:00
Deluan
114fdce09e Fix Last.fm's artist.getInfo 2021-07-03 21:48:53 -04:00
Deluan
fa8b4d40b4 Fix arranging songs in PlayQueue 2021-07-03 21:34:24 -04:00
Deluan
ace5c905eb Made the Player behaviour more consistent 2021-07-02 23:36:33 -04:00
Deluan
26b5e6b1b4 Better scrobble log message when buffer is disabled 2021-07-02 10:19:16 -04:00
certuna
77f6bc83ac Update SongList.js (#1219)
Genre and Comments columns in Songs listview (hidden by default)
2021-07-02 10:18:45 -04:00
Deluan
94e36d7f60 Remove old feature flag for cache layout 2021-07-02 10:04:41 -04:00
Deluan
f49205733b Add feature flag for buffered scrobbling 2021-07-02 10:04:41 -04:00
Deluan
cfb113bd33 Disable Last.FM features based on LastFM.Enabled config option 2021-07-02 10:04:41 -04:00
Deluan
289da56f64 Implement Scrobble buffering/retrying 2021-07-02 10:04:41 -04:00
Deluan
fb183e58e9 Only encrypts NewPassword if it is not empty, when updating the user details. Fixes #1222 2021-07-01 16:09:49 -04:00
Deluan
ed286c7103 Don't rely on goroutines to send keepalive events 2021-07-01 13:31:46 -04:00
Deluan
452c8dc44b Fixed the enduring nasty "too many files open" bug!! Fix #446 2021-07-01 12:07:32 -04:00
Deluan
0c2ca2a5e4 Assign event ids in the main loop, to avoid out-of-order events 2021-07-01 10:58:41 -04:00
Deluan
5bd33455a1 Fix deadlock situation when events are sent too fast to the broker 2021-07-01 10:42:00 -04:00
Deluan
4ea0f235e1 Fix scrollbar colour for Dark/ExtraDark theme. Fixes #1216 2021-06-29 12:29:00 -04:00
Deluan Quintão
b16d473d4c Update es.json (POEditor.com) 2021-06-28 17:19:01 -04:00
Deluan
fd82b8f2dc Default for EnableCoverAnimation in dev mode is true 2021-06-28 17:18:32 -04:00
Deluan
a73f885afb Add option to disable album cover animation in the player. Closes #1185 2021-06-28 17:11:05 -04:00
Brian Schrameck
167fe46288 Addresses a bug that would prevent users from changing their own passwords, introduced as part of #1187. (#1214) 2021-06-28 16:36:14 -04:00
Deluan Quintão
cb1827ccbf Update translations (#1134)
* Update de.json (POEditor.com)

* Update ja.json (POEditor.com)

* Update cs.json (POEditor.com)

* Update nl.json (POEditor.com)

* Update fr.json (POEditor.com)

* Update de.json (POEditor.com)

* Update es.json (POEditor.com)

* Update uk.json (POEditor.com)

* Update sl.json (POEditor.com)

* Update fr.json (POEditor.com)

* Update it.json (POEditor.com)

* Update it.json (POEditor.com)

* Update cs.json (POEditor.com)

* Update sl.json (POEditor.com)

* Update de.json (POEditor.com)

* Update pt.json (POEditor.com)

* Update it.json (POEditor.com)

* Update pt.json (POEditor.com)

* Update zh-Hans.json (POEditor.com)

* Update nl.json (POEditor.com)

* Update es.json (POEditor.com)

* Update cs.json (POEditor.com)

* Update zh-Hans.json (POEditor.com)

* Update de.json (POEditor.com)

* Update fr.json (POEditor.com)

* Update sl.json (POEditor.com)

* Update ja.json (POEditor.com)

* Update uk.json (POEditor.com)

* Update cs.json (POEditor.com)

* Update nl.json (POEditor.com)
2021-06-28 09:55:46 -04:00
Deluan
25f0e11562 Add 'AlbumArtist' column to SongList 2021-06-28 09:54:17 -04:00
Deluan
292cf99f49 Add 'Year' column to Album and Playlists song list 2021-06-28 09:45:30 -04:00
Deluan
d2fcab78a5 Fix ND_DEVFASTACCESSCOVERART flag not available as env var 2021-06-26 15:40:12 -04:00
Deluan
94533e585c Add tests to /scrobble endpoint 2021-06-26 13:52:29 -04:00
Deluan
6dd38376f7 Add referential integrity to remove user's props when user is deleted 2021-06-25 23:09:10 -04:00
Deluan
26bcf0b877 Enable Last.fm scrobbling by default (still requires user's authorization) 2021-06-25 23:09:09 -04:00
Deluan
92634a7408 Only show message after 2 seconds, giving time for the browser to close it first 2021-06-25 22:23:35 -04:00
Deluan
ee21f3957e Pass userId explicitly to UserPropsRepository methods 2021-06-25 22:21:37 -04:00
Deluan
a1551074bb Add a hacky way to style the react-player. 2021-06-25 18:19:57 -04:00
Deluan
823fef8e43 Fix JS console error 2021-06-25 14:11:58 -04:00
Deluan
82105c3a16 Remove React.Strict mode 2021-06-25 14:08:00 -04:00
Deluan
b684a47f80 Show DiscSubtitle even if the album has only one disc.
Closes #947
2021-06-25 11:30:24 -04:00
Deluan
da2334e10c Remove submenu "Library". Relates to #430 2021-06-25 00:01:38 -04:00
Deluan
4853760fb5 Suppress logs of successful DB migrations applied when running for the first time 2021-06-24 23:43:20 -04:00
Deluan
0cbb0acad3 Skip songs with less than 31 seconds, as per Last.fm specification
See https://www.last.fm/api/scrobbling#when-is-a-scrobble-a-scrobble
2021-06-23 21:08:01 -04:00
Deluan
5040f6fd97 Fix label 2021-06-23 18:09:05 -04:00
Deluan
abe8015745 Add option to disable external scrobbling per player 2021-06-23 17:55:58 -04:00
Deluan
5001518260 Move user properties (like session keys) to their own table 2021-06-23 17:49:32 -04:00
certuna
265f33ed9d Remove clearServiceWorkerCache, not needed anymore. (#1205)
remove clearServiceWorkerCache, not needed anymore.
2021-06-23 12:11:35 -04:00
Deluan
99be8444d3 Disable completely external scrobblers if feature is disabled (DevEnableScrobble) 2021-06-23 11:01:58 -04:00
Deluan
f4ddd201f2 Send the time the track started playing when scrobbling 2021-06-23 11:01:58 -04:00
Deluan
056f0b944f Refactor: Consolidate scrobbling logic in play_tracker 2021-06-23 11:01:58 -04:00
Deluan
76acd7da89 Don't send scrobbles/nowPlaying updates to Last.fm if user has not authorized 2021-06-23 11:01:58 -04:00
Deluan
8af7dab23d Fix wrong warning about ignored NowPlaying 2021-06-23 11:01:58 -04:00
Deluan
a7509c9ff7 Send NowPlaying and Scrobbles to Last.fm 2021-06-23 11:01:58 -04:00
Deluan
d5461d0ae9 Refactor Agents to be singleton
Initial work for Last.fm scrobbler
2021-06-23 11:01:58 -04:00
Steve Richter
f9fa9667a3 Show user-friendly message when error occurs in Last.fm callback endpoint 2021-06-23 11:01:58 -04:00
Steve Richter
5fbfd9c81e Implement Last.fm account linking UI 2021-06-23 11:01:58 -04:00
Deluan
8b62a58b4c Remove limitation of only scrobbling tracks longer than 30 seconds 2021-06-22 09:59:00 -04:00
Deluan
743e469795 Use singleton in other places as well 2021-06-21 18:59:26 -04:00
Deluan
1f997357a9 Expose Last.fm's ApiKey to UI 2021-06-21 18:14:01 -04:00
Deluan
143cde37e5 Implement Last.FM Web authentication flow 2021-06-21 18:14:01 -04:00
Deluan
502a719e96 Implement Last.FM Desktop Auth flow endpoints 2021-06-21 18:14:01 -04:00
Steve Richter
8ee5c1f245 Initial Last.fm UI implementation 2021-06-21 18:14:01 -04:00
Deluan
0495e421fe Fix Last.fm API method signature 2021-06-21 18:14:01 -04:00
Deluan
ffa76bba6a Add flag to disable Scrobble config in the UI 2021-06-21 18:14:01 -04:00
Deluan
a4f91b74d2 Add Last.FM Authentication methods 2021-06-21 18:14:01 -04:00
Deluan
73e1a8fa06 Remove false-positive on new version detection 2021-06-21 17:46:26 -04:00
Deluan
877f01bd38 Show notification if server is updated 2021-06-21 13:48:39 -04:00
Deluan
47bcf719f2 Fix cookie warning 2021-06-20 13:27:50 -04:00
Deluan
197d430d15 Fix lint error 2021-06-20 12:07:34 -04:00
Deluan
4e1957ca71 Update Go dependencies 2021-06-20 12:06:21 -04:00
Deluan
25db2cb075 Add concurrency test for singleton 2021-06-20 11:51:32 -04:00
Deluan
80b2c2f3cf Try to register all playing music in GetNowPlaying 2021-06-20 11:25:15 -04:00
Deluan
97434c1789 Fix GetNowPlaying endpoint showing only the last play 2021-06-20 10:39:19 -04:00
Deluan
f8ee6db72a New implementation of NowPlaying 2021-06-20 10:39:16 -04:00
Deluan
0df0ac0715 Add logos to badges 2021-06-19 11:32:22 -04:00
Deluan
c09468e135 Option to allow auto-login during development. 2021-06-19 10:56:39 -04:00
Deluan
cf553ce812 Don't show "logout" when authenticated by Header 2021-06-18 19:08:25 -04:00
Deluan
31ea033880 Fix subsonic token when authenticating by Header 2021-06-18 19:00:13 -04:00
Deluan Quintão
66b74c81f1 Encrypt passwords in DB (#1187)
* Encode/Encrypt passwords in DB

* Only decrypts passwords if it is necessary

* Add tests for encryption functions
2021-06-18 18:38:38 -04:00
Deluan
d42dfafad4 Add username to request.Context 2021-06-18 18:28:51 -04:00
dependabot[bot]
84413b542e Bump @testing-library/jest-dom from 5.13.0 to 5.14.1 in /ui (#1176)
Bumps [@testing-library/jest-dom](https://github.com/testing-library/jest-dom) from 5.13.0 to 5.14.1.
- [Release notes](https://github.com/testing-library/jest-dom/releases)
- [Changelog](https://github.com/testing-library/jest-dom/blob/main/CHANGELOG.md)
- [Commits](https://github.com/testing-library/jest-dom/compare/v5.13.0...v5.14.1)

---
updated-dependencies:
- dependency-name: "@testing-library/jest-dom"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-06-18 09:39:23 -04:00
Deluan
b590c31e4e Fix stream url, after changes to subsonic client api 2021-06-16 16:38:50 -04:00
Deluan
c4623d7bc3 Don't show "empty" dates 2021-06-16 12:28:49 -04:00
Deluan
e0fd1c6ad8 Add "Last Played" column to SongList 2021-06-16 11:57:02 -04:00
Deluan
86271f0412 Optimize refresh events for scrobble endpoint 2021-06-16 10:23:34 -04:00
Deluan
fb7229a53e Refech using getMany, reducing the number of API calls 2021-06-16 10:01:09 -04:00
Deluan
521d1ff2bf Disable realip middleware when using the reverse proxy authentication feature
Should fix https://github.com/navidrome/navidrome/pull/1152#issuecomment-862306847
2021-06-16 10:01:09 -04:00
Deluan
d3db41ae7d Bump github.com/go-chi/httprate version 2021-06-15 19:58:29 -04:00
Deluan
8bf0089abf Bump github.com/ReneKroon/ttlcache/ and github.com/microcosm-cc/bluemonday versions 2021-06-15 19:54:18 -04:00
Deluan
b65e76293a Only send events to clients who need it
- User events (star, rating, plays) only sent to same user
- Don't send to the client (browser window) that originated the event
2021-06-15 18:59:26 -04:00
Deluan
5f6f74ff2d Always use httpClient to call APIs 2021-06-15 17:29:01 -04:00
Deluan
8383527aab Only refetch changed resources when receive a "refreshResource" event 2021-06-15 16:12:13 -04:00
Deluan
8a56584aed Removed the albumSong workaround, as React-Admin's cache seems to behave better now 2021-06-15 11:31:41 -04:00
Deluan
667701be02 Less warning messages when first running it.
They are actually `info` messages
2021-06-13 19:27:01 -04:00
Deluan
59b99d2206 No need to check for first time when authenticating. One less SQL call per request 2021-06-13 19:26:25 -04:00
Deluan
d54129ecd2 Rename app package to nativeapi 2021-06-13 19:15:41 -04:00
Deluan Quintão
03efc48137 Refactor routing, changes API URLs (#1171)
* Make authentication part of the server, so it can be reused outside the Native API

This commit has broken tests after a rebase

* Serve frontend assets from `server`, and not from Native API

* Change Native API URL

* Fix auth tests

* Refactor server authentication

* Simplify authProvider, now subsonic token+salt comes from the server

* Don't send JWT token to UI when authenticated via Request Header

* Enable ReverseProxyWhitelist to be read from environment
2021-06-13 12:46:36 -04:00
Deluan
bed2f017af Fix index of songs in downloaded playlist 2021-06-12 23:02:34 -04:00
Igor Rzegocki
6bd4c0f6bf Reverse proxy authentication support (#1152)
* feat(auth): reverse proxy authentication support - #176

* address PR remarks

* Fix redaction of UI appConfig

Co-authored-by: Deluan <deluan@navidrome.org>
2021-06-11 23:17:21 -04:00
Deluan
b445cdd641 Use a dedicated api-key/secret pair for Last.FM 2021-06-10 15:07:06 -04:00
Deluan
e31802d2d3 Only send "refresh" event if SetRating was successful 2021-06-10 15:03:30 -04:00
Deluan
cefc939909 Trigger UI refresh on media annotation events: star, setRating and scrobble 2021-06-10 12:20:52 -04:00
Deluan
2afb2db7ef Refactor for readability 2021-06-09 22:35:20 -04:00
Deluan
7f85ecd515 Trigger a UI refresh when the scanner finds changes.
Closes #1025
2021-06-09 21:02:20 -04:00
dependabot[bot]
cb6aa49439 Bump github.com/lestrrat-go/jwx from 1.2.0 to 1.2.1 (#1167)
Bumps [github.com/lestrrat-go/jwx](https://github.com/lestrrat-go/jwx) from 1.2.0 to 1.2.1.
- [Release notes](https://github.com/lestrrat-go/jwx/releases)
- [Changelog](https://github.com/lestrrat-go/jwx/blob/main/Changes)
- [Commits](https://github.com/lestrrat-go/jwx/compare/v1.2.0...v1.2.1)

---
updated-dependencies:
- dependency-name: github.com/lestrrat-go/jwx
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-06-09 15:33:04 -04:00
dependabot[bot]
b7f47c8833 Bump github.com/onsi/ginkgo from 1.16.3 to 1.16.4 (#1163)
Bumps [github.com/onsi/ginkgo](https://github.com/onsi/ginkgo) from 1.16.3 to 1.16.4.
- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v1.16.3...v1.16.4)

---
updated-dependencies:
- dependency-name: github.com/onsi/ginkgo
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-06-09 15:18:31 -04:00
dependabot[bot]
adb09c9c69 Bump @testing-library/jest-dom from 5.12.0 to 5.13.0 in /ui (#1162)
Bumps [@testing-library/jest-dom](https://github.com/testing-library/jest-dom) from 5.12.0 to 5.13.0.
- [Release notes](https://github.com/testing-library/jest-dom/releases)
- [Changelog](https://github.com/testing-library/jest-dom/blob/main/CHANGELOG.md)
- [Commits](https://github.com/testing-library/jest-dom/compare/v5.12.0...v5.13.0)

---
updated-dependencies:
- dependency-name: "@testing-library/jest-dom"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-06-09 15:18:17 -04:00
dependabot[bot]
0c9e0ff886 Bump prettier from 2.3.0 to 2.3.1 in /ui (#1161)
Bumps [prettier](https://github.com/prettier/prettier) from 2.3.0 to 2.3.1.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/2.3.0...2.3.1)

---
updated-dependencies:
- dependency-name: prettier
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-06-09 15:17:56 -04:00
Deluan
f9eec5e4dc Refactored agents calling into its own struct 2021-06-08 17:00:02 -04:00
Deluan
6c1ba8f0d0 Add tests to core.Share 2021-06-08 16:32:08 -04:00
Deluan
110e17b004 Make MockRepo names more consistent 2021-06-08 16:30:19 -04:00
Deluan
779571a086 go mod tidy 2021-06-08 15:47:34 -04:00
Yash Jipkate
af210c8903 Add Native Sharing REST API (#1150)
* Initial draft - UNTESTED

* changes to Save() and Update()

* apply col filter and limit nanoid

* remove columns to not update
2021-06-08 15:44:30 -04:00
Deluan
e80cf80d05 Move all Spotify and LastFM code into only one folder for each 2021-06-08 11:25:46 -04:00
Ye61123
182e3ec78e Update zh-Hans.json (#1160)
Complete untranslated items
2021-06-08 10:41:01 -04:00
Steve Richter
65ccd4c99d Parse ParamBool case-insensitively (#1151) 2021-06-04 23:37:01 -04:00
Deluan
bebfe296a5 Allow updating only specific columns 2021-06-02 18:40:29 -04:00
Deluan
9da9d73c1d Don't panic when taglib returns an error 2021-05-31 18:26:46 -04:00
Deluan
cd242695ba Foundational work to enable multi-valued tags 2021-05-31 18:08:12 -04:00
Deluan
519c89345e Omit empty fields from Native API responses 2021-05-31 12:20:21 -04:00
Deluan
336d891e58 Bump github.com/ReneKroon/ttlcache/v2 from 2.5.0 to 2.6.0 2021-05-31 10:49:42 -04:00
Deluan
9b4b28f685 Bump ginkgo/gomega versions 2021-05-31 10:32:37 -04:00
Deluan
39c560a5c2 Remove unused web-vitals package 2021-05-31 10:21:24 -04:00
Deluan
c5abdc19bc Fix recursive bug in Last.FM calls without mbid 2021-05-30 22:46:23 -04:00
Deluan
ead2095dd0 Respect EnableLogRedacting config when pretty printing configuration 2021-05-30 16:02:23 -04:00
Yash Jipkate
7b05c49215 Add devEnableShare config option (#1141)
* add devEnableShare config option

* Toggle in config.js
2021-05-30 15:36:10 -04:00
Yash Jipkate
327c259a3d Create share table and repository. (#930)
* Add share table and repository

* Add datastore mock

* Try fixing indent

* Try fixing indent - 2

* Try fixing indent - 3

* Implement rest.Repository and rest.Persistance

* Renew date

* Better error handling

* Improve field name

* Fix json name conventionally
2021-05-30 11:50:35 -04:00
Deluan
675cbe11b3 Fix updatePlaylist not updating fields comment and public.
Fix #1140
2021-05-29 17:16:56 -04:00
Deluan
91a91f7e06 GetCoverArt returns placeholder if id is missing
This mimics Subsonic behaviour, even if it contradicts the API documentation, which states `id` is required

Fixes #1139
2021-05-29 11:37:00 -04:00
Deluan
7bbb09e546 Add tests for WeightedRandomChooser 2021-05-28 23:51:56 -04:00
Deluan
dd56a7798e Rename variable with conflicting name 2021-05-28 23:00:39 -04:00
Deluan
a38e478a47 Better SimilarSongs algorithm 2021-05-28 22:55:34 -04:00
Deluan
1940267a18 Handle functions with params in sort order.
Related to #1023
2021-05-28 17:35:32 -04:00
Deluan
01f3ce0228 Add a timeout to background task 2021-05-28 11:37:53 -04:00
Deluan
48b6fa7feb Don't use request's context when refreshing artist info in background 2021-05-28 09:34:15 -04:00
Deluan
25d62cd751 Set retention time for uploaded artifacts to 7 days 2021-05-27 23:39:20 -04:00
Deluan
ed01946ace Embed Last.FM error responses, making the tests faster 2021-05-27 21:04:03 -04:00
Deluan Quintão
89b12b34be Retry calls to Last.FM without MBIDs when if returns artist invalid (#1138)
* Call Last.FM's getInfo again without mbid when artist is not found

* Call Last.FM's getSimilar again without mbid when artist is not found

* Call Last.FM's getTopTracks again without mbid when artist is not found
2021-05-27 20:53:24 -04:00
Deluan
4e0177ee53 Always update artist info, even if info is fresh 2021-05-27 20:32:26 -04:00
Deluan
b398053223 Include a shared Last.FM api key, providing zero conf ArtistInfo (bio/top songs/similar artists) 2021-05-27 16:14:24 -04:00
Deluan
db11b6b8f8 Remove decoration from reflex output 2021-05-26 12:24:02 -04:00
Deluan
60d50de8c9 Refactoring to make common components usage more uniform 2021-05-26 09:35:13 -04:00
Aldrin Jenson
0941fbc0cd Fix lag on albumList toggling (#1136) 2021-05-26 08:42:39 -04:00
Deluan
4217c75c9f Upgrade to Node v16 2021-05-25 10:53:16 -04:00
Deluan
409020a502 Bump github.com/ReneKroon/ttlcache/v2 from 2.4.0 to 2.5.0 2021-05-25 10:24:40 -04:00
Deluan
b4832c36b7 Bump github.com/golangci/golangci-lint from 1.40.0 to 1.40.1 2021-05-25 10:23:08 -04:00
Deluan
1de7366ece Bump @material-ui/lab from 4.0.0-alpha.57 to 4.0.0-alpha.58 in /ui 2021-05-25 10:16:44 -04:00
Deluan
ab1bc6194a Bump @testing-library dependencies 2021-05-25 10:13:57 -04:00
dependabot[bot]
ad4db122fb Bump hosted-git-info from 2.8.8 to 2.8.9 in /ui (#1111)
Bumps [hosted-git-info](https://github.com/npm/hosted-git-info) from 2.8.8 to 2.8.9.
- [Release notes](https://github.com/npm/hosted-git-info/releases)
- [Changelog](https://github.com/npm/hosted-git-info/blob/v2.8.9/CHANGELOG.md)
- [Commits](https://github.com/npm/hosted-git-info/compare/v2.8.8...v2.8.9)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-25 10:13:40 -04:00
dependabot[bot]
200b815c67 Bump url-parse from 1.4.7 to 1.5.1 in /ui (#1107)
Bumps [url-parse](https://github.com/unshiftio/url-parse) from 1.4.7 to 1.5.1.
- [Release notes](https://github.com/unshiftio/url-parse/releases)
- [Commits](https://github.com/unshiftio/url-parse/compare/1.4.7...1.5.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-25 10:12:42 -04:00
Deluan Quintão
5631493cc4 Upgrade Web UI to Create-React-App 4 and React 17 (#1105)
* Upgrade to CRA 4.0.3

* Try to fix tests. No lucky

* Fix new ESLint errors

* Fix JS tests and remove unwanted dependency. (#1106)

* Fix tests

* Fix lint

* Remove React v16 workaround (fixed in v17)

* Force eslint to break on warnings

* Lint now needs to be called explicitly in the pipeline

Co-authored-by: Yash Jipkate <34203227+YashJipkate@users.noreply.github.com>
2021-05-25 09:58:06 -04:00
Deluan
d9f268266c Rename List view mode to Table 2021-05-24 12:58:15 -04:00
Deluan
882519738f Change back mounting order, for better logs 2021-05-24 12:17:55 -04:00
Deluan
86d3a219a9 Show name of router in log 2021-05-24 11:55:39 -04:00
Deluan
1d0e75151a Update Portuguese translation 2021-05-24 11:24:28 -04:00
Deluan
107a11b445 Bump React-Admin to 3.15.2 2021-05-24 11:17:06 -04:00
Aldrin Jenson
cf8ee251ee Option to toggle fields in songs, albums & artists (#923)
* Add toggleColumns

- Add logic for toggling columns
- Add MenuComponent + useSelectedFields hook

* Refactoring

* eslint-fixes

* Typo

* skip menu in albumGridView

* add omittedFields

* Add toggling for playlists and albumSong

* Refactoring

* defaultProps - fix

* Add toggling for PlaylistSongs

* remove accidental console log

* Refactoring for future compatibility

* Hide ToggleMenu in albumGridView

* Add TopBarComponent in ToggleFieldsMenu

* Add defaultOff for useSelectedFields

* Fix edge case

* eslint fix

* Refactoring

* Add propType for forwardRef

* Fix issues

* add translation for grid and table

* add translation for grid and table

* Ignore menuBtn for spotify-ish and Ligera themes

* hide bpm by default in playlistSongs

* Add memoization

* Default album view must be Grid

Co-authored-by: Deluan <deluan@navidrome.org>
2021-05-24 11:09:06 -04:00
Deluan Quintão
6a17717e30 Update translations (#1130)
* Update zh-Hant.json (POEditor.com)

* Update cs.json (POEditor.com)

* Update nl.json (POEditor.com)

* Update ja.json (POEditor.com)

* Update pl.json (POEditor.com)

* Update es.json (POEditor.com)

* Update th.json (POEditor.com)

* Update sl.json (POEditor.com)
2021-05-24 10:21:59 -04:00
Deluan
b8a274e4e8 Move Swedish translation to right folder 2021-05-24 10:09:44 -04:00
Deluan
9800823015 Bump react-jinke-music-player from 4.24.0 to 4.24.1 in /ui 2021-05-24 10:04:37 -04:00
deeeeeebs
02606f43b8 Add Swedish translation (#1126)
* Swedish translation

* Updated and renamed to sv.json

Added further lines/translations from the english.json and corrected some of the previous translations

* Update sv.json

* Update sv.json

Ok now i'm done! :P
2021-05-24 10:03:06 -04:00
Deluan
e529390034 Remove md5-hex wrapper and use blueimp-md5 directly 2021-05-20 13:42:56 -04:00
Deluan
0ec7a305a2 Reorder Makefile dev targets 2021-05-20 13:42:29 -04:00
Deluan
b6cb81c3a3 Update Portuguese translations 2021-05-16 13:31:04 -04:00
Steve Richter
e60f2bfa3d User management improvements (#1101)
* Show more descriptive success messages for User actions

* Check username uniqueness when creating/updating User

* Adjust translations

* Add tests for `validateUsernameUnique()`

Co-authored-by: Deluan <deluan@navidrome.org>
2021-05-16 13:25:38 -04:00
dependabot[bot]
666c006579 Bump lodash from 4.17.19 to 4.17.21 in /ui (#1110)
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.19 to 4.17.21.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.19...4.17.21)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-15 14:33:29 -04:00
Deluan
6ad94548f3 Add explicit dependency for inflection 2021-05-15 11:59:56 -04:00
Deluan
fa0e6dda5b Remove C++11 warning in macOS 2021-05-14 16:03:11 -04:00
Deluan
e047008f7d Fix test 2021-05-14 15:38:28 -04:00
Deluan
3cac00ad13 Upgrade TagLib to 1.12 2021-05-14 14:31:07 -04:00
Deluan
39d68e8287 Restore pretty formatted config options in debug level 2021-05-12 14:43:09 -04:00
Deluan
751e2d6147 Make ScanInterval=0 disable the periodic scan 2021-05-12 14:08:38 -04:00
Dnouv
74300adbc8 Fix Ligera Error (#1117)
* Fix Ligera Error

* Run make setup
2021-05-12 10:21:56 -04:00
Deluan
a484adfcfb Add Slovenian translation. Thanks @jernejml 2021-05-11 22:36:22 -04:00
Deluan
25bd36dbc5 Bump react-admin to 3.15.1 2021-05-11 22:24:24 -04:00
Deluan
87298f616f Add more explicit npm dependencies 2021-05-11 22:22:32 -04:00
Deluan
4699902369 Remove dependency on lodash.get 2021-05-11 22:08:07 -04:00
Deluan
978933aa48 Add explicit npm dependencies 2021-05-11 22:07:47 -04:00
Deluan
77e736ccfd Do not use ra-core directly 2021-05-11 21:39:53 -04:00
Deluan
a77635e883 Only setup event stream when mounting the app 2021-05-11 20:27:12 -04:00
Dnouv
0c93db816c Fix PWA notification toolbar color (#1083)
* Fix PWA notification color

* Add React hook

* Convert component into a hook

Co-authored-by: Deluan <deluan@navidrome.org>
2021-05-11 20:11:54 -04:00
Deluan
c0243580c0 Integrate goose log with our own log system 2021-05-11 19:08:06 -04:00
Deluan
22ce5b6282 Removed unnecessary code 2021-05-11 18:55:58 -04:00
Deluan
fa9083ddec Upgrade prettier to 2.3.0
Some reformatting was needed... :/
2021-05-11 18:13:03 -04:00
Deluan
da684ff44c Bump github.com/lestrrat-go/jwx from 1.1.6 to 1.2.0 2021-05-11 17:44:00 -04:00
Deluan
7d96167abc Upgrade to go-chi 5 2021-05-11 17:21:18 -04:00
Deluan
fb5840705e Bump github.com/golangci/golangci-lint from 1.39.0 to 1.40.0 2021-05-11 12:30:11 -04:00
Dnouv
089d4abab1 Replace Feature Policy with Permissions Policy (#1112)
* Add Permissions Policy

* Remove Display capture option
2021-05-11 11:29:55 -04:00
Paul TREHIOU
62ccbaad8b Improve systemd unit security (#677)
Applied suggestions from `systemd-analyze` and also using StateDirectory to ensure /var/lib/navidrome exists and is writeable
2021-05-09 11:59:08 -04:00
Deluan
8419a2a5d1 Schedule periodic scan before starting initial scan 2021-05-08 19:19:31 -04:00
Aniket Biswas
71c2ed9922 Restart Current Song on previous (#1104)
* fixed on previous song behaviour

* rebased with master
2021-05-08 14:27:33 -04:00
Deluan
72ec808a2c Bump react-jinke-music-player from 4.21.2 to 4.24.0 in /ui 2021-05-08 13:15:39 -04:00
Deluan
702a65059f Fix redaction for query parameters. Fix #1103 2021-05-07 21:42:35 -04:00
Deluan
3e8d3e78c2 Fix Bookmarks Subsonic support (#1099)
JSON responses were incorrect
2021-05-07 09:47:13 -04:00
Deluan
47f4e0a4de Refactor to remove some nesting 2021-05-06 20:49:26 -04:00
Deluan
1f8949929d Fix(?) possible TypeError 2021-05-06 20:46:01 -04:00
Deluan
c92a24b3e2 Bump github.com/onsi/gomega from 1.11.0 to 1.12.0 2021-05-06 18:54:45 -04:00
dependabot[bot]
cbe0d9763b Bump github.com/robfig/cron/v3 from 3.0.0 to 3.0.1 (#1098)
Bumps [github.com/robfig/cron/v3](https://github.com/robfig/cron) from 3.0.0 to 3.0.1.
- [Release notes](https://github.com/robfig/cron/releases)
- [Commits](https://github.com/robfig/cron/compare/v3.0.0...v3.0.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-06 18:46:12 -04:00
dependabot[bot]
44dd414d25 Bump github.com/microcosm-cc/bluemonday from 1.0.8 to 1.0.9 (#1056)
Bumps [github.com/microcosm-cc/bluemonday](https://github.com/microcosm-cc/bluemonday) from 1.0.8 to 1.0.9.
- [Release notes](https://github.com/microcosm-cc/bluemonday/releases)
- [Commits](https://github.com/microcosm-cc/bluemonday/compare/v1.0.8...v1.0.9)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-06 18:45:10 -04:00
Samarjeet
d85db8ffff Fix Spotify-ish playlist title is cut off (#1094) 2021-05-06 18:33:54 -04:00
dependabot[bot]
c7378c0fa5 Bump @testing-library/user-event from 13.1.5 to 13.1.8 in /ui (#1082)
Bumps [@testing-library/user-event](https://github.com/testing-library/user-event) from 13.1.5 to 13.1.8.
- [Release notes](https://github.com/testing-library/user-event/releases)
- [Changelog](https://github.com/testing-library/user-event/blob/master/CHANGELOG.md)
- [Commits](https://github.com/testing-library/user-event/compare/v13.1.5...v13.1.8)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-06 18:27:02 -04:00
plr20
18696c5517 Update Czech translation (#1095)
* Update Czech translation

* Adjust translations
2021-05-06 18:22:47 -04:00
dependabot[bot]
5a5d763c19 Bump github.com/onsi/ginkgo from 1.16.1 to 1.16.2 (#1096)
Bumps [github.com/onsi/ginkgo](https://github.com/onsi/ginkgo) from 1.16.1 to 1.16.2.
- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v1.16.1...v1.16.2)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-06 18:19:40 -04:00
Deluan
f8dbc41b6d Breaking change: Add ScanSchedule, allows interval and cron based configurations.
See https://pkg.go.dev/github.com/robfig/cron#hdr-CRON_Expression_Format for expression syntax.

`ScanInterval` will still work for the time being. The only situation it does not work is when you want to disable periodic scanning by setting `ScanInterval=0`. If you want to disable it, please set `ScanSchedule=""`

Closes #1085
2021-05-06 17:56:10 -04:00
Deluan
1d6aa70033 Fix possible TypeError 2021-05-06 09:49:25 -04:00
Brian Schrameck
30bb3f7b43 BPM metadata enhancement (#1087)
* BPM metadata enhancement

Related to #1036.

Adds BPM to the stored metadata about MediaFiles.

Displays BPM in the following locations:
- Listing songs in the song list (desktop, sortable)
- Listing songs in playlists (desktop, sortable)
- Listing songs in albums (desktop)
- Expanding song details

When listing, shows a blank field if no BPM is present. When showing song details, shows a question mark.

Updates test MP3 file to have BPM tag. Updated test to ensure tag is read correctly.

Updated localization files. Most languages just use "BPM" as discovered during research on Wikipedia. However, a couple use some different nomenclature. Spanish uses PPM and Japanese uses M.M.

* Enhances support for BPM metadata extraction

- Supports reading floating point BPM (still storing it as an integer) and FFmpeg as the extractor
- Replaces existing .ogg test file with one that shouldn't fail randomly
- Adds supporting tests for both FFmpeg and TagLib

* Addresses various issues with PR #1087.

- Adds index for BPM. Removes drop column as it's not supported by SQLite (duh).
- Removes localizations for BPM as those will be done in POEditor.
- Moves BPM before Comment in Song Details and removes BPM altogether if it's empty.
- Omits empty BPM in JSON responses, eliminating need for FunctionField.
- Fixes copy/paste error in ffmpeg_test.
2021-05-05 21:35:01 -04:00
Deluan
fb33aa4496 Fix possible TypeError 2021-05-05 21:14:36 -04:00
Deluan
9e559311ad Fix Album Grid flickering 2021-05-05 16:18:08 -04:00
Deluan
a5fc5f0ff6 Revert "Better way to invoke make single"
This reverts commit 73efbd90
2021-05-04 17:05:41 -04:00
Deluan
73efbd90ab Better way to invoke make single 2021-05-04 16:47:47 -04:00
Deluan
cbc4cb483d Fix QuickFilter by favourites in Album/All view 2021-05-04 16:28:19 -04:00
Deluan
986473393f Fix missing translation error in console. Closes #1038 2021-05-04 16:01:26 -04:00
Deluan
66b31644fa Upgrade React-Admin to 3.15.0 2021-05-03 22:32:30 -04:00
Deluan
b478b0af02 FIx ffmpeg output regex too rigid 2021-05-03 21:26:44 -04:00
Deluan
c3316e201e Fix cover art detection with ffmpeg 4.4 2021-05-03 20:25:31 -04:00
Deluan
874b17b8f6 Require user to provide current password to be able to change it
Admins can change other users' password without providing the current one, but not when changing their own
2021-05-03 15:03:34 -04:00
Deluan
5808b9fb71 Fix Transcodings menu 2021-05-03 13:54:08 -04:00
Deluan
c33ebabde8 Fix warning about promise being ignored 2021-05-03 13:38:34 -04:00
Deluan
7feda4bea4 Add EnableUserEditing, to control whether a regular user can change their own details (default true) 2021-05-02 17:11:12 -04:00
Deluan
2ff1c79b64 Fix EnableLogRedacting case 2021-05-02 16:49:20 -04:00
Deluan
cfbc39fb7f Add log redacting, controlled by the new EnableLogRedacting config option (default true)
Imported redacting code from https://github.com/whuang8/redactrus (thanks William Huang)
Didn't use it as a dependency as it was too small and we want to keep dependencies at a minimum
2021-05-02 16:45:16 -04:00
Deluan
2372f1d12b Change visibility of helper function 2021-05-02 15:23:51 -04:00
Deluan
490a7fcf52 Add test to Login function 2021-05-02 15:19:21 -04:00
Deluan
ad153f5f63 Fix User delete button not showing 2021-05-02 15:03:15 -04:00
Deluan
b8138ebad6 Fix create first login 2021-05-02 14:13:17 -04:00
Deluan
e3fe8399c8 Fix DevAutoCreateAdminPassword 2021-05-01 18:40:02 -04:00
Deluan
88105d5c30 Clean-up Makefile, add help 2021-05-01 11:14:24 -04:00
Deluan
b180386d23 Simplify build targets 2021-05-01 09:16:45 -04:00
Deluan
70e7bf6b5b Clean up some make targets 2021-05-01 08:51:29 -04:00
Samarjeet
d41137ad8e [Spotify-ish] Login consistent with other themes (#1073) 2021-05-01 08:48:12 -04:00
Deluan
88f2fc35cd Fix regular users not able to edit their info before logging in again 2021-04-30 17:53:17 -04:00
Deluan
bc62efb059 More auth tests 2021-04-30 10:00:03 -04:00
Deluan
eaf40efdf4 Never send passwords to the UI 2021-04-29 20:04:01 -04:00
Deluan
71dc0dddaf Show Person icon for non admin users 2021-04-29 18:26:53 -04:00
Deluan
bcda53f115 Less waiting for cache to be ready 2021-04-29 13:58:01 -04:00
Deluan
8a07bac2a2 Fix SIGUSR1 work when ScanInterval=0 2021-04-29 13:10:10 -04:00
Deluan
a35de2bfd1 Allow regular users to change their info, including password.
Should fix #199
2021-04-28 22:35:25 -04:00
Deluan
22582392a0 Fix "Failed prop type: Invalid prop variant" in console 2021-04-28 22:07:16 -04:00
Deluan
932c108e82 Fix "SharedArrayBuffer will require cross-origin isolation"
This is a workaround for React v16 while we don't upgrade to v17

See https://github.com/facebook/create-react-app/issues/10474
2021-04-28 21:42:17 -04:00
whorfin
20d2726faa Improve scanner (#1054)
* Handle subdirectories without rx permission correctly
Allow ogg files w/o metadata, having taglib behave more like ffmpeg

* Fix test for walk_dir_tree, fix full reading of files in permission-
constrained directories, allow directories with leading ellipses

* Sorted directory traversal is preferred, and cleanup tests

* Small refactoring to clean-up `loadDir` function and to remove some "warnings" from IntelliJ

Co-authored-by: Deluan <deluan@navidrome.org>
2021-04-28 19:51:02 -04:00
Samarjeet
771c91d2dd [Spotify-ish] Indicate active page number (#1068) 2021-04-28 08:57:08 -04:00
Deluan Quintão
b8173124f4 Update ja.json (POEditor.com) 2021-04-27 18:39:45 -04:00
Deluan
d1605dcfbe Replace godirwalk with standard Go 1.16 filepath.WalkDir
Should fix https://github.com/navidrome/navidrome/issues/1048
2021-04-27 11:28:47 -04:00
Deluan
10cfaad95c Bump react-redux version to 7.2.4 2021-04-26 23:22:53 -04:00
Deluan
07f6a7cc9f Bump @testing-library dependencies 2021-04-26 23:19:30 -04:00
Deluan
6e73c23704 Keepalive must return an ID to be used with dataProvider.getOne 2021-04-26 23:19:30 -04:00
Deluan
862c6d3c73 Upgrade React-Admin to 3.14.5 2021-04-26 23:19:28 -04:00
Deluan
692663680b Uses GoLang 1.16.3
Also add a target to build snapshots for a single platform
2021-04-26 17:21:59 -04:00
Deluan
0d409e37e2 Fix aspect ratio of login icon 2021-04-26 12:35:49 -04:00
dependabot[bot]
f1bd736b20 Bump jest-environment-jsdom-sixteen from 1.0.3 to 2.0.0 in /ui
Bumps [jest-environment-jsdom-sixteen](https://github.com/SimenB/jest-environment-jsdom-sixteen) from 1.0.3 to 2.0.0.
- [Release notes](https://github.com/SimenB/jest-environment-jsdom-sixteen/releases)
- [Commits](https://github.com/SimenB/jest-environment-jsdom-sixteen/compare/v1.0.3...v2.0.0)

Signed-off-by: dependabot[bot] <support@github.com>
2021-04-26 09:11:34 -04:00
Deluan
cde6626016 Fix logo aspect ratio in Safari 2021-04-25 21:16:45 -04:00
Deluan
1c7d4c5630 Improve Logo resolution in login dialog 2021-04-25 21:16:24 -04:00
Dnouv
c75314c605 Enhanced Mobile Login Screen (#953)
* Enhanced Mobile Login Screen

* Removed duplicate line of code

* Add support for desktop

* Remove conflict

* Reset button style

* Change Login
2021-04-25 21:09:23 -04:00
Deluan
b10f491de8 Fix Song details row height 2021-04-24 23:06:14 -04:00
caiocotts
b671d0ff7b Better handling of album comments (#1013)
* Change album comment behaviour

* Don't check first item

* Fix previously imported album comments.

* Remove song comments if album comment is present
2021-04-24 21:40:55 -04:00
Deluan
4b5a5abe1b Fix Web Scroller compatibility
This fixes https://github.com/web-scrobbler/web-scrobbler/issues/2828
2021-04-24 21:13:14 -04:00
Deluan
3cede28161 Reorganize AudioTitle classes.
Should fix https://github.com/web-scrobbler/web-scrobbler/issues/2828
2021-04-24 18:06:24 -04:00
Deluan
79bbff0e98 Make Playlist grid more responsive 2021-04-24 11:29:12 -04:00
Deluan
0142352280 Fix build tag 2021-04-23 21:42:22 -04:00
Deluan
d5c7a81888 Disable SIGUSR1 handler for Windows (not available) 2021-04-23 21:22:04 -04:00
Deluan
1e539f4e54 Add trigger scan when receiving SIGUSR1 signal 2021-04-23 20:40:28 -04:00
Dnouv
e83a0b23a3 Hide volume bar in lower resolutions (#889)
This gives more space for the song and artist names in the player

* fix min-width of AlbumDetails

* Fix song play time display

* Song duration display fix#2

* Removed important

* Resolve conflicts

* Update Player.js

* Change breakdown and hide volume

Co-authored-by: Deluan <deluan@navidrome.org>
2021-04-23 19:04:37 -04:00
dependabot[bot]
9f39f062d8 Bump @testing-library/user-event from 13.1.2 to 13.1.5 in /ui (#1051)
Bumps [@testing-library/user-event](https://github.com/testing-library/user-event) from 13.1.2 to 13.1.5.
- [Release notes](https://github.com/testing-library/user-event/releases)
- [Changelog](https://github.com/testing-library/user-event/blob/master/CHANGELOG.md)
- [Commits](https://github.com/testing-library/user-event/compare/v13.1.2...v13.1.5)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-23 18:44:32 -04:00
Yash Jipkate
df57cd6bb5 Allow adding songs to multiple playlists at once. (#995)
* Add support for multiple playlists

* Fix lint

* Remove console log comment

* Disable 'check' when loading

* Fix lint

* reset playlists on closeAddToPlaylist

* new playlist: accomodate string type on enter

* Fix lint

* multiple new playlists are added correctly

* use makestyle()

* Add tests

* Fix lint
2021-04-23 18:37:08 -04:00
Arbaz Ahmed
d829a63686 fix: refactored some styles in jinkie player and removed br tag - #865 (#1047)
* refactored some styles in jinkieplayer

* fix: refactored some styles in jinkie player and removed br tag - #865

* fix: refactored some styles in jinkie player and removed br tag - #865

Signed-off-by: armedev <epiratesdev@gmail.com>
2021-04-23 18:06:39 -04:00
Deluan Quintão
4b061427ad Update translations (#1002)
* Update cs.json (POEditor.com)

* Update nl.json (POEditor.com)

* Update fr.json (POEditor.com)

* Update ja.json (POEditor.com)

* Update pt.json (POEditor.com)

* Update es.json (POEditor.com)

* Update uk.json (POEditor.com)

* Update zh-Hans.json (POEditor.com)

* Update zh-Hant.json (POEditor.com)

* Update eo.json (POEditor.com)

* Update de.json (POEditor.com)

* Update zh-Hant.json (POEditor.com)

* Update cs.json (POEditor.com)

* Update cs.json (POEditor.com)
2021-04-22 23:12:02 -04:00
Deluan
aa9cf8ef17 Add a cleanup to tests 2021-04-22 14:04:48 -04:00
Deluan
240de70026 Add tests for SpreadFS 2021-04-22 14:02:42 -04:00
Shishir A S
6da9dee7d3 Fade in QualityInfo while hovering on Song title (#1041)
* feat(Player/QualityInfo) : Animate Quality Info + Increased audio player dimensions

Signed-off-by: Shishir <shishir.srik@gmail.com>

* fix(Player.js) : Converted JS hover functionality to pure CSS

Signed-off-by: Shishir <shishir.srik@gmail.com>

* Removed unused useState

* fix(Player) : Reverted player height adjustment

Signed-off-by: Shishir <shishir.srik@gmail.com>
2021-04-22 09:53:33 -04:00
Deluan
467eb345ad Don't panic if fscache could not be initialized due to a FS error 2021-04-21 23:39:23 -04:00
Deluan
31b553e972 Add missing error log message in fscache initialization 2021-04-21 14:15:42 -04:00
Deluan
da30923a95 Replace default Login backgrounds with Navidrome's collection 2021-04-20 15:26:24 -04:00
dependabot[bot]
e7be2f6f9c Bump ssri from 6.0.1 to 6.0.2 in /ui (#1045)
Bumps [ssri](https://github.com/npm/ssri) from 6.0.1 to 6.0.2.
- [Release notes](https://github.com/npm/ssri/releases)
- [Changelog](https://github.com/npm/ssri/blob/v6.0.2/CHANGELOG.md)
- [Commits](https://github.com/npm/ssri/compare/v6.0.1...v6.0.2)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-20 15:06:46 -04:00
Deluan
9a509c749a Workaround for https://github.com/lijinke666/react-music-player/issues/351 2021-04-20 11:01:31 -04:00
Deluan
abaecf2b88 Add Nginx header to not buffer SSE connection
This should allow the Activity Panel, that uses a Server-Side Events/ Event Source connection, to work with default Nginx reverse proxy configuration.
2021-04-20 10:16:20 -04:00
Deluan
f63a912341 Add config option to set default theme 2021-04-18 13:51:00 -04:00
Deluan
b6f525bda5 Fix exception when running in Firefox over insecure http. Fix #1039 2021-04-18 02:21:58 -04:00
Deluan
0063720cc2 Change size and position of QualityInfo in the Player 2021-04-17 22:49:36 -04:00
Ruchi Kushwaha
b441260186 Change icon on active menu item (#903)
* add icons

* add logic to change the icon

* make the active menu bold

* Encapsulate the dynamic icon behaviour into a self-contained component

Co-authored-by: Deluan <deluan@navidrome.org>
2021-04-17 00:40:07 -04:00
Deluan
16a5ac323b Fix migration error caused by #868 2021-04-15 21:25:02 -04:00
Praveen Kumar
749f5d45c6 Fix welcome message styles (#1015)
* style(login): welcome-message-wrapping - #1014

Signed-off-by: Praveen Kumar <pkspyder007@gmail.com>

* style(login): welcome-message-wrapping - #1014

Signed-off-by: Praveen Kumar <pkspyder007@gmail.com>

* chore(makefile): Removed-lint-timeout

Signed-off-by: Praveen Kumar <pkspyder007@gmail.com>
2021-04-15 20:18:35 -04:00
Deluan
a81ef0923b Fix cover art not showing in notification when using a BaseURL 2021-04-14 13:30:14 -04:00
Samarjeet
c86d2a93b1 Fix transparent background in Spotify-ish (#1030) 2021-04-13 21:05:25 -04:00
Deluan
b55d582882 go mod tidy 2021-04-13 16:55:26 -04:00
Deluan
6635149f3c Fix pre-commit hook 2021-04-13 16:54:31 -04:00
Deluan
01b34f4f14 Bumps @testing-library/user-event from 13.1.1 to 13.1.2 2021-04-13 16:52:11 -04:00
Deluan
cb9cabe0ec Bumps github.com/ReneKroon/ttlcache/v2 from 2.3.0 to 2.4.0 2021-04-13 16:48:41 -04:00
Deluan
29aff05f70 Bump github.com/onsi/ginkgo from 1.16.0 to 1.16.1 2021-04-13 16:48:05 -04:00
Deluan
f48bfb6ad1 Bump github.com/microcosm-cc/bluemonday version 2021-04-13 16:47:31 -04:00
Deluan
ef9a16ac9f Change order of themes 2021-04-10 19:52:01 -04:00
Dnouv
ca9d42714f New Ligera (light) Theme (#990)
* Enhanced Light Theme

* New Login Screen

* Fix Appbar for sm screen

* Reverse Gradient

* Fix test error

* Fix color

* Fix Gradient

* Theme color change

* Fix playlist autocomp popup

* Rename theme

* Fix hover icon color
2021-04-10 19:49:39 -04:00
Deluan
efe8240c0a Remove inline style in favour of MUI's styling solution 2021-04-09 17:38:08 -04:00
Ayush Naidu
f7dfabaa40 Replaced literal 302 with http constant (#1006) 2021-04-08 14:17:14 -04:00
Deluan
d8a1773d50 Increase golangci-lint timeout. Fix #1001 2021-04-08 10:19:58 -04:00
Aries
e105e2d2a2 Update Japanese translation (#997) 2021-04-07 23:18:49 -04:00
Deluan
f41bc31ba8 Fix layout when album comment is visible 2021-04-07 22:16:38 -04:00
Deluan
96a14ec484 Hide QualityInfo on small screens 2021-04-07 16:10:31 -04:00
Neil Chauhan
48ae4f7479 Add 5-star rating system(#986)
* Added Star Rating functionality for Songs

* Added Star Rating functionality for Artists

* Added Star Rating functionality for AlbumListView

* Added Star Rating functionality for AlbumDetails and improved typography for title

* Added functionality to turn on/off Star Rating functionality for Songs

* Added functionality to turn on/off Star Rating functionality for Artists

* Added functionality to turn on/off Star Rating functionality for Albums

* Added enableStarRating to server config

* Resolved the bugs and improved the ratings functionality.

* synced repo and removed duplicate key

* changed the default rating size to small, and changed the color to match the theme.

* Added translations for ratings, and Top Rated tab in side menu.

* Changed rating translation to topRated in albumLists, and added has_rating filter to topRated.

* Added empty stars icon to RatingField.

* Added sortable=false in AlbumSongs and added sortByOrder=DESC in all List components.

* Added translations for rating, for artists and albums, and removed the sortByOrder=DESC from SimpleLists.
2021-04-07 16:02:52 -04:00
Deluan
840521ffe2 Fix console errors for QualityInfo component 2021-04-07 13:08:41 -04:00
Deluan
5178f44094 Add has_rating filter to albums 2021-04-07 11:04:36 -04:00
Deluan
10dcc3fb37 Remove unnecessary export mapping (bad refactoring) 2021-04-07 10:57:14 -04:00
Aries
49b1c40fbd Update Japanese translation (#992) 2021-04-07 10:14:45 -04:00
Deluan
156a53c2ac Add support for artist 5-star rating in Subsonic API 2021-04-06 23:06:31 -04:00
Deluan
9913b92905 Get lossless format list from server 2021-04-06 22:18:48 -04:00
Himanshu maurya
52812fa48b Added quality info (#918)
* added quality info

* fixed formatting

* implemented various suggestions

* npm run prettier

* applied suggestions

* npm run prettier

* corrected lossless formats and other suggestions

* moved losslessformats into consts.js

* added some test

* typo while resolving conflicts

* fetch

* removed a bug causing component (as suggested)

* Update PlayerToolbar.js

* implemented suggestions

* added few more tests

* npm run prettier

* added size

* updated qualityInfo

* implemented suggestions

* added test for when no record is recieved

* Update QualityInfo.js
2021-04-06 21:30:17 -04:00
Shishir A S
c57fa7a8fc Fixes play icon color in "Light" theme (#972)
* fix(ui/src/album): White play button on hover for all themes - #960

* fix(PlayButton) : White play button for light theme - #960

* fix(PlayButton) : White play button for light theme - #960

* bug(AlbumGridView.js) - Album play button defaults to white, can be overridden - #960

Signed-off-by: Shishir <shishir.srik@gmail.com>

* bug(AlbumGridView.js) - Album play button defaults to white, can be overridden - #960

* Reverted package.json and package-lock.json - #960

Signed-off-by: Shishir <shishir.srik@gmail.com>

* Missing lint script added - #960

Signed-off-by: Shishir <shishir.srik@gmail.com>

* Removed color, added className and made record required in PlayButton.propTypes - #960
2021-04-06 10:02:44 -04:00
Deluan
dbda8712f2 npm audit fix 2021-04-05 22:33:13 -04:00
Deluan
5f685bca30 Hide ❤️ in Playlists 2021-04-05 22:32:25 -04:00
Deluan
37f6ff02cf Do not disable eslint rule 2021-04-05 22:21:05 -04:00
dependabot[bot]
01ef4d2f21 Bump @testing-library/jest-dom from 5.11.9 to 5.11.10 in /ui
Bumps [@testing-library/jest-dom](https://github.com/testing-library/jest-dom) from 5.11.9 to 5.11.10.
- [Release notes](https://github.com/testing-library/jest-dom/releases)
- [Changelog](https://github.com/testing-library/jest-dom/blob/main/CHANGELOG.md)
- [Commits](https://github.com/testing-library/jest-dom/compare/v5.11.9...v5.11.10)

Signed-off-by: dependabot[bot] <support@github.com>
2021-04-05 21:21:14 -04:00
dependabot[bot]
32ad982b11 Bump github.com/golangci/golangci-lint from 1.38.0 to 1.39.0
Bumps [github.com/golangci/golangci-lint](https://github.com/golangci/golangci-lint) from 1.38.0 to 1.39.0.
- [Release notes](https://github.com/golangci/golangci-lint/releases)
- [Changelog](https://github.com/golangci/golangci-lint/blob/master/CHANGELOG.md)
- [Commits](https://github.com/golangci/golangci-lint/compare/v1.38.0...v1.39.0)

Signed-off-by: dependabot[bot] <support@github.com>
2021-04-05 21:20:57 -04:00
dependabot[bot]
87b04607ad Bump @testing-library/react-hooks from 5.1.0 to 5.1.1 in /ui
Bumps [@testing-library/react-hooks](https://github.com/testing-library/react-hooks-testing-library) from 5.1.0 to 5.1.1.
- [Release notes](https://github.com/testing-library/react-hooks-testing-library/releases)
- [Changelog](https://github.com/testing-library/react-hooks-testing-library/blob/master/CHANGELOG.md)
- [Commits](https://github.com/testing-library/react-hooks-testing-library/compare/v5.1.0...v5.1.1)

Signed-off-by: dependabot[bot] <support@github.com>
2021-04-05 21:17:18 -04:00
dependabot[bot]
4e41ef7542 Bump github.com/microcosm-cc/bluemonday from 1.0.4 to 1.0.6
Bumps [github.com/microcosm-cc/bluemonday](https://github.com/microcosm-cc/bluemonday) from 1.0.4 to 1.0.6.
- [Release notes](https://github.com/microcosm-cc/bluemonday/releases)
- [Commits](https://github.com/microcosm-cc/bluemonday/compare/v1.0.4...v1.0.6)

Signed-off-by: dependabot[bot] <support@github.com>
2021-04-05 21:14:41 -04:00
dependabot[bot]
6af45d6eea Bump @testing-library/user-event from 13.0.7 to 13.1.1 in /ui
Bumps [@testing-library/user-event](https://github.com/testing-library/user-event) from 13.0.7 to 13.1.1.
- [Release notes](https://github.com/testing-library/user-event/releases)
- [Changelog](https://github.com/testing-library/user-event/blob/master/CHANGELOG.md)
- [Commits](https://github.com/testing-library/user-event/compare/v13.0.7...v13.1.1)

Signed-off-by: dependabot[bot] <support@github.com>
2021-04-05 21:14:13 -04:00
dependabot[bot]
69fe771819 Bump @testing-library/react from 11.2.5 to 11.2.6 in /ui
Bumps [@testing-library/react](https://github.com/testing-library/react-testing-library) from 11.2.5 to 11.2.6.
- [Release notes](https://github.com/testing-library/react-testing-library/releases)
- [Changelog](https://github.com/testing-library/react-testing-library/blob/master/CHANGELOG.md)
- [Commits](https://github.com/testing-library/react-testing-library/compare/v11.2.5...v11.2.6)

Signed-off-by: dependabot[bot] <support@github.com>
2021-04-05 21:10:30 -04:00
dependabot[bot]
ce675d478a Bump github.com/onsi/ginkgo from 1.15.2 to 1.16.0
Bumps [github.com/onsi/ginkgo](https://github.com/onsi/ginkgo) from 1.15.2 to 1.16.0.
- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v1.15.2...v1.16.0)

Signed-off-by: dependabot[bot] <support@github.com>
2021-04-05 21:10:18 -04:00
Balaguru Ragupathi
6988b9a086 Improved Header Readability for Songs List (#985)
* style(SongDataGrid): Table Header Definition - #943

Signed-off-by: Balaguru4580 <balaguru4580@gmail.com>

* style(SongDataGrid): Improved Header Readability - #943

Signed-off-by: Balaguru4580 <balaguru4580@gmail.com>

* Shadow Effect

* Shadow Effect Opacity Adjustment

* Fixed Songs Context Menu

* Fixed the Songs Context Menu
2021-04-05 20:32:57 -04:00
Aldrin Jenson
55c2431b17 Fix undefined variant prop in DateField (#987) 2021-04-05 20:27:40 -04:00
Ritik Pandey
69ee17402f Add pagination to playlists (#969)
* add pagination

* prettier applied

* perPage_bug_fixed

* pagination_component_changed

* getAllSongs function added

* pagination component updated

* catch_error from data provider

* getAllSongsAndDispatch added

* remove ids from action function
2021-04-05 18:21:47 -04:00
Deluan
cdfdf78c73 Revert "style(SongDataGrid): Improved Header Readability (#954)"
This reverts commit 3d58c5ab. It broke the SongContextMenu
2021-04-04 21:46:44 -04:00
sobhanbera
ca51372d8d Add Extra Dark theme (#955)
* added new theme - night

* removed a unused field

* fixed a typo from previous change

* night theme in login window

* changed name

changed the theme name from "Night" to "Extra Dark"

* changed the theme name

* Update index.js

* Rename night.js to extradark.js

* trying something

* formatted

the JS build was failing because I haven't formatted the index.js file with prettier. I got to know about this now.
I think now it will be resolved.
2021-04-04 21:25:54 -04:00
Éric Gaspar
a4d07734cd Update fr.json (#975) 2021-04-04 20:53:47 -04:00
Balaguru Ragupathi
3d58c5ab54 style(SongDataGrid): Improved Header Readability (#954)
* style(SongDataGrid): Table Header Definition - #943

Signed-off-by: Balaguru4580 <balaguru4580@gmail.com>

* style(SongDataGrid): Improved Header Readability - #943

Signed-off-by: Balaguru4580 <balaguru4580@gmail.com>

* Shadow Effect

* Shadow Effect Opacity Adjustment
2021-04-02 22:15:59 -04:00
Aldrin Jenson
12223b2a83 Fix extra multiline Prop error (#966)
* Fix extra multiline Prop error

* Remove multiline prop from MultiLineTextField
2021-04-02 22:09:28 -04:00
Samarjeet
c7dc3628e2 Fix transparent bg in suggestions [Spotify-ish] (#964) 2021-04-02 13:30:27 -04:00
certuna
9871919fae New service worker (#952)
* Add files via upload

* Update serviceWorker.js
2021-04-02 11:58:45 -04:00
Deluan
0cb7d3853f Add required prop order in random album list. Fix #957 2021-04-01 23:14:59 -04:00
rochakjain361
d0d18e8512 Album details UI fix (#922)
* Fix the Album Details UI to look similar to Song Details UI

* Remove the unused components

* Fix the gap between row and the first field in the details view

* Fix the width of the row of Album Details UI
2021-04-01 23:03:05 -04:00
Deluan
0d95c4bb9d Fix Code of Conduct link 2021-04-01 17:38:55 -04:00
Samarjeet
ea65da484b Make spotify-ish more spotify-ish (#914)
* [Theme] Allow customising album and player parts

* [Theme] Allow customizing song lists view

* Make spotify-ish more spotify-ish

* Fix responsive issues in spotify-ish

* Spotify-ish login page

* Add back the previous "Spotify-ish" theme as "Green"

Co-authored-by: Deluan <deluan@navidrome.org>
2021-03-31 16:58:47 -04:00
harshavardhanpb
5128c049d7 Rename diodo_test.go to diode_test.go (#956)
Simple typo fix
2021-03-31 15:41:38 -04:00
Deluan
16f6d9466f Remove redundant backgroundColor from Login icon 2021-03-31 13:31:03 -04:00
Samarjeet
cf72bbfad4 Fix login page UI contrast in dark,spotify (#946) 2021-03-31 01:11:05 -04:00
Aldrin Jenson
20f5778694 Fix prop undefined bug #925 (#942)
* fix(albumListView)  prop undefined bug  #925

* Fix undefinedProp bug
2021-03-31 01:07:07 -04:00
Deluan
46d4c48d44 Login backgrounds from unsplash collection (#936) 2021-03-31 00:49:12 -04:00
Samarjeet
166410eb50 Login backgrounds from unsplash collection (#936) 2021-03-31 00:24:37 -04:00
Deluan
43cbde97ad Remove "minimize" button from Player when in Desktop resolution 2021-03-30 22:43:39 -04:00
Deluan
13e80e651c Fix issue with classes being removed from DOM. Fix #864 2021-03-30 22:42:46 -04:00
Deluan
16e495a80f Revert: Fix theme not being applied to PlayerToolbar
It was causing issues with classes being removed from DOM
2021-03-30 22:21:24 -04:00
Samarjeet
1f2b5294c3 Allow theme customizing Login Page (#940) 2021-03-29 23:25:32 -04:00
Aldrin Jenson
a36a8c2372 Fix LinkWrapping Error in the console #921 (#924) 2021-03-29 21:45:48 -04:00
Aldrin Jenson
5245b4c62b Fix cacheUndefined bug - #901 (#915)
- add check to see if cache is defined before deleting
2021-03-29 20:43:51 -04:00
Deluan
3b0defefec Fix UI loading redirections. Should fix #906 2021-03-29 20:13:04 -04:00
Neil Chauhan
404253a881 Enable turn on/off Favorites/Loved feature. (#917)
* Added option to enable/disable favorites in Song

* Added option to enable/disable favorites in Artist

* Added option to enable/disable favorites in Albums and Favorites tab in sidebar

* Added option to enable/disable favorites in Player

* Set default value of enableFavourites as true

* Improved the functionality to enable/disable Favorites

* Add `EnableFavourites` config option

Co-authored-by: Deluan <deluan@navidrome.org>
2021-03-28 20:35:49 -04:00
Adrian Edwards
5dfcb316cf Remove unused prop from ArtistList (#926)
* remove syncWithLocation prop from ArtistList to fix #925

* run prettier
2021-03-28 19:53:25 -04:00
Deluan
90cf118349 Fix context menu/heart column header alignment in SongList 2021-03-27 22:09:43 -04:00
Deluan
b42532dd7c Fix theme not being applied to PlayerToolbar 2021-03-27 14:24:59 -04:00
Neil Chauhan
ac37bf3631 Refactored the current ️/Star feature to ❤️/Love/Favourite feature. (#908)
* Added setRating feature to AlbumListView

* Refactored the iconography from  to ❤️

* Refactored the current component from StarButton to LoveButton

* Refactored all translations from Starred to Loved, and all props from showStar to showLove

* Refactored useToggleStar hook to useToggleLove

* rebased repository from master and removed stray commmits

* Refactored handler name from TOGGLE_STAR to TOGGLE_LOVE in PlayerToolbar.js

* Change "starred" translation to "Favorite"

Co-authored-by: Deluan <deluan@navidrome.org>
2021-03-26 23:56:19 -04:00
Deluan
db208600e4 Fix theme not being applied to Player's audioTitle 2021-03-26 22:22:36 -04:00
Arbaz Ahmed
01ba00ccdd New component for mobile Artist List (#891)
Fixes #890
2021-03-25 22:45:21 -04:00
Yash Jipkate
e575825c33 Add / to _ mapping for paths based on tags. (#888)
Closes  #592
2021-03-25 21:48:28 -04:00
Ritik Pandey
5abc215270 Hide BulkActionsToolbar after removing songs from playlist (#898) 2021-03-25 21:40:31 -04:00
Deluan Quintão
210f34bbbe Update CONTRIBUTING.md 2021-03-24 23:36:48 -04:00
Kushal Kumar
5e70e0702c docs(contributing.md): Contributing guidelines added (#831)
* docs(contributing.md): Contributing guidelines added - I827

Signed-off-by: k-kumar-01 <kushalkumargupta4@gmail.com>

* docs(contributing.md): Contributing guidelines added - I827

Updated the issue number to match the existing issue
Changed channel from IRC to Discord

Signed-off-by: k-kumar-01 <kushalkumargupta4@gmail.com>

* Fix typos, remove issue template

Co-authored-by: Deluan <deluan@navidrome.org>
2021-03-24 23:33:48 -04:00
Danshil Kokil Mungur
a85b70e9db feat(github): add issue templates (#892) 2021-03-24 23:21:48 -04:00
Josep Mª Domingo
515aa7108b Move logger middleware to capture routing errors (ex: 405). (#877)
* Fix #836

* Remove requestLogger middleware from MountRouter
2021-03-24 23:17:36 -04:00
rohitgeddam
cdb387b22f Properly break long comment lines. Fix #855 (#856)
Make ui responsive on smaller screen when the comment block is longer
2021-03-24 23:14:10 -04:00
Deluan
d5434d4169 Add 'lint to pre-push git hook 2021-03-24 12:21:20 -04:00
Deluan
c46aa72ede Add lint script to UI project 2021-03-24 12:05:58 -04:00
Deluan
4b68260b83 Move constant to consts.go file 2021-03-24 10:21:31 -04:00
Deluan Quintão
ba922bbfce Update translations (#894)
* Add Ukrainian translation

* Update cs.json (POEditor.com)

* Update nl.json (POEditor.com)

* Update eo.json (POEditor.com)

* Update de.json (POEditor.com)
2021-03-23 22:28:22 -04:00
dependabot[bot]
b4a2683e65 Bump @testing-library/user-event from 12.6.2 to 13.0.7 in /ui
Bumps [@testing-library/user-event](https://github.com/testing-library/user-event) from 12.6.2 to 13.0.7.
- [Release notes](https://github.com/testing-library/user-event/releases)
- [Changelog](https://github.com/testing-library/user-event/blob/master/CHANGELOG.md)
- [Commits](https://github.com/testing-library/user-event/compare/v12.6.2...v13.0.7)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-23 22:26:09 -04:00
ImgBotApp
7e225c0f47 [ImgBot] Optimize images
*Total -- 8,303.62kb -> 6,130.18kb (26.17%)

/ui/src/icons/paused-light.png -- 0.81kb -> 0.29kb (63.6%)
/.github/screenshots/ss-mobile-login.png -- 1,189.03kb -> 736.30kb (38.08%)
/ui/src/icons/playing-dark.gif -- 2.29kb -> 1.58kb (31.29%)
/ui/src/icons/playing-light.gif -- 2.29kb -> 1.58kb (31.29%)
/.github/screenshots/ss-mobile-player.png -- 1,257.85kb -> 886.30kb (29.54%)
/ui/public/apple-touch-icon-152x152.png -- 9.51kb -> 6.74kb (29.21%)
/ui/public/mstile-144x144.png -- 9.63kb -> 6.88kb (28.55%)
/ui/public/mstile-70x70.png -- 6.59kb -> 4.73kb (28.22%)
/ui/public/apple-touch-icon-180x180.png -- 11.34kb -> 8.18kb (27.89%)
/ui/public/apple-touch-icon.png -- 11.34kb -> 8.18kb (27.89%)
/ui/public/apple-touch-icon-76x76.png -- 4.52kb -> 3.27kb (27.68%)
/ui/public/apple-touch-icon-120x120.png -- 7.27kb -> 5.30kb (27.2%)
/ui/public/android-chrome-192x192.png -- 13.14kb -> 9.78kb (25.59%)
/ui/public/mstile-150x150.png -- 9.57kb -> 7.20kb (24.8%)
/ui/public/apple-touch-icon-60x60.png -- 3.40kb -> 2.57kb (24.51%)
/ui/src/icons/android-icon-72x72.png -- 6.25kb -> 4.75kb (24%)
/.github/screenshots/ss-desktop-player.png -- 5,016.75kb -> 3,833.46kb (23.59%)
/resources/logo-192x192.png -- 17.10kb -> 13.21kb (22.78%)
/ui/public/android-chrome-512x512.png -- 38.32kb -> 29.78kb (22.3%)
/ui/public/mstile-310x310.png -- 21.04kb -> 16.49kb (21.61%)
/ui/public/mstile-310x150.png -- 10.35kb -> 8.12kb (21.56%)
/resources/navidrome-600x600.png -- 369.74kb -> 297.43kb (19.56%)
/ui/public/favicon-32x32.png -- 2.18kb -> 1.80kb (17.52%)
/.github/screenshots/ss-mobile-album-view.png -- 282.95kb -> 236.01kb (16.59%)
/ui/src/icons/paused-dark.png -- 0.34kb -> 0.29kb (14.25%)

Signed-off-by: ImgBotApp <ImgBotHelp@gmail.com>
2021-03-23 10:29:13 -04:00
Arbaz Ahmed
4e44d841dd New component for song display in song list (#833)
* added new component SONGSIMPLELIST for smaller displays

* added new component SONGSIMPLELIST for smaller displays

* added new component SONGSIMPLELIST for smaller displays

* Updated songsimplelist

Removed truncation

* removed garbage code

* refactored some issues of overlapping

* refactored some issues of overlapping

* changed the song ui design

* refactored some bugs in artist display

* refactored some bugs in artist display

* removed garbage dependencies

* removed div bugs

* added all the logic to the component itself
2021-03-23 00:12:19 -04:00
rochakjain361
b552eb15b3 Make the version number clickable for the SNAPSHOT version in development docker build (#843)
* Make the version number clickable for the SNAPSHOT version while using development docker build

* Update the snapshot version link in to view list of commits since the release

* Create a new component for the link to the version

* Add tests and refactored a bit

Co-authored-by: Deluan <deluan@navidrome.org>
2021-03-22 23:34:34 -04:00
Deluan
190bcd836e Bump @testing-library's dependencies 2021-03-22 15:40:06 -04:00
dependabot-preview[bot]
488a05bc5a Create Dependabot config file 2021-03-22 15:12:29 -04:00
Deluan
2daefb851a Bump golangci-lint to 1.38.0 2021-03-22 14:52:16 -04:00
Deluan
7414216094 Bump gomega and ginkgo versions 2021-03-22 14:49:57 -04:00
Deluan
300a0292ba Add timestamp indexes 2021-03-22 13:38:20 -04:00
Deluan
a4abe6ea2b Rename migration package to migrations 2021-03-22 13:38:04 -04:00
Nelyah
2671933791 Update Spanish translation
This also fixes some minor typos
2021-03-22 10:23:26 -04:00
Nelyah
da6556bb78 Update French translation 2021-03-22 10:23:26 -04:00
Deluan
c33c71ae6d Comment out flaky tests 2021-03-22 09:52:29 -04:00
certuna
1ea3d005e8 clear the ServiceWorker cache on logout (#854)
* Update authProvider.js

This fixes https://github.com/navidrome/navidrome/issues/613 : clears the ServiceWorker cache on logout. Based on this code: https://developer.mozilla.org/en-US/docs/Web/API/CacheStorage/keys . Seems to work on macOS and iOS, but please test on other platforms.

* Format code with `prettier`

Co-authored-by: Deluan <deluan@navidrome.org>
2021-03-21 14:34:10 -04:00
Ritik Pandey
9fb55d4025 Add duplicate song warning. Fix #554
* duplicate_song_warning added

* dialog_for_multiple_songs

* skip button updated

* duplicate_song_skip import removed

* duplicate_song msg updated

* handleSkip and checkDuplicateSong func modified

* Update AddToPlaylistDialog.js

* prettier applied

* go.sum file added

* duplicated songs bug fixed
2021-03-21 13:29:35 -04:00
Yash Jipkate
3e0e11c01e Fix #260: Add Auto theme preference to set theme automatically. (#835)
* Auto theme preference added

* Fix lint

* Add and use AUTO from consts

* Add shared custom hook to get current theme

* Moved up 'Auto' choice

* AUTO -> AUTO_THEME_ID & extract useCurrentTheme to file

* Liberalise theme setting

* Add tests
2021-03-21 13:19:43 -04:00
Deluan
fa479f0a9a Show go mod download commands 2021-03-17 10:33:40 -04:00
Ketan Gupta
8b6e32588c improved makefile message to developer 2021-03-17 10:27:55 -04:00
Dnouv
b62c270b7f fix min-width of AlbumDetails 2021-03-16 23:19:11 -04:00
Ye61123
5488a829c7 Update zh-Hans.json
Improve the simplified Chinese translation accuracy and fix the parts that are not translated.
2021-03-16 23:13:40 -04:00
Daniel Morante
9d87eefd6a Create rc.d startup script for FreeBSD
Same startup script used by the port multimedia/navidrome
2021-03-16 23:11:42 -04:00
Deluan
03afb4ff0e Call go mod tidy after go mod download to undo any changes to go.sum 2021-03-16 12:49:20 -04:00
Deluan
63b9353452 Lowering the expectations caused by the name :) 2021-03-14 12:51:08 -04:00
k-kumar-01
72d6df15c6 style: New Theme Added - Spotify
Signed-off-by: k-kumar-01 <kushalkumargupta4@gmail.com>
2021-03-14 12:51:08 -04:00
Deluan
18fda0d954 Update DevContainer to GoLang 1.16 2021-03-12 18:43:03 -05:00
Deluan
34516ccf97 Bump github.com/sirupsen/logrus from 1.7.0 to 1.8.1 2021-03-12 18:19:39 -05:00
Deluan
720e2357b7 Add option to sort Recently Added by file's mtime instead of time of import. 2021-03-12 18:18:35 -05:00
Deluan
1ec105a245 Invalidate cached images when album changes 2021-03-12 15:41:11 -05:00
Deluan
0049d8d311 Bump GoLang to 1.16.2 for releases 2021-03-12 15:14:12 -05:00
Deluan Quintão
2d528bbc87 Remove dependency of go-bindata (#818)
* Use new embed functionality for serving UI assets

* Use new embed functionality for serving resources. Remove dependency on go-bindata

* Remove Go 1.15
2021-03-12 11:06:51 -05:00
rochakjain361
5a259ef3ff Make the version number in the about dialog clickable (#817)
* Make the version number in the about dialog clickable

* Fix prettier errors

* Fix build errors
2021-03-12 10:26:41 -05:00
Nelyah
43f2d82956 Accept more recent node and Go versions when building dev or server
This will allow developers to experiment with different versions of Go.
2021-03-11 23:02:13 -05:00
Nelyah
dff18101bb Bump Go version number in go.mod to 1.16
This will allow to use 'embed' and 'fs' packages.
This also makes the check for the Go environment when running the
Makefile fail if Golang version isn't 1.16.x
2021-03-11 23:02:13 -05:00
dpirad007
6cf15748c4 SelectPlaylist Input mobile view fix 2021-03-11 22:53:55 -05:00
Deluan
54a3394559 Upgrade to GoLang 1.16.0 2021-02-19 21:00:38 -05:00
Deluan
8436e18175 Update pipeline tests to Go 1.16.0 2021-02-19 19:52:14 -05:00
Deluan
a140c222c2 Fix race condition in test 2021-02-19 19:36:55 -05:00
Deluan
64ceb5371b Revert "Bump github.com/go-chi/chi from 1.5.1 to 1.5.2"
This caused a panic and needs more investigation:

http: superfluous response.WriteHeader call from github.com/go-chi/chi/middleware.Recoverer.func1.1 (recoverer.go:33)

 panic: interface conversion: *middleware.compressResponseWriter is not io.ReaderFrom: missing method ReadFrom

 -> github.com/go-chi/chi/middleware.(*httpFancyWriter).ReadFrom
 ->   /Users/deluan/go/pkg/mod/github.com/go-chi/chi@v1.5.2/middleware/wrap_writer.go:135
2021-02-15 14:21:43 -05:00
Deluan
7eb99d0b8d Remove duplicated call to split 2021-02-15 14:18:57 -05:00
Deluan
07f815edfd go mod tidy 2021-02-15 14:08:49 -05:00
Deluan
781155ff39 Replace cursor with pointer when hovering over an expandable comment.
Fixes https://github.com/navidrome/navidrome/issues/637#issuecomment-778599670
2021-02-15 14:05:34 -05:00
dependabot-preview[bot]
0d6717ce69 Bump github.com/spf13/cobra from 1.1.1 to 1.1.3
Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.1.1 to 1.1.3.
- [Release notes](https://github.com/spf13/cobra/releases)
- [Changelog](https://github.com/spf13/cobra/blob/master/CHANGELOG.md)
- [Commits](https://github.com/spf13/cobra/compare/v1.1.1...v1.1.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-02-15 13:21:16 -05:00
dependabot-preview[bot]
e0198f741f Bump github.com/go-chi/chi from 1.5.1 to 1.5.2
Bumps [github.com/go-chi/chi](https://github.com/go-chi/chi) from 1.5.1 to 1.5.2.
- [Release notes](https://github.com/go-chi/chi/releases)
- [Changelog](https://github.com/go-chi/chi/blob/master/CHANGELOG.md)
- [Commits](https://github.com/go-chi/chi/compare/v1.5.1...v1.5.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-02-15 13:19:53 -05:00
Deluan
cfc9162729 Use a Waiter diode, to avoid constant CPU usage. Fixes #777 2021-02-13 12:08:32 -05:00
Deluan
48847ae479 Don't break if it tries to render ContextMenu without data. Fix #776 2021-02-13 12:04:02 -05:00
CgX
fbe6ecea95 Update fr.json 2021-02-11 09:13:24 -05:00
Nelyah
9d098e2302 Update French translation 2021-02-10 10:00:33 -05:00
Deluan
107803e037 Update list of Not Implemented / Gone Subsonic API endpoints 2021-02-09 21:25:14 -05:00
Gosz
1c285439ca Update spanish translation 2021-02-09 16:47:01 -05:00
Deluan
bde8692add Update Dutch translation 2021-02-09 16:46:29 -05:00
Deluan
1d681d92d3 Better explanation of NewSpreadFS 2021-02-09 15:33:34 -05:00
Deluan
157faad028 Rename ExternalInfo to ExternalMetadata 2021-02-09 15:33:33 -05:00
Deluan
5fdd8b32d7 Move utilitarian/generic packages to utils: lastfm, spotify, gravatar, cache, and pool 2021-02-09 15:33:33 -05:00
Deluan
b855fe865e Add artist ID to agent's interfaces 2021-02-09 11:19:32 -05:00
Andy Klimczak
501af14186 Upgrade pipeline to use docker/setup-buildx-action
Issue #563
2021-02-08 21:05:27 -05:00
Deluan
29465d92a7 Add Chinese Traditional translation (thanks @Leitftw) 2021-02-08 19:09:42 -05:00
Deluan
7cc026ac35 Add some info about how to create new agents 2021-02-08 17:18:43 -05:00
Deluan
fefbe0b117 Cleanup, add Placeholder agent 2021-02-08 16:54:51 -05:00
Deluan
e5cbfac483 Implement TopSongs 2021-02-08 16:54:51 -05:00
Deluan
e1cb52689e Implement SimilarSongs 2021-02-08 16:54:51 -05:00
Deluan
84a50d5dce Use MBID in calls to Last.FM, if it is available 2021-02-08 16:54:51 -05:00
Deluan
6c1fc5f836 Clean names before calling agents 2021-02-08 16:54:51 -05:00
Deluan
a76a52e99a Get MBID first, if it is not yet available 2021-02-08 16:54:51 -05:00
Deluan
52a407b84b Clean up, comments and logs 2021-02-08 16:54:51 -05:00
Deluan
365dff6435 Fix lint errors 2021-02-08 16:54:51 -05:00
Deluan
877cdf1d5c Get images 2021-02-08 16:54:51 -05:00
Deluan
28cdf1e693 Add a cached http client 2021-02-08 16:54:51 -05:00
Deluan
9d24106066 Incomplete implementation of agents 2021-02-08 16:54:51 -05:00
dependabot-preview[bot]
d8a4db36ef Bump react-icons from 4.1.0 to 4.2.0 in /ui
Bumps [react-icons](https://github.com/react-icons/react-icons) from 4.1.0 to 4.2.0.
- [Release notes](https://github.com/react-icons/react-icons/releases)
- [Commits](https://github.com/react-icons/react-icons/compare/v4.1.0...v4.2.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-02-08 11:29:27 -05:00
Deluan
8799358a04 go mod tidy 2021-02-07 14:26:32 -05:00
dependabot-preview[bot]
58fd5326f5 Bump github.com/onsi/gomega from 1.10.4 to 1.10.5
Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.10.4 to 1.10.5.
- [Release notes](https://github.com/onsi/gomega/releases)
- [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/gomega/compare/v1.10.4...v1.10.5)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-02-06 22:21:02 -05:00
dependabot-preview[bot]
e9066690fd Bump github.com/onsi/ginkgo from 1.14.2 to 1.15.0
Bumps [github.com/onsi/ginkgo](https://github.com/onsi/ginkgo) from 1.14.2 to 1.15.0.
- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v1.14.2...v1.15.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-02-06 22:09:41 -05:00
Deluan
a427d6c940 Fix Docker pulls badge 2021-02-06 22:04:01 -05:00
Deluan
949bcde9f5 Move project to Navidrome GitHub organization 2021-02-06 21:47:19 -05:00
Deluan
6ee45a9ccc Move project to Navidrome GitHub organization 2021-02-06 21:46:35 -05:00
Deluan
bc609be3c9 Fix space hotkey 2021-02-05 13:10:58 -05:00
Deluan
4b373560c6 Do not trigger next/prev event handlers when Cmd (meta) is pressed 2021-02-05 13:03:36 -05:00
Deluan
bb6197360b Upgrade to latest go-chi 2021-02-05 12:13:35 -05:00
Deluan
2f4a5fd9ae Fix test suite name 2021-02-04 15:44:44 -05:00
Deluan
26f838167e Add tests to diode 2021-02-04 15:42:08 -05:00
Deluan
c7af3b8256 Add test to Event 2021-02-04 12:43:46 -05:00
Deluan
7adacbac0d Removed event.type from SSE, as it was causing the browser to hang.
Needs more investigation, but for now, back to the simple message format
2021-02-04 12:37:06 -05:00
Deluan
77fc4841e4 Remove option to download discs of a set 2021-02-03 23:05:15 -05:00
Aries
f43999842f Update Japanese translation (#757)
* Fix ja translation

* Fix japanese translation
2021-02-03 22:08:11 -05:00
Deluan
64b22688ba Fix Portuguese transaltion 2021-02-03 19:27:14 -05:00
Deluan
e9dad3dd67 Update Portuguese transaltion 2021-02-03 19:13:44 -05:00
Deluan
847531391d Help dialog with available hotkeys 2021-02-03 19:08:03 -05:00
Deluan
a168f46b95 Better hotkey organization 2021-02-03 18:29:33 -05:00
Deluan
22145e070f Replace custom chunking logic with a utils.BreakUpStringSlice call 2021-02-03 17:26:03 -05:00
Deluan Quintão
9a3e75be00 Add tests with GoLang 1.16-RC to the pipeline 2021-02-03 14:39:59 -05:00
Deluan
618d5fc81f Better duration formatting in logs 2021-02-03 13:04:20 -05:00
Deluan
9668263235 Logging when triggering manual scan 2021-02-03 00:27:59 -05:00
Deluan
9959862791 Replace react-hotkeys-hook with react-hotkeys 2021-02-02 23:13:18 -05:00
Deluan
8e02659441 Do not sanitize Album comments. This was already handled in the backend, when importing. Fix #715 2021-02-02 19:08:00 -05:00
Deluan
905c685696 Use diodes instead of channels in SSE broker 2021-02-02 18:55:08 -05:00
Deluan
591a5344ac Workaround to remember logarithmic volume 2021-02-02 18:29:20 -05:00
Deluan
e79922def1 Fix React "unique key prop" warning 2021-02-02 18:00:06 -05:00
Deluan
a3eb14d7f4 Fix displaying album year when viewing an artist's albums 2021-02-02 17:49:11 -05:00
Deluan
3b52df5efd Update golangci-lint in the pipeline 2021-02-01 16:54:40 -05:00
Deluan
031756caf6 Update canisue-lite 2021-02-01 16:45:33 -05:00
Deluan
70470e4c81 Increase heap memory for JS job 2021-02-01 16:40:55 -05:00
Deluan
58a52c31c2 Turn off memory profiling, saving a couple of megabytes 2021-02-01 16:30:06 -05:00
Deluan
1f3bc4d202 Use tools.go commands without installing 2021-02-01 16:16:30 -05:00
Deluan
950ba9f77e Bump github.com/google/wire from 0.4.0 to 0.5.0 2021-02-01 08:54:35 -05:00
Deluan
861b406575 Use new simplified uuid.NewString() syntax 2021-02-01 01:22:31 -05:00
Deluan
b47ec02f02 Reenable ImageCache and ActivityPanel as default 2021-02-01 00:31:02 -05:00
Deluan
7cc9fbaaf9 Revert: Use modified time as updated_at and created_at when refreshing/creating albums 2021-02-01 00:30:45 -05:00
Deluan
9807b0b6c0 Use modified time as updated_at and created_at when refreshing/creating albums. Closes #717 2021-01-31 22:17:40 -05:00
Deluan
1af78e9503 Build and publish Docker image for armv6 (closes #747) 2021-01-31 19:36:18 -05:00
Deluan Quintão
02c228da1b Update Translations (#751)
* Update zn.json (POEditor.com)

* Update cs.json (POEditor.com)

* Update da.json (POEditor.com)

* Update nl.json (POEditor.com)

* Update eo.json (POEditor.com)

* Update fr.json (POEditor.com)

* Update de.json (POEditor.com)

* Update it.json (POEditor.com)

* Update ja.json (POEditor.com)

* Update pl.json (POEditor.com)

* Update pt.json (POEditor.com)

* Update ru.json (POEditor.com)

* Update es.json (POEditor.com)

* Update th.json (POEditor.com)

* Update tr.json (POEditor.com)

* Update pt.json (POEditor.com)
2021-01-31 19:04:48 -05:00
Deluan
f7aa5c452c Add translation to skip_nav 2021-01-31 19:01:56 -05:00
Deluan Quintão
6c53ce4bb3 Update en.json (POEditor.com) 2021-01-31 18:57:07 -05:00
Deluan
f325907da4 Upgrade react-admin to 3.12.0 2021-01-31 18:50:27 -05:00
Deluan
2c89e0dc13 Bump react-drag-listview from 0.1.7 to 0.1.8 in /ui 2021-01-31 18:30:26 -05:00
Deluan
7d23eca721 Bump @testing-library dependencies 2021-01-31 18:26:25 -05:00
Deluan
77705e4d79 Upgrade react-jinke-music-player to 4.21.2
Enable fadeIn/fadeOut when pausing/playing and logarithmic volume (fix #668)
2021-01-31 18:23:32 -05:00
Deluan
afbd9a3b37 Bump github.com/google/uuid from 1.1.2 to 1.2.0 2021-01-31 17:56:06 -05:00
Deluan
41ec44ae1b Bump github.com/pressly/goose from 2.6.0+incompatible to 2.7.0+incompatible 2021-01-31 17:48:08 -05:00
Deluan
dc8051ed53 Bump github.com/golangci/golangci-lint from 1.33.0 to 1.36.0 2021-01-31 17:43:03 -05:00
Deluan
c5686c4884 Replace periodic scanner cancellation channel with a context 2021-01-31 17:37:54 -05:00
Deluan
9520c30c32 Fix "failed" Subsonic response. Fix #716 2021-01-07 08:24:13 -05:00
Deluan
069199b2d8 Removed invalid comment 2021-01-03 18:16:37 -05:00
Deluan
05ffdede56 Bump react-hotkeys-hook from 2.4.0 to 3.0.0 in /ui 2021-01-03 18:05:31 -05:00
Deluan
b12b3c49bd Bump @material-ui/lab from 4.0.0-alpha.56 to 4.0.0-alpha.57 2021-01-03 18:00:39 -05:00
Deluan
0f29da966e Bump @testing-library/user-event from 12.5.0 to 12.6.0 in /ui 2021-01-03 17:56:25 -05:00
Deluan
81d9750fa6 Bump react-icons from 3.11.0 to 4.1.0 in /ui 2021-01-03 17:52:53 -05:00
dependabot-preview[bot]
0af54e43dc Bump uuid from 8.3.1 to 8.3.2 in /ui
Bumps [uuid](https://github.com/uuidjs/uuid) from 8.3.1 to 8.3.2.
- [Release notes](https://github.com/uuidjs/uuid/releases)
- [Changelog](https://github.com/uuidjs/uuid/blob/master/CHANGELOG.md)
- [Commits](https://github.com/uuidjs/uuid/compare/v8.3.1...v8.3.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-01-02 13:26:27 -05:00
Steve Richter
0d35148152 Check for window.isSecureContext when determining Notification support 2020-12-28 20:46:11 -05:00
Deluan
7c23bd0890 Fix log message, as it is also used for taglib 2020-12-25 12:45:38 -05:00
Deluan
10e52bdd3f Use order_* fields for sorting by album and artist 2020-12-25 12:37:16 -05:00
Deluan
9e84ce42b5 Use same album songs order for UI and Subsonic API 2020-12-25 12:37:16 -05:00
lbonn
15b289201a Fall back to media file path when sorting
If files cannot be sorted by disc and track id, try by artist then
title.

One use case is a loose compilation of files with same album, album
artist, and no track numbers. File order was then undetermined, in
practice depended on insertion order in the database.
2020-12-25 12:37:16 -05:00
Deluan
cd1c693a23 Remove superfluous argument 2020-12-24 10:39:10 -05:00
Deluan
2073871fa1 Use netgo (instead of C bindings). Fix #700 2020-12-23 15:29:18 -05:00
Deluan
dab83c4f6a Disambiguate tracks by AlbumArtist when sorting by album 2020-12-23 11:38:40 -05:00
Deluan
db5b9246dd Handle more sort/order cases 2020-12-23 11:37:38 -05:00
Deluan
cdae4347a6 Make ServerStart variable global 2020-12-21 11:39:38 -05:00
Deluan
8c063c4f0c Removed unused variable 2020-12-21 10:01:37 -05:00
Deluan
14b060a42a Only close connection if the write times out 2020-12-20 15:21:46 -05:00
Deluan
1804fb3e50 Fix duration formatting, add days and don't show 60 seconds 2020-12-20 13:29:09 -05:00
Deluan
ea2f94658a Error should always be nil 2020-12-20 13:28:33 -05:00
Deluan
4b38a13243 Make event handlers naming consistent (camelCase) 2020-12-20 13:28:11 -05:00
Deluan
29817db9f2 Simplify worker pool 2020-12-15 20:48:06 -05:00
dependabot-preview[bot]
fc4ddee122 Bump github.com/onsi/gomega from 1.10.3 to 1.10.4
Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.10.3 to 1.10.4.
- [Release notes](https://github.com/onsi/gomega/releases)
- [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/gomega/compare/v1.10.3...v1.10.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-12-14 08:06:22 -05:00
Deluan
a241865209 Add elapsed time when scanner does not detect any new changes 2020-12-13 20:48:16 -05:00
Deluan
ea09629803 Fix another possible race condition 2020-12-13 20:17:20 -05:00
Deluan
f2fd7ed016 Fix race condition that could cause multiple EventSource connections from the same client 2020-12-13 14:44:57 -05:00
Deluan
4f90fa9924 Add denormalized list of artist_ids to album, to speed-up artist's albums queries
This will be removed once we have a proper many-to-many relationship between album and artist
2020-12-13 14:05:48 -05:00
Deluan
f86bc070de Don't break on login when activity panel is disabled 2020-12-13 12:16:02 -05:00
Deluan
1d338417e9 Make done channel buffered 2020-12-13 11:58:00 -05:00
Deluan
d685aefab3 Don't ever stop the listen go routine 2020-12-12 23:04:50 -05:00
Deluan
e27d917bd4 Forgot to allocate done channel 2020-12-12 21:10:43 -05:00
Deluan
8b92796a5c Disconnect the client if the output buffer fills up 2020-12-12 18:26:30 -05:00
Deluan
17833cd9d2 Make names more consistent 2020-12-12 13:46:36 -05:00
Deluan
e2969aa34c Use non-blocking event sending 2020-12-12 13:35:49 -05:00
Deluan
500da8bc7b Bump react-icons from 3.11.0 to 4.1.0 2020-12-11 12:21:53 -05:00
Deluan
db3b53ff43 Bump prettier from 2.1.2 to 2.2.1 2020-12-11 12:13:21 -05:00
Deluan
291d28887b Bump @testing-library dependencies 2020-12-11 12:10:46 -05:00
Deluan
3c6b8d18cd Bump golangci-lint from 1.32.2 to 1.33.0 2020-12-11 11:40:06 -05:00
Deluan
0111d3ae60 Bump react-admin from 3.10.2 to 3.10.4 2020-12-11 11:34:25 -05:00
Deluan
0cde8cbf2e Fix logging field case 2020-12-11 11:26:06 -05:00
dependabot-preview[bot]
b08eac54fd Bump github.com/Masterminds/squirrel from 1.4.0 to 1.5.0
Bumps [github.com/Masterminds/squirrel](https://github.com/Masterminds/squirrel) from 1.4.0 to 1.5.0.
- [Release notes](https://github.com/Masterminds/squirrel/releases)
- [Commits](https://github.com/Masterminds/squirrel/compare/v1.4.0...v1.5.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-12-10 16:04:29 -05:00
dependabot-preview[bot]
f7411558e3 [Security] Bump ini from 1.3.5 to 1.3.7 in /ui (#686)
Bumps [ini](https://github.com/isaacs/ini) from 1.3.5 to 1.3.7. **This update includes a security fix.**
- [Release notes](https://github.com/isaacs/ini/releases)
- [Commits](https://github.com/isaacs/ini/compare/v1.3.5...v1.3.7)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-12-10 15:38:44 -05:00
Deluan
a74b365feb Only adds route to /events if Activity Panel is enabled 2020-12-09 15:33:37 -05:00
Deluan
350f1dc951 Docker run does not need to be interactive for building snapshots 2020-12-07 13:37:22 -05:00
Deluan
25ae1c6cdd Return album art as a Reader 2020-12-02 09:13:36 -05:00
Deluan
0aaa261a71 Don't show warning about image cache disabled if pre-cache warmer is disabled 2020-12-02 08:52:35 -05:00
Deluan Quintão
f2a8308925 Update translations (#673)
* Update zn.json (POEditor.com)

* Update nl.json (POEditor.com)

* Update de.json (POEditor.com)

* Update ja.json (POEditor.com)
2020-12-01 16:34:50 -05:00
Deluan
240149cda4 DIsable Image Cache by default.
See: https://github.com/deluan/navidrome/issues/446#issuecomment-736574447
2020-12-01 18:01:16 +00:00
Deluan
ae58ac6a6c Add configuration for VSCode's Remote Container development 2020-12-01 17:57:29 +00:00
Deluan
a8c5fa6d49 Fix file descriptor leak in SSE implementation.master
See https://github.com/deluan/navidrome/issues/446#issuecomment-736296465
2020-12-01 09:24:44 -05:00
Deluan
9414ce6549 Bump react-admin to 3.10.2 2020-11-29 22:55:07 -05:00
Deluan
7bd31da0d5 Fix console warning about required property 2020-11-29 20:31:07 -05:00
Deluan
975579ab26 Add option for player to report real paths in Subsonic API. Closes #625 2020-11-28 10:25:23 -05:00
Deluan
7becc18da9 Don't explode when record is not loaded yet 2020-11-28 09:44:07 -05:00
Deluan
4ca98fb827 Add hotkey s to toggle star.
Refers to #585
2020-11-28 00:52:38 -05:00
Deluan
aae66cfcf0 Always show context menu if not in desktop 2020-11-27 23:52:23 -05:00
Deluan
2010fcf4ca Remove possible undefined error 2020-11-27 18:53:25 -05:00
Deluan
2ffb28fc2d Replace classnames with clsx 2020-11-27 18:27:32 -05:00
Deluan
0b729e1cf9 Hide star completely if in Playlist view 2020-11-27 16:24:22 -05:00
Deluan
ab856e3dd1 Wrap comment text. Fixes #637 2020-11-27 16:02:02 -05:00
Deluan
90c407b7f6 Also use PureDatagridRow to speed up SongDatagrid 2020-11-27 14:24:22 -05:00
Deluan
f7d1b80b69 Simplify AudioTitle on mobile view 2020-11-27 13:30:51 -05:00
Deluan
2b95422e88 Make "star" column aligned with context menu in Album List view 2020-11-27 13:13:51 -05:00
Deluan
7d075b1882 Make SongDatagrid faster by using PureDatagridBody 2020-11-27 13:13:51 -05:00
Deluan
0e9b0d466c Hide row when reordering playlist 2020-11-27 13:13:51 -05:00
Deluan
e5c7819586 Fix playlists 2020-11-27 13:13:51 -05:00
Deluan
a42fb024be Fix song context menu "on hover" visibility 2020-11-27 13:13:51 -05:00
Deluan
f5808288ab Fix Album View 2020-11-27 13:13:51 -05:00
Deluan
3209430ebd Fix artists 2020-11-27 13:13:51 -05:00
Deluan
d9893cf84d Bump React-Admin to 3.10.1 2020-11-27 13:13:51 -05:00
Deluan
9064697123 Remove stray console.log 2020-11-27 13:13:51 -05:00
Deluan
b6c578e3a2 Change format of events sent by server, leveraging event type and id 2020-11-25 20:46:21 -05:00
Deluan
cc5eaf4caf go mod tidy 2020-11-25 19:09:08 -05:00
Deluan
f29bb211d1 Better termination handling in Scanner's progress 2020-11-25 19:05:36 -05:00
Deluan Quintão
3ad36ebd2a Update translations (#644)
* Update eo.json (POEditor.com)

* Update fr.json (POEditor.com)

* Update pt.json (POEditor.com)
2020-11-25 15:58:45 -05:00
Deluan
31d6508b1d Remove reactjkplayer's audioTitle, as it is not used and only causes warnings in the console 2020-11-25 15:37:10 -05:00
Steve Richter
bc72f41180 Adjust AudioTitle in Player
- Show info on 2 lines
- Show album
2020-11-25 15:37:10 -05:00
Deluan
63171368ed Disable Activity Panel by default.
You'll need to set `DevActivityPanel` (or `ND_DEVACTIVITYPANEL`) to `true` to re-enable it
2020-11-25 15:29:46 -05:00
Deluan
5137407377 Add "keepalive" resource. It was causing issues in Firefox when using the dataProvider 2020-11-23 21:28:09 -05:00
Deluan
92ba658606 Don't panic if log parameters are invalid 2020-11-22 17:12:53 -05:00
Zane van Iperen
763a3ef267 Fix startup failure when image cache is disabled.
Fixes #655
2020-11-22 17:03:09 -05:00
Deluan
a89afb5fcf Fix aspect ratio in Album show view 2020-11-22 15:03:41 -05:00
Jason Walton
69b2fe92f5 Fix aspect ratio for non-square album art. 2020-11-22 15:03:41 -05:00
stefanomarty
3996764486 Update it.json
Just a correction to a few mistypings.
2020-11-21 18:14:44 -05:00
Deluan
a288e7e858 Allow the NotificationToggle to be in sync with the selected option in the browser 2020-11-21 02:03:54 -05:00
Steve Richter
14525cd056 Fix formatting 2020-11-21 02:03:54 -05:00
Steve Richter
2397a7e464 Add Desktop Notifications 2020-11-21 02:03:54 -05:00
Steve Richter
b8d47d1db4 Fix default getPerPage for 'md' widths 2020-11-21 01:34:36 -05:00
Deluan
48a6ba2956 Bump GoLang to 1.15.5 2020-11-20 21:55:03 -05:00
Deluan
3e8bee4f65 Make eventStream connection/reconnection more reliable
Also more logs on the server
2020-11-20 20:27:30 -05:00
Deluan
c8c95bfb47 Remove React console warning 2020-11-20 19:59:54 -05:00
Deluan
666b058ce4 Request album covers when DevFastAccessCoverArt is true 2020-11-18 16:59:06 -05:00
JG
d6066c514d Updated spanish translation nov 2020 (#642)
* Updatind translation

* Updatind translation

* Update spanish translation

Co-authored-by: Gosz <gosh@4geeksmx.com>
2020-11-18 16:58:57 -05:00
Deluan
3c4903bc4e No need to create a new instance of the Artwork service 2020-11-17 12:16:13 -05:00
Deluan
af4609727c Goto album page when clicking on player's album cover 2020-11-17 10:33:53 -05:00
Deluan
53b2cdd33d Update Thai language 2020-11-16 16:49:13 -05:00
Deluan
088af9004a Only try to check cover art file for lastUpdated if fast access is not set 2020-11-16 16:39:31 -05:00
Deluan
1ee39835dd Retry connecting to the events endpoint more frequently on first load 2020-11-16 15:38:03 -05:00
Deluan Quintão
972a94dbf0 Update translations (#623)
* Update fr.json (POEditor.com)

* Update de.json (POEditor.com)

* Update es.json (POEditor.com)

* Update tr.json (POEditor.com)

* Update ru.json (POEditor.com)

* Add Thai translation, thanks to AZ11244

* Update cs.json (POEditor.com)

* Update zn.json (POEditor.com)

* Update da.json (POEditor.com)

* Update nl.json (POEditor.com)

* Update it.json (POEditor.com)

* Update th.json (POEditor.com)
2020-11-16 15:22:37 -05:00
Deluan
b87f7b6126 Bump react-redux from 7.2.1 to 7.2.2 2020-11-16 15:09:25 -05:00
Deluan
f09a6423f7 Bump react-dom from 16.13.1 to 16.14.0 2020-11-16 15:08:02 -05:00
Deluan
49d28d34b4 Bump jwt-decode from 3.0.0 to 3.1.2 2020-11-16 15:06:57 -05:00
Deluan
8b09f0369c Bump react-ga from 3.1.2 to 3.2.1 2020-11-16 15:03:16 -05:00
Deluan
41cbd3aba7 Bump @testing-library dependencies 2020-11-16 15:01:52 -05:00
Deluan
a1dcb9a4e3 Show folders scanned instead of files scanned 2020-11-16 00:36:12 -05:00
Deluan
be715c3696 Disable scan buttons when there's a scan in progress 2020-11-15 23:13:59 -05:00
Deluan
fddded3260 Move star to end of album title. Use flex for album details 2020-11-15 22:00:40 -05:00
Deluan
cf90f0a245 Update sizes with SQL, instead of a full rescan 2020-11-15 19:26:16 -05:00
Deluan
8bfaa0ad9d Better detection of ID fields, to use = instead of LIKE 2020-11-15 18:24:13 -05:00
Deluan
15697a6fa2 Bump github.com/golangci/golangci-lint from 1.32.1 to 1.32.2 2020-11-14 21:37:08 -05:00
Deluan
bcb3e1479f Bump github.com/astaxie/beego from 1.12.2 to 1.12.3 2020-11-14 21:35:39 -05:00
Deluan
44d13bd37c Remove stray Printf 2020-11-14 13:38:31 -05:00
Aries
9629c26537 Fix ja translation (#624) 2020-11-14 11:15:21 -05:00
Deluan
1c7f859b5e Add more broker log 2020-11-14 00:44:58 -05:00
Deluan
8b2a550368 Fix test 2020-11-14 00:25:25 -05:00
Deluan
b0ea517fdd Add Album comment to Album details 2020-11-14 00:13:43 -05:00
Deluan
08f96639f4 Add Uptime to Activity Panel 2020-11-13 20:09:23 -05:00
Deluan
b64bb706f7 Use Gravatar in GetAvatar Subsonic API 2020-11-13 14:57:49 -05:00
Deluan
a4ef31251d Enable activity panel by default 2020-11-13 13:44:38 -05:00
Deluan
84cd6b7f34 Add Esperanto translation, thanks to @ebanDev 2020-11-13 12:56:30 -05:00
Deluan
df86a8153e New translated terms 2020-11-13 12:51:32 -05:00
Deluan
b38be69b14 Make AppBar back to original height 2020-11-13 10:14:01 -05:00
Deluan
48e0d2c99e Trunc long names 2020-11-13 09:33:56 -05:00
Deluan
3dac9ae666 Fix linting error 2020-11-13 00:44:26 -05:00
Deluan
9d7995fd4d Redesign UserMenu, now with support for Gravatar 2020-11-13 00:40:30 -05:00
Deluan
7efc32d136 Ignore "Cover (front)" tag when using ffmpeg extractor 2020-11-12 23:17:06 -05:00
Deluan
153cf8f5af Don't display "Comment" field in details if it is empty 2020-11-12 22:01:59 -05:00
Deluan
b3f373cdb4 Better Activity panel layout 2020-11-12 21:57:28 -05:00
Deluan
08399c4854 Fix some JS console errors 2020-11-12 20:51:26 -05:00
Deluan
25db696c06 Detect different discs, even when missing the first track of the disc. Fix #620. 2020-11-12 20:33:20 -05:00
Deluan
bdad927f11 Fix color of activity icon on light themes 2020-11-12 18:19:54 -05:00
Deluan
b1a9dfee13 Replace <hr/> with Material-UI's <Divider/> 2020-11-12 17:08:20 -05:00
Deluan
c09ba509b2 Fine tune scan status behaviour 2020-11-12 16:12:31 -05:00
Deluan
0e7163eb2c Sanitize comments and lyrics on import, as they are rendered as HTML on the UI 2020-11-11 12:26:47 -05:00
Deluan
5111cf8c33 Display comments in SongDetails and AlbumList's details 2020-11-11 11:58:03 -05:00
Deluan
98af68ac99 Import comments and lyrics 2020-11-11 10:43:17 -05:00
Deluan
aee4eb71c4 Add support for multi-line tags 2020-11-11 09:45:46 -05:00
Deluan
99d454d8b0 Fix import 2020-11-10 20:51:43 -05:00
Deluan
11012302fd Add tests for formatters 2020-11-10 20:45:04 -05:00
Deluan
9d2426a601 Use a better notation for exporting JS components and functions 2020-11-10 19:27:28 -05:00
Deluan
8a44f61189 Fix setting up Event Stream message handler on first login 2020-11-10 16:53:09 -05:00
Deluan
7afad2c96e Fix download single track from Playlist 2020-11-10 16:24:34 -05:00
Deluan
08e63c867b Add config option to globally enable/disable downloads 2020-11-10 16:14:43 -05:00
Deluan
bf69c5589f Fix log message 2020-11-10 14:46:12 -05:00
Deluan
714100e24b Remove old TODO 2020-11-09 19:50:14 -05:00
Deluan
8f2fe6f9fa Add buffer to broker SendMessage 2020-11-09 19:24:27 -05:00
Deluan
08dbf44529 Better broker logging 2020-11-09 19:24:04 -05:00
Deluan
84080a0e44 Hide activity menu from non-admin users 2020-11-09 16:12:50 -05:00
Deluan
1b624b2505 Do not create the EventStream if unauthenticated 2020-11-09 16:12:50 -05:00
Deluan
a2e76d6898 Add flag to enable activity menu 2020-11-09 16:12:50 -05:00
Deluan
56803d0151 Auto-reconnect to event stream after 20secs timeout 2020-11-09 16:12:50 -05:00
Deluan
2b1a5f579a Adding a communication channel between server and clients using SSE 2020-11-09 16:12:50 -05:00
Deluan
3fc81638c7 Moved all reducers and actions to their own folders 2020-11-08 13:19:38 -05:00
Deluan
24b040adf9 Add more keyboard shortcuts
- : volume down
= : volume up
m : toggle sidebar menu

Refers to #585
2020-11-07 23:11:57 -05:00
Deluan
8d608ac5b2 Faster display of cover in album detail view 2020-11-07 22:45:04 -05:00
Deluan
02160465a5 Remove unused file 2020-11-07 12:46:52 -05:00
Deluan
b5abd80927 Update react-jinke-music-player to 4.9.1. Fix #568 2020-11-07 12:20:42 -05:00
Deluan
6542842938 Make sure we don't get durations with decimals 2020-11-05 18:27:46 -05:00
Deluan
8d7931b3bc Fix "seekable" log message (was always "false") 2020-11-05 18:11:12 -05:00
Deluan
9224a67a7b Add <- and -> hotkeys, to jump to previous or next song
Refers to #585
2020-11-05 17:38:53 -05:00
Deluan
873cea4046 Fix "Something went wrong" error when deleting a playlist 2020-11-05 14:06:21 -05:00
JorisL
0b977df8dd Fixed duration formatter to properly count lengths longer than 24 hours (#612)
* Fixed durationfield formatter to properly count lengths longer than
24 hours.

* formatted DurationField.js with npm prettier
2020-11-05 14:02:09 -05:00
Deluan
fb1461fd0b Fix reading dirs from a MergeFS 2020-11-05 13:36:10 -05:00
Deluan
9cbeddae8f Avoid cross-site scripting
See: https://lgtm.com/rules/1510377426397/
2020-11-05 12:32:39 -05:00
Deluan
c9b119f0a4 Make scrobble submits compatible with Last.FM specification
See https://github.com/deluan/navidrome/issues/18#issuecomment-656977060
2020-11-04 23:51:48 -05:00
Deluan
a6bd9f627e Make new cache layout the default 2020-11-04 23:25:38 -05:00
Deluan
861c742b3e Move notifications to the top
This avoids notifications getting covered by the player
2020-11-04 19:29:55 -05:00
Deluan
36596d4fdb Don't send the transcoded file if it is a HEAD request 2020-11-03 16:06:02 -05:00
Deluan
94f28f6216 Generate a better salt for Subsonic token authentication 2020-11-03 15:13:40 -05:00
Deluan
2f56f1b178 Use new fscache's SetKeyMapper
See a0daa9e527
2020-11-03 12:52:44 -05:00
Deluan
f4a88b8319 Update screenshots 2020-11-03 09:23:04 -05:00
Deluan
f50aeb0b21 Bump golangci-lint version in pipeline 2020-11-02 20:56:52 -05:00
Deluan
fd1604b1d2 Add user's name to UserMenu 2020-11-02 17:13:12 -05:00
Deluan
7fbdcf8ddc Upgrade react-admin to 3.9.6 2020-11-02 17:12:52 -05:00
Deluan
7f7b0c1f0d Move Settings options to UserMenu 2020-11-02 16:57:21 -05:00
Deluan
68e0fe574f Bump github.com/golangci/golangci-lint from 1.32.0 to 1.32.1 2020-11-02 11:45:58 -05:00
Deluan Quintão
8ddf4d62af Update README.md 2020-11-02 11:43:05 -05:00
Deluan
9bcd606fe8 Fix Artist full_text refresh 2020-11-02 10:27:01 -05:00
Deluan
7819e834c8 Fix Artist filtering 2020-11-02 09:58:51 -05:00
Deluan
779d4a1c85 Revert "Process empty folders as changed folders"
This reverts commit e07152b695.
2020-11-02 07:57:47 -05:00
Deluan
e07152b695 Process empty folders as changed folders
This is a workaround for rclone not changing the directory modtime when you delete all folders from it (happens when you are moveing things around with beets)
2020-11-01 23:25:34 -05:00
Deluan
ee5a0698c0 Simplify scanner utilization 2020-11-01 18:37:17 -05:00
Deluan
71b77cba2b Bump Subsonic API to 1.16.1 2020-11-01 17:04:53 -05:00
Deluan
8e584ee020 Update count on getScanStatus 2020-11-01 16:54:33 -05:00
Deluan
3ea5b85b36 go mod tidy 2020-11-01 14:40:48 -05:00
Deluan
cfad35544b Add artistImageUrl available in getArtists endpoint
Also cache artist info in the DB for 1 hour
2020-11-01 14:37:29 -05:00
dependabot-preview[bot]
7583ddac65 Bump github.com/golangci/golangci-lint from 1.31.0 to 1.32.0
Bumps [github.com/golangci/golangci-lint](https://github.com/golangci/golangci-lint) from 1.31.0 to 1.32.0.
- [Release notes](https://github.com/golangci/golangci-lint/releases)
- [Changelog](https://github.com/golangci/golangci-lint/blob/master/CHANGELOG.md)
- [Commits](https://github.com/golangci/golangci-lint/compare/v1.31.0...v1.32.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-11-01 14:37:08 -05:00
Deluan
6b89679e08 Remove hardcoded color 2020-10-31 19:40:25 -04:00
Deluan
3535fba9dd Fix BulkActions contrast once and for all(?) 2020-10-31 18:46:14 -04:00
Deluan
488db26675 Improve bulk actions color contrast 2020-10-31 11:05:33 -04:00
Deluan
1f842b08e2 Remove duplicated code for SongBulkActions 2020-10-31 10:46:38 -04:00
Deluan
aabef62b11 Add "PlayNow" button to bulk actions 2020-10-31 10:14:12 -04:00
Deluan
6c0778a867 Add log to pool 2020-10-31 01:13:36 -04:00
Deluan
58d6b0a84f Cache Warmer now waits for Cache to be available 2020-10-31 00:40:21 -04:00
Deluan
145a5708ca Stop tag_scanner when waltDirTree is interrupted by errors
Otherwise, tag_scanner remove tracks from folders that would come after the error
2020-10-31 00:06:28 -04:00
Deluan
6ccdc2e068 Fine tune colors, remove PlayButton from AlbumDetail 2020-10-30 19:39:47 -04:00
Chris Newton
6da2f1ba92 feat: ran prettier over changes 2020-10-30 19:39:47 -04:00
Chris Newton
28bcd3f99e feat: fixed linting error 2020-10-30 19:39:47 -04:00
Chris Newton
1076dda011 feat: changed hvoer state for album list, added play icon to album details 2020-10-30 19:39:47 -04:00
Chris Newton
e30704fe0f feat: altered grid layout to be more like itunes 2020-10-30 19:39:47 -04:00
Deluan
84384da8d1 Better naming for function 2020-10-30 13:14:53 -04:00
Victorhck
62fe1cdc43 improve spanish translation 2020-10-30 10:05:16 -04:00
Deluan
4d6c9482ff Recover from panic when reading invalid id2 tags
Workaround for #596
2020-10-30 09:53:38 -04:00
Deluan
cdd44a2830 Abort scan when media folder is empty
This is to prevent all data being deleted in the case where a mount is not available
2020-10-30 09:39:36 -04:00
Deluan
ba8d2f5da8 Log when a cache has finished loading 2020-10-30 00:33:39 -04:00
Deluan
00ec6cf042 Process changed folders as they are discovered 2020-10-29 23:47:43 -04:00
Deluan
2f394623c8 WIP 2020-10-29 23:19:26 -04:00
Deluan
f1a24b971a Use timestamp of artwork file instead of album's UpdatedAt in the cache key 2020-10-29 23:19:26 -04:00
Deluan
d913108de2 Add option to disable track cover art. Should help with cloud mounting (rclone) 2020-10-29 10:57:33 -04:00
Deluan
32bac11b61 Make CreatePlaylist response compatible with API >1.14.0 2020-10-28 12:46:06 -04:00
Deluan
78630d427d Limit startScan to admins only 2020-10-27 20:22:05 -04:00
Deluan
1e57852eff Make pool's queue buffered. Workaround while we don't put the queue in disk 2020-10-27 20:12:27 -04:00
Deluan
464e251d19 Only start the cache warming after all folders were scanned 2020-10-27 20:11:25 -04:00
Deluan
d9f7a154cf Implements library scanning endpoints. Also:
- Bumped Subsonic API version to 1.15:
- Better User/Users Subsonic endpoint implementations, not final though
2020-10-27 18:20:50 -04:00
Deluan
9b756faef5 Make caches singletons 2020-10-27 18:20:50 -04:00
Deluan
515528ee6d Disable flaky test 2020-10-27 16:07:53 -04:00
Deluan
4bd6012f11 Fix job dependencies in pipeline 2020-10-27 15:58:27 -04:00
Deluan
216491815c Increased pool test timeout (hate time based tests...) 2020-10-27 15:58:13 -04:00
Deluan
4777cf0aba Simplify error responses 2020-10-27 15:33:28 -04:00
Deluan
0f418a93cd Completely removed engine package, fewer abstraction layers \o/ 2020-10-27 15:27:37 -04:00
Deluan
d0bf37a8a9 Move mock datastore to tests package 2020-10-27 15:23:49 -04:00
Deluan
313a088f86 Make mocks strongly typed 2020-10-27 15:23:49 -04:00
Deluan
6152fadd92 Removed list_generator completely 2020-10-27 15:23:48 -04:00
Deluan
3037ea01e2 Removed more layers of indirection from the engine package 2020-10-27 15:23:48 -04:00
Deluan
acba4b16ee Add test for pool 2020-10-27 15:23:48 -04:00
dependabot-preview[bot]
8dfa929666 Bump github.com/kr/pretty from 0.2.0 to 0.2.1
Bumps [github.com/kr/pretty](https://github.com/kr/pretty) from 0.2.0 to 0.2.1.
- [Release notes](https://github.com/kr/pretty/releases)
- [Commits](https://github.com/kr/pretty/compare/v0.2.0...v0.2.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-10-27 00:15:32 -04:00
Deluan
c1fb32cedb Replace unicode quotes and dash with simple ascii chars
External services do not use these unicode chars. Ex:
- “Weird Al” Yankovic
- Bachman–Turner Overdrive
- The Go‐Go’s
are not found in Last.FM and Spotify
2020-10-27 00:13:39 -04:00
Deluan
b6a6422fac Upgrade GoLang to 1.15.3 2020-10-26 13:04:14 -04:00
Deluan
21ed7348c6 Remove invalid migration 2020-10-26 10:57:21 -04:00
Deluan
95cc211659 Revert "Make caches singletons" 2020-10-26 10:11:47 -04:00
Deluan
bf5318d776 Add flag to enable new cache layout 2020-10-26 09:54:36 -04:00
Deluan
81d7556cdf Make caches singletons 2020-10-25 23:22:52 -04:00
Deluan
1e56f4da76 Add simple cache warmer, disabled by default 2020-10-25 23:22:52 -04:00
Deluan
f3bb51f01b Add formatting to config dump 2020-10-25 23:22:52 -04:00
Deluan
197d4024f7 Add dedicated Item interface for cache items 2020-10-25 23:22:52 -04:00
Deluan
7eaa42797a Uses cached original image when requesting a resized image 2020-10-25 23:22:52 -04:00
Deluan
d39bd0219a Fix cache key-mapping 2020-10-25 23:22:52 -04:00
Deluan
9f533b2108 New Cache FileSystem implementation 2020-10-25 23:22:52 -04:00
Deluan
1cfa7b2272 Change MediaFolder.ID type to int32 2020-10-25 23:22:52 -04:00
Deluan
d24709b521 Add getScanStatus Subsonic response 2020-10-25 23:22:52 -04:00
Deluan
af7eaa2b7a Add scanner status 2020-10-25 23:22:52 -04:00
Deluan
c0ec0b28b9 Add better process lifecycle management 2020-10-24 22:43:59 -04:00
Deluan
6d08a9446d Fix test suite name 2020-10-23 21:43:33 -04:00
Deluan
04fd72e1fa Change avatar placeholder to new logo 2020-10-23 21:37:53 -04:00
Deluan
fc19199fbe Add Russian translation. Thanks @lun4r 2020-10-23 09:54:25 -04:00
Deluan
4514a54744 Fix ignoring hidden folders when scanning 2020-10-22 13:59:54 -04:00
Deluan
f9e0de31b8 Fix missing last.fm and spotify config keys. Closes #589 2020-10-22 08:31:47 -04:00
Deluan
1cd2f015c2 Get Similar Artists in parallel
Also don't fail `GetArtistInfo` when Last.FM is not configured
2020-10-21 21:44:03 -04:00
Deluan
ed84c5a0a3 Bump @testing-library/react from 11.0.4 to 11.1.0 in /ui 2020-10-21 18:24:20 -04:00
Deluan
b88f9013dc Fix getAlbumList.byYear. See https://github.com/daneren2005/Subsonic/issues/967 2020-10-21 17:32:10 -04:00
dependabot-preview[bot]
62ed30afed Bump @testing-library/user-event from 12.1.7 to 12.1.8 in /ui
Bumps [@testing-library/user-event](https://github.com/testing-library/user-event) from 12.1.7 to 12.1.8.
- [Release notes](https://github.com/testing-library/user-event/releases)
- [Changelog](https://github.com/testing-library/user-event/blob/master/CHANGELOG.md)
- [Commits](https://github.com/testing-library/user-event/compare/v12.1.7...v12.1.8)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-10-21 17:10:39 -04:00
Deluan
6dc21d0595 Check for Last.FM and Spotify configuration at startup 2020-10-21 17:10:06 -04:00
Deluan
79710fbee0 go mod tidy 2020-10-21 16:58:55 -04:00
dependabot-preview[bot]
c89b89cd92 Bump react from 16.13.1 to 16.14.0 in /ui
Bumps [react](https://github.com/facebook/react/tree/HEAD/packages/react) from 16.13.1 to 16.14.0.
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/facebook/react/blob/master/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/commits/v16.14.0/packages/react)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-10-21 16:58:07 -04:00
dependabot-preview[bot]
dcea5eb449 Bump github.com/spf13/cobra from 1.1.0 to 1.1.1
Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.1.0 to 1.1.1.
- [Release notes](https://github.com/spf13/cobra/releases)
- [Changelog](https://github.com/spf13/cobra/blob/master/CHANGELOG.md)
- [Commits](https://github.com/spf13/cobra/compare/v1.1.0...v1.1.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-10-21 16:51:55 -04:00
Deluan Quintão
b5c68c971d Update pl.json (POEditor.com) 2020-10-21 16:50:31 -04:00
Deluan Quintão
fe38f99739 Update nl.json (POEditor.com) 2020-10-21 16:50:31 -04:00
Deluan Quintão
ff3a89b15a Update cs.json (POEditor.com) 2020-10-21 16:50:31 -04:00
Deluan
078a7c24e6 Add userRating to Subsonic API's Artist 2020-10-21 15:51:12 -04:00
Deluan
69e1059705 Prefer starred and high rated versions for Top Songs 2020-10-21 15:11:26 -04:00
Deluan
075c28d2e5 Fix performance and precision of TopSongs 2020-10-21 14:07:01 -04:00
Deluan
a45b5a037f Match Top Songs by mbid, add indexes to media_file 2020-10-21 10:13:03 -04:00
Deluan
3cf8b8e97d Fix migration that adds MBIDs 2020-10-21 09:02:51 -04:00
Deluan
b93a3db267 Fix sort order for TopSongs 2020-10-21 00:10:46 -04:00
Deluan
53c1e9ec35 Include tracks in TopSongs where the requested artist is the album artist 2020-10-20 23:52:45 -04:00
Deluan
12cedee867 Prefer older versions on GetTopSongs 2020-10-20 23:46:45 -04:00
Deluan
2f11c2dc8f Bump Subsonic API compatibility to 1.13 2020-10-20 22:54:37 -04:00
Deluan
049ac70b2b Add "real" TopSongs 2020-10-20 22:53:52 -04:00
Deluan
b5e20c1934 Ignore invalid MBIDs (ex: discogs IDs) 2020-10-20 17:45:32 -04:00
Deluan
173dd52fe1 Use MBID with most occurrences 2020-10-20 17:16:24 -04:00
Deluan
6663c079e0 Add MBIDs to media_file, album and artist 2020-10-20 16:27:22 -04:00
Deluan
64ccb4d188 Add SimilarSongs functionality 2020-10-20 16:07:31 -04:00
Deluan
a289a1945f Remove redundant interfaces 2020-10-20 16:07:31 -04:00
Deluan
a257891b46 Get better artist images results 2020-10-20 16:07:31 -04:00
Deluan
40fd5bab34 Search for artists case-insensitive 2020-10-20 16:07:31 -04:00
Deluan
e9e09a7480 Add dedicated SimilarArtists call 2020-10-20 16:07:31 -04:00
Deluan
29d8950e5b Better ArtistInfo field names 2020-10-20 16:07:31 -04:00
Deluan
00b6f895bb Fix lint errors 2020-10-20 16:07:31 -04:00
Deluan
07d96f8308 Add missing fields to ArtistInfo 2020-10-20 16:07:31 -04:00
Deluan
07535e1518 Add ExternalInformation core service (not a great name, I know) 2020-10-20 16:07:31 -04:00
Deluan
19ead8f7e8 Add initial spotify client implementation 2020-10-20 16:07:31 -04:00
Deluan
eb74dad7cd Add initial last.fm client implementation 2020-10-20 16:07:31 -04:00
Deluan
61d0bd4729 Add support for WavPack files 2020-10-20 10:47:29 -04:00
Deluan
def5db9729 Update dependencies (go mod tidy) 2020-10-16 16:24:10 -04:00
dependabot-preview[bot]
3d11bdcfd1 Bump github.com/spf13/cobra from 1.0.0 to 1.1.0
Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.0.0 to 1.1.0.
- [Release notes](https://github.com/spf13/cobra/releases)
- [Changelog](https://github.com/spf13/cobra/blob/master/CHANGELOG.md)
- [Commits](https://github.com/spf13/cobra/compare/v1.0.0...v1.1.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-10-15 09:21:26 -04:00
Deluan
0ff89679ba Use new renderAudioTitle (to avoid the [Object object] song title on iOS) 2020-10-13 11:20:30 -04:00
Deluan
0c095f6d5d Upgrade ginkgo/gomega 2020-10-13 09:35:31 -04:00
Deluan
2f8dc794de Add and show Playlists sizes 2020-10-12 22:31:01 -04:00
Deluan
68a9be5e86 Add Artist (discography) size, and show sizes in Download caption 2020-10-12 22:31:01 -04:00
Deluan
1ffc8d619e Log ffmpeg detection as Info 2020-10-12 21:59:03 -04:00
Deluan
2de0a40c6f Fix Album size should be int64 2020-10-12 21:04:12 -04:00
Deluan
5417031d79 Update some GH actions 2020-10-12 12:21:01 -04:00
Deluan
ae817da223 Upgrade golangci-lint
- Fix a SQL string concatenation
- Install golangci-lint using `tools.go`
2020-10-12 12:21:01 -04:00
Jay R. Wren
fd6edf967f Add size to album details (#561)
* add size to album details

for #534

* addressing review comments:

* create index album(size)
* remove unneeded Size field from refresh struct
* add whitespace to album details
* add size to album list view

* prettier
2020-10-12 11:10:07 -04:00
Deluan
c60e56828b Fix ffmpeg detection 2020-10-12 10:59:42 -04:00
Deluan
edc9344327 Only link from current playing song title to album view if not in iOS.
Ideally the react-player should accept a Link as the audioTitle
2020-10-11 15:04:15 -04:00
Deluan
fea5d23fc7 Add ffmpeg detection at start-up 2020-10-06 17:24:16 -04:00
Deluan
26d2af17a3 Fix read DISCNUMBER as a DiscNumber tag in ffmpeg extractor 2020-10-06 17:06:47 -04:00
Gosz
f373f5f83e Updating spanish translation 2020-10-06 11:38:54 -04:00
Deluan
92b7ef40af Disable CSP for now 2020-10-06 11:24:59 -04:00
Deluan
39cb3455db Prepare for release: go mod tidy 2020-10-06 09:55:40 -04:00
Deluan Quintão
4ac4806bf8 Update fr.json (POEditor.com) 2020-10-06 09:33:59 -04:00
Deluan Quintão
a282f62395 Update zn.json (POEditor.com) 2020-10-06 09:33:59 -04:00
dependabot-preview[bot]
3aac03d253 Bump @testing-library/user-event from 12.1.6 to 12.1.7 in /ui
Bumps [@testing-library/user-event](https://github.com/testing-library/user-event) from 12.1.6 to 12.1.7.
- [Release notes](https://github.com/testing-library/user-event/releases)
- [Changelog](https://github.com/testing-library/user-event/blob/master/CHANGELOG.md)
- [Commits](https://github.com/testing-library/user-event/compare/v12.1.6...v12.1.7)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-10-06 09:29:11 -04:00
Deluan
cd171c40cb Add secure middleware, with sensible values 2020-10-06 08:46:58 -04:00
dependabot-preview[bot]
78c40ab6b4 Bump jwt-decode from 2.2.0 to 3.0.0 in /ui
Bumps [jwt-decode](https://github.com/auth0/jwt-decode) from 2.2.0 to 3.0.0.
- [Release notes](https://github.com/auth0/jwt-decode/releases)
- [Changelog](https://github.com/auth0/jwt-decode/blob/master/CHANGELOG.md)
- [Commits](https://github.com/auth0/jwt-decode/compare/v2.2.0...v3.0.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-10-06 08:41:08 -04:00
Deluan
21f7c1906d Fix ByPath queries should not match partial filenames 2020-10-06 08:13:25 -04:00
dependabot-preview[bot]
23fe8cdee6 Bump uuid from 8.3.0 to 8.3.1 in /ui
Bumps [uuid](https://github.com/uuidjs/uuid) from 8.3.0 to 8.3.1.
- [Release notes](https://github.com/uuidjs/uuid/releases)
- [Changelog](https://github.com/uuidjs/uuid/blob/master/CHANGELOG.md)
- [Commits](https://github.com/uuidjs/uuid/compare/v8.3.0...v8.3.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-10-06 08:12:57 -04:00
Deluan
af55b93ac8 Make taglib the default metadata extractor 2020-10-05 21:01:03 -04:00
Deluan
665b1f6898 Fix auto-imported playlists losing the "Public" status. Fix #479 2020-10-05 12:40:44 -04:00
Deluan
35f748e0fb Use logo on signup page 2020-10-04 18:29:46 -04:00
Deluan
fc7a027d59 Update mobile login screenshot 2020-10-04 18:23:48 -04:00
Deluan
38c1999fcd Use logo on login page. Closes #247 2020-10-04 11:47:25 -04:00
Deluan
180f1354fc Make package name compatible with version installed by make setup 2020-10-03 20:13:47 -04:00
Deluan
abd51b2156 Use Subsonic API to star/unstar
This removes the need to update the annotations on Put(model), removing complexity and making it less buggy
2020-10-03 20:08:51 -04:00
Deluan
47976e13b1 Create index to make sort by starred faster 2020-10-03 20:08:51 -04:00
Deluan
bbd4503ac8 Move tools installation to tools.go 2020-10-03 11:14:19 -04:00
Aries
b40df6380e Update Japanese translation (#544) 2020-10-03 09:56:10 -04:00
Deluan
2d036b5966 Small refactoring, simplify function 2020-10-02 22:36:46 -04:00
Deluan
f859772723 Remove dangling tracks after changing MusicFolder. Fix #445 2020-10-02 16:18:45 -04:00
Deluan
1be79fa945 Reload translations when reloading the app 2020-10-02 14:28:55 -04:00
Deluan
1825b29737 Better PT translation 2020-10-02 14:17:34 -04:00
Deluan Quintão
13f08d3eae Update translations (#543)
* Update zn.json (POEditor.com)

* Update cs.json (POEditor.com)

* Update nl.json (POEditor.com)

* Update fr.json (POEditor.com)

* Update de.json (POEditor.com)

* Update it.json (POEditor.com)

* Update ja.json (POEditor.com)

* Update pl.json (POEditor.com)

* Update pt.json (POEditor.com)

* Update tr.json (POEditor.com)

* Update es.json (POEditor.com)

* Fix translations
2020-10-02 12:18:00 -04:00
Deluan
52d8aaa865 Add about dialog, with version and helpful links 2020-10-02 12:03:19 -04:00
Deluan
8dfc259857 Serve robots.txt from root (http://server/robots.txt) 2020-10-02 10:15:19 -04:00
Deluan
deef8e162d Hide the player when queue is empty, instead of removing it from the DOM 2020-10-01 13:40:44 -04:00
Deluan
b18e3289fb Add StarButton to player 2020-10-01 13:40:44 -04:00
certuna
4d60f72b7e Update index.js
registers the default serverworker, required to be installable on desktop as a PWA - as far as I can test, it doesn't seem to break anything
2020-10-01 12:12:18 -04:00
Deluan
e0fa85be28 Avoid Bing bot to automatically add Navidrome to the MS Store
See: https://docs.microsoft.com/en-us/microsoft-edge/progressive-web-apps-edgehtml/microsoft-store#criteria-for-automatic-submission
2020-10-01 12:09:56 -04:00
Deluan
5b167031d2 Enable PWA's when setting BaseURL 2020-10-01 12:04:38 -04:00
Deluan
cf8756b14b Unexport private function 2020-10-01 09:56:09 -04:00
certuna
03867bd8b2 Update manifest.json
added start_url (required)
fixed icon paths
2020-10-01 08:56:27 -04:00
Deluan
943f35f7a5 Update to GoLang 1.15 2020-10-01 08:41:11 -04:00
Deluan
ca283f45ea Update serviceWorker to the latest from create-react-app 2020-09-29 17:10:06 -04:00
Deluan
bf93b5614c Bump react-admin to 3.8.5 2020-09-29 16:33:36 -04:00
Deluan
377d8f6b87 Fix continuous loop when showing an album or playlist 2020-09-29 16:29:34 -04:00
dependabot-preview[bot]
1eb62ee671 Bump github.com/sirupsen/logrus from 1.6.0 to 1.7.0
Bumps [github.com/sirupsen/logrus](https://github.com/sirupsen/logrus) from 1.6.0 to 1.7.0.
- [Release notes](https://github.com/sirupsen/logrus/releases)
- [Changelog](https://github.com/sirupsen/logrus/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sirupsen/logrus/compare/v1.6.0...v1.7.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-09-29 16:28:58 -04:00
Deluan
39c94d3cd9 Play/Pause current song with <Space> key 2020-09-28 19:05:19 -04:00
Deluan
3fa4ef0166 Fix low severity vulnerabilities (npm audit fix) 2020-09-28 18:36:25 -04:00
Deluan
9116529b6d Change favicon to new logo 2020-09-28 16:48:35 -04:00
Deluan
bd8b573743 Update react-jinke-music-player 2020-09-28 12:22:05 -04:00
Deluan
a65318a00a Fix 'Play Next' icon in AlbumSongBulkActions 2020-09-27 12:50:58 -04:00
Deluan
a817701ee8 Use new ci-goreleaser. Fix pipeline 2020-09-26 20:30:48 -04:00
Deluan
4a4a8aff34 Build ARM with armel instead of armhf. Fixes #525 2020-09-26 13:08:45 -04:00
Fernando Rios
80b8b69cee Fix compilation of C++ code on certain linux systems 2020-09-26 13:08:28 -04:00
Deluan
ab0e091736 Fix link to Artist's albums in mobile view 2020-09-25 16:48:31 -04:00
Deluan
e6d1e67297 Add more padding tertiary info and the star icon, in Mobile simple list views. Fixes #466 2020-09-24 21:29:26 -04:00
Deluan
a99924ea20 Converted pre-push hook into a make target, avoid calling tests twice when releasing 2020-09-24 17:24:31 -04:00
Deluan
27adb84177 Add "Close" icon to player 2020-09-24 13:34:17 -04:00
Deluan
7a3bd935c2 Change default Opus transcoding format name to opus. Closes #521 2020-09-24 12:27:13 -04:00
Deluan
cff5c1ee53 Start player in pause mode if windows is reloaded/refreshed. Fixes #457 2020-09-24 11:12:47 -04:00
Deluan
fd32a28788 Fix JS linting error 2020-09-24 09:58:01 -04:00
Deluan Quintão
4f25e9ebf4 Update pl.json (POEditor.com) 2020-09-24 09:51:11 -04:00
Deluan Quintão
514117a477 Update ja.json (POEditor.com) 2020-09-24 09:51:11 -04:00
Deluan Quintão
07e8f41849 Update it.json (POEditor.com) 2020-09-24 09:51:11 -04:00
Deluan Quintão
133626dcd0 Update fr.json (POEditor.com) 2020-09-24 09:51:11 -04:00
Deluan Quintão
56a6fb91ab Update cs.json (POEditor.com) 2020-09-24 09:51:11 -04:00
Deluan Quintão
6e518d90d5 Update zn.json (POEditor.com) 2020-09-24 09:51:11 -04:00
Deluan Quintão
96b94106e6 Update en.json (POEditor.com) 2020-09-24 09:49:42 -04:00
Deluan
a1c670b40d Start player in pause mode if windows is reloaded/refreshed. Fixes #457 2020-09-24 09:40:04 -04:00
certuna
2230a9052f Update manifest.json
Updated name/description/colours in the manifest
2020-09-24 08:51:35 -04:00
Gosz
9b1be35c14 Updatind translation 2020-09-24 08:50:05 -04:00
Deluan
afe5a5b32a Fix extracting tags with spaces in the tagname ("Ex: Album Artist") 2020-09-22 14:42:36 -04:00
dependabot-preview[bot]
9edd7e9025 Bump @testing-library/user-event from 12.1.5 to 12.1.6 in /ui
Bumps [@testing-library/user-event](https://github.com/testing-library/user-event) from 12.1.5 to 12.1.6.
- [Release notes](https://github.com/testing-library/user-event/releases)
- [Changelog](https://github.com/testing-library/user-event/blob/master/CHANGELOG.md)
- [Commits](https://github.com/testing-library/user-event/compare/v12.1.5...v12.1.6)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-09-22 09:36:13 -04:00
dependabot-preview[bot]
2be9a7dbec Bump prettier from 2.1.1 to 2.1.2 in /ui
Bumps [prettier](https://github.com/prettier/prettier) from 2.1.1 to 2.1.2.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/master/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/2.1.1...2.1.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-09-22 09:32:25 -04:00
Deluan
7305e3aa17 Add "Play Next" action (finally) 2020-09-21 20:10:52 -04:00
Deluan
aa133e6b00 Upgrade react-music-player to 4.18.2 2020-09-21 15:24:15 -04:00
JG
1f72399f44 Add Spanish translation
Spanish translation
2020-09-18 09:46:12 -04:00
KITblue
3aef62f201 Update Chinese translation
* Update zn.json

Already formatted

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Update zn.json

* Add files via upload
2020-09-17 13:43:51 -04:00
dependabot-preview[bot]
e5535f6aff Bump @testing-library/user-event from 12.1.3 to 12.1.5 in /ui
Bumps [@testing-library/user-event](https://github.com/testing-library/user-event) from 12.1.3 to 12.1.5.
- [Release notes](https://github.com/testing-library/user-event/releases)
- [Changelog](https://github.com/testing-library/user-event/blob/master/CHANGELOG.md)
- [Commits](https://github.com/testing-library/user-event/compare/v12.1.3...v12.1.5)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-09-16 20:20:57 -04:00
dependabot-preview[bot]
76fc5b1425 Bump @testing-library/react from 11.0.2 to 11.0.4 in /ui
Bumps [@testing-library/react](https://github.com/testing-library/react-testing-library) from 11.0.2 to 11.0.4.
- [Release notes](https://github.com/testing-library/react-testing-library/releases)
- [Changelog](https://github.com/testing-library/react-testing-library/blob/master/CHANGELOG.md)
- [Commits](https://github.com/testing-library/react-testing-library/compare/v11.0.2...v11.0.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-09-16 20:09:00 -04:00
dependabot-preview[bot]
a38f205c0b Bump react-measure from 2.5.0 to 2.5.2 in /ui
Bumps [react-measure](https://github.com/souporserious/react-measure) from 2.5.0 to 2.5.2.
- [Release notes](https://github.com/souporserious/react-measure/releases)
- [Changelog](https://github.com/souporserious/react-measure/blob/main/CHANGELOG.md)
- [Commits](https://github.com/souporserious/react-measure/compare/v2.5.0...v2.5.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-09-16 15:54:53 -04:00
Deluan
944107cb3d Update Italian translation (thanks @matteoipri) 2020-09-14 20:00:30 -04:00
Deluan
94fd0a10b5 Fix titles in Playlist create/edit views 2020-09-14 19:56:05 -04:00
Deluan
669f293f1f Fix ci-goreleaser 2020-09-10 17:49:25 -04:00
Deluan
532833ac7c Fix ci-goreleaser 2020-09-10 17:02:52 -04:00
Deluan
59f1d7e88a Use new ci-goreleaser, to fix generating Linux binaries for old kernels 2020-09-10 16:24:39 -04:00
Deluan
caeff2862a Remove dependency on C++17 2020-09-10 15:16:47 -04:00
Deluan
841c1129ff Break-up album/artist refresh in chunks 2020-09-09 08:57:59 -04:00
Deluan
ba30f7f8be Fix label for items per page (not always rows) 2020-09-08 14:55:41 -04:00
Deluan Quintão
6026638c03 Update fr.json (POEditor.com) 2020-09-08 14:54:02 -04:00
Deluan
cbab2e4eec go mod tidy 2020-09-08 13:33:07 -04:00
Deluan
a3ecc41e47 Change taglib extractor log level to trace 2020-09-08 13:33:07 -04:00
Deluan
4d18212f5d Extract all id3 frames from file 2020-09-08 13:33:07 -04:00
Deluan
5dea258058 Extract basic tags, as a fallback 2020-09-08 13:33:07 -04:00
Deluan
0802ab73d7 Trim tag value, not tag key 2020-09-08 13:33:07 -04:00
Deluan
865b9cd545 Trim spaces from tags 2020-09-08 13:33:07 -04:00
Deluan
e70ec53983 Rewrite taglib integration, now with TCMP 2020-09-08 13:33:07 -04:00
Deluan
2d0031f709 Parse more date formats 2020-09-08 13:33:07 -04:00
Deluan
78ecda5239 Get the first occurrence of multi-valued tags 2020-09-08 13:33:07 -04:00
Deluan
a1879ff871 Reorganize tests 2020-09-08 13:33:07 -04:00
Deluan
34eda3c8fc Add config option to select tag extractor (taglib, ffmpeg) 2020-09-08 13:33:07 -04:00
Deluan
506899b083 Add more fallback options for main tags 2020-09-08 13:33:07 -04:00
Deluan
3a4e2523dd Fix possible concurrency issue 2020-09-08 13:33:07 -04:00
Deluan
674b56a53d Install taglib in lint and go jobs 2020-09-08 13:33:07 -04:00
Deluan
58a0c44600 Embed audiotags lib, to make it static compilable 2020-09-08 13:33:07 -04:00
Deluan
df4328819d Initial implementation of taglib MetadataExtractor 2020-09-08 13:33:07 -04:00
Deluan
b6aa6eb7b2 Disable some jobs for now, as taglib is not available 2020-09-08 13:33:07 -04:00
Deluan
1187ee7cc1 Moved Metadata Extraction to its own package 2020-09-08 13:33:07 -04:00
Deluan
0beec552b1 Introduce Metadata and MetadataExtractor interfaces 2020-09-08 13:33:07 -04:00
Deluan
6a6d4c3f87 Use new ci-releaser image, that contains static taglib library 2020-09-08 13:33:07 -04:00
Deluan
1216c9bdb8 Bump react-measure version to 2.5.0 2020-09-08 13:12:00 -04:00
Deluan
2a888395fa Bump prettier version to 2.1.1 2020-09-08 13:06:34 -04:00
Deluan
56772f5c62 Bump @testing libraries 2020-09-08 13:05:04 -04:00
Deluan
07b5469b4c Bump uuid to v.1.1.2 2020-09-08 13:00:04 -04:00
Deluan
58324b411f Bump ginkgo to v1.14.1 2020-09-08 12:59:06 -04:00
dependabot-preview[bot]
c0e5b445cf Bump github.com/onsi/gomega from 1.10.1 to 1.10.2
Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.10.1 to 1.10.2.
- [Release notes](https://github.com/onsi/gomega/releases)
- [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/gomega/compare/v1.10.1...v1.10.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-09-08 12:55:59 -04:00
Deluan
6820e120cb Test for accented article sanitization 2020-09-08 09:40:41 -04:00
Deluan
28aefb4858 Fix sanitizing accented articles 2020-09-08 09:36:08 -04:00
Deluan
e50a720818 Sort by album name, then artist name 2020-09-07 16:21:29 -04:00
Deluan
900337081b Upgrade React-Player to 4.18.0 2020-09-06 12:11:02 -04:00
Deluan
34af6fc671 Clean up code a bit 2020-09-06 11:54:30 -04:00
Deluan
a25044bdf6 Reorder action buttons 2020-09-06 11:44:15 -04:00
Anders Moberg
30e98843ed Adding playlist button to Album Actions 2020-09-06 11:35:33 -04:00
Anders Moberg
8fe335ed97 Adding playlist button to Playlist actions 2020-09-06 11:35:33 -04:00
Deluan
8549451ee7 Fix potential undefined property
Not sure the reason, but I got this error:

```
Cannot read property 'id' of undefined
    at tn (SongTitleField.js:35)
    at Ka (react-dom.production.min.js:153)
    at vl (react-dom.production.min.js:261)
    at sc (react-dom.production.min.js:246)
    at lc (react-dom.production.min.js:246)
```
2020-09-02 12:41:21 -04:00
Deluan
596a4897a3 Do not force username to always be lowercase in the DB 2020-09-01 18:00:19 -04:00
Deluan
95eea0e9f8 Update ja.json (POEditor.com) (+2 squashed commits)
Squashed commits:
[e9c4218] Update ja.json (POEditor.com)
[10d3992] Add initial Japanese translation
2020-09-01 12:43:59 -04:00
Deluan Quintão
61c286a77e Update pl.json (POEditor.com) (+1 squashed commit)
Squashed commits:
[5c45ca0] Create pl.json
2020-09-01 12:43:59 -04:00
Deluan Quintão
15d11a9519 Update fr.json (POEditor.com) 2020-09-01 12:43:59 -04:00
Deluan Quintão
35625020e2 Update cs.json (POEditor.com) 2020-09-01 12:43:59 -04:00
Deluan
76e522710a New option: SearchFullString, to match query strings anywhere in searchable fields, not only in word boundaries
Based on feedback from @orlea, in https://github.com/deluan/navidrome/issues/255#issuecomment-683427754
2020-08-30 13:08:10 -04:00
Deluan Quintão
aae9d89e8c Update README.md 2020-08-26 13:54:23 -04:00
Deluan
0eae6d2a61 Hide "star" from disc subtitle rows 2020-08-25 22:48:05 -04:00
Deluan
f6982fd8ae Remove unused prop 2020-08-25 19:07:51 -04:00
Deluan
b364170d4f Remove duplicated star code from SongContextMenu 2020-08-24 19:51:41 -04:00
Deluan
0aceda9b89 Add star button to album detail view 2020-08-22 23:41:25 -04:00
Deluan
9df405a8ce Add export as m3u button to playlist 2020-08-22 13:23:50 -04:00
Deluan
366054e8cc Handle exporting playlists as m3u files 2020-08-22 12:15:26 -04:00
Deluan
8fa5544af7 Add option to download playlist 2020-08-21 13:28:20 -04:00
Deluan
073e40dc87 Add album cover lightbox 2020-08-21 12:41:23 -04:00
Deluan
a45c08f217 Ignore "hidden" files when importing a folder 2020-08-21 11:50:18 -04:00
Deluan
6c8535c54a Add support for reading webp artwork 2020-08-21 11:33:23 -04:00
Deluan
e2e79d6471 Fix getTopSongs endpoint mapping 2020-08-20 11:27:38 -04:00
Deluan
b5567090ed Remove -e option from grep, make the command more portable 2020-08-19 15:40:31 -04:00
Deluan
b836871161 Handle CR, LF and CRLF line endings when importing Playlists 2020-08-19 12:22:41 -04:00
Deluan
45e708f591 Loosen up constraints for email. Fixes #362 2020-08-19 12:22:41 -04:00
Deluan
608129963f Fix migration target 2020-08-19 11:18:30 -04:00
Deluan
f3d8222ddb Fix color of star in Album grid when using Light theme 2020-08-19 11:12:12 -04:00
Deluan
c83808a445 Revert "Use outlined Material-UI variant for login inputs as well"
This reverts commit c23e5c291c.
2020-08-18 09:58:09 -04:00
Deluan
c23e5c291c Use outlined Material-UI variant for login inputs as well 2020-08-17 16:10:49 -04:00
Deluan
bd1c3d9229 Use outlined Material-UI variant for all inputs 2020-08-17 11:19:39 -04:00
ericgaspar
48c0e1ca4b correct french translations 2020-08-17 09:22:20 -04:00
Deluan
16397e08fc Close cache reader. Should fix #446 2020-08-17 09:14:08 -04:00
Deluan
15a06fcd27 Removed support for Jamstash in dev mode. Not needed anymore :) 2020-08-15 23:11:31 -04:00
Deluan
a2e0acd6a2 Fix starring albums. Seems I may have lost a commit? 2020-08-15 15:03:03 -04:00
Deluan
5f38e70a2b Bump react-redux to 7.2.1 2020-08-15 12:58:22 -04:00
Deluan
c19c599521 Bump @testing-library 2020-08-15 12:57:18 -04:00
Deluan
dd398224e7 go mod tidy 2020-08-15 10:48:56 -04:00
Deluan
5ac76ae7e0 Fix broken image href 2020-08-14 17:00:24 -04:00
Deluan
c14147e6c5 More updated screenshots 2020-08-14 16:59:45 -04:00
Deluan
59ce940cd6 Use new screenshot in README 2020-08-14 16:53:36 -04:00
Deluan
cfecd7c6a2 Add new screenshot 2020-08-14 16:52:54 -04:00
Deluan
d81a4472a0 Update Czech translation 2020-08-14 16:32:30 -04:00
Deluan
147d26fb75 Enable sort by "starred" in Album and Artist lists 2020-08-14 15:35:15 -04:00
Deluan
848318932d Remove unused import 2020-08-14 14:47:54 -04:00
Deluan
49153dc1c1 Add playCount to artist list 2020-08-14 14:35:00 -04:00
Deluan
ca5da5b0ea Use active filters when shuffling songs 2020-08-14 14:10:39 -04:00
Deluan
c2e03c8162 Add stars to Albums 2020-08-14 13:35:28 -04:00
Deluan
f2ebbd26fa Add stars to Artist 2020-08-14 13:19:32 -04:00
Deluan
bbc4f9f91f Add artist context menu 2020-08-14 12:55:22 -04:00
Deluan
6fe1f84c68 Add download for songs 2020-08-14 12:11:35 -04:00
Deluan
d72468003f User album or artist name as zip name in download endpoint 2020-08-14 12:10:37 -04:00
Deluan
100f6a0645 Removed engine.Users 2020-08-14 12:10:37 -04:00
Deluan
bc2073fbd5 Removed unused function 2020-08-14 12:10:37 -04:00
Deluan
278d0ea8f3 Fix album fields in simulated browsing by folder 2020-08-14 12:10:37 -04:00
Deluan
0e16d7cfbb Fix regression: Show artwork in Music Stash when browsing by folder 2020-08-14 12:10:37 -04:00
Deluan
419884db7c Removed engine.Scrobbler 2020-08-14 12:10:37 -04:00
Deluan
eacfc41665 Removed engine.Search 2020-08-14 12:10:37 -04:00
Deluan
c271aa24d1 Make all Subsonic helper functions private 2020-08-14 12:10:37 -04:00
Deluan
22f34b3347 Refactor getGenres. Remove engine.Browser 2020-08-14 12:10:37 -04:00
Deluan
eba8395146 Refactor getSong 2020-08-14 12:10:37 -04:00
Deluan
f16dc5f8f8 Refactor getMusicDirectory 2020-08-14 12:10:37 -04:00
Deluan
15c8f4c0ef Refactor getAlbum 2020-08-14 12:10:37 -04:00
Deluan
e344f616b3 Refactor getArtist 2020-08-14 12:10:37 -04:00
Deluan
ef81caf3ed Refactor getMusicFolders and getIndexes 2020-08-14 12:10:37 -04:00
dependabot-preview[bot]
8513f1a899 Bump github.com/spf13/viper from 1.7.0 to 1.7.1
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.7.0 to 1.7.1.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.7.0...v1.7.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-14 09:44:35 -04:00
dependabot-preview[bot]
a9a25713e8 Bump github.com/microcosm-cc/bluemonday from 1.0.3 to 1.0.4
Bumps [github.com/microcosm-cc/bluemonday](https://github.com/microcosm-cc/bluemonday) from 1.0.3 to 1.0.4.
- [Release notes](https://github.com/microcosm-cc/bluemonday/releases)
- [Commits](https://github.com/microcosm-cc/bluemonday/compare/v1.0.3...v1.0.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-14 08:01:50 -04:00
Deluan
a5e1986072 Fix getTopSongs endpoint 2020-08-13 18:56:13 -04:00
Deluan Quintão
97c98e3369 Update tr.json (POEditor.com) 2020-08-13 17:19:25 -04:00
Deluan Quintão
6effd603e2 Update de.json (POEditor.com) 2020-08-13 17:19:25 -04:00
Deluan Quintão
8a783ef967 Update fr.json (POEditor.com) 2020-08-13 17:19:25 -04:00
Deluan
b74bd30b72 Fix Security Issue CVE-2020-7660 2020-08-13 11:14:13 -04:00
Deluan Quintão
9fa09e41cc Update README.md 2020-08-11 16:05:23 -04:00
Deluan
4ef12f91e0 Support Linux 32 bits releases 2020-08-07 13:36:00 -04:00
Deluan
0730c667a2 Add "Shuffle All" option to Song List. Closes #256 2020-08-07 10:47:55 -04:00
Deluan
4ec451aecb Add content-disposition header to set a download name 2020-08-05 18:40:46 -04:00
Deluan
883dd7f728 Use Outlined download icon
Also remove dangling console.log
2020-08-05 15:25:59 -04:00
Deluan
38c19eddc3 Add 'download' option to album context menu 2020-08-05 14:57:59 -04:00
Deluan
8e4b2e1c06 Add GetTopSongs placeholder, to make AVSub work 2020-08-05 13:48:50 -04:00
Deluan
a541afbfba Revert "Return absolute paths in Subsonic API responses"
This reverts commit 338cbacb
2020-08-05 12:37:43 -04:00
Deluan
df05760769 Move engine package under subsonic, as it should only be used by the Subsonic API.master
The idea is to move reusable code from `engine` to `core`, in future refactorings
2020-08-04 21:29:35 -04:00
Deluan
9a1133601a Store uncompressed files in zip 2020-08-04 13:38:32 -04:00
Deluan
2c370cae28 Support downloading full album and artist discography through Subsonic API 2020-08-04 12:39:13 -04:00
Deluan
f745b8d223 Use transaction's DataStore 2020-08-04 11:53:19 -04:00
Deluan
f1b6703ab0 Update React Player, fix song title maxWidth
See https://github.com/lijinke666/react-music-player/issues/141
2020-08-04 08:41:30 -04:00
Deluan
28d1428c90 Add option to disable .m3u auto-import 2020-08-02 23:17:13 -04:00
Deluan
696a0feb31 Remove ratings from engine package 2020-08-02 17:58:07 -04:00
Deluan
f29e1eb248 Remove repeated call 2020-08-02 15:19:42 -04:00
Deluan
d4e599233e Increase timeout of lint job in pipeline 2020-08-02 14:53:47 -04:00
Deluan
aaec8e080b Remove unused code 2020-08-02 11:16:46 -04:00
Deluan Quintão
09442eccd4 Update README.md 2020-08-01 23:29:27 -04:00
Deluan
21b9f51b71 Rename migrations package, to match goose generated migration files 2020-08-01 16:49:01 -04:00
Deluan
ed726c2126 Better implementation of Bookmarks, using its own table 2020-08-01 12:17:15 -04:00
Deluan
23d69d26e0 Add Bookmarks to Subsonic API 2020-07-31 17:45:49 -04:00
Deluan
3d0e70e907 Add MediaFile to Bookmark 2020-07-31 17:45:49 -04:00
Deluan
34e843a4b3 Add updatedAt to Bookmarks 2020-07-31 17:45:49 -04:00
Deluan
924ada0dab Add bookmark API repsonse 2020-07-31 17:45:49 -04:00
Deluan
2d3ed85311 Add bookmark in persistence layer 2020-07-31 17:45:49 -04:00
Deluan
3d4f4b4e2b Fix lint errors 2020-07-31 17:45:49 -04:00
Deluan
338cbacb79 Return absolute paths in Subsonic API responses 2020-07-31 17:45:49 -04:00
Deluan
0cf574198e Use Last.FM "white star" URL for artist info 2020-07-31 17:45:49 -04:00
Deluan
3000238a3c Implements the get/save play queue Subsonic endpoints and bumps API version to 1.12.0 2020-07-31 17:45:49 -04:00
Deluan
16c38eb344 Add PlayQueue Subsonic response 2020-07-31 17:45:49 -04:00
Deluan
721a959735 Create playqueue table and repository 2020-07-31 17:45:49 -04:00
Deluan
3c2b14d362 Rename make target for creating a new migration 2020-07-31 11:38:56 -04:00
Deluan
2b59d4b87a Rename 'Cover' to the more generic term 'Artwork' 2020-07-31 11:38:56 -04:00
Deluan
cefdeee495 Update Danish translations 2020-07-30 14:26:32 -04:00
Deluan
3383327c51 Show year range over the album art when in "artist view" mode 2020-07-29 22:34:33 -04:00
dependabot-preview[bot]
38b341ebc5 [Security] Bump elliptic from 6.5.2 to 6.5.3 in /ui
Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.2 to 6.5.3. **This update includes a security fix.**
- [Release notes](https://github.com/indutny/elliptic/releases)
- [Commits](https://github.com/indutny/elliptic/compare/v6.5.2...v6.5.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-29 18:47:01 -04:00
Deluan
ef0e5b130d Add a xl breakpoint to the album grid 2020-07-29 15:42:03 -04:00
Deluan
3092f83a00 Add option to select default album view 2020-07-29 15:34:48 -04:00
Deluan
8daac43e99 Add list type to album list view title 2020-07-29 15:34:48 -04:00
Deluan
d5da23ae42 Redirect from plain /album path to a default album list 2020-07-29 15:34:48 -04:00
Deluan
eae46d15bf Fix pagination 2020-07-29 15:34:48 -04:00
Deluan
f6c518fd8b Add Portuguese translation for album lists 2020-07-29 15:34:48 -04:00
Deluan
db8a48bba6 Implement album lists 2020-07-29 15:34:48 -04:00
Deluan
d877928f11 Add UpdatedAt to transcoding cache key 2020-07-28 17:16:01 -04:00
Deluan
0403ec2a07 Use OS-independent path separators 2020-07-28 08:49:07 -04:00
Deluan
8d27c77c2c Highlight compilations in Features 2020-07-27 15:00:03 -04:00
Deluan
f992b5663f Remove old scanner 2020-07-27 12:34:44 -04:00
Deluan
4e4fcb2304 Small refactorings, better var/function names 2020-07-27 10:51:50 -04:00
Deluan
ddb30ceb11 Add a v prefix to the version in the description 2020-07-26 10:52:20 -04:00
Deluan
67da83c84d Use a RWMutex instead of an AtomicBool, to reduce contention 2020-07-26 00:45:33 -04:00
Deluan
f8f16d676d Fix Cached flag 2020-07-24 18:48:28 -04:00
Deluan
58b816c2ed Show cached in info log 2020-07-24 18:43:03 -04:00
Deluan
9b1d5c196f Load cache asynchronously 2020-07-24 16:54:04 -04:00
Deluan
a0bed9beeb Handle missing index.html template 2020-07-24 13:59:41 -04:00
Deluan
9f4f2f7381 Use new FileCache in cover service 2020-07-24 13:30:27 -04:00
Deluan
433e31acc8 Refactor FileCache, allow disabling Trasncoding cache 2020-07-24 12:42:11 -04:00
Deluan
b795ad55a3 Allow SeekStart in a merged dir 2020-07-23 22:00:59 -04:00
Deluan
72efc18158 Allow translations to be overridden in the data folder 2020-07-23 18:11:10 -04:00
Deluan
93626129b6 Also import .m3u8 playlists 2020-07-23 03:26:39 -04:00
Deluan
60178c264d Keep annotations if tracks were already in DB 2020-07-23 03:26:39 -04:00
Deluan Quintão
de6afa16ec Update da.json 2020-07-22 16:07:22 -04:00
Deluan Quintão
fd2df12263 Update cs.json (POEditor.com) 2020-07-22 15:39:50 -04:00
Deluan
37d66a7d41 Add Danish translation 2020-07-22 15:39:50 -04:00
Deluan
040c7f1e7d Add missing call to refresh artists 2020-07-22 15:37:24 -04:00
Deluan
d4a5508f6a Remove LogLevel from Dockerfile 2020-07-22 12:56:50 -04:00
Deluan
036f9d6730 Flush albums and artists after each folder added/updated/deleted 2020-07-22 12:56:50 -04:00
Deluan
1b7f628759 Add tests for paths with UTF8 chars 2020-07-22 11:48:09 -04:00
Deluan
5a891fda9e Handle utf8 chars in paths 2020-07-22 09:36:22 -04:00
Deluan
f96e2f6c4f Process deleted folders even if there are no changed folders 2020-07-22 01:29:44 -04:00
Deluan
7a5285ae47 When deleting folders, only flush artists/albums after deleting the mediaFiles 2020-07-22 01:00:16 -04:00
Deluan
ba347bc0b1 Detect moved folders 2020-07-22 00:42:12 -04:00
Deluan
1bee98af52 Increase streamer test timeout 2020-07-21 20:43:59 -04:00
Deluan
ff623a8dce Run pre-push linting in verbose more 2020-07-21 20:30:04 -04:00
Deluan
f28e8118dc Strip 'v' prefix from version, to make it consistent for release and snapshot 2020-07-21 20:22:23 -04:00
Deluan
167fca86d0 Fix pipeline 2020-07-21 18:12:59 -04:00
Deluan
b828650cc5 Reduce the availability of old pipeline binaries artifacts 2020-07-21 18:11:09 -04:00
Deluan
e6846de0fa Small change, to trigger the pipeline that is stuck! 2020-07-21 17:46:35 -04:00
Deluan
6c6254a3c3 Get all git history when building the binaries 2020-07-21 17:37:36 -04:00
Deluan
0a9ad4e73a Bump action/upload-artifact and action/download-artifact to v2 2020-07-21 16:59:33 -04:00
Deluan
9f6eb4174f Do not upload packaged binaries as artifacts 2020-07-21 16:07:36 -04:00
Deluan
25cc523006 Output git tag info in the pipeline 2020-07-21 15:38:23 -04:00
Deluan
4c0000a809 Use Contributor Covenant v2.0 2020-07-21 14:40:21 -04:00
Deluan Quintão
0f7193f85d Create CODE_OF_CONDUCT.md 2020-07-21 14:19:34 -04:00
Deluan
715855280e Bump react-admin to 3.7.1 2020-07-21 13:15:03 -04:00
Deluan
c322253fde Upgrade react-player to 4.16.3 2020-07-21 13:06:33 -04:00
Deluan
17cea91e10 Bump @testing-library versions 2020-07-21 10:37:11 -04:00
Deluan
6caa5ee81f Bump react-ga from 3.0.0 to 3.1.2 2020-07-21 10:31:16 -04:00
Deluan
d46a8cf89f Allows config file to be specified with env var ND_CONFIGFILE. Fixes #415 2020-07-20 18:36:12 -04:00
Deluan
7e81a3b895 Fix default background image for login 2020-07-20 14:34:02 -04:00
Deluan
d268075046 Change the default scanner to use new implementation 2020-07-19 21:39:06 -04:00
Deluan
482f46f3fd Remove unneeded context in log calls 2020-07-19 15:28:50 -04:00
Deluan
f0160f5d2a Rate limit login attempts using a Sliding Window counter rate-limiter 2020-07-19 14:45:05 -04:00
Deluan
feca030c6d Give warning when playlists are not imported due to not having an admin user 2020-07-19 13:58:46 -04:00
Deluan
41138bd665 Only show auto-import info for auto-imported playlists 2020-07-18 01:03:44 -04:00
Deluan
178e42487b Remove invalid config options 2020-07-17 23:16:04 -04:00
dependabot[bot]
ae04919585 Bump lodash from 4.17.15 to 4.17.19 in /ui
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.19.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.15...4.17.19)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-17 22:58:51 -04:00
Deluan
6adba03868 Renamed misleading function name 2020-07-17 22:55:51 -04:00
Deluan
609d172259 Use first admin user for all scan operations 2020-07-17 22:55:51 -04:00
Deluan
9cf8c92cae Break up processChangedDir into smaller functions 2020-07-17 22:55:51 -04:00
Deluan
38c3013ddf Add auto-import fields to the UI 2020-07-17 22:55:51 -04:00
Deluan
8f512a40f7 Refactored playlist auto-import support 2020-07-17 22:55:51 -04:00
Deluan
b9b6ce066b Auto-Import playlists found in the Music Folder 2020-07-17 22:55:51 -04:00
Deluan
35114be5f7 Add path to playlist 2020-07-17 22:55:51 -04:00
Deluan
3239be4a4d Change log level of some scanner operations 2020-07-17 12:49:37 -04:00
Deluan
a706cb46fa Fix pre-push hook 2020-07-17 12:16:23 -04:00
Deluan
3095bee5d9 Fix lint error 2020-07-17 12:16:16 -04:00
Deluan
51c295d1de Add new scanner algorithm, can be enabled with DevNewScanner config option 2020-07-17 12:06:49 -04:00
Deluan
de0cc1f268 Move LoadAllAudioFiles tests to the proper test file 2020-07-16 18:18:48 -04:00
Deluan
037f6b606e Replace lefthook with shell script 2020-07-16 18:14:02 -04:00
Deluan
e7f6ba8f35 Move LoadAllAudioFiles function to the right file 2020-07-16 17:42:26 -04:00
Deluan
25f68b6c89 If mediafile does not have an embedded coverart, use album's 2020-07-16 17:08:52 -04:00
Deluan
dc50f672b8 Fix Makefile target name 2020-07-16 16:57:19 -04:00
Deluan
d14a6031f0 Add test for case-sensitive DeleteByPath 2020-07-14 15:35:42 -04:00
Deluan
8b20c26e04 Make "ByPath" queries case-sensitive 2020-07-14 15:27:27 -04:00
Deluan
1ef0869a54 Strip debugging info from binaries. Closes #405 2020-07-14 13:58:39 -04:00
Deluan Quintão
ca10e800a9 Add demo site to README.md 2020-07-14 07:59:14 -04:00
Deluan
33d5459c20 Escape paths in "ByPath" queries 2020-07-14 07:20:27 -04:00
Deluan
aae43f4452 Remove unneeded \n 2020-07-13 11:49:06 -04:00
Deluan
0bd842869b go mod tidy 2020-07-13 09:35:39 -04:00
Deluan
394d3b0e67 Turn off Go 1.14 async preemption as it causes issues with CIFS/SMB access. See #393 2020-07-12 22:09:51 -04:00
Deluan
1ef17e2986 Remove version command 2020-07-12 20:44:19 -04:00
Deluan
d4347f20ae Remove redundant log message 2020-07-12 20:42:38 -04:00
Deluan
3319f78de0 Remove unnecessary config from docker images 2020-07-12 14:09:32 -04:00
Deluan
ee0ae0a06c Fix lint errors 2020-07-12 13:36:22 -04:00
Deluan
064da8e034 Add more trace logging to scanner 2020-07-12 13:30:03 -04:00
Deluan
74cf0ee1c1 Create Data Folder if it does not exist 2020-07-12 12:36:08 -04:00
Deluan
c2f40ea8a3 Show totals at the end of scan 2020-07-12 12:35:23 -04:00
Deluan
f694e471fb Make private types unexported 2020-07-12 11:55:19 -04:00
Deluan
dc8368c89c Return counter from DeleteByPath 2020-07-12 11:53:07 -04:00
Deluan
e55397fcdc Bump github.com/onsi/ginkgo from 1.13.0 to 1.14.0 2020-07-12 11:24:55 -04:00
Deluan
8260b46e8f Fix migration 2020-07-12 11:22:24 -04:00
Deluan
b59c6c85e0 Add support for armv5. Closes #395 2020-07-12 09:43:18 -04:00
Deluan
b96ff9c210 Use ci-goreleaser 1.14.4-2
This should generate binaries compatible with OpenVZ (kernel 2.6.32)
2020-07-12 09:43:18 -04:00
Deluan
c758780e38 Remove MUSL build 2020-07-11 14:33:23 -04:00
Deluan
9e35534dad Fix lint errors
New environment, forgot to setup it properly...
2020-07-10 13:11:02 -04:00
Deluan
5620c58a30 Started the big refactor to extract common logic from engine package (Subsonic only) to core package (more generic) 2020-07-10 12:53:11 -04:00
Deluan
5418a6b6b1 Remove unused docker files 2020-07-09 00:45:04 -04:00
Deluan
865bad1550 Send play song event to GA 2020-07-08 21:23:51 -04:00
Deluan
7c3fd38559 Add option to change IP address to bind 2020-07-08 20:54:56 -04:00
Deluan Quintão
933052583a Update FUNDING.yml 2020-07-08 13:07:08 -04:00
Deluan Quintão
941e252d44 Update FUNDING.yml 2020-07-08 12:57:46 -04:00
Deluan
f0a5df7cd7 Move transcodings initialization to a migration
This will make it run only once, not every
time the transcoding table is empty
2020-07-06 23:48:43 -04:00
Deluan
fdc38b5ca5 Enable DSD (.dsf) support 2020-07-06 12:25:55 -04:00
Deluan
2f8b01015d Change log level for "path unavailable" 2020-07-04 11:36:57 -04:00
Deluan
2a302de42f Set default session timeout to 24h (agan) 2020-07-03 22:08:32 -04:00
Deluan
681849d174 Fix pls ignoring 2020-07-03 21:15:01 -04:00
Deluan
17830d63b4 Ignore m3u files when scanning 2020-07-03 21:06:33 -04:00
Deluan
1cc03fdd8c Add initial support for Google Analytics 2020-07-03 13:51:31 -04:00
Deluan
dd91f983b5 Add new config option to show a custom welcome message in the login screen 2020-07-03 11:51:15 -04:00
Deluan
3a7d70c908 Add scan command 2020-07-03 10:49:11 -04:00
Deluan
8181aba61f Clean up a bit 2020-07-03 10:19:44 -04:00
Deluan
2d0539300d Exit if specified config file is not present 2020-07-03 10:10:49 -04:00
Deluan
f45045d1c0 Bump viper version to 1.7.0 2020-07-03 09:49:52 -04:00
Deluan
6954e1b4eb Fix linting error 2020-07-03 09:46:58 -04:00
Deluan
ef9af6ed1a Don't fail if config file isnot found 2020-07-03 09:39:28 -04:00
Deluan
99e269208e Fix lint errors 2020-07-02 18:17:31 -04:00
Deluan
f980e24868 Add missing wire file 2020-07-02 18:17:26 -04:00
Deluan
a65c9bbb16 Refactor and clean up 2020-07-02 17:53:51 -04:00
Deluan
d2e4cade62 Change duration config types 2020-07-02 17:53:51 -04:00
Deluan
5021c0fd0c Replace multiconfig with cobra+viper 2020-07-02 17:53:51 -04:00
Deluan Quintão
fea060e4f2 Update FUNDING.yml 2020-07-02 11:57:52 -04:00
Deluan
7a9b848f38 Add quality to image cache key 2020-07-01 17:02:27 -04:00
Deluan Quintão
2d8f0a740e Add FUNDING.yml 2020-07-01 12:31:04 -04:00
Deluan
fa107a6b65 Bump Beego version to v1.12.2 2020-07-01 10:00:19 -04:00
Deluan
2371e9b943 Add option to set jpeg quality level. Closes #371 2020-06-29 17:20:38 -04:00
Deluan
f0ee52a98e Fix album refresh query. Fixes #373 2020-06-29 14:17:28 -04:00
Deluan
c01d81802d Fix album's songCount. Fixes #373 2020-06-29 11:35:51 -04:00
Deluan
890ca64f51 Fix cover.jpg discovery 2020-06-29 10:50:38 -04:00
Deluan
bcaf330233 Make sure to select cover art from media_file that has it. Fix #360 2020-06-27 22:16:07 -04:00
Deluan
ab1c943d1f Force album/artist refresh when folder changes, to cater for cover art files 2020-06-27 18:41:55 -04:00
Deluan
703875b895 Fallback to album art if mediaFile does not have cover art 2020-06-27 13:11:51 -04:00
Deluan
5f40801a78 Add more logs to GC call 2020-06-26 10:23:05 -04:00
Deluan
eb109ebeb4 Remove duplicated helper functions, move them to utils package 2020-06-24 20:48:42 -04:00
Alex Palaistras
bb9a7fadc0 Add tests for external album cover processing
This implements basic tests for functionality related to loading and
processing external album covers, both on the scanning size, and on the
display side.
2020-06-24 20:48:42 -04:00
Alex Palaistras
ac5d99c079 Check MIME type for cover on refresh, display
Files that match the `CoverArtPriority` setting will now be considered
eligible only if their extensions are of an 'image/*' MIME type (e.g.
'.png' for 'image/png', '.jpg' for 'image/jpeg'). This prevents matching
files that will likely not be valid during display.

In addition to the above, code for returning the cover image file from
scanned data will also check against the MIME type for the path stored,
instead of attempting to re-trace `CoverArtPriority` matches. This
simplifies the code and bypasses a number of edge-cases related to
inconsistent matching.
2020-06-24 20:48:42 -04:00
Alex Palaistras
d9c991e325 Return error when no matching cover is found
When checking stored references to cover images (whether embedded or
external), it's possible that configured patterns do no match, and a
valid error should be returned in those cases.
2020-06-24 20:48:42 -04:00
Alex Palaistras
08cd28af2d Load cover art from file directory
This commit adds support for loading cover art from media file
directories, according to configured filename priorities (of which an
additional, special choice of `embedded` is given).

Cover art paths are resolved during scanning and stored in the database
as part of the `album.cover_art_path` column; if embedded cover art is
matched, this will default to the path of the media file itself, and if
no cover art is matched at all.

Similarly, the `album.cover_art_id` column will default to a reference
to `media_file.id` if embedded cover art is wanted, but if an external
cover art file is matched, this will instead be set to a reference to
the `album.id` value itself, prefixed with the `al-` constant.

Stored cover art paths are once again resolved and matched against
configuration when covers are requested; that is, any change in
configuration between scanning and requesting cover art may not return
correct data until a re-scan is complete.

Tests will be added in future commits.
2020-06-24 20:48:42 -04:00
Deluan
6563897692 Restore volume level after a refresh 2020-06-24 15:33:59 -04:00
Deluan Quintão
04d598819d Update tr.json (POEditor.com) 2020-06-23 16:53:55 -04:00
Deluan Quintão
965c04469e Update it.json (POEditor.com) 2020-06-23 16:53:55 -04:00
Deluan Quintão
416ca2c063 Update de.json (POEditor.com) 2020-06-23 16:53:55 -04:00
Deluan Quintão
ab35586b0c Update fr.json (POEditor.com) 2020-06-23 16:53:55 -04:00
Deluan Quintão
acb5985127 Update cs.json (POEditor.com) 2020-06-23 16:53:55 -04:00
Deluan
9b75b729ba Convert function to arrow-function 2020-06-22 19:55:04 -04:00
dependabot-preview[bot]
e1968b0953 Bump @testing-library/user-event from 11.2.0 to 12.0.6 in /ui
Bumps [@testing-library/user-event](https://github.com/testing-library/user-event) from 11.2.0 to 12.0.6.
- [Release notes](https://github.com/testing-library/user-event/releases)
- [Changelog](https://github.com/testing-library/user-event/blob/master/CHANGELOG.md)
- [Commits](https://github.com/testing-library/user-event/compare/v11.2.0...v12.0.6)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-22 10:58:37 -04:00
Deluan
f36e15cfeb Upgrade dependencies 2020-06-22 10:23:26 -04:00
Deluan
7547c775fa Bump React-Admin version to 3.6.1 2020-06-22 10:17:55 -04:00
dependabot-preview[bot]
ad21b5f0d0 Bump react-drag-listview from 0.1.6 to 0.1.7 in /ui
Bumps [react-drag-listview](https://github.com/raisezhang/react-drag-listview) from 0.1.6 to 0.1.7.
- [Release notes](https://github.com/raisezhang/react-drag-listview/releases)
- [Commits](https://github.com/raisezhang/react-drag-listview/compare/0.1.6...0.1.7)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-22 09:31:30 -04:00
Deluan
4427900d84 Fix formatting 2020-06-22 08:56:55 -04:00
Deluan
0ca70b1e4d Add back Artist column header to Album List View. Fixes #363 2020-06-22 08:50:41 -04:00
Deluan
0292a334fe Fix mistranslation 2020-06-22 00:11:19 -04:00
Deluan
f93e2d0c04 Use memoization to avoid re-renders 2020-06-19 19:43:33 -04:00
Deluan
3a9324c6ef Enable autoPlay in React Player 2020-06-19 16:32:54 -04:00
Deluan
cf692140a9 Revert "Upgrade to React Player 4.15.1"
This reverts commit de693b8206. (+1 squashed commit)
Squashed commits:
[cc80cb8] Revert "Simplify handle"

This reverts commit 83b8fa14c6.
2020-06-19 16:31:38 -04:00
Deluan
83b8fa14c6 Simplify handle 2020-06-19 12:37:26 -04:00
Deluan
de693b8206 Upgrade to React Player 4.15.1 2020-06-19 12:24:11 -04:00
Deluan
1686e358fe Simplify PlayQueue store 2020-06-19 11:32:24 -04:00
Deluan
804d969427 Clear play queue on login and logout 2020-06-19 11:32:23 -04:00
Deluan
9d23b191b5 Show indicator on current playing song. Fixes #128 2020-06-19 11:32:23 -04:00
Deluan
eb4c0f0b84 go mod tidy 2020-06-18 12:51:15 -04:00
dependabot-preview[bot]
c507e344ff Bump github.com/onsi/ginkgo from 1.12.3 to 1.13.0
Bumps [github.com/onsi/ginkgo](https://github.com/onsi/ginkgo) from 1.12.3 to 1.13.0.
- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v1.12.3...v1.13.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-15 09:16:51 -04:00
Deluan
a6af46dbad Always use lowercase username, as it is used for referential integrity. Fixes #352 2020-06-14 20:20:10 -04:00
Deluan
2d1d992e17 Support Windows paths 2020-06-14 03:11:16 -04:00
Deluan
653b5ea9d3 Replace map[string]bool with map[string]struct{} 2020-06-14 03:11:16 -04:00
Deluan
e73b71aaf7 Remove tracks from DB that were deleted while Navidrome was not running. Fixes #151 2020-06-14 03:11:16 -04:00
Deluan
01919661e9 Skip unreadable directories. Fixes #328 2020-06-14 03:11:16 -04:00
Deluan
3190611ec8 Call ffmpeg in batches 2020-06-14 03:11:16 -04:00
Deluan
6a3dabbb06 Optimize queries by path 2020-06-14 03:11:16 -04:00
Deluan
238020c839 Handle folders with lots of albums and/or artists 2020-06-14 03:11:16 -04:00
Deluan
72b2e756f7 Revert "Show indicator on current playing song. Fixes #128"
This implementation causes performance issues
2020-06-13 16:41:11 -04:00
Deluan
86bc8d97a0 Support dark themes in "Playing" indicator 2020-06-13 14:38:25 -04:00
Deluan
003b73fe1a Remove invalid propType 2020-06-13 14:04:45 -04:00
Deluan
be2afb94ae Show indicator on current playing song. Fixes #128 2020-06-13 14:04:45 -04:00
Deluan
f8a18b59b0 Add link to album from player's song title. Fixes #324 2020-06-12 17:02:13 -04:00
Deluan Quintão
c216b14655 Add total downloads badge 2020-06-12 14:21:02 -04:00
Deluan
4702c5abbd Add track/artist being played to the page title. Closes #317 2020-06-11 22:40:35 -04:00
Deluan
c742ae0843 Remove unused feature toggles 2020-06-11 22:11:59 -04:00
Deluan
0033966c25 No need to delete the playlist tracks explicitly 2020-06-10 18:07:10 -04:00
Deluan
f072ffd377 Add confirmation when deleting user 2020-06-10 18:07:10 -04:00
Deluan
94d88395e7 Add referential integrity to player and playlist tables 2020-06-10 18:07:10 -04:00
Deluan
c9bcb333ae Add more options to Players list 2020-06-10 18:07:10 -04:00
Deluan Quintão
84ed3eb427 Update README.md 2020-06-10 11:34:51 -04:00
Deluan
8bd9787c51 Fix function naming 2020-06-09 20:45:53 -04:00
Deluan
1c466d6083 Fix formatting 2020-06-09 20:34:36 -04:00
Deluan
a64b15c174 Fix navigation issues caused by the use of useListParams 2020-06-09 20:29:12 -04:00
Deluan
7148741a4f Revert "Keep image aspect ratio when resizing"
This reverts commit 50f4bd86
2020-06-09 19:36:19 -04:00
Deluan
630c71119a Use fix for Opus cover art from https://github.com/dhowden/tag/pull/69 2020-06-09 17:08:05 -04:00
Deluan
50f4bd86a3 Keep image aspect ratio when resizing 2020-06-09 10:34:47 -04:00
Deluan
44c74f42e1 Add clickToPlay functionality to playlists 2020-06-09 08:54:11 -04:00
Deluan
29c7513879 Update updated_at field when modifying the playlist 2020-06-09 07:55:35 -04:00
Deluan
82d437f004 Better defaults to sort orders in List views 2020-06-09 07:46:28 -04:00
Deluan
b54d4c75ae Show year in album tile if album grid is filtered bu artist 2020-06-08 20:37:18 -04:00
Deluan
b636565c62 Disable public toggle if user is not the playlist's owner 2020-06-08 19:19:38 -04:00
Deluan
b4e06c416d Allow toggling a playlist public from the Playlist list view. Closes #344 2020-06-08 18:39:31 -04:00
dependabot-preview[bot]
5e2d463129 Bump @testing-library/user-event from 10.4.1 to 11.2.0 in /ui
Bumps [@testing-library/user-event](https://github.com/testing-library/user-event) from 10.4.1 to 11.2.0.
- [Release notes](https://github.com/testing-library/user-event/releases)
- [Changelog](https://github.com/testing-library/user-event/blob/master/CHANGELOG.md)
- [Commits](https://github.com/testing-library/user-event/compare/v10.4.1...v11.2.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-05 13:42:40 -04:00
Deluan
12d5d9573e Bum @testing-library dependencies 2020-06-05 13:34:46 -04:00
dependabot-preview[bot]
42ee8b64cb [Security] Bump websocket-extensions from 0.1.3 to 0.1.4 in /ui
Bumps [websocket-extensions](https://github.com/faye/websocket-extensions-node) from 0.1.3 to 0.1.4. **This update includes a security fix.**
- [Release notes](https://github.com/faye/websocket-extensions-node/releases)
- [Changelog](https://github.com/faye/websocket-extensions-node/blob/master/CHANGELOG.md)
- [Commits](https://github.com/faye/websocket-extensions-node/compare/0.1.3...0.1.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-05 13:31:47 -04:00
Deluan
3908ad2681 Upgrade ReactAdmin to 3.6.0 2020-06-05 12:13:50 -04:00
Deluan
e9115dab4c Allow Writable to have multiple children 2020-06-05 11:55:30 -04:00
Deluan
79cf33281c Redirect to Playlists list after creating or editing 2020-06-05 11:55:30 -04:00
Deluan
2adb290c34 Do not show a "loading" datagrid for an empty playlist 2020-06-05 11:55:29 -04:00
Deluan
c6f23139bc Handle playlist's permissions on server 2020-06-05 11:55:29 -04:00
Deluan
4906b816af Only allows adding to a writable playlist 2020-06-05 10:26:53 -04:00
Deluan
39afe0c669 Check permissions for playlists 2020-06-05 10:22:31 -04:00
Deluan
f8a7ef1e19 Fix typo 2020-06-04 20:13:25 -04:00
Deluan
4776dba003 Make cursor=move for the whole playlist item row 2020-06-04 19:44:26 -04:00
Deluan
331fa1d952 Add ability to reorder playlist items 2020-06-04 19:05:41 -04:00
Deluan
b597a34cb4 Remove flickering when loading/refreshing Playlist show view 2020-06-04 16:54:30 -04:00
Deluan
51fb1d1349 Increase cover art max-age to maximum 2020-06-04 14:45:00 -04:00
Deluan
8fd86def18 Bump ginkgo version to 1.12.3 2020-06-03 09:43:34 -04:00
Deluan
5d285f92f5 Bump chi version to 4.1.2 2020-06-03 09:42:16 -04:00
Deluan
888151728f Increase album art placeholder's resolution 2020-06-03 09:40:37 -04:00
Deluan
b836dfe7f4 Do not reset the SongList query params 2020-05-31 14:27:02 -04:00
Deluan
ddcfc546fb Link is not on the album cover, leaving a gap between albums.
Other small improvements
2020-05-31 13:57:17 -04:00
Deluan
86a9f9e410 Show album info on hover 2020-05-30 19:42:08 -04:00
Deluan
14d7a69088 Fix context menu "display on hover" in playlists 2020-05-30 11:18:01 -04:00
Deluan
35e4eec293 Add album to playlist 2020-05-30 11:17:33 -04:00
Deluan
7547888f10 Change default session timeout to 24h 2020-05-30 10:34:16 -04:00
Deluan
fbedbb7893 Fix context menu on mobile, removed console warnings 2020-05-29 22:50:33 -04:00
Deluan
a7640c9df4 Optimized call to retrieve album songs 2020-05-29 17:34:54 -04:00
Deluan
8f8d992da4 Only add to playlist songs from selected discNumber (if present) 2020-05-29 16:42:13 -04:00
Deluan
3fe8b02cbd Make album context menu only visible on hover 2020-05-29 12:33:50 -04:00
Deluan
ba8c8725dd Refactor: move multiDisc detection logic to SongDatagrid 2020-05-29 12:20:17 -04:00
Deluan
915b701e44 Add context menu to individual discs in a set 2020-05-29 12:08:07 -04:00
Deluan
596100b58d Refactor: improve readability 2020-05-29 11:21:53 -04:00
Deluan
d8699b03bd Fix album sort fields 2020-05-28 20:48:58 -04:00
Deluan
7b36096153 Fix class of disc subtitle row 2020-05-28 09:25:53 -04:00
Deluan
62290bca77 Remove extra , 2020-05-28 08:16:31 -04:00
Deluan
498e196d48 Allow playing one disc of a set, by clicking on its number/name 2020-05-27 21:07:51 -04:00
Deluan Quintão
432fe10a5e Update tr.json (POEditor.com) 2020-05-27 09:48:04 -04:00
Deluan Quintão
7e625d68b5 Update de.json (POEditor.com) 2020-05-27 09:48:04 -04:00
Deluan
50f3a2c11d Upgrade Node to v14 2020-05-27 05:35:25 -04:00
Deluan
9028d301f0 Change log level for playlist log messages 2020-05-26 22:03:25 -04:00
Deluan
26dba27778 Always show song context menu on tablets 2020-05-26 22:02:15 -04:00
Deluan
7170485d08 Rename property 2020-05-26 17:59:04 -04:00
Deluan
2c68ba3934 only show playlist tracks' context menu on hover 2020-05-26 16:18:28 -04:00
Deluan
201a22e613 Change index in playlist to start from 1 2020-05-26 13:50:15 -04:00
Deluan Quintão
3ca295c863 Update it.json (POEditor.com) 2020-05-25 23:23:36 -04:00
Deluan Quintão
be85fe3773 Update de.json (POEditor.com) 2020-05-25 23:23:36 -04:00
Deluan Quintão
7c3d96cf6c Update fr.json (POEditor.com) 2020-05-25 23:23:36 -04:00
Deluan Quintão
50b44c1991 Update cs.json (POEditor.com) 2020-05-25 23:23:36 -04:00
Deluan
f9dae2dd2a Added individual AddToPlaylistDialogs to each list view 2020-05-25 22:51:31 -04:00
Deluan
00811f8000 Cancel the dialog when clicking the backdrop 2020-05-25 22:51:31 -04:00
Deluan
9c940cd44f Show AutomcompleteInput even if the list of playlists is not loaded yet 2020-05-25 22:51:31 -04:00
Deluan
1607dc8b88 Remove unused dependency 2020-05-25 22:51:31 -04:00
Deluan
a42a16696e Translate messages 2020-05-25 22:51:31 -04:00
Deluan
6db63e4dfc Use creatable autocomplete, to select or create a new playlist 2020-05-25 22:51:31 -04:00
Deluan
23bd5e1131 First version of dialog 2020-05-25 22:51:31 -04:00
Deluan
8973477fe5 npm audit fix 2020-05-25 21:43:50 -04:00
Deluan
fbd6c965b0 Always return public attribute in playlist response 2020-05-25 21:00:05 -04:00
Deluan
aaa4f1531e Ignore brackets in search 2020-05-25 11:05:30 -04:00
Deluan
72e92c7318 Fix nil pointer dereference 2020-05-25 10:54:07 -04:00
Deluan
72cb3850d1 Update React Admin to 3.5.3 2020-05-24 23:32:36 -04:00
Deluan
a6cc88177c Fix "starred" sorting 2020-05-24 12:49:32 -04:00
Deluan Quintão
d6ad833538 Update de.json (POEditor.com) 2020-05-24 12:23:39 -04:00
Deluan Quintão
eb1749ce71 Update fr.json (POEditor.com) 2020-05-24 12:23:39 -04:00
Deluan Quintão
acebe18c95 Update cs.json (POEditor.com) 2020-05-24 12:23:39 -04:00
Deluan
cac1a20ec8 Use a ☆ instead of the word "starred" 2020-05-24 12:14:45 -04:00
Deluan
ac8f92d7ac Fix ContextMenu column label 2020-05-24 12:14:44 -04:00
Deluan
207565bde0 Update pt translation 2020-05-24 11:33:05 -04:00
Deluan
3ae1586e10 Add "No playlists available" to context menu 2020-05-24 11:27:17 -04:00
Deluan
5c46f7822f Better disc subtitle 2020-05-23 15:33:29 -04:00
Deluan
c13766bbc3 More optimization for small screens 2020-05-23 14:11:39 -04:00
Deluan
290e8c4bf0 Make Starred into a "QuickFilter" 2020-05-23 13:25:30 -04:00
Deluan
442671578d Fix warning in JS console (wrong property type) 2020-05-23 12:49:39 -04:00
Deluan
1bca8fca97 Enable UI starred by default 2020-05-23 01:07:34 -04:00
Deluan
e811816021 Fix pagination in Songs when filtered by starred 2020-05-23 00:43:45 -04:00
Deluan
9331be67a3 Fix pagination in Songs 2020-05-23 00:17:35 -04:00
Deluan
55ad5c9fc9 Remove unused import, fix build 2020-05-22 23:33:40 -04:00
Deluan
ec0002e77a Add a sortable Starred column and a Starred filter to Song List 2020-05-22 23:10:58 -04:00
Deluan
3632608de0 Replace child.type.name, as it is not available in the production build 2020-05-22 22:23:00 -04:00
Deluan
0a3e6c66c1 Alwasy show context menu on mobile views 2020-05-22 21:47:48 -04:00
Deluan
52a46e61e0 Remove duplication 2020-05-22 21:31:45 -04:00
Deluan
de2759b3d5 Fix react key conflic 2020-05-22 20:48:49 -04:00
Deluan
978e7f2eaa Only show SongContextMenu on hover 2020-05-22 20:15:58 -04:00
delucks
ae847103a2 Correct response body for getSongsByGenre 2020-05-22 18:08:35 -04:00
Deluan
6f6b223453 Disable ToggleStar on playlist tracks 2020-05-22 15:45:03 -04:00
Deluan
8a68cecdb9 Add ToggleStar to SongContextMenu (WIP) 2020-05-22 15:23:42 -04:00
Deluan
e21262675e More log to media_streamer 2020-05-21 21:26:48 -04:00
Deluan
a3ba05b2cc Use latest ci-goreleaser: Go 1.14.3 and Goreleaser 1.35.0 2020-05-21 14:56:56 -04:00
Deluan
294712739a Bump ginkgo/gomega dependencies 2020-05-21 13:41:12 -04:00
Fup
ad725ac355 Add [Install] section to systemd unit file.
This section apparently is mandatory now.
2020-05-21 08:19:12 -04:00
Deluan
17df63b550 Fix child.size and directory.playCount compatibility with Subsonic API. Fixes #304 2020-05-19 23:51:23 -04:00
Deluan
c2d1e9df9f Remove orphan tracks from playlists after they are removed from library 2020-05-18 20:32:01 -04:00
Deluan
0e4f7036eb Make playlist songs look better in mobile 2020-05-18 18:00:55 -04:00
Deluan
a4183aea8c Unexport private functions 2020-05-18 15:06:33 -04:00
Deluan
9e845cb116 Skip scanning folders if they contain a .ndignore file. Closes #297 2020-05-18 14:37:01 -04:00
Deluan Quintão
f82fefe0ab Update de.json (POEditor.com) 2020-05-18 13:52:30 -04:00
Deluan
f28531b609 Add album name to song details 2020-05-18 13:32:12 -04:00
Deluan
14f3ffbee6 Allow sorting playlist tracks 2020-05-18 13:21:47 -04:00
Deluan
94e1b1f65d Add context menu to playlist songs 2020-05-18 13:05:54 -04:00
Deluan
274eb805f9 Upgrade golangci-lint 2020-05-18 12:43:03 -04:00
Deluan
84ea852339 Prettier 2020-05-18 12:19:01 -04:00
Deluan
cf019849f0 Add missing translation key 2020-05-18 12:15:46 -04:00
Deluan
76a5d1928e Fix some JS warnings 2020-05-18 12:12:04 -04:00
Deluan
3dced978c7 Add button to edit playlist details 2020-05-18 12:12:04 -04:00
Deluan
6071ae143e Bump ginkgo version 2020-05-18 10:25:33 -04:00
Deluan
05a07f31c9 Bump react-music-player version 2020-05-18 10:14:50 -04:00
Deluan
1afbbbf189 Add SongContextMenu to Album Songs 2020-05-17 20:57:38 -04:00
Deluan
308163c2e0 Add "AddToPlaylist" to AlbumContextMenu 2020-05-17 20:30:05 -04:00
Deluan Quintão
176bfe1506 Update fr.json (POEditor.com) 2020-05-17 15:51:52 -04:00
Deluan Quintão
4c3f3f3573 Update cs.json (POEditor.com) 2020-05-17 15:51:50 -04:00
Deluan
1aef21a4a9 Update translations for playlists 2020-05-17 10:23:46 -04:00
Deluan
d1a0ffaaee Check permissions in playlists 2020-05-16 23:14:28 -04:00
Deluan
41010515ee Enable Playlist Management in the UI by default 2020-05-16 19:16:48 -04:00
Deluan
a734a1aaa3 Add filter by name to Playlist list 2020-05-16 19:14:19 -04:00
Deluan
bf1dc33782 Add option to shuffle playlist 2020-05-16 19:11:52 -04:00
Deluan
c43798c5dd Filter out songs not in the playlist 2020-05-16 19:02:33 -04:00
Deluan
12cf2f1104 Remove tracks from playlist 2020-05-16 18:35:34 -04:00
Deluan
5c95eed517 Rename actions 2020-05-16 18:35:34 -04:00
Deluan
e81a9dd1b5 Add tracks to playlist 2020-05-16 18:35:34 -04:00
Deluan
fd49ae319f Add Playlist action 2020-05-16 18:35:34 -04:00
Deluan
f881e2a54b Add option to enable (experimental) playlists in UI 2020-05-16 18:35:34 -04:00
Deluan
0ca79eead4 Show Playlist tracks 2020-05-16 18:35:34 -04:00
Deluan
8a709c489a Add playlist views 2020-05-16 18:35:34 -04:00
Deluan
b1f5d35f73 Fix DurationField breaking when the record does not have the source field 2020-05-16 18:35:34 -04:00
Deluan
5682d0e721 Remove tracks from Playlist's GetAll 2020-05-16 18:35:34 -04:00
Deluan
ab690215ef Make Playlist's songCount sortable 2020-05-16 18:35:34 -04:00
Deluan
8f9601090c Add helper functions tests 2020-05-16 18:35:34 -04:00
Deluan
aebee651ac Add nested resource playlist/{id}/tracks 2020-05-16 18:35:34 -04:00
Deluan
a56e588c8e Create relation table for playlist tracks 2020-05-16 18:35:34 -04:00
Deluan
27de18f8c9 Fix typo 2020-05-16 18:35:34 -04:00
Deluan
5afcd0ad22 Make field names camelCase 2020-05-16 18:35:34 -04:00
Deluan
fec589dce5 Add playlist list 2020-05-16 18:35:34 -04:00
Deluan
4e613be960 Add playlists REST endpoint 2020-05-16 18:35:34 -04:00
Deluan
8e2480a82d Fix duration (in web player) when playing transcoded files. Thanks @lijinke666
See: https://github.com/lijinke666/react-music-player/issues/90
2020-05-16 13:24:25 -04:00
Deluan
50eda78ca1 Revert "Save perPage selection in localstorage"
This reverts commit 9490374faa.
2020-05-15 11:04:48 -04:00
Deluan
b3af0f880b Use a custom List component 2020-05-15 11:03:59 -04:00
Deluan
9490374faa Save perPage selection in localstorage 2020-05-14 23:05:14 -04:00
Deluan
a340b62fdf Link to artist from album list 2020-05-14 20:42:21 -04:00
Deluan
0d1af8c635 Fix potential null reference exception 2020-05-14 19:01:07 -04:00
Deluan
377c9e6be6 Revert "Upgrade golangci-lint"
This reverts commit b8ae5ccb02.
2020-05-13 23:26:54 -04:00
Deluan
b8ae5ccb02 Upgrade golangci-lint 2020-05-13 16:50:13 -04:00
Deluan
f8362a4acb Fix staticcheck's SA1029 2020-05-13 16:49:55 -04:00
Deluan
5ce3135f00 Fix gosec's G601 2020-05-13 15:32:42 -04:00
Deluan
162971f7b3 Remove console.log 2020-05-12 14:48:22 -04:00
Deluan
49dd13002c Update Portuguese translation 2020-05-12 14:20:24 -04:00
Deluan
1e5c879fc6 Extract disc subtitle strings for translation 2020-05-12 14:13:34 -04:00
Deluan
e369cbf493 Disable album songs sorting 2020-05-12 13:47:59 -04:00
Deluan
a88270a22b Add multidisc labels, even if there are no disc subtitles 2020-05-12 13:14:23 -04:00
Deluan
4355f4fe2d Show disc subtitles (if available) 2020-05-12 12:57:53 -04:00
Deluan
0d9361734f Import and display disc subtitles 2020-05-12 12:57:53 -04:00
Deluan
7f75994906 go mod tidy 2020-05-11 10:54:35 -04:00
dependabot-preview[bot]
e9d594ebcf Bump github.com/Masterminds/squirrel from 1.3.0 to 1.4.0
Bumps [github.com/Masterminds/squirrel](https://github.com/Masterminds/squirrel) from 1.3.0 to 1.4.0.
- [Release notes](https://github.com/Masterminds/squirrel/releases)
- [Commits](https://github.com/Masterminds/squirrel/compare/v1.3.0...v1.4.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-11 09:46:22 -04:00
Deluan
0d1e2a92f6 Make MediaFolder ID int32 2020-05-09 22:29:02 -04:00
Deluan
1ed6d130b1 Fix getMusicFolders and getIndexes API compliance. Fix #286 2020-05-09 21:02:38 -04:00
Deluan
09267d2ffd Don't skip to the next song when there is an streaming error 2020-05-09 15:56:12 -04:00
Deluan
3a6639f820 Fix golangci-lint installation 2020-05-09 15:02:23 -04:00
Deluan Quintão
8b79b288eb Update de.json (POEditor.com) 2020-05-09 12:53:51 -04:00
Deluan Quintão
a0cde80c52 Update pt.json (POEditor.com) 2020-05-09 12:53:51 -04:00
Deluan Quintão
458636d2b8 Update fr.json (POEditor.com) 2020-05-09 12:53:51 -04:00
Deluan Quintão
8b30af561e Update cs.json (POEditor.com) 2020-05-09 12:53:51 -04:00
Deluan Quintão
1fb2b9bf1d Update tr.json (POEditor.com) (+1 squashed commit)
Squashed commits:
[9480cf8] Update tr.json (POEditor.com)
2020-05-09 12:53:51 -04:00
Deluan
5c9fdb064d Use official golangci-lint GH action 2020-05-08 17:13:57 -04:00
Deluan
70047fe20e Add songCount column to Artist table 2020-05-08 10:05:48 -04:00
Deluan
1c41582d79 Run pre-push hooks in parallel 2020-05-07 12:00:10 -04:00
Deluan
9a854f6cc4 Add golangci-lint to git pre-push hook 2020-05-07 11:57:07 -04:00
Deluan
06ab88415a Refactor album actions, simplify usage 2020-05-07 11:24:28 -04:00
Deluan
16f2b056ef Fix deprecated version input 2020-05-07 09:50:01 -04:00
Deluan
a761e6f2d0 go mod tidy 2020-05-07 09:31:10 -04:00
dependabot-preview[bot]
da7489cecd Bump github.com/onsi/gomega from 1.9.0 to 1.10.0
Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.9.0 to 1.10.0.
- [Release notes](https://github.com/onsi/gomega/releases)
- [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/gomega/compare/v1.9.0...v1.10.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-07 09:12:17 -04:00
Deluan
0472988645 Fix next track not working after adding to queue 2020-05-07 00:28:32 -04:00
Deluan
7e0881f0ec Play the remainder of the album when clicking on a album's song 2020-05-06 16:02:31 -04:00
Deluan
f8fb4c8f54 Make the borders of the AlbumSongs round 2020-05-06 08:44:25 -04:00
Deluan
ddcacbb6e5 Fix covers overflow in some resolutions 2020-05-06 08:44:10 -04:00
Deluan Quintão
9d7512e9ab Update README 2020-05-06 08:08:43 -04:00
Deluan Quintão
2e31b4d046 Update translations (#264)
* Update it.json (POEditor.com)

* Update de.json (POEditor.com)

* Update pt.json (POEditor.com)

* Add Czech translation

* Update fr.json (POEditor.com)

* Update nl.json (POEditor.com)

* Update cs.json (POEditor.com)

* Update English translation

* Update it.json (POEditor.com)
2020-05-05 18:32:22 -04:00
Deluan
c585ca7131 Add random as a valid sort option for song resource 2020-05-05 16:17:09 -04:00
Deluan
29e2ab1b4a Set default view to Album list 2020-05-05 15:44:05 -04:00
Deluan
8880294ee7 Change default album view mode to Grid 2020-05-05 15:34:31 -04:00
Deluan
a8d3466b0e Unselect album songs after clicking on bulk "Play Later" button 2020-05-05 15:08:30 -04:00
Deluan
0ee000a8a0 Resolve TODO (workaround is necessary) 2020-05-05 12:35:50 -04:00
Deluan
0833d87f94 Add "Play Later" action to AlbumContextMenu 2020-05-05 12:20:41 -04:00
Deluan
23836d7c3c Change addTrack action to addTracks, supporting multiple tracks to be added to the queue in one call 2020-05-05 12:07:50 -04:00
Deluan
5495451448 Use only one call to the server when adding songs to the queue
Also show a message when there's an error communication with the server
2020-05-05 11:19:41 -04:00
Deluan
bb01c8973f Fix lint error 2020-05-04 20:46:16 -04:00
Deluan
2f4d4c6e38 Add missing translation terms 2020-05-04 20:27:09 -04:00
Deluan
8d99c3ab92 Add validation tests to translations files 2020-05-04 19:54:10 -04:00
Deluan Quintão
8f66e87099 Install reflex in setup-dev target 2020-05-04 17:07:01 -04:00
Deluan
3e778e6007 Bump github.com/sirupsen/logrus from 1.5.0 to 1.6.0 2020-05-04 13:28:16 -04:00
Deluan
b2d6dd0254 Add pr-# tag to Docker image 2020-05-04 12:19:20 -04:00
Deluan
589c4cf225 Fix screenshot proportion 2020-05-03 19:25:39 -04:00
Deluan
4b70cc52d6 Reduce log level of config file being used 2020-05-03 14:09:31 -04:00
Deluan
cc1205c79d Simplify README.md 2020-05-03 00:04:33 -04:00
Deluan
cccd0235cf Add option to specify ConfigFile path 2020-05-02 23:17:38 -04:00
Deluan
17e51756ef Removed dependencies for ra language files 2020-05-02 22:30:55 -04:00
Deluan
13ce21843f go mod tidy 2020-05-02 18:00:18 -04:00
Deluan
151f43b95f Refactor i18n functions a bit 2020-05-02 17:44:24 -04:00
Deluan
055c77b38c Remove "default" from Dark theme name 2020-05-02 14:50:46 -04:00
Deluan
8dc2d7a5e0 Make context menu icon smaller 2020-05-02 14:50:15 -04:00
Deluan
a71d5b3954 Add remaining languages 2020-05-02 14:19:01 -04:00
Deluan
854a923fea Don't sort ReadAll translations, as it will be sorted in the UI 2020-05-02 14:19:01 -04:00
Deluan
496b467c1d Cater for differences when loading embedded Assets and in dev mode 2020-05-02 14:19:01 -04:00
Deluan
056d5e7111 Remove empty keys to allow English fallback 2020-05-02 14:19:01 -04:00
Deluan Quintão
e43c172d96 Update de.json (POEditor.com) 2020-05-02 14:19:01 -04:00
Deluan Quintão
0b56c3f026 Update pt.json (POEditor.com) 2020-05-02 14:19:01 -04:00
Deluan Quintão
5445d20ecd Update en.json (POEditor.com) 2020-05-02 14:19:01 -04:00
Deluan
2f7443e4bd Use English as fallback language 2020-05-02 14:19:01 -04:00
Deluan
41cf99541d Move translations to server 2020-05-02 14:19:01 -04:00
Deluan
1a9663d432 Move static to resources. Embed them at build time 2020-05-02 14:19:01 -04:00
Deluan
b7dcdedf41 More error handling 2020-05-02 14:19:01 -04:00
Deluan
bf8f9d2be8 Fix context menu icon color on Light theme 2020-05-01 12:08:32 -04:00
Deluan
6d20ca27f6 Add mobile album list view 2020-05-01 11:50:07 -04:00
Deluan
3bb573b45f Add AlbumContextMenu to AlbumListView 2020-05-01 11:27:09 -04:00
Deluan
9b2d91c0f2 Fix songs pagination param in AlbumContextMenu 2020-05-01 11:05:36 -04:00
Deluan
b002a69bf8 Fix language sorting 2020-05-01 10:48:28 -04:00
Deluan
e341df1e26 Rename Chinese translation file to zh 2020-05-01 10:43:49 -04:00
Deluan
35e8c1c407 Add all translation keys to English 2020-05-01 10:41:47 -04:00
Deluan
d1a88ed8d6 Remove duplicated translation key 2020-05-01 10:28:31 -04:00
Deluan
10a7dfeb15 Add SongContextMenu 2020-05-01 10:22:24 -04:00
Deluan
dbde5330bd Mark helper function as unexported 2020-05-01 09:17:21 -04:00
Deluan
9b817edd1a go mod tidy 2020-05-01 09:08:35 -04:00
dependabot-preview[bot]
261d73410a Bump github.com/Masterminds/squirrel from 1.2.0 to 1.3.0
Bumps [github.com/Masterminds/squirrel](https://github.com/Masterminds/squirrel) from 1.2.0 to 1.3.0.
- [Release notes](https://github.com/Masterminds/squirrel/releases)
- [Commits](https://github.com/Masterminds/squirrel/compare/v1.2.0...v1.3.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-01 09:01:49 -04:00
Deluan
555c78f536 Reduce flickering of album covers 2020-05-01 09:00:00 -04:00
Deluan
0270a9c924 Remove dangling create-react-app README 2020-04-30 15:19:55 -04:00
Deluan
a45e278cda Bum react-music-player version to 4.12.0 2020-04-30 14:18:05 -04:00
stncrn
bdbee7f541 Add setup step: download node dependencies 2020-04-30 09:54:15 -04:00
Deluan
b453ee6598 Fix color of album context menu when in Light mode.
Fix is to make it always white
2020-04-29 22:46:34 -04:00
Deluan
716de24f1e Localize translation config notice 2020-04-29 21:59:05 -04:00
Deluan
c816ca4525 Add config option to enable/disable Transcoding configuration 2020-04-29 21:59:05 -04:00
Srihari Chandana
eb7d2dcaa1 fixed compile errors 2020-04-29 21:51:44 -04:00
Srihari Chandana
e6d4cfba96 cleaned up logic 2020-04-29 21:51:44 -04:00
Srihari Chandana
2a5d2d70ba replaced GridButton with GridMenu 2020-04-29 21:51:44 -04:00
Srihari Chandana
e539ddceb9 fixed code to remove warnings 2020-04-29 21:51:44 -04:00
Srihari Chandana
00666da9c1 added grid play button 2020-04-29 21:51:44 -04:00
Deluan
7ad9c385b5 Fix typo 2020-04-29 17:38:03 -04:00
Sumner Evans
e65fb189ce Added back configs that I totally missed because I was tired 2020-04-29 17:18:44 -04:00
Sumner Evans
1afe409a79 Update the sample navidrome.service for use in Arch Linux 2020-04-29 17:18:44 -04:00
jvoisin
dbf9c8be7d An other batch of linters 2020-04-29 14:09:45 -04:00
jvoisin
26188e6d8a Enable a couple of linters 2020-04-29 09:03:07 -04:00
Brian Pierson
d6c70554b3 Fixing 50 shades of blue 2020-04-29 08:15:28 -04:00
Deluan
5990a4285f Replace goreman with node-foreman 2020-04-28 23:24:57 -04:00
Deluan
08e9ac63b1 Add cron workflow to remove old pipeline artifacts 2020-04-28 14:13:34 -04:00
Deluan
71a1f65be2 Bump @testing-library dependencies 2020-04-28 12:06:05 -04:00
Deluan
5862157a2c Move test file to fixtures folder 2020-04-28 11:59:47 -04:00
Deluan
d4f17f2b73 Fix username English translation (fix #231) 2020-04-27 23:23:03 -04:00
Deluan
ea1d534c29 Fix NavBar title translations 2020-04-27 23:22:17 -04:00
Deluan
069de0f9ea Add a try catch to display the record when DurationField fails 2020-04-27 22:46:40 -04:00
Deluan
e871c7daee Add links to documentation on how to contribute with themes and translations 2020-04-27 20:43:58 -04:00
dependabot-preview[bot]
320fe11a66 Bump prettier from 2.0.4 to 2.0.5 in /ui
Bumps [prettier](https://github.com/prettier/prettier) from 2.0.4 to 2.0.5.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/master/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/2.0.4...2.0.5)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-04-27 17:41:49 -04:00
Deluan
5fdc09a5b9 Fix pipeline (disable docker job when running on a PR from a forked repo) 2020-04-27 14:59:12 -04:00
Deluan
46f1b33812 Fix logging when first arg is a context.Context without a logger 2020-04-26 19:33:57 -04:00
Deluan
b44218fdcc Move the shuffleAlbum logic into an action 2020-04-26 19:15:52 -04:00
Deluan
4441ae1f0b Break up setup target, to avoid installing tools not required for building only 2020-04-26 16:10:40 -04:00
Deluan
1c3ee89ab4 Disable docker steps if secrets are not available 2020-04-26 15:52:21 -04:00
Deluan
ebc7964157 Fix formatting 2020-04-26 15:07:36 -04:00
Deluan
ad6c86d78a Check formatting in pipeline 2020-04-26 15:07:36 -04:00
Deluan
f3097496c6 Add golangci-lint to Go build step 2020-04-26 15:07:36 -04:00
Deluan
ddeefad501 Fix goimport and gosec warnings 2020-04-26 15:07:36 -04:00
Deluan
5cd453afeb Fix all errcheck warnings 2020-04-26 15:07:36 -04:00
Deluan
03c3c192ed Fixing static checks about passing nil context 2020-04-26 15:07:36 -04:00
Deluan
95790b9eff Remove unused code 2020-04-26 15:07:36 -04:00
ElleshaHackett
6bf7c751a1 Add Dutch language 2020-04-26 15:07:14 -04:00
Kevin Morssink
1019bb8258 Add Dutch language 2020-04-26 15:07:14 -04:00
Deluan
531155d016 Check if persistedState exists beforetrying to use it (fix #214) 2020-04-25 13:37:02 -04:00
Deluan
47311d16cf Trigger pipeline on new tags 2020-04-25 12:35:36 -04:00
Deluan
ef3466787d Fix the pipeline 2020-04-25 12:12:48 -04:00
Deluan
b7fd116bd8 Only triggers the pipeline on pushes to master and PRs 2020-04-25 12:06:05 -04:00
Deluan
34ad740e07 Enable French translation 2020-04-25 11:59:37 -04:00
Deluan
79454d7a92 Fix artist link contrast in light theme 2020-04-25 11:57:52 -04:00
Deluan
87cc397bc3 Add current playing track id to the Redux store 2020-04-25 11:57:52 -04:00
jvoisin
37602a2049 Bump the french traduction 2020-04-25 11:57:22 -04:00
Deluan
56ea380bb3 Add link to artist's albums on the album cover 2020-04-25 09:47:56 -04:00
Deluan
177ace1cee Turn off autoplay when reloading the play queue from the Redux store 2020-04-25 09:30:43 -04:00
Deluan
61e3fe21ff Add 'SNAPSHOT' to version when building locally, as this is not an "official" build 2020-04-25 09:27:22 -04:00
Deluan
8dcca76ec9 Fix various small sort issues 2020-04-24 17:37:28 -04:00
Deluan
1dd3a794f8 Reduce level of "invalid year" log message 2020-04-24 16:00:14 -04:00
Deluan
6c5dd245fe Parse TSO2 (seems that ffmpeg does not process this tag in some situations) 2020-04-24 15:02:20 -04:00
Deluan
3b3ad65612 Use order fields to sort by artist and album 2020-04-24 15:02:20 -04:00
Deluan
e6f798811d Generate Artist Index using the OrderArtistName 2020-04-24 15:02:20 -04:00
Deluan
371e8ab6ca Generate Order Fields based on sanitized version of original fields 2020-04-24 15:02:20 -04:00
Deluan
69c19e946c Add sort tags and use them in search 2020-04-24 15:02:20 -04:00
Deluan
d7edbf93f0 Make test more reliable
In some systems, it was detecting the `go.mod` file as an audio file, probably because of the system's mime-type configuration
2020-04-24 11:05:17 -04:00
Deluan
fb4d920fba Small change to trigger the pipeline 2020-04-23 22:29:33 -04:00
Deluan
5a072fbd10 Follow symlinks to directories when scanning 2020-04-23 20:31:44 -04:00
Deluan
79c9d8f4f4 Parameterize docker image name 2020-04-23 19:31:24 -04:00
Deluan
871bf5a70a Rename pipeline 2020-04-23 19:31:24 -04:00
Deluan
e4af235ce9 Move chmod to copy image, make the final image smaller 2020-04-23 19:31:24 -04:00
Deluan
00384a60f3 Unify GH actions 2020-04-23 19:31:24 -04:00
Deluan
f7b3ff4b34 Build and release docker images 2020-04-23 19:31:24 -04:00
Deluan
eaa48306fc Make Dockerfile platform independent
Thanks @0xERROR: https://github.com/deluan/navidrome/issues/92#issuecomment-614630429
2020-04-23 19:31:24 -04:00
Deluan
f5572b8447 Fix git tag detection 2020-04-23 19:31:24 -04:00
Deluan
a756751cc6 Build binary artifacts 2020-04-23 19:31:24 -04:00
Deluan
b8a3af090d Add cache to build workflow 2020-04-23 19:31:24 -04:00
Deluan
d534cb96a9 Replace math.Max with utils.MaxInt 2020-04-21 08:41:04 -04:00
Dimitri Herzog
f1e1d3bc07 request throttling only for media group api 2020-04-21 08:39:14 -04:00
Deluan
694be54428 Replace math.Max with utils.MaxInt 2020-04-20 12:17:01 -04:00
Deluan
76531fb1cd Remove old pre-commit script (in favour of lefthook) 2020-04-20 11:57:38 -04:00
Dimitri Herzog
716f4c5cf7 configuration for request throttling 2020-04-20 11:51:00 -04:00
jvoisin
ba2d4b6859 Add a .git-blame-ignore-revs file 2020-04-20 10:41:41 -04:00
Deluan
2ec5e47328 Set version correctly when building locally 2020-04-20 09:47:44 -04:00
Deluan
b3f70538a9 Upgrade Prettier to 2.0.4. Reformatted all JS files 2020-04-20 09:09:29 -04:00
Deluan
de115ff466 Bump Testing Library and moved it to devDependencies 2020-04-20 09:02:08 -04:00
Deluan
129f02b36b Bump ReactAdmin to 3.4.2 2020-04-20 08:50:21 -04:00
Deluan
1a8d219197 Remove generated comments from migrations 2020-04-19 23:29:08 -04:00
Deluan
80c8d85cb9 Fine tune search functionality 2020-04-19 23:29:07 -04:00
Deluan
db02f5f07f go mod tidy 2020-04-19 14:51:16 -04:00
Deluan
579294b0f1 Make Players and Transcodings view mobile-friendly 2020-04-19 13:54:51 -04:00
Deluan
f83d0d471d Fix getRandomSongs filters 2020-04-19 13:37:25 -04:00
Deluan Quintão
3b7d7bdb04 Disable French translation 2020-04-18 14:24:27 -04:00
jvoisin
05958f5195 Add French localization 2020-04-18 14:24:27 -04:00
Deluan
6cf4b81de9 Fix year range when querying by year 2020-04-18 14:05:44 -04:00
Deluan
689449df9e Force reindex to fix album by year searches 2020-04-18 11:08:54 -04:00
Deluan
dae938de6f Don't try to install Jamstash as part of initial setup 2020-04-17 22:11:58 -04:00
Deluan
f6617ff77d Add Chinese Simplified translation 2020-04-17 21:54:41 -04:00
Deluan
defdc2ea6b Bump Subsonic API to 1.10.2 2020-04-17 21:44:34 -04:00
Deluan
1fd6571a87 Refactored getSongsByGenre 2020-04-17 21:44:34 -04:00
Deluan
4c0250f9f8 Add fromYear/toYear params to getRandomSongs 2020-04-17 21:44:34 -04:00
Deluan
0e1735e7a9 Add getSongsByGenre endpoint 2020-04-17 21:44:34 -04:00
Deluan
a698e434fd Refactor list_generator to use new filters 2020-04-17 21:44:34 -04:00
Deluan
95f658336c Implement byYear and byGenre AlbumLists 2020-04-17 21:44:34 -04:00
Deluan
69dc4d97b3 Always fill album's min_year if max_year is filled 2020-04-17 21:44:34 -04:00
jvoisin
4aeb63c16e Add a couple of patterns to .gitignore 2020-04-17 10:06:35 -04:00
dependabot-preview[bot]
e5efadf99e Bump github.com/go-chi/chi from 4.1.0+incompatible to 4.1.1+incompatible
Bumps [github.com/go-chi/chi](https://github.com/go-chi/chi) from 4.1.0+incompatible to 4.1.1+incompatible.
- [Release notes](https://github.com/go-chi/chi/releases)
- [Changelog](https://github.com/go-chi/chi/blob/master/CHANGELOG.md)
- [Commits](https://github.com/go-chi/chi/compare/v4.1.0...v4.1.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-04-17 08:09:06 -04:00
AlphaJack
d117d5794d Add Italian localization 2020-04-17 08:05:30 -04:00
Deluan
d09a2182e0 Lax Node version (only matches major version 13) 2020-04-17 00:21:42 -04:00
Deluan
b8b09820b1 Use deluan/ci-goreleaser 2020-04-16 17:44:12 -04:00
Deluan
2cfd7babb3 Add more Portuguese translations 2020-04-16 13:02:39 -04:00
Deluan
161a9b340c Add more Portuguese translations 2020-04-16 12:53:46 -04:00
Deluan
605253446a Fix AlbumLink label in Songs view 2020-04-16 10:26:24 -04:00
Deluan
f8d9b1508e Add prettier npm script 2020-04-15 22:11:23 -04:00
Deluan
3c4de3c8b5 Move language merge logic to i18n/index
This simplifies implementations one new languages
2020-04-15 22:11:23 -04:00
Deluan
a6c9bf1b15 Persist language selection to localStorage 2020-04-15 22:11:23 -04:00
Deluan
bf6ec67528 Add Language Selector to Personal settings 2020-04-15 22:11:23 -04:00
Deluan
289ba68824 Add Portuguese translation (incomplete) 2020-04-15 22:11:23 -04:00
Deluan
2dfe01963a Build binary for Linux MUSL (ex: Alpine). Fix #142 2020-04-15 08:49:30 -04:00
Deluan
5ed1d5c19f Upgrade github.com/djherbis/fscache to v0.10.1, tentatively fix #177 2020-04-15 08:45:10 -04:00
Deluan
db4479e720 Allow cache image to be disabled (workaround for #177) 2020-04-14 19:28:54 -04:00
Deluan
66275d3b94 Make song details table dense 2020-04-14 17:09:47 -04:00
Deluan
57f2c3f823 Better layout for Song Details 2020-04-14 16:21:59 -04:00
Deluan
afba4c9915 Add size and play count/date to Song Details 2020-04-14 15:23:11 -04:00
Deluan
f0d18d2cb3 Add Song Details to Album view 2020-04-14 14:59:16 -04:00
Deluan
da45bcf448 Make player theme configurable from Navidrome's theme 2020-04-14 11:54:49 -04:00
Deluan
3a54246b15 Change default sort for albums view to alphabetically (list) or most recent (grid) 2020-04-14 09:26:59 -04:00
Deluan
2b06f20f41 Close the sidebar menu when clicking "Personal" in mobile screens 2020-04-14 08:52:26 -04:00
Deluan
88f44b2e77 Upgrade React Player to 4.11.2, fix to MediaSession "close" action 2020-04-14 01:42:07 -04:00
Deluan
4dff067e0b Upgrade React Player to 4.11.1, enabled MediaSession 2020-04-13 14:24:50 -04:00
Deluan
d81bf8a518 Update github.com/go-chi/cors 2020-04-13 10:50:18 -04:00
Deluan
adfaf39489 Mark more endpoints as "gone" (won't be implemented) 2020-04-12 23:12:28 -04:00
Deluan
f6a15905d7 Move Album View toolbar to left 2020-04-12 20:43:51 -04:00
jvoisin
52b8c5f151 Correctly handle error in migration 2020-04-12 14:58:08 -04:00
Deluan
c4eab5db86 Update dhowden/tag library, to fix extracting images from Ogg files
see https://github.com/dhowden/tag/issues/64
2020-04-11 23:40:35 -04:00
Deluan
4b1c76e307 Keep the order of the playlist when adding new songs. Also allow adding a song more than once 2020-04-11 21:24:15 -04:00
Deluan
e476a5f6f1 Make fields songCount, duration, created and changed mandatory in playlists responses (fixes #164) 2020-04-11 19:15:15 -04:00
Deluan
9fb4f5ef52 Removed Playlist.GetWithTracks, not needed anymore 2020-04-11 19:05:51 -04:00
Deluan
e232c5c561 Add created and changed fields to playlists responses 2020-04-11 18:58:43 -04:00
Deluan
803a5776ae Update link to Subsonic API compatibility doc 2020-04-11 13:19:58 -04:00
Deluan
a6dfcafdab Update themes doc, link to documentation site 2020-04-11 13:13:53 -04:00
Deluan
8f2c7b7913 go mod tidy 2020-04-11 13:10:54 -04:00
jvoisin
2ab647efe1 Add a test 2020-04-11 13:08:21 -04:00
jvoisin
04eb421186 Refactor a bit how ffmpeg is used to get metadata
- createProbeCommand returns a []string instead of (string, string[])
- Simplify the loop of createProbeCommand
2020-04-11 13:08:21 -04:00
Deluan
6a3a66975c Update dhowden/tag library, to fix extracting images from some id3v4 tags
See https://github.com/dhowden/tag/issues/62
2020-04-10 23:42:06 -04:00
jvoisin
1ef4fa970f Simplify a bit ffmpeg's transcoder
- Remove the useless "format" parameter
- createTranscodeCommand now returns a list of string, instead of (string, string[])
2020-04-10 13:00:29 -04:00
jvoisin
b34523e196 Warn if ffmpeg can't be found 2020-04-10 10:56:58 -04:00
Deluan
09985453aa Show a Datagrid placeholder while loading 2020-04-09 22:38:40 -04:00
jvoisin
159a6e1cad Simplify the openrc unit 2020-04-09 19:21:23 -04:00
Deluan
b429949dd9 Keep optimistic rendering when changing the sort order for the current album 2020-04-09 18:53:44 -04:00
Deluan
b9f601dfb4 Remove unused import 2020-04-09 18:31:37 -04:00
Deluan
5b488b72b1 Add a custom AlbumSongs list component, to disable the optimistic rendering (should fix #158) 2020-04-09 18:28:47 -04:00
Deluan
03044bcb68 Ignore data folder when watching for changes in folders (when in dev mode) 2020-04-09 16:48:04 -04:00
Deluan
7bc3dace4c Revert "Improve ffmpeg's error diagnostic"
This reverts commit 4fc88f23
2020-04-09 14:26:42 -04:00
Deluan
c2ec142ce3 More tests 2020-04-09 13:36:05 -04:00
Deluan
2d39a6df8d Remove duplicated fscache creation 2020-04-09 13:15:01 -04:00
Deluan
5265d0234f Fix tests for Cover service 2020-04-09 12:13:54 -04:00
jvoisin
4fc88f23e9 Improve ffmpeg's error diagnostic
This should close #155
2020-04-09 10:40:16 -04:00
Deluan
5412bb2dc8 Fine tune album grid for mobile view 2020-04-09 09:53:53 -04:00
Deluan
b661d52477 Force full scan to enable search by tracks' artists in albums 2020-04-09 00:24:26 -04:00
Deluan
43ce81af67 Add all individual artists from album in searchable full text field. Should fix #94 2020-04-08 23:54:54 -04:00
Deluan
b8d1185f7f Remove duplicated words and extra spaces from full text searchable fields 2020-04-08 23:29:28 -04:00
Deluan
0fa8290ed3 Don't transcode if original format/bitrate is the same as the selected ones 2020-04-08 19:10:55 -04:00
Deluan
519e3f014d Re-stage files after formatting 2020-04-08 13:23:39 -04:00
Deluan
d38f8544d5 Remove unused localStorage config 2020-04-08 13:20:02 -04:00
Deluan
089a92157f Pass version to UI through AppConfig, instead of login payload.master
This makes the version info updated with a browser refresh (no need to logout and login again)
2020-04-08 11:00:30 -04:00
Deluan
db246900a6 Introduce a new configuration to select the login background image URL 2020-04-08 09:07:15 -04:00
Deluan
a0f389fc3e Consolidate UI configuration in one place, allowing it to be overridden from the server 2020-04-08 09:07:15 -04:00
Deluan
d0188db4f9 Fine tune album grid 2020-04-07 21:25:06 -04:00
Deluan
f537984bbf Use trackId instead of simply id, as it seems to conflict with internal id generated by the player. fixes #153 2020-04-07 11:55:45 -04:00
Deluan
7e6c0e3894 Less noisy logs for scrobble 2020-04-06 19:42:35 -04:00
Deluan
b930c7253a Fix tests in pipeline 2020-04-06 17:01:48 -04:00
Deluan
c1afe70d98 Fix: also pass the custom authorization header in all requests 2020-04-06 16:23:47 -04:00
Deluan
3f9ddb915e Use a custom authorization header, to avoid conflicts with proxies using basic auth (fixes #146) 2020-04-06 16:03:20 -04:00
Deluan
c3edc7f449 Add test for ServeIndex 2020-04-06 15:37:15 -04:00
Deluan
9b272c8021 Small log tweak 2020-04-06 14:02:50 -04:00
Deluan
6d1221164b Download and install latest Jamstash when calling make Jamstash-master 2020-04-06 00:40:51 -04:00
Deluan
647132625c Logs new stream sessions 2020-04-06 00:26:51 -04:00
Deluan
a17a98a75f Log API requests and responses at Debug level 2020-04-05 23:57:04 -04:00
Deluan
59707b3a8f Detect embedded art in ogg containers 2020-04-05 23:41:10 -04:00
Deluan
fa378ab4e4 Add tracing log to Cover service 2020-04-05 22:48:07 -04:00
Deluan
05ffb1acad Cache cover arts. closes #19 2020-04-05 22:02:06 -04:00
Deluan
a1ba5c59b2 Returns default cover on any error (not found, encoding, or unknown)
Only returns error if it cannot read the default image
2020-04-05 22:02:06 -04:00
Deluan
1bc68c20fc Create and configure image cache 2020-04-05 22:02:06 -04:00
Deluan
d308e7ca46 Fix typo 2020-04-05 17:49:14 -04:00
jvoisin
2b5433dc6e Add an openrc unit file 2020-04-05 13:07:00 -04:00
Deluan
86a23f9b14 Add more indexes to MediaFile table 2020-04-04 21:56:22 -04:00
Deluan
0ba5840a65 Don't set a playerId cookie it cannot register the player 2020-04-04 20:26:36 -04:00
Deluan
93646b964e More logging tests 2020-04-04 19:11:21 -04:00
Deluan
13be8d297c Converted last GoConvey tests to Ginkgo
Removed GoConvey dependency
2020-04-04 18:54:12 -04:00
Deluan
a4b97121ab Changes when pipelines are triggered:
- Build now on new Pull Requests
- Release only on new pushed tags
2020-04-04 16:39:43 -04:00
Deluan
660f9c205b Rename dist target to snapshot 2020-04-04 14:36:23 -04:00
Deluan
28852ce7d7 go mod tidy 2020-04-03 22:57:59 -04:00
Deluan
656ca1f3b5 Fix colour of album actions 2020-04-03 22:35:55 -04:00
Deluan
b8f7715a74 Fix ReactAdmin console warnings 2020-04-03 21:03:34 -04:00
Deluan
096ed396c8 Add link to all artist's albums from an album 2020-04-03 20:51:15 -04:00
Deluan
3b6d0b3d15 Add a catchall route to redirect everything to app/index.html 2020-04-03 19:45:35 -04:00
Deluan
75cd21da1f Add BaseURL configuration (fixes #103) 2020-04-03 19:05:38 -04:00
Deluan
b8eb22d162 Add git hooks on check_env 2020-04-03 16:00:17 -04:00
Deluan
9b461735f4 Add comments to createXxxxCommand functions to clarify about the filepaths arguments being absolute paths 2020-04-03 14:49:35 -04:00
Deluan
63bf49b3c4 Add lefthook for handling git hooks 2020-04-03 14:48:14 -04:00
Deluan
559848299c Fix default mp3 encoding ffmpeg command 2020-04-03 00:26:41 -04:00
Deluan
8510273216 Send estimated content length if requested 2020-04-03 00:24:40 -04:00
Deluan
2392060bc1 Don't try to transcode a file if the requested format is the same and the client is not requesting to downsample 2020-04-02 22:17:52 -04:00
Deluan
2d7998de59 Return cover from album even if client does not prefix the id with al-. Fixes #46 2020-04-02 22:03:27 -04:00
Deluan
40638688b2 Remove React warnings by omit properties not used downstream 2020-04-02 19:58:34 -04:00
Deluan
ea22b2fc6d Bump react-admin to 3.3.3 2020-04-02 19:47:10 -04:00
Deluan
1182218787 Upgrade Node to 13.12 2020-04-02 19:41:10 -04:00
Deluan
14f7c5610e Bump @testing-library/jest-dom, @testing-library/user-event and react-dom 2020-04-02 19:31:49 -04:00
Deluan
27579b99a3 Removed album list selection, for now 2020-04-02 19:20:39 -04:00
Deluan
c58021e645 Make Personal settings form more consistent with the rest of the App 2020-04-02 18:46:09 -04:00
Deluan
1810cc7ac7 Simplify album lists tabs handling 2020-04-02 18:18:52 -04:00
Deluan
86f73eecca Only add padding to layout if the player is visible 2020-04-02 18:09:02 -04:00
Deluan
3d6ce8a77f Skip calling ffmpeg if there are no files to probe 2020-04-02 17:38:20 -04:00
Deluan
670be29d7b Revert "Pause the player with <space>"
This reverts commit 6e6cfdd0
2020-04-02 16:52:46 -04:00
dependabot-preview[bot]
2b3e506583 build(deps): bump github.com/go-chi/chi
Bumps [github.com/go-chi/chi](https://github.com/go-chi/chi) from 4.0.4+incompatible to 4.1.0+incompatible.
- [Release notes](https://github.com/go-chi/chi/releases)
- [Changelog](https://github.com/go-chi/chi/blob/master/CHANGELOG.md)
- [Commits](https://github.com/go-chi/chi/compare/v4.0.4...v4.1.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-04-02 08:53:28 -04:00
jvoisin
6e6cfdd02b Pause the player with <space> 2020-04-01 17:14:09 -04:00
Deeparghya Dutta Barua
a18093e255 Fix systemd unit to allow FFmpeg execution 2020-04-01 15:15:59 -04:00
Deluan
a35636999d feat: fine tune album art image size. better, but still not ideal 2020-04-01 09:09:51 -04:00
Deluan
13a3d38e4f fix: Personal view title and menu tooltip 2020-03-31 21:40:06 -04:00
Deluan
9f00fb0f05 feat: move Configuration menu to Personal settings 2020-03-31 21:28:50 -04:00
Deluan
6cddcd6f0d docs: update README 2020-03-31 20:42:59 -04:00
Deluan Quintão
c6d1cfeceb docs: update theme's README 2020-03-31 20:07:11 -04:00
Deluan
de43c27b3c docs: basic documentation on creating themes.
#129
2020-03-31 19:54:38 -04:00
Deluan
747b5ea25e refactor: rename theme name attribute to themeName 2020-03-31 19:36:45 -04:00
Deluan
dd2e98fca2 feat: make theme select input longer 2020-03-31 18:43:54 -04:00
Deluan
eb621be646 feat: load themes dynamically 2020-03-31 18:31:14 -04:00
Deluan
d223a4f4db docs: mentions our Subreddit in the README 2020-03-31 15:11:33 -04:00
Deluan
7aa182e33d fix: add padding at the bottom of the layout, to accommodate the audio player (relates to #132) 2020-03-31 14:52:54 -04:00
Deluan
7fec503b72 feat: persist the queue in the localStorage 2020-03-31 14:34:08 -04:00
Deluan
083a11a563 feat: store state in localStorage 2020-03-31 14:07:33 -04:00
Deluan
944f3695c4 fix: disable click on version menu item 2020-03-31 13:04:04 -04:00
Deluan
dfc8691262 fix: add "Version" message to translations 2020-03-31 11:17:11 -04:00
Deluan
395b598bb1 fix: don't show tooltips in profile menu items 2020-03-31 11:07:45 -04:00
Deluan
d04b434d96 fix: profile menu items colors 2020-03-31 10:49:47 -04:00
Deluan
f041503a85 feat: simple theme selector. only works with hardcoded light and dark for now 2020-03-31 09:35:44 -04:00
Deluan
500207f7b8 refactor: extract themes to their own folder 2020-03-31 09:05:46 -04:00
Deluan
1e0a79ebb7 fix: "Recent" should sort by play_date, not starred_at 2020-03-30 19:34:44 -04:00
Deluan
301fa2a957 fix: sort by album in songs view 2020-03-30 19:34:00 -04:00
Deluan
46f4f63212 feat: initial implementation of album lists 2020-03-29 00:01:08 -04:00
Deluan
fec8b5f731 feat: add playcounts to album and songs
(fix year in song list)
2020-03-28 20:38:41 -04:00
Deluan
777231ea79 feat: expose album, song and artist annotations in the RESTful API 2020-03-28 19:22:55 -04:00
Deluan
0e36ed35a3 fix: typo 2020-03-28 18:50:18 -04:00
Deluan
f1af646cee feat: option to display albums as a grid 2020-03-28 16:25:55 -04:00
Deluan
fc0621646b feat: add link to album from Songs view 2020-03-28 00:34:09 -04:00
Deluan
575800dcff docs: add badge with link to subreddit 2020-03-27 21:51:24 -04:00
Deluan
0ca849a61a feat: show year range in album view and match ranges in year filter. #118 2020-03-27 21:11:06 -04:00
Deluan
53e8a92fed feat: rename year to max_year and add min_year to album. #118 2020-03-27 21:11:06 -04:00
Deluan
fc650cd127 chore: upgrade to Node 13.11 2020-03-27 19:23:52 -04:00
Deluan
b03519b09c fix: configured transcodings not appearing in players view 2020-03-27 19:12:11 -04:00
Deluan
39b9f818be feat: use ND_PORT env var in health check 2020-03-26 15:26:40 -04:00
Deluan
7febe05ed5 feat: add health check to docker image 2020-03-26 15:15:40 -04:00
Deluan
2c42e4e12e feat: add icons for playlists 2020-03-26 12:33:30 -04:00
Deluan
dcb3b3b5d1 fix: various album_artists <-> artists mismatches 2020-03-26 09:08:53 -04:00
Deluan
5331732236 fix: remove sql injection 2020-03-25 20:40:18 -04:00
Deluan
dc973ae670 refactor: remove unused code 2020-03-25 20:40:18 -04:00
Deluan
100db2bcfd feat: add artist filter to album view 2020-03-25 20:40:18 -04:00
dependabot-preview[bot]
c84a58ff7d build(deps): bump github.com/go-chi/chi
Bumps [github.com/go-chi/chi](https://github.com/go-chi/chi) from 4.0.3+incompatible to 4.0.4+incompatible.
- [Release notes](https://github.com/go-chi/chi/releases)
- [Changelog](https://github.com/go-chi/chi/blob/master/CHANGELOG.md)
- [Commits](https://github.com/go-chi/chi/compare/v4.0.3...v4.0.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-03-25 18:12:33 -04:00
Deluan
2d7fda1b2f docs: add default config vars to docker-compose.yml example 2020-03-24 12:34:31 -04:00
Deluan
3cba5f70fd chore: add tests for all utils, removed unused functions 2020-03-24 11:59:10 -04:00
Deluan
b4c7cac964 refactor: moved magic strings to consts 2020-03-24 11:59:10 -04:00
dependabot-preview[bot]
5ef80d2490 build(deps): bump github.com/sirupsen/logrus from 1.4.2 to 1.5.0
Bumps [github.com/sirupsen/logrus](https://github.com/sirupsen/logrus) from 1.4.2 to 1.5.0.
- [Release notes](https://github.com/sirupsen/logrus/releases)
- [Changelog](https://github.com/sirupsen/logrus/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sirupsen/logrus/compare/v1.4.2...v1.5.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-03-24 09:09:12 -04:00
dependabot-preview[bot]
3b798cf943 build(deps): bump react-scripts from 3.4.0 to 3.4.1 in /ui
Bumps [react-scripts](https://github.com/facebook/create-react-app/tree/HEAD/packages/react-scripts) from 3.4.0 to 3.4.1.
- [Release notes](https://github.com/facebook/create-react-app/releases)
- [Changelog](https://github.com/facebook/create-react-app/blob/master/CHANGELOG.md)
- [Commits](https://github.com/facebook/create-react-app/commits/react-scripts@3.4.1/packages/react-scripts)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-03-23 08:49:40 -04:00
dependabot-preview[bot]
50b7756159 build(deps): bump react from 16.13.0 to 16.13.1 in /ui
Bumps [react](https://github.com/facebook/react/tree/HEAD/packages/react) from 16.13.0 to 16.13.1.
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/facebook/react/blob/master/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/commits/v16.13.1/packages/react)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-03-23 08:48:48 -04:00
Deluan
15606770ca chore: removed non-working config flag 2020-03-22 01:13:55 -04:00
Deluan
f403a8da34 feat: add version to index.html description meta tag 2020-03-22 01:04:10 -04:00
Deluan
20075ae68d refactor: extracted restful helpers into their own composable struct 2020-03-21 20:00:46 -04:00
Deluan
91a743623a feat: always show artist name in Album view 2020-03-21 19:15:39 -04:00
Deluan
e23a290812 fix: logging of scanner startup 2020-03-21 14:20:22 -04:00
Deluan
dee68559ab docs: uses less space for client list 2020-03-21 14:11:57 -04:00
Deluan
9f42e330b4 fix: change web requests log level to debug 2020-03-21 13:03:04 -04:00
jvoisin
ad63b8b1b4 Add a systemd startup unit 2020-03-21 12:47:05 -04:00
Deluan
0d8a2b310f fix: the default session timeout must be 30 minutes, not seconds! 2020-03-21 12:17:20 -04:00
Deluan
3977575563 build: add a simple build as default target, trying to make LGTM work 2020-03-20 12:21:41 -04:00
Deluan
47244cb770 refactor: remove unused static file 2020-03-20 12:00:14 -04:00
Deluan
57aaf5a26b refactor: remove unused property 2020-03-20 00:30:16 -04:00
Deluan
352d686d94 chore: upgrade react-admin to 3.3.1 2020-03-20 00:23:04 -04:00
Deluan
f6e448c1ba refactor: removed unused code, unnecessary typecasts and fixed small warnings 2020-03-20 00:07:36 -04:00
Deluan
270b0ae74e feat: add "Compilation" filter to albums 2020-03-19 23:25:40 -04:00
Deluan
8401d85f78 feat: search in WebUI now is more flexible, searching in all relevant fields in the current view 2020-03-19 22:26:18 -04:00
Deluan
32fbf2e9eb refactor: drop search table, integrated full_text into main tables 2020-03-19 21:44:48 -04:00
Deluan
8cdd4e317d feat: allow restful filter customization per field 2020-03-19 21:09:57 -04:00
Deluan
97d95ea794 fix: group compilations together in the restful API. fix #93 2020-03-19 15:02:11 -04:00
Deluan
cbbebb3264 fix: version position under banner 2020-03-18 23:21:01 -04:00
Deluan
8b108905a3 feat: use Navidrome's icon in getAvatar 2020-03-18 22:46:47 -04:00
Deluan
5b40ec400e build: go mod tidy 2020-03-18 21:35:15 -04:00
Deluan
29e661e1fe docs: update README 2020-03-18 21:23:45 -04:00
Deluan
b466ec75a4 build: always add latest tag to version 2020-03-18 21:05:17 -04:00
Deluan
c8cd755451 feat: use human readable sizes in cache size configuration 2020-03-18 20:39:10 -04:00
Deluan
faac303eff feat: allow session timeout to be configurable. closes #101 2020-03-18 20:16:18 -04:00
Deluan
ced87be57b fix: when searching player by id, create new player if client name does not match the one found 2020-03-17 19:10:09 -04:00
Deluan
811703ab60 fix: create default transcodings on existing installations 2020-03-17 16:49:37 -04:00
Deluan
bc1f767123 docs: Update README 2020-03-17 15:22:37 -04:00
Deluan
7055dc514b docs: update basic transcoding info 2020-03-17 15:20:35 -04:00
Deluan
e02f3d3ec9 refactor: clean up unused config options 2020-03-17 15:20:35 -04:00
Deluan
68a49befc8 feat: allow regular users to change their players' configuration 2020-03-17 15:20:35 -04:00
Deluan
c8b0d2bfae feat: select correct transcoding for streaming 2020-03-17 15:20:35 -04:00
Deluan
39993810b3 feat: add transcodedSuffix to Subsonic API responses 2020-03-17 15:20:35 -04:00
Deluan
45180115a6 feat: player CRUD 2020-03-17 15:20:35 -04:00
Deluan
353c48d8d8 refactor: rename player to audioplayer 2020-03-17 15:20:35 -04:00
Deluan
da36941252 feat: better getPlayer middleware setup 2020-03-17 15:20:35 -04:00
Deluan
8ec78900c5 feat: transcoding and player datastores and configuration 2020-03-17 15:20:35 -04:00
dependabot-preview[bot]
a0e0fbad58 build(deps): bump @testing-library/react from 9.5.0 to 10.0.1 in /ui
Bumps [@testing-library/react](https://github.com/testing-library/react-testing-library) from 9.5.0 to 10.0.1.
- [Release notes](https://github.com/testing-library/react-testing-library/releases)
- [Changelog](https://github.com/testing-library/react-testing-library/blob/master/CHANGELOG.md)
- [Commits](https://github.com/testing-library/react-testing-library/compare/v9.5.0...v10.0.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-03-16 10:06:23 -04:00
dependabot-preview[bot]
75e7ba8b1e build(deps): bump github.com/go-chi/cors from 1.0.0 to 1.0.1
Bumps [github.com/go-chi/cors](https://github.com/go-chi/cors) from 1.0.0 to 1.0.1.
- [Release notes](https://github.com/go-chi/cors/releases)
- [Commits](https://github.com/go-chi/cors/compare/v1.0.0...v1.0.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-03-16 10:04:16 -04:00
Deluan
74c30b5a66 docs: add list of tested clients 2020-03-15 13:26:48 -04:00
Deluan Quintão
e67bdbbc32 docs: add link to transcoding issue 2020-03-15 13:09:43 -04:00
Deluan
9554c8f783 build: rename generated archives 2020-03-14 21:09:39 -04:00
Deluan
e36a42f356 build: generate binaries for Linux armv6, armv7 and arm68 (v8) (fixes #92) 2020-03-14 21:09:39 -04:00
dependabot-preview[bot]
9d1960232c build(deps): [security] bump acorn from 5.7.3 to 5.7.4 in /ui
Bumps [acorn](https://github.com/acornjs/acorn) from 5.7.3 to 5.7.4. **This update includes a security fix.**
- [Release notes](https://github.com/acornjs/acorn/releases)
- [Commits](https://github.com/acornjs/acorn/compare/5.7.3...5.7.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-03-13 18:01:31 -04:00
Deluan
d3547544bf feat: new WebUI icon 2020-03-11 20:18:22 -04:00
Deluan
9cb42606ba fix: force full rescan to enable search by album artist 2020-03-10 17:23:25 -04:00
dependabot-preview[bot]
7772afce1c build(deps): bump @testing-library/react from 9.4.1 to 9.5.0 in /ui
Bumps [@testing-library/react](https://github.com/testing-library/react-testing-library) from 9.4.1 to 9.5.0.
- [Release notes](https://github.com/testing-library/react-testing-library/releases)
- [Changelog](https://github.com/testing-library/react-testing-library/blob/master/CHANGELOG.md)
- [Commits](https://github.com/testing-library/react-testing-library/compare/v9.4.1...v9.5.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-03-10 17:11:46 -04:00
Deluan
10e76257c6 fix: increase contrast in WebUI's dark theme 2020-03-08 16:12:12 -04:00
Deluan
9235ab6414 fix: index albumArtist as part of the album searchable fields 2020-03-07 13:10:20 -05:00
Deluan
59356f0029 refactor: removed indirect call introduced by intellij's refactor 2020-03-06 16:28:20 -05:00
Deluan
9ae14015a1 build: get Go and Node versions from go.mod and .nvmrc respectively 2020-03-06 11:50:39 -05:00
Deluan
0b131e91c1 chore: upgrade to NodeJS 13.10 2020-03-06 10:57:00 -05:00
Deluan
77b12eafde chore: upgrade react-jinke-music-player 2020-03-05 08:57:40 -05:00
Deluan
050778460d fix: missing id in queue items was preventing scrobble to work properly 2020-03-04 10:30:58 -05:00
Deluan
28bc9c1d4f fix: AlbumShow was adding previous played tracks when trying to shuffle the album 2020-03-02 14:51:52 -05:00
Deluan
5e7aaa667b fix: missing id in queue items was preventing scrobble to work properly 2020-03-02 14:20:57 -05:00
Deluan
1afc495920 chore: upgrade react, react-dom and react-redux 2020-03-02 13:06:27 -05:00
Deluan
cf7d877714 chore: upgrade @testing-library/user-event 2020-03-02 12:04:58 -05:00
Deluan
81831da67a chore: upgrade react-admin 2020-03-02 11:58:23 -05:00
Deluan
fcd2fcae67 chore: upgrade @testing-library, react-scripts 2020-03-02 11:52:06 -05:00
Deluan
1c33b0aea8 docs: update API compatibility chart 2020-03-02 09:48:46 -05:00
Deluan
fc06163b5a refactor: remove superfluous (and untested) code 2020-03-02 09:37:47 -05:00
Deluan
72f0a6fb66 chore: removed unused (video) mime types 2020-03-02 00:16:15 -05:00
Deluan
6f5a322927 fix: login must be case-insensitive 2020-03-01 15:45:41 -05:00
Deluan
a7f8e4ee2b fix: only set created_at when adding data to DB 2020-02-28 18:43:22 -05:00
Deluan
0850872b0f fix: ormer.Driver() is not available when creating orms with NewOrmWithDB() 2020-02-28 16:09:27 -05:00
Deluan
1d886156d5 feat: better SQLite3 configuration, to avoid DB contention 2020-02-28 15:06:31 -05:00
Deluan
faa2a978c0 refactor: use only one DB instance for the whole application 2020-02-28 15:06:31 -05:00
Deluan
38faffa907 feat: notice function to notify (in logs) about important changes in migrations 2020-02-28 14:00:41 -05:00
Deluan
65a792be3a fix: handle nil pointer dereference 2020-02-28 11:02:38 -05:00
Deluan
876354e58e feat: MaxTranscodingCacheSize is now specified in MB 2020-02-26 14:08:14 -05:00
Deluan
14b33bc34d fix: there are no docker images available for node 13.9 2020-02-26 12:00:00 -05:00
Deluan
9044aa8740 chore: upgrade NodeJS to 13.9.0 2020-02-26 09:52:25 -05:00
Deluan
07ac14f810 chore: upgrade Go to 1.14 2020-02-26 09:37:48 -05:00
Deluan
0370f0a3ea refactor: rename ffmpeg to transcoder 2020-02-25 10:32:34 -05:00
Deluan
33ede13eef fix: check if album is starred before adding the starred date in the response. also return "starred" in search responses 2020-02-24 22:06:12 -05:00
Deluan
e032bfcf6b refactor: make parameters consistent 2020-02-24 19:04:54 -05:00
Deluan
f4014c475d refactor: make fakeFFmpeg more configurable, change test name 2020-02-24 14:17:32 -05:00
Deluan
f394de664a refactor: new transcoding engine. third (fourth?) time is a charm! 2020-02-24 13:56:09 -05:00
Deluan
d2eea64528 fix: typo 2020-02-23 21:41:10 -05:00
Deluan
d7b5e6a36c fix: add public attribute to playlists. Even though it is optional,
DSub requires it
2020-02-23 00:10:05 -05:00
Deluan
b49b9e3ca0 chore: remove unused script 2020-02-22 20:29:57 -05:00
Deluan
1322bb3bf3 refactor: move cache constructor 2020-02-21 09:36:29 -05:00
Deluan
13a046a679 fix: change stream cache eviction check period to every 10 minutes 2020-02-20 20:12:52 -05:00
Deluan
e6d2056438 fix: typo 2020-02-20 19:39:32 -05:00
Deluan
a6b0c57ce0 feat: add a proper caching system to the transcoding functionality 2020-02-20 19:25:39 -05:00
Deluan
fc14e346b9 feat: store duration as float, to cater for milliseconds 2020-02-20 17:02:06 -05:00
Deluan
5525145906 fix: audio stream's bitrate has precedence over container's bitrate 2020-02-20 13:56:45 -05:00
Deluan
74d87790b8 refactor: better ffmpeg output metadata parsing 2020-02-20 10:41:16 -05:00
Deluan
8ce796756f fix: error message 2020-02-19 15:34:05 -05:00
Deluan
a412989f7e refactor: more stable transcoder, based on http.FileSystem 2020-02-19 14:53:35 -05:00
Deluan
ae02dc203e chore: remove unused code 2020-02-19 09:08:05 -05:00
Deluan
fc7595a464 fix: cover art detection regex 2020-02-18 11:19:22 -05:00
Deluan
4ceaea7732 fix: extract stream level metadata 2020-02-18 10:00:05 -05:00
Deluan
894536c8ec Revert "fix: extract stream level metadata"
This reverts commit 92f6e55821.
2020-02-15 23:18:37 -05:00
Deluan
92f6e55821 fix: extract stream level metadata 2020-02-15 20:47:06 -05:00
Deluan
c3bd181648 feat: use tini to help in avoiding dangling processes 2020-02-15 18:34:47 -05:00
Deluan
3b12c92ad5 feat: add cache to the getCoverArt endpoint, avoid it being reloaded every single time in the UI 2020-02-15 14:32:11 -05:00
Deluan
272d897ec9 chore: go mod tidy 2020-02-15 11:37:27 -05:00
Deluan
e6d717cbbc fix: prevent zombies in transcoding 2020-02-15 11:05:03 -05:00
Deluan
b7f1fc0374 refactor: remove unused import 2020-02-14 09:16:59 -05:00
Deluan
de525edde0 feat: add song count and duration to AlbumDetails 2020-02-14 09:14:50 -05:00
Deluan
7f94660183 feat: use different resource for listing songs in albums 2020-02-14 09:02:32 -05:00
Deluan
b2d022b823 fix: ignore environment dependant test 2020-02-13 20:19:51 -05:00
Deluan
ba08f00c20 feat: make rescan faster, only loading metadata from changed files 2020-02-13 20:18:17 -05:00
Deluan
d9993c5877 refactor: separate metadata extraction from audio files scanning 2020-02-13 10:03:52 -05:00
Deluan
edb839a41d fix: only update artists and albums if there were any changes in files 2020-02-12 23:05:10 -05:00
Deluan
9fa73e3b7b feat: implement AlbumShow using a Datagrid. WIP: still need to make it responsive 2020-02-12 20:35:35 -05:00
dependabot-preview[bot]
8ebb85b0af build(deps): bump github.com/astaxie/beego from 1.12.0 to 1.12.1
Bumps [github.com/astaxie/beego](https://github.com/astaxie/beego) from 1.12.0 to 1.12.1.
- [Release notes](https://github.com/astaxie/beego/releases)
- [Commits](https://github.com/astaxie/beego/compare/v1.12.0...v1.12.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-10 08:57:04 -05:00
Deluan
a37beac753 feat: add X-Content-Duration header to the stream response 2020-02-09 22:09:18 -05:00
Deluan
8a31e80b7a fix: find songs and albums when sending an artist name search query 2020-02-09 19:52:06 -05:00
Deluan
ce11a2f3be feat: fake getArtistInfo/getArtistInfo2, just to enable artist browsing in MusicStash 2020-02-09 19:42:37 -05:00
Deluan
5a95feeedc fix: allow searches with 2 chars. closes #65 2020-02-09 12:20:34 -05:00
Deluan
400fa65326 feat: better scanner logging when level = info 2020-02-08 23:36:09 -05:00
Deluan
ab10719d27 fix: use a regex to match year in ffmpeg date field. close #63 2020-02-08 23:17:12 -05:00
Deluan
029290f304 fix: set default play_count to 0
IncPlayCount was not incrementing when the annotation already existed with play_count = null
2020-02-08 22:55:05 -05:00
Deluan
2c146ea1fe feat: add option to auto-create admin user on first start-up
Useful for development purposes
2020-02-08 14:50:33 -05:00
Deluan
10ead1f5f2 feat: better way to detect initial account creation 2020-02-08 14:32:55 -05:00
Deluan
730722cfe3 feat: better track number formatting 2020-02-08 11:50:11 -05:00
Deluan
dc352834b9 fix: workaround to force check for initial setup 2020-02-08 00:11:15 -05:00
Deluan
313a3342a0 fix: remove unused import 2020-02-07 22:35:04 -05:00
Deluan
0f13bbdbd0 docs: update screenshots 2020-02-07 18:21:51 -05:00
Deluan
4310f2c94f docs: update README 2020-02-07 18:02:44 -05:00
Deluan
6ce4811460 feat: add the remainder of the album to the queue when clicking on an album's track 2020-02-07 17:36:50 -05:00
Deluan
52cd17963f feat: limit size of cover art 2020-02-07 16:51:14 -05:00
Deluan
8f0c07d29f refactor: simplify PlayButton usage 2020-02-07 16:38:01 -05:00
Deluan
a50735a94c feat: custom SimpleList, to allow onClick handle 2020-02-07 16:08:53 -05:00
Deluan
f0e7f3ef25 feat: responsive album view 2020-02-07 16:08:52 -05:00
Deluan
2ca98d8e81 feat: optimized for small screens (only) 2020-02-07 13:50:25 -05:00
Deluan
81e1a7088f feat: new album view (initial implementation) 2020-02-07 11:49:26 -05:00
Deluan
d37351610a feat: initial support for i18n 2020-02-07 10:12:32 -05:00
Deluan
99361c0d9f fix: create a subsonic token on login, to use for subsonic API calls 2020-02-06 20:57:00 -05:00
Deluan
8673533cd4 refactor: move request param extractors to utils 2020-02-06 18:55:38 -05:00
Deluan
d9dd9fe587 refactor: put all subsonic client URLs together 2020-02-06 18:41:34 -05:00
Deluan
abb99a8501 feat: add authentication via JWT token 2020-02-06 18:41:34 -05:00
Deluan
690f92a671 feat: make song list more responsive 2020-02-06 18:41:34 -05:00
Deluan
c57007db52 feat: song list xsmall view 2020-02-06 18:41:34 -05:00
Deluan
cc229dcee6 chore: add direct dependency to react-redux 2020-02-06 18:41:34 -05:00
Deluan
7aab82c246 feat: enable overriding sql sorting 2020-02-06 18:41:34 -05:00
Deluan
989deb1200 feat: change pagination options 2020-02-06 18:41:34 -05:00
Deluan
6aaee4342e feat: smaller play button 2020-02-06 18:41:34 -05:00
Deluan
b5dadf55f4 feat: add an authenticated keepalive, to keep the UI session alive while playing songs 2020-02-06 18:41:34 -05:00
Deluan
18c7397709 feat: scrobbling 2020-02-06 18:41:34 -05:00
Deluan
4a82a6cb02 feat: initial integration of react-jinke-music-player 2020-02-06 18:41:33 -05:00
Deluan
220ffd5324 chore: removed unused code 2020-02-06 18:41:16 -05:00
Deluan
e33d2305a1 feat: support multiple year formats in the date tag (#63) 2020-02-06 14:44:50 -05:00
Deluan
7815b57920 fix: remove docker-compose.override.yml from repo 2020-02-06 12:14:10 -05:00
Deluan
18cbb153f3 chore: add a docker-compose.override.yml file, to support local testing 2020-02-06 12:12:10 -05:00
Deluan
9f086b5f7b docs: fix typo 2020-02-06 09:19:32 -05:00
Deluan
c8d6f2d506 feat: add m4b to mime-type list. fix #62 2020-02-06 08:48:02 -05:00
Deluan
6619b0986a chore: go mod tidy 2020-02-05 23:15:19 -05:00
Deluan
2dbd645292 feat: show server version in User Menu 2020-02-05 23:08:04 -05:00
Deluan
6978790e96 feat: allow regular users to login to the UI 2020-02-05 22:22:44 -05:00
Deluan
e0308acef3 feat: add lapsed time to SQL logger, to help detect SQL bottlenecks 2020-02-05 08:47:32 -05:00
Deluan
5fbde33b97 docs: update README 2020-02-05 08:40:15 -05:00
Deluan
19fb29e520 docs: add Discord invite button 2020-02-05 08:33:07 -05:00
Deluan
e5e35516d7 fix: initialize mimetypes for tests 2020-02-04 20:44:54 -05:00
Deluan
28bad95e66 test: removed unused file property 2020-02-04 19:59:04 -05:00
Deluan
9260957271 docs: update README 2020-02-04 15:17:10 -05:00
Deluan
79b0f1f57b docs: add link to ffmpeg static binaries download 2020-02-04 15:13:37 -05:00
Deluan
4dffcb7b46 fix: removed invalid make rule 2020-02-04 15:02:43 -05:00
Deluan
d1f8d39866 refactor: move banner to consts, closer to version 2020-02-04 10:14:53 -05:00
Deluan
0996272943 refactor: more reliable stream seek implementation 2020-02-04 10:01:31 -05:00
Deluan
d093191659 test: createTranscodeCommand 2020-02-04 09:34:26 -05:00
Deluan
998323b364 docs: update README re: transcoding 2020-02-04 09:09:14 -05:00
Deluan
6dfe56c1c4 feat: transcoding info in responses, to enable Jamstash to play transcoded FLAC. hardcoded for now 2020-02-04 09:01:22 -05:00
dependabot-preview[bot]
fd5548f890 build(deps): bump github.com/go-chi/jwtauth
Bumps [github.com/go-chi/jwtauth](https://github.com/go-chi/jwtauth) from 4.0.3+incompatible to 4.0.4+incompatible.
- [Release notes](https://github.com/go-chi/jwtauth/releases)
- [Commits](https://github.com/go-chi/jwtauth/compare/v4.0.3...v4.0.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-04 07:00:20 -05:00
Deluan
6e2454f6cc refactor: add -i to ffmpeg ProbeCommand. make it more consistent with the DownsampleCommand 2020-02-03 23:04:58 -05:00
Deluan
8372dee000 feat: experimental downsampling support 2020-02-03 22:53:57 -05:00
Deluan
41fd5862b8 chore: try to make goreleaser add all changes to changelog 2020-02-03 20:13:32 -05:00
Deluan
a6b5be7b0a ci: use latest ci-goreleaser 2020-02-03 18:24:14 -05:00
Deluan
4d06d250e6 fix: relative path was not working for rootFolder started with '.' 2020-02-03 17:53:59 -05:00
Deluan
694b5d1d39 tests: change test folder permissions 2020-02-03 17:53:59 -05:00
Deluan
5329ac7b72 refactor: better format for list of folders 2020-02-03 17:53:59 -05:00
Deluan
464880dd31 refactor: use stdlib filepath.FromSlash 2020-02-03 17:53:59 -05:00
Deluan
0e01f9a0f9 fix: use filepath.Join instead of path.Join 2020-02-03 17:53:59 -05:00
Deluan
d9eb3e58cd fix: only create db entities in first migration if they don't exist 2020-02-03 17:48:48 -05:00
Deluan
0d64fb05c7 feat: disable scanner if ScanInterval is set to 0 2020-02-03 11:58:21 -05:00
Deluan Quintão
0849d6b901 docs: update stream notes 2020-02-03 11:50:46 -05:00
Deluan
40ad6a7bef fix: always build everything when calling buildall target 2020-02-03 08:42:15 -05:00
Deluan
ddae5588d4 chore: update ginkgo/gomega dependencies 2020-02-03 08:41:36 -05:00
Deluan
67c20f36b1 chore: update all node dependencies 2020-02-03 08:39:39 -05:00
Deluan
ff8c18e0f4 fix: don't log empty sql responses as errors 2020-02-02 21:29:27 -05:00
Deluan
203754726b refactor: better request logging 2020-02-01 20:07:15 -05:00
Deluan
e97d805444 docs: update api compatibility 2020-02-01 18:46:16 -05:00
Deluan
d4365b9f64 refactor: read musicFolderId from request (but still don't use it) 2020-02-01 17:23:03 -05:00
Deluan
b62b78edfe refactor: better SQL logging 2020-02-01 17:23:03 -05:00
Deluan
7c4511e33a refactor: consolidate query executions into two functions queryOne and queryAll 2020-02-01 17:23:03 -05:00
Deluan
7e65bb8f20 refactor: better integration between db and persistence packages
Will address support for different DBs in the future (+1 squashed commit)
Squashed commits:
[a014757] refactor: better integration between `db` and `persistence` packages
2020-02-01 17:23:03 -05:00
Deluan
76ca8afc84 refactor: better migration description 2020-02-01 17:23:03 -05:00
Deluan
a6b8f40ac3 refactor: remove prefix New from SQLStore 2020-02-01 17:23:03 -05:00
Deluan
0d0787e656 refactor:clean annotations in GC 2020-02-01 17:23:03 -05:00
Deluan
88e01d05f6 refactor: annotations 2020-02-01 17:23:03 -05:00
Deluan
de1fea64bc refactor: introduce GC, to delete old data 2020-02-01 17:23:03 -05:00
Deluan
5d1df19291 fix: manually set timestamps, as we don't rely on the ORM anymore 2020-02-01 17:23:03 -05:00
Deluan
0b91d8a30e refactor: more SQL logs 2020-02-01 17:23:03 -05:00
Deluan
cdbbb2f596 fix: Find/DeleteByPath 2020-02-01 17:23:03 -05:00
Deluan
44671c59c0 refactor: fix rest filter 2020-02-01 17:23:03 -05:00
Deluan
d9f61a278c refactor: some clean-up 2020-02-01 17:23:03 -05:00
Deluan
a260e65307 refactor: add GetStarred to artists 2020-02-01 17:23:03 -05:00
Deluan
5a4c763510 refactor: add search back to albums and artists 2020-02-01 17:23:03 -05:00
Deluan
d755609d13 refactor: add search back to mediafiles 2020-02-01 17:23:03 -05:00
Deluan
4f4af34595 fix: DB pagination 2020-02-01 17:23:03 -05:00
Deluan
f5071d1614 refactor: adding back artist and album tables 2020-02-01 17:23:03 -05:00
Deluan
d389d40db1 feat: improve logs, remove config for disable authentication 2020-02-01 17:23:03 -05:00
Deluan
72d9ddf532 refactor: remove annotation handling from engine 2020-02-01 17:23:03 -05:00
Deluan
67ed830a68 refactor: add filters 2020-02-01 17:23:03 -05:00
Deluan
71c1844bca refactor: new persistence, more SQL, less ORM 2020-02-01 17:23:03 -05:00
Deluan
b26a5ef2d0 feat: add name to user list 2020-02-01 17:23:03 -05:00
Deluan
b286034977 chore: upgrade squirrel 2020-02-01 17:23:03 -05:00
Deluan
c9f5625abf fix: skip files with errors during scan 2020-02-01 11:25:31 -05:00
Deluan
22d57a7c26 chore: go mod tidy 2020-01-30 16:36:43 -05:00
Deluan
0c5bf18d80 build: add release and dist targets 2020-01-30 16:33:27 -05:00
Deluan
9b7d1757e7 build: add goose to setup target, add dist target 2020-01-30 16:08:39 -05:00
Deluan
c34a5dcb07 docs: update README 2020-01-30 16:07:54 -05:00
Deluan
90a1e6d213 feat: add server name and version to all responses
This is inline with other Subsonic compatible servers, like funkwhale, madsonic, ampache...
2020-01-30 14:43:24 -05:00
Deluan
482350c076 build: run tests in Dockerfile 2020-01-29 17:09:46 -05:00
Deluan
64388b2d4a fix: correct description meta in index.html 2020-01-29 16:56:22 -05:00
Deluan
3007ca68d5 fix: disable User.lastAccessAt field for now.
Updating it on every request was cause DB retentions/lock errors
2020-01-28 16:20:59 -05:00
Deluan
d4edff3aaa fix: only add the latest tag to version if the tag is attached to the current commit, or else use the branch name 2020-01-28 15:28:39 -05:00
Deluan
99b1dc1421 feat: upgrade ffmpeg in docker image 2020-01-28 15:01:23 -05:00
dependabot-preview[bot]
37dfe4c092 Bump github.com/mattn/go-sqlite3
Bumps [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3) from 2.0.2+incompatible to 2.0.3+incompatible.
- [Release notes](https://github.com/mattn/go-sqlite3/releases)
- [Commits](https://github.com/mattn/go-sqlite3/compare/v2.0.2...v2.0.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-01-28 07:37:40 -05:00
Deluan
1278863416 feat: support clients that send the API params as a x-www-form-urlencoded POST 2020-01-27 15:10:46 -05:00
Deluan
cffae3a7d6 ci: don't add 'ci:' commits to the changelog 2020-01-27 09:44:28 -05:00
Deluan
0d2911daf9 refactor: add Context to the persistence layer 2020-01-27 09:41:33 -05:00
Deluan
3c54b776d6 docs: add Discord invite 2020-01-27 08:05:15 -05:00
Deluan Quintão
34127805ce doc: update compatibility table 2020-01-27 04:32:55 -05:00
Deluan
ac4aa1ebe2 feat: PORT env var can override configured port 2020-01-26 22:18:30 -05:00
Deluan
b7d7251cf4 fix: user's email is not mandatory 2020-01-26 22:17:58 -05:00
Deluan
7bf110f22f docs: update readme 2020-01-26 21:54:49 -05:00
Deluan
3b651a3728 docs: update readme 2020-01-26 21:34:53 -05:00
Deluan
13390c2edb fix: add git sha and tag to built image 2020-01-26 20:52:09 -05:00
Deluan
221d320ccf docs: add latest released version to README 2020-01-26 20:51:41 -05:00
931 changed files with 97741 additions and 19641 deletions

20
.devcontainer/Dockerfile Normal file
View File

@@ -0,0 +1,20 @@
# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.148.1/containers/go/.devcontainer/base.Dockerfile
# [Choice] Go version: 1, 1.15, 1.14
ARG VARIANT="1"
FROM mcr.microsoft.com/vscode/devcontainers/go:0-${VARIANT}
# [Option] Install Node.js
ARG INSTALL_NODE="true"
ARG NODE_VERSION="lts/*"
RUN if [ "${INSTALL_NODE}" = "true" ]; then su vscode -c "source /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi
# [Optional] Uncomment this section to install additional OS packages.
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
&& apt-get -y install --no-install-recommends libtag1-dev ffmpeg
# [Optional] Uncomment the next line to use go get to install anything else you need
# RUN go get -x <your-dependency-or-tool>
# [Optional] Uncomment this line to install global node packages.
# RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g <your-package-here>" 2>&1

View File

@@ -0,0 +1,61 @@
{
"name": "Go",
"build": {
"dockerfile": "Dockerfile",
"args": {
// Update the VARIANT arg to pick a version of Go: 1, 1.15, 1.14
"VARIANT": "1.19",
// Options
"INSTALL_NODE": "true",
"NODE_VERSION": "v16"
}
},
"workspaceMount": "",
"runArgs": [
"--cap-add=SYS_PTRACE",
"--security-opt",
"seccomp=unconfined",
"--volume=${localWorkspaceFolder}:/workspaces/${localWorkspaceFolderBasename}:Z"
],
// Set *default* container specific settings.json values on container create.
"settings": {
"terminal.integrated.shell.linux": "/bin/bash",
"go.useGoProxyToCheckForToolUpdates": false,
"go.useLanguageServer": true,
"go.gopath": "/go",
"go.goroot": "/usr/local/go",
"go.toolsGopath": "/go/bin",
"go.formatTool": "goimports",
"go.lintOnSave": "package",
"go.lintTool": "golangci-lint",
"editor.formatOnSave": true,
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[jsonc]": {
"editor.defaultFormatter": "vscode.json-language-features"
}
},
// Add the IDs of extensions you want installed when the container is created.
"extensions": [
"golang.Go",
"esbenp.prettier-vscode",
"tamasfe.even-better-toml"
],
// Use 'forwardPorts' to make a list of ports inside the container available locally.
"forwardPorts": [
4533,
4633
],
// Use 'postCreateCommand' to run commands after the container is created.
// "postCreateCommand": "make setup-dev",
// Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
"remoteUser": "vscode",
"remoteEnv": {
"ND_MUSICFOLDER": "./music",
"ND_DATAFOLDER": "./data"
}
}

View File

@@ -1,14 +1,10 @@
.DS_Store
ui/node_modules
ui/build
Jamstash-master
Dockerfile
docker-compose*.yml
data
*.db
testDB
*_test.go
navidrome
navidrome.db
navidrome.toml
assets/*gen.go
dist

4
.git-blame-ignore-revs Normal file
View File

@@ -0,0 +1,4 @@
# Upgrade Prettier to 2.0.4. Reformatted all JS files
b3f70538a9138bc279a451f4f358605097210d41
# Move project to Navidrome GitHub organization
6ee45a9ccc5e7ea4290c89030e67c99c0514bd25

12
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,12 @@
# These are supported funding model platforms
github: deluan
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: deluan
liberapay: deluan
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

103
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

@@ -0,0 +1,103 @@
name: Bug Report
description: Before opening a new issue, please search to see if an issue already exists for the bug you encountered.
title: "[Bug]: "
labels: ["bug", "triage"]
#assignees:
# - deluan
body:
- type: markdown
attributes:
value: |
### Thanks for taking the time to fill out this bug report!
- type: checkboxes
id: requirements
attributes:
label: "I confirm that:"
options:
- label: I have searched the existing [open AND closed issues](https://github.com/navidrome/navidrome/issues?q=is%3Aissue) to see if an issue already exists for the bug I've encountered
required: true
- label: I'm using the latest version (your issue may have been fixed already)
required: false
- type: input
id: version
attributes:
label: Version
description: What version of Navidrome are you running? (please try upgrading first, as your issue may have been fixed already).
validations:
required: true
- type: textarea
attributes:
label: Current Behavior
description: A concise description of what you're experiencing.
validations:
required: true
- type: textarea
attributes:
label: Expected Behavior
description: A concise description of what you expected to happen.
validations:
required: true
- type: textarea
attributes:
label: Steps To Reproduce
description: Steps to reproduce the behavior.
placeholder: |
1. In this scenario...
2. With this config...
3. Click (or Execute) '...'
4. See error...
validations:
required: false
- type: textarea
id: env
attributes:
label: Environment
description: |
examples:
- **OS**: Ubuntu 20.04
- **Browser**: Chrome 110.0.5481.177 on Windows 11
- **Client**: DSub 5.5.1
value: |
- OS:
- Browser:
- Client:
render: markdown
- type: dropdown
id: distribution
attributes:
label: How Navidrome is installed?
multiple: false
options:
- Docker
- Binary (from downloads page)
- Package
- Built from sources
validations:
required: true
- type: textarea
id: config
attributes:
label: Configuration
description: Please copy and paste your `navidrome.toml` (and/or `docker-compose.yml`) configuration. This will be automatically formatted into code, so no need for backticks.
render: toml
- type: textarea
id: logs
attributes:
label: Relevant log output
description: Please copy and paste any relevant log output (change your `LogLevel` (`ND_LOGLEVEL`) to debug). This will be automatically formatted into code, so no need for backticks. ([Where I can find the logs?](https://www.navidrome.org/docs/faq/#where-are-the-logs))
render: shell
- type: textarea
attributes:
label: Anything else?
description: |
Links? References? Anything that will give us more context about the issue you are encountering!
Tip: You can attach screenshots by clicking this area to highlight it and then dragging files in.
- type: checkboxes
id: terms
attributes:
label: Code of Conduct
description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/navidrome/navidrome/blob/master/CODE_OF_CONDUCT.md).
options:
- label: I agree to follow Navidrome's Code of Conduct
required: true

8
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Ideas for new features
url: https://github.com/navidrome/navidrome/discussions/categories/ideas
about: This is the place to share and discuss new ideas and potentially new features.
- name: Support requests
url: https://github.com/navidrome/navidrome/discussions/categories/q-a
about: This is the place to ask questions.

12
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,12 @@
version: 2
updates:
- package-ecosystem: npm
directory: "/ui"
schedule:
interval: weekly
open-pull-requests-limit: 10
- package-ecosystem: gomod
directory: "/"
schedule:
interval: weekly
open-pull-requests-limit: 10

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 264 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 709 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 MiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 KiB

BIN
.github/screenshots/ss-mobile-login.png vendored Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 735 KiB

BIN
.github/screenshots/ss-mobile-player.png vendored Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 885 KiB

View File

@@ -1,53 +0,0 @@
name: Build
on: [push]
jobs:
go:
name: Test Server on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
# TODO Fix tests in Windows
# os: [macOS-latest, ubuntu-latest, windows-latest]
os: [macOS-latest, ubuntu-latest]
steps:
- name: Set up Go 1.13
uses: actions/setup-go@v1
with:
go-version: 1.13
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v1
- name: Download dependencies
run: go mod download
- name: Test
run: go test -cover ./... -v
js:
name: Test UI
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/setup-node@v1
with:
node-version: 13
- name: npm install dependencies
run: |
cd ui
npm ci
# TODO: Enable when there are tests to run
# - name: npm test
# run: |
# cd ui
# CI=test npm test
- name: npm build
run: |
cd ui
npm run build

View File

@@ -0,0 +1,54 @@
name: Add download link to PR
on:
workflow_run:
workflows: ['Pipeline: Test, Lint, Build']
types: [completed]
jobs:
pr_comment:
if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v3
with:
# This snippet is public-domain, taken from
# https://github.com/oprypin/nightly.link/blob/master/.github/workflows/pr-comment.yml
script: |
const {owner, repo} = context.repo;
const run_id = ${{github.event.workflow_run.id}};
const pull_head_sha = '${{github.event.workflow_run.head_sha}}';
const pull_user_id = ${{github.event.sender.id}};
const issue_number = await (async () => {
const pulls = await github.pulls.list({owner, repo});
for await (const {data} of github.paginate.iterator(pulls)) {
for (const pull of data) {
if (pull.head.sha === pull_head_sha && pull.user.id === pull_user_id) {
return pull.number;
}
}
}
})();
if (issue_number) {
core.info(`Using pull request ${issue_number}`);
} else {
return core.error(`No matching pull request found`);
}
const {data: {artifacts}} = await github.actions.listWorkflowRunArtifacts({owner, repo, run_id});
if (!artifacts.length) {
return core.error(`No artifacts found`);
}
let body = `Download the artifacts for this pull request:\n`;
for (const art of artifacts) {
body += `\n* [${art.name}.zip](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
}
const {data: comments} = await github.issues.listComments({repo, owner, issue_number});
const existing_comment = comments.find((c) => c.user.login === 'github-actions[bot]');
if (existing_comment) {
core.info(`Updating comment ${existing_comment.id}`);
await github.issues.updateComment({repo, owner, comment_id: existing_comment.id, body});
} else {
core.info(`Creating a comment`);
await github.issues.createComment({repo, owner, issue_number, body});
}

38
.github/workflows/pipeline.dockerfile vendored Normal file
View File

@@ -0,0 +1,38 @@
#####################################################
### Copy platform specific binary
FROM bash as copy-binary
ARG TARGETPLATFORM
RUN echo "Target Platform = ${TARGETPLATFORM}"
COPY dist .
RUN if [ "$TARGETPLATFORM" = "linux/amd64" ]; then cp navidrome_linux_amd64_linux_amd64_v1/navidrome /navidrome; fi
RUN if [ "$TARGETPLATFORM" = "linux/386" ]; then cp navidrome_linux_386_linux_386/navidrome /navidrome; fi
RUN if [ "$TARGETPLATFORM" = "linux/arm64" ]; then cp navidrome_linux_arm64_linux_arm64/navidrome /navidrome; fi
RUN if [ "$TARGETPLATFORM" = "linux/arm/v6" ]; then cp navidrome_linux_arm_linux_arm_6/navidrome /navidrome; fi
RUN if [ "$TARGETPLATFORM" = "linux/arm/v7" ]; then cp navidrome_linux_arm_linux_arm_7/navidrome /navidrome; fi
RUN chmod +x /navidrome
#####################################################
### Build Final Image
FROM alpine as release
LABEL maintainer="deluan@navidrome.org"
# Install ffmpeg and output build config
RUN apk add --no-cache ffmpeg
RUN ffmpeg -buildconf
COPY --from=copy-binary /navidrome /app/
VOLUME ["/data", "/music"]
ENV ND_MUSICFOLDER /music
ENV ND_DATAFOLDER /data
ENV ND_PORT 4533
ENV GODEBUG "asyncpreemptoff=1"
EXPOSE ${ND_PORT}
HEALTHCHECK CMD wget -O- http://localhost:${ND_PORT}/ping || exit 1
WORKDIR /app
ENTRYPOINT ["/app/navidrome"]

224
.github/workflows/pipeline.yml vendored Normal file
View File

@@ -0,0 +1,224 @@
name: 'Pipeline: Test, Lint, Build'
on:
push:
branches:
- master
tags:
- "v*"
pull_request:
branches:
- master
jobs:
go-lint:
name: Lint Go code
runs-on: ubuntu-latest
steps:
- name: Install taglib
run: sudo apt-get install libtag1-dev
- name: Set up Go 1.20
uses: actions/setup-go@v3
with:
go-version: 1.20.x
- uses: actions/checkout@v3
- name: golangci-lint
uses: golangci/golangci-lint-action@v3
with:
version: latest
github-token: ${{ secrets.GITHUB_TOKEN }}
args: --timeout 2m
- name: Install goimports
run: go install golang.org/x/tools/cmd/goimports
- run: goimports -w `find . -name '*.go' | grep -v '_gen.go$'`
- run: go mod tidy
- name: Verify no changes from goimports and go mod tidy
run: |
git status --porcelain
if [ -n "$(git status --porcelain)" ]; then
echo 'To fix this check, run "goimports -w $(find . -name '*.go' | grep -v '_gen.go$') && go mod tidy"'
exit 1
fi
go:
name: Test with Go ${{ matrix.go_version }}
runs-on: ubuntu-latest
strategy:
matrix:
go_version: [1.20.x,1.19.x]
steps:
- name: Install taglib
run: sudo apt-get install libtag1-dev
- name: Check out code into the Go module directory
uses: actions/checkout@v3
- name: Set up Go ${{ matrix.go_version }}
uses: actions/setup-go@v3
with:
go-version: ${{ matrix.go_version }}
cache: true
- name: Download dependencies
if: steps.cache-go.outputs.cache-hit != 'true'
continue-on-error: ${{contains(matrix.go_version, 'beta') || contains(matrix.go_version, 'rc')}}
run: go mod download
- name: Test
continue-on-error: ${{contains(matrix.go_version, 'beta') || contains(matrix.go_version, 'rc')}}
run: go test -shuffle=on -race -cover ./... -v
js:
name: Build JS bundle
runs-on: ubuntu-latest
env:
NODE_OPTIONS: '--max_old_space_size=4096'
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
- name: npm install dependencies
run: |
cd ui
npm ci
- name: npm lint
run: |
cd ui
npm run check-formatting && npm run lint
- name: npm test
run: |
cd ui
npm test
- name: npm build
run: |
cd ui
npm run build
- uses: actions/upload-artifact@v3
with:
name: js-bundle
path: ui/build
retention-days: 7
binaries:
name: Build binaries
needs: [js, go, go-lint]
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: actions/download-artifact@v3
with:
name: js-bundle
path: ui/build
- name: Config /github/workspace folder as trusted
uses: docker://deluan/ci-goreleaser:1.20.3-1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
args: /bin/bash -c "git config --global --add safe.directory /github/workspace; git describe --dirty --always --tags"
- name: Run GoReleaser - SNAPSHOT
if: startsWith(github.ref, 'refs/tags/') != true
uses: docker://deluan/ci-goreleaser:1.20.3-1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
args: goreleaser release --rm-dist --skip-publish --snapshot
- name: Run GoReleaser - RELEASE
if: startsWith(github.ref, 'refs/tags/')
uses: docker://deluan/ci-goreleaser:1.20.3-1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
args: goreleaser release --rm-dist
- uses: actions/upload-artifact@v3
with:
name: binaries
path: |
dist
!dist/*.tar.gz
!dist/*.zip
retention-days: 7
docker:
name: Build and publish Docker images
needs: [binaries]
runs-on: ubuntu-latest
env:
DOCKER_IMAGE: ${{secrets.DOCKER_IMAGE}}
steps:
- name: Set up QEMU
id: qemu
uses: docker/setup-qemu-action@v2
if: env.DOCKER_IMAGE != ''
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v2
if: env.DOCKER_IMAGE != ''
- uses: actions/checkout@v3
if: env.DOCKER_IMAGE != ''
- uses: actions/download-artifact@v3
if: env.DOCKER_IMAGE != ''
with:
name: binaries
path: dist
- name: Login to Docker Hub
if: env.DOCKER_IMAGE != ''
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Login to GitHub Container Registry
if: env.DOCKER_IMAGE != ''
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata for Docker
if: env.DOCKER_IMAGE != ''
id: meta
uses: docker/metadata-action@v4
with:
labels: |
maintainer=deluan
images: |
name=${{secrets.DOCKER_IMAGE}},enable=${{env.GITHUB_REF_TYPE == 'tag' || github.ref == format('refs/heads/{0}', 'master')}}
name=ghcr.io/${{ github.repository }}
tags: |
type=ref,event=pr
type=semver,pattern={{version}}
type=raw,value=develop,enable={{is_default_branch}}
- name: Build and Push
if: env.DOCKER_IMAGE != ''
uses: docker/build-push-action@v4
with:
context: .
file: .github/workflows/pipeline.dockerfile
platforms: linux/amd64,linux/386,linux/arm/v6,linux/arm/v7,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}

View File

@@ -1,31 +0,0 @@
name: Release
on:
create:
tags:
- v*.*.*
jobs:
release:
name: Release
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v1
with:
fetch-depth: 0
- uses: actions/setup-node@v1
with:
node-version: 13
- name: Build UI
run: |
cd ui
npm ci
npm run build
- name: Fetch tags
run: git fetch --depth=1 origin +refs/tags/*:refs/tags/*
- name: Run GoReleaser
uses: docker://bepsays/ci-goreleaser:1.13-4
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
args: goreleaser release --rm-dist

55
.github/workflows/stale.yml vendored Normal file
View File

@@ -0,0 +1,55 @@
name: 'Close stale issues and PRs'
on:
workflow_dispatch:
schedule:
- cron: '30 1 * * *'
permissions:
contents: read
jobs:
stale:
permissions:
issues: write
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: dessant/lock-threads@v4.0.0
with:
issue-inactive-days: 120
pr-inactive-days: 120
log-output: true
add-issue-labels: 'frozen-due-to-age'
add-pr-labels: 'frozen-due-to-age'
issue-comment: >
This issue has been automatically locked since there
has not been any recent activity after it was closed.
Please open a new issue for related bugs.
pr-comment: >
This pull request has been automatically locked since there
has not been any recent activity after it was closed.
Please open a new issue for related bugs.
- uses: actions/stale@v7
with:
operations-per-run: 999
days-before-issue-stale: 180
days-before-pr-stale: 180
days-before-issue-close: 30
days-before-pr-close: 30
stale-issue-message: >
This issue has been automatically marked as stale because it has not had
recent activity. The resources of the Navidrome team are limited, and so we are asking for your help.
If this is a **bug** and you can still reproduce this error on the <code>master</code> branch, please reply with all of the information you have about it in order to keep the issue open.
If this is a **feature request**, and you feel that it is still relevant and valuable, please tell us why.
This issue will automatically be closed in the near future if no further activity occurs. Thank you for all your contributions.
stale-pr-message: This PR has been automatically marked as stale because it has not had
recent activity. The resources of the Navidrome team are limited, and so we are asking for your help.
Please check https://github.com/navidrome/navidrome/blob/master/CONTRIBUTING.md#pull-requests and verify that this code contribution fits with the description. If yes, tell it in a comment.
This PR will automatically be closed in the near future if no further activity occurs. Thank you for all your contributions.
stale-issue-label: 'stale'
exempt-issue-labels: 'keep,security'
stale-pr-label: 'stale'
exempt-pr-labels: 'keep,security'

View File

@@ -0,0 +1,27 @@
name: POEditor import
on:
workflow_dispatch:
schedule:
- cron: '0 10 * * *'
jobs:
update-translations:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Get updated translations
env:
POEDITOR_PROJECTID: ${{ secrets.POEDITOR_PROJECTID }}
POEDITOR_APIKEY: ${{ secrets.POEDITOR_APIKEY }}
run: |
./update-translations.sh
- name: Show changes, if any
run: |
git status --porcelain
git diff
- name: Create Pull Request
uses: peter-evans/create-pull-request@v4
with:
token: ${{ secrets.PAT }}
commit-message: Update translations
title: Update translations from POEditor
branch: update-translations

13
.gitignore vendored
View File

@@ -1,5 +1,6 @@
.DS_Store
.idea
.vscode
.envrc
/navidrome
/iTunes*.xml
@@ -9,12 +10,18 @@ vendor/*/
wiki
TODO.md
var
Artwork
navidrome.toml
master.zip
Jamstash-master
testDB
navidrome.db
cache/*
*.swp
*_gen.go
embedded_gen.go
dist
music
navidrome.db-shm
navidrome.db-wal
tags
.gitinfo
docker-compose.yml
!contrib/docker-compose.yml

36
.golangci.yml Normal file
View File

@@ -0,0 +1,36 @@
run:
go: "1.19"
linters:
enable:
- asasalint
- asciicheck
- bidichk
- bodyclose
- depguard
- dogsled
- durationcheck
- errcheck
- errorlint
- exportloopref
- gocyclo
- goprintffuncname
- gosec
- gosimple
- govet
- ineffassign
- misspell
- nakedret
- nilerr
- rowserrcheck
- staticcheck
- typecheck
- unconvert
- unused
- whitespace
issues:
exclude-rules:
- linters:
- gosec
text: "(G501|G401|G505):"

View File

@@ -1,27 +1,8 @@
# GoReleaser config
before:
hooks:
- go get -u github.com/go-bindata/go-bindata/...
- go-bindata -fs -prefix ui/build -tags embed -nocompress -pkg assets -o assets/embedded_gen.go ui/build/...
- git checkout .
project_name: navidrome
builds:
- id: navidrome_darwin
env:
- CGO_ENABLED=1
- CC=o64-clang
- CXX=o64-clang++
goos:
- darwin
goarch:
- amd64
flags:
- -tags=embed
ldflags:
- -X main.gitSha={{.ShortCommit}} -X main.gitTag={{.Tag}}
- id: navidrome_linux
- id: navidrome_linux_amd64
env:
- CGO_ENABLED=1
goos:
@@ -29,58 +10,130 @@ builds:
goarch:
- amd64
flags:
- -tags=embed
- -tags=netgo
ldflags:
- "-extldflags '-static -lz'"
- -s -w -X github.com/navidrome/navidrome/consts.gitSha={{.ShortCommit}} -X github.com/navidrome/navidrome/consts.gitTag={{.Version}}
- id: navidrome_linux_386
env:
- CGO_ENABLED=1
- PKG_CONFIG_PATH=/i386/lib/pkgconfig
goos:
- linux
goarch:
- "386"
flags:
- -tags=netgo
ldflags:
- "-extldflags '-static'"
- -X main.gitSha={{.ShortCommit}} -X main.gitTag={{.Tag}}
- -s -w -X github.com/navidrome/navidrome/consts.gitSha={{.ShortCommit}} -X github.com/navidrome/navidrome/consts.gitTag={{.Version}}
- id: navidrome_windows_i686
- id: navidrome_linux_arm
env:
- CGO_ENABLED=1
- CC=arm-linux-gnueabi-gcc
- CXX=arm-linux-gnueabi-g++
- PKG_CONFIG_PATH=/arm/lib/pkgconfig
goos:
- linux
goarch:
- arm
goarm:
- "5"
- "6"
- "7"
flags:
- -tags=netgo
ldflags:
- "-extldflags '-static'"
- -s -w -X github.com/navidrome/navidrome/consts.gitSha={{.ShortCommit}} -X github.com/navidrome/navidrome/consts.gitTag={{.Version}}
- id: navidrome_linux_arm64
env:
- CGO_ENABLED=1
- CC=aarch64-linux-gnu-gcc
- CXX=aarch64-linux-gnu-g++
- PKG_CONFIG_PATH=/arm64/lib/pkgconfig
goos:
- linux
goarch:
- arm64
flags:
- -tags=netgo
ldflags:
- "-extldflags '-static'"
- -s -w -X github.com/navidrome/navidrome/consts.gitSha={{.ShortCommit}} -X github.com/navidrome/navidrome/consts.gitTag={{.Version}}
- id: navidrome_windows_386
env:
- CGO_ENABLED=1
- CC=i686-w64-mingw32-gcc
- CXX=i686-w64-mingw32-g++
- PKG_CONFIG_PATH=/mingw32/lib/pkgconfig
goos:
- windows
goarch:
- 386
- "386"
flags:
- -tags=embed
- -tags=netgo
ldflags:
- "-extldflags '-static'"
- -X main.gitSha={{.ShortCommit}} -X main.gitTag={{.Tag}}
- -s -w -X github.com/navidrome/navidrome/consts.gitSha={{.ShortCommit}} -X github.com/navidrome/navidrome/consts.gitTag={{.Version}}
- id: navidrome_windows_x64
- id: navidrome_windows_amd64
env:
- CGO_ENABLED=1
- CC=x86_64-w64-mingw32-gcc
- CXX=x86_64-w64-mingw32-g++
- PKG_CONFIG_PATH=/mingw64/lib/pkgconfig
goos:
- windows
goarch:
- amd64
flags:
- -tags=embed
- -tags=netgo
ldflags:
- "-extldflags '-static'"
- -X main.gitSha={{.ShortCommit}} -X main.gitTag={{.Tag}}
- -s -w -X github.com/navidrome/navidrome/consts.gitSha={{.ShortCommit}} -X github.com/navidrome/navidrome/consts.gitTag={{.Version}}
- id: navidrome_darwin_amd64
env:
- CGO_ENABLED=1
- CC=o64-clang
- CXX=o64-clang++
- PKG_CONFIG_PATH=/darwin/lib/pkgconfig
goos:
- darwin
goarch:
- amd64
flags:
- -tags=netgo
ldflags:
- -s -w -X github.com/navidrome/navidrome/consts.gitSha={{.ShortCommit}} -X github.com/navidrome/navidrome/consts.gitTag={{.Version}}
archives:
-
format_overrides:
- format_overrides:
- goos: windows
format: zip
replacements:
darwin: macOS
linux: Linux
windows: Windows
386: i386
amd64: x86_64
checksum:
name_template: 'checksums.txt'
name_template: "{{ .ProjectName }}_checksums.txt"
snapshot:
name_template: "{{ .Tag }}-next"
name_template: "{{ .Tag }}-SNAPSHOT"
release:
draft: true
changelog:
sort: asc
# sort: asc
filters:
exclude:
- '^docs:'
- '^test:'
- "^docs:"

2
.nvmrc
View File

@@ -1 +1 @@
v13.7.0
v16

View File

@@ -1,69 +0,0 @@
### Supported Subsonic API endpoints
Navidrome is currently compatible with [Subsonic API](http://www.subsonic.org/pages/api.jsp) v1.8.0, with some exceptions.
This is an (almost) up to date list of all Subsonic API endpoints implemented by Navidrome.
Check the "Notes" column for limitations/missing behaviour. Also keep in mind these differences between
Navidrome and Subsonic:
* Right now, Navidrome only works with a single Music Library (Music Folder)
* Navidrome does not mark songs as played by calls to `stream`, only when
`scrobble` is called with `submission=true`
* Next features to be implemented: Playlists (WIP), MultiUser (WIP), Jukebox, Sharing, Podcasts, Bookmarks, Internet Radio.
Navidrome is actively being tested with:
[DSub](http://www.subsonic.org/pages/apps.jsp#dsub),
[Music Stash](https://play.google.com/store/apps/details?id=com.ghenry22.mymusicstash) and
[Jamstash](http://www.subsonic.org/pages/apps.jsp#jamstash))
| ENDPOINT | NOTES |
|------------------------|-------|
| _SYSTEM_ ||
| `ping` | |
| `getLicense` | Always valid ;) |
| ||
| _BROWSING_ ||
| `getMusicFolders` | Hardcoded to just one, configured in `app.conf` |
| `getIndexes` | Doesn't support shortcuts, nor direct children |
| `getMusicDirectory` | |
| `getSong` | |
| `getArtists` | |
| `getArtist` | |
| `getAlbum` | |
| `getGenres` | |
| ||
| _ALBUM/SONGS LISTS_ ||
| `getAlbumList` | `byYear` and `byGenre` are not implemented |
| `getAlbumList2` | `byYear` and `byGenre` are not implemented |
| `getStarred` | |
| `getStarred2` | |
| `getNowPlaying` | |
| `getRandomSongs` | Ignores `year` parameter |
| ||
| _SEARCHING_ ||
| `search2` | Doesn't support Lucene queries, only simple auto complete queries |
| `search3` | Doesn't support Lucene queries, only simple auto complete queries |
| ||
| _PLAYLISTS_ ||
| `getPlaylists` | `username` parameter is not implemented |
| `getPlaylist` | |
| `createPlaylist` | Return empty response on success |
| `updatePlaylist` | `comment` and `public` are not implemented. All playlists are public |
| `deletePlaylist` | |
| ||
| _MEDIA RETRIEVAL_ ||
| `stream` | Returns wrong content-length when downsampling |
| `download` | |
| `getCoverArt` | Only gets embedded artwork |
| `getAvatar` | Always returns the same image |
| ||
| _MEDIA ANNOTATION_ ||
| `star` | |
| `unstar` | |
| `setRating` | Doesn't work with artists |
| `scrobble` | No Last.FM support yet. It is used to update play count, last played, skip count and last skipped |
| ||
| _USER MANAGEMENT_ ||
| `getUser` | Hardcoded all roles, ignores `username` parameter|

129
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,129 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
navidrome@navidrome.org.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.

92
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,92 @@
# Navidrome Contribution Guide
Navidrome is a streaming service which allows you to enjoy your music collection from anywhere. We'd welcome you to contribute to our open source project and make Navidrome even better. There are some basic guidelines which you need to follow if you like to contribute to Navidrome.
- [Asking Support Questions](#asking-support-questions)
- [Code of Conduct](#code-of-conduct)
- [Issues](#issues)
- [Pull Requests](#pull-requests)
## Asking Support Questions
We have an active [discussion forum](https://github.com/navidrome/navidrome/discussions) where users and developers can ask questions. Please don't use the GitHub issue tracker to ask questions.
## Code of Conduct
Please read the following [Code of Conduct](https://github.com/navidrome/navidrome/blob/master/CODE_OF_CONDUCT.md).
## Issues
Found any issue or bug in our codebase? Have a great idea you want to propose or discuss with
the developers? You can help by submitting an [issue](https://github.com/navidrome/navidrome/issues/new/choose)
to the GitHub repository.
**Before opening a new issue, please check if the issue has not been already made by searching
the [issues](https://github.com/navidrome/navidrome/issues)**
## Pull requests
Before submitting a pull request, ensure that you go through the following:
- Open a corresponding issue for the Pull Request, if not existing. The issue can be opened following [these guidelines](#issues)
- Ensure that there is no open or closed Pull Request corresponding to your submission to avoid duplication of effort.
- Setup the [development environment](https://www.navidrome.org/docs/developers/dev-environment/)
- Create a new branch on your forked repo and make the changes in it. Naming conventions for branch are: `<Issue Title>/<Issue Number>`. Example:
```
git checkout -b adding-docs/834 master
```
- The commits should follow a [specific convention](#commit-conventions)
- Ensure that a DCO sign-off for commits is provided via `--signoff` option of git commit
- Provide a link to the issue that will be closed via your Pull request.
### Commit Conventions
Each commit message must adhere to the following format:
```
<type>(scope): <description> - <issue number>
[optional body]
```
This improves the readability of the messages
#### Type
It can be one of the following:
1. **feat**: Addition of a new feature
2. **fix**: Bug fix
3. **docs**: Documentation Changes
4. **style**: Changes to styling
5. **refactor**: Refactoring of code
6. **perf**: Code that affects performance
7. **test**: Updating or improving the current tests
8. **build**: Changes to Build process
9. **revert**: Reverting to a previous commit
10. **chore** : updating grunt tasks etc
If there is a breaking change in your Pull Request, please add `BREAKING CHANGE` in the optional body section
#### Scope
The file or folder where the changes are made. If there are more than one, you can mention any
#### Description
A short description of the issue
#### Issue number
The issue fixed by this Pull Request.
The body is optional. It may contain short description of changes made.
Following all the guidelines an ideal commit will look like:
```
git commit --signoff -m "feat(themes): New-theme - #834"
```
After committing, push your commits to your forked branch and create a Pull Request from there.
The Pull Request Title can be the same as `<type>(scope): <description> - <issue number>`
A demo layout of how the Pull request body can look:
```
Closes <Issue number along with link>
Description (What does the pull request do)
Changes (What changes were made )
Screenshots or Videos
Related Issues and Pull Requests(if any)
```

View File

@@ -1,55 +0,0 @@
#####################################################
### Build UI bundles
FROM node:13.7-alpine AS jsbuilder
WORKDIR /src
COPY ui/package.json ui/package-lock.json ./
RUN npm ci
COPY ui/ .
RUN npm run build
#####################################################
### Build executable
FROM golang:1.13-alpine AS gobuilder
# Download build tools
RUN mkdir -p /src/ui/build
RUN apk add -U --no-cache build-base git
RUN go get -u github.com/go-bindata/go-bindata/...
# Download and unpack static ffmpeg
ARG FFMPEG_VERSION=4.1.4
ARG FFMPEG_URL=https://www.johnvansickle.com/ffmpeg/old-releases/ffmpeg-${FFMPEG_VERSION}-amd64-static.tar.xz
RUN wget -O /tmp/ffmpeg.tar.xz ${FFMPEG_URL}
RUN cd /tmp && tar xJf ffmpeg.tar.xz && rm ffmpeg.tar.xz
# Download project dependencies
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
# Copy source and UI bundle, build executable
COPY . .
COPY --from=jsbuilder /src/build/* /src/ui/build/
COPY --from=jsbuilder /src/build/static/css/* /src/ui/build/static/css/
COPY --from=jsbuilder /src/build/static/js/* /src/ui/build/static/js/
RUN rm -rf /src/build/css /src/build/js
RUN go-bindata -fs -prefix ui/build -tags embed -nocompress -pkg assets -o assets/embedded_gen.go ui/build/...\
&& go build -ldflags="-X main.gitSha=${SOURCE_COMMIT} -X main.gitTag=${SOURCE_BRANCH}" -tags=embed
#####################################################
### Build Final Image
FROM alpine
COPY --from=gobuilder /src/navidrome /app/
COPY --from=gobuilder /tmp/ffmpeg*/ffmpeg /usr/bin/
VOLUME ["/data", "/music"]
ENV ND_MUSICFOLDER /music
ENV ND_DATAFOLDER /data
ENV ND_SCANINTERVAL 1m
ENV ND_LOGLEVEL info
ENV ND_PORT 4533
EXPOSE 4533
WORKDIR /app
CMD "/app/navidrome"

209
Makefile
View File

@@ -1,76 +1,171 @@
GO_VERSION=1.13
NODE_VERSION=v13.7.0
GO_VERSION=$(shell grep "^go " go.mod | cut -f 2 -d ' ')
NODE_VERSION=$(shell cat .nvmrc)
ifneq ("$(wildcard .git/HEAD)","")
GIT_SHA=$(shell git rev-parse --short HEAD)
GIT_TAG=$(shell git describe --tags `git rev-list --tags --max-count=1`)
else
GIT_SHA=source_archive
GIT_TAG=$(patsubst navidrome-%,v%,$(notdir $(PWD)))
endif
.PHONY: dev
dev: check_env data
@goreman -f Procfile.dev -b 4533 start
CI_RELEASER_VERSION=1.20.3-1 ## https://github.com/navidrome/ci-goreleaser
.PHONY: server
server: check_go_env data
@reflex -d none -c reflex.conf
.PHONY: watch
watch: check_go_env
ginkgo watch -notify ./...
.PHONY: test
test: check_go_env
go test ./... -v
# @(cd ./ui && npm test -- --watchAll=false)
.PHONY: testall
testall: check_go_env test
@(cd ./ui && npm test -- --watchAll=false)
.PHONY: setup
setup: Jamstash-master
@which reflex || (echo "Installing Reflex" && GO111MODULE=off go get -u github.com/cespare/reflex)
@which goconvey || (echo "Installing GoConvey" && GO111MODULE=off go get -u github.com/smartystreets/goconvey)
@which wire || (echo "Installing Wire" && GO111MODULE=off go get -u github.com/google/wire/cmd/wire)
@which goreman || (echo "Installing Goreman" && GO111MODULE=off go get -u github.com/mattn/goreman)
@which ginkgo || (echo "Installing Ginkgo" && GO111MODULE=off go get -u github.com/onsi/ginkgo/ginkgo)
@which go-bindata || (echo "Installing BinData" && GO111MODULE=off go get -u github.com/go-bindata/go-bindata/...)
go mod download
setup: check_env download-deps setup-git ##@1_Run_First Install dependencies and prepare development environment
@echo Downloading Node dependencies...
@(cd ./ui && npm ci)
.PHONY: setup
.PHONY: static
static:
cd static && go-bindata -fs -prefix "static" -nocompress -ignore="\\\*.go" -pkg static .
dev: check_env ##@Development Start Navidrome in development mode, with hot-reload for both frontend and backend
npx foreman -j Procfile.dev -p 4533 start
.PHONY: dev
Jamstash-master:
wget -N https://github.com/tsquillario/Jamstash/archive/master.zip
unzip -o master.zip
rm master.zip
server: check_go_env ##@Development Start the backend in development mode
@go run github.com/cespare/reflex@latest -d none -c reflex.conf
.PHONY: server
watch: ##@Development Start Go tests in watch mode (re-run when code changes)
go run github.com/onsi/ginkgo/v2/ginkgo@latest watch -notify ./...
.PHONY: watch
test: ##@Development Run Go tests
go test -race -shuffle=on ./...
.PHONY: test
testall: test ##@Development Run Go and JS tests
@(cd ./ui && npm test -- --watchAll=false)
.PHONY: testall
lint: ##@Development Lint Go code
go run github.com/golangci/golangci-lint/cmd/golangci-lint@latest run -v --timeout 5m
.PHONY: lint
lintall: lint ##@Development Lint Go and JS code
@(cd ./ui && npm run check-formatting) || (echo "\n\nPlease run 'npm run prettier' to fix formatting issues." && exit 1)
@(cd ./ui && npm run lint)
.PHONY: lintall
wire: check_go_env ##@Development Update Dependency Injection
go run github.com/google/wire/cmd/wire@latest ./...
.PHONY: wire
snapshots: ##@Development Update (GoLang) Snapshot tests
UPDATE_SNAPSHOTS=true go run github.com/onsi/ginkgo/v2/ginkgo@latest ./server/subsonic/...
.PHONY: snapshots
migration-sql: ##@Development Create an empty SQL migration file
@if [ -z "${name}" ]; then echo "Usage: make migration-sql name=name_of_migration_file"; exit 1; fi
go run github.com/pressly/goose/v3/cmd/goose@latest -dir db/migration create ${name} sql
.PHONY: migration
migration-go: ##@Development Create an empty Go migration file
@if [ -z "${name}" ]; then echo "Usage: make migration-go name=name_of_migration_file"; exit 1; fi
go run github.com/pressly/goose/v3/cmd/goose@latest -dir db/migration create ${name}
.PHONY: migration
setup-dev: setup
.PHONY: setup-dev
setup-git: ##@Development Setup Git hooks (pre-commit and pre-push)
@echo Setting up git hooks
@mkdir -p .git/hooks
@(cd .git/hooks && ln -sf ../../git/* .)
.PHONY: setup-git
buildall: buildjs build ##@Build Build the project, both frontend and backend
.PHONY: buildall
build: warning-noui-build check_go_env ##@Build Build only backend
go build -ldflags="-X github.com/navidrome/navidrome/consts.gitSha=$(GIT_SHA) -X github.com/navidrome/navidrome/consts.gitTag=$(GIT_TAG)-SNAPSHOT" -tags=netgo
.PHONY: build
buildjs: check_node_env ##@Build Build only frontend
@(cd ./ui && npm run build)
.PHONY: buildjs
all: warning-noui-build ##@Cross_Compilation Build binaries for all supported platforms. It does not build the frontend
docker run -t -v $(PWD):/workspace -w /workspace deluan/ci-goreleaser:$(CI_RELEASER_VERSION) \
goreleaser release --rm-dist --skip-publish --snapshot
.PHONY: all
single: warning-noui-build ##@Cross_Compilation Build binaries for a single supported platforms. It does not build the frontend
@if [ -z "${GOOS}" -o -z "${GOARCH}" ]; then \
echo "Usage: GOOS=<os> GOARCH=<arch> make single"; \
echo "Options:"; \
grep -- "- id: navidrome_" .goreleaser.yml | sed 's/- id: navidrome_//g'; \
exit 1; \
fi
@echo "Building binaries for ${GOOS}/${GOARCH}"
docker run -t -v $(PWD):/workspace -e GOOS -e GOARCH -w /workspace deluan/ci-goreleaser:$(CI_RELEASER_VERSION) \
goreleaser build --rm-dist --snapshot --single-target --id navidrome_${GOOS}_${GOARCH}
.PHONY: single
warning-noui-build:
@echo "WARNING: This command does not build the frontend, it uses the latest built with 'make buildjs'"
.PHONY: warning-noui-build
get-music: ##@Development Download some free music from Navidrome's demo instance
mkdir -p music
( cd music; \
curl "https://demo.navidrome.org/rest/download?u=demo&p=demo&f=json&v=1.8.0&c=dev_download&id=ec2093ec4801402f1e17cc462195cdbb" > brock.zip; \
curl "https://demo.navidrome.org/rest/download?u=demo&p=demo&f=json&v=1.8.0&c=NavidromeUI&id=b376eeb4652d2498aa2b25ba0696725e" > back_on_earth.zip; \
curl "https://demo.navidrome.org/rest/download?u=demo&p=demo&f=json&v=1.8.0&c=NavidromeUI&id=e49c609b542fc51899ee8b53aa858cb4" > ugress.zip; \
curl "https://demo.navidrome.org/rest/download?u=demo&p=demo&f=json&v=1.8.0&c=NavidromeUI&id=350bcab3a4c1d93869e39ce496464f03" > voodoocuts.zip; \
for file in *.zip; do unzip -n $${file}; done )
@echo "Done. Remember to set your MusicFolder to ./music"
.PHONY: get-music
##########################################
#### Miscellaneous
release:
@if [[ ! "${V}" =~ ^[0-9]+\.[0-9]+\.[0-9]+.*$$ ]]; then echo "Usage: make release V=X.X.X"; exit 1; fi
go mod tidy
@if [ -n "`git status -s`" ]; then echo "\n\nThere are pending changes. Please commit or stash first"; exit 1; fi
make pre-push
git tag v${V}
git push origin v${V} --no-verify
.PHONY: release
download-deps:
@echo Downloading Go dependencies...
@go mod download
@go mod tidy # To revert any changes made by the `go mod download` command
.PHONY: download-deps
.PHONE: check_env
check_env: check_go_env check_node_env
.PHONY: check_env
.PHONY: check_go_env
check_go_env:
@(hash go) || (echo "\nERROR: GO environment not setup properly!\n"; exit 1)
@go version | grep -q $(GO_VERSION) || (echo "\nERROR: Please upgrade your GO version\n"; exit 1)
@current_go_version=`go version | cut -d ' ' -f 3 | cut -c3-` && \
echo "$(GO_VERSION) $$current_go_version" | \
tr ' ' '\n' | sort -V | tail -1 | \
grep -q "^$${current_go_version}$$" || \
(echo "\nERROR: Please upgrade your GO version\nThis project requires at least the version $(GO_VERSION)"; exit 1)
.PHONY: check_go_env
.PHONY: check_node_env
check_node_env:
@(hash node) || (echo "\nERROR: Node environment not setup properly!\n"; exit 1)
@node --version | grep -q $(NODE_VERSION) || (echo "\nERROR: Please check your Node version. Should be $(NODE_VERSION)\n"; exit 1)
@current_node_version=`node --version` && \
echo "$(NODE_VERSION) $$current_node_version" | \
tr ' ' '\n' | sort -V | tail -1 | \
grep -q "^$${current_node_version}$$" || \
(echo "\nERROR: Please check your Node version. Should be at least $(NODE_VERSION)\n"; exit 1)
.PHONY: check_node_env
data:
mkdir data
pre-push: lintall testall
.PHONY: pre-push
UI_SRC = $(shell find ui/src ui/public -name "*.js")
ui/build: $(UI_SRC) $(UI_PUBLIC) ui/package-lock.json
@(cd ./ui && npm run build)
.DEFAULT_GOAL := help
assets/embedded_gen.go: ui/build
go-bindata -fs -prefix "ui/build" -tags embed -nocompress -pkg assets -o assets/embedded_gen.go ui/build/...
HELP_FUN = \
%help; while(<>){push@{$$help{$$2//'options'}},[$$1,$$3] \
if/^([\w-_]+)\s*:.*\#\#(?:@(\w+))?\s(.*)$$/}; \
print"$$_:\n", map" $$_->[0]".(" "x(20-length($$_->[0])))."$$_->[1]\n",\
@{$$help{$$_}},"\n" for sort keys %help; \
.PHONY: build
build: check_go_env
go build -ldflags="-X main.gitSha=$(GIT_SHA) -X main.gitTag=master"
.PHONY: buildall
buildall: check_go_env assets/embedded_gen.go
go build -ldflags="-X main.gitSha=$(GIT_SHA) -X main.gitTag=master" -tags=embed
help: ##@Miscellaneous Show this help
@echo "Usage: make [target] ...\n"
@perl -e '$(HELP_FUN)' $(MAKEFILE_LIST)

View File

@@ -1,2 +1,2 @@
JS: sh -c "cd ./ui && npm start"
GO: reflex -c reflex.conf
GO: go run github.com/cespare/reflex@latest -d none -c reflex.conf

163
README.md
View File

@@ -1,121 +1,78 @@
# Navidrome Music Streamer
<a href="https://www.navidrome.org"><img src="resources/logo-192x192.png" alt="Navidrome logo" title="navidrome" align="right" height="60px" /></a>
[![Build Status](https://github.com/deluan/navidrome/workflows/Build/badge.svg)](https://github.com/deluan/navidrome/actions)
# Navidrome Music Server &nbsp;[![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://twitter.com/intent/tweet?text=Tired%20of%20paying%20for%20music%20subscriptions%2C%20and%20not%20finding%20what%20you%20really%20like%3F%20Roll%20your%20own%20streaming%20service%21&url=https://navidrome.org&via=navidrome)
Navidrome is an open source web-based music collection server and streamer. It gives you freedom to listen to your
music collection from any browser or mobile device.
[![Last Release](https://img.shields.io/github/v/release/navidrome/navidrome?logo=github&label=latest&style=flat-square)](https://github.com/navidrome/navidrome/releases)
[![Build](https://img.shields.io/github/actions/workflow/status/navidrome/navidrome/pipeline.yml?branch=master&logo=github&style=flat-square)](https://nightly.link/navidrome/navidrome/workflows/pipeline/master)
[![Downloads](https://img.shields.io/github/downloads/navidrome/navidrome/total?logo=github&style=flat-square)](https://github.com/navidrome/navidrome/releases/latest)
[![Docker Pulls](https://img.shields.io/docker/pulls/deluan/navidrome?logo=docker&label=pulls&style=flat-square)](https://hub.docker.com/r/deluan/navidrome)
[![Dev Chat](https://img.shields.io/discord/671335427726114836?logo=discord&label=discord&style=flat-square)](https://discord.gg/xh7j7yF)
[![Subreddit](https://img.shields.io/reddit/subreddit-subscribers/navidrome?logo=reddit&label=/r/navidrome&style=flat-square)](https://www.reddit.com/r/navidrome/)
[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-v2.0-ff69b4.svg?style=flat-square)](CODE_OF_CONDUCT.md)
This is _alpha quality_ software. Expect some changes in the feature set and the way it works.
Navidrome is an open source web-based music collection server and streamer. It gives you freedom to listen to your
music collection from any browser or mobile device. It's like your personal Spotify!
**Note**: The `master` branch may be in an unstable or even broken state during development.
Please use [releases](https://github.com/navidrome/navidrome/releases) instead of
the `master` branch in order to get a stable set of binaries.
## [Check out our Live Demo!](https://www.navidrome.org/demo/)
__Any feedback is welcome!__ If you need/want a new feature, find a bug or think of any way to improve Navidrome,
please fill a [GitHub issue](https://github.com/deluan/navidrome/issues)
## Features
- Handles very large music collections
- Streams virtually any audio format available
- Reads and uses all your beautifully curated metadata (id3 tags)
- Multi-user, each user has their own play counts, playlists, favourites, etc..
- Very low resource usage: Ex: with a library of 300GB (~29000 songs), it uses less than 50MB of RAM
- Multi-platform, runs on macOS, Linux and Windows. Docker images are also provided
- Automatically monitors your library for changes, importing new files and reloading new metadata
- Modern and responsive Web interface based on Material UI, to manage users and browse your library
- Compatible with the huge selection of clients for [Subsonic](http://www.subsonic.org),
[Airsonic](https://airsonic.github.io/) and [Madsonic](https://www.madsonic.org/).
See the [complete list of available mobile and web apps](https://airsonic.github.io/docs/apps/)
## Road map
This project is being actively worked on. Expect a more polished experience and new features/releases
on a frequent basis. Some upcoming features planned:
- Transcoding/Downsampling on-the-fly
- Last.FM integration
- Integrated music player
- Pre-build binaries for all platforms, including Raspberry Pi
- Smart/dynamic playlists (similar to iTunes)
- Jukebox mode
- Sharing links to albums/songs/playlists
- Podcasts
please file a [GitHub issue](https://github.com/navidrome/navidrome/issues) or join the discussion in our
[Subreddit](https://www.reddit.com/r/navidrome/). If you want to contribute to the project in any other way
([ui/backend dev](https://www.navidrome.org/docs/developers/),
[translations](https://www.navidrome.org/docs/developers/translations/),
[themes](https://www.navidrome.org/docs/developers/creating-themes)), please join the chat in our
[Discord server](https://discord.gg/xh7j7yF).
## Installation
Various options are available:
See instructions on the [project's website](https://www.navidrome.org/docs/installation/)
### Pre-build executables
## Cloud Hosting
Just head to the [releases page](https://github.com/deluan/navidrome/releases) and download the latest version for you
platform. There are builds available for Linux, macOS and Windows (32 and 64 bits).
[PikaPods](https://www.pikapods.com) has partnered with us to offer you an
[officially supported, cloud-hosted solution](https://www.navidrome.org/docs/installation/managed/#pikapods).
A share of the revenue helps fund the development of Navidrome at no additional cost for you.
Remember to install [ffmpeg](https://ffmpeg.org/download.html) in your system, a requirement for Navidrome to work properly.
[![PikaPods](https://www.pikapods.com/static/run-button.svg)](https://www.pikapods.com/pods?run=navidrome)
If you have any issues with these binaries, or need a binary for a different platform, please
[open an issue](https://github.com/deluan/navidrome/issues)
## Features
- Handles very **large music collections**
- Streams virtually **any audio format** available
- Reads and uses all your beautifully curated **metadata**
- Great support for **compilations** (Various Artists albums) and **box sets** (multi-disc albums)
- **Multi-user**, each user has their own play counts, playlists, favourites, etc...
- Very **low resource usage**
- **Multi-platform**, runs on macOS, Linux and Windows. **Docker** images are also provided
- Ready to use binaries for all major platforms, including **Raspberry Pi**
- Automatically **monitors your library** for changes, importing new files and reloading new metadata
- **Themeable**, modern and responsive **Web interface** based on [Material UI](https://material-ui.com)
- **Compatible** with all Subsonic/Madsonic/Airsonic [clients](https://www.navidrome.org/docs/overview/#apps)
- **Transcoding** on the fly. Can be set per user/player. **Opus encoding is supported**
- Translated to **various languages**
### Docker
## Documentation
All documentation can be found in the project's website: https://www.navidrome.org/docs.
Here are some useful direct links:
Docker images are available. Example of usage:
```yaml
# This is just an example. Customize it to your needs.
version: "3"
services:
navidrome:
image: deluan/navidrome:latest
ports:
- "4533:4533"
environment:
# All options with their default values:
ND_MUSICFOLDER: /music
ND_DATAFOLDER: /data
ND_SCANINTERVAL: 1m
ND_LOGLEVEL: info
ND_PORT: 4533
volumes:
- "./data:/data"
- "/Users/deluan/Music/iTunes/iTunes Media/Music:/music"
```
### Build it yourself
You will need to install [Go 1.13](https://golang.org/dl/) and [Node 13.7](http://nodejs.org).
You'll also need [ffmpeg](https://ffmpeg.org) installed in your system
After the prerequisites above are installed, build the application with:
```
$ make setup
$ make buildall
```
This will generate the `navidrome` binary executable in the project's root folder.
### Running for the first time
Start the server with:
```shell script
./navidrome
```
The server should start listening for requests on the default port __4533__
After starting Navidrome for the first time, go to http://localhost:4533. It will ask you to create your first admin
user.
- [Overview](https://www.navidrome.org/docs/overview/)
- [Installation](https://www.navidrome.org/docs/installation/)
- [Docker](https://www.navidrome.org/docs/installation/docker/)
- [Binaries](https://www.navidrome.org/docs/installation/pre-built-binaries/)
- [Build from source](https://www.navidrome.org/docs/installation/build-from-source/)
- [Development](https://www.navidrome.org/docs/developers/)
- [Subsonic API Compatibility](https://www.navidrome.org/docs/developers/subsonic-api/)
## Screenshots
<p align="center">
<p float="left">
<img width="270" src="https://raw.githubusercontent.com/deluan/navidrome/master/.github/screenshots/screenshot-login-mobile.png">
<img width="270" src="https://raw.githubusercontent.com/deluan/navidrome/master/.github/screenshots/screenshot-mobile.png">
<img width="270" src="https://raw.githubusercontent.com/deluan/navidrome/master/.github/screenshots/screenshot-users-mobile.png">
<img width="900"src="https://raw.githubusercontent.com/deluan/navidrome/master/.github/screenshots/screenshot-desktop.png">
<p align="left">
<img height="550" src="https://raw.githubusercontent.com/navidrome/navidrome/master/.github/screenshots/ss-mobile-login.png">
<img height="550" src="https://raw.githubusercontent.com/navidrome/navidrome/master/.github/screenshots/ss-mobile-player.png">
<img height="550" src="https://raw.githubusercontent.com/navidrome/navidrome/master/.github/screenshots/ss-mobile-album-view.png">
<img width="550" src="https://raw.githubusercontent.com/navidrome/navidrome/master/.github/screenshots/ss-desktop-player.png">
</p>
</p>
## Subsonic API Version Compatibility
Check the up to date [compatibility table](https://github.com/deluan/navidrome/blob/master/API_COMPATIBILITY.md)
for the latest Subsonic features available.

View File

@@ -1,20 +0,0 @@
// +build !embed
package assets
import (
"net/http"
"sync"
"github.com/deluan/navidrome/consts"
"github.com/deluan/navidrome/log"
)
var once sync.Once
func AssetFile() http.FileSystem {
once.Do(func() {
log.Warn("Using external assets from " + consts.UIAssetsLocalPath)
})
return http.Dir(consts.UIAssetsLocalPath)
}

View File

@@ -1,36 +0,0 @@
package main
import (
"fmt"
"strings"
"github.com/deluan/navidrome/static"
)
var (
// This will be set in build time. If not, version will be set to "dev"
gitTag string
gitSha string
)
// Formats:
// dev
// v0.2.0 (5b84188)
// master (9ed35cb)
func getVersion() string {
if gitSha == "" {
return "dev"
}
return fmt.Sprintf("%s (%s)", gitTag, gitSha)
}
func getBanner() string {
data, _ := static.Asset("banner.txt")
return strings.TrimSuffix(string(data), "\n")
}
func ShowBanner() {
version := "Version: " + getVersion()
padding := strings.Repeat(" ", 52-len(version))
fmt.Printf("%s%s%s\n\n", getBanner(), padding, version)
}

View File

@@ -1,98 +0,0 @@
#!/bin/bash
# Script to transfort .itc files into images (JPG or PNG)
#
# .itc files are located in ~/Music/iTunes/Album Artwork
#
# This script uses (/!\ needs ) ImageMagick's convert, hexdump, printf and dd.
#
# This script might be a little slow, You might want to look at Simon Kennedy's work at http://www.sffjunkie.co.uk/python-itc.html
#
# ~/{Library Path}/Album Artwork/Cache/D989408F65D05F99/04/13/04/D989408F65D05F99-EB5B7A9086F4B4D4.itc
#
# The filenames are an amalgam of the library ID (D989408F65D05F99) and the track's ID (EB5B7A9086F4B4D4).
# The directory structure comes from the library ID and the last three digits of the track's ID converted to decimal,
# ie 4D4 becomes 04, 13, 04.
#
AlbumArtwork="${HOME}/Music/iTunes 1/Album Artwork"
DestinationDir="Artwork"
IFS=$'\n'
if [ ! -d "$DestinationDir" ]; then
mkdir "$DestinationDir"
echo "new Images dir"
fi
for file in `find "$AlbumArtwork" -name '*.itc'`; do
start=0x11C
exit=0;
i=1;
echo $file
while [ 1 ]; do
typeOffset=$(($start+0x30))
imageType=$(hexdump -n 4 -s $typeOffset -e '"0x"4/1 "%02x" "\n"' $file)
#If there is no next byte, jump to the next itc file.
if [[ -z $imageType ]]; then
break
fi
imageOffsetOffset=$(($start+8))
itemSize=$(hexdump -n 4 -s $start -e '"0x"4/1 "%02x" "\n"' $file)
imageOffset=$(hexdump -n 4 -s $imageOffsetOffset -e '"0x"4/1 "%02x" "\n"' $file)
imageStart=$(($start+$imageOffset))
imageSize=$(($itemSize-imageOffset))
imageWidth=$(hexdump -n 4 -s $(($start+56)) -e '"0x"4/1 "%02x" "\n"' $file)
imageWidth=$(printf "%d" $imageWidth)
imageHeight=$(hexdump -n 4 -s $(($start+60)) -e '"0x"4/1 "%02x" "\n"' $file)
imageHeight=$(printf "%d" $imageHeight)
dir=$(dirname "$file")
xbase=${file##*/} #file.etc
xpref=${xbase%.*} #file prefix
#echo $file
#echo itemsize $itemSize
#echo start $start
#echo imageOffset $imageOffset
#echo imageStart $imageStart
#echo imageSize $imageSize
#echo imageWidth $imageWidth
#echo imageHeight $imageHeight
if [[ $imageType -eq 0x504E4766 ]] || [[ $imageType -eq 0x0000000E ]] ; then
targetFile="$DestinationDir/$xpref-$i.png"
if [ ! -f "$targetFile" ]; then
echo PNG
dd skip=$imageStart count=$imageSize if="$file" of="$targetFile" bs=1 &> /dev/null
fi
elif [[ $imageType -eq 0x41524762 ]] ; then
targetFile="$DestinationDir/$xpref-$i.png"
if [ ! -f "$targetFile" ]; then
echo ARGB
dd skip=$imageStart count=$imageSize if="$file" of="$TMPDIR/test$i" bs=1 &> /dev/null
#Using a matrix to convert ARGB to RGBA since imagemagick does only support rgba input
convert -size $imageWidth"x"$imageHeight -depth 8 -color-matrix '0 1 0 0 0 0 1 0 0 0 0 1 1 0 0 0' rgba:"$TMPDIR/test$i" "$targetFile"
fi
elif [[ $imageType -eq 0x0000000D ]] ; then
targetFile="$DestinationDir/$xpref-$i.jpg"
if [ ! -f "$targetFile" ]; then
echo JPG
dd skip=$imageStart count=$imageSize if="$file" of="$targetFile" bs=1 &> /dev/null
fi
else
echo $imageType
exit=1
break;
fi
start=$(($start+$itemSize))
i=$(($i+1))
done
done

View File

@@ -1,14 +0,0 @@
#!/usr/bin/env bash
gofmtcmd=`which goimports || echo "gofmt"`
gofiles=$(git diff --name-only --diff-filter=ACM | grep '.go$')
[ -z "$gofiles" ] && exit 0
unformatted=`$gofmtcmd -l $gofiles`
[ -z "$unformatted" ] && exit 0
for f in $unformatted; do
$gofmtcmd -w -l "$f"
gofmt -s -w -l "$f"
done

72
cmd/pls.go Normal file
View File

@@ -0,0 +1,72 @@
package cmd
import (
"context"
"errors"
"os"
"github.com/Masterminds/squirrel"
"github.com/navidrome/navidrome/core/auth"
"github.com/navidrome/navidrome/db"
"github.com/navidrome/navidrome/log"
"github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/persistence"
"github.com/spf13/cobra"
)
var (
playlistID string
outputFile string
)
func init() {
plsCmd.Flags().StringVarP(&playlistID, "playlist", "p", "", "playlist name or ID")
plsCmd.Flags().StringVarP(&outputFile, "output", "o", "", "output file (default stdout)")
_ = plsCmd.MarkFlagRequired("playlist")
rootCmd.AddCommand(plsCmd)
}
var plsCmd = &cobra.Command{
Use: "playlists",
Aliases: []string{"pls", "playlist"},
Short: "Export playlists",
Long: "Export Navidrome playlists to M3U files",
Run: func(cmd *cobra.Command, args []string) {
runExporter()
},
}
func runExporter() {
sqlDB := db.Db()
ds := persistence.New(sqlDB)
ctx := auth.WithAdminUser(context.Background(), ds)
playlist, err := ds.Playlist(ctx).GetWithTracks(playlistID, true)
if err != nil && !errors.Is(err, model.ErrNotFound) {
log.Fatal("Error retrieving playlist", "name", playlistID, err)
}
if errors.Is(err, model.ErrNotFound) {
playlists, err := ds.Playlist(ctx).GetAll(model.QueryOptions{Filters: squirrel.Eq{"playlist.name": playlistID}})
if err != nil {
log.Fatal("Error retrieving playlist", "name", playlistID, err)
}
if len(playlists) > 0 {
playlist, err = ds.Playlist(ctx).GetWithTracks(playlists[0].ID, true)
if err != nil {
log.Fatal("Error retrieving playlist", "name", playlistID, err)
}
}
}
if playlist == nil {
log.Fatal("Playlist not found", "name", playlistID)
}
pls := playlist.ToM3U8()
if outputFile == "-" || outputFile == "" {
println(pls)
return
}
err = os.WriteFile(outputFile, []byte(pls), 0600)
if err != nil {
log.Fatal("Error writing to the output file", "file", outputFile, err)
}
}

198
cmd/root.go Normal file
View File

@@ -0,0 +1,198 @@
package cmd
import (
"context"
"errors"
"fmt"
"os"
"strings"
"time"
"github.com/go-chi/chi/v5/middleware"
"github.com/navidrome/navidrome/conf"
"github.com/navidrome/navidrome/consts"
"github.com/navidrome/navidrome/core"
"github.com/navidrome/navidrome/db"
"github.com/navidrome/navidrome/log"
"github.com/navidrome/navidrome/resources"
"github.com/navidrome/navidrome/scheduler"
"github.com/navidrome/navidrome/server/backgrounds"
"github.com/prometheus/client_golang/prometheus/promhttp"
"golang.org/x/sync/errgroup"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var interrupted = errors.New("service was interrupted")
var (
cfgFile string
noBanner bool
rootCmd = &cobra.Command{
Use: "navidrome",
Short: "Navidrome is a self-hosted music server and streamer",
Long: `Navidrome is a self-hosted music server and streamer.
Complete documentation is available at https://www.navidrome.org/docs`,
PersistentPreRun: func(cmd *cobra.Command, args []string) {
preRun()
},
Run: func(cmd *cobra.Command, args []string) {
runNavidrome(context.Background())
},
Version: consts.Version,
}
)
func Execute() {
rootCmd.SetVersionTemplate(`{{println .Version}}`)
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
func preRun() {
if !noBanner {
println(resources.Banner())
}
conf.Load()
}
func runNavidrome(ctx context.Context) {
db.Init()
defer func() {
if err := db.Close(); err != nil {
log.Error("Error closing DB", err)
}
log.Info("Navidrome stopped, bye.")
}()
g, ctx := errgroup.WithContext(ctx)
g.Go(startServer(ctx))
g.Go(startSignaler(ctx))
g.Go(startScheduler(ctx))
g.Go(schedulePeriodicScan(ctx))
if err := g.Wait(); err != nil && !errors.Is(err, interrupted) {
log.Error("Fatal error in Navidrome. Aborting", err)
}
}
func startServer(ctx context.Context) func() error {
return func() error {
a := CreateServer(conf.Server.MusicFolder)
a.MountRouter("Native API", consts.URLPathNativeAPI, CreateNativeAPIRouter())
a.MountRouter("Subsonic API", consts.URLPathSubsonicAPI, CreateSubsonicAPIRouter())
a.MountRouter("Public Endpoints", consts.URLPathPublic, CreatePublicRouter())
if conf.Server.LastFM.Enabled {
a.MountRouter("LastFM Auth", consts.URLPathNativeAPI+"/lastfm", CreateLastFMRouter())
}
if conf.Server.ListenBrainz.Enabled {
a.MountRouter("ListenBrainz Auth", consts.URLPathNativeAPI+"/listenbrainz", CreateListenBrainzRouter())
}
if conf.Server.Prometheus.Enabled {
// blocking call because takes <1ms but useful if fails
core.WriteInitialMetrics()
a.MountRouter("Prometheus metrics", conf.Server.Prometheus.MetricsPath, promhttp.Handler())
}
if conf.Server.DevEnableProfiler {
a.MountRouter("Profiling", "/debug", middleware.Profiler())
}
if strings.HasPrefix(conf.Server.UILoginBackgroundURL, "/") {
a.MountRouter("Background images", consts.DefaultUILoginBackgroundURL, backgrounds.NewHandler())
}
return a.Run(ctx, conf.Server.Address, conf.Server.Port, conf.Server.TLSCert, conf.Server.TLSKey)
}
}
func schedulePeriodicScan(ctx context.Context) func() error {
return func() error {
schedule := conf.Server.ScanSchedule
if schedule == "" {
log.Warn("Periodic scan is DISABLED")
return nil
}
scanner := GetScanner()
schedulerInstance := scheduler.GetInstance()
log.Info("Scheduling periodic scan", "schedule", schedule)
err := schedulerInstance.Add(schedule, func() {
_ = scanner.RescanAll(ctx, false)
})
if err != nil {
log.Error("Error scheduling periodic scan", err)
}
time.Sleep(2 * time.Second) // Wait 2 seconds before the initial scan
log.Debug("Executing initial scan")
if err := scanner.RescanAll(ctx, false); err != nil {
log.Error("Error executing initial scan", err)
}
log.Debug("Finished initial scan")
return nil
}
}
func startScheduler(ctx context.Context) func() error {
log.Info(ctx, "Starting scheduler")
schedulerInstance := scheduler.GetInstance()
return func() error {
schedulerInstance.Run(ctx)
return nil
}
}
// TODO: Implement some struct tags to map flags to viper
func init() {
cobra.OnInitialize(func() {
conf.InitConfig(cfgFile)
})
rootCmd.PersistentFlags().StringVarP(&cfgFile, "configfile", "c", "", `config file (default "./navidrome.toml")`)
rootCmd.PersistentFlags().BoolVarP(&noBanner, "nobanner", "n", false, `don't show banner`)
rootCmd.PersistentFlags().String("musicfolder", viper.GetString("musicfolder"), "folder where your music is stored")
rootCmd.PersistentFlags().String("datafolder", viper.GetString("datafolder"), "folder to store application data (DB, cache...), needs write access")
rootCmd.PersistentFlags().StringP("loglevel", "l", viper.GetString("loglevel"), "log level, possible values: error, info, debug, trace")
_ = viper.BindPFlag("musicfolder", rootCmd.PersistentFlags().Lookup("musicfolder"))
_ = viper.BindPFlag("datafolder", rootCmd.PersistentFlags().Lookup("datafolder"))
_ = viper.BindPFlag("loglevel", rootCmd.PersistentFlags().Lookup("loglevel"))
rootCmd.Flags().StringP("address", "a", viper.GetString("address"), "IP address to bind to")
rootCmd.Flags().IntP("port", "p", viper.GetInt("port"), "HTTP port Navidrome will listen to")
rootCmd.Flags().String("baseurl", viper.GetString("baseurl"), "base URL to configure Navidrome behind a proxy (ex: /music or http://my.server.com)")
rootCmd.Flags().String("tlscert", viper.GetString("tlscert"), "optional path to a TLS cert file (enables HTTPS listening)")
rootCmd.Flags().String("tlskey", viper.GetString("tlskey"), "optional path to a TLS key file (enables HTTPS listening)")
rootCmd.Flags().Duration("sessiontimeout", viper.GetDuration("sessiontimeout"), "how long Navidrome will wait before closing web ui idle sessions")
rootCmd.Flags().Duration("scaninterval", viper.GetDuration("scaninterval"), "how frequently to scan for changes in your music library")
rootCmd.Flags().String("uiloginbackgroundurl", viper.GetString("uiloginbackgroundurl"), "URL to a backaground image used in the Login page")
rootCmd.Flags().Bool("enabletranscodingconfig", viper.GetBool("enabletranscodingconfig"), "enables transcoding configuration in the UI")
rootCmd.Flags().String("transcodingcachesize", viper.GetString("transcodingcachesize"), "size of transcoding cache")
rootCmd.Flags().String("imagecachesize", viper.GetString("imagecachesize"), "size of image (art work) cache. set to 0 to disable cache")
rootCmd.Flags().Bool("autoimportplaylists", viper.GetBool("autoimportplaylists"), "enable/disable .m3u playlist auto-import`")
rootCmd.Flags().Bool("prometheus.enabled", viper.GetBool("prometheus.enabled"), "enable/disable prometheus metrics endpoint`")
rootCmd.Flags().String("prometheus.metricspath", viper.GetString("prometheus.metricspath"), "http endpoint for prometheus metrics")
_ = viper.BindPFlag("address", rootCmd.Flags().Lookup("address"))
_ = viper.BindPFlag("port", rootCmd.Flags().Lookup("port"))
_ = viper.BindPFlag("tlscert", rootCmd.Flags().Lookup("tlscert"))
_ = viper.BindPFlag("tlskey", rootCmd.Flags().Lookup("tlskey"))
_ = viper.BindPFlag("baseurl", rootCmd.Flags().Lookup("baseurl"))
_ = viper.BindPFlag("sessiontimeout", rootCmd.Flags().Lookup("sessiontimeout"))
_ = viper.BindPFlag("scaninterval", rootCmd.Flags().Lookup("scaninterval"))
_ = viper.BindPFlag("uiloginbackgroundurl", rootCmd.Flags().Lookup("uiloginbackgroundurl"))
_ = viper.BindPFlag("prometheus.enabled", rootCmd.Flags().Lookup("prometheus.enabled"))
_ = viper.BindPFlag("prometheus.metricspath", rootCmd.Flags().Lookup("prometheus.metricspath"))
_ = viper.BindPFlag("enabletranscodingconfig", rootCmd.Flags().Lookup("enabletranscodingconfig"))
_ = viper.BindPFlag("transcodingcachesize", rootCmd.Flags().Lookup("transcodingcachesize"))
_ = viper.BindPFlag("imagecachesize", rootCmd.Flags().Lookup("imagecachesize"))
}

34
cmd/scan.go Normal file
View File

@@ -0,0 +1,34 @@
package cmd
import (
"context"
"github.com/navidrome/navidrome/log"
"github.com/spf13/cobra"
)
var fullRescan bool
func init() {
scanCmd.Flags().BoolVarP(&fullRescan, "full", "f", false, "check all subfolders, ignoring timestamps")
rootCmd.AddCommand(scanCmd)
}
var scanCmd = &cobra.Command{
Use: "scan",
Short: "Scan music folder",
Long: "Scan music folder for updates",
Run: func(cmd *cobra.Command, args []string) {
runScanner()
},
}
func runScanner() {
scanner := GetScanner()
_ = scanner.RescanAll(context.Background(), fullRescan)
if fullRescan {
log.Info("Finished full rescan")
} else {
log.Info("Finished rescan")
}
}

27
cmd/signaler_nonunix.go Normal file
View File

@@ -0,0 +1,27 @@
//go:build windows || plan9
package cmd
import (
"context"
"os"
"os/signal"
"github.com/navidrome/navidrome/log"
)
func startSignaler(ctx context.Context) func() error {
log.Info(ctx, "Starting signaler")
return func() error {
var sigChan = make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt)
select {
case sig := <-sigChan:
log.Info(ctx, "Received termination signal", "signal", sig)
return interrupted
case <-ctx.Done():
return nil
}
}
}

51
cmd/signaler_unix.go Normal file
View File

@@ -0,0 +1,51 @@
//go:build !windows && !plan9
package cmd
import (
"context"
"os"
"os/signal"
"syscall"
"time"
"github.com/navidrome/navidrome/log"
)
const triggerScanSignal = syscall.SIGUSR1
func startSignaler(ctx context.Context) func() error {
log.Info(ctx, "Starting signaler")
scanner := GetScanner()
return func() error {
var sigChan = make(chan os.Signal, 1)
signal.Notify(
sigChan,
os.Interrupt,
triggerScanSignal,
syscall.SIGHUP,
syscall.SIGTERM,
syscall.SIGABRT,
)
for {
select {
case sig := <-sigChan:
if sig != triggerScanSignal {
log.Info(ctx, "Received termination signal", "signal", sig)
return interrupted
}
log.Info(ctx, "Received signal, triggering a new scan", "signal", sig)
start := time.Now()
err := scanner.RescanAll(ctx, false)
if err != nil {
log.Error(ctx, "Error scanning", err)
}
log.Info(ctx, "Triggered scan complete", "elapsed", time.Since(start).Round(100*time.Millisecond))
case <-ctx.Done():
return nil
}
}
}
}

191
cmd/svc.go Normal file
View File

@@ -0,0 +1,191 @@
package cmd
import (
"context"
"fmt"
"os"
"path/filepath"
"sync"
"github.com/kardianos/service"
"github.com/navidrome/navidrome/conf"
"github.com/navidrome/navidrome/log"
"github.com/spf13/cobra"
)
var (
svcStatusLabels = map[service.Status]string{
service.StatusUnknown: "Unknown",
service.StatusStopped: "Stopped",
service.StatusRunning: "Running",
}
)
func init() {
svcCmd.AddCommand(buildInstallCmd())
svcCmd.AddCommand(buildUninstallCmd())
svcCmd.AddCommand(buildStartCmd())
svcCmd.AddCommand(buildStopCmd())
svcCmd.AddCommand(buildStatusCmd())
rootCmd.AddCommand(svcCmd)
}
var svcCmd = &cobra.Command{
Use: "service",
Aliases: []string{"svc"},
Short: "Manage Navidrome as a service",
Long: fmt.Sprintf("Manage Navidrome as a service, using the OS service manager (%s)", service.Platform()),
Run: runServiceCmd,
}
type svcControl struct {
ctx context.Context
cancel context.CancelFunc
}
func (p *svcControl) Start(_ service.Service) error {
p.ctx, p.cancel = context.WithCancel(context.Background())
go p.run()
return nil
}
func (p *svcControl) run() {
runNavidrome(p.ctx)
}
func (p *svcControl) Stop(_ service.Service) error {
log.Info("Stopping service")
p.cancel()
return nil
}
var (
svc service.Service
svcOnce = sync.Once{}
)
func svcInstance() service.Service {
svcOnce.Do(func() {
options := make(service.KeyValue)
options["Restart"] = "on-success"
options["SuccessExitStatus"] = "1 2 8 SIGKILL"
options["UserService"] = true
options["LogDirectory"] = conf.Server.DataFolder
svcConfig := &service.Config{
Name: "Navidrome",
DisplayName: "Navidrome",
Description: "Navidrome is a self-hosted music server and streamer",
Dependencies: []string{
"Requires=network.target",
"After=network-online.target syslog.target"},
WorkingDirectory: executablePath(),
Option: options,
}
if conf.Server.ConfigFile != "" {
svcConfig.Arguments = []string{"-c", conf.Server.ConfigFile}
}
prg := &svcControl{}
var err error
svc, err = service.New(prg, svcConfig)
if err != nil {
log.Fatal(err)
}
})
return svc
}
func runServiceCmd(cmd *cobra.Command, _ []string) {
_ = cmd.Help()
}
func executablePath() string {
ex, err := os.Executable()
if err != nil {
log.Fatal(err)
}
return filepath.Dir(ex)
}
func buildInstallCmd() *cobra.Command {
runInstallCmd := func(_ *cobra.Command, _ []string) {
var err error
println("Installing service with:")
println(" working directory: " + executablePath())
println(" music folder: " + conf.Server.MusicFolder)
println(" data folder: " + conf.Server.DataFolder)
if cfgFile != "" {
conf.Server.ConfigFile, err = filepath.Abs(cfgFile)
if err != nil {
log.Fatal(err)
}
println(" config file: " + conf.Server.ConfigFile)
}
err = svcInstance().Install()
if err != nil {
log.Fatal(err)
}
println("Service installed. Use 'navidrome svc start' to start it.")
}
return &cobra.Command{
Use: "install",
Short: "Install Navidrome service.",
Run: runInstallCmd,
}
}
func buildUninstallCmd() *cobra.Command {
return &cobra.Command{
Use: "uninstall",
Short: "Uninstall Navidrome service. Does not delete the music or data folders",
Run: func(cmd *cobra.Command, args []string) {
err := svcInstance().Uninstall()
if err != nil {
log.Fatal(err)
}
println("Service uninstalled. Music and data folders are still intact.")
},
}
}
func buildStartCmd() *cobra.Command {
return &cobra.Command{
Use: "start",
Short: "Start Navidrome service",
Run: func(cmd *cobra.Command, args []string) {
err := svcInstance().Start()
if err != nil {
log.Fatal(err)
}
println("Service started. Use 'navidrome svc status' to check its status.")
},
}
}
func buildStopCmd() *cobra.Command {
return &cobra.Command{
Use: "stop",
Short: "Stop Navidrome service",
Run: func(cmd *cobra.Command, args []string) {
err := svcInstance().Stop()
if err != nil {
log.Fatal(err)
}
println("Service stopped. Use 'navidrome svc status' to check its status.")
},
}
}
func buildStatusCmd() *cobra.Command {
return &cobra.Command{
Use: "status",
Short: "Show Navidrome service status",
Run: func(cmd *cobra.Command, args []string) {
status, err := svcInstance().Status()
if err != nil {
log.Fatal(err)
}
fmt.Printf("Navidrome is %s.\n", svcStatusLabels[status])
},
}
}

128
cmd/wire_gen.go Normal file
View File

@@ -0,0 +1,128 @@
// Code generated by Wire. DO NOT EDIT.
//go:generate go run github.com/google/wire/cmd/wire
//go:build !wireinject
// +build !wireinject
package cmd
import (
"github.com/google/wire"
"github.com/navidrome/navidrome/core"
"github.com/navidrome/navidrome/core/agents"
"github.com/navidrome/navidrome/core/agents/lastfm"
"github.com/navidrome/navidrome/core/agents/listenbrainz"
"github.com/navidrome/navidrome/core/artwork"
"github.com/navidrome/navidrome/core/ffmpeg"
"github.com/navidrome/navidrome/core/scrobbler"
"github.com/navidrome/navidrome/db"
"github.com/navidrome/navidrome/persistence"
"github.com/navidrome/navidrome/scanner"
"github.com/navidrome/navidrome/server"
"github.com/navidrome/navidrome/server/events"
"github.com/navidrome/navidrome/server/nativeapi"
"github.com/navidrome/navidrome/server/public"
"github.com/navidrome/navidrome/server/subsonic"
"sync"
)
// Injectors from wire_injectors.go:
func CreateServer(musicFolder string) *server.Server {
sqlDB := db.Db()
dataStore := persistence.New(sqlDB)
broker := events.GetBroker()
serverServer := server.New(dataStore, broker)
return serverServer
}
func CreateNativeAPIRouter() *nativeapi.Router {
sqlDB := db.Db()
dataStore := persistence.New(sqlDB)
share := core.NewShare(dataStore)
router := nativeapi.New(dataStore, share)
return router
}
func CreateSubsonicAPIRouter() *subsonic.Router {
sqlDB := db.Db()
dataStore := persistence.New(sqlDB)
fileCache := artwork.GetImageCache()
fFmpeg := ffmpeg.New()
agentsAgents := agents.New(dataStore)
externalMetadata := core.NewExternalMetadata(dataStore, agentsAgents)
artworkArtwork := artwork.NewArtwork(dataStore, fileCache, fFmpeg, externalMetadata)
transcodingCache := core.GetTranscodingCache()
mediaStreamer := core.NewMediaStreamer(dataStore, fFmpeg, transcodingCache)
share := core.NewShare(dataStore)
archiver := core.NewArchiver(mediaStreamer, dataStore, share)
players := core.NewPlayers(dataStore)
scanner := GetScanner()
broker := events.GetBroker()
playlists := core.NewPlaylists(dataStore)
playTracker := scrobbler.GetPlayTracker(dataStore, broker)
router := subsonic.New(dataStore, artworkArtwork, mediaStreamer, archiver, players, externalMetadata, scanner, broker, playlists, playTracker, share)
return router
}
func CreatePublicRouter() *public.Router {
sqlDB := db.Db()
dataStore := persistence.New(sqlDB)
fileCache := artwork.GetImageCache()
fFmpeg := ffmpeg.New()
agentsAgents := agents.New(dataStore)
externalMetadata := core.NewExternalMetadata(dataStore, agentsAgents)
artworkArtwork := artwork.NewArtwork(dataStore, fileCache, fFmpeg, externalMetadata)
transcodingCache := core.GetTranscodingCache()
mediaStreamer := core.NewMediaStreamer(dataStore, fFmpeg, transcodingCache)
share := core.NewShare(dataStore)
archiver := core.NewArchiver(mediaStreamer, dataStore, share)
router := public.New(dataStore, artworkArtwork, mediaStreamer, share, archiver)
return router
}
func CreateLastFMRouter() *lastfm.Router {
sqlDB := db.Db()
dataStore := persistence.New(sqlDB)
router := lastfm.NewRouter(dataStore)
return router
}
func CreateListenBrainzRouter() *listenbrainz.Router {
sqlDB := db.Db()
dataStore := persistence.New(sqlDB)
router := listenbrainz.NewRouter(dataStore)
return router
}
func createScanner() scanner.Scanner {
sqlDB := db.Db()
dataStore := persistence.New(sqlDB)
playlists := core.NewPlaylists(dataStore)
fileCache := artwork.GetImageCache()
fFmpeg := ffmpeg.New()
agentsAgents := agents.New(dataStore)
externalMetadata := core.NewExternalMetadata(dataStore, agentsAgents)
artworkArtwork := artwork.NewArtwork(dataStore, fileCache, fFmpeg, externalMetadata)
cacheWarmer := artwork.NewCacheWarmer(artworkArtwork, fileCache)
broker := events.GetBroker()
scannerScanner := scanner.New(dataStore, playlists, cacheWarmer, broker)
return scannerScanner
}
// wire_injectors.go:
var allProviders = wire.NewSet(core.Set, artwork.Set, subsonic.New, nativeapi.New, public.New, persistence.New, lastfm.NewRouter, listenbrainz.NewRouter, events.GetBroker, db.Db)
// Scanner must be a Singleton
var (
onceScanner sync.Once
scannerInstance scanner.Scanner
)
func GetScanner() scanner.Scanner {
onceScanner.Do(func() {
scannerInstance = createScanner()
})
return scannerInstance
}

92
cmd/wire_injectors.go Normal file
View File

@@ -0,0 +1,92 @@
//go:build wireinject
package cmd
import (
"sync"
"github.com/google/wire"
"github.com/navidrome/navidrome/core"
"github.com/navidrome/navidrome/core/agents/lastfm"
"github.com/navidrome/navidrome/core/agents/listenbrainz"
"github.com/navidrome/navidrome/core/artwork"
"github.com/navidrome/navidrome/db"
"github.com/navidrome/navidrome/persistence"
"github.com/navidrome/navidrome/scanner"
"github.com/navidrome/navidrome/server"
"github.com/navidrome/navidrome/server/events"
"github.com/navidrome/navidrome/server/nativeapi"
"github.com/navidrome/navidrome/server/public"
"github.com/navidrome/navidrome/server/subsonic"
)
var allProviders = wire.NewSet(
core.Set,
artwork.Set,
subsonic.New,
nativeapi.New,
public.New,
persistence.New,
lastfm.NewRouter,
listenbrainz.NewRouter,
events.GetBroker,
db.Db,
)
func CreateServer(musicFolder string) *server.Server {
panic(wire.Build(
server.New,
allProviders,
))
}
func CreateNativeAPIRouter() *nativeapi.Router {
panic(wire.Build(
allProviders,
))
}
func CreateSubsonicAPIRouter() *subsonic.Router {
panic(wire.Build(
allProviders,
GetScanner,
))
}
func CreatePublicRouter() *public.Router {
panic(wire.Build(
allProviders,
))
}
func CreateLastFMRouter() *lastfm.Router {
panic(wire.Build(
allProviders,
))
}
func CreateListenBrainzRouter() *listenbrainz.Router {
panic(wire.Build(
allProviders,
))
}
// Scanner must be a Singleton
var (
onceScanner sync.Once
scannerInstance scanner.Scanner
)
func GetScanner() scanner.Scanner {
onceScanner.Do(func() {
scannerInstance = createScanner()
})
return scannerInstance
}
func createScanner() scanner.Scanner {
panic(wire.Build(
allProviders,
scanner.New,
))
}

View File

@@ -0,0 +1,10 @@
package configtest
import "github.com/navidrome/navidrome/conf"
func SetupConfig() func() {
oldValues := *conf.Server
return func() {
conf.Server = &oldValues
}
}

View File

@@ -1,93 +1,358 @@
package conf
import (
"flag"
"fmt"
"net/url"
"os"
"path/filepath"
"runtime"
"strings"
"time"
"github.com/deluan/navidrome/consts"
"github.com/deluan/navidrome/log"
"github.com/koding/multiconfig"
"github.com/kr/pretty"
"github.com/navidrome/navidrome/consts"
"github.com/navidrome/navidrome/log"
"github.com/navidrome/navidrome/utils/number"
"github.com/robfig/cron/v3"
"github.com/spf13/viper"
)
type nd struct {
Port string `default:"4533"`
MusicFolder string `default:"./music"`
DataFolder string `default:"./"`
DbPath string
LogLevel string `default:"info"`
type configOptions struct {
ConfigFile string
Address string
Port int
MusicFolder string
DataFolder string
DbPath string
LogLevel string
ScanInterval time.Duration
ScanSchedule string
SessionTimeout time.Duration
BaseURL string
BasePath string
BaseHost string
BaseScheme string
TLSCert string
TLSKey string
UILoginBackgroundURL string
UIWelcomeMessage string
MaxSidebarPlaylists int
EnableTranscodingConfig bool
EnableDownloads bool
EnableExternalServices bool
EnableMediaFileCoverArt bool
TranscodingCacheSize string
ImageCacheSize string
EnableArtworkPrecache bool
AutoImportPlaylists bool
PlaylistsPath string
AutoTranscodeDownload bool
DefaultDownsamplingFormat string
SearchFullString bool
RecentlyAddedByModTime bool
IgnoredArticles string
IndexGroups string
SubsonicArtistParticipations bool
FFmpegPath string
CoverArtPriority string
CoverJpegQuality int
ArtistArtPriority string
EnableGravatar bool
EnableFavourites bool
EnableStarRating bool
EnableUserEditing bool
EnableSharing bool
DefaultDownloadableShare bool
DefaultTheme string
DefaultLanguage string
DefaultUIVolume int
EnableReplayGain bool
EnableCoverAnimation bool
GATrackingID string
EnableLogRedacting bool
AuthRequestLimit int
AuthWindowLength time.Duration
PasswordEncryptionKey string
ReverseProxyUserHeader string
ReverseProxyWhitelist string
Prometheus prometheusOptions
Scanner scannerOptions
IgnoredArticles string `default:"The El La Los Las Le Les Os As O A"`
IndexGroups string `default:"A B C D E F G H I J K L M N O P Q R S T U V W X-Z(XYZ) [Unknown]([)"`
DisableDownsampling bool `default:"false"`
DownsampleCommand string `default:"ffmpeg -i %s -map 0:0 -b:a %bk -v 0 -f mp3 -"`
ProbeCommand string `default:"ffmpeg %s -f ffmetadata"`
ScanInterval string `default:"1m"`
Agents string
LastFM lastfmOptions
Spotify spotifyOptions
ListenBrainz listenBrainzOptions
// DevFlags. These are used to enable/disable debugging and incomplete features
DevDisableAuthentication bool `default:"false"`
DevDisableBanner bool `default:"false"`
DevLogSourceLine bool
DevLogLevels map[string]string
DevEnableProfiler bool
DevAutoCreateAdminPassword string
DevAutoLoginUsername string
DevActivityPanel bool
DevSidebarPlaylists bool
DevEnableBufferedScrobble bool
DevShowArtistPage bool
DevArtworkMaxRequests int
DevArtworkThrottleBacklogLimit int
DevArtworkThrottleBacklogTimeout time.Duration
DevArtistInfoTimeToLive time.Duration
DevAlbumInfoTimeToLive time.Duration
}
var Server = &nd{}
func newWithPath(path string, skipFlags ...bool) *multiconfig.DefaultLoader {
var loaders []multiconfig.Loader
// Read default values defined via tag fields "default"
loaders = append(loaders, &multiconfig.TagLoader{})
if _, err := os.Stat(path); err == nil {
if strings.HasSuffix(path, "toml") {
loaders = append(loaders, &multiconfig.TOMLLoader{Path: path})
}
if strings.HasSuffix(path, "json") {
loaders = append(loaders, &multiconfig.JSONLoader{Path: path})
}
if strings.HasSuffix(path, "yml") || strings.HasSuffix(path, "yaml") {
loaders = append(loaders, &multiconfig.YAMLLoader{Path: path})
}
} else {
println("Skipping config file not found: ", path)
}
e := &multiconfig.EnvironmentLoader{}
loaders = append(loaders, e)
if len(skipFlags) == 0 || !skipFlags[0] {
f := &multiconfig.FlagLoader{}
loaders = append(loaders, f)
}
loader := multiconfig.MultiLoader(loaders...)
d := &multiconfig.DefaultLoader{}
d.Loader = loader
d.Validator = multiconfig.MultiValidator(&multiconfig.RequiredValidator{})
return d
type scannerOptions struct {
Extractor string
GenreSeparators string
}
func LoadFromFile(confFile string, skipFlags ...bool) {
m := newWithPath(confFile, skipFlags...)
err := m.Load(Server)
if err == flag.ErrHelp {
os.Exit(1)
}
if err != nil {
fmt.Printf("Error trying to load config '%s'. Error: %v", confFile, err)
os.Exit(2)
}
if Server.DbPath == "" {
Server.DbPath = filepath.Join(Server.DataFolder, "navidrome.db")
}
log.SerLevelString(Server.LogLevel)
log.Trace("Loaded configuration", "file", confFile, "config", fmt.Sprintf("%#v", Server))
type lastfmOptions struct {
Enabled bool
ApiKey string
Secret string
Language string
}
type spotifyOptions struct {
ID string
Secret string
}
type listenBrainzOptions struct {
Enabled bool
BaseURL string
}
type prometheusOptions struct {
Enabled bool
MetricsPath string
}
var (
Server = &configOptions{}
hooks []func()
)
func LoadFromFile(confFile string) {
viper.SetConfigFile(confFile)
Load()
}
func Load() {
LoadFromFile(consts.LocalConfigFile)
err := viper.Unmarshal(&Server)
if err != nil {
_, _ = fmt.Fprintln(os.Stderr, "FATAL: Error parsing config:", err)
os.Exit(1)
}
err = os.MkdirAll(Server.DataFolder, os.ModePerm)
if err != nil {
_, _ = fmt.Fprintln(os.Stderr, "FATAL: Error creating data path:", "path", Server.DataFolder, err)
os.Exit(1)
}
Server.ConfigFile = viper.GetViper().ConfigFileUsed()
if Server.DbPath == "" {
Server.DbPath = filepath.Join(Server.DataFolder, consts.DefaultDbPath)
}
log.SetLevelString(Server.LogLevel)
log.SetLogLevels(Server.DevLogLevels)
log.SetLogSourceLine(Server.DevLogSourceLine)
log.SetRedacting(Server.EnableLogRedacting)
if err := validateScanSchedule(); err != nil {
os.Exit(1)
}
if Server.BaseURL != "" {
u, err := url.Parse(Server.BaseURL)
if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "FATAL: Invalid BaseURL %s: %s\n", Server.BaseURL, err.Error())
os.Exit(1)
}
Server.BasePath = u.Path
u.Path = ""
u.RawQuery = ""
Server.BaseHost = u.Host
Server.BaseScheme = u.Scheme
}
// Print current configuration if log level is Debug
if log.CurrentLevel() >= log.LevelDebug {
prettyConf := pretty.Sprintf("Loaded configuration from '%s': %# v", Server.ConfigFile, Server)
if Server.EnableLogRedacting {
prettyConf = log.Redact(prettyConf)
}
_, _ = fmt.Fprintln(os.Stderr, prettyConf)
}
if !Server.EnableExternalServices {
disableExternalServices()
}
// Call init hooks
for _, hook := range hooks {
hook()
}
}
func disableExternalServices() {
log.Info("All external integrations are DISABLED!")
Server.LastFM.Enabled = false
Server.Spotify.ID = ""
Server.ListenBrainz.Enabled = false
Server.Agents = ""
if Server.UILoginBackgroundURL == consts.DefaultUILoginBackgroundURL {
Server.UILoginBackgroundURL = consts.DefaultUILoginBackgroundURLOffline
}
}
func validateScanSchedule() error {
if Server.ScanInterval != -1 {
log.Warn("ScanInterval is DEPRECATED. Please use ScanSchedule. See docs at https://navidrome.org/docs/usage/configuration-options/")
if Server.ScanSchedule != "@every 1m" {
log.Error("You cannot specify both ScanInterval and ScanSchedule, ignoring ScanInterval")
} else {
if Server.ScanInterval == 0 {
Server.ScanSchedule = ""
} else {
Server.ScanSchedule = fmt.Sprintf("@every %s", Server.ScanInterval)
}
log.Warn("Setting ScanSchedule", "schedule", Server.ScanSchedule)
}
}
if Server.ScanSchedule == "0" || Server.ScanSchedule == "" {
Server.ScanSchedule = ""
return nil
}
if _, err := time.ParseDuration(Server.ScanSchedule); err == nil {
Server.ScanSchedule = "@every " + Server.ScanSchedule
}
c := cron.New()
_, err := c.AddFunc(Server.ScanSchedule, func() {})
if err != nil {
log.Error("Invalid ScanSchedule. Please read format spec at https://pkg.go.dev/github.com/robfig/cron#hdr-CRON_Expression_Format", "schedule", Server.ScanSchedule, err)
}
return err
}
// AddHook is used to register initialization code that should run as soon as the config is loaded
func AddHook(hook func()) {
hooks = append(hooks, hook)
}
func init() {
viper.SetDefault("musicfolder", filepath.Join(".", "music"))
viper.SetDefault("datafolder", ".")
viper.SetDefault("loglevel", "info")
viper.SetDefault("address", "0.0.0.0")
viper.SetDefault("port", 4533)
viper.SetDefault("sessiontimeout", consts.DefaultSessionTimeout)
viper.SetDefault("scaninterval", -1)
viper.SetDefault("scanschedule", "@every 1m")
viper.SetDefault("baseurl", "")
viper.SetDefault("tlscert", "")
viper.SetDefault("tlskey", "")
viper.SetDefault("uiloginbackgroundurl", consts.DefaultUILoginBackgroundURL)
viper.SetDefault("uiwelcomemessage", "")
viper.SetDefault("maxsidebarplaylists", consts.DefaultMaxSidebarPlaylists)
viper.SetDefault("enabletranscodingconfig", false)
viper.SetDefault("transcodingcachesize", "100MB")
viper.SetDefault("imagecachesize", "100MB")
viper.SetDefault("enableartworkprecache", true)
viper.SetDefault("autoimportplaylists", true)
viper.SetDefault("playlistspath", consts.DefaultPlaylistsPath)
viper.SetDefault("enabledownloads", true)
viper.SetDefault("enableexternalservices", true)
viper.SetDefault("enableMediaFileCoverArt", true)
viper.SetDefault("autotranscodedownload", false)
viper.SetDefault("defaultdownsamplingformat", consts.DefaultDownsamplingFormat)
viper.SetDefault("searchfullstring", false)
viper.SetDefault("recentlyaddedbymodtime", false)
viper.SetDefault("ignoredarticles", "The El La Los Las Le Les Os As O A")
viper.SetDefault("indexgroups", "A B C D E F G H I J K L M N O P Q R S T U V W X-Z(XYZ) [Unknown]([)")
viper.SetDefault("subsonicartistparticipations", false)
viper.SetDefault("ffmpegpath", "")
viper.SetDefault("coverartpriority", "cover.*, folder.*, front.*, embedded, external")
viper.SetDefault("coverjpegquality", 75)
viper.SetDefault("artistartpriority", "artist.*, album/artist.*, external")
viper.SetDefault("enablegravatar", false)
viper.SetDefault("enablefavourites", true)
viper.SetDefault("enablestarrating", true)
viper.SetDefault("enableuserediting", true)
viper.SetDefault("defaulttheme", "Dark")
viper.SetDefault("defaultlanguage", "")
viper.SetDefault("defaultuivolume", consts.DefaultUIVolume)
viper.SetDefault("enablereplaygain", true)
viper.SetDefault("enablecoveranimation", true)
viper.SetDefault("gatrackingid", "")
viper.SetDefault("enablelogredacting", true)
viper.SetDefault("authrequestlimit", 5)
viper.SetDefault("authwindowlength", 20*time.Second)
viper.SetDefault("passwordencryptionkey", "")
viper.SetDefault("reverseproxyuserheader", "Remote-User")
viper.SetDefault("reverseproxywhitelist", "")
viper.SetDefault("prometheus.enabled", false)
viper.SetDefault("prometheus.metricspath", "/metrics")
viper.SetDefault("scanner.extractor", consts.DefaultScannerExtractor)
viper.SetDefault("scanner.genreseparators", ";/,")
viper.SetDefault("agents", "lastfm,spotify")
viper.SetDefault("lastfm.enabled", true)
viper.SetDefault("lastfm.language", "en")
viper.SetDefault("lastfm.apikey", consts.LastFMAPIKey)
viper.SetDefault("lastfm.secret", consts.LastFMAPISecret)
viper.SetDefault("spotify.id", "")
viper.SetDefault("spotify.secret", "")
viper.SetDefault("listenbrainz.enabled", true)
viper.SetDefault("listenbrainz.baseurl", "https://api.listenbrainz.org/1/")
// DevFlags. These are used to enable/disable debugging and incomplete features
viper.SetDefault("devlogsourceline", false)
viper.SetDefault("devenableprofiler", false)
viper.SetDefault("devautocreateadminpassword", "")
viper.SetDefault("devautologinusername", "")
viper.SetDefault("devactivitypanel", true)
viper.SetDefault("enablesharing", false)
viper.SetDefault("defaultdownloadableshare", false)
viper.SetDefault("devenablebufferedscrobble", true)
viper.SetDefault("devsidebarplaylists", true)
viper.SetDefault("devshowartistpage", true)
viper.SetDefault("devartworkmaxrequests", number.Max(2, runtime.NumCPU()/3))
viper.SetDefault("devartworkthrottlebackloglimit", consts.RequestThrottleBacklogLimit)
viper.SetDefault("devartworkthrottlebacklogtimeout", consts.RequestThrottleBacklogTimeout)
viper.SetDefault("devartistinfotimetolive", consts.ArtistInfoTimeToLive)
viper.SetDefault("devalbuminfotimetolive", consts.AlbumInfoTimeToLive)
}
func InitConfig(cfgFile string) {
cfgFile = getConfigFile(cfgFile)
if cfgFile != "" {
// Use config file from the flag.
viper.SetConfigFile(cfgFile)
} else {
// Search config in local directory with name "navidrome" (without extension).
viper.AddConfigPath(".")
viper.SetConfigName("navidrome")
}
_ = viper.BindEnv("port")
viper.SetEnvPrefix("ND")
replacer := strings.NewReplacer(".", "_")
viper.SetEnvKeyReplacer(replacer)
viper.AutomaticEnv()
err := viper.ReadInConfig()
if viper.ConfigFileUsed() != "" && err != nil {
_, _ = fmt.Fprintln(os.Stderr, "FATAL: Navidrome could not open config file: ", err)
os.Exit(1)
}
}
func getConfigFile(cfgFile string) string {
if cfgFile != "" {
return cfgFile
}
return os.Getenv("ND_CONFIGFILE")
}

View File

@@ -1,16 +1,125 @@
package consts
import "time"
import (
"crypto/md5"
"fmt"
"path/filepath"
"strings"
"time"
)
const (
LocalConfigFile = "./navidrome.toml"
AppName = "navidrome"
DefaultDbPath = "navidrome.db?cache=shared&_busy_timeout=15000&_journal_mode=WAL&_foreign_keys=on"
InitialSetupFlagKey = "InitialSetup"
JWTSecretKey = "JWTSecret"
JWTIssuer = "ND"
JWTTokenExpiration = 30 * time.Minute
UIAuthorizationHeader = "X-ND-Authorization"
UIClientUniqueIDHeader = "X-ND-Client-Unique-Id"
JWTSecretKey = "JWTSecret"
JWTIssuer = "ND"
DefaultSessionTimeout = 24 * time.Hour
CookieExpiry = 365 * 24 * 3600 // One year
InitialUserName = "admin"
// DefaultEncryptionKey This is the encryption key used if none is specified in the `PasswordEncryptionKey` option
// Never ever change this! Or it will break all Navidrome installations that don't set the config option
DefaultEncryptionKey = "just for obfuscation"
PasswordsEncryptedKey = "PasswordsEncryptedKey"
PasswordAutogenPrefix = "__NAVIDROME_AUTOGEN__" //nolint:gosec
UIAssetsLocalPath = "ui/build"
DevInitialUserName = "admin"
DevInitialName = "Dev Admin"
URLPathUI = "/app"
URLPathNativeAPI = "/api"
URLPathSubsonicAPI = "/rest"
URLPathPublic = "/share"
URLPathPublicImages = URLPathPublic + "/img"
// DefaultUILoginBackgroundURL uses Navidrome curated background images collection,
// available at https://unsplash.com/collections/20072696/navidrome
DefaultUILoginBackgroundURL = "/backgrounds"
// DefaultUILoginBackgroundOffline Background image used in case external integrations are disabled
DefaultUILoginBackgroundOffline = "iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAIAAAAiOjnJAAAABGdBTUEAALGPC/xhBQAAAiJJREFUeF7t0IEAAAAAw6D5Ux/khVBhwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDDwMDDVlwABBWcSrQAAAABJRU5ErkJggg=="
DefaultUILoginBackgroundURLOffline = "data:image/png;base64," + DefaultUILoginBackgroundOffline
DefaultMaxSidebarPlaylists = 100
RequestThrottleBacklogLimit = 100
RequestThrottleBacklogTimeout = time.Minute
ServerReadHeaderTimeout = 3 * time.Second
ArtistInfoTimeToLive = 24 * time.Hour
AlbumInfoTimeToLive = 7 * 24 * time.Hour
I18nFolder = "i18n"
SkipScanFile = ".ndignore"
PlaceholderArtistArt = "artist-placeholder.webp"
PlaceholderAlbumArt = "placeholder.png"
PlaceholderAvatar = "logo-192x192.png"
UICoverArtSize = 300
DefaultUIVolume = 100
DefaultHttpClientTimeOut = 10 * time.Second
DefaultScannerExtractor = "taglib"
Zwsp = string('\u200b')
)
// Cache options
const (
TranscodingCacheDir = "cache/transcoding"
DefaultTranscodingCacheMaxItems = 0 // Unlimited
ImageCacheDir = "cache/images"
DefaultImageCacheMaxItems = 0 // Unlimited
DefaultCacheSize = 100 * 1024 * 1024 // 100MB
DefaultCacheCleanUpInterval = 10 * time.Minute
)
// Shared secrets (only add here "secrets" that can be public)
const (
LastFMAPIKey = "9b94a5515ea66b2da3ec03c12300327e" // nolint:gosec
LastFMAPISecret = "74cb6557cec7171d921af5d7d887c587" // nolint:gosec
)
var (
DefaultDownsamplingFormat = "opus"
DefaultTranscodings = []map[string]interface{}{
{
"name": "mp3 audio",
"targetFormat": "mp3",
"defaultBitRate": 192,
"command": "ffmpeg -i %s -map 0:0 -b:a %bk -v 0 -f mp3 -",
},
{
"name": "opus audio",
"targetFormat": "opus",
"defaultBitRate": 128,
"command": "ffmpeg -i %s -map 0:0 -b:a %bk -v 0 -c:a libopus -f opus -",
},
{
"name": "aac audio",
"targetFormat": "aac",
"defaultBitRate": 256,
"command": "ffmpeg -i %s -map 0:0 -b:a %bk -v 0 -c:a aac -f adts -",
},
}
DefaultPlaylistsPath = strings.Join([]string{".", "**/**"}, string(filepath.ListSeparator))
)
var (
VariousArtists = "Various Artists"
VariousArtistsID = fmt.Sprintf("%x", md5.Sum([]byte(strings.ToLower(VariousArtists))))
UnknownAlbum = "[Unknown Album]"
UnknownArtist = "[Unknown Artist]"
UnknownArtistID = fmt.Sprintf("%x", md5.Sum([]byte(strings.ToLower(UnknownArtist))))
VariousArtistsMbzId = "89ad4ac3-39f7-470e-963a-56509c546377"
ServerStart = time.Now()
)

64
consts/mime_types.go Normal file
View File

@@ -0,0 +1,64 @@
package consts
import (
"mime"
"sort"
"strings"
)
type format struct {
typ string
lossless bool
}
var audioFormats = map[string]format{
".mp3": {typ: "audio/mpeg"},
".ogg": {typ: "audio/ogg"},
".oga": {typ: "audio/ogg"},
".opus": {typ: "audio/ogg"},
".aac": {typ: "audio/mp4"},
".alac": {typ: "audio/mp4", lossless: true},
".m4a": {typ: "audio/mp4"},
".m4b": {typ: "audio/mp4"},
".flac": {typ: "audio/flac", lossless: true},
".wav": {typ: "audio/x-wav", lossless: true},
".wma": {typ: "audio/x-ms-wma"},
".ape": {typ: "audio/x-monkeys-audio", lossless: true},
".mpc": {typ: "audio/x-musepack"},
".shn": {typ: "audio/x-shn", lossless: true},
".aif": {typ: "audio/x-aiff"},
".aiff": {typ: "audio/x-aiff"},
".m3u": {typ: "audio/x-mpegurl"},
".pls": {typ: "audio/x-scpls"},
".dsf": {typ: "audio/dsd", lossless: true},
".wv": {typ: "audio/x-wavpack", lossless: true},
".wvp": {typ: "audio/x-wavpack", lossless: true},
".mka": {typ: "audio/x-matroska"},
}
var imageFormats = map[string]string{
".gif": "image/gif",
".jpg": "image/jpeg",
".jpeg": "image/jpeg",
".webp": "image/webp",
".png": "image/png",
".bmp": "image/bmp",
}
var LosslessFormats []string
func init() {
for ext, fmt := range audioFormats {
_ = mime.AddExtensionType(ext, fmt.typ)
if fmt.lossless {
LosslessFormats = append(LosslessFormats, strings.TrimPrefix(ext, "."))
}
}
sort.Strings(LosslessFormats)
for ext, typ := range imageFormats {
_ = mime.AddExtensionType(ext, typ)
}
// In some circumstances, Windows sets JS mime-type to `text/plain`!
_ = mime.AddExtensionType(".js", "text/javascript")
_ = mime.AddExtensionType(".css", "text/css")
}

26
consts/version.go Normal file
View File

@@ -0,0 +1,26 @@
package consts
import (
"fmt"
"strings"
)
var (
// This will be set in build time. If not, version will be set to "dev"
gitTag string
gitSha string
)
// Version holds the version string, with tag and git sha info.
// Examples:
// dev
// v0.2.0 (5b84188)
// v0.3.2-SNAPSHOT (715f552)
// master (9ed35cb)
var Version = func() string {
if gitSha == "" {
return "dev"
}
gitTag = strings.TrimPrefix(gitTag, "v")
return fmt.Sprintf("%s (%s)", gitTag, gitSha)
}()

View File

@@ -0,0 +1,7 @@
https://your.website {
reverse_proxy * navidrome:4533 {
header_up Host {http.reverse_proxy.upstream.hostport}
header_up X-Forwarded-For {http.request.remote}
header_up X-Real-IP {http.reverse_proxy.upstream.port}
}
}

View File

@@ -0,0 +1,31 @@
version: '3.6'
volumes:
caddy_data:
navidrome_data:
services:
caddy:
container_name: "caddy"
image: caddy:2.6-alpine
restart: unless-stopped
read_only: true
volumes:
- "caddy_data:/data:rw"
- "./Caddyfile:/etc/caddy/Caddyfile:ro"
ports:
- "80:80"
- "443:443"
navidrome:
container_name: "navidrome"
image: deluan/navidrome:latest
restart: unless-stopped
read_only: true
# user: 1000:1000
ports:
- "4533:4533"
volumes:
- "navidrome_data:/data"
#- "/mnt/music:/music:ro"

View File

@@ -0,0 +1,51 @@
version: "3.6"
volumes:
traefik_data:
navidrome_data:
services:
traefik:
container_name: "traefik"
image: traefik:2.9
restart: unless-stopped
read_only: true
command:
- "--log.level=ERROR"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.tc.acme.tlschallenge=true"
#- "--certificatesresolvers.tc.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
- "--certificatesresolvers.tc.acme.email=foo@foo.com"
- "--certificatesresolvers.tc.acme.storage=/letsencrypt/acme.json"
ports:
- "443:443"
volumes:
- "traefik_data:/letsencrypt"
#- "/var/run/docker.sock:/var/run/docker.sock:ro"
navidrome:
container_name: "navidrome"
image: deluan/navidrome:latest
restart: unless-stopped
read_only: true
# user: 1000:1000
ports:
- "4533:4533"
environment:
ND_SCANINTERVAL: 6h
ND_LOGLEVEL: info
ND_SESSIONTIMEOUT: 168h
ND_BASEURL: ""
volumes:
- "navidrome_data:/data"
#- "/mnt/music:/music:ro"
labels:
- "traefik.enable=true"
- "traefik.http.routers.navidrome.rule=Host(`foo.com`)"
- "traefik.http.routers.navidrome.entrypoints=websecure"
- "traefik.http.routers.navidrome.tls=true"
- "traefik.http.routers.navidrome.tls.certresolver=tc"
- "traefik.http.services.navidrome.loadbalancer.server.port=4533"

View File

@@ -0,0 +1,18 @@
version: '3.6'
volumes:
navidrome_data:
services:
navidrome:
container_name: "navidrome"
image: deluan/navidrome:latest
restart: unless-stopped
read_only: true
# user: 1000:1000
ports:
- "4533:4533"
volumes:
- "navidrome_data:/data"
#- "/mnt/music:/music:ro"

52
contrib/freebsd_rc Normal file
View File

@@ -0,0 +1,52 @@
#!/bin/sh
#
# $FreeBSD: $
#
# PROVIDE: navidrome
# REQUIRE: NETWORKING
# KEYWORD:
#
# Add the following lines to /etc/rc.conf to enable navidrome:
# navidrome_enable="YES"
#
# navidrome_enable (bool): Set to YES to enable navidrome
# Default: NO
# navidrome_config (str): navidrome configration file
# Default: /usr/local/etc/navidrome/config.toml
# navidrome_datafolder (str): navidrome Folder to store application data
# Default: www
# navidrome_user (str): navidrome daemon user
# Default: www
# navidrome_group (str): navidrome daemon group
# Default: www
. /etc/rc.subr
name="navidrome"
rcvar="navidrome_enable"
load_rc_config $name
: ${navidrome_user:="www"}
: ${navidrome_group:="www"}
: ${navidrome_enable:="NO"}
: ${navidrome_config:="/usr/local/etc/navidrome/config.toml"}
: ${navidrome_flags=""}
: ${navidrome_facility:="daemon"}
: ${navidrome_priority:="debug"}
: ${navidrome_datafolder:="/var/db/${name}"}
required_dirs=${navidrome_datafolder}
required_files=${navidrome_config}
procname="/usr/local/bin/${name}"
pidfile="/var/run/${name}.pid"
start_precmd="${name}_precmd"
command=/usr/sbin/daemon
command_args="-S -l ${navidrome_facility} -s ${navidrome_priority} -T ${name} -t ${name} -p ${pidfile} \
${procname} --configfile ${navidrome_config} --datafolder ${navidrome_datafolder} ${navidrome_flags}"
navidrome_precmd()
{
install -o ${navidrome_user} /dev/null ${pidfile}
}
run_rc_command "$1"

11
contrib/k8s/README.md Normal file
View File

@@ -0,0 +1,11 @@
# Kubernetes
A couple things to keep in mind with this manifest:
1. This creates a namespace called `navidrome`. Adjust this as needed.
1. This manifest was created on [K3s](https://github.com/k3s-io/k3s), which uses its own storage provisioner called [local-path-provisioner](https://github.com/rancher/local-path-provisioner). Be sure to change the `storageClassName` of the `PersistentVolumeClaim` as needed.
1. The `PersistentVolumeClaim` sets up a 2Gi volume for Navidrome's database. Adjust this as needed.
1. Be sure to change the `image` tag from `ghcr.io/navidrome/navidrome:0.49.3` to whatever the newest version is.
1. This assumes your music is mounted on the host using `hostPath` at `/path/to/your/music/on/the/host`. Adjust this as needed.
1. The `Ingress` is already configured for `cert-manager` to obtain a Let's Encrypt TLS certificate and uses Traefik for routing. Adjust this as needed.
1. The `Ingress` presents the service at `navidrome.${SECRET_INTERNAL_DOMAIN_NAME}`, which needs to already be setup in DNS.

111
contrib/k8s/manifest.yml Normal file
View File

@@ -0,0 +1,111 @@
---
apiVersion: v1
kind: Namespace
metadata:
name: navidrome
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: navidrome-data-pvc
namespace: navidrome
annotations:
volumeType: local
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 2Gi
storageClassName: local-path
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: navidrome-deployment
namespace: navidrome
spec:
replicas: 1
revisionHistoryLimit: 2
selector:
matchLabels:
app: navidrome
template:
metadata:
labels:
app: navidrome
spec:
containers:
- name: navidrome
image: ghcr.io/navidrome/navidrome:0.49.3
ports:
- containerPort: 4533
env:
- name: ND_SCANSCHEDULE
value: "12h"
- name: ND_SESSIONTIMEOUT
value: "24h"
- name: ND_LOGLEVEL
value: "info"
- name: ND_ENABLETRANSCODINGCONFIG
value: "false"
- name: ND_TRANSCODINGCACHESIZE
value: "512MB"
- name: ND_ENABLESTARRATING
value: "false"
- name: ND_ENABLEFAVOURITES
value: "false"
volumeMounts:
- name: data
mountPath: /data
- name: music
mountPath: /music
readOnly: true
volumes:
- name: data
persistentVolumeClaim:
claimName: navidrome-data-pvc
- name: music
hostPath:
path: /path/to/your/music/on/the/host
type: Directory
---
apiVersion: v1
kind: Service
metadata:
name: navidrome-service
namespace: navidrome
spec:
type: ClusterIP
ports:
- name: http
targetPort: 4533
port: 4533
protocol: TCP
selector:
app: navidrome
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: navidrome-ingress
namespace: navidrome
annotations:
cert-manager.io/cluster-issuer: letsencrypt-production
traefik.ingress.kubernetes.io/router.tls: "true"
spec:
rules:
- host: navidrome.${SECRET_INTERNAL_DOMAIN_NAME}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: navidrome-service
port:
number: 4533
tls:
- hosts:
- navidrome.${SECRET_INTERNAL_DOMAIN_NAME}
secretName: navidrome-tls

15
contrib/navidrome Normal file
View File

@@ -0,0 +1,15 @@
#!/sbin/openrc-run
name=$RC_SVCNAME
command="/opt/navidrome/${RC_SVCNAME}"
command_args="-datafolder /opt/navidrome"
command_user="${RC_SVCNAME}"
pidfile="/var/run/${RC_SVCNAME}.pid"
output_log="/opt/navidrome/${RC_SVCNAME}.log"
error_log="/opt/navidrome/${RC_SVCNAME}.err"
command_background="yes"
depend() {
need net
}

56
contrib/navidrome.service Normal file
View File

@@ -0,0 +1,56 @@
# This file ususaly goes in /etc/systemd/system
[Unit]
Description=Navidrome Music Server and Streamer compatible with Subsonic/Airsonic
After=remote-fs.target network.target
[Install]
WantedBy=multi-user.target
[Service]
User=navidrome
Group=navidrome
Type=simple
ExecStart=/usr/bin/navidrome
StateDirectory=navidrome
WorkingDirectory=/var/lib/navidrome
TimeoutStopSec=20
KillMode=process
Restart=on-failure
EnvironmentFile=-/etc/sysconfig/navidrome
# See https://www.freedesktop.org/software/systemd/man/systemd.exec.html
CapabilityBoundingSet=
DevicePolicy=closed
NoNewPrivileges=yes
LockPersonality=yes
PrivateTmp=yes
PrivateUsers=yes
ProtectControlGroups=yes
ProtectKernelModules=yes
ProtectKernelTunables=yes
ProtectClock=yes
ProtectHostname=yes
ProtectKernelLogs=yes
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
RestrictNamespaces=yes
RestrictRealtime=yes
SystemCallFilter=@system-service
SystemCallFilter=~@privileged @resources
SystemCallFilter=setrlimit
SystemCallArchitectures=native
UMask=0066
# You can uncomment the following line if you're not using the jukebox This
# will prevent navidrome from accessing any real (physical) devices
#PrivateDevices=yes
# You can change the following line to `strict` instead of `full` if you don't
# want navidrome to be able to write anything on your filesystem outside of
# /var/lib/navidrome.
ProtectSystem=full
# You can comment the following line if you don't have any media in /home/*.
# This will prevent navidrome from ever reading/writing anything there.
ProtectHome=true

12
core/agents/README.md Normal file
View File

@@ -0,0 +1,12 @@
This folder abstracts metadata lookup into "agents". Each agent can be implemented to get as
much info as the external source provides, by using a granular set of interfaces
(see [interfaces](interfaces.go)).
A new agent must comply with these simple implementation rules:
1) Implement the `AgentName()` method. It just returns the name of the agent for logging purposes.
2) Implement one or more of the `*Retriever()` interfaces. That's where the agent's logic resides.
3) Register itself (in its `init()` function).
For an agent to be used it needs to be listed in the `Agents` config option (default is `"lastfm,spotify"`). The order dictates the priority of the agents
For a simple Agent example, look at the [local_agent](local_agent.go) agent source code.

228
core/agents/agents.go Normal file
View File

@@ -0,0 +1,228 @@
package agents
import (
"context"
"strings"
"time"
"github.com/navidrome/navidrome/conf"
"github.com/navidrome/navidrome/consts"
"github.com/navidrome/navidrome/log"
"github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/utils"
)
type Agents struct {
ds model.DataStore
agents []Interface
}
func New(ds model.DataStore) *Agents {
var order []string
if conf.Server.Agents != "" {
order = strings.Split(conf.Server.Agents, ",")
}
order = append(order, LocalAgentName)
var res []Interface
for _, name := range order {
init, ok := Map[name]
if !ok {
log.Error("Agent not available. Check configuration", "name", name)
continue
}
res = append(res, init(ds))
}
return &Agents{ds: ds, agents: res}
}
func (a *Agents) AgentName() string {
return "agents"
}
func (a *Agents) GetArtistMBID(ctx context.Context, id string, name string) (string, error) {
switch id {
case consts.UnknownArtistID:
return "", ErrNotFound
case consts.VariousArtistsID:
return "", nil
}
start := time.Now()
for _, ag := range a.agents {
if utils.IsCtxDone(ctx) {
break
}
agent, ok := ag.(ArtistMBIDRetriever)
if !ok {
continue
}
mbid, err := agent.GetArtistMBID(ctx, id, name)
if mbid != "" && err == nil {
log.Debug(ctx, "Got MBID", "agent", ag.AgentName(), "artist", name, "mbid", mbid, "elapsed", time.Since(start))
return mbid, nil
}
}
return "", ErrNotFound
}
func (a *Agents) GetArtistURL(ctx context.Context, id, name, mbid string) (string, error) {
switch id {
case consts.UnknownArtistID:
return "", ErrNotFound
case consts.VariousArtistsID:
return "", nil
}
start := time.Now()
for _, ag := range a.agents {
if utils.IsCtxDone(ctx) {
break
}
agent, ok := ag.(ArtistURLRetriever)
if !ok {
continue
}
url, err := agent.GetArtistURL(ctx, id, name, mbid)
if url != "" && err == nil {
log.Debug(ctx, "Got External Url", "agent", ag.AgentName(), "artist", name, "url", url, "elapsed", time.Since(start))
return url, nil
}
}
return "", ErrNotFound
}
func (a *Agents) GetArtistBiography(ctx context.Context, id, name, mbid string) (string, error) {
switch id {
case consts.UnknownArtistID:
return "", ErrNotFound
case consts.VariousArtistsID:
return "", nil
}
start := time.Now()
for _, ag := range a.agents {
if utils.IsCtxDone(ctx) {
break
}
agent, ok := ag.(ArtistBiographyRetriever)
if !ok {
continue
}
bio, err := agent.GetArtistBiography(ctx, id, name, mbid)
if err == nil {
log.Debug(ctx, "Got Biography", "agent", ag.AgentName(), "artist", name, "len", len(bio), "elapsed", time.Since(start))
return bio, nil
}
}
return "", ErrNotFound
}
func (a *Agents) GetSimilarArtists(ctx context.Context, id, name, mbid string, limit int) ([]Artist, error) {
switch id {
case consts.UnknownArtistID:
return nil, ErrNotFound
case consts.VariousArtistsID:
return nil, nil
}
start := time.Now()
for _, ag := range a.agents {
if utils.IsCtxDone(ctx) {
break
}
agent, ok := ag.(ArtistSimilarRetriever)
if !ok {
continue
}
similar, err := agent.GetSimilarArtists(ctx, id, name, mbid, limit)
if len(similar) > 0 && err == nil {
if log.CurrentLevel() >= log.LevelTrace {
log.Debug(ctx, "Got Similar Artists", "agent", ag.AgentName(), "artist", name, "similar", similar, "elapsed", time.Since(start))
} else {
log.Debug(ctx, "Got Similar Artists", "agent", ag.AgentName(), "artist", name, "similarReceived", len(similar), "elapsed", time.Since(start))
}
return similar, err
}
}
return nil, ErrNotFound
}
func (a *Agents) GetArtistImages(ctx context.Context, id, name, mbid string) ([]ExternalImage, error) {
switch id {
case consts.UnknownArtistID:
return nil, ErrNotFound
case consts.VariousArtistsID:
return nil, nil
}
start := time.Now()
for _, ag := range a.agents {
if utils.IsCtxDone(ctx) {
break
}
agent, ok := ag.(ArtistImageRetriever)
if !ok {
continue
}
images, err := agent.GetArtistImages(ctx, id, name, mbid)
if len(images) > 0 && err == nil {
log.Debug(ctx, "Got Images", "agent", ag.AgentName(), "artist", name, "images", images, "elapsed", time.Since(start))
return images, nil
}
}
return nil, ErrNotFound
}
func (a *Agents) GetArtistTopSongs(ctx context.Context, id, artistName, mbid string, count int) ([]Song, error) {
switch id {
case consts.UnknownArtistID:
return nil, ErrNotFound
case consts.VariousArtistsID:
return nil, nil
}
start := time.Now()
for _, ag := range a.agents {
if utils.IsCtxDone(ctx) {
break
}
agent, ok := ag.(ArtistTopSongsRetriever)
if !ok {
continue
}
songs, err := agent.GetArtistTopSongs(ctx, id, artistName, mbid, count)
if len(songs) > 0 && err == nil {
log.Debug(ctx, "Got Top Songs", "agent", ag.AgentName(), "artist", artistName, "songs", songs, "elapsed", time.Since(start))
return songs, nil
}
}
return nil, ErrNotFound
}
func (a *Agents) GetAlbumInfo(ctx context.Context, name, artist, mbid string) (*AlbumInfo, error) {
if name == consts.UnknownAlbum {
return nil, ErrNotFound
}
start := time.Now()
for _, ag := range a.agents {
if utils.IsCtxDone(ctx) {
break
}
agent, ok := ag.(AlbumInfoRetriever)
if !ok {
continue
}
album, err := agent.GetAlbumInfo(ctx, name, artist, mbid)
if err == nil {
log.Debug(ctx, "Got Album Info", "agent", ag.AgentName(), "album", name, "artist", artist,
"mbid", mbid, "elapsed", time.Since(start))
return album, nil
}
}
return nil, ErrNotFound
}
var _ Interface = (*Agents)(nil)
var _ ArtistMBIDRetriever = (*Agents)(nil)
var _ ArtistURLRetriever = (*Agents)(nil)
var _ ArtistBiographyRetriever = (*Agents)(nil)
var _ ArtistSimilarRetriever = (*Agents)(nil)
var _ ArtistImageRetriever = (*Agents)(nil)
var _ ArtistTopSongsRetriever = (*Agents)(nil)
var _ AlbumInfoRetriever = (*Agents)(nil)

View File

@@ -0,0 +1,17 @@
package agents
import (
"testing"
"github.com/navidrome/navidrome/log"
"github.com/navidrome/navidrome/tests"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
func TestAgents(t *testing.T) {
tests.Init(t, false)
log.SetLevel(log.LevelFatal)
RegisterFailHandler(Fail)
RunSpecs(t, "Agents Test Suite")
}

346
core/agents/agents_test.go Normal file
View File

@@ -0,0 +1,346 @@
package agents
import (
"context"
"errors"
"github.com/navidrome/navidrome/consts"
"github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/tests"
"github.com/navidrome/navidrome/conf"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("Agents", func() {
var ctx context.Context
var cancel context.CancelFunc
var ds model.DataStore
var mfRepo *tests.MockMediaFileRepo
BeforeEach(func() {
ctx, cancel = context.WithCancel(context.Background())
mfRepo = tests.CreateMockMediaFileRepo()
ds = &tests.MockDataStore{MockedMediaFile: mfRepo}
})
Describe("Local", func() {
var ag *Agents
BeforeEach(func() {
conf.Server.Agents = ""
ag = New(ds)
})
It("calls the placeholder GetArtistImages", func() {
mfRepo.SetData(model.MediaFiles{{ID: "1", Title: "One", MbzReleaseTrackID: "111"}, {ID: "2", Title: "Two", MbzReleaseTrackID: "222"}})
songs, err := ag.GetArtistTopSongs(ctx, "123", "John Doe", "mb123", 2)
Expect(err).ToNot(HaveOccurred())
Expect(songs).To(ConsistOf([]Song{{Name: "One", MBID: "111"}, {Name: "Two", MBID: "222"}}))
})
})
Describe("Agents", func() {
var ag *Agents
var mock *mockAgent
BeforeEach(func() {
mock = &mockAgent{}
Register("fake", func(ds model.DataStore) Interface {
return mock
})
Register("empty", func(ds model.DataStore) Interface {
return struct {
Interface
}{}
})
conf.Server.Agents = "empty,fake"
ag = New(ds)
Expect(ag.AgentName()).To(Equal("agents"))
})
Describe("GetArtistMBID", func() {
It("returns on first match", func() {
Expect(ag.GetArtistMBID(ctx, "123", "test")).To(Equal("mbid"))
Expect(mock.Args).To(ConsistOf("123", "test"))
})
It("returns empty if artist is Various Artists", func() {
mbid, err := ag.GetArtistMBID(ctx, consts.VariousArtistsID, consts.VariousArtists)
Expect(err).ToNot(HaveOccurred())
Expect(mbid).To(BeEmpty())
Expect(mock.Args).To(BeEmpty())
})
It("returns not found if artist is Unknown Artist", func() {
mbid, err := ag.GetArtistMBID(ctx, consts.VariousArtistsID, consts.VariousArtists)
Expect(err).ToNot(HaveOccurred())
Expect(mbid).To(BeEmpty())
Expect(mock.Args).To(BeEmpty())
})
It("skips the agent if it returns an error", func() {
mock.Err = errors.New("error")
_, err := ag.GetArtistMBID(ctx, "123", "test")
Expect(err).To(MatchError(ErrNotFound))
Expect(mock.Args).To(ConsistOf("123", "test"))
})
It("interrupts if the context is canceled", func() {
cancel()
_, err := ag.GetArtistMBID(ctx, "123", "test")
Expect(err).To(MatchError(ErrNotFound))
Expect(mock.Args).To(BeEmpty())
})
})
Describe("GetArtistURL", func() {
It("returns on first match", func() {
Expect(ag.GetArtistURL(ctx, "123", "test", "mb123")).To(Equal("url"))
Expect(mock.Args).To(ConsistOf("123", "test", "mb123"))
})
It("returns empty if artist is Various Artists", func() {
url, err := ag.GetArtistURL(ctx, consts.VariousArtistsID, consts.VariousArtists, "")
Expect(err).ToNot(HaveOccurred())
Expect(url).To(BeEmpty())
Expect(mock.Args).To(BeEmpty())
})
It("returns not found if artist is Unknown Artist", func() {
url, err := ag.GetArtistURL(ctx, consts.VariousArtistsID, consts.VariousArtists, "")
Expect(err).ToNot(HaveOccurred())
Expect(url).To(BeEmpty())
Expect(mock.Args).To(BeEmpty())
})
It("skips the agent if it returns an error", func() {
mock.Err = errors.New("error")
_, err := ag.GetArtistURL(ctx, "123", "test", "mb123")
Expect(err).To(MatchError(ErrNotFound))
Expect(mock.Args).To(ConsistOf("123", "test", "mb123"))
})
It("interrupts if the context is canceled", func() {
cancel()
_, err := ag.GetArtistURL(ctx, "123", "test", "mb123")
Expect(err).To(MatchError(ErrNotFound))
Expect(mock.Args).To(BeEmpty())
})
})
Describe("GetArtistBiography", func() {
It("returns on first match", func() {
Expect(ag.GetArtistBiography(ctx, "123", "test", "mb123")).To(Equal("bio"))
Expect(mock.Args).To(ConsistOf("123", "test", "mb123"))
})
It("returns empty if artist is Various Artists", func() {
bio, err := ag.GetArtistBiography(ctx, consts.VariousArtistsID, consts.VariousArtists, "")
Expect(err).ToNot(HaveOccurred())
Expect(bio).To(BeEmpty())
Expect(mock.Args).To(BeEmpty())
})
It("returns not found if artist is Unknown Artist", func() {
bio, err := ag.GetArtistBiography(ctx, consts.VariousArtistsID, consts.VariousArtists, "")
Expect(err).ToNot(HaveOccurred())
Expect(bio).To(BeEmpty())
Expect(mock.Args).To(BeEmpty())
})
It("skips the agent if it returns an error", func() {
mock.Err = errors.New("error")
_, err := ag.GetArtistBiography(ctx, "123", "test", "mb123")
Expect(err).To(MatchError(ErrNotFound))
Expect(mock.Args).To(ConsistOf("123", "test", "mb123"))
})
It("interrupts if the context is canceled", func() {
cancel()
_, err := ag.GetArtistBiography(ctx, "123", "test", "mb123")
Expect(err).To(MatchError(ErrNotFound))
Expect(mock.Args).To(BeEmpty())
})
})
Describe("GetArtistImages", func() {
It("returns on first match", func() {
Expect(ag.GetArtistImages(ctx, "123", "test", "mb123")).To(Equal([]ExternalImage{{
URL: "imageUrl",
Size: 100,
}}))
Expect(mock.Args).To(ConsistOf("123", "test", "mb123"))
})
It("skips the agent if it returns an error", func() {
mock.Err = errors.New("error")
_, err := ag.GetArtistImages(ctx, "123", "test", "mb123")
Expect(err).To(MatchError("not found"))
Expect(mock.Args).To(ConsistOf("123", "test", "mb123"))
})
It("interrupts if the context is canceled", func() {
cancel()
_, err := ag.GetArtistImages(ctx, "123", "test", "mb123")
Expect(err).To(MatchError(ErrNotFound))
Expect(mock.Args).To(BeEmpty())
})
})
Describe("GetSimilarArtists", func() {
It("returns on first match", func() {
Expect(ag.GetSimilarArtists(ctx, "123", "test", "mb123", 1)).To(Equal([]Artist{{
Name: "Joe Dohn",
MBID: "mbid321",
}}))
Expect(mock.Args).To(ConsistOf("123", "test", "mb123", 1))
})
It("skips the agent if it returns an error", func() {
mock.Err = errors.New("error")
_, err := ag.GetSimilarArtists(ctx, "123", "test", "mb123", 1)
Expect(err).To(MatchError(ErrNotFound))
Expect(mock.Args).To(ConsistOf("123", "test", "mb123", 1))
})
It("interrupts if the context is canceled", func() {
cancel()
_, err := ag.GetSimilarArtists(ctx, "123", "test", "mb123", 1)
Expect(err).To(MatchError(ErrNotFound))
Expect(mock.Args).To(BeEmpty())
})
})
Describe("GetArtistTopSongs", func() {
It("returns on first match", func() {
Expect(ag.GetArtistTopSongs(ctx, "123", "test", "mb123", 2)).To(Equal([]Song{{
Name: "A Song",
MBID: "mbid444",
}}))
Expect(mock.Args).To(ConsistOf("123", "test", "mb123", 2))
})
It("skips the agent if it returns an error", func() {
mock.Err = errors.New("error")
_, err := ag.GetArtistTopSongs(ctx, "123", "test", "mb123", 2)
Expect(err).To(MatchError(ErrNotFound))
Expect(mock.Args).To(ConsistOf("123", "test", "mb123", 2))
})
It("interrupts if the context is canceled", func() {
cancel()
_, err := ag.GetArtistTopSongs(ctx, "123", "test", "mb123", 2)
Expect(err).To(MatchError(ErrNotFound))
Expect(mock.Args).To(BeEmpty())
})
})
Describe("GetAlbumInfo", func() {
It("returns meaningful data", func() {
Expect(ag.GetAlbumInfo(ctx, "album", "artist", "mbid")).To(Equal(&AlbumInfo{
Name: "A Song",
MBID: "mbid444",
Description: "A Description",
URL: "External URL",
Images: []ExternalImage{
{
Size: 174,
URL: "https://lastfm.freetls.fastly.net/i/u/174s/00000000000000000000000000000000.png",
}, {
Size: 64,
URL: "https://lastfm.freetls.fastly.net/i/u/64s/00000000000000000000000000000000.png",
}, {
Size: 34,
URL: "https://lastfm.freetls.fastly.net/i/u/34s/00000000000000000000000000000000.png",
},
},
}))
Expect(mock.Args).To(ConsistOf("album", "artist", "mbid"))
})
It("skips the agent if it returns an error", func() {
mock.Err = errors.New("error")
_, err := ag.GetAlbumInfo(ctx, "album", "artist", "mbid")
Expect(err).To(MatchError(ErrNotFound))
Expect(mock.Args).To(ConsistOf("album", "artist", "mbid"))
})
It("interrupts if the context is canceled", func() {
cancel()
_, err := ag.GetAlbumInfo(ctx, "album", "artist", "mbid")
Expect(err).To(MatchError(ErrNotFound))
Expect(mock.Args).To(BeEmpty())
})
})
})
})
type mockAgent struct {
Args []interface{}
Err error
}
func (a *mockAgent) AgentName() string {
return "fake"
}
func (a *mockAgent) GetArtistMBID(_ context.Context, id string, name string) (string, error) {
a.Args = []interface{}{id, name}
if a.Err != nil {
return "", a.Err
}
return "mbid", nil
}
func (a *mockAgent) GetArtistURL(_ context.Context, id, name, mbid string) (string, error) {
a.Args = []interface{}{id, name, mbid}
if a.Err != nil {
return "", a.Err
}
return "url", nil
}
func (a *mockAgent) GetArtistBiography(_ context.Context, id, name, mbid string) (string, error) {
a.Args = []interface{}{id, name, mbid}
if a.Err != nil {
return "", a.Err
}
return "bio", nil
}
func (a *mockAgent) GetArtistImages(_ context.Context, id, name, mbid string) ([]ExternalImage, error) {
a.Args = []interface{}{id, name, mbid}
if a.Err != nil {
return nil, a.Err
}
return []ExternalImage{{
URL: "imageUrl",
Size: 100,
}}, nil
}
func (a *mockAgent) GetSimilarArtists(_ context.Context, id, name, mbid string, limit int) ([]Artist, error) {
a.Args = []interface{}{id, name, mbid, limit}
if a.Err != nil {
return nil, a.Err
}
return []Artist{{
Name: "Joe Dohn",
MBID: "mbid321",
}}, nil
}
func (a *mockAgent) GetArtistTopSongs(_ context.Context, id, artistName, mbid string, count int) ([]Song, error) {
a.Args = []interface{}{id, artistName, mbid, count}
if a.Err != nil {
return nil, a.Err
}
return []Song{{
Name: "A Song",
MBID: "mbid444",
}}, nil
}
func (a *mockAgent) GetAlbumInfo(ctx context.Context, name, artist, mbid string) (*AlbumInfo, error) {
a.Args = []interface{}{name, artist, mbid}
if a.Err != nil {
return nil, a.Err
}
return &AlbumInfo{
Name: "A Song",
MBID: "mbid444",
Description: "A Description",
URL: "External URL",
Images: []ExternalImage{
{
Size: 174,
URL: "https://lastfm.freetls.fastly.net/i/u/174s/00000000000000000000000000000000.png",
}, {
Size: 64,
URL: "https://lastfm.freetls.fastly.net/i/u/64s/00000000000000000000000000000000.png",
}, {
Size: 34,
URL: "https://lastfm.freetls.fastly.net/i/u/34s/00000000000000000000000000000000.png",
},
},
}, nil
}

79
core/agents/interfaces.go Normal file
View File

@@ -0,0 +1,79 @@
package agents
import (
"context"
"errors"
"github.com/navidrome/navidrome/model"
)
type Constructor func(ds model.DataStore) Interface
type Interface interface {
AgentName() string
}
type AlbumInfo struct {
Name string
MBID string
Description string
URL string
Images []ExternalImage
}
type Artist struct {
Name string
MBID string
}
type ExternalImage struct {
URL string
Size int
}
type Song struct {
Name string
MBID string
}
var (
ErrNotFound = errors.New("not found")
)
// TODO Break up this interface in more specific methods, like artists
type AlbumInfoRetriever interface {
GetAlbumInfo(ctx context.Context, name, artist, mbid string) (*AlbumInfo, error)
}
type ArtistMBIDRetriever interface {
GetArtistMBID(ctx context.Context, id string, name string) (string, error)
}
type ArtistURLRetriever interface {
GetArtistURL(ctx context.Context, id, name, mbid string) (string, error)
}
type ArtistBiographyRetriever interface {
GetArtistBiography(ctx context.Context, id, name, mbid string) (string, error)
}
type ArtistSimilarRetriever interface {
GetSimilarArtists(ctx context.Context, id, name, mbid string, limit int) ([]Artist, error)
}
type ArtistImageRetriever interface {
GetArtistImages(ctx context.Context, id, name, mbid string) ([]ExternalImage, error)
}
type ArtistTopSongsRetriever interface {
GetArtistTopSongs(ctx context.Context, id, artistName, mbid string, count int) ([]Song, error)
}
var Map map[string]Constructor
func Register(name string, init Constructor) {
if Map == nil {
Map = make(map[string]Constructor)
}
Map[name] = init
}

322
core/agents/lastfm/agent.go Normal file
View File

@@ -0,0 +1,322 @@
package lastfm
import (
"context"
"errors"
"net/http"
"regexp"
"strconv"
"strings"
"github.com/navidrome/navidrome/conf"
"github.com/navidrome/navidrome/consts"
"github.com/navidrome/navidrome/core/agents"
"github.com/navidrome/navidrome/core/scrobbler"
"github.com/navidrome/navidrome/log"
"github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/utils"
)
const (
lastFMAgentName = "lastfm"
sessionKeyProperty = "LastFMSessionKey"
)
var ignoredBiographies = []string{
// Unknown Artist
`<a href="https://www.last.fm/music/`,
}
type lastfmAgent struct {
ds model.DataStore
sessionKeys *agents.SessionKeys
apiKey string
secret string
lang string
client *client
}
func lastFMConstructor(ds model.DataStore) *lastfmAgent {
l := &lastfmAgent{
ds: ds,
lang: conf.Server.LastFM.Language,
apiKey: conf.Server.LastFM.ApiKey,
secret: conf.Server.LastFM.Secret,
sessionKeys: &agents.SessionKeys{DataStore: ds, KeyName: sessionKeyProperty},
}
hc := &http.Client{
Timeout: consts.DefaultHttpClientTimeOut,
}
chc := utils.NewCachedHTTPClient(hc, consts.DefaultHttpClientTimeOut)
l.client = newClient(l.apiKey, l.secret, l.lang, chc)
return l
}
func (l *lastfmAgent) AgentName() string {
return lastFMAgentName
}
var imageRegex = regexp.MustCompile(`u\/(\d+)`)
func (l *lastfmAgent) GetAlbumInfo(ctx context.Context, name, artist, mbid string) (*agents.AlbumInfo, error) {
a, err := l.callAlbumGetInfo(ctx, name, artist, mbid)
if err != nil {
return nil, err
}
response := agents.AlbumInfo{
Name: a.Name,
MBID: a.MBID,
Description: a.Description.Summary,
URL: a.URL,
Images: make([]agents.ExternalImage, 0),
}
// Last.fm can return duplicate sizes.
seenSizes := map[int]bool{}
// This assumes that Last.fm returns images with size small, medium, and large.
// This is true as of December 29, 2022
for _, img := range a.Image {
size := imageRegex.FindStringSubmatch(img.URL)
// Last.fm can return images without URL
if len(size) == 0 || len(size[0]) < 4 {
log.Trace(ctx, "LastFM/albuminfo image URL does not match expected regex or is empty", "url", img.URL, "size", img.Size)
continue
}
numericSize, err := strconv.Atoi(size[0][2:])
if err != nil {
log.Error(ctx, "LastFM/albuminfo image URL does not match expected regex", "url", img.URL, "size", img.Size, err)
return nil, err
} else {
if _, exists := seenSizes[numericSize]; !exists {
response.Images = append(response.Images, agents.ExternalImage{
Size: numericSize,
URL: img.URL,
})
seenSizes[numericSize] = true
}
}
}
return &response, nil
}
func (l *lastfmAgent) GetArtistMBID(ctx context.Context, id string, name string) (string, error) {
a, err := l.callArtistGetInfo(ctx, name, "")
if err != nil {
return "", err
}
if a.MBID == "" {
return "", agents.ErrNotFound
}
return a.MBID, nil
}
func (l *lastfmAgent) GetArtistURL(ctx context.Context, id, name, mbid string) (string, error) {
a, err := l.callArtistGetInfo(ctx, name, mbid)
if err != nil {
return "", err
}
if a.URL == "" {
return "", agents.ErrNotFound
}
return a.URL, nil
}
func (l *lastfmAgent) GetArtistBiography(ctx context.Context, id, name, mbid string) (string, error) {
a, err := l.callArtistGetInfo(ctx, name, mbid)
if err != nil {
return "", err
}
a.Bio.Summary = strings.TrimSpace(a.Bio.Summary)
if a.Bio.Summary == "" {
return "", agents.ErrNotFound
}
for _, ign := range ignoredBiographies {
if strings.HasPrefix(a.Bio.Summary, ign) {
return "", nil
}
}
return a.Bio.Summary, nil
}
func (l *lastfmAgent) GetSimilarArtists(ctx context.Context, id, name, mbid string, limit int) ([]agents.Artist, error) {
resp, err := l.callArtistGetSimilar(ctx, name, mbid, limit)
if err != nil {
return nil, err
}
if len(resp) == 0 {
return nil, agents.ErrNotFound
}
var res []agents.Artist
for _, a := range resp {
res = append(res, agents.Artist{
Name: a.Name,
MBID: a.MBID,
})
}
return res, nil
}
func (l *lastfmAgent) GetArtistTopSongs(ctx context.Context, id, artistName, mbid string, count int) ([]agents.Song, error) {
resp, err := l.callArtistGetTopTracks(ctx, artistName, mbid, count)
if err != nil {
return nil, err
}
if len(resp) == 0 {
return nil, agents.ErrNotFound
}
var res []agents.Song
for _, t := range resp {
res = append(res, agents.Song{
Name: t.Name,
MBID: t.MBID,
})
}
return res, nil
}
func (l *lastfmAgent) callAlbumGetInfo(ctx context.Context, name, artist, mbid string) (*Album, error) {
a, err := l.client.albumGetInfo(ctx, name, artist, mbid)
var lfErr *lastFMError
isLastFMError := errors.As(err, &lfErr)
if mbid != "" && (isLastFMError && lfErr.Code == 6) {
log.Warn(ctx, "LastFM/album.getInfo could not find album by mbid, trying again", "album", name, "mbid", mbid)
return l.callAlbumGetInfo(ctx, name, artist, "")
}
if err != nil {
if isLastFMError && lfErr.Code == 6 {
log.Debug(ctx, "Album not found", "album", name, "mbid", mbid, err)
} else {
log.Error(ctx, "Error calling LastFM/album.getInfo", "album", name, "mbid", mbid, err)
}
return nil, err
}
return a, nil
}
func (l *lastfmAgent) callArtistGetInfo(ctx context.Context, name string, mbid string) (*Artist, error) {
a, err := l.client.artistGetInfo(ctx, name, mbid)
var lfErr *lastFMError
isLastFMError := errors.As(err, &lfErr)
if mbid != "" && ((err == nil && a.Name == "[unknown]") || (isLastFMError && lfErr.Code == 6)) {
log.Warn(ctx, "LastFM/artist.getInfo could not find artist by mbid, trying again", "artist", name, "mbid", mbid)
return l.callArtistGetInfo(ctx, name, "")
}
if err != nil {
log.Error(ctx, "Error calling LastFM/artist.getInfo", "artist", name, "mbid", mbid, err)
return nil, err
}
return a, nil
}
func (l *lastfmAgent) callArtistGetSimilar(ctx context.Context, name string, mbid string, limit int) ([]Artist, error) {
s, err := l.client.artistGetSimilar(ctx, name, mbid, limit)
var lfErr *lastFMError
isLastFMError := errors.As(err, &lfErr)
if mbid != "" && ((err == nil && s.Attr.Artist == "[unknown]") || (isLastFMError && lfErr.Code == 6)) {
log.Warn(ctx, "LastFM/artist.getSimilar could not find artist by mbid, trying again", "artist", name, "mbid", mbid)
return l.callArtistGetSimilar(ctx, name, "", limit)
}
if err != nil {
log.Error(ctx, "Error calling LastFM/artist.getSimilar", "artist", name, "mbid", mbid, err)
return nil, err
}
return s.Artists, nil
}
func (l *lastfmAgent) callArtistGetTopTracks(ctx context.Context, artistName, mbid string, count int) ([]Track, error) {
t, err := l.client.artistGetTopTracks(ctx, artistName, mbid, count)
var lfErr *lastFMError
isLastFMError := errors.As(err, &lfErr)
if mbid != "" && ((err == nil && t.Attr.Artist == "[unknown]") || (isLastFMError && lfErr.Code == 6)) {
log.Warn(ctx, "LastFM/artist.getTopTracks could not find artist by mbid, trying again", "artist", artistName, "mbid", mbid)
return l.callArtistGetTopTracks(ctx, artistName, "", count)
}
if err != nil {
log.Error(ctx, "Error calling LastFM/artist.getTopTracks", "artist", artistName, "mbid", mbid, err)
return nil, err
}
return t.Track, nil
}
func (l *lastfmAgent) NowPlaying(ctx context.Context, userId string, track *model.MediaFile) error {
sk, err := l.sessionKeys.Get(ctx, userId)
if err != nil || sk == "" {
return scrobbler.ErrNotAuthorized
}
err = l.client.updateNowPlaying(ctx, sk, ScrobbleInfo{
artist: track.Artist,
track: track.Title,
album: track.Album,
trackNumber: track.TrackNumber,
mbid: track.MbzTrackID,
duration: int(track.Duration),
albumArtist: track.AlbumArtist,
})
if err != nil {
log.Warn(ctx, "Last.fm client.updateNowPlaying returned error", "track", track.Title, err)
return scrobbler.ErrUnrecoverable
}
return nil
}
func (l *lastfmAgent) Scrobble(ctx context.Context, userId string, s scrobbler.Scrobble) error {
sk, err := l.sessionKeys.Get(ctx, userId)
if err != nil || sk == "" {
return scrobbler.ErrNotAuthorized
}
if s.Duration <= 30 {
log.Debug(ctx, "Skipping Last.fm scrobble for short song", "track", s.Title, "duration", s.Duration)
return nil
}
err = l.client.scrobble(ctx, sk, ScrobbleInfo{
artist: s.Artist,
track: s.Title,
album: s.Album,
trackNumber: s.TrackNumber,
mbid: s.MbzTrackID,
duration: int(s.Duration),
albumArtist: s.AlbumArtist,
timestamp: s.TimeStamp,
})
if err == nil {
return nil
}
var lfErr *lastFMError
isLastFMError := errors.As(err, &lfErr)
if !isLastFMError {
log.Warn(ctx, "Last.fm client.scrobble returned error", "track", s.Title, err)
return scrobbler.ErrRetryLater
}
if lfErr.Code == 11 || lfErr.Code == 16 {
return scrobbler.ErrRetryLater
}
return scrobbler.ErrUnrecoverable
}
func (l *lastfmAgent) IsAuthorized(ctx context.Context, userId string) bool {
sk, err := l.sessionKeys.Get(ctx, userId)
return err == nil && sk != ""
}
func init() {
conf.AddHook(func() {
if conf.Server.LastFM.Enabled {
agents.Register(lastFMAgentName, func(ds model.DataStore) agents.Interface {
return lastFMConstructor(ds)
})
scrobbler.Register(lastFMAgentName, func(ds model.DataStore) scrobbler.Scrobbler {
return lastFMConstructor(ds)
})
}
})
}

View File

@@ -0,0 +1,438 @@
package lastfm
import (
"bytes"
"context"
"errors"
"io"
"net/http"
"os"
"strconv"
"time"
"github.com/navidrome/navidrome/conf"
"github.com/navidrome/navidrome/core/agents"
"github.com/navidrome/navidrome/core/scrobbler"
"github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/tests"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
const (
lastfmError3 = `{"error":3,"message":"Invalid Method - No method with that name in this package","links":[]}`
lastfmError6 = `{"error":6,"message":"The artist you supplied could not be found","links":[]}`
)
var _ = Describe("lastfmAgent", func() {
var ds model.DataStore
var ctx context.Context
BeforeEach(func() {
ds = &tests.MockDataStore{}
ctx = context.Background()
})
Describe("lastFMConstructor", func() {
It("uses configured api key and language", func() {
conf.Server.LastFM.ApiKey = "123"
conf.Server.LastFM.Secret = "secret"
conf.Server.LastFM.Language = "pt"
agent := lastFMConstructor(ds)
Expect(agent.apiKey).To(Equal("123"))
Expect(agent.secret).To(Equal("secret"))
Expect(agent.lang).To(Equal("pt"))
})
})
Describe("GetArtistBiography", func() {
var agent *lastfmAgent
var httpClient *tests.FakeHttpClient
BeforeEach(func() {
httpClient = &tests.FakeHttpClient{}
client := newClient("API_KEY", "SECRET", "pt", httpClient)
agent = lastFMConstructor(ds)
agent.client = client
})
It("returns the biography", func() {
f, _ := os.Open("tests/fixtures/lastfm.artist.getinfo.json")
httpClient.Res = http.Response{Body: f, StatusCode: 200}
Expect(agent.GetArtistBiography(ctx, "123", "U2", "mbid-1234")).To(Equal("U2 é uma das mais importantes bandas de rock de todos os tempos. Formada em 1976 em Dublin, composta por Bono (vocalista e guitarrista), The Edge (guitarrista, pianista e backing vocal), Adam Clayton (baixista), Larry Mullen, Jr. (baterista e percussionista).\n\nDesde a década de 80, U2 é uma das bandas mais populares no mundo. Seus shows são únicos e um verdadeiro festival de efeitos especiais, além de serem um dos que mais arrecadam anualmente. <a href=\"https://www.last.fm/music/U2\">Read more on Last.fm</a>"))
Expect(httpClient.RequestCount).To(Equal(1))
Expect(httpClient.SavedRequest.URL.Query().Get("mbid")).To(Equal("mbid-1234"))
})
It("returns an error if Last.fm call fails", func() {
httpClient.Err = errors.New("error")
_, err := agent.GetArtistBiography(ctx, "123", "U2", "mbid-1234")
Expect(err).To(HaveOccurred())
Expect(httpClient.RequestCount).To(Equal(1))
Expect(httpClient.SavedRequest.URL.Query().Get("mbid")).To(Equal("mbid-1234"))
})
It("returns an error if Last.fm call returns an error", func() {
httpClient.Res = http.Response{Body: io.NopCloser(bytes.NewBufferString(lastfmError3)), StatusCode: 200}
_, err := agent.GetArtistBiography(ctx, "123", "U2", "mbid-1234")
Expect(err).To(HaveOccurred())
Expect(httpClient.RequestCount).To(Equal(1))
Expect(httpClient.SavedRequest.URL.Query().Get("mbid")).To(Equal("mbid-1234"))
})
It("returns an error if Last.fm call returns an error 6 and mbid is empty", func() {
httpClient.Res = http.Response{Body: io.NopCloser(bytes.NewBufferString(lastfmError6)), StatusCode: 200}
_, err := agent.GetArtistBiography(ctx, "123", "U2", "")
Expect(err).To(HaveOccurred())
Expect(httpClient.RequestCount).To(Equal(1))
})
Context("MBID non existent in Last.fm", func() {
It("calls again when the response is artist == [unknown]", func() {
f, _ := os.Open("tests/fixtures/lastfm.artist.getinfo.unknown.json")
httpClient.Res = http.Response{Body: f, StatusCode: 200}
_, _ = agent.GetArtistBiography(ctx, "123", "U2", "mbid-1234")
Expect(httpClient.RequestCount).To(Equal(2))
Expect(httpClient.SavedRequest.URL.Query().Get("mbid")).To(BeEmpty())
})
It("calls again when last.fm returns an error 6", func() {
httpClient.Res = http.Response{Body: io.NopCloser(bytes.NewBufferString(lastfmError6)), StatusCode: 200}
_, _ = agent.GetArtistBiography(ctx, "123", "U2", "mbid-1234")
Expect(httpClient.RequestCount).To(Equal(2))
Expect(httpClient.SavedRequest.URL.Query().Get("mbid")).To(BeEmpty())
})
})
})
Describe("GetSimilarArtists", func() {
var agent *lastfmAgent
var httpClient *tests.FakeHttpClient
BeforeEach(func() {
httpClient = &tests.FakeHttpClient{}
client := newClient("API_KEY", "SECRET", "pt", httpClient)
agent = lastFMConstructor(ds)
agent.client = client
})
It("returns similar artists", func() {
f, _ := os.Open("tests/fixtures/lastfm.artist.getsimilar.json")
httpClient.Res = http.Response{Body: f, StatusCode: 200}
Expect(agent.GetSimilarArtists(ctx, "123", "U2", "mbid-1234", 2)).To(Equal([]agents.Artist{
{Name: "Passengers", MBID: "e110c11f-1c94-4471-a350-c38f46b29389"},
{Name: "INXS", MBID: "481bf5f9-2e7c-4c44-b08a-05b32bc7c00d"},
}))
Expect(httpClient.RequestCount).To(Equal(1))
Expect(httpClient.SavedRequest.URL.Query().Get("mbid")).To(Equal("mbid-1234"))
})
It("returns an error if Last.fm call fails", func() {
httpClient.Err = errors.New("error")
_, err := agent.GetSimilarArtists(ctx, "123", "U2", "mbid-1234", 2)
Expect(err).To(HaveOccurred())
Expect(httpClient.RequestCount).To(Equal(1))
Expect(httpClient.SavedRequest.URL.Query().Get("mbid")).To(Equal("mbid-1234"))
})
It("returns an error if Last.fm call returns an error", func() {
httpClient.Res = http.Response{Body: io.NopCloser(bytes.NewBufferString(lastfmError3)), StatusCode: 200}
_, err := agent.GetSimilarArtists(ctx, "123", "U2", "mbid-1234", 2)
Expect(err).To(HaveOccurred())
Expect(httpClient.RequestCount).To(Equal(1))
Expect(httpClient.SavedRequest.URL.Query().Get("mbid")).To(Equal("mbid-1234"))
})
It("returns an error if Last.fm call returns an error 6 and mbid is empty", func() {
httpClient.Res = http.Response{Body: io.NopCloser(bytes.NewBufferString(lastfmError6)), StatusCode: 200}
_, err := agent.GetSimilarArtists(ctx, "123", "U2", "", 2)
Expect(err).To(HaveOccurred())
Expect(httpClient.RequestCount).To(Equal(1))
})
Context("MBID non existent in Last.fm", func() {
It("calls again when the response is artist == [unknown]", func() {
f, _ := os.Open("tests/fixtures/lastfm.artist.getsimilar.unknown.json")
httpClient.Res = http.Response{Body: f, StatusCode: 200}
_, _ = agent.GetSimilarArtists(ctx, "123", "U2", "mbid-1234", 2)
Expect(httpClient.RequestCount).To(Equal(2))
Expect(httpClient.SavedRequest.URL.Query().Get("mbid")).To(BeEmpty())
})
It("calls again when last.fm returns an error 6", func() {
httpClient.Res = http.Response{Body: io.NopCloser(bytes.NewBufferString(lastfmError6)), StatusCode: 200}
_, _ = agent.GetSimilarArtists(ctx, "123", "U2", "mbid-1234", 2)
Expect(httpClient.RequestCount).To(Equal(2))
Expect(httpClient.SavedRequest.URL.Query().Get("mbid")).To(BeEmpty())
})
})
})
Describe("GetArtistTopSongs", func() {
var agent *lastfmAgent
var httpClient *tests.FakeHttpClient
BeforeEach(func() {
httpClient = &tests.FakeHttpClient{}
client := newClient("API_KEY", "SECRET", "pt", httpClient)
agent = lastFMConstructor(ds)
agent.client = client
})
It("returns top songs", func() {
f, _ := os.Open("tests/fixtures/lastfm.artist.gettoptracks.json")
httpClient.Res = http.Response{Body: f, StatusCode: 200}
Expect(agent.GetArtistTopSongs(ctx, "123", "U2", "mbid-1234", 2)).To(Equal([]agents.Song{
{Name: "Beautiful Day", MBID: "f7f264d0-a89b-4682-9cd7-a4e7c37637af"},
{Name: "With or Without You", MBID: "6b9a509f-6907-4a6e-9345-2f12da09ba4b"},
}))
Expect(httpClient.RequestCount).To(Equal(1))
Expect(httpClient.SavedRequest.URL.Query().Get("mbid")).To(Equal("mbid-1234"))
})
It("returns an error if Last.fm call fails", func() {
httpClient.Err = errors.New("error")
_, err := agent.GetArtistTopSongs(ctx, "123", "U2", "mbid-1234", 2)
Expect(err).To(HaveOccurred())
Expect(httpClient.RequestCount).To(Equal(1))
Expect(httpClient.SavedRequest.URL.Query().Get("mbid")).To(Equal("mbid-1234"))
})
It("returns an error if Last.fm call returns an error", func() {
httpClient.Res = http.Response{Body: io.NopCloser(bytes.NewBufferString(lastfmError3)), StatusCode: 200}
_, err := agent.GetArtistTopSongs(ctx, "123", "U2", "mbid-1234", 2)
Expect(err).To(HaveOccurred())
Expect(httpClient.RequestCount).To(Equal(1))
Expect(httpClient.SavedRequest.URL.Query().Get("mbid")).To(Equal("mbid-1234"))
})
It("returns an error if Last.fm call returns an error 6 and mbid is empty", func() {
httpClient.Res = http.Response{Body: io.NopCloser(bytes.NewBufferString(lastfmError6)), StatusCode: 200}
_, err := agent.GetArtistTopSongs(ctx, "123", "U2", "", 2)
Expect(err).To(HaveOccurred())
Expect(httpClient.RequestCount).To(Equal(1))
})
Context("MBID non existent in Last.fm", func() {
It("calls again when the response is artist == [unknown]", func() {
f, _ := os.Open("tests/fixtures/lastfm.artist.gettoptracks.unknown.json")
httpClient.Res = http.Response{Body: f, StatusCode: 200}
_, _ = agent.GetArtistTopSongs(ctx, "123", "U2", "mbid-1234", 2)
Expect(httpClient.RequestCount).To(Equal(2))
Expect(httpClient.SavedRequest.URL.Query().Get("mbid")).To(BeEmpty())
})
It("calls again when last.fm returns an error 6", func() {
httpClient.Res = http.Response{Body: io.NopCloser(bytes.NewBufferString(lastfmError6)), StatusCode: 200}
_, _ = agent.GetArtistTopSongs(ctx, "123", "U2", "mbid-1234", 2)
Expect(httpClient.RequestCount).To(Equal(2))
Expect(httpClient.SavedRequest.URL.Query().Get("mbid")).To(BeEmpty())
})
})
})
Describe("Scrobbling", func() {
var agent *lastfmAgent
var httpClient *tests.FakeHttpClient
var track *model.MediaFile
BeforeEach(func() {
_ = ds.UserProps(ctx).Put("user-1", sessionKeyProperty, "SK-1")
httpClient = &tests.FakeHttpClient{}
client := newClient("API_KEY", "SECRET", "en", httpClient)
agent = lastFMConstructor(ds)
agent.client = client
track = &model.MediaFile{
ID: "123",
Title: "Track Title",
Album: "Track Album",
Artist: "Track Artist",
AlbumArtist: "Track AlbumArtist",
TrackNumber: 1,
Duration: 180,
MbzTrackID: "mbz-123",
}
})
Describe("NowPlaying", func() {
It("calls Last.fm with correct params", func() {
httpClient.Res = http.Response{Body: io.NopCloser(bytes.NewBufferString("{}")), StatusCode: 200}
err := agent.NowPlaying(ctx, "user-1", track)
Expect(err).ToNot(HaveOccurred())
Expect(httpClient.SavedRequest.Method).To(Equal(http.MethodPost))
sentParams := httpClient.SavedRequest.URL.Query()
Expect(sentParams.Get("method")).To(Equal("track.updateNowPlaying"))
Expect(sentParams.Get("sk")).To(Equal("SK-1"))
Expect(sentParams.Get("track")).To(Equal(track.Title))
Expect(sentParams.Get("album")).To(Equal(track.Album))
Expect(sentParams.Get("artist")).To(Equal(track.Artist))
Expect(sentParams.Get("albumArtist")).To(Equal(track.AlbumArtist))
Expect(sentParams.Get("trackNumber")).To(Equal(strconv.Itoa(track.TrackNumber)))
Expect(sentParams.Get("duration")).To(Equal(strconv.FormatFloat(float64(track.Duration), 'G', -1, 32)))
Expect(sentParams.Get("mbid")).To(Equal(track.MbzTrackID))
})
It("returns ErrNotAuthorized if user is not linked", func() {
err := agent.NowPlaying(ctx, "user-2", track)
Expect(err).To(MatchError(scrobbler.ErrNotAuthorized))
})
})
Describe("scrobble", func() {
It("calls Last.fm with correct params", func() {
ts := time.Now()
httpClient.Res = http.Response{Body: io.NopCloser(bytes.NewBufferString("{}")), StatusCode: 200}
err := agent.Scrobble(ctx, "user-1", scrobbler.Scrobble{MediaFile: *track, TimeStamp: ts})
Expect(err).ToNot(HaveOccurred())
Expect(httpClient.SavedRequest.Method).To(Equal(http.MethodPost))
sentParams := httpClient.SavedRequest.URL.Query()
Expect(sentParams.Get("method")).To(Equal("track.scrobble"))
Expect(sentParams.Get("sk")).To(Equal("SK-1"))
Expect(sentParams.Get("track")).To(Equal(track.Title))
Expect(sentParams.Get("album")).To(Equal(track.Album))
Expect(sentParams.Get("artist")).To(Equal(track.Artist))
Expect(sentParams.Get("albumArtist")).To(Equal(track.AlbumArtist))
Expect(sentParams.Get("trackNumber")).To(Equal(strconv.Itoa(track.TrackNumber)))
Expect(sentParams.Get("duration")).To(Equal(strconv.FormatFloat(float64(track.Duration), 'G', -1, 32)))
Expect(sentParams.Get("mbid")).To(Equal(track.MbzTrackID))
Expect(sentParams.Get("timestamp")).To(Equal(strconv.FormatInt(ts.Unix(), 10)))
})
It("skips songs with less than 31 seconds", func() {
track.Duration = 29
httpClient.Res = http.Response{Body: io.NopCloser(bytes.NewBufferString("{}")), StatusCode: 200}
err := agent.Scrobble(ctx, "user-1", scrobbler.Scrobble{MediaFile: *track, TimeStamp: time.Now()})
Expect(err).ToNot(HaveOccurred())
Expect(httpClient.SavedRequest).To(BeNil())
})
It("returns ErrNotAuthorized if user is not linked", func() {
err := agent.Scrobble(ctx, "user-2", scrobbler.Scrobble{MediaFile: *track, TimeStamp: time.Now()})
Expect(err).To(MatchError(scrobbler.ErrNotAuthorized))
})
It("returns ErrRetryLater on error 11", func() {
httpClient.Res = http.Response{
Body: io.NopCloser(bytes.NewBufferString(`{"error":11,"message":"Service Offline - This service is temporarily offline. Try again later."}`)),
StatusCode: 400,
}
err := agent.Scrobble(ctx, "user-1", scrobbler.Scrobble{MediaFile: *track, TimeStamp: time.Now()})
Expect(err).To(MatchError(scrobbler.ErrRetryLater))
})
It("returns ErrRetryLater on error 16", func() {
httpClient.Res = http.Response{
Body: io.NopCloser(bytes.NewBufferString(`{"error":16,"message":"There was a temporary error processing your request. Please try again"}`)),
StatusCode: 400,
}
err := agent.Scrobble(ctx, "user-1", scrobbler.Scrobble{MediaFile: *track, TimeStamp: time.Now()})
Expect(err).To(MatchError(scrobbler.ErrRetryLater))
})
It("returns ErrRetryLater on http errors", func() {
httpClient.Res = http.Response{
Body: io.NopCloser(bytes.NewBufferString(`internal server error`)),
StatusCode: 500,
}
err := agent.Scrobble(ctx, "user-1", scrobbler.Scrobble{MediaFile: *track, TimeStamp: time.Now()})
Expect(err).To(MatchError(scrobbler.ErrRetryLater))
})
It("returns ErrUnrecoverable on other errors", func() {
httpClient.Res = http.Response{
Body: io.NopCloser(bytes.NewBufferString(`{"error":8,"message":"Operation failed - Something else went wrong"}`)),
StatusCode: 400,
}
err := agent.Scrobble(ctx, "user-1", scrobbler.Scrobble{MediaFile: *track, TimeStamp: time.Now()})
Expect(err).To(MatchError(scrobbler.ErrUnrecoverable))
})
})
})
Describe("GetAlbumInfo", func() {
var agent *lastfmAgent
var httpClient *tests.FakeHttpClient
BeforeEach(func() {
httpClient = &tests.FakeHttpClient{}
client := newClient("API_KEY", "SECRET", "pt", httpClient)
agent = lastFMConstructor(ds)
agent.client = client
})
It("returns the biography", func() {
f, _ := os.Open("tests/fixtures/lastfm.album.getinfo.json")
httpClient.Res = http.Response{Body: f, StatusCode: 200}
Expect(agent.GetAlbumInfo(ctx, "Believe", "Cher", "03c91c40-49a6-44a7-90e7-a700edf97a62")).To(Equal(&agents.AlbumInfo{
Name: "Believe",
MBID: "03c91c40-49a6-44a7-90e7-a700edf97a62",
Description: "Believe is the twenty-third studio album by American singer-actress Cher, released on November 10, 1998 by Warner Bros. Records. The RIAA certified it Quadruple Platinum on December 23, 1999, recognizing four million shipments in the United States; Worldwide, the album has sold more than 20 million copies, making it the biggest-selling album of her career. In 1999 the album received three Grammy Awards nominations including \"Record of the Year\", \"Best Pop Album\" and winning \"Best Dance Recording\" for the single \"Believe\". It was released by Warner Bros. Records at the end of 1998. The album was executive produced by Rob <a href=\"https://www.last.fm/music/Cher/Believe\">Read more on Last.fm</a>.",
URL: "https://www.last.fm/music/Cher/Believe",
Images: []agents.ExternalImage{
{
URL: "https://lastfm.freetls.fastly.net/i/u/34s/3b54885952161aaea4ce2965b2db1638.png",
Size: 34,
},
{
URL: "https://lastfm.freetls.fastly.net/i/u/64s/3b54885952161aaea4ce2965b2db1638.png",
Size: 64,
},
{
URL: "https://lastfm.freetls.fastly.net/i/u/174s/3b54885952161aaea4ce2965b2db1638.png",
Size: 174,
},
{
URL: "https://lastfm.freetls.fastly.net/i/u/300x300/3b54885952161aaea4ce2965b2db1638.png",
Size: 300,
},
},
}))
Expect(httpClient.RequestCount).To(Equal(1))
Expect(httpClient.SavedRequest.URL.Query().Get("mbid")).To(Equal("03c91c40-49a6-44a7-90e7-a700edf97a62"))
})
It("returns empty images if no images are available", func() {
f, _ := os.Open("tests/fixtures/lastfm.album.getinfo.empty_urls.json")
httpClient.Res = http.Response{Body: f, StatusCode: 200}
Expect(agent.GetAlbumInfo(ctx, "The Definitive Less Damage And More Joy", "The Jesus and Mary Chain", "")).To(Equal(&agents.AlbumInfo{
Name: "The Definitive Less Damage And More Joy",
URL: "https://www.last.fm/music/The+Jesus+and+Mary+Chain/The+Definitive+Less+Damage+And+More+Joy",
Images: []agents.ExternalImage{},
}))
Expect(httpClient.RequestCount).To(Equal(1))
Expect(httpClient.SavedRequest.URL.Query().Get("album")).To(Equal("The Definitive Less Damage And More Joy"))
})
It("returns an error if Last.fm call fails", func() {
httpClient.Err = errors.New("error")
_, err := agent.GetAlbumInfo(ctx, "123", "U2", "mbid-1234")
Expect(err).To(HaveOccurred())
Expect(httpClient.RequestCount).To(Equal(1))
Expect(httpClient.SavedRequest.URL.Query().Get("mbid")).To(Equal("mbid-1234"))
})
It("returns an error if Last.fm call returns an error", func() {
httpClient.Res = http.Response{Body: io.NopCloser(bytes.NewBufferString(lastfmError3)), StatusCode: 200}
_, err := agent.GetAlbumInfo(ctx, "123", "U2", "mbid-1234")
Expect(err).To(HaveOccurred())
Expect(httpClient.RequestCount).To(Equal(1))
Expect(httpClient.SavedRequest.URL.Query().Get("mbid")).To(Equal("mbid-1234"))
})
It("returns an error if Last.fm call returns an error 6 and mbid is empty", func() {
httpClient.Res = http.Response{Body: io.NopCloser(bytes.NewBufferString(lastfmError6)), StatusCode: 200}
_, err := agent.GetAlbumInfo(ctx, "123", "U2", "")
Expect(err).To(HaveOccurred())
Expect(httpClient.RequestCount).To(Equal(1))
})
Context("MBID non existent in Last.fm", func() {
It("calls again when last.fm returns an error 6", func() {
httpClient.Res = http.Response{Body: io.NopCloser(bytes.NewBufferString(lastfmError6)), StatusCode: 200}
_, _ = agent.GetAlbumInfo(ctx, "123", "U2", "mbid-1234")
Expect(httpClient.RequestCount).To(Equal(2))
Expect(httpClient.SavedRequest.URL.Query().Get("mbid")).To(BeEmpty())
})
})
})
})

View File

@@ -0,0 +1,129 @@
package lastfm
import (
"bytes"
"context"
_ "embed"
"errors"
"net/http"
"time"
"github.com/deluan/rest"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/navidrome/navidrome/conf"
"github.com/navidrome/navidrome/consts"
"github.com/navidrome/navidrome/core/agents"
"github.com/navidrome/navidrome/log"
"github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/model/request"
"github.com/navidrome/navidrome/server"
"github.com/navidrome/navidrome/utils"
)
//go:embed token_received.html
var tokenReceivedPage []byte
type Router struct {
http.Handler
ds model.DataStore
sessionKeys *agents.SessionKeys
client *client
apiKey string
secret string
}
func NewRouter(ds model.DataStore) *Router {
r := &Router{
ds: ds,
apiKey: conf.Server.LastFM.ApiKey,
secret: conf.Server.LastFM.Secret,
sessionKeys: &agents.SessionKeys{DataStore: ds, KeyName: sessionKeyProperty},
}
r.Handler = r.routes()
hc := &http.Client{
Timeout: consts.DefaultHttpClientTimeOut,
}
r.client = newClient(r.apiKey, r.secret, "en", hc)
return r
}
func (s *Router) routes() http.Handler {
r := chi.NewRouter()
r.Group(func(r chi.Router) {
r.Use(server.Authenticator(s.ds))
r.Use(server.JWTRefresher)
r.Get("/link", s.getLinkStatus)
r.Delete("/link", s.unlink)
})
r.Get("/link/callback", s.callback)
return r
}
func (s *Router) getLinkStatus(w http.ResponseWriter, r *http.Request) {
resp := map[string]interface{}{}
u, _ := request.UserFrom(r.Context())
key, err := s.sessionKeys.Get(r.Context(), u.ID)
if err != nil && !errors.Is(err, model.ErrNotFound) {
resp["error"] = err
resp["status"] = false
_ = rest.RespondWithJSON(w, http.StatusInternalServerError, resp)
return
}
resp["status"] = key != ""
_ = rest.RespondWithJSON(w, http.StatusOK, resp)
}
func (s *Router) unlink(w http.ResponseWriter, r *http.Request) {
u, _ := request.UserFrom(r.Context())
err := s.sessionKeys.Delete(r.Context(), u.ID)
if err != nil {
_ = rest.RespondWithError(w, http.StatusInternalServerError, err.Error())
} else {
_ = rest.RespondWithJSON(w, http.StatusOK, map[string]string{})
}
}
func (s *Router) callback(w http.ResponseWriter, r *http.Request) {
token := utils.ParamString(r, "token")
if token == "" {
_ = rest.RespondWithError(w, http.StatusBadRequest, "token not received")
return
}
uid := utils.ParamString(r, "uid")
if uid == "" {
_ = rest.RespondWithError(w, http.StatusBadRequest, "uid not received")
return
}
// Need to add user to context, as this is a non-authenticated endpoint, so it does not
// automatically contain any user info
ctx := request.WithUser(r.Context(), model.User{ID: uid})
err := s.fetchSessionKey(ctx, uid, token)
if err != nil {
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.WriteHeader(http.StatusBadRequest)
_, _ = w.Write([]byte("An error occurred while authorizing with Last.fm. \n\nRequest ID: " + middleware.GetReqID(ctx)))
return
}
http.ServeContent(w, r, "response", time.Now(), bytes.NewReader(tokenReceivedPage))
}
func (s *Router) fetchSessionKey(ctx context.Context, uid, token string) error {
sessionKey, err := s.client.getSession(ctx, token)
if err != nil {
log.Error(ctx, "Could not fetch LastFM session key", "userId", uid, "token", token,
"requestId", middleware.GetReqID(ctx), err)
return err
}
err = s.sessionKeys.Put(ctx, uid, sessionKey)
if err != nil {
log.Error("Could not save LastFM session key", "userId", uid, "requestId", middleware.GetReqID(ctx), err)
}
return err
}

View File

@@ -0,0 +1,236 @@
package lastfm
import (
"context"
"crypto/md5"
"encoding/hex"
"encoding/json"
"fmt"
"net/http"
"net/url"
"sort"
"strconv"
"strings"
"time"
"github.com/navidrome/navidrome/log"
"golang.org/x/exp/slices"
)
const (
apiBaseUrl = "https://ws.audioscrobbler.com/2.0/"
)
type lastFMError struct {
Code int
Message string
}
func (e *lastFMError) Error() string {
return fmt.Sprintf("last.fm error(%d): %s", e.Code, e.Message)
}
type httpDoer interface {
Do(req *http.Request) (*http.Response, error)
}
func newClient(apiKey string, secret string, lang string, hc httpDoer) *client {
return &client{apiKey, secret, lang, hc}
}
type client struct {
apiKey string
secret string
lang string
hc httpDoer
}
func (c *client) albumGetInfo(ctx context.Context, name string, artist string, mbid string) (*Album, error) {
params := url.Values{}
params.Add("method", "album.getInfo")
params.Add("album", name)
params.Add("artist", artist)
params.Add("mbid", mbid)
params.Add("lang", c.lang)
response, err := c.makeRequest(ctx, http.MethodGet, params, false)
if err != nil {
return nil, err
}
return &response.Album, nil
}
func (c *client) artistGetInfo(ctx context.Context, name string, mbid string) (*Artist, error) {
params := url.Values{}
params.Add("method", "artist.getInfo")
params.Add("artist", name)
params.Add("mbid", mbid)
params.Add("lang", c.lang)
response, err := c.makeRequest(ctx, http.MethodGet, params, false)
if err != nil {
return nil, err
}
return &response.Artist, nil
}
func (c *client) artistGetSimilar(ctx context.Context, name string, mbid string, limit int) (*SimilarArtists, error) {
params := url.Values{}
params.Add("method", "artist.getSimilar")
params.Add("artist", name)
params.Add("mbid", mbid)
params.Add("limit", strconv.Itoa(limit))
response, err := c.makeRequest(ctx, http.MethodGet, params, false)
if err != nil {
return nil, err
}
return &response.SimilarArtists, nil
}
func (c *client) artistGetTopTracks(ctx context.Context, name string, mbid string, limit int) (*TopTracks, error) {
params := url.Values{}
params.Add("method", "artist.getTopTracks")
params.Add("artist", name)
params.Add("mbid", mbid)
params.Add("limit", strconv.Itoa(limit))
response, err := c.makeRequest(ctx, http.MethodGet, params, false)
if err != nil {
return nil, err
}
return &response.TopTracks, nil
}
func (c *client) GetToken(ctx context.Context) (string, error) {
params := url.Values{}
params.Add("method", "auth.getToken")
c.sign(params)
response, err := c.makeRequest(ctx, http.MethodGet, params, true)
if err != nil {
return "", err
}
return response.Token, nil
}
func (c *client) getSession(ctx context.Context, token string) (string, error) {
params := url.Values{}
params.Add("method", "auth.getSession")
params.Add("token", token)
response, err := c.makeRequest(ctx, http.MethodGet, params, true)
if err != nil {
return "", err
}
return response.Session.Key, nil
}
type ScrobbleInfo struct {
artist string
track string
album string
trackNumber int
mbid string
duration int
albumArtist string
timestamp time.Time
}
func (c *client) updateNowPlaying(ctx context.Context, sessionKey string, info ScrobbleInfo) error {
params := url.Values{}
params.Add("method", "track.updateNowPlaying")
params.Add("artist", info.artist)
params.Add("track", info.track)
params.Add("album", info.album)
params.Add("trackNumber", strconv.Itoa(info.trackNumber))
params.Add("mbid", info.mbid)
params.Add("duration", strconv.Itoa(info.duration))
params.Add("albumArtist", info.albumArtist)
params.Add("sk", sessionKey)
resp, err := c.makeRequest(ctx, http.MethodPost, params, true)
if err != nil {
return err
}
if resp.NowPlaying.IgnoredMessage.Code != "0" {
log.Warn(ctx, "LastFM: NowPlaying was ignored", "code", resp.NowPlaying.IgnoredMessage.Code,
"text", resp.NowPlaying.IgnoredMessage.Text)
}
return nil
}
func (c *client) scrobble(ctx context.Context, sessionKey string, info ScrobbleInfo) error {
params := url.Values{}
params.Add("method", "track.scrobble")
params.Add("timestamp", strconv.FormatInt(info.timestamp.Unix(), 10))
params.Add("artist", info.artist)
params.Add("track", info.track)
params.Add("album", info.album)
params.Add("trackNumber", strconv.Itoa(info.trackNumber))
params.Add("mbid", info.mbid)
params.Add("duration", strconv.Itoa(info.duration))
params.Add("albumArtist", info.albumArtist)
params.Add("sk", sessionKey)
resp, err := c.makeRequest(ctx, http.MethodPost, params, true)
if err != nil {
return err
}
if resp.Scrobbles.Scrobble.IgnoredMessage.Code != "0" {
log.Warn(ctx, "LastFM: scrobble was ignored", "code", resp.Scrobbles.Scrobble.IgnoredMessage.Code,
"text", resp.Scrobbles.Scrobble.IgnoredMessage.Text, "info", info)
}
if resp.Scrobbles.Attr.Accepted != 1 {
log.Warn(ctx, "LastFM: scrobble was not accepted", "code", resp.Scrobbles.Scrobble.IgnoredMessage.Code,
"text", resp.Scrobbles.Scrobble.IgnoredMessage.Text, "info", info)
}
return nil
}
func (c *client) makeRequest(ctx context.Context, method string, params url.Values, signed bool) (*Response, error) {
params.Add("format", "json")
params.Add("api_key", c.apiKey)
if signed {
c.sign(params)
}
req, _ := http.NewRequestWithContext(ctx, method, apiBaseUrl, nil)
req.URL.RawQuery = params.Encode()
log.Trace(ctx, fmt.Sprintf("Sending Last.fm %s request", req.Method), "url", req.URL)
resp, err := c.hc.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
decoder := json.NewDecoder(resp.Body)
var response Response
jsonErr := decoder.Decode(&response)
if resp.StatusCode != 200 && jsonErr != nil {
return nil, fmt.Errorf("last.fm http status: (%d)", resp.StatusCode)
}
if jsonErr != nil {
return nil, jsonErr
}
if response.Error != 0 {
return &response, &lastFMError{Code: response.Error, Message: response.Message}
}
return &response, nil
}
func (c *client) sign(params url.Values) {
// the parameters must be in order before hashing
keys := make([]string, 0, len(params))
for k := range params {
if slices.Contains([]string{"format", "callback"}, k) {
continue
}
keys = append(keys, k)
}
sort.Strings(keys)
msg := strings.Builder{}
for _, k := range keys {
msg.WriteString(k)
msg.WriteString(params[k][0])
}
msg.WriteString(c.secret)
hash := md5.Sum([]byte(msg.String()))
params.Add("api_sig", hex.EncodeToString(hash[:]))
}

View File

@@ -0,0 +1,173 @@
package lastfm
import (
"bytes"
"context"
"crypto/md5"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"os"
"github.com/navidrome/navidrome/tests"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("client", func() {
var httpClient *tests.FakeHttpClient
var client *client
BeforeEach(func() {
httpClient = &tests.FakeHttpClient{}
client = newClient("API_KEY", "SECRET", "pt", httpClient)
})
Describe("albumGetInfo", func() {
It("returns an album on successful response", func() {
f, _ := os.Open("tests/fixtures/lastfm.album.getinfo.json")
httpClient.Res = http.Response{Body: f, StatusCode: 200}
album, err := client.albumGetInfo(context.Background(), "Believe", "U2", "mbid-1234")
Expect(err).To(BeNil())
Expect(album.Name).To(Equal("Believe"))
Expect(httpClient.SavedRequest.URL.String()).To(Equal(apiBaseUrl + "?album=Believe&api_key=API_KEY&artist=U2&format=json&lang=pt&mbid=mbid-1234&method=album.getInfo"))
})
})
Describe("artistGetInfo", func() {
It("returns an artist for a successful response", func() {
f, _ := os.Open("tests/fixtures/lastfm.artist.getinfo.json")
httpClient.Res = http.Response{Body: f, StatusCode: 200}
artist, err := client.artistGetInfo(context.Background(), "U2", "123")
Expect(err).To(BeNil())
Expect(artist.Name).To(Equal("U2"))
Expect(httpClient.SavedRequest.URL.String()).To(Equal(apiBaseUrl + "?api_key=API_KEY&artist=U2&format=json&lang=pt&mbid=123&method=artist.getInfo"))
})
It("fails if Last.fm returns an http status != 200", func() {
httpClient.Res = http.Response{
Body: io.NopCloser(bytes.NewBufferString(`Internal Server Error`)),
StatusCode: 500,
}
_, err := client.artistGetInfo(context.Background(), "U2", "123")
Expect(err).To(MatchError("last.fm http status: (500)"))
})
It("fails if Last.fm returns an http status != 200", func() {
httpClient.Res = http.Response{
Body: io.NopCloser(bytes.NewBufferString(`{"error":3,"message":"Invalid Method - No method with that name in this package"}`)),
StatusCode: 400,
}
_, err := client.artistGetInfo(context.Background(), "U2", "123")
Expect(err).To(MatchError(&lastFMError{Code: 3, Message: "Invalid Method - No method with that name in this package"}))
})
It("fails if Last.fm returns an error", func() {
httpClient.Res = http.Response{
Body: io.NopCloser(bytes.NewBufferString(`{"error":6,"message":"The artist you supplied could not be found"}`)),
StatusCode: 200,
}
_, err := client.artistGetInfo(context.Background(), "U2", "123")
Expect(err).To(MatchError(&lastFMError{Code: 6, Message: "The artist you supplied could not be found"}))
})
It("fails if HttpClient.Do() returns error", func() {
httpClient.Err = errors.New("generic error")
_, err := client.artistGetInfo(context.Background(), "U2", "123")
Expect(err).To(MatchError("generic error"))
})
It("fails if returned body is not a valid JSON", func() {
httpClient.Res = http.Response{
Body: io.NopCloser(bytes.NewBufferString(`<xml>NOT_VALID_JSON</xml>`)),
StatusCode: 200,
}
_, err := client.artistGetInfo(context.Background(), "U2", "123")
Expect(err).To(MatchError("invalid character '<' looking for beginning of value"))
})
})
Describe("artistGetSimilar", func() {
It("returns an artist for a successful response", func() {
f, _ := os.Open("tests/fixtures/lastfm.artist.getsimilar.json")
httpClient.Res = http.Response{Body: f, StatusCode: 200}
similar, err := client.artistGetSimilar(context.Background(), "U2", "123", 2)
Expect(err).To(BeNil())
Expect(len(similar.Artists)).To(Equal(2))
Expect(httpClient.SavedRequest.URL.String()).To(Equal(apiBaseUrl + "?api_key=API_KEY&artist=U2&format=json&limit=2&mbid=123&method=artist.getSimilar"))
})
})
Describe("artistGetTopTracks", func() {
It("returns top tracks for a successful response", func() {
f, _ := os.Open("tests/fixtures/lastfm.artist.gettoptracks.json")
httpClient.Res = http.Response{Body: f, StatusCode: 200}
top, err := client.artistGetTopTracks(context.Background(), "U2", "123", 2)
Expect(err).To(BeNil())
Expect(len(top.Track)).To(Equal(2))
Expect(httpClient.SavedRequest.URL.String()).To(Equal(apiBaseUrl + "?api_key=API_KEY&artist=U2&format=json&limit=2&mbid=123&method=artist.getTopTracks"))
})
})
Describe("GetToken", func() {
It("returns a token when the request is successful", func() {
httpClient.Res = http.Response{
Body: io.NopCloser(bytes.NewBufferString(`{"token":"TOKEN"}`)),
StatusCode: 200,
}
Expect(client.GetToken(context.Background())).To(Equal("TOKEN"))
queryParams := httpClient.SavedRequest.URL.Query()
Expect(queryParams.Get("method")).To(Equal("auth.getToken"))
Expect(queryParams.Get("format")).To(Equal("json"))
Expect(queryParams.Get("api_key")).To(Equal("API_KEY"))
Expect(queryParams.Get("api_sig")).ToNot(BeEmpty())
})
})
Describe("getSession", func() {
It("returns a session key when the request is successful", func() {
httpClient.Res = http.Response{
Body: io.NopCloser(bytes.NewBufferString(`{"session":{"name":"Navidrome","key":"SESSION_KEY","subscriber":0}}`)),
StatusCode: 200,
}
Expect(client.getSession(context.Background(), "TOKEN")).To(Equal("SESSION_KEY"))
queryParams := httpClient.SavedRequest.URL.Query()
Expect(queryParams.Get("method")).To(Equal("auth.getSession"))
Expect(queryParams.Get("format")).To(Equal("json"))
Expect(queryParams.Get("token")).To(Equal("TOKEN"))
Expect(queryParams.Get("api_key")).To(Equal("API_KEY"))
Expect(queryParams.Get("api_sig")).ToNot(BeEmpty())
})
})
Describe("sign", func() {
It("adds an api_sig param with the signature", func() {
params := url.Values{}
params.Add("d", "444")
params.Add("callback", "https://myserver.com")
params.Add("a", "111")
params.Add("format", "json")
params.Add("c", "333")
params.Add("b", "222")
client.sign(params)
Expect(params).To(HaveKey("api_sig"))
sig := params.Get("api_sig")
expected := fmt.Sprintf("%x", md5.Sum([]byte("a111b222c333d444SECRET")))
Expect(sig).To(Equal(expected))
})
})
})

View File

@@ -0,0 +1,17 @@
package lastfm
import (
"testing"
"github.com/navidrome/navidrome/log"
"github.com/navidrome/navidrome/tests"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
func TestLastFM(t *testing.T) {
tests.Init(t, false)
log.SetLevel(log.LevelFatal)
RegisterFailHandler(Fail)
RunSpecs(t, "LastFM Test Suite")
}

View File

@@ -0,0 +1,119 @@
package lastfm
type Response struct {
Artist Artist `json:"artist"`
SimilarArtists SimilarArtists `json:"similarartists"`
TopTracks TopTracks `json:"toptracks"`
Album Album `json:"album"`
Error int `json:"error"`
Message string `json:"message"`
Token string `json:"token"`
Session Session `json:"session"`
NowPlaying NowPlaying `json:"nowplaying"`
Scrobbles Scrobbles `json:"scrobbles"`
}
type Album struct {
Name string `json:"name"`
MBID string `json:"mbid"`
URL string `json:"url"`
Image []ExternalImage `json:"image"`
Description Description `json:"wiki"`
}
type Artist struct {
Name string `json:"name"`
MBID string `json:"mbid"`
URL string `json:"url"`
Image []ExternalImage `json:"image"`
Bio Description `json:"bio"`
}
type SimilarArtists struct {
Artists []Artist `json:"artist"`
Attr Attr `json:"@attr"`
}
type Attr struct {
Artist string `json:"artist"`
}
type ExternalImage struct {
URL string `json:"#text"`
Size string `json:"size"`
}
type Description struct {
Published string `json:"published"`
Summary string `json:"summary"`
Content string `json:"content"`
}
type Track struct {
Name string `json:"name"`
MBID string `json:"mbid"`
}
type TopTracks struct {
Track []Track `json:"track"`
Attr Attr `json:"@attr"`
}
type Session struct {
Name string `json:"name"`
Key string `json:"key"`
Subscriber int `json:"subscriber"`
}
type NowPlaying struct {
Artist struct {
Corrected string `json:"corrected"`
Text string `json:"#text"`
} `json:"artist"`
IgnoredMessage struct {
Code string `json:"code"`
Text string `json:"#text"`
} `json:"ignoredMessage"`
Album struct {
Corrected string `json:"corrected"`
Text string `json:"#text"`
} `json:"album"`
AlbumArtist struct {
Corrected string `json:"corrected"`
Text string `json:"#text"`
} `json:"albumArtist"`
Track struct {
Corrected string `json:"corrected"`
Text string `json:"#text"`
} `json:"track"`
}
type Scrobbles struct {
Attr struct {
Accepted int `json:"accepted"`
Ignored int `json:"ignored"`
} `json:"@attr"`
Scrobble struct {
Artist struct {
Corrected string `json:"corrected"`
Text string `json:"#text"`
} `json:"artist"`
IgnoredMessage struct {
Code string `json:"code"`
Text string `json:"#text"`
} `json:"ignoredMessage"`
AlbumArtist struct {
Corrected string `json:"corrected"`
Text string `json:"#text"`
} `json:"albumArtist"`
Timestamp string `json:"timestamp"`
Album struct {
Corrected string `json:"corrected"`
Text string `json:"#text"`
} `json:"album"`
Track struct {
Corrected string `json:"corrected"`
Text string `json:"#text"`
} `json:"track"`
} `json:"scrobble"`
}

View File

@@ -0,0 +1,65 @@
package lastfm
import (
"encoding/json"
"os"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("LastFM responses", func() {
Describe("Artist", func() {
It("parses the response correctly", func() {
var resp Response
body, _ := os.ReadFile("tests/fixtures/lastfm.artist.getinfo.json")
err := json.Unmarshal(body, &resp)
Expect(err).To(BeNil())
Expect(resp.Artist.Name).To(Equal("U2"))
Expect(resp.Artist.MBID).To(Equal("a3cb23fc-acd3-4ce0-8f36-1e5aa6a18432"))
Expect(resp.Artist.URL).To(Equal("https://www.last.fm/music/U2"))
Expect(resp.Artist.Bio.Summary).To(ContainSubstring("U2 é uma das mais importantes bandas de rock de todos os tempos"))
})
})
Describe("SimilarArtists", func() {
It("parses the response correctly", func() {
var resp Response
body, _ := os.ReadFile("tests/fixtures/lastfm.artist.getsimilar.json")
err := json.Unmarshal(body, &resp)
Expect(err).To(BeNil())
Expect(resp.SimilarArtists.Artists).To(HaveLen(2))
Expect(resp.SimilarArtists.Artists[0].Name).To(Equal("Passengers"))
Expect(resp.SimilarArtists.Artists[1].Name).To(Equal("INXS"))
})
})
Describe("TopTracks", func() {
It("parses the response correctly", func() {
var resp Response
body, _ := os.ReadFile("tests/fixtures/lastfm.artist.gettoptracks.json")
err := json.Unmarshal(body, &resp)
Expect(err).To(BeNil())
Expect(resp.TopTracks.Track).To(HaveLen(2))
Expect(resp.TopTracks.Track[0].Name).To(Equal("Beautiful Day"))
Expect(resp.TopTracks.Track[0].MBID).To(Equal("f7f264d0-a89b-4682-9cd7-a4e7c37637af"))
Expect(resp.TopTracks.Track[1].Name).To(Equal("With or Without You"))
Expect(resp.TopTracks.Track[1].MBID).To(Equal("6b9a509f-6907-4a6e-9345-2f12da09ba4b"))
})
})
Describe("Error", func() {
It("parses the error response correctly", func() {
var error Response
body := []byte(`{"error":3,"message":"Invalid Method - No method with that name in this package"}`)
err := json.Unmarshal(body, &error)
Expect(err).To(BeNil())
Expect(error.Error).To(Equal(3))
Expect(error.Message).To(Equal("Invalid Method - No method with that name in this package"))
})
})
})

View File

@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Account Linking Success</title>
</head>
<body>
<h2 id="msg"></h2>
<script>
setTimeout("document.getElementById('msg').innerHTML = 'Success! Your account is linked to Last.fm. You can close this tab now.';",2000)
document.addEventListener("DOMContentLoaded", () => {
window.close();
});
</script>
</body>
</html>

View File

@@ -0,0 +1,119 @@
package listenbrainz
import (
"context"
"errors"
"net/http"
"github.com/navidrome/navidrome/conf"
"github.com/navidrome/navidrome/consts"
"github.com/navidrome/navidrome/core/agents"
"github.com/navidrome/navidrome/core/scrobbler"
"github.com/navidrome/navidrome/log"
"github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/utils"
)
const (
listenBrainzAgentName = "listenbrainz"
sessionKeyProperty = "ListenBrainzSessionKey"
)
type listenBrainzAgent struct {
ds model.DataStore
sessionKeys *agents.SessionKeys
baseURL string
client *client
}
func listenBrainzConstructor(ds model.DataStore) *listenBrainzAgent {
l := &listenBrainzAgent{
ds: ds,
sessionKeys: &agents.SessionKeys{DataStore: ds, KeyName: sessionKeyProperty},
baseURL: conf.Server.ListenBrainz.BaseURL,
}
hc := &http.Client{
Timeout: consts.DefaultHttpClientTimeOut,
}
chc := utils.NewCachedHTTPClient(hc, consts.DefaultHttpClientTimeOut)
l.client = newClient(l.baseURL, chc)
return l
}
func (l *listenBrainzAgent) AgentName() string {
return listenBrainzAgentName
}
func (l *listenBrainzAgent) formatListen(track *model.MediaFile) listenInfo {
li := listenInfo{
TrackMetadata: trackMetadata{
ArtistName: track.Artist,
TrackName: track.Title,
ReleaseName: track.Album,
AdditionalInfo: additionalInfo{
SubmissionClient: consts.AppName,
SubmissionClientVersion: consts.Version,
TrackNumber: track.TrackNumber,
ArtistMbzIDs: []string{track.MbzArtistID},
TrackMbzID: track.MbzTrackID,
ReleaseMbID: track.MbzAlbumID,
},
},
}
return li
}
func (l *listenBrainzAgent) NowPlaying(ctx context.Context, userId string, track *model.MediaFile) error {
sk, err := l.sessionKeys.Get(ctx, userId)
if err != nil || sk == "" {
return scrobbler.ErrNotAuthorized
}
li := l.formatListen(track)
err = l.client.updateNowPlaying(ctx, sk, li)
if err != nil {
log.Warn(ctx, "ListenBrainz updateNowPlaying returned error", "track", track.Title, err)
return scrobbler.ErrUnrecoverable
}
return nil
}
func (l *listenBrainzAgent) Scrobble(ctx context.Context, userId string, s scrobbler.Scrobble) error {
sk, err := l.sessionKeys.Get(ctx, userId)
if err != nil || sk == "" {
return scrobbler.ErrNotAuthorized
}
li := l.formatListen(&s.MediaFile)
li.ListenedAt = int(s.TimeStamp.Unix())
err = l.client.scrobble(ctx, sk, li)
if err == nil {
return nil
}
var lbErr *listenBrainzError
isListenBrainzError := errors.As(err, &lbErr)
if !isListenBrainzError {
log.Warn(ctx, "ListenBrainz Scrobble returned HTTP error", "track", s.Title, err)
return scrobbler.ErrRetryLater
}
if lbErr.Code == 500 || lbErr.Code == 503 {
return scrobbler.ErrRetryLater
}
return scrobbler.ErrUnrecoverable
}
func (l *listenBrainzAgent) IsAuthorized(ctx context.Context, userId string) bool {
sk, err := l.sessionKeys.Get(ctx, userId)
return err == nil && sk != ""
}
func init() {
conf.AddHook(func() {
if conf.Server.ListenBrainz.Enabled {
scrobbler.Register(listenBrainzAgentName, func(ds model.DataStore) scrobbler.Scrobbler {
return listenBrainzConstructor(ds)
})
}
})
}

View File

@@ -0,0 +1,161 @@
package listenbrainz
import (
"bytes"
"context"
"encoding/json"
"io"
"net/http"
"time"
"github.com/navidrome/navidrome/consts"
"github.com/navidrome/navidrome/core/scrobbler"
"github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/tests"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
. "github.com/onsi/gomega/gstruct"
)
var _ = Describe("listenBrainzAgent", func() {
var ds model.DataStore
var ctx context.Context
var agent *listenBrainzAgent
var httpClient *tests.FakeHttpClient
var track *model.MediaFile
BeforeEach(func() {
ds = &tests.MockDataStore{}
ctx = context.Background()
_ = ds.UserProps(ctx).Put("user-1", sessionKeyProperty, "SK-1")
httpClient = &tests.FakeHttpClient{}
agent = listenBrainzConstructor(ds)
agent.client = newClient("http://localhost:8080", httpClient)
track = &model.MediaFile{
ID: "123",
Title: "Track Title",
Album: "Track Album",
Artist: "Track Artist",
TrackNumber: 1,
MbzTrackID: "mbz-123",
MbzAlbumID: "mbz-456",
MbzArtistID: "mbz-789",
}
})
Describe("formatListen", func() {
It("constructs the listenInfo properly", func() {
var idArtistId = func(element interface{}) string {
return element.(string)
}
lr := agent.formatListen(track)
Expect(lr).To(MatchAllFields(Fields{
"ListenedAt": Equal(0),
"TrackMetadata": MatchAllFields(Fields{
"ArtistName": Equal(track.Artist),
"TrackName": Equal(track.Title),
"ReleaseName": Equal(track.Album),
"AdditionalInfo": MatchAllFields(Fields{
"SubmissionClient": Equal(consts.AppName),
"SubmissionClientVersion": Equal(consts.Version),
"TrackNumber": Equal(track.TrackNumber),
"TrackMbzID": Equal(track.MbzTrackID),
"ReleaseMbID": Equal(track.MbzAlbumID),
"ArtistMbzIDs": MatchAllElements(idArtistId, Elements{
"mbz-789": Equal(track.MbzArtistID),
}),
}),
}),
}))
})
})
Describe("NowPlaying", func() {
It("updates NowPlaying successfully", func() {
httpClient.Res = http.Response{Body: io.NopCloser(bytes.NewBufferString(`{"status": "ok"}`)), StatusCode: 200}
err := agent.NowPlaying(ctx, "user-1", track)
Expect(err).ToNot(HaveOccurred())
})
It("returns ErrNotAuthorized if user is not linked", func() {
err := agent.NowPlaying(ctx, "user-2", track)
Expect(err).To(MatchError(scrobbler.ErrNotAuthorized))
})
})
Describe("Scrobble", func() {
var sc scrobbler.Scrobble
BeforeEach(func() {
sc = scrobbler.Scrobble{MediaFile: *track, TimeStamp: time.Now()}
})
It("sends a Scrobble successfully", func() {
httpClient.Res = http.Response{Body: io.NopCloser(bytes.NewBufferString(`{"status": "ok"}`)), StatusCode: 200}
err := agent.Scrobble(ctx, "user-1", sc)
Expect(err).ToNot(HaveOccurred())
})
It("sets the Timestamp properly", func() {
httpClient.Res = http.Response{Body: io.NopCloser(bytes.NewBufferString(`{"status": "ok"}`)), StatusCode: 200}
err := agent.Scrobble(ctx, "user-1", sc)
Expect(err).ToNot(HaveOccurred())
decoder := json.NewDecoder(httpClient.SavedRequest.Body)
var lr listenBrainzRequestBody
err = decoder.Decode(&lr)
Expect(err).ToNot(HaveOccurred())
Expect(lr.Payload[0].ListenedAt).To(Equal(int(sc.TimeStamp.Unix())))
})
It("returns ErrNotAuthorized if user is not linked", func() {
err := agent.Scrobble(ctx, "user-2", sc)
Expect(err).To(MatchError(scrobbler.ErrNotAuthorized))
})
It("returns ErrRetryLater on error 503", func() {
httpClient.Res = http.Response{
Body: io.NopCloser(bytes.NewBufferString(`{"code": 503, "error": "Cannot submit listens to queue, please try again later."}`)),
StatusCode: 503,
}
err := agent.Scrobble(ctx, "user-1", sc)
Expect(err).To(MatchError(scrobbler.ErrRetryLater))
})
It("returns ErrRetryLater on error 500", func() {
httpClient.Res = http.Response{
Body: io.NopCloser(bytes.NewBufferString(`{"code": 500, "error": "Something went wrong. Please try again."}`)),
StatusCode: 500,
}
err := agent.Scrobble(ctx, "user-1", sc)
Expect(err).To(MatchError(scrobbler.ErrRetryLater))
})
It("returns ErrRetryLater on http errors", func() {
httpClient.Res = http.Response{
Body: io.NopCloser(bytes.NewBufferString(`Bad Gateway`)),
StatusCode: 500,
}
err := agent.Scrobble(ctx, "user-1", sc)
Expect(err).To(MatchError(scrobbler.ErrRetryLater))
})
It("returns ErrUnrecoverable on other errors", func() {
httpClient.Res = http.Response{
Body: io.NopCloser(bytes.NewBufferString(`{"code": 400, "error": "BadRequest: Invalid JSON document submitted."}`)),
StatusCode: 400,
}
err := agent.Scrobble(ctx, "user-1", sc)
Expect(err).To(MatchError(scrobbler.ErrUnrecoverable))
})
})
})

View File

@@ -0,0 +1,121 @@
package listenbrainz
import (
"context"
"encoding/json"
"errors"
"net/http"
"github.com/deluan/rest"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/navidrome/navidrome/conf"
"github.com/navidrome/navidrome/consts"
"github.com/navidrome/navidrome/core/agents"
"github.com/navidrome/navidrome/log"
"github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/model/request"
"github.com/navidrome/navidrome/server"
)
type sessionKeysRepo interface {
Put(ctx context.Context, userId, sessionKey string) error
Get(ctx context.Context, userId string) (string, error)
Delete(ctx context.Context, userId string) error
}
type Router struct {
http.Handler
ds model.DataStore
sessionKeys sessionKeysRepo
client *client
}
func NewRouter(ds model.DataStore) *Router {
r := &Router{
ds: ds,
sessionKeys: &agents.SessionKeys{DataStore: ds, KeyName: sessionKeyProperty},
}
r.Handler = r.routes()
hc := &http.Client{
Timeout: consts.DefaultHttpClientTimeOut,
}
r.client = newClient(conf.Server.ListenBrainz.BaseURL, hc)
return r
}
func (s *Router) routes() http.Handler {
r := chi.NewRouter()
r.Group(func(r chi.Router) {
r.Use(server.Authenticator(s.ds))
r.Use(server.JWTRefresher)
r.Get("/link", s.getLinkStatus)
r.Put("/link", s.link)
r.Delete("/link", s.unlink)
})
return r
}
func (s *Router) getLinkStatus(w http.ResponseWriter, r *http.Request) {
resp := map[string]interface{}{}
u, _ := request.UserFrom(r.Context())
key, err := s.sessionKeys.Get(r.Context(), u.ID)
if err != nil && !errors.Is(err, model.ErrNotFound) {
resp["error"] = err
resp["status"] = false
_ = rest.RespondWithJSON(w, http.StatusInternalServerError, resp)
return
}
resp["status"] = key != ""
_ = rest.RespondWithJSON(w, http.StatusOK, resp)
}
func (s *Router) link(w http.ResponseWriter, r *http.Request) {
type tokenPayload struct {
Token string `json:"token"`
}
var payload tokenPayload
err := json.NewDecoder(r.Body).Decode(&payload)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
if payload.Token == "" {
_ = rest.RespondWithError(w, http.StatusBadRequest, "Token is required")
return
}
u, _ := request.UserFrom(r.Context())
resp, err := s.client.validateToken(r.Context(), payload.Token)
if err != nil {
log.Error(r.Context(), "Could not validate ListenBrainz token", "userId", u.ID, "requestId", middleware.GetReqID(r.Context()), err)
_ = rest.RespondWithError(w, http.StatusInternalServerError, err.Error())
return
}
if !resp.Valid {
_ = rest.RespondWithError(w, http.StatusBadRequest, "Invalid token")
return
}
err = s.sessionKeys.Put(r.Context(), u.ID, payload.Token)
if err != nil {
log.Error("Could not save ListenBrainz token", "userId", u.ID, "requestId", middleware.GetReqID(r.Context()), err)
_ = rest.RespondWithError(w, http.StatusInternalServerError, err.Error())
return
}
_ = rest.RespondWithJSON(w, http.StatusOK, map[string]interface{}{"status": resp.Valid, "user": resp.UserName})
}
func (s *Router) unlink(w http.ResponseWriter, r *http.Request) {
u, _ := request.UserFrom(r.Context())
err := s.sessionKeys.Delete(r.Context(), u.ID)
if err != nil {
_ = rest.RespondWithError(w, http.StatusInternalServerError, err.Error())
} else {
_ = rest.RespondWithJSON(w, http.StatusOK, map[string]string{})
}
}

View File

@@ -0,0 +1,130 @@
package listenbrainz
import (
"bytes"
"context"
"encoding/json"
"io"
"net/http"
"net/http/httptest"
"strings"
"github.com/navidrome/navidrome/tests"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("ListenBrainz Auth Router", func() {
var sk *fakeSessionKeys
var httpClient *tests.FakeHttpClient
var r Router
var req *http.Request
var resp *httptest.ResponseRecorder
BeforeEach(func() {
sk = &fakeSessionKeys{KeyName: sessionKeyProperty}
httpClient = &tests.FakeHttpClient{}
cl := newClient("http://localhost/", httpClient)
r = Router{
sessionKeys: sk,
client: cl,
}
resp = httptest.NewRecorder()
})
Describe("getLinkStatus", func() {
It("returns false when there is no stored session key", func() {
req = httptest.NewRequest("GET", "/listenbrainz/link", nil)
r.getLinkStatus(resp, req)
Expect(resp.Code).To(Equal(http.StatusOK))
var parsed map[string]interface{}
Expect(json.Unmarshal(resp.Body.Bytes(), &parsed)).To(BeNil())
Expect(parsed["status"]).To(Equal(false))
})
It("returns true when there is a stored session key", func() {
sk.KeyValue = "sk-1"
req = httptest.NewRequest("GET", "/listenbrainz/link", nil)
r.getLinkStatus(resp, req)
Expect(resp.Code).To(Equal(http.StatusOK))
var parsed map[string]interface{}
Expect(json.Unmarshal(resp.Body.Bytes(), &parsed)).To(BeNil())
Expect(parsed["status"]).To(Equal(true))
})
})
Describe("link", func() {
It("returns bad request when no token is sent", func() {
req = httptest.NewRequest("PUT", "/listenbrainz/link", strings.NewReader(`{}`))
r.link(resp, req)
Expect(resp.Code).To(Equal(http.StatusBadRequest))
})
It("returns bad request when the token is invalid", func() {
httpClient.Res = http.Response{
Body: io.NopCloser(bytes.NewBufferString(`{"code": 200, "message": "Token invalid.", "valid": false}`)),
StatusCode: 200,
}
req = httptest.NewRequest("PUT", "/listenbrainz/link", strings.NewReader(`{"token": "invalid-tok-1"}`))
r.link(resp, req)
Expect(resp.Code).To(Equal(http.StatusBadRequest))
})
It("returns true and the username when the token is valid", func() {
httpClient.Res = http.Response{
Body: io.NopCloser(bytes.NewBufferString(`{"code": 200, "message": "Token valid.", "user_name": "ListenBrainzUser", "valid": true}`)),
StatusCode: 200,
}
req = httptest.NewRequest("PUT", "/listenbrainz/link", strings.NewReader(`{"token": "tok-1"}`))
r.link(resp, req)
Expect(resp.Code).To(Equal(http.StatusOK))
var parsed map[string]interface{}
Expect(json.Unmarshal(resp.Body.Bytes(), &parsed)).To(BeNil())
Expect(parsed["status"]).To(Equal(true))
Expect(parsed["user"]).To(Equal("ListenBrainzUser"))
})
It("saves the session key when the token is valid", func() {
httpClient.Res = http.Response{
Body: io.NopCloser(bytes.NewBufferString(`{"code": 200, "message": "Token valid.", "user_name": "ListenBrainzUser", "valid": true}`)),
StatusCode: 200,
}
req = httptest.NewRequest("PUT", "/listenbrainz/link", strings.NewReader(`{"token": "tok-1"}`))
r.link(resp, req)
Expect(resp.Code).To(Equal(http.StatusOK))
Expect(sk.KeyValue).To(Equal("tok-1"))
})
})
Describe("unlink", func() {
It("removes the session key when unlinking", func() {
sk.KeyValue = "tok-1"
req = httptest.NewRequest("DELETE", "/listenbrainz/link", nil)
r.unlink(resp, req)
Expect(resp.Code).To(Equal(http.StatusOK))
Expect(sk.KeyValue).To(Equal(""))
})
})
})
type fakeSessionKeys struct {
KeyName string
KeyValue string
}
func (sk *fakeSessionKeys) Put(ctx context.Context, userId, sessionKey string) error {
sk.KeyValue = sessionKey
return nil
}
func (sk *fakeSessionKeys) Get(ctx context.Context, userId string) (string, error) {
return sk.KeyValue, nil
}
func (sk *fakeSessionKeys) Delete(ctx context.Context, userId string) error {
sk.KeyValue = ""
return nil
}

View File

@@ -0,0 +1,176 @@
package listenbrainz
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"net/url"
"path"
"github.com/navidrome/navidrome/log"
)
type listenBrainzError struct {
Code int
Message string
}
func (e *listenBrainzError) Error() string {
return fmt.Sprintf("ListenBrainz error(%d): %s", e.Code, e.Message)
}
type httpDoer interface {
Do(req *http.Request) (*http.Response, error)
}
func newClient(baseURL string, hc httpDoer) *client {
return &client{baseURL, hc}
}
type client struct {
baseURL string
hc httpDoer
}
type listenBrainzResponse struct {
Code int `json:"code"`
Message string `json:"message"`
Error string `json:"error"`
Status string `json:"status"`
Valid bool `json:"valid"`
UserName string `json:"user_name"`
}
type listenBrainzRequest struct {
ApiKey string
Body listenBrainzRequestBody
}
type listenBrainzRequestBody struct {
ListenType listenType `json:"listen_type,omitempty"`
Payload []listenInfo `json:"payload,omitempty"`
}
type listenType string
const (
Single listenType = "single"
PlayingNow listenType = "playing_now"
)
type listenInfo struct {
ListenedAt int `json:"listened_at,omitempty"`
TrackMetadata trackMetadata `json:"track_metadata,omitempty"`
}
type trackMetadata struct {
ArtistName string `json:"artist_name,omitempty"`
TrackName string `json:"track_name,omitempty"`
ReleaseName string `json:"release_name,omitempty"`
AdditionalInfo additionalInfo `json:"additional_info,omitempty"`
}
type additionalInfo struct {
SubmissionClient string `json:"submission_client,omitempty"`
SubmissionClientVersion string `json:"submission_client_version,omitempty"`
TrackNumber int `json:"tracknumber,omitempty"`
TrackMbzID string `json:"track_mbid,omitempty"`
ArtistMbzIDs []string `json:"artist_mbids,omitempty"`
ReleaseMbID string `json:"release_mbid,omitempty"`
}
func (c *client) validateToken(ctx context.Context, apiKey string) (*listenBrainzResponse, error) {
r := &listenBrainzRequest{
ApiKey: apiKey,
}
response, err := c.makeRequest(ctx, http.MethodGet, "validate-token", r)
if err != nil {
return nil, err
}
return response, nil
}
func (c *client) updateNowPlaying(ctx context.Context, apiKey string, li listenInfo) error {
r := &listenBrainzRequest{
ApiKey: apiKey,
Body: listenBrainzRequestBody{
ListenType: PlayingNow,
Payload: []listenInfo{li},
},
}
resp, err := c.makeRequest(ctx, http.MethodPost, "submit-listens", r)
if err != nil {
return err
}
if resp.Status != "ok" {
log.Warn(ctx, "ListenBrainz: NowPlaying was not accepted", "status", resp.Status)
}
return nil
}
func (c *client) scrobble(ctx context.Context, apiKey string, li listenInfo) error {
r := &listenBrainzRequest{
ApiKey: apiKey,
Body: listenBrainzRequestBody{
ListenType: Single,
Payload: []listenInfo{li},
},
}
resp, err := c.makeRequest(ctx, http.MethodPost, "submit-listens", r)
if err != nil {
return err
}
if resp.Status != "ok" {
log.Warn(ctx, "ListenBrainz: Scrobble was not accepted", "status", resp.Status)
}
return nil
}
func (c *client) path(endpoint string) (string, error) {
u, err := url.Parse(c.baseURL)
if err != nil {
return "", err
}
u.Path = path.Join(u.Path, endpoint)
return u.String(), nil
}
func (c *client) makeRequest(ctx context.Context, method string, endpoint string, r *listenBrainzRequest) (*listenBrainzResponse, error) {
b, _ := json.Marshal(r.Body)
uri, err := c.path(endpoint)
if err != nil {
return nil, err
}
req, _ := http.NewRequestWithContext(ctx, method, uri, bytes.NewBuffer(b))
req.Header.Add("Content-Type", "application/json; charset=UTF-8")
if r.ApiKey != "" {
req.Header.Add("Authorization", fmt.Sprintf("Token %s", r.ApiKey))
}
log.Trace(ctx, fmt.Sprintf("Sending ListenBrainz %s request", req.Method), "url", req.URL)
resp, err := c.hc.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
decoder := json.NewDecoder(resp.Body)
var response listenBrainzResponse
jsonErr := decoder.Decode(&response)
if resp.StatusCode != 200 && jsonErr != nil {
return nil, fmt.Errorf("ListenBrainz: HTTP Error, Status: (%d)", resp.StatusCode)
}
if jsonErr != nil {
return nil, jsonErr
}
if response.Code != 0 && response.Code != 200 {
return &response, &listenBrainzError{Code: response.Code, Message: response.Error}
}
return &response, nil
}

View File

@@ -0,0 +1,118 @@
package listenbrainz
import (
"bytes"
"context"
"encoding/json"
"io"
"net/http"
"os"
"github.com/navidrome/navidrome/tests"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("client", func() {
var httpClient *tests.FakeHttpClient
var client *client
BeforeEach(func() {
httpClient = &tests.FakeHttpClient{}
client = newClient("BASE_URL/", httpClient)
})
Describe("listenBrainzResponse", func() {
It("parses a response properly", func() {
var response listenBrainzResponse
err := json.Unmarshal([]byte(`{"code": 200, "message": "Message", "user_name": "UserName", "valid": true, "status": "ok", "error": "Error"}`), &response)
Expect(err).ToNot(HaveOccurred())
Expect(response.Code).To(Equal(200))
Expect(response.Message).To(Equal("Message"))
Expect(response.UserName).To(Equal("UserName"))
Expect(response.Valid).To(BeTrue())
Expect(response.Status).To(Equal("ok"))
Expect(response.Error).To(Equal("Error"))
})
})
Describe("validateToken", func() {
BeforeEach(func() {
httpClient.Res = http.Response{
Body: io.NopCloser(bytes.NewBufferString(`{"code": 200, "message": "Token valid.", "user_name": "ListenBrainzUser", "valid": true}`)),
StatusCode: 200,
}
})
It("formats the request properly", func() {
_, err := client.validateToken(context.Background(), "LB-TOKEN")
Expect(err).ToNot(HaveOccurred())
Expect(httpClient.SavedRequest.Method).To(Equal(http.MethodGet))
Expect(httpClient.SavedRequest.URL.String()).To(Equal("BASE_URL/validate-token"))
Expect(httpClient.SavedRequest.Header.Get("Authorization")).To(Equal("Token LB-TOKEN"))
Expect(httpClient.SavedRequest.Header.Get("Content-Type")).To(Equal("application/json; charset=UTF-8"))
})
It("parses and returns the response", func() {
res, err := client.validateToken(context.Background(), "LB-TOKEN")
Expect(err).ToNot(HaveOccurred())
Expect(res.Valid).To(Equal(true))
Expect(res.UserName).To(Equal("ListenBrainzUser"))
})
})
Context("with listenInfo", func() {
var li listenInfo
BeforeEach(func() {
httpClient.Res = http.Response{
Body: io.NopCloser(bytes.NewBufferString(`{"status": "ok"}`)),
StatusCode: 200,
}
li = listenInfo{
TrackMetadata: trackMetadata{
ArtistName: "Track Artist",
TrackName: "Track Title",
ReleaseName: "Track Album",
AdditionalInfo: additionalInfo{
TrackNumber: 1,
TrackMbzID: "mbz-123",
ArtistMbzIDs: []string{"mbz-789"},
ReleaseMbID: "mbz-456",
},
},
}
})
Describe("updateNowPlaying", func() {
It("formats the request properly", func() {
Expect(client.updateNowPlaying(context.Background(), "LB-TOKEN", li)).To(Succeed())
Expect(httpClient.SavedRequest.Method).To(Equal(http.MethodPost))
Expect(httpClient.SavedRequest.URL.String()).To(Equal("BASE_URL/submit-listens"))
Expect(httpClient.SavedRequest.Header.Get("Authorization")).To(Equal("Token LB-TOKEN"))
Expect(httpClient.SavedRequest.Header.Get("Content-Type")).To(Equal("application/json; charset=UTF-8"))
body, _ := io.ReadAll(httpClient.SavedRequest.Body)
f, _ := os.ReadFile("tests/fixtures/listenbrainz.nowplaying.request.json")
Expect(body).To(MatchJSON(f))
})
})
Describe("scrobble", func() {
BeforeEach(func() {
li.ListenedAt = 1635000000
})
It("formats the request properly", func() {
Expect(client.scrobble(context.Background(), "LB-TOKEN", li)).To(Succeed())
Expect(httpClient.SavedRequest.Method).To(Equal(http.MethodPost))
Expect(httpClient.SavedRequest.URL.String()).To(Equal("BASE_URL/submit-listens"))
Expect(httpClient.SavedRequest.Header.Get("Authorization")).To(Equal("Token LB-TOKEN"))
Expect(httpClient.SavedRequest.Header.Get("Content-Type")).To(Equal("application/json; charset=UTF-8"))
body, _ := io.ReadAll(httpClient.SavedRequest.Body)
f, _ := os.ReadFile("tests/fixtures/listenbrainz.scrobble.request.json")
Expect(body).To(MatchJSON(f))
})
})
})
})

View File

@@ -0,0 +1,17 @@
package listenbrainz
import (
"testing"
"github.com/navidrome/navidrome/log"
"github.com/navidrome/navidrome/tests"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
func TestListenBrainz(t *testing.T) {
tests.Init(t, false)
log.SetLevel(log.LevelFatal)
RegisterFailHandler(Fail)
RunSpecs(t, "ListenBrainz Test Suite")
}

View File

@@ -0,0 +1,52 @@
package agents
import (
"context"
"github.com/Masterminds/squirrel"
"github.com/navidrome/navidrome/model"
)
const LocalAgentName = "local"
type localAgent struct {
ds model.DataStore
}
func localsConstructor(ds model.DataStore) Interface {
return &localAgent{ds}
}
func (p *localAgent) AgentName() string {
return LocalAgentName
}
func (p *localAgent) GetArtistTopSongs(ctx context.Context, id, artistName, mbid string, count int) ([]Song, error) {
top, err := p.ds.MediaFile(ctx).GetAll(model.QueryOptions{
Sort: "playCount",
Order: "desc",
Max: count,
Filters: squirrel.And{
squirrel.Eq{"artist_id": id},
squirrel.Or{
squirrel.Eq{"starred": true},
squirrel.Eq{"rating": 5},
},
},
})
if err != nil {
return nil, err
}
var result []Song
for _, s := range top {
result = append(result, Song{
Name: s.Title,
MBID: s.MbzReleaseTrackID,
})
}
return result, nil
}
func init() {
Register(LocalAgentName, localsConstructor)
}

View File

@@ -0,0 +1,25 @@
package agents
import (
"context"
"github.com/navidrome/navidrome/model"
)
// SessionKeys is a simple wrapper around the UserPropsRepository
type SessionKeys struct {
model.DataStore
KeyName string
}
func (sk *SessionKeys) Put(ctx context.Context, userId, sessionKey string) error {
return sk.DataStore.UserProps(ctx).Put(userId, sk.KeyName, sessionKey)
}
func (sk *SessionKeys) Get(ctx context.Context, userId string) (string, error) {
return sk.DataStore.UserProps(ctx).Get(userId, sk.KeyName)
}
func (sk *SessionKeys) Delete(ctx context.Context, userId string) error {
return sk.DataStore.UserProps(ctx).Delete(userId, sk.KeyName)
}

View File

@@ -0,0 +1,37 @@
package agents
import (
"context"
"github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/tests"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("SessionKeys", func() {
ctx := context.Background()
user := model.User{ID: "u-1"}
ds := &tests.MockDataStore{MockedUserProps: &tests.MockedUserPropsRepo{}}
sk := SessionKeys{DataStore: ds, KeyName: "fakeSessionKey"}
It("uses the assigned key name", func() {
Expect(sk.KeyName).To(Equal("fakeSessionKey"))
})
It("stores a value in the DB", func() {
Expect(sk.Put(ctx, user.ID, "test-stored-value")).To(BeNil())
})
It("fetches the stored value", func() {
value, err := sk.Get(ctx, user.ID)
Expect(err).ToNot(HaveOccurred())
Expect(value).To(Equal("test-stored-value"))
})
It("deletes the stored value", func() {
Expect(sk.Delete(ctx, user.ID)).To(BeNil())
})
It("handles a not found value", func() {
_, err := sk.Get(ctx, "u-2")
Expect(err).To(MatchError(model.ErrNotFound))
})
})

View File

@@ -0,0 +1,116 @@
package spotify
import (
"context"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"strconv"
"strings"
"github.com/navidrome/navidrome/log"
)
const apiBaseUrl = "https://api.spotify.com/v1/"
var (
ErrNotFound = errors.New("spotify: not found")
)
type httpDoer interface {
Do(req *http.Request) (*http.Response, error)
}
func newClient(id, secret string, hc httpDoer) *client {
return &client{id, secret, hc}
}
type client struct {
id string
secret string
hc httpDoer
}
func (c *client) searchArtists(ctx context.Context, name string, limit int) ([]Artist, error) {
token, err := c.authorize(ctx)
if err != nil {
return nil, err
}
params := url.Values{}
params.Add("type", "artist")
params.Add("q", name)
params.Add("offset", "0")
params.Add("limit", strconv.Itoa(limit))
req, _ := http.NewRequestWithContext(ctx, "GET", apiBaseUrl+"search", nil)
req.URL.RawQuery = params.Encode()
req.Header.Add("Authorization", "Bearer "+token)
var results SearchResults
err = c.makeRequest(req, &results)
if err != nil {
return nil, err
}
if len(results.Artists.Items) == 0 {
return nil, ErrNotFound
}
return results.Artists.Items, err
}
func (c *client) authorize(ctx context.Context) (string, error) {
payload := url.Values{}
payload.Add("grant_type", "client_credentials")
encodePayload := payload.Encode()
req, _ := http.NewRequestWithContext(ctx, "POST", "https://accounts.spotify.com/api/token", strings.NewReader(encodePayload))
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
req.Header.Add("Content-Length", strconv.Itoa(len(encodePayload)))
auth := c.id + ":" + c.secret
req.Header.Add("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(auth)))
response := map[string]interface{}{}
err := c.makeRequest(req, &response)
if err != nil {
return "", err
}
if v, ok := response["access_token"]; ok {
return v.(string), nil
}
log.Error(ctx, "Invalid spotify response", "resp", response)
return "", errors.New("invalid response")
}
func (c *client) makeRequest(req *http.Request, response interface{}) error {
log.Trace(req.Context(), fmt.Sprintf("Sending Spotify %s request", req.Method), "url", req.URL)
resp, err := c.hc.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
data, err := io.ReadAll(resp.Body)
if err != nil {
return err
}
if resp.StatusCode != 200 {
return c.parseError(data)
}
return json.Unmarshal(data, response)
}
func (c *client) parseError(data []byte) error {
var e Error
err := json.Unmarshal(data, &e)
if err != nil {
return err
}
return fmt.Errorf("spotify error(%s): %s", e.Code, e.Message)
}

View File

@@ -0,0 +1,131 @@
package spotify
import (
"bytes"
"context"
"io"
"net/http"
"os"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("client", func() {
var httpClient *fakeHttpClient
var client *client
BeforeEach(func() {
httpClient = &fakeHttpClient{}
client = newClient("SPOTIFY_ID", "SPOTIFY_SECRET", httpClient)
})
Describe("ArtistImages", func() {
It("returns artist images from a successful request", func() {
f, _ := os.Open("tests/fixtures/spotify.search.artist.json")
httpClient.mock("https://api.spotify.com/v1/search", http.Response{Body: f, StatusCode: 200})
httpClient.mock("https://accounts.spotify.com/api/token", http.Response{
StatusCode: 200,
Body: io.NopCloser(bytes.NewBufferString(`{"access_token": "NEW_ACCESS_TOKEN","token_type": "Bearer","expires_in": 3600}`)),
})
artists, err := client.searchArtists(context.TODO(), "U2", 10)
Expect(err).To(BeNil())
Expect(artists).To(HaveLen(20))
Expect(artists[0].Popularity).To(Equal(82))
images := artists[0].Images
Expect(images).To(HaveLen(3))
Expect(images[0].Width).To(Equal(640))
Expect(images[1].Width).To(Equal(320))
Expect(images[2].Width).To(Equal(160))
})
It("fails if artist was not found", func() {
httpClient.mock("https://api.spotify.com/v1/search", http.Response{
StatusCode: 200,
Body: io.NopCloser(bytes.NewBufferString(`{
"artists" : {
"href" : "https://api.spotify.com/v1/search?query=dasdasdas%2Cdna&type=artist&offset=0&limit=20",
"items" : [ ], "limit" : 20, "next" : null, "offset" : 0, "previous" : null, "total" : 0
}}`)),
})
httpClient.mock("https://accounts.spotify.com/api/token", http.Response{
StatusCode: 200,
Body: io.NopCloser(bytes.NewBufferString(`{"access_token": "NEW_ACCESS_TOKEN","token_type": "Bearer","expires_in": 3600}`)),
})
_, err := client.searchArtists(context.TODO(), "U2", 10)
Expect(err).To(MatchError(ErrNotFound))
})
It("fails if not able to authorize", func() {
f, _ := os.Open("tests/fixtures/spotify.search.artist.json")
httpClient.mock("https://api.spotify.com/v1/search", http.Response{Body: f, StatusCode: 200})
httpClient.mock("https://accounts.spotify.com/api/token", http.Response{
StatusCode: 400,
Body: io.NopCloser(bytes.NewBufferString(`{"error":"invalid_client","error_description":"Invalid client"}`)),
})
_, err := client.searchArtists(context.TODO(), "U2", 10)
Expect(err).To(MatchError("spotify error(invalid_client): Invalid client"))
})
})
Describe("authorize", func() {
It("returns an access_token on successful authorization", func() {
httpClient.mock("https://accounts.spotify.com/api/token", http.Response{
StatusCode: 200,
Body: io.NopCloser(bytes.NewBufferString(`{"access_token": "NEW_ACCESS_TOKEN","token_type": "Bearer","expires_in": 3600}`)),
})
token, err := client.authorize(context.TODO())
Expect(err).To(BeNil())
Expect(token).To(Equal("NEW_ACCESS_TOKEN"))
auth := httpClient.lastRequest.Header.Get("Authorization")
Expect(auth).To(Equal("Basic U1BPVElGWV9JRDpTUE9USUZZX1NFQ1JFVA=="))
})
It("fails on unsuccessful authorization", func() {
httpClient.mock("https://accounts.spotify.com/api/token", http.Response{
StatusCode: 400,
Body: io.NopCloser(bytes.NewBufferString(`{"error":"invalid_client","error_description":"Invalid client"}`)),
})
_, err := client.authorize(context.TODO())
Expect(err).To(MatchError("spotify error(invalid_client): Invalid client"))
})
It("fails on invalid JSON response", func() {
httpClient.mock("https://accounts.spotify.com/api/token", http.Response{
StatusCode: 200,
Body: io.NopCloser(bytes.NewBufferString(`{NOT_VALID}`)),
})
_, err := client.authorize(context.TODO())
Expect(err).To(MatchError("invalid character 'N' looking for beginning of object key string"))
})
})
})
type fakeHttpClient struct {
responses map[string]*http.Response
lastRequest *http.Request
}
func (c *fakeHttpClient) mock(url string, response http.Response) {
if c.responses == nil {
c.responses = make(map[string]*http.Response)
}
c.responses[url] = &response
}
func (c *fakeHttpClient) Do(req *http.Request) (*http.Response, error) {
c.lastRequest = req
u := req.URL
u.RawQuery = ""
if resp, ok := c.responses[u.String()]; ok {
return resp, nil
}
panic("URL not mocked: " + u.String())
}

View File

@@ -0,0 +1,30 @@
package spotify
type SearchResults struct {
Artists ArtistsResult `json:"artists"`
}
type ArtistsResult struct {
HRef string `json:"href"`
Items []Artist `json:"items"`
}
type Artist struct {
Genres []string `json:"genres"`
HRef string `json:"href"`
ID string `json:"id"`
Popularity int `json:"popularity"`
Images []Image `json:"images"`
Name string `json:"name"`
}
type Image struct {
URL string `json:"url"`
Width int `json:"width"`
Height int `json:"height"`
}
type Error struct {
Code string `json:"error"`
Message string `json:"error_description"`
}

View File

@@ -0,0 +1,48 @@
package spotify
import (
"encoding/json"
"os"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("Responses", func() {
Describe("Search type=artist", func() {
It("parses the artist search result correctly ", func() {
var resp SearchResults
body, _ := os.ReadFile("tests/fixtures/spotify.search.artist.json")
err := json.Unmarshal(body, &resp)
Expect(err).To(BeNil())
Expect(resp.Artists.Items).To(HaveLen(20))
u2 := resp.Artists.Items[0]
Expect(u2.Name).To(Equal("U2"))
Expect(u2.Genres).To(ContainElements("irish rock", "permanent wave", "rock"))
Expect(u2.ID).To(Equal("51Blml2LZPmy7TTiAg47vQ"))
Expect(u2.HRef).To(Equal("https://api.spotify.com/v1/artists/51Blml2LZPmy7TTiAg47vQ"))
Expect(u2.Images[0].URL).To(Equal("https://i.scdn.co/image/e22d5c0c8139b8439440a69854ed66efae91112d"))
Expect(u2.Images[0].Width).To(Equal(640))
Expect(u2.Images[0].Height).To(Equal(640))
Expect(u2.Images[1].URL).To(Equal("https://i.scdn.co/image/40d6c5c14355cfc127b70da221233315497ec91d"))
Expect(u2.Images[1].Width).To(Equal(320))
Expect(u2.Images[1].Height).To(Equal(320))
Expect(u2.Images[2].URL).To(Equal("https://i.scdn.co/image/7293d6752ae8a64e34adee5086858e408185b534"))
Expect(u2.Images[2].Width).To(Equal(160))
Expect(u2.Images[2].Height).To(Equal(160))
})
})
Describe("Error", func() {
It("parses the error response correctly", func() {
var errorResp Error
body := []byte(`{"error":"invalid_client","error_description":"Invalid client"}`)
err := json.Unmarshal(body, &errorResp)
Expect(err).To(BeNil())
Expect(errorResp.Code).To(Equal("invalid_client"))
Expect(errorResp.Message).To(Equal("Invalid client"))
})
})
})

View File

@@ -0,0 +1,95 @@
package spotify
import (
"context"
"errors"
"fmt"
"net/http"
"sort"
"strings"
"github.com/navidrome/navidrome/conf"
"github.com/navidrome/navidrome/consts"
"github.com/navidrome/navidrome/core/agents"
"github.com/navidrome/navidrome/log"
"github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/utils"
"github.com/xrash/smetrics"
)
const spotifyAgentName = "spotify"
type spotifyAgent struct {
ds model.DataStore
id string
secret string
client *client
}
func spotifyConstructor(ds model.DataStore) agents.Interface {
l := &spotifyAgent{
ds: ds,
id: conf.Server.Spotify.ID,
secret: conf.Server.Spotify.Secret,
}
hc := &http.Client{
Timeout: consts.DefaultHttpClientTimeOut,
}
chc := utils.NewCachedHTTPClient(hc, consts.DefaultHttpClientTimeOut)
l.client = newClient(l.id, l.secret, chc)
return l
}
func (s *spotifyAgent) AgentName() string {
return spotifyAgentName
}
func (s *spotifyAgent) GetArtistImages(ctx context.Context, id, name, mbid string) ([]agents.ExternalImage, error) {
a, err := s.searchArtist(ctx, name)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
log.Warn(ctx, "Artist not found in Spotify", "artist", name)
} else {
log.Error(ctx, "Error calling Spotify", "artist", name, err)
}
return nil, err
}
var res []agents.ExternalImage
for _, img := range a.Images {
res = append(res, agents.ExternalImage{
URL: img.URL,
Size: img.Width,
})
}
return res, nil
}
func (s *spotifyAgent) searchArtist(ctx context.Context, name string) (*Artist, error) {
artists, err := s.client.searchArtists(ctx, name, 40)
if err != nil || len(artists) == 0 {
return nil, model.ErrNotFound
}
name = strings.ToLower(name)
// Sort results, prioritizing artists with images, with similar names and with high popularity, in this order
sort.Slice(artists, func(i, j int) bool {
ai := fmt.Sprintf("%-5t-%03d-%04d", len(artists[i].Images) == 0, smetrics.WagnerFischer(name, strings.ToLower(artists[i].Name), 1, 1, 2), 1000-artists[i].Popularity)
aj := fmt.Sprintf("%-5t-%03d-%04d", len(artists[j].Images) == 0, smetrics.WagnerFischer(name, strings.ToLower(artists[j].Name), 1, 1, 2), 1000-artists[j].Popularity)
return ai < aj
})
// If the first one has the same name, that's the one
if strings.ToLower(artists[0].Name) != name {
return nil, model.ErrNotFound
}
return &artists[0], err
}
func init() {
conf.AddHook(func() {
if conf.Server.Spotify.ID != "" && conf.Server.Spotify.Secret != "" {
agents.Register(spotifyAgentName, spotifyConstructor)
}
})
}

View File

@@ -0,0 +1,17 @@
package spotify
import (
"testing"
"github.com/navidrome/navidrome/log"
"github.com/navidrome/navidrome/tests"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
func TestSpotify(t *testing.T) {
tests.Init(t, false)
log.SetLevel(log.LevelFatal)
RegisterFailHandler(Fail)
RunSpecs(t, "Spotify Test Suite")
}

172
core/archiver.go Normal file
View File

@@ -0,0 +1,172 @@
package core
import (
"archive/zip"
"context"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"github.com/Masterminds/squirrel"
"github.com/navidrome/navidrome/log"
"github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/utils/slice"
)
type Archiver interface {
ZipAlbum(ctx context.Context, id string, format string, bitrate int, w io.Writer) error
ZipArtist(ctx context.Context, id string, format string, bitrate int, w io.Writer) error
ZipShare(ctx context.Context, id string, w io.Writer) error
ZipPlaylist(ctx context.Context, id string, format string, bitrate int, w io.Writer) error
}
func NewArchiver(ms MediaStreamer, ds model.DataStore, shares Share) Archiver {
return &archiver{ds: ds, ms: ms, shares: shares}
}
type archiver struct {
ds model.DataStore
ms MediaStreamer
shares Share
}
func (a *archiver) ZipAlbum(ctx context.Context, id string, format string, bitrate int, out io.Writer) error {
return a.zipAlbums(ctx, id, format, bitrate, out, squirrel.Eq{"album_id": id})
}
func (a *archiver) ZipArtist(ctx context.Context, id string, format string, bitrate int, out io.Writer) error {
return a.zipAlbums(ctx, id, format, bitrate, out, squirrel.Eq{"album_artist_id": id})
}
func (a *archiver) zipAlbums(ctx context.Context, id string, format string, bitrate int, out io.Writer, filters squirrel.Sqlizer) error {
mfs, err := a.ds.MediaFile(ctx).GetAll(model.QueryOptions{Filters: filters, Sort: "album"})
if err != nil {
log.Error(ctx, "Error loading mediafiles from artist", "id", id, err)
return err
}
z := createZipWriter(out, format, bitrate)
albums := slice.Group(mfs, func(mf model.MediaFile) string {
return mf.AlbumID
})
for _, album := range albums {
discs := slice.Group(album, func(mf model.MediaFile) int { return mf.DiscNumber })
isMultDisc := len(discs) > 1
log.Debug(ctx, "Zipping album", "name", album[0].Album, "artist", album[0].AlbumArtist,
"format", format, "bitrate", bitrate, "isMultDisc", isMultDisc, "numTracks", len(album))
for _, mf := range album {
file := a.albumFilename(mf, format, isMultDisc)
_ = a.addFileToZip(ctx, z, mf, format, bitrate, file)
}
}
err = z.Close()
if err != nil {
log.Error(ctx, "Error closing zip file", "id", id, err)
}
return err
}
func createZipWriter(out io.Writer, format string, bitrate int) *zip.Writer {
z := zip.NewWriter(out)
comment := "Downloaded from Navidrome"
if format != "raw" && format != "" {
comment = fmt.Sprintf("%s, transcoded to %s %dbps", comment, format, bitrate)
}
_ = z.SetComment(comment)
return z
}
func (a *archiver) albumFilename(mf model.MediaFile, format string, isMultDisc bool) string {
_, file := filepath.Split(mf.Path)
if format != "raw" {
file = strings.TrimSuffix(file, mf.Suffix) + format
}
if isMultDisc {
file = fmt.Sprintf("Disc %02d/%s", mf.DiscNumber, file)
}
return fmt.Sprintf("%s/%s", mf.Album, file)
}
func (a *archiver) ZipShare(ctx context.Context, id string, out io.Writer) error {
s, err := a.shares.Load(ctx, id)
if !s.Downloadable {
return model.ErrNotAuthorized
}
if err != nil {
return err
}
log.Debug(ctx, "Zipping share", "name", s.ID, "format", s.Format, "bitrate", s.MaxBitRate, "numTracks", len(s.Tracks))
return a.zipMediaFiles(ctx, id, s.Format, s.MaxBitRate, out, s.Tracks)
}
func (a *archiver) ZipPlaylist(ctx context.Context, id string, format string, bitrate int, out io.Writer) error {
pls, err := a.ds.Playlist(ctx).GetWithTracks(id, true)
if err != nil {
log.Error(ctx, "Error loading mediafiles from playlist", "id", id, err)
return err
}
mfs := pls.MediaFiles()
log.Debug(ctx, "Zipping playlist", "name", pls.Name, "format", format, "bitrate", bitrate, "numTracks", len(mfs))
return a.zipMediaFiles(ctx, id, format, bitrate, out, mfs)
}
func (a *archiver) zipMediaFiles(ctx context.Context, id string, format string, bitrate int, out io.Writer, mfs model.MediaFiles) error {
z := createZipWriter(out, format, bitrate)
for idx, mf := range mfs {
file := a.playlistFilename(mf, format, idx)
_ = a.addFileToZip(ctx, z, mf, format, bitrate, file)
}
err := z.Close()
if err != nil {
log.Error(ctx, "Error closing zip file", "id", id, err)
}
return err
}
func (a *archiver) playlistFilename(mf model.MediaFile, format string, idx int) string {
ext := mf.Suffix
if format != "" && format != "raw" {
ext = format
}
file := fmt.Sprintf("%02d - %s - %s.%s", idx+1, mf.Artist, mf.Title, ext)
return file
}
func (a *archiver) addFileToZip(ctx context.Context, z *zip.Writer, mf model.MediaFile, format string, bitrate int, filename string) error {
w, err := z.CreateHeader(&zip.FileHeader{
Name: filename,
Modified: mf.UpdatedAt,
Method: zip.Store,
})
if err != nil {
log.Error(ctx, "Error creating zip entry", "file", mf.Path, err)
return err
}
var r io.ReadCloser
if format != "raw" && format != "" {
r, err = a.ms.DoStream(ctx, &mf, format, bitrate)
} else {
r, err = os.Open(mf.Path)
}
if err != nil {
log.Error(ctx, "Error opening file for zipping", "file", mf.Path, "format", format, err)
return err
}
defer func() {
if err := r.Close(); err != nil && log.CurrentLevel() >= log.LevelDebug {
log.Error(ctx, "Error closing stream", "id", mf.ID, "file", mf.Path, err)
}
}()
_, err = io.Copy(w, r)
if err != nil {
log.Error(ctx, "Error zipping file", "file", mf.Path, err)
return err
}
return nil
}

211
core/archiver_test.go Normal file
View File

@@ -0,0 +1,211 @@
package core_test
import (
"archive/zip"
"bytes"
"context"
"io"
"strings"
"github.com/Masterminds/squirrel"
"github.com/navidrome/navidrome/core"
"github.com/navidrome/navidrome/model"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/stretchr/testify/mock"
)
var _ = Describe("Archiver", func() {
var (
arch core.Archiver
ms *mockMediaStreamer
ds *mockDataStore
sh *mockShare
)
BeforeEach(func() {
ms = &mockMediaStreamer{}
ds = &mockDataStore{}
sh = &mockShare{}
arch = core.NewArchiver(ms, ds, sh)
})
Context("ZipAlbum", func() {
It("zips an album correctly", func() {
mfs := model.MediaFiles{
{Path: "test_data/01 - track1.mp3", Suffix: "mp3", AlbumID: "1", Album: "Album 1", DiscNumber: 1},
{Path: "test_data/02 - track2.mp3", Suffix: "mp3", AlbumID: "1", Album: "Album 1", DiscNumber: 1},
}
mfRepo := &mockMediaFileRepository{}
mfRepo.On("GetAll", []model.QueryOptions{{
Filters: squirrel.Eq{"album_id": "1"},
Sort: "album",
}}).Return(mfs, nil)
ds.On("MediaFile", mock.Anything).Return(mfRepo)
ms.On("DoStream", mock.Anything, mock.Anything, "mp3", 128).Return(io.NopCloser(strings.NewReader("test")), nil).Times(2)
out := new(bytes.Buffer)
err := arch.ZipAlbum(context.Background(), "1", "mp3", 128, out)
Expect(err).To(BeNil())
zr, err := zip.NewReader(bytes.NewReader(out.Bytes()), int64(out.Len()))
Expect(err).To(BeNil())
Expect(len(zr.File)).To(Equal(2))
Expect(zr.File[0].Name).To(Equal("Album 1/01 - track1.mp3"))
Expect(zr.File[1].Name).To(Equal("Album 1/02 - track2.mp3"))
})
})
Context("ZipArtist", func() {
It("zips an artist's albums correctly", func() {
mfs := model.MediaFiles{
{Path: "test_data/01 - track1.mp3", Suffix: "mp3", AlbumArtistID: "1", AlbumID: "1", Album: "Album 1", DiscNumber: 1},
{Path: "test_data/02 - track2.mp3", Suffix: "mp3", AlbumArtistID: "1", AlbumID: "1", Album: "Album 1", DiscNumber: 1},
}
mfRepo := &mockMediaFileRepository{}
mfRepo.On("GetAll", []model.QueryOptions{{
Filters: squirrel.Eq{"album_artist_id": "1"},
Sort: "album",
}}).Return(mfs, nil)
ds.On("MediaFile", mock.Anything).Return(mfRepo)
ms.On("DoStream", mock.Anything, mock.Anything, "mp3", 128).Return(io.NopCloser(strings.NewReader("test")), nil).Times(2)
out := new(bytes.Buffer)
err := arch.ZipArtist(context.Background(), "1", "mp3", 128, out)
Expect(err).To(BeNil())
zr, err := zip.NewReader(bytes.NewReader(out.Bytes()), int64(out.Len()))
Expect(err).To(BeNil())
Expect(len(zr.File)).To(Equal(2))
Expect(zr.File[0].Name).To(Equal("Album 1/01 - track1.mp3"))
Expect(zr.File[1].Name).To(Equal("Album 1/02 - track2.mp3"))
})
})
Context("ZipShare", func() {
It("zips a share correctly", func() {
mfs := model.MediaFiles{
{ID: "1", Path: "test_data/01 - track1.mp3", Suffix: "mp3", Artist: "Artist 1", Title: "track1"},
{ID: "2", Path: "test_data/02 - track2.mp3", Suffix: "mp3", Artist: "Artist 2", Title: "track2"},
}
share := &model.Share{
ID: "1",
Downloadable: true,
Format: "mp3",
MaxBitRate: 128,
Tracks: mfs,
}
sh.On("Load", mock.Anything, "1").Return(share, nil)
ms.On("DoStream", mock.Anything, mock.Anything, "mp3", 128).Return(io.NopCloser(strings.NewReader("test")), nil).Times(2)
out := new(bytes.Buffer)
err := arch.ZipShare(context.Background(), "1", out)
Expect(err).To(BeNil())
zr, err := zip.NewReader(bytes.NewReader(out.Bytes()), int64(out.Len()))
Expect(err).To(BeNil())
Expect(len(zr.File)).To(Equal(2))
Expect(zr.File[0].Name).To(Equal("01 - Artist 1 - track1.mp3"))
Expect(zr.File[1].Name).To(Equal("02 - Artist 2 - track2.mp3"))
})
})
Context("ZipPlaylist", func() {
It("zips a playlist correctly", func() {
tracks := []model.PlaylistTrack{
{MediaFile: model.MediaFile{Path: "test_data/01 - track1.mp3", Suffix: "mp3", AlbumID: "1", Album: "Album 1", DiscNumber: 1, Artist: "Artist 1", Title: "track1"}},
{MediaFile: model.MediaFile{Path: "test_data/02 - track2.mp3", Suffix: "mp3", AlbumID: "1", Album: "Album 1", DiscNumber: 1, Artist: "Artist 2", Title: "track2"}},
}
pls := &model.Playlist{
ID: "1",
Name: "Test Playlist",
Tracks: tracks,
}
plRepo := &mockPlaylistRepository{}
plRepo.On("GetWithTracks", "1", true).Return(pls, nil)
ds.On("Playlist", mock.Anything).Return(plRepo)
ms.On("DoStream", mock.Anything, mock.Anything, "mp3", 128).Return(io.NopCloser(strings.NewReader("test")), nil).Times(2)
out := new(bytes.Buffer)
err := arch.ZipPlaylist(context.Background(), "1", "mp3", 128, out)
Expect(err).To(BeNil())
zr, err := zip.NewReader(bytes.NewReader(out.Bytes()), int64(out.Len()))
Expect(err).To(BeNil())
Expect(len(zr.File)).To(Equal(2))
Expect(zr.File[0].Name).To(Equal("01 - Artist 1 - track1.mp3"))
Expect(zr.File[1].Name).To(Equal("02 - Artist 2 - track2.mp3"))
})
})
})
type mockDataStore struct {
mock.Mock
model.DataStore
}
func (m *mockDataStore) MediaFile(ctx context.Context) model.MediaFileRepository {
args := m.Called(ctx)
return args.Get(0).(model.MediaFileRepository)
}
func (m *mockDataStore) Playlist(ctx context.Context) model.PlaylistRepository {
args := m.Called(ctx)
return args.Get(0).(model.PlaylistRepository)
}
type mockMediaFileRepository struct {
mock.Mock
model.MediaFileRepository
}
func (m *mockMediaFileRepository) GetAll(options ...model.QueryOptions) (model.MediaFiles, error) {
args := m.Called(options)
return args.Get(0).(model.MediaFiles), args.Error(1)
}
type mockPlaylistRepository struct {
mock.Mock
model.PlaylistRepository
}
func (m *mockPlaylistRepository) GetWithTracks(id string, includeTracks bool) (*model.Playlist, error) {
args := m.Called(id, includeTracks)
return args.Get(0).(*model.Playlist), args.Error(1)
}
type mockMediaStreamer struct {
mock.Mock
core.MediaStreamer
}
func (m *mockMediaStreamer) DoStream(ctx context.Context, mf *model.MediaFile, format string, bitrate int) (*core.Stream, error) {
args := m.Called(ctx, mf, format, bitrate)
if args.Error(1) != nil {
return nil, args.Error(1)
}
return &core.Stream{ReadCloser: args.Get(0).(io.ReadCloser)}, nil
}
type mockShare struct {
mock.Mock
core.Share
}
func (m *mockShare) Load(ctx context.Context, id string) (*model.Share, error) {
args := m.Called(ctx, id)
return args.Get(0).(*model.Share), args.Error(1)
}

130
core/artwork/artwork.go Normal file
View File

@@ -0,0 +1,130 @@
package artwork
import (
"context"
"errors"
_ "image/gif"
"io"
"time"
"github.com/navidrome/navidrome/consts"
"github.com/navidrome/navidrome/core"
"github.com/navidrome/navidrome/core/ffmpeg"
"github.com/navidrome/navidrome/log"
"github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/resources"
"github.com/navidrome/navidrome/utils/cache"
_ "golang.org/x/image/webp"
)
var ErrUnavailable = errors.New("artwork unavailable")
type Artwork interface {
Get(ctx context.Context, artID model.ArtworkID, size int) (io.ReadCloser, time.Time, error)
GetOrPlaceholder(ctx context.Context, id string, size int) (io.ReadCloser, time.Time, error)
}
func NewArtwork(ds model.DataStore, cache cache.FileCache, ffmpeg ffmpeg.FFmpeg, em core.ExternalMetadata) Artwork {
return &artwork{ds: ds, cache: cache, ffmpeg: ffmpeg, em: em}
}
type artwork struct {
ds model.DataStore
cache cache.FileCache
ffmpeg ffmpeg.FFmpeg
em core.ExternalMetadata
}
type artworkReader interface {
cache.Item
LastUpdated() time.Time
Reader(ctx context.Context) (io.ReadCloser, string, error)
}
func (a *artwork) GetOrPlaceholder(ctx context.Context, id string, size int) (reader io.ReadCloser, lastUpdate time.Time, err error) {
artID, err := a.getArtworkId(ctx, id)
if err == nil {
reader, lastUpdate, err = a.Get(ctx, artID, size)
}
if errors.Is(err, ErrUnavailable) {
if artID.Kind == model.KindArtistArtwork {
reader, _ = resources.FS().Open(consts.PlaceholderArtistArt)
} else {
reader, _ = resources.FS().Open(consts.PlaceholderAlbumArt)
}
return reader, consts.ServerStart, nil
}
return reader, lastUpdate, err
}
func (a *artwork) Get(ctx context.Context, artID model.ArtworkID, size int) (reader io.ReadCloser, lastUpdate time.Time, err error) {
artReader, err := a.getArtworkReader(ctx, artID, size)
if err != nil {
return nil, time.Time{}, err
}
r, err := a.cache.Get(ctx, artReader)
if err != nil {
if !errors.Is(err, context.Canceled) && !errors.Is(err, ErrUnavailable) {
log.Error(ctx, "Error accessing image cache", "id", artID, "size", size, err)
}
return nil, time.Time{}, err
}
return r, artReader.LastUpdated(), nil
}
type coverArtGetter interface {
CoverArtID() model.ArtworkID
}
func (a *artwork) getArtworkId(ctx context.Context, id string) (model.ArtworkID, error) {
if id == "" {
return model.ArtworkID{}, ErrUnavailable
}
artID, err := model.ParseArtworkID(id)
if err == nil {
return artID, nil
}
log.Trace(ctx, "ArtworkID invalid. Trying to figure out kind based on the ID", "id", id)
entity, err := model.GetEntityByID(ctx, a.ds, id)
if err != nil {
return model.ArtworkID{}, err
}
if e, ok := entity.(coverArtGetter); ok {
artID = e.CoverArtID()
}
switch e := entity.(type) {
case *model.Artist:
log.Trace(ctx, "ID is for an Artist", "id", id, "name", e.Name, "artist", e.Name)
case *model.Album:
log.Trace(ctx, "ID is for an Album", "id", id, "name", e.Name, "artist", e.AlbumArtist)
case *model.MediaFile:
log.Trace(ctx, "ID is for a MediaFile", "id", id, "title", e.Title, "album", e.Album)
case *model.Playlist:
log.Trace(ctx, "ID is for a Playlist", "id", id, "name", e.Name)
}
return artID, nil
}
func (a *artwork) getArtworkReader(ctx context.Context, artID model.ArtworkID, size int) (artworkReader, error) {
var artReader artworkReader
var err error
if size > 0 {
artReader, err = resizedFromOriginal(ctx, a, artID, size)
} else {
switch artID.Kind {
case model.KindArtistArtwork:
artReader, err = newArtistReader(ctx, a, artID, a.em)
case model.KindAlbumArtwork:
artReader, err = newAlbumArtworkReader(ctx, a, artID, a.em)
case model.KindMediaFileArtwork:
artReader, err = newMediafileArtworkReader(ctx, a, artID)
case model.KindPlaylistArtwork:
artReader, err = newPlaylistArtworkReader(ctx, a, artID)
default:
return nil, ErrUnavailable
}
}
return artReader, err
}

View File

@@ -0,0 +1,243 @@
package artwork
import (
"context"
"errors"
"image"
"io"
"github.com/navidrome/navidrome/conf"
"github.com/navidrome/navidrome/conf/configtest"
"github.com/navidrome/navidrome/consts"
"github.com/navidrome/navidrome/log"
"github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/tests"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("Artwork", func() {
var aw *artwork
var ds model.DataStore
var ffmpeg *tests.MockFFmpeg
ctx := log.NewContext(context.TODO())
var alOnlyEmbed, alEmbedNotFound, alOnlyExternal, alExternalNotFound, alMultipleCovers model.Album
var arMultipleCovers model.Artist
var mfWithEmbed, mfAnotherWithEmbed, mfWithoutEmbed, mfCorruptedCover model.MediaFile
BeforeEach(func() {
DeferCleanup(configtest.SetupConfig())
conf.Server.ImageCacheSize = "0" // Disable cache
conf.Server.CoverArtPriority = "folder.*, cover.*, embedded , front.*"
ds = &tests.MockDataStore{MockedTranscoding: &tests.MockTranscodingRepo{}}
alOnlyEmbed = model.Album{ID: "222", Name: "Only embed", EmbedArtPath: "tests/fixtures/artist/an-album/test.mp3"}
alEmbedNotFound = model.Album{ID: "333", Name: "Embed not found", EmbedArtPath: "tests/fixtures/NON_EXISTENT.mp3"}
alOnlyExternal = model.Album{ID: "444", Name: "Only external", ImageFiles: "tests/fixtures/artist/an-album/front.png"}
alExternalNotFound = model.Album{ID: "555", Name: "External not found", ImageFiles: "tests/fixtures/NON_EXISTENT.png"}
arMultipleCovers = model.Artist{ID: "777", Name: "All options"}
alMultipleCovers = model.Album{
ID: "666",
Name: "All options",
EmbedArtPath: "tests/fixtures/artist/an-album/test.mp3",
Paths: "tests/fixtures/artist/an-album",
ImageFiles: "tests/fixtures/artist/an-album/cover.jpg" + consts.Zwsp +
"tests/fixtures/artist/an-album/front.png" + consts.Zwsp +
"tests/fixtures/artist/an-album/artist.png",
AlbumArtistID: "777",
}
mfWithEmbed = model.MediaFile{ID: "22", Path: "tests/fixtures/test.mp3", HasCoverArt: true, AlbumID: "222"}
mfAnotherWithEmbed = model.MediaFile{ID: "23", Path: "tests/fixtures/artist/an-album/test.mp3", HasCoverArt: true, AlbumID: "666"}
mfWithoutEmbed = model.MediaFile{ID: "44", Path: "tests/fixtures/test.ogg", AlbumID: "444"}
mfCorruptedCover = model.MediaFile{ID: "45", Path: "tests/fixtures/test.ogg", HasCoverArt: true, AlbumID: "444"}
cache := GetImageCache()
ffmpeg = tests.NewMockFFmpeg("content from ffmpeg")
aw = NewArtwork(ds, cache, ffmpeg, nil).(*artwork)
})
Describe("albumArtworkReader", func() {
Context("ID not found", func() {
It("returns ErrNotFound if album is not in the DB", func() {
_, err := newAlbumArtworkReader(ctx, aw, model.MustParseArtworkID("al-NOT-FOUND"), nil)
Expect(err).To(MatchError(model.ErrNotFound))
})
})
Context("Embed images", func() {
BeforeEach(func() {
ds.Album(ctx).(*tests.MockAlbumRepo).SetData(model.Albums{
alOnlyEmbed,
alEmbedNotFound,
})
})
It("returns embed cover", func() {
aw, err := newAlbumArtworkReader(ctx, aw, alOnlyEmbed.CoverArtID(), nil)
Expect(err).ToNot(HaveOccurred())
_, path, err := aw.Reader(ctx)
Expect(err).ToNot(HaveOccurred())
Expect(path).To(Equal("tests/fixtures/artist/an-album/test.mp3"))
})
It("returns ErrUnavailable if embed path is not available", func() {
ffmpeg.Error = errors.New("not available")
aw, err := newAlbumArtworkReader(ctx, aw, alEmbedNotFound.CoverArtID(), nil)
Expect(err).ToNot(HaveOccurred())
_, _, err = aw.Reader(ctx)
Expect(err).To(MatchError(ErrUnavailable))
})
})
Context("External images", func() {
BeforeEach(func() {
ds.Album(ctx).(*tests.MockAlbumRepo).SetData(model.Albums{
alOnlyExternal,
alExternalNotFound,
})
})
It("returns external cover", func() {
aw, err := newAlbumArtworkReader(ctx, aw, alOnlyExternal.CoverArtID(), nil)
Expect(err).ToNot(HaveOccurred())
_, path, err := aw.Reader(ctx)
Expect(err).ToNot(HaveOccurred())
Expect(path).To(Equal("tests/fixtures/artist/an-album/front.png"))
})
It("returns ErrUnavailable if external file is not available", func() {
aw, err := newAlbumArtworkReader(ctx, aw, alExternalNotFound.CoverArtID(), nil)
Expect(err).ToNot(HaveOccurred())
_, _, err = aw.Reader(ctx)
Expect(err).To(MatchError(ErrUnavailable))
})
})
Context("Multiple covers", func() {
BeforeEach(func() {
ds.Album(ctx).(*tests.MockAlbumRepo).SetData(model.Albums{
alMultipleCovers,
})
})
DescribeTable("CoverArtPriority",
func(priority string, expected string) {
conf.Server.CoverArtPriority = priority
aw, err := newAlbumArtworkReader(ctx, aw, alMultipleCovers.CoverArtID(), nil)
Expect(err).ToNot(HaveOccurred())
_, path, err := aw.Reader(ctx)
Expect(err).ToNot(HaveOccurred())
Expect(path).To(Equal(expected))
},
Entry(nil, " folder.* , cover.*,embedded,front.*", "tests/fixtures/artist/an-album/cover.jpg"),
Entry(nil, "front.* , cover.*, embedded ,folder.*", "tests/fixtures/artist/an-album/front.png"),
Entry(nil, " embedded , front.* , cover.*,folder.*", "tests/fixtures/artist/an-album/test.mp3"),
)
})
})
Describe("artistArtworkReader", func() {
Context("Multiple covers", func() {
BeforeEach(func() {
ds.Artist(ctx).(*tests.MockArtistRepo).SetData(model.Artists{
arMultipleCovers,
})
ds.Album(ctx).(*tests.MockAlbumRepo).SetData(model.Albums{
alMultipleCovers,
})
ds.MediaFile(ctx).(*tests.MockMediaFileRepo).SetData(model.MediaFiles{
mfAnotherWithEmbed,
})
})
DescribeTable("ArtistArtPriority",
func(priority string, expected string) {
conf.Server.ArtistArtPriority = priority
aw, err := newArtistReader(ctx, aw, arMultipleCovers.CoverArtID(), nil)
Expect(err).ToNot(HaveOccurred())
_, path, err := aw.Reader(ctx)
Expect(err).ToNot(HaveOccurred())
Expect(path).To(Equal(expected))
},
Entry(nil, " folder.* , artist.*,album/artist.*", "tests/fixtures/artist/artist.jpg"),
Entry(nil, "album/artist.*, folder.*,artist.*", "tests/fixtures/artist/an-album/artist.png"),
)
})
})
Describe("mediafileArtworkReader", func() {
Context("ID not found", func() {
It("returns ErrNotFound if mediafile is not in the DB", func() {
_, err := newAlbumArtworkReader(ctx, aw, alMultipleCovers.CoverArtID(), nil)
Expect(err).To(MatchError(model.ErrNotFound))
})
})
Context("Embed images", func() {
BeforeEach(func() {
ds.Album(ctx).(*tests.MockAlbumRepo).SetData(model.Albums{
alOnlyEmbed,
alOnlyExternal,
})
ds.MediaFile(ctx).(*tests.MockMediaFileRepo).SetData(model.MediaFiles{
mfWithEmbed,
mfWithoutEmbed,
mfCorruptedCover,
})
})
It("returns embed cover", func() {
aw, err := newMediafileArtworkReader(ctx, aw, mfWithEmbed.CoverArtID())
Expect(err).ToNot(HaveOccurred())
_, path, err := aw.Reader(ctx)
Expect(err).ToNot(HaveOccurred())
Expect(path).To(Equal("tests/fixtures/test.mp3"))
})
It("returns embed cover if successfully extracted by ffmpeg", func() {
aw, err := newMediafileArtworkReader(ctx, aw, mfCorruptedCover.CoverArtID())
Expect(err).ToNot(HaveOccurred())
r, path, err := aw.Reader(ctx)
Expect(err).ToNot(HaveOccurred())
Expect(io.ReadAll(r)).To(Equal([]byte("content from ffmpeg")))
Expect(path).To(Equal("tests/fixtures/test.ogg"))
})
It("returns album cover if cannot read embed artwork", func() {
ffmpeg.Error = errors.New("not available")
aw, err := newMediafileArtworkReader(ctx, aw, mfCorruptedCover.CoverArtID())
Expect(err).ToNot(HaveOccurred())
_, path, err := aw.Reader(ctx)
Expect(err).ToNot(HaveOccurred())
Expect(path).To(Equal("al-444_0"))
})
It("returns album cover if media file has no cover art", func() {
aw, err := newMediafileArtworkReader(ctx, aw, model.MustParseArtworkID("mf-"+mfWithoutEmbed.ID))
Expect(err).ToNot(HaveOccurred())
_, path, err := aw.Reader(ctx)
Expect(err).ToNot(HaveOccurred())
Expect(path).To(Equal("al-444_0"))
})
})
})
Describe("resizedArtworkReader", func() {
BeforeEach(func() {
ds.Album(ctx).(*tests.MockAlbumRepo).SetData(model.Albums{
alMultipleCovers,
})
})
It("returns a PNG if original image is a PNG", func() {
conf.Server.CoverArtPriority = "front.png"
r, _, err := aw.Get(context.Background(), alMultipleCovers.CoverArtID(), 15)
Expect(err).ToNot(HaveOccurred())
br, format, err := asImageReader(r)
Expect(format).To(Equal("image/png"))
Expect(err).ToNot(HaveOccurred())
img, _, err := image.Decode(br)
Expect(err).ToNot(HaveOccurred())
Expect(img.Bounds().Size().X).To(Equal(15))
Expect(img.Bounds().Size().Y).To(Equal(15))
})
It("returns a JPEG if original image is not a PNG", func() {
conf.Server.CoverArtPriority = "cover.jpg"
r, _, err := aw.Get(context.Background(), alMultipleCovers.CoverArtID(), 200)
Expect(err).ToNot(HaveOccurred())
br, format, err := asImageReader(r)
Expect(format).To(Equal("image/jpeg"))
Expect(err).ToNot(HaveOccurred())
img, _, err := image.Decode(br)
Expect(err).ToNot(HaveOccurred())
Expect(img.Bounds().Size().X).To(Equal(200))
Expect(img.Bounds().Size().Y).To(Equal(200))
})
})
})

View File

@@ -0,0 +1,17 @@
package artwork
import (
"testing"
"github.com/navidrome/navidrome/log"
"github.com/navidrome/navidrome/tests"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
func TestArtwork(t *testing.T) {
tests.Init(t, false)
log.SetLevel(log.LevelFatal)
RegisterFailHandler(Fail)
RunSpecs(t, "Artwork Suite")
}

View File

@@ -0,0 +1,57 @@
package artwork_test
import (
"context"
"io"
"github.com/navidrome/navidrome/conf"
"github.com/navidrome/navidrome/conf/configtest"
"github.com/navidrome/navidrome/consts"
"github.com/navidrome/navidrome/core/artwork"
"github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/resources"
"github.com/navidrome/navidrome/tests"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("Artwork", func() {
var aw artwork.Artwork
var ds model.DataStore
var ffmpeg *tests.MockFFmpeg
BeforeEach(func() {
DeferCleanup(configtest.SetupConfig())
conf.Server.ImageCacheSize = "0" // Disable cache
cache := artwork.GetImageCache()
ffmpeg = tests.NewMockFFmpeg("content from ffmpeg")
aw = artwork.NewArtwork(ds, cache, ffmpeg, nil)
})
Context("GetOrPlaceholder", func() {
Context("Empty ID", func() {
It("returns placeholder if album is not in the DB", func() {
r, _, err := aw.GetOrPlaceholder(context.Background(), "", 0)
Expect(err).ToNot(HaveOccurred())
ph, err := resources.FS().Open(consts.PlaceholderAlbumArt)
Expect(err).ToNot(HaveOccurred())
phBytes, err := io.ReadAll(ph)
Expect(err).ToNot(HaveOccurred())
result, err := io.ReadAll(r)
Expect(err).ToNot(HaveOccurred())
Expect(result).To(Equal(phBytes))
})
})
})
Context("Get", func() {
Context("Empty ID", func() {
It("returns an ErrUnavailable error", func() {
_, _, err := aw.Get(context.Background(), model.ArtworkID{}, 0)
Expect(err).To(MatchError(artwork.ErrUnavailable))
})
})
})
})

View File

@@ -0,0 +1,146 @@
package artwork
import (
"context"
"fmt"
"io"
"sync"
"time"
"github.com/navidrome/navidrome/conf"
"github.com/navidrome/navidrome/consts"
"github.com/navidrome/navidrome/log"
"github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/model/request"
"github.com/navidrome/navidrome/utils/cache"
"github.com/navidrome/navidrome/utils/pl"
"golang.org/x/exp/maps"
)
type CacheWarmer interface {
PreCache(artID model.ArtworkID)
}
func NewCacheWarmer(artwork Artwork, cache cache.FileCache) CacheWarmer {
// If image cache is disabled, return a NOOP implementation
if conf.Server.ImageCacheSize == "0" || !conf.Server.EnableArtworkPrecache {
return &noopCacheWarmer{}
}
a := &cacheWarmer{
artwork: artwork,
cache: cache,
buffer: make(map[model.ArtworkID]struct{}),
wakeSignal: make(chan struct{}, 1),
}
// Create a context with a fake admin user, to be able to pre-cache Playlist CoverArts
ctx := request.WithUser(context.TODO(), model.User{IsAdmin: true})
go a.run(ctx)
return a
}
type cacheWarmer struct {
artwork Artwork
buffer map[model.ArtworkID]struct{}
mutex sync.Mutex
cache cache.FileCache
wakeSignal chan struct{}
}
var ignoredIds = map[string]struct{}{
consts.VariousArtistsID: {},
consts.UnknownArtistID: {},
}
func (a *cacheWarmer) PreCache(artID model.ArtworkID) {
if _, shouldIgnore := ignoredIds[artID.ID]; shouldIgnore {
return
}
a.mutex.Lock()
defer a.mutex.Unlock()
a.buffer[artID] = struct{}{}
a.sendWakeSignal()
}
func (a *cacheWarmer) sendWakeSignal() {
// Don't block if the previous signal was not read yet
select {
case a.wakeSignal <- struct{}{}:
default:
}
}
func (a *cacheWarmer) run(ctx context.Context) {
for {
a.waitSignal(ctx, 10*time.Second)
if ctx.Err() != nil {
break
}
// If cache not available, keep waiting
if !a.cache.Available(ctx) {
if len(a.buffer) > 0 {
log.Trace(ctx, "Cache not available, buffering precache request", "bufferLen", len(a.buffer))
}
continue
}
a.mutex.Lock()
// If there's nothing to send, keep waiting
if len(a.buffer) == 0 {
a.mutex.Unlock()
continue
}
batch := maps.Keys(a.buffer)
a.buffer = make(map[model.ArtworkID]struct{})
a.mutex.Unlock()
a.processBatch(ctx, batch)
}
}
func (a *cacheWarmer) waitSignal(ctx context.Context, timeout time.Duration) {
var to <-chan time.Time
if !a.cache.Available(ctx) {
tmr := time.NewTimer(timeout)
defer tmr.Stop()
to = tmr.C
}
select {
case <-to:
case <-a.wakeSignal:
case <-ctx.Done():
}
}
func (a *cacheWarmer) processBatch(ctx context.Context, batch []model.ArtworkID) {
log.Trace(ctx, "PreCaching a new batch of artwork", "batchSize", len(batch))
input := pl.FromSlice(ctx, batch)
errs := pl.Sink(ctx, 2, input, a.doCacheImage)
for err := range errs {
log.Warn(ctx, "Error warming cache", err)
}
}
func (a *cacheWarmer) doCacheImage(ctx context.Context, id model.ArtworkID) error {
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
r, _, err := a.artwork.Get(ctx, id, consts.UICoverArtSize)
if err != nil {
return fmt.Errorf("error cacheing id='%s': %w", id, err)
}
defer r.Close()
_, err = io.Copy(io.Discard, r)
if err != nil {
return err
}
return nil
}
type noopCacheWarmer struct{}
func (a *noopCacheWarmer) PreCache(model.ArtworkID) {}

View File

@@ -0,0 +1,44 @@
package artwork
import (
"context"
"fmt"
"io"
"time"
"github.com/navidrome/navidrome/conf"
"github.com/navidrome/navidrome/consts"
"github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/utils/cache"
"github.com/navidrome/navidrome/utils/singleton"
)
type cacheKey struct {
artID model.ArtworkID
lastUpdate time.Time
}
func (k *cacheKey) Key() string {
return fmt.Sprintf(
"%s-%s.%d",
k.artID.Kind,
k.artID.ID,
k.lastUpdate.UnixMilli(),
)
}
type imageCache struct {
cache.FileCache
}
func GetImageCache() cache.FileCache {
return singleton.GetInstance(func() *imageCache {
return &imageCache{
FileCache: cache.NewFileCache("Image", conf.Server.ImageCacheSize, consts.ImageCacheDir, consts.DefaultImageCacheMaxItems,
func(ctx context.Context, arg cache.Item) (io.Reader, error) {
r, _, err := arg.(artworkReader).Reader(ctx)
return r, err
}),
}
})
}

View File

@@ -0,0 +1,74 @@
package artwork
import (
"context"
"crypto/md5"
"fmt"
"io"
"strings"
"time"
"github.com/navidrome/navidrome/conf"
"github.com/navidrome/navidrome/core"
"github.com/navidrome/navidrome/core/ffmpeg"
"github.com/navidrome/navidrome/model"
)
type albumArtworkReader struct {
cacheKey
a *artwork
em core.ExternalMetadata
album model.Album
}
func newAlbumArtworkReader(ctx context.Context, artwork *artwork, artID model.ArtworkID, em core.ExternalMetadata) (*albumArtworkReader, error) {
al, err := artwork.ds.Album(ctx).Get(artID.ID)
if err != nil {
return nil, err
}
a := &albumArtworkReader{
a: artwork,
em: em,
album: *al,
}
a.cacheKey.artID = artID
a.cacheKey.lastUpdate = al.UpdatedAt
return a, nil
}
func (a *albumArtworkReader) Key() string {
var hash [16]byte
if conf.Server.EnableExternalServices {
hash = md5.Sum([]byte(conf.Server.Agents + conf.Server.CoverArtPriority))
}
return fmt.Sprintf(
"%s.%x.%t",
a.cacheKey.Key(),
hash,
conf.Server.EnableExternalServices,
)
}
func (a *albumArtworkReader) LastUpdated() time.Time {
return a.album.UpdatedAt
}
func (a *albumArtworkReader) Reader(ctx context.Context) (io.ReadCloser, string, error) {
var ff = a.fromCoverArtPriority(ctx, a.a.ffmpeg, conf.Server.CoverArtPriority)
return selectImageReader(ctx, a.artID, ff...)
}
func (a *albumArtworkReader) fromCoverArtPriority(ctx context.Context, ffmpeg ffmpeg.FFmpeg, priority string) []sourceFunc {
var ff []sourceFunc
for _, pattern := range strings.Split(strings.ToLower(priority), ",") {
pattern = strings.TrimSpace(pattern)
switch {
case pattern == "embedded":
ff = append(ff, fromTag(a.album.EmbedArtPath), fromFFmpegTag(ctx, ffmpeg, a.album.EmbedArtPath))
case pattern == "external":
ff = append(ff, fromAlbumExternalSource(ctx, a.album, a.em))
case a.album.ImageFiles != "":
ff = append(ff, fromExternalFile(ctx, a.album.ImageFiles, pattern))
}
}
return ff
}

View File

@@ -0,0 +1,127 @@
package artwork
import (
"context"
"crypto/md5"
"fmt"
"io"
"io/fs"
"os"
"path/filepath"
"strings"
"time"
"github.com/Masterminds/squirrel"
"github.com/navidrome/navidrome/conf"
"github.com/navidrome/navidrome/consts"
"github.com/navidrome/navidrome/core"
"github.com/navidrome/navidrome/log"
"github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/utils"
)
type artistReader struct {
cacheKey
a *artwork
em core.ExternalMetadata
artist model.Artist
artistFolder string
files string
}
func newArtistReader(ctx context.Context, artwork *artwork, artID model.ArtworkID, em core.ExternalMetadata) (*artistReader, error) {
ar, err := artwork.ds.Artist(ctx).Get(artID.ID)
if err != nil {
return nil, err
}
als, err := artwork.ds.Album(ctx).GetAll(model.QueryOptions{Filters: squirrel.Eq{"album_artist_id": artID.ID}})
if err != nil {
return nil, err
}
a := &artistReader{
a: artwork,
em: em,
artist: *ar,
}
// TODO Find a way to factor in the ExternalUpdateInfoAt in the cache key. Problem is that it can
// change _after_ retrieving from external sources, making the key invalid
//a.cacheKey.lastUpdate = ar.ExternalInfoUpdatedAt
var files []string
var paths []string
for _, al := range als {
files = append(files, al.ImageFiles)
paths = append(paths, splitList(al.Paths)...)
if a.cacheKey.lastUpdate.Before(al.UpdatedAt) {
a.cacheKey.lastUpdate = al.UpdatedAt
}
}
a.files = strings.Join(files, consts.Zwsp)
a.artistFolder = utils.LongestCommonPrefix(paths)
if !strings.HasSuffix(a.artistFolder, string(filepath.Separator)) {
a.artistFolder, _ = filepath.Split(a.artistFolder)
}
a.cacheKey.artID = artID
return a, nil
}
func (a *artistReader) Key() string {
hash := md5.Sum([]byte(conf.Server.Agents + conf.Server.Spotify.ID))
return fmt.Sprintf(
"%s.%t.%x",
a.cacheKey.Key(),
conf.Server.EnableExternalServices,
hash,
)
}
func (a *artistReader) LastUpdated() time.Time {
return a.lastUpdate
}
func (a *artistReader) Reader(ctx context.Context) (io.ReadCloser, string, error) {
var ff = a.fromArtistArtPriority(ctx, conf.Server.ArtistArtPriority)
return selectImageReader(ctx, a.artID, ff...)
}
func (a *artistReader) fromArtistArtPriority(ctx context.Context, priority string) []sourceFunc {
var ff []sourceFunc
for _, pattern := range strings.Split(strings.ToLower(priority), ",") {
pattern = strings.TrimSpace(pattern)
switch {
case pattern == "external":
ff = append(ff, fromArtistExternalSource(ctx, a.artist, a.em))
case strings.HasPrefix(pattern, "album/"):
ff = append(ff, fromExternalFile(ctx, a.files, strings.TrimPrefix(pattern, "album/")))
default:
ff = append(ff, fromArtistFolder(ctx, a.artistFolder, pattern))
}
}
return ff
}
func fromArtistFolder(ctx context.Context, artistFolder string, pattern string) sourceFunc {
return func() (io.ReadCloser, string, error) {
fsys := os.DirFS(artistFolder)
matches, err := fs.Glob(fsys, pattern)
if err != nil {
log.Warn(ctx, "Error matching artist image pattern", "pattern", pattern, "folder", artistFolder)
return nil, "", err
}
if len(matches) == 0 {
return nil, "", fmt.Errorf(`no matches for '%s' in '%s'`, pattern, artistFolder)
}
for _, m := range matches {
filePath := filepath.Join(artistFolder, m)
if !model.IsImageFile(m) {
continue
}
f, err := os.Open(filePath)
if err != nil {
log.Warn(ctx, "Could not open cover art file", "file", filePath, err)
return nil, "", err
}
return f, filePath, nil
}
return nil, "", nil
}
}

Some files were not shown because too many files have changed in this diff Show More