mirror of
https://github.com/navidrome/navidrome.git
synced 2025-12-31 19:08:06 -05:00
Compare commits
9 Commits
plugin-spi
...
v0.42.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
35027bbeb8 | ||
|
|
3af4677303 | ||
|
|
3c30cefc2e | ||
|
|
d0fea6bffb | ||
|
|
fff717395c | ||
|
|
90a0739251 | ||
|
|
ab68524763 | ||
|
|
125c4fe5ec | ||
|
|
616a44bac4 |
53
cmd/root.go
53
cmd/root.go
@@ -53,8 +53,16 @@ func runNavidrome() {
|
||||
db.EnsureLatestVersion()
|
||||
|
||||
var g run.Group
|
||||
|
||||
g.Add(startServer())
|
||||
g.Add(startScanner())
|
||||
g.Add(startSignaler())
|
||||
|
||||
interval := conf.Server.ScanInterval
|
||||
if interval != 0 {
|
||||
g.Add(startScanner(interval))
|
||||
} else {
|
||||
log.Warn("Periodic scan is DISABLED", "interval", interval)
|
||||
}
|
||||
|
||||
if err := g.Run(); err != nil {
|
||||
log.Error("Fatal error in Navidrome. Aborting", err)
|
||||
@@ -76,20 +84,45 @@ func startServer() (func() error, func(err error)) {
|
||||
}
|
||||
}
|
||||
|
||||
func startScanner() (func() error, func(err error)) {
|
||||
interval := conf.Server.ScanInterval
|
||||
var sigChan = make(chan os.Signal, 1)
|
||||
|
||||
func startSignaler() (func() error, func(err error)) {
|
||||
scanner := GetScanner()
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
return func() error {
|
||||
for {
|
||||
select {
|
||||
case sig := <-sigChan:
|
||||
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():
|
||||
break
|
||||
}
|
||||
}
|
||||
}, func(err error) {
|
||||
cancel()
|
||||
if err != nil {
|
||||
log.Error("Shutting down Signaler due to error", err)
|
||||
} else {
|
||||
log.Info("Shutting down Signaler")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func startScanner(interval time.Duration) (func() error, func(err error)) {
|
||||
log.Info("Starting scanner", "interval", interval.String())
|
||||
scanner := GetScanner()
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
return func() error {
|
||||
if interval != 0 {
|
||||
time.Sleep(2 * time.Second) // Wait 2 seconds before the first scan
|
||||
scanner.Run(ctx, interval)
|
||||
} else {
|
||||
log.Warn("Periodic scan is DISABLED", "interval", interval)
|
||||
<-ctx.Done()
|
||||
}
|
||||
time.Sleep(2 * time.Second) // Wait 2 seconds before the first scan
|
||||
scanner.Run(ctx, interval)
|
||||
|
||||
return nil
|
||||
}, func(err error) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// +build !windows
|
||||
// +build !plan9
|
||||
|
||||
package scanner
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"os"
|
||||
@@ -67,10 +67,10 @@ var (
|
||||
durationRx = regexp.MustCompile(`^\s\sDuration: ([\d.:]+).*bitrate: (\d+)`)
|
||||
|
||||
// Stream #0:0: Audio: mp3, 44100 Hz, stereo, fltp, 192 kb/s
|
||||
bitRateRx = regexp.MustCompile(`^\s{4}Stream #\d+:\d+: (Audio):.*, (\d+) kb/s`)
|
||||
bitRateRx = regexp.MustCompile(`^\s{2,4}Stream #\d+:\d+: (Audio):.*, (\d+) kb/s`)
|
||||
|
||||
// Stream #0:1: Video: mjpeg, yuvj444p(pc, bt470bg/unknown/unknown), 600x600 [SAR 1:1 DAR 1:1], 90k tbr, 90k tbn, 90k tbc`
|
||||
coverRx = regexp.MustCompile(`^\s{4}Stream #\d+:\d+: (Video):.*`)
|
||||
coverRx = regexp.MustCompile(`^\s{2,4}Stream #\d+:\d+: (Video):.*`)
|
||||
)
|
||||
|
||||
func (e *ffmpegExtractor) parseOutput(output string) map[string]string {
|
||||
|
||||
@@ -92,6 +92,21 @@ Input #0, mp3, from '/Users/deluan/Music/iTunes/iTunes Media/Music/Compilations/
|
||||
Expect(md.HasPicture()).To(BeTrue())
|
||||
})
|
||||
|
||||
It("detects embedded cover art in ffmpeg 4.4 output", func() {
|
||||
const output = `
|
||||
|
||||
Input #0, flac, from '/run/media/naomi/Archivio/Musica/Katy Perry/Chained to the Rhythm/01 Katy Perry featuring Skip Marley - Chained to the Rhythm.flac':
|
||||
Metadata:
|
||||
ARTIST : Katy Perry featuring Skip Marley
|
||||
Duration: 00:03:57.91, start: 0.000000, bitrate: 983 kb/s
|
||||
Stream #0:0: Audio: flac, 44100 Hz, stereo, s16
|
||||
Stream #0:1: Video: mjpeg (Baseline), yuvj444p(pc, bt470bg/unknown/unknown), 599x518, 90k tbr, 90k tbn, 90k tbc (attached pic)
|
||||
Metadata:
|
||||
comment : Cover (front)`
|
||||
md, _ := e.extractMetadata("tests/fixtures/test.mp3", output)
|
||||
Expect(md.HasPicture()).To(BeTrue())
|
||||
})
|
||||
|
||||
It("detects embedded cover art in ogg containers", func() {
|
||||
const output = `
|
||||
Input #0, ogg, from '/Users/deluan/Music/iTunes/iTunes Media/Music/_Testes/Jamaican In New York/01-02 Jamaican In New York (Album Version).opus':
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -40,10 +39,7 @@ type FolderScanner interface {
|
||||
Scan(ctx context.Context, lastModifiedSince time.Time, progress chan uint32) error
|
||||
}
|
||||
|
||||
var (
|
||||
isScanning utils.AtomicBool
|
||||
sigChan = make(chan os.Signal, 1)
|
||||
)
|
||||
var isScanning utils.AtomicBool
|
||||
|
||||
type scanner struct {
|
||||
folders map[string]FolderScanner
|
||||
@@ -87,9 +83,6 @@ func (s *scanner) Run(ctx context.Context, interval time.Duration) {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
continue
|
||||
case sig := <-sigChan:
|
||||
log.Info(ctx, "Received signal, triggering a new scan", "signal", sig)
|
||||
continue
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
|
||||
@@ -69,7 +69,9 @@ func (app *Router) routes(path string) http.Handler {
|
||||
app.addPlaylistTrackRoute(r)
|
||||
|
||||
// Keepalive endpoint to be used to keep the session valid (ex: while playing songs)
|
||||
r.Get("/keepalive/*", func(w http.ResponseWriter, r *http.Request) { _, _ = w.Write([]byte(`{"response":"ok"}`)) })
|
||||
r.Get("/keepalive/*", func(w http.ResponseWriter, r *http.Request) {
|
||||
_, _ = w.Write([]byte(`{"response":"ok", "id":"keepalive"}`))
|
||||
})
|
||||
|
||||
if conf.Server.DevActivityPanel {
|
||||
r.Handle("/events", app.broker)
|
||||
|
||||
@@ -33,6 +33,11 @@
|
||||
<script>
|
||||
window.__APP_CONFIG__ = "{{.AppConfig}}"
|
||||
</script>
|
||||
<!-- Issue workaround for React v16. -->
|
||||
<script>
|
||||
// See https://github.com/facebook/react/issues/20829#issuecomment-802088260
|
||||
if (!crossOriginIsolated) SharedArrayBuffer = ArrayBuffer;
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
|
||||
@@ -67,8 +67,9 @@ const SelectLanguage = (props) => {
|
||||
openInNewTab(docsUrl('/docs/developers/translations/'))
|
||||
return
|
||||
}
|
||||
setLocale(event.target.value)
|
||||
localStorage.setItem('locale', event.target.value)
|
||||
setLocale(event.target.value).then(() => {
|
||||
localStorage.setItem('locale', event.target.value)
|
||||
})
|
||||
}}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
} from 'react-admin'
|
||||
import { Title } from '../common'
|
||||
|
||||
const SyncFragment = ({ formData, ...rest }) => {
|
||||
const SyncFragment = ({ formData, variant, ...rest }) => {
|
||||
return (
|
||||
<Fragment>
|
||||
{formData.path && <BooleanInput source="sync" {...rest} />}
|
||||
|
||||
@@ -346,6 +346,7 @@ export default {
|
||||
icon: {
|
||||
backgroundColor: 'transparent',
|
||||
width: '100px',
|
||||
height: '100px',
|
||||
},
|
||||
card: {
|
||||
minWidth: 300,
|
||||
|
||||
@@ -267,6 +267,7 @@ export default {
|
||||
icon: {
|
||||
backgroundColor: 'inherit',
|
||||
width: '5em',
|
||||
height: '5em',
|
||||
},
|
||||
card: {
|
||||
background: 'none',
|
||||
@@ -306,6 +307,9 @@ export default {
|
||||
},
|
||||
},
|
||||
RaPaginationActions: {
|
||||
currentPageButton: {
|
||||
border: '1px solid #b3b3b3',
|
||||
},
|
||||
button: {
|
||||
backgroundColor: 'inherit',
|
||||
minWidth: 48,
|
||||
|
||||
Reference in New Issue
Block a user