Compare commits

...

12 Commits

Author SHA1 Message Date
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
55 changed files with 148 additions and 110 deletions

View File

@@ -19,7 +19,7 @@ builds:
flags:
- -tags=embed
ldflags:
- -X main.gitSha={{.ShortCommit}} -X main.gitTag={{.Tag}}
- -X github.com/deluan/navidrome/consts.gitSha={{.ShortCommit}} -X github.com/deluan/navidrome/consts.gitTag={{.Tag}}
- id: navidrome_linux
env:
@@ -32,7 +32,7 @@ builds:
- -tags=embed
ldflags:
- "-extldflags '-static'"
- -X main.gitSha={{.ShortCommit}} -X main.gitTag={{.Tag}}
- -X github.com/deluan/navidrome/consts.gitSha={{.ShortCommit}} -X github.com/deluan/navidrome/consts.gitTag={{.Tag}}
- id: navidrome_windows_i686
env:
@@ -47,7 +47,7 @@ builds:
- -tags=embed
ldflags:
- "-extldflags '-static'"
- -X main.gitSha={{.ShortCommit}} -X main.gitTag={{.Tag}}
- -X github.com/deluan/navidrome/consts.gitSha={{.ShortCommit}} -X github.com/deluan/navidrome/consts.gitTag={{.Tag}}
- id: navidrome_windows_x64
env:
@@ -62,7 +62,7 @@ builds:
- -tags=embed
ldflags:
- "-extldflags '-static'"
- -X main.gitSha={{.ShortCommit}} -X main.gitTag={{.Tag}}
- -X github.com/deluan/navidrome/consts.gitSha={{.ShortCommit}} -X github.com/deluan/navidrome/consts.gitTag={{.Tag}}
archives:
-
@@ -84,4 +84,5 @@ changelog:
exclude:
- '^docs:'
- '^test:'
- '^chore:'
- '^ci:'

View File

@@ -18,8 +18,7 @@ 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
ARG FFMPEG_URL=https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-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
@@ -28,30 +27,41 @@ WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
# Copy source and UI bundle, build executable
# Copy source, test it
COPY . .
RUN go test ./...
# Copy UI bundle, build executable
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 GIT_SHA=$(git rev-parse --short HEAD) && \
GIT_TAG=$(git describe --tags --abbrev=0 2> /dev/null) && \
RUN GIT_TAG=$(git name-rev --name-only HEAD) && \
GIT_TAG=${GIT_TAG#"tags/"} && \
GIT_SHA=$(git rev-parse --short HEAD) && \
echo "Building version: ${GIT_TAG} (${GIT_SHA})" && \
go-bindata -fs -prefix ui/build -tags embed -nocompress -pkg assets -o assets/embedded_gen.go ui/build/... && \
go build -ldflags="-X main.gitSha=${GIT_SHA} -X main.gitTag=${GIT_TAG}" -tags=embed
go build -ldflags="-X github.com/deluan/navidrome/consts.gitSha=${GIT_SHA} -X github.com/deluan/navidrome/consts.gitTag=${GIT_TAG}" -tags=embed
#####################################################
### Build Final Image
FROM alpine
FROM alpine as release
MAINTAINER Deluan Quintao <navidrome@deluan.com>
COPY --from=gobuilder /src/navidrome /app/
COPY --from=gobuilder /tmp/ffmpeg*/ffmpeg /usr/bin/
# Check if ffmpeg runs properly
RUN ffmpeg -buildconf
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
EXPOSE 4533
WORKDIR /app
CMD "/app/navidrome"
ENTRYPOINT "/app/navidrome"

View File

@@ -4,11 +4,11 @@ NODE_VERSION=v13.7.0
GIT_SHA=$(shell git rev-parse --short HEAD)
.PHONY: dev
dev: check_env data
dev: check_env
@goreman -f Procfile.dev -b 4533 start
.PHONY: server
server: check_go_env data
server: check_go_env
@reflex -d none -c reflex.conf
.PHONY: watch
@@ -26,12 +26,13 @@ testall: check_go_env test
.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 go-bindata || (echo "Installing BinData" && GO111MODULE=off go get -u github.com/go-bindata/go-bindata/...)
@which reflex || (echo "Installing Reflex" && GO111MODULE=off go get -u github.com/cespare/reflex)
@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/...)
@which goose || (echo "Installing Goose" && GO111MODULE=off go get -u github.com/pressly/goose/cmd/goose)
go mod download
@(cd ./ui && npm ci)
@@ -57,9 +58,6 @@ 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)
data:
mkdir data
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)
@@ -69,8 +67,22 @@ assets/embedded_gen.go: ui/build
.PHONY: build
build: check_go_env
go build -ldflags="-X main.gitSha=$(GIT_SHA) -X main.gitTag=master"
go build -ldflags="-X github.com/deluan/navidrome/consts.gitSha=$(GIT_SHA) -X github.com/deluan/navidrome/consts.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
buildall: check_env assets/embedded_gen.go
go build -ldflags="-X github.com/deluan/navidrome/consts.gitSha=$(GIT_SHA) -X github.com/deluan/navidrome/consts.gitTag=master" -tags=embed
.PHONY: release
release:
@if [[ ! "${V}" =~ ^[0-9]+\.[0-9]+\.[0-9]+.*$$ ]]; then echo "Usage: make release V=X.X.X"; exit 1; fi
go mod tidy
make test
@if [ -n "`git status -s`" ]; then echo "\n\nThere are pending changes. Please commit or stash first"; exit 1; fi
git tag v${V}
git push origin v${V}
git push origin master
.PHONY: dist
dist:
docker run -it -v $(PWD):/github/workspace -w /github/workspace bepsays/ci-goreleaser:1.13-4 goreleaser release --rm-dist --skip-publish --snapshot

