Merge branch 'navidrome:master' into feature/externalize_mpv_template

This commit is contained in:
Jonathan 2024-04-15 22:06:56 +02:00 committed by GitHub
commit 1f963699e8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 144 additions and 82 deletions

View File

@ -111,6 +111,12 @@ single: warning-noui-build ##@Cross_Compilation Build binaries for a single supp
goreleaser build --clean --snapshot -p 2 --single-target --id navidrome_${GOOS}_${GOARCH}
.PHONY: single
docker: buildjs ##@Build Build Docker linux/amd64 image (tagged as `deluan/navidrome:develop`)
GOOS=linux GOARCH=amd64 make single
@echo "Building Docker image"
docker build . --platform linux/amd64 -t deluan/navidrome:develop -f .github/workflows/pipeline.dockerfile
.PHONY: docker
warning-noui-build:
@echo "WARNING: This command does not build the frontend, it uses the latest built with 'make buildjs'"
.PHONY: warning-noui-build

View File

@ -131,7 +131,7 @@ func (a *cacheWarmer) doCacheImage(ctx context.Context, id model.ArtworkID) erro
r, _, err := a.artwork.Get(ctx, id, consts.UICoverArtSize)
if err != nil {
return fmt.Errorf("error cacheing id='%s': %w", id, err)
return fmt.Errorf("error caching id='%s': %w", id, err)
}
defer r.Close()
_, err = io.Copy(io.Discard, r)

View File

@ -0,0 +1,9 @@
//go:build !windows
package mpv
import "github.com/navidrome/navidrome/utils"
func socketName(prefix, suffix string) string {
return utils.TempFileName(prefix, suffix)
}

View File

@ -0,0 +1,15 @@
//go:build windows
package mpv
import (
"path/filepath"
"github.com/google/uuid"
)
func socketName(prefix, suffix string) string {
// Windows needs to use a named pipe for the socket
// see https://mpv.io/manual/master#using-mpv-from-other-programs-or-scripts
return filepath.Join(`\\.\pipe\mpvsocket`, prefix+uuid.NewString()+suffix)
}

View File

