diff --git a/conf/configuration.go b/conf/configuration.go index 0614c1a29..1af859558 100644 --- a/conf/configuration.go +++ b/conf/configuration.go @@ -51,6 +51,7 @@ type configOptions struct { DefaultDownsamplingFormat string SearchFullString bool RecentlyAddedByModTime bool + PreferSortTags bool IgnoredArticles string IndexGroups string SubsonicArtistParticipations bool @@ -296,6 +297,7 @@ func init() { viper.SetDefault("defaultdownsamplingformat", consts.DefaultDownsamplingFormat) viper.SetDefault("searchfullstring", false) viper.SetDefault("recentlyaddedbymodtime", false) + viper.SetDefault("prefersorttags", 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) @@ -335,8 +337,8 @@ func init() { 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("lastfm.apikey", "") + viper.SetDefault("lastfm.secret", "") viper.SetDefault("spotify.id", "") viper.SetDefault("spotify.secret", "") viper.SetDefault("listenbrainz.enabled", true) diff --git a/consts/consts.go b/consts/consts.go index 3f99d0347..b1f284abf 100644 --- a/consts/consts.go +++ b/consts/consts.go @@ -81,12 +81,6 @@ const ( 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{}{ diff --git a/core/agents/lastfm/agent.go b/core/agents/lastfm/agent.go index a108f222b..ca3045c48 100644 --- a/core/agents/lastfm/agent.go +++ b/core/agents/lastfm/agent.go @@ -311,12 +311,14 @@ func (l *lastfmAgent) IsAuthorized(ctx context.Context, userId string) bool { 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) - }) + if conf.Server.LastFM.ApiKey != "" && conf.Server.LastFM.Secret != "" { + agents.Register(lastFMAgentName, func(ds model.DataStore) agents.Interface { + return lastFMConstructor(ds) + }) + scrobbler.Register(lastFMAgentName, func(ds model.DataStore) scrobbler.Scrobbler { + return lastFMConstructor(ds) + }) + } } }) } diff --git a/core/auth/auth.go b/core/auth/auth.go index 2dd7923ee..7725de8d6 100644 --- a/core/auth/auth.go +++ b/core/auth/auth.go @@ -6,6 +6,7 @@ import ( "time" "github.com/go-chi/jwtauth/v5" + "github.com/google/uuid" "github.com/lestrrat-go/jwx/v2/jwt" "github.com/navidrome/navidrome/conf" "github.com/navidrome/navidrome/consts" @@ -23,9 +24,10 @@ var ( func Init(ds model.DataStore) { once.Do(func() { log.Info("Setting Session Timeout", "value", conf.Server.SessionTimeout) - secret, err := ds.Property(context.TODO()).DefaultGet(consts.JWTSecretKey, "not so secret") - if err != nil { + secret, err := ds.Property(context.TODO()).Get(consts.JWTSecretKey) + if err != nil || secret == "" { log.Error("No JWT secret found in DB. Setting a temp one, but please report this error", err) + secret = uuid.NewString() } Secret = []byte(secret) TokenAuth = jwtauth.New("HS256", Secret, nil) diff --git a/go.mod b/go.mod index f8eca8d97..1a56daab8 100644 --- a/go.mod +++ b/go.mod @@ -3,12 +3,11 @@ module github.com/navidrome/navidrome go 1.21 require ( - code.cloudfoundry.org/go-diodes v0.0.0-20231113191959-85adc333ee36 github.com/DexterLB/mpvipc v0.0.0-20230829142118-145d6eabdc37 github.com/Masterminds/squirrel v1.5.4 github.com/ReneKroon/ttlcache/v2 v2.11.0 github.com/bradleyjkemp/cupaloy/v2 v2.8.0 - github.com/deluan/rest v0.0.0-20211101235434-380523c4bb47 + github.com/deluan/rest v0.0.0-20211102003136-6260bc399cbf github.com/deluan/sanitize v0.0.0-20230310221930-6e18967d9fc1 github.com/dhowden/tag v0.0.0-20230630033851-978a0926ee25 github.com/disintegration/imaging v1.6.2 @@ -21,16 +20,16 @@ require ( github.com/fatih/structs v1.1.0 github.com/go-chi/chi/v5 v5.0.10 github.com/go-chi/cors v1.2.1 - github.com/go-chi/httprate v0.7.4 - github.com/go-chi/jwtauth/v5 v5.2.0 - github.com/google/uuid v1.4.0 + github.com/go-chi/httprate v0.8.0 + github.com/go-chi/jwtauth/v5 v5.3.0 + github.com/google/uuid v1.5.0 github.com/google/wire v0.5.0 github.com/hashicorp/go-multierror v1.1.1 github.com/kr/pretty v0.3.1 github.com/lestrrat-go/jwx/v2 v2.0.18 github.com/matoous/go-nanoid/v2 v2.0.0 github.com/mattn/go-sqlite3 v1.14.18 - github.com/mattn/go-zglob v0.0.3 + github.com/mattn/go-zglob v0.0.4 github.com/microcosm-cc/bluemonday v1.0.26 github.com/mileusna/useragent v1.3.4 github.com/onsi/ginkgo/v2 v2.13.2 diff --git a/go.sum b/go.sum index a1f0a26f6..d396d3aa1 100644 --- a/go.sum +++ b/go.sum @@ -35,8 +35,6 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= -code.cloudfoundry.org/go-diodes v0.0.0-20231113191959-85adc333ee36 h1:rvJrEWqzJVIgzhVGzm3zRFWN46W+Rk6QkmfATsvv7MA= -code.cloudfoundry.org/go-diodes v0.0.0-20231113191959-85adc333ee36/go.mod h1:+ARlrrIHjT9DE+ge1VugVtIs/DAOW8Cim8dKtspdkbc= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= @@ -72,8 +70,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= -github.com/deluan/rest v0.0.0-20211101235434-380523c4bb47 h1:IhGAYGDi212gspq0XkYAI+DN5e9lfAIm8Qgu1wj9yN4= -github.com/deluan/rest v0.0.0-20211101235434-380523c4bb47/go.mod h1:tSgDythFsl0QgS/PFWfIZqcJKnkADWneY80jaVRlqK8= +github.com/deluan/rest v0.0.0-20211102003136-6260bc399cbf h1:tb246l2Zmpt/GpF9EcHCKTtwzrd0HGfEmoODFA/qnk4= +github.com/deluan/rest v0.0.0-20211102003136-6260bc399cbf/go.mod h1:tSgDythFsl0QgS/PFWfIZqcJKnkADWneY80jaVRlqK8= github.com/deluan/sanitize v0.0.0-20230310221930-6e18967d9fc1 h1:mGvOb3zxl4vCLv+dbf7JA6CAaM2UH/AGP1KX4DsJmTI= github.com/deluan/sanitize v0.0.0-20230310221930-6e18967d9fc1/go.mod h1:ZNCLJfehvEf34B7BbLKjgpsL9lyW7q938w/GY1XgV4E= github.com/dhowden/tag v0.0.0-20230630033851-978a0926ee25 h1:simG0vMYFvNriGhaaat7QVVkaVkXzvqcohaBoLZl9Hg= @@ -113,10 +111,10 @@ github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk= github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= -github.com/go-chi/httprate v0.7.4 h1:a2GIjv8he9LRf3712zxxnRdckQCm7I8y8yQhkJ84V6M= -github.com/go-chi/httprate v0.7.4/go.mod h1:6GOYBSwnpra4CQfAKXu8sQZg+nZ0M1g9QnyFvxrAB8A= -github.com/go-chi/jwtauth/v5 v5.2.0 h1:rw2wRNY6QHxyjYhoZYrQ4IeXVpPeun9nCZ9DBItDFPc= -github.com/go-chi/jwtauth/v5 v5.2.0/go.mod h1:2PoGm/KbnzRN9ILY6HFZAI6fTnb1gEZAKogAyqkd6fY= +github.com/go-chi/httprate v0.8.0 h1:CyKng28yhGnlGXH9EDGC/Qizj29afJQSNW15W/yj34o= +github.com/go-chi/httprate v0.8.0/go.mod h1:6GOYBSwnpra4CQfAKXu8sQZg+nZ0M1g9QnyFvxrAB8A= +github.com/go-chi/jwtauth/v5 v5.3.0 h1:X7RKGks1lrVeIe2omGyz47pNaNjG2YmwlRN5UKhN8qg= +github.com/go-chi/jwtauth/v5 v5.3.0/go.mod h1:2PoGm/KbnzRN9ILY6HFZAI6fTnb1gEZAKogAyqkd6fY= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -189,8 +187,8 @@ github.com/google/pprof v0.0.0-20230323073829-e72429f035bd/go.mod h1:79YE0hCXdHa github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= -github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= +github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/wire v0.5.0 h1:I7ELFeVBr3yfPIcc8+MWvrjk+3VjbcSzoXm3JVa+jD8= github.com/google/wire v0.5.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= @@ -270,8 +268,8 @@ github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-sqlite3 v1.14.18 h1:JL0eqdCOq6DJVNPSvArO/bIV9/P7fbGrV00LZHc+5aI= github.com/mattn/go-sqlite3 v1.14.18/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= -github.com/mattn/go-zglob v0.0.3 h1:6Ry4EYsScDyt5di4OI6xw1bYhOqfE5S33Z1OPy+d+To= -github.com/mattn/go-zglob v0.0.3/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo= +github.com/mattn/go-zglob v0.0.4 h1:LQi2iOm0/fGgu80AioIJ/1j9w9Oh+9DZ39J4VAGzHQM= +github.com/mattn/go-zglob v0.0.4/go.mod h1:MxxjyoXXnMxfIpxTK2GAkw1w8glPsQILx3N5wrKakiY= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mewkiz/flac v1.0.7 h1:uIXEjnuXqdRaZttmSFM5v5Ukp4U6orrZsnYGGR3yow8= diff --git a/persistence/album_repository.go b/persistence/album_repository.go index b4e450e05..c4aeb7678 100644 --- a/persistence/album_repository.go +++ b/persistence/album_repository.go @@ -50,13 +50,6 @@ func NewAlbumRepository(ctx context.Context, db dbx.Builder) model.AlbumReposito r.ctx = ctx r.db = db r.tableName = "album" - r.sortMappings = map[string]string{ - "name": "order_album_name asc, order_album_artist_name asc", - "artist": "compilation asc, order_album_artist_name asc, order_album_name asc", - "random": "RANDOM()", - "max_year": "coalesce(nullif(original_date,''), cast(max_year as text)), release_date, name, order_album_name asc", - "recently_added": recentlyAddedSort(), - } r.filterMappings = map[string]filterFunc{ "id": idFilter(r.tableName), "name": fullTextFilter, @@ -67,6 +60,24 @@ func NewAlbumRepository(ctx context.Context, db dbx.Builder) model.AlbumReposito "starred": booleanFilter, "has_rating": hasRatingFilter, } + if conf.Server.PreferSortTags { + r.sortMappings = map[string]string{ + "name": "COALESCE(NULLIF(sort_album_name,''),order_album_name)", + "artist": "compilation asc, COALESCE(NULLIF(sort_album_artist_name,''),order_album_artist_name) asc, COALESCE(NULLIF(sort_album_name,''),order_album_name) asc", + "albumArtist": "compilation asc, COALESCE(NULLIF(sort_album_artist_name,''),order_album_artist_name) asc, COALESCE(NULLIF(sort_album_name,''),order_album_name) asc", + "max_year": "coalesce(nullif(original_date,''), cast(max_year as text)), release_date, name, COALESCE(NULLIF(sort_album_name,''),order_album_name) asc", + "random": "RANDOM()", + "recently_added": recentlyAddedSort(), + } + } else { + r.sortMappings = map[string]string{ + "name": "order_album_name asc, order_album_artist_name asc", + "artist": "compilation asc, order_album_artist_name asc, order_album_name asc", + "max_year": "coalesce(nullif(original_date,''), cast(max_year as text)), release_date, name, order_album_name asc", + "random": "RANDOM()", + "recently_added": recentlyAddedSort(), + } + } return r } diff --git a/persistence/artist_repository.go b/persistence/artist_repository.go index ff75109ec..25288c3a1 100644 --- a/persistence/artist_repository.go +++ b/persistence/artist_repository.go @@ -60,14 +60,20 @@ func NewArtistRepository(ctx context.Context, db dbx.Builder) model.ArtistReposi r.db = db r.indexGroups = utils.ParseIndexGroups(conf.Server.IndexGroups) r.tableName = "artist" - r.sortMappings = map[string]string{ - "name": "order_artist_name", - } r.filterMappings = map[string]filterFunc{ "id": idFilter(r.tableName), "name": fullTextFilter, "starred": booleanFilter, } + if conf.Server.PreferSortTags { + r.sortMappings = map[string]string{ + "name": "COALESCE(NULLIF(sort_artist_name,''),order_artist_name)", + } + } else { + r.sortMappings = map[string]string{ + "name": "order_artist_name", + } + } return r } diff --git a/persistence/mediafile_repository.go b/persistence/mediafile_repository.go index 54a66ee48..3ff036569 100644 --- a/persistence/mediafile_repository.go +++ b/persistence/mediafile_repository.go @@ -10,6 +10,7 @@ import ( . "github.com/Masterminds/squirrel" "github.com/deluan/rest" + "github.com/navidrome/navidrome/conf" "github.com/navidrome/navidrome/log" "github.com/navidrome/navidrome/model" "github.com/pocketbase/dbx" @@ -25,16 +26,25 @@ func NewMediaFileRepository(ctx context.Context, db dbx.Builder) *mediaFileRepos r.ctx = ctx r.db = db r.tableName = "media_file" - r.sortMappings = map[string]string{ - "artist": "order_artist_name asc, order_album_name asc, release_date asc, disc_number asc, track_number asc", - "album": "order_album_name asc, release_date asc, disc_number asc, track_number asc, order_artist_name asc, title asc", - "random": "RANDOM()", - } r.filterMappings = map[string]filterFunc{ "id": idFilter(r.tableName), "title": fullTextFilter, "starred": booleanFilter, } + if conf.Server.PreferSortTags { + r.sortMappings = map[string]string{ + "title": "COALESCE(NULLIF(sort_title,''),title)", + "artist": "COALESCE(NULLIF(sort_artist_name,''),order_artist_name) asc, COALESCE(NULLIF(sort_album_name,''),order_album_name) asc, release_date asc, disc_number asc, track_number asc", + "album": "COALESCE(NULLIF(sort_album_name,''),order_album_name) asc, release_date asc, disc_number asc, track_number asc, COALESCE(NULLIF(sort_artist_name,''),order_artist_name) asc, COALESCE(NULLIF(sort_title,''),title) asc", + "random": "RANDOM()", + } + } else { + r.sortMappings = map[string]string{ + "artist": "order_artist_name asc, order_album_name asc, release_date asc, disc_number asc, track_number asc", + "album": "order_album_name asc, release_date asc, disc_number asc, track_number asc, order_artist_name asc, title asc", + "random": "RANDOM()", + } + } return r } diff --git a/persistence/sql_base_repository.go b/persistence/sql_base_repository.go index b2c7421c6..b3fd7001b 100644 --- a/persistence/sql_base_repository.go +++ b/persistence/sql_base_repository.go @@ -129,7 +129,7 @@ func (r sqlRepository) executeSQL(sq Sqlizer) (int64, error) { } start := time.Now() var c int64 - res, err := r.db.NewQuery(query).Bind(args).Execute() + res, err := r.db.NewQuery(query).Bind(args).WithContext(r.ctx).Execute() if res != nil { c, _ = res.RowsAffected() } @@ -165,7 +165,7 @@ func (r sqlRepository) queryOne(sq Sqlizer, response interface{}) error { return err } start := time.Now() - err = r.db.NewQuery(query).Bind(args).One(response) + err = r.db.NewQuery(query).Bind(args).WithContext(r.ctx).One(response) if errors.Is(err, sql.ErrNoRows) { r.logSQL(query, args, nil, 0, start) return model.ErrNotFound @@ -183,7 +183,7 @@ func (r sqlRepository) queryAll(sq SelectBuilder, response interface{}, options return err } start := time.Now() - err = r.db.NewQuery(query).Bind(args).All(response) + err = r.db.NewQuery(query).Bind(args).WithContext(r.ctx).All(response) if errors.Is(err, sql.ErrNoRows) { r.logSQL(query, args, nil, -1, start) return model.ErrNotFound @@ -199,7 +199,7 @@ func (r sqlRepository) queryAllSlice(sq SelectBuilder, response interface{}) err return err } start := time.Now() - err = r.db.NewQuery(query).Bind(args).Column(response) + err = r.db.NewQuery(query).Bind(args).WithContext(r.ctx).Column(response) if errors.Is(err, sql.ErrNoRows) { r.logSQL(query, args, nil, -1, start) return model.ErrNotFound diff --git a/scanner/rescanall.go b/scanner/rescanall.go new file mode 100644 index 000000000..a48247e38 --- /dev/null +++ b/scanner/rescanall.go @@ -0,0 +1,11 @@ +//go:build go1.21 + +package scanner + +import ( + "context" +) + +func contextWithoutCancel(ctx context.Context) context.Context { + return context.WithoutCancel(ctx) +} diff --git a/scanner/rescanall_go1.20.go b/scanner/rescanall_go1.20.go new file mode 100644 index 000000000..e4b6b2d1a --- /dev/null +++ b/scanner/rescanall_go1.20.go @@ -0,0 +1,12 @@ +//go:build !go1.21 + +package scanner + +import ( + "context" +) + +// TODO Remove this file when we drop support for go 1.20 +func contextWithoutCancel(ctx context.Context) context.Context { + return context.TODO() +} diff --git a/scanner/scanner.go b/scanner/scanner.go index 41a7925da..f3eb48542 100644 --- a/scanner/scanner.go +++ b/scanner/scanner.go @@ -139,27 +139,6 @@ func (s *scanner) startProgressTracker(mediaFolder string) (chan uint32, context return progress, cancel } -func (s *scanner) RescanAll(ctx context.Context, fullRescan bool) error { - if !isScanning.TryLock() { - log.Debug("Scanner already running, ignoring request for rescan.") - return ErrAlreadyScanning - } - defer isScanning.Unlock() - - var hasError bool - for folder := range s.folders { - err := s.rescan(ctx, folder, fullRescan) - hasError = hasError || err != nil - } - if hasError { - log.Error("Errors while scanning media. Please check the logs") - core.WriteAfterScanMetrics(ctx, s.ds, false) - return ErrScanError - } - core.WriteAfterScanMetrics(ctx, s.ds, true) - return nil -} - func (s *scanner) getStatus(folder string) (scanStatus, bool) { s.lock.RLock() defer s.lock.RUnlock() @@ -198,6 +177,27 @@ func (s *scanner) setStatusEnd(folder string, lastUpdate time.Time) { } } +func (s *scanner) RescanAll(ctx context.Context, fullRescan bool) error { + ctx = contextWithoutCancel(ctx) + if !isScanning.TryLock() { + log.Debug(ctx, "Scanner already running, ignoring request for rescan.") + return ErrAlreadyScanning + } + defer isScanning.Unlock() + + var hasError bool + for folder := range s.folders { + err := s.rescan(ctx, folder, fullRescan) + hasError = hasError || err != nil + } + if hasError { + log.Error(ctx, "Errors while scanning media. Please check the logs") + core.WriteAfterScanMetrics(ctx, s.ds, false) + return ErrScanError + } + core.WriteAfterScanMetrics(ctx, s.ds, true) + return nil +} func (s *scanner) Status(mediaFolder string) (*StatusInfo, error) { status, ok := s.getStatus(mediaFolder) if !ok { diff --git a/server/auth.go b/server/auth.go index 6442e7e40..44cc94fdd 100644 --- a/server/auth.go +++ b/server/auth.go @@ -193,7 +193,7 @@ func UsernameFromToken(r *http.Request) string { } func UsernameFromReverseProxyHeader(r *http.Request) string { - if conf.Server.ReverseProxyWhitelist == "" { + if conf.Server.ReverseProxyWhitelist == "" && !strings.HasPrefix(conf.Server.Address, "unix:") { return "" } if !validateIPAgainstList(r.RemoteAddr, conf.Server.ReverseProxyWhitelist) { @@ -316,6 +316,12 @@ func handleLoginFromHeaders(ds model.DataStore, r *http.Request) map[string]inte } func validateIPAgainstList(ip string, comaSeparatedList string) bool { + // Per https://github.com/golang/go/issues/49825, the remote address + // on a unix socket is '@' + if ip == "@" && strings.HasPrefix(conf.Server.Address, "unix:") { + return true + } + if comaSeparatedList == "" || ip == "" { return false } diff --git a/server/server.go b/server/server.go index 2ee7022bc..889ca6dbb 100644 --- a/server/server.go +++ b/server/server.go @@ -34,8 +34,8 @@ type Server struct { func New(ds model.DataStore, broker events.Broker) *Server { s := &Server{ds: ds, broker: broker} - auth.Init(s.ds) initialSetup(ds) + auth.Init(s.ds) s.initRoutes() s.mountAuthenticationRoutes() s.mountRootRedirector() diff --git a/utils/diodes/diodes.go b/utils/diodes/diodes.go deleted file mode 100644 index 64e2e436f..000000000 --- a/utils/diodes/diodes.go +++ /dev/null @@ -1,38 +0,0 @@ -package diodes - -import ( - "context" - - "code.cloudfoundry.org/go-diodes" -) - -type Diode[T any] struct { - d *diodes.Waiter -} - -type Alerter = diodes.Alerter - -type AlertFunc = diodes.AlertFunc - -func New[T any](ctx context.Context, size int, alerter Alerter) *Diode[T] { - return &Diode[T]{ - d: diodes.NewWaiter(diodes.NewOneToOne(size, alerter), diodes.WithWaiterContext(ctx)), - } -} - -func (d *Diode[T]) Put(data T) { - d.d.Set(diodes.GenericDataType(&data)) -} - -func (d *Diode[T]) TryNext() (*T, bool) { - data, ok := d.d.TryNext() - if !ok { - return nil, ok - } - return (*T)(data), true -} - -func (d *Diode[T]) Next() *T { - data := d.d.Next() - return (*T)(data) -} diff --git a/utils/diodes/diodes_test.go b/utils/diodes/diodes_test.go deleted file mode 100644 index fda9746e2..000000000 --- a/utils/diodes/diodes_test.go +++ /dev/null @@ -1,62 +0,0 @@ -package diodes_test - -import ( - "context" - "testing" - - "github.com/navidrome/navidrome/tests" - . "github.com/navidrome/navidrome/utils/diodes" - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" -) - -func TestDiodes(t *testing.T) { - tests.Init(t, false) - RegisterFailHandler(Fail) - RunSpecs(t, "Diodes Suite") -} - -var _ = Describe("Diode", func() { - type message struct { - data string - } - var diode *Diode[message] - var ctx context.Context - var ctxCancel context.CancelFunc - var missed int - - BeforeEach(func() { - missed = 0 - ctx, ctxCancel = context.WithCancel(context.Background()) - diode = New[message](ctx, 2, AlertFunc(func(m int) { missed = m })) - }) - - It("enqueues the data correctly", func() { - diode.Put(message{data: "1"}) - diode.Put(message{data: "2"}) - Expect(diode.Next()).To(Equal(&message{data: "1"})) - Expect(diode.Next()).To(Equal(&message{data: "2"})) - Expect(missed).To(BeZero()) - }) - - It("drops messages when Diode is full", func() { - diode.Put(message{data: "1"}) - diode.Put(message{data: "2"}) - diode.Put(message{data: "3"}) - next, ok := diode.TryNext() - Expect(ok).To(BeTrue()) - Expect(next).To(Equal(&message{data: "3"})) - - _, ok = diode.TryNext() - Expect(ok).To(BeFalse()) - - Expect(missed).To(Equal(2)) - }) - - It("returns nil when Diode is empty and the context is canceled", func() { - diode.Put(message{data: "1"}) - ctxCancel() - Expect(diode.Next()).To(Equal(&message{data: "1"})) - Expect(diode.Next()).To(BeNil()) - }) -})