View File

@@ -80,16 +80,19 @@ services:
- "/Users/deluan/Music/iTunes/iTunes Media/Music:/music"
```
### Build it yourself
### Build from source
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
You will need to install [Go 1.13](https://golang.org/dl/) and [Node 13.7.0](http://nodejs.org).
You'll also need [ffmpeg](https://ffmpeg.org) installed in your system. The setup is very strict, and
the steps bellow only work with these specific versions (enforced in the Makefile)
After the prerequisites above are installed, build the application with:
After the prerequisites above are installed, clone this repository and build the application with:
```
$ make setup
$ make buildall
```shell script
$ git clone https://github.com/deluan/navidrome
$ cd navidrome
$ make setup # Install tools required for Navidrome's development
$ make buildall # Build UI and server, generates a single executable
```
This will generate the `navidrome` binary executable in the project's root folder.

View File

@@ -4,33 +4,17 @@ import (
"fmt"
"strings"
"github.com/deluan/navidrome/consts"
"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()
version := "Version: " + consts.Version()
padding := strings.Repeat(" ", 52-len(version))
fmt.Printf("%s%s%s\n\n", getBanner(), padding, version)
}

View File

@@ -52,8 +52,6 @@ func newWithPath(path string, skipFlags ...bool) *multiconfig.DefaultLoader {
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{}

View File

@@ -3,6 +3,8 @@ package consts
import "time"
const (
AppName = "navidrome"
LocalConfigFile = "./navidrome.toml"
InitialSetupFlagKey = "InitialSetup"
@@ -10,7 +12,5 @@ const (
JWTIssuer = "ND"
JWTTokenExpiration = 30 * time.Minute
InitialUserName = "admin"
UIAssetsLocalPath = "ui/build"
)

20
consts/version.go Normal file
View File

@@ -0,0 +1,20 @@
package consts
import "fmt"
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 Version() string {
if gitSha == "" {
return "dev"
}
return fmt.Sprintf("%s (%s)", gitTag, gitSha)
}

View File

@@ -3,7 +3,6 @@ package engine
import (
"context"
"github.com/deluan/navidrome/consts"
"github.com/deluan/navidrome/model"
"github.com/deluan/navidrome/utils"
)
@@ -52,12 +51,11 @@ func (p *playlists) Create(ctx context.Context, playlistId, name string, ids []s
}
func (p *playlists) getUser(ctx context.Context) string {
owner := consts.InitialUserName
user, ok := ctx.Value("user").(*model.User)
if ok {
owner = user.UserName
return user.UserName
}
return owner
return ""
}
func (p *playlists) Delete(ctx context.Context, playlistId string) error {

View File

@@ -7,7 +7,6 @@ import (
"fmt"
"strings"
"github.com/deluan/navidrome/log"
"github.com/deluan/navidrome/model"
)
@@ -49,11 +48,12 @@ func (u *users) Authenticate(ctx context.Context, username, pass, token, salt st
if !valid {
return nil, model.ErrInvalidAuth
}
go func() {
err := u.ds.User(ctx).UpdateLastAccessAt(user.ID)
if err != nil {
log.Error(ctx, "Could not update user's lastAccessAt", "user", user.UserName)
}
}()
// TODO: Find a way to update LastAccessAt without causing too much retention in the DB
//go func() {
// err := u.ds.User(ctx).UpdateLastAccessAt(user.ID)
// if err != nil {
// log.Error(ctx, "Could not update user's lastAccessAt", "user", user.UserName)
// }
//}()
return user, nil
}

2
go.mod
View File

@@ -21,7 +21,7 @@ require (
github.com/koding/multiconfig v0.0.0-20170327155832-26b6dfd3a84a
github.com/kr/pretty v0.1.0 // indirect
github.com/lib/pq v1.3.0
github.com/mattn/go-sqlite3 v2.0.2+incompatible
github.com/mattn/go-sqlite3 v2.0.3+incompatible
github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5
github.com/onsi/ginkgo v1.11.0
github.com/onsi/gomega v1.8.1

4
go.sum
View File

@@ -85,8 +85,8 @@ github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU=
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v2.0.2+incompatible h1:qzw9c2GNT8UFrgWNDhCTqRqYUSmu/Dav/9Z58LGpk7U=
github.com/mattn/go-sqlite3 v2.0.2+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5 h1:BvoENQQU+fZ9uukda/RzCAL/191HHwJA5b13R6diVlY=
github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=

View File

@@ -83,6 +83,7 @@ func probe(inputs []string) (map[string]*Metadata, error) {
infos := parseOutput(string(output))
for file, info := range infos {
md, err := extractMetadata(file, info)
// Skip files with errors
if err == nil {
mds[file] = md
}
@@ -117,9 +118,16 @@ func parseOutput(output string) map[string]string {
func extractMetadata(filePath, info string) (*Metadata, error) {
m := &Metadata{filePath: filePath, tags: map[string]string{}}
m.suffix = strings.ToLower(strings.TrimPrefix(path.Ext(filePath), "."))
var err error
m.fileInfo, err = os.Stat(filePath)
if err != nil {
log.Warn("Error stating file. Skipping", "filePath", filePath, err)
return nil, errors.New("error stating file")
}
m.parseInfo(info)
m.fileInfo, _ = os.Stat(filePath)
if len(m.tags) == 0 {
log.Trace("Not a media file. Skipping", "filePath", filePath)
return nil, errors.New("not a media file")
}
return m, nil

View File

@@ -109,7 +109,7 @@ func CreateAdmin(ds model.DataStore) func(w http.ResponseWriter, r *http.Request
func createDefaultUser(ctx context.Context, ds model.DataStore, username, password string) error {
id, _ := uuid.NewRandom()
log.Warn("Creating initial user", "user", consts.InitialUserName)
log.Warn("Creating initial user", "user", username)
initialUser := model.User{
ID: id.String(),
UserName: username,

View File

@@ -151,11 +151,12 @@ func HGone(r chi.Router, path string) {
}
func SendError(w http.ResponseWriter, r *http.Request, err error) {
response := &responses.Subsonic{Version: Version, Status: "fail"}
response := NewResponse()
code := responses.ErrorGeneric
if e, ok := err.(SubsonicError); ok {
code = e.code
}
response.Status = "fail"
response.Error = &responses.Error{Code: code, Message: err.Error()}
SendResponse(w, r, response)

View File

@@ -7,6 +7,7 @@ import (
"strings"
"time"
"github.com/deluan/navidrome/consts"
"github.com/deluan/navidrome/engine"
"github.com/deluan/navidrome/model"
"github.com/deluan/navidrome/server/subsonic/responses"
@@ -14,7 +15,7 @@ import (
)
func NewResponse() *responses.Subsonic {
return &responses.Subsonic{Status: "ok", Version: Version}
return &responses.Subsonic{Status: "ok", Version: Version, Type: consts.AppName, ServerVersion: consts.Version()}
}
func RequiredParamString(r *http.Request, param string, msg string) (string, error) {

View File

@@ -1 +1 @@
{"status":"ok","version":"1.8.0","albumList":{"album":[{"id":"1","isDir":false,"title":"title"}]}}
{"status":"ok","version":"1.8.0","type":"navidrome","serverVersion":"v0.0.0","albumList":{"album":[{"id":"1","isDir":false,"title":"title"}]}}

View File

@@ -1 +1 @@
<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.8.0"><albumList><album id="1" isDir="false" title="title"></album></albumList></subsonic-response>
<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.8.0" type="navidrome" serverVersion="v0.0.0"><albumList><album id="1" isDir="false" title="title"></album></albumList></subsonic-response>

View File

@@ -1 +1 @@
{"status":"ok","version":"1.8.0","albumList":{}}
{"status":"ok","version":"1.8.0","type":"navidrome","serverVersion":"v0.0.0","albumList":{}}

View File

@@ -1 +1 @@
<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.8.0"><albumList></albumList></subsonic-response>
<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.8.0" type="navidrome" serverVersion="v0.0.0"><albumList></albumList></subsonic-response>

View File

@@ -1 +1 @@
{"status":"ok","version":"1.8.0","directory":{"child":[{"id":"1","isDir":true,"title":"title","album":"album","artist":"artist","track":1,"year":1985,"genre":"Rock","coverArt":"1","size":"8421341","contentType":"audio/flac","suffix":"flac","starred":"2016-03-02T20:30:00Z","transcodedContentType":"audio/mpeg","transcodedSuffix":"mp3","duration":146,"bitRate":320}],"id":"1","name":"N"}}
{"status":"ok","version":"1.8.0","type":"navidrome","serverVersion":"v0.0.0","directory":{"child":[{"id":"1","isDir":true,"title":"title","album":"album","artist":"artist","track":1,"year":1985,"genre":"Rock","coverArt":"1","size":"8421341","contentType":"audio/flac","suffix":"flac","starred":"2016-03-02T20:30:00Z","transcodedContentType":"audio/mpeg","transcodedSuffix":"mp3","duration":146,"bitRate":320}],"id":"1","name":"N"}}

View File

@@ -1 +1 @@
<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.8.0"><directory id="1" name="N"><child id="1" isDir="true" title="title" album="album" artist="artist" track="1" year="1985" genre="Rock" coverArt="1" size="8421341" contentType="audio/flac" suffix="flac" starred="2016-03-02T20:30:00Z" transcodedContentType="audio/mpeg" transcodedSuffix="mp3" duration="146" bitRate="320"></child></directory></subsonic-response>
<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.8.0" type="navidrome" serverVersion="v0.0.0"><directory id="1" name="N"><child id="1" isDir="true" title="title" album="album" artist="artist" track="1" year="1985" genre="Rock" coverArt="1" size="8421341" contentType="audio/flac" suffix="flac" starred="2016-03-02T20:30:00Z" transcodedContentType="audio/mpeg" transcodedSuffix="mp3" duration="146" bitRate="320"></child></directory></subsonic-response>

View File

@@ -1 +1 @@
{"status":"ok","version":"1.8.0","directory":{"child":[{"id":"1","isDir":false,"title":"title"}],"id":"1","name":"N"}}
{"status":"ok","version":"1.8.0","type":"navidrome","serverVersion":"v0.0.0","directory":{"child":[{"id":"1","isDir":false,"title":"title"}],"id":"1","name":"N"}}

View File

@@ -1 +1 @@
<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.8.0"><directory id="1" name="N"><child id="1" isDir="false" title="title"></child></directory></subsonic-response>
<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.8.0" type="navidrome" serverVersion="v0.0.0"><directory id="1" name="N"><child id="1" isDir="false" title="title"></child></directory></subsonic-response>

View File

@@ -1 +1 @@
{"status":"ok","version":"1.8.0","directory":{"id":"1","name":"N"}}
{"status":"ok","version":"1.8.0","type":"navidrome","serverVersion":"v0.0.0","directory":{"id":"1","name":"N"}}

View File

@@ -1 +1 @@
<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.8.0"><directory id="1" name="N"></directory></subsonic-response>
<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.8.0" type="navidrome" serverVersion="v0.0.0"><directory id="1" name="N"></directory></subsonic-response>

View File

@@ -1 +1 @@
{"status":"ok","version":"1.8.0"}
{"status":"ok","version":"1.8.0","type":"navidrome","serverVersion":"v0.0.0"}

View File

@@ -1 +1 @@
<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.8.0"></subsonic-response>
<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.8.0" type="navidrome" serverVersion="v0.0.0"></subsonic-response>

View File

@@ -1 +1 @@
{"status":"ok","version":"1.8.0","genres":{"genre":[{"value":"Rock","songCount":1000,"albumCount":100},{"value":"Reggae","songCount":500,"albumCount":50},{"value":"Pop","songCount":0,"albumCount":0}]}}
{"status":"ok","version":"1.8.0","type":"navidrome","serverVersion":"v0.0.0","genres":{"genre":[{"value":"Rock","songCount":1000,"albumCount":100},{"value":"Reggae","songCount":500,"albumCount":50},{"value":"Pop","songCount":0,"albumCount":0}]}}

View File

@@ -1 +1 @@
<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.8.0"><genres><genre songCount="1000" albumCount="100">Rock</genre><genre songCount="500" albumCount="50">Reggae</genre><genre songCount="0" albumCount="0">Pop</genre></genres></subsonic-response>
<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.8.0" type="navidrome" serverVersion="v0.0.0"><genres><genre songCount="1000" albumCount="100">Rock</genre><genre songCount="500" albumCount="50">Reggae</genre><genre songCount="0" albumCount="0">Pop</genre></genres></subsonic-response>

View File

@@ -1 +1 @@
{"status":"ok","version":"1.8.0","genres":{}}
{"status":"ok","version":"1.8.0","type":"navidrome","serverVersion":"v0.0.0","genres":{}}

View File

@@ -1 +1 @@
<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.8.0"><genres></genres></subsonic-response>
<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.8.0" type="navidrome" serverVersion="v0.0.0"><genres></genres></subsonic-response>

View File

@@ -1 +1 @@
{"status":"ok","version":"1.8.0","indexes":{"index":[{"name":"A","artist":[{"id":"111","name":"aaa","starred":"2016-03-02T20:30:00Z"}]}],"lastModified":"1","ignoredArticles":"A"}}
{"status":"ok","version":"1.8.0","type":"navidrome","serverVersion":"v0.0.0","indexes":{"index":[{"name":"A","artist":[{"id":"111","name":"aaa","starred":"2016-03-02T20:30:00Z"}]}],"lastModified":"1","ignoredArticles":"A"}}

View File

@@ -1 +1 @@
<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.8.0"><indexes lastModified="1" ignoredArticles="A"><index name="A"><artist id="111" name="aaa" starred="2016-03-02T20:30:00Z"></artist></index></indexes></subsonic-response>
<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.8.0" type="navidrome" serverVersion="v0.0.0"><indexes lastModified="1" ignoredArticles="A"><index name="A"><artist id="111" name="aaa" starred="2016-03-02T20:30:00Z"></artist></index></indexes></subsonic-response>

View File

@@ -1 +1 @@
{"status":"ok","version":"1.8.0","indexes":{"lastModified":"1","ignoredArticles":"A"}}
{"status":"ok","version":"1.8.0","type":"navidrome","serverVersion":"v0.0.0","indexes":{"lastModified":"1","ignoredArticles":"A"}}

View File

@@ -1 +1 @@
<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.8.0"><indexes lastModified="1" ignoredArticles="A"></indexes></subsonic-response>
<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.8.0" type="navidrome" serverVersion="v0.0.0"><indexes lastModified="1" ignoredArticles="A"></indexes></subsonic-response>

View File

@@ -1 +1 @@
{"status":"ok","version":"1.8.0","license":{"valid":true}}
{"status":"ok","version":"1.8.0","type":"navidrome","serverVersion":"v0.0.0","license":{"valid":true}}

View File

@@ -1 +1 @@
<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.8.0"><license valid="true"></license></subsonic-response>
<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.8.0" type="navidrome" serverVersion="v0.0.0"><license valid="true"></license></subsonic-response>

View File

@@ -1 +1 @@
{"status":"ok","version":"1.8.0","musicFolders":{"musicFolder":[{"id":"111","name":"aaa"},{"id":"222","name":"bbb"}]}}
{"status":"ok","version":"1.8.0","type":"navidrome","serverVersion":"v0.0.0","musicFolders":{"musicFolder":[{"id":"111","name":"aaa"},{"id":"222","name":"bbb"}]}}

View File

@@ -1 +1 @@
<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.8.0"><musicFolders><musicFolder id="111" name="aaa"></musicFolder><musicFolder id="222" name="bbb"></musicFolder></musicFolders></subsonic-response>
<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.8.0" type="navidrome" serverVersion="v0.0.0"><musicFolders><musicFolder id="111" name="aaa"></musicFolder><musicFolder id="222" name="bbb"></musicFolder></musicFolders></subsonic-response>

View File

@@ -1 +1 @@
{"status":"ok","version":"1.8.0","musicFolders":{}}
{"status":"ok","version":"1.8.0","type":"navidrome","serverVersion":"v0.0.0","musicFolders":{}}

View File

@@ -1 +1 @@
<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.8.0"><musicFolders></musicFolders></subsonic-response>
<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.8.0" type="navidrome" serverVersion="v0.0.0"><musicFolders></musicFolders></subsonic-response>

View File

@@ -1 +1 @@
{"status":"ok","version":"1.8.0","playlists":{"playlist":[{"id":"111","name":"aaa"},{"id":"222","name":"bbb"}]}}
{"status":"ok","version":"1.8.0","type":"navidrome","serverVersion":"v0.0.0","playlists":{"playlist":[{"id":"111","name":"aaa"},{"id":"222","name":"bbb"}]}}

View File

@@ -1 +1 @@
<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.8.0"><playlists><playlist id="111" name="aaa"></playlist><playlist id="222" name="bbb"></playlist></playlists></subsonic-response>
<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.8.0" type="navidrome" serverVersion="v0.0.0"><playlists><playlist id="111" name="aaa"></playlist><playlist id="222" name="bbb"></playlist></playlists></subsonic-response>

View File

@@ -1 +1 @@
{"status":"ok","version":"1.8.0","playlists":{}}
{"status":"ok","version":"1.8.0","type":"navidrome","serverVersion":"v0.0.0","playlists":{}}

View File

@@ -1 +1 @@
<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.8.0"><playlists></playlists></subsonic-response>
<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.8.0" type="navidrome" serverVersion="v0.0.0"><playlists></playlists></subsonic-response>

View File

@@ -1 +1 @@
{"status":"ok","version":"1.8.0","user":{"username":"deluan","email":"navidrome@deluan.com","scrobblingEnabled":false,"adminRole":false,"settingsRole":false,"downloadRole":false,"uploadRole":false,"playlistRole":false,"coverArtRole":false,"commentRole":false,"podcastRole":false,"streamRole":false,"jukeboxRole":false,"shareRole":false,"videoConversionRole":false,"folder":[1]}}
{"status":"ok","version":"1.8.0","type":"navidrome","serverVersion":"v0.0.0","user":{"username":"deluan","email":"navidrome@deluan.com","scrobblingEnabled":false,"adminRole":false,"settingsRole":false,"downloadRole":false,"uploadRole":false,"playlistRole":false,"coverArtRole":false,"commentRole":false,"podcastRole":false,"streamRole":false,"jukeboxRole":false,"shareRole":false,"videoConversionRole":false,"folder":[1]}}

View File

@@ -1 +1 @@
<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.8.0"><user username="deluan" email="navidrome@deluan.com" scrobblingEnabled="false" adminRole="false" settingsRole="false" downloadRole="false" uploadRole="false" playlistRole="false" coverArtRole="false" commentRole="false" podcastRole="false" streamRole="false" jukeboxRole="false" shareRole="false" videoConversionRole="false"><folder>1</folder></user></subsonic-response>
<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.8.0" type="navidrome" serverVersion="v0.0.0"><user username="deluan" email="navidrome@deluan.com" scrobblingEnabled="false" adminRole="false" settingsRole="false" downloadRole="false" uploadRole="false" playlistRole="false" coverArtRole="false" commentRole="false" podcastRole="false" streamRole="false" jukeboxRole="false" shareRole="false" videoConversionRole="false"><folder>1</folder></user></subsonic-response>

View File

@@ -1 +1 @@
{"status":"ok","version":"1.8.0","user":{"username":"deluan","scrobblingEnabled":false,"adminRole":false,"settingsRole":false,"downloadRole":false,"uploadRole":false,"playlistRole":false,"coverArtRole":false,"commentRole":false,"podcastRole":false,"streamRole":false,"jukeboxRole":false,"shareRole":false,"videoConversionRole":false}}
{"status":"ok","version":"1.8.0","type":"navidrome","serverVersion":"v0.0.0","user":{"username":"deluan","scrobblingEnabled":false,"adminRole":false,"settingsRole":false,"downloadRole":false,"uploadRole":false,"playlistRole":false,"coverArtRole":false,"commentRole":false,"podcastRole":false,"streamRole":false,"jukeboxRole":false,"shareRole":false,"videoConversionRole":false}}

View File

@@ -1 +1 @@
<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.8.0"><user username="deluan" scrobblingEnabled="false" adminRole="false" settingsRole="false" downloadRole="false" uploadRole="false" playlistRole="false" coverArtRole="false" commentRole="false" podcastRole="false" streamRole="false" jukeboxRole="false" shareRole="false" videoConversionRole="false"></user></subsonic-response>
<subsonic-response xmlns="http://subsonic.org/restapi" status="ok" version="1.8.0" type="navidrome" serverVersion="v0.0.0"><user username="deluan" scrobblingEnabled="false" adminRole="false" settingsRole="false" downloadRole="false" uploadRole="false" playlistRole="false" coverArtRole="false" commentRole="false" podcastRole="false" streamRole="false" jukeboxRole="false" shareRole="false" videoConversionRole="false"></user></subsonic-response>

View File

@@ -9,6 +9,8 @@ type Subsonic struct {
XMLName xml.Name `xml:"http://subsonic.org/restapi subsonic-response" json:"-"`
Status string `xml:"status,attr" json:"status"`
Version string `xml:"version,attr" json:"version"`
Type string `xml:"type,attr" json:"type"`
ServerVersion string `xml:"serverVersion,attr" json:"serverVersion"`
Error *Error `xml:"error,omitempty" json:"error,omitempty"`
License *License `xml:"license,omitempty" json:"license,omitempty"`
MusicFolders *MusicFolders `xml:"musicFolders,omitempty" json:"musicFolders,omitempty"`

View File

@@ -10,6 +10,7 @@ import (
"encoding/xml"
"time"
"github.com/deluan/navidrome/consts"
. "github.com/deluan/navidrome/server/subsonic/responses"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
@@ -18,7 +19,7 @@ import (
var _ = Describe("Responses", func() {
var response *Subsonic
BeforeEach(func() {
response = &Subsonic{Status: "ok", Version: "1.8.0"}
response = &Subsonic{Status: "ok", Version: "1.8.0", Type: consts.AppName, ServerVersion: "v0.0.0"}
})
Describe("EmptyResponse", func() {

View File

@@ -7,7 +7,7 @@
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
content="Navidrome Music Server"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--

View File

@@ -23,7 +23,7 @@ const UserEdit = (props) => (
<PasswordInput source="password" validate={[required()]} />
<BooleanInput source="isAdmin" initialValue={false} />
<DateField source="lastLoginAt" showTime />
<DateField source="lastAccessAt" showTime />
{/*<DateField source="lastAccessAt" showTime />*/}
<DateField source="updatedAt" showTime />
<DateField source="createdAt" showTime />
</SimpleForm>

View File

@@ -33,8 +33,7 @@ const UserList = (props) => {
<SimpleList
primaryText={(record) => record.name}
secondaryText={(record) =>
record.lastAccessAt &&
new Date(record.lastAccessAt).toLocaleString()
record.lastLoginAt && new Date(record.lastLoginAt).toLocaleString()
}
tertiaryText={(record) => (record.isAdmin ? '[admin]' : '')}
/>
@@ -42,7 +41,7 @@ const UserList = (props) => {
<Datagrid rowClick="edit">
<TextField source="userName" />
<BooleanField source="isAdmin" />
<DateField source="lastAccessAt" locales="pt-BR" />
<DateField source="lastLoginAt" locales="pt-BR" />
<DateField source="updatedAt" locales="pt-BR" />
</Datagrid>
)}