@ -13,7 +13,6 @@ import (
"github.com/dexterlb/mpvipc"
"github.com/navidrome/navidrome/log"
"github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/utils"
)
type MpvTrack struct {
@ -32,7 +31,7 @@ func NewTrack(playbackDoneChannel chan bool, deviceName string, mf model.MediaFi
return nil, err
}
tmpSocketName := utils.TempFileName("mpv-ctrl-", ".socket")
tmpSocketName := socketName("mpv-ctrl-", ".socket")
args := createMPVCommand(deviceName, mf.Path, tmpSocketName)
exe, err := start(args)
@ -103,35 +102,6 @@ func (t *MpvTrack) Pause() {
}
}
func (t *MpvTrack) Close() {
log.Debug("Closing resources", "track", t)
t.CloseCalled = true
// trying to shutdown mpv process using socket
if t.isSocketFilePresent() {
log.Debug("sending shutdown command")
_, err := t.Conn.Call("quit")
if err != nil {
log.Error("Error sending quit command to mpv-ipc socket", err)
if t.Exe != nil {
log.Debug("cancelling executor")
err = t.Exe.Cancel()
if err != nil {
log.Error("Error canceling executor", err)
}
}
}
}
if t.isSocketFilePresent() {
log.Debug("Removing socketfile", "socketfile", t.IPCSocketName)
err := os.Remove(t.IPCSocketName)
if err != nil {
log.Error("Error cleaning up socketfile", "socketfile", t.IPCSocketName, err)
}
}
}
func (t *MpvTrack) isSocketFilePresent() bool {
if len(t.IPCSocketName) < 1 {
return false

View File

@ -0,0 +1,38 @@
//go:build !windows
package mpv
import (
"os"
"github.com/navidrome/navidrome/log"
)
func (t *MpvTrack) Close() {
log.Debug("Closing resources", "track", t)
t.CloseCalled = true
// trying to shutdown mpv process using socket
if t.isSocketFilePresent() {
log.Debug("sending shutdown command")
_, err := t.Conn.Call("quit")
if err != nil {
log.Error("Error sending quit command to mpv-ipc socket", err)
if t.Exe != nil {
log.Debug("cancelling executor")
err = t.Exe.Cancel()
if err != nil {
log.Error("Error canceling executor", err)
}
}
}
}
if t.isSocketFilePresent() {
log.Debug("Removing socketfile", "socketfile", t.IPCSocketName)
err := os.Remove(t.IPCSocketName)
if err != nil {
log.Error("Error cleaning up socketfile", "socketfile", t.IPCSocketName, err)
}
}
}

View File

@ -0,0 +1,8 @@
//go:build windows
package mpv
func (t *MpvTrack) Close() {
// Windows automatically handles closing
// and cleaning up named pipe
}

View File

@ -68,7 +68,7 @@ func (ps *playbackServer) initDeviceStatus(devices []conf.AudioDeviceDefinition,
defaultDeviceFound := false
if defaultDevice == "" {
// if there are no devices given and no default device, we create a sythetic device named "auto"
// if there are no devices given and no default device, we create a synthetic device named "auto"
if len(devices) == 0 {
pbDevices[0] = *NewPlaybackDevice(ps, "auto", "auto")
}

2
go.mod
View File

@ -33,7 +33,7 @@ require (
github.com/mileusna/useragent v1.3.4
github.com/onsi/ginkgo/v2 v2.17.1
github.com/onsi/gomega v1.32.0
github.com/pelletier/go-toml/v2 v2.2.0
github.com/pelletier/go-toml/v2 v2.2.1
github.com/pocketbase/dbx v1.10.1
github.com/pressly/goose/v3 v3.19.2
github.com/prometheus/client_golang v1.19.0

4
go.sum
View File

@ -328,8 +328,8 @@ github.com/ory/dockertest/v3 v3.10.0 h1:4K3z2VMe8Woe++invjaTB7VRyQXQy5UY+loujO4a
github.com/ory/dockertest/v3 v3.10.0/go.mod h1:nr57ZbRWMqfsdGdFNLHz5jjNdDb7VVFnzAeW1n5N1Lg=
github.com/paulmach/orb v0.10.0 h1:guVYVqzxHE/CQ1KpfGO077TR0ATHSNjp4s6XGLn3W9s=
github.com/paulmach/orb v0.10.0/go.mod h1:5mULz1xQfs3bmQm63QEJA6lNGujuRafwA5S/EnuLaLU=
github.com/pelletier/go-toml/v2 v2.2.0 h1:QLgLl2yMN7N+ruc31VynXs1vhMZa7CeHHejIeBAsoHo=
github.com/pelletier/go-toml/v2 v2.2.0/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/pelletier/go-toml/v2 v2.2.1 h1:9TA9+T8+8CUCO2+WYnDLCgrYi9+omqKXyjDtosvtEhg=
github.com/pelletier/go-toml/v2 v2.2.1/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/pierrec/lz4/v4 v4.1.18 h1:xaKrnTkyoqfh1YItXl56+6KJNVYWlEEPuAQW9xsplYQ=
github.com/pierrec/lz4/v4 v4.1.18/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=

View File

@ -148,9 +148,9 @@
"name": "Nom",
"duration": "Durée",
"ownerName": "Propriétaire",
"public": "Public",
"public": "Publique",
"updatedAt": "Mise à jour le",
"createdAt": "Créé le",
"createdAt": "Créée le",
"songCount": "Titres",
"comment": "Commentaire",
"sync": "Import automatique",
@ -174,7 +174,7 @@
"name": "Nom",
"streamUrl": "Lien du stream",
"homePageUrl": "Lien de la page d'accueil",
"updatedAt": "Mis à jour le",
"updatedAt": "Mise à jour le",
"createdAt": "Créée le"
},
"actions": {
@ -195,7 +195,7 @@
"maxBitRate": "Bitrate maximum",
"updatedAt": "Mis à jour le",
"createdAt": "Créé le",
"downloadable": "Autoriser les téléchargements?"
"downloadable": "Autoriser les téléchargements ?"
}
}
},
@ -274,7 +274,7 @@
"not_found": "Page manquante",
"show": "%{name} #%{id}",
"empty": "Pas encore de %{name}.",
"invite": "Voulez-vous en créer un ?"
"invite": "Voulez-vous en créer ?"
},
"input": {
"file": {
@ -362,18 +362,18 @@
"musicbrainz": "Ouvrir dans MusicBrainz"
},
"lastfmLink": "Lire plus...",
"listenBrainzLinkSuccess": "La liaison et le scrobble avec ListenBrainz sont maintenant activés pour l'utilisateur: %{user}",
"listenBrainzLinkFailure": "Échec lors de la liaison avec ListenBrainz: %{error}",
"listenBrainzLinkSuccess": "La liaison et le scrobble avec ListenBrainz sont maintenant activés pour l'utilisateur : %{user}",
"listenBrainzLinkFailure": "Échec lors de la liaison avec ListenBrainz : %{error}",
"listenBrainzUnlinkSuccess": "La liaison et le scrobble avec ListenBrainz sont maintenant désactivés",
"listenBrainzUnlinkFailure": "Échec lors de la désactivation de la liaison avec ListenBrainz",
"downloadOriginalFormat": "Télécharger au format original",
"shareOriginalFormat": "Partager avec le format original",
"shareDialogTitle": "Partager %{resource} '%{name}'",
"shareBatchDialogTitle": "Partager 1 %{resource} |||| Partager %{smart_count} %{resource}",
"shareSuccess": "Lien copié vers le presse-papier: %{url}",
"shareSuccess": "Lien copié vers le presse-papier : %{url}",
"shareFailure": "Erreur en copiant le lien %{url} vers le presse-papier",
"downloadDialogTitle": "Télécharger %{resource} '%{name}' (%{size})",
"shareCopyToClipboard": "Copier vers le presse-papier: Ctrl+C, Enter"
"shareCopyToClipboard": "Copier vers le presse-papier : Ctrl+C, Enter"
},
"menu": {
"library": "Bibliothèque",
@ -390,7 +390,7 @@
"lastfmScrobbling": "Scrobbler vers Last.fm",
"listenBrainzScrobbling": "Scrobbler vers ListenBrainz",
"replaygain": "Mode ReplayGain",
"preAmp": "Pre-amplification ReplayGain (dB)",
"preAmp": "Pré-amplification ReplayGain (dB)",
"gain": {
"none": "Désactivé",
"album": "Utiliser le gain de l'album",
@ -457,4 +457,4 @@
"current_song": "Aller à la chanson en cours"
}
}
}
}

58
ui/package-lock.json generated
View File

@ -33,7 +33,7 @@
"react-drag-listview": "^0.1.8",
"react-ga": "^3.3.1",
"react-hotkeys": "^2.0.0",
"react-icons": "^5.0.1",
"react-icons": "^5.1.0",
"react-image-lightbox": "^5.1.4",
"react-measure": "^2.5.2",
"react-redux": "^7.2.9",
@ -43,12 +43,12 @@
"uuid": "^9.0.1"
},
"devDependencies": {
"@testing-library/jest-dom": "^6.2.0",
"@testing-library/jest-dom": "^6.4.2",
"@testing-library/react": "^12.1.5",
"@testing-library/react-hooks": "^7.0.2",
"@testing-library/user-event": "^14.5.2",
"css-mediaquery": "^0.1.2",
"prettier": "3.2.2",
"prettier": "3.2.5",
"ra-test": "^3.19.12",
"react-scripts": "5.0.1",
"workbox-cli": "^7.0.0"
@ -4715,9 +4715,9 @@
"dev": true
},
"node_modules/@testing-library/jest-dom": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.2.0.tgz",
"integrity": "sha512-+BVQlJ9cmEn5RDMUS8c2+TU6giLvzaHZ8sU/x0Jj7fk+6/46wPdwlgOPcpxS17CjcanBi/3VmGMqVr2rmbUmNw==",
"version": "6.4.2",
"resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.4.2.tgz",
"integrity": "sha512-CzqH0AFymEMG48CpzXFriYYkOjk6ZGPCLMhW9e9jg3KMCn5OfJecF8GtGW7yGfR/IgCe3SX8BSwjdzI6BBbZLw==",
"dev": true,
"dependencies": {
"@adobe/css-tools": "^4.3.2",
@ -4736,6 +4736,7 @@
},
"peerDependencies": {
"@jest/globals": ">= 28",
"@types/bun": "latest",
"@types/jest": ">= 28",
"jest": ">= 28",
"vitest": ">= 0.32"
@ -4744,6 +4745,9 @@
"@jest/globals": {
"optional": true
},
"@types/bun": {
"optional": true
},
"@types/jest": {
"optional": true
},
@ -10131,9 +10135,9 @@
"dev": true
},
"node_modules/follow-redirects": {
"version": "1.15.4",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz",
"integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==",
"version": "1.15.6",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
"integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
"dev": true,
"funding": [
{
@ -18504,9 +18508,9 @@
}
},
"node_modules/prettier": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.2.tgz",
"integrity": "sha512-HTByuKZzw7utPiDO523Tt2pLtEyK7OibUD9suEJQrPUCYQqrHr74GGX6VidMrovbf/I50mPqr8j/II6oBAuc5A==",
"version": "3.2.5",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz",
"integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==",
"dev": true,
"bin": {
"prettier": "bin/prettier.cjs"
@ -19412,9 +19416,9 @@
}
},
"node_modules/react-icons": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.0.1.tgz",
"integrity": "sha512-WqLZJ4bLzlhmsvme6iFdgO8gfZP17rfjYEJ2m9RsZjZ+cc4k1hTzknEz63YS1MeT50kVzoa1Nz36f4BEx+Wigw==",
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.1.0.tgz",
"integrity": "sha512-D3zug1270S4hbSlIRJ0CUS97QE1yNNKDjzQe3HqY0aefp2CBn9VgzgES27sRR2gOvFK+0CNx/BW0ggOESp6fqQ==",
"peerDependencies": {
"react": "*"
}
@ -28190,9 +28194,9 @@
}
},
"@testing-library/jest-dom": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.2.0.tgz",
"integrity": "sha512-+BVQlJ9cmEn5RDMUS8c2+TU6giLvzaHZ8sU/x0Jj7fk+6/46wPdwlgOPcpxS17CjcanBi/3VmGMqVr2rmbUmNw==",
"version": "6.4.2",
"resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.4.2.tgz",
"integrity": "sha512-CzqH0AFymEMG48CpzXFriYYkOjk6ZGPCLMhW9e9jg3KMCn5OfJecF8GtGW7yGfR/IgCe3SX8BSwjdzI6BBbZLw==",
"dev": true,
"requires": {
"@adobe/css-tools": "^4.3.2",
@ -32389,9 +32393,9 @@
"dev": true
},
"follow-redirects": {
"version": "1.15.4",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz",
"integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==",
"version": "1.15.6",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
"integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
"dev": true
},
"fork-ts-checker-webpack-plugin": {
@ -38703,9 +38707,9 @@
"dev": true
},
"prettier": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.2.tgz",
"integrity": "sha512-HTByuKZzw7utPiDO523Tt2pLtEyK7OibUD9suEJQrPUCYQqrHr74GGX6VidMrovbf/I50mPqr8j/II6oBAuc5A==",
"version": "3.2.5",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz",
"integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==",
"dev": true
},
"pretty-bytes": {
@ -39386,9 +39390,9 @@
}
},
"react-icons": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.0.1.tgz",
"integrity": "sha512-WqLZJ4bLzlhmsvme6iFdgO8gfZP17rfjYEJ2m9RsZjZ+cc4k1hTzknEz63YS1MeT50kVzoa1Nz36f4BEx+Wigw==",
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.1.0.tgz",
"integrity": "sha512-D3zug1270S4hbSlIRJ0CUS97QE1yNNKDjzQe3HqY0aefp2CBn9VgzgES27sRR2gOvFK+0CNx/BW0ggOESp6fqQ==",
"requires": {}
},
"react-image-lightbox": {

View File

@ -28,7 +28,7 @@
"react-drag-listview": "^0.1.8",
"react-ga": "^3.3.1",
"react-hotkeys": "^2.0.0",
"react-icons": "^5.0.1",
"react-icons": "^5.1.0",
"react-image-lightbox": "^5.1.4",
"react-measure": "^2.5.2",
"react-redux": "^7.2.9",
@ -38,12 +38,12 @@
"uuid": "^9.0.1"
},
"devDependencies": {
"@testing-library/jest-dom": "^6.2.0",
"@testing-library/jest-dom": "^6.4.2",
"@testing-library/react": "^12.1.5",
"@testing-library/react-hooks": "^7.0.2",
"@testing-library/user-event": "^14.5.2",
"css-mediaquery": "^0.1.2",
"prettier": "3.2.2",
"prettier": "3.2.5",
"ra-test": "^3.19.12",
"react-scripts": "5.0.1",
"workbox-cli": "^7.0.0"

View File

@ -54,15 +54,19 @@ export const AddToPlaylistDialog = () => {
})
.then(() => {
const len = trackIds.length
notify('message.songsAddedToPlaylist', 'info', { smart_count: len })
notify('message.songsAddedToPlaylist', {
messageArgs: { smart_count: len },
})
onSuccess && onSuccess(value, len)
refresh()
})
.catch(() => {
notify('ra.page.error', 'warning')
notify('ra.page.error', { type: 'warning' })
})
} else {
notify('message.songsAddedToPlaylist', 'info', { smart_count: 0 })
notify('message.songsAddedToPlaylist', {
messageArgs: { smart_count: 0 },
})
}
}

View File

@ -23,7 +23,9 @@ const Progress = (props) => {
)
const callbackUrl = `${window.location.origin}${callbackEndpoint}`
openedTab.current = openInNewTab(
`https://www.last.fm/api/auth/?api_key=${localStorage.getItem('lastfm-apikey')}&cb=${callbackUrl}`,
`https://www.last.fm/api/auth/?api_key=${localStorage.getItem(
'lastfm-apikey',
)}&cb=${callbackUrl}`,
)
}, [])

View File

@ -3,6 +3,7 @@
set -e
download_lang() {
filename=resources/i18n/"$1".json
url=$(curl -s -X POST https://poeditor.com/api/ \
-d api_token="${POEDITOR_APIKEY}" \
-d action="export" \
@ -13,7 +14,12 @@ download_lang() {
echo "Failed to export $1"
return 1
fi
curl -sSL "$url" > resources/i18n/"$1".json
curl -sSL "$url" > poeditor.json
if [ "$(jq -c . < $filename)" != "$(jq -c . < poeditor.json)" ]; then
mv poeditor.json "$filename"
else
rm poeditor.json
fi
}
for file in resources/i18n/*.json; do