mirror of
https://github.com/navidrome/navidrome.git
synced 2026-08-31 07:30:32 +00:00
Merge branch 'master' into subsonic-folder
This commit is contained in:
commit
25b950126d
@ -127,6 +127,17 @@ var _ = Describe("Extractor", func() {
|
||||
Expect(m.Tags).To(HaveKeyWithValue("albumartist", []string{"Album Artist"}))
|
||||
Expect(m.Tags).To(HaveKeyWithValue("genre", []string{"Rock"}))
|
||||
Expect(m.Tags).To(HaveKeyWithValue("date", []string{"2014"}))
|
||||
// Still as of TagLib v2.2.1, TagLib only maps values in ID3, MP4, and ASF tags
|
||||
// to `originaldate`.
|
||||
if strings.HasSuffix(file, ".mp3") || strings.HasSuffix(file, ".wav") || strings.HasSuffix(file, ".aiff") || strings.HasSuffix(file, ".m4a") || strings.HasSuffix(file, ".wma") {
|
||||
Expect(m.Tags).To(HaveKeyWithValue("originaldate", []string{"1996-11-21"}))
|
||||
}
|
||||
// MP3Tag sets `ORIGYEAR` in several formats for which it has no built-in mapping
|
||||
// for original release dates.
|
||||
Expect(m.Tags).To(Or(
|
||||
HaveKeyWithValue("origyear", []string{"1998-07-28"}),
|
||||
HaveKeyWithValue("----:com.apple.itunes:origyear", []string{"1998-07-28"}),
|
||||
))
|
||||
|
||||
Expect(m.Tags).To(HaveKeyWithValue("bpm", []string{"123"}))
|
||||
Expect(m.Tags).To(Or(
|
||||
|
||||
@ -49,6 +49,7 @@ type FFmpeg interface {
|
||||
ProbeAudioStream(ctx context.Context, filePath string) (*AudioProbeResult, error)
|
||||
CmdPath() (string, error)
|
||||
IsAvailable() bool
|
||||
IsProbeAvailable() bool
|
||||
Version() string
|
||||
}
|
||||
|
||||
@ -224,6 +225,19 @@ func (e *ffmpeg) IsAvailable() bool {
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func (e *ffmpeg) IsProbeAvailable() bool {
|
||||
if _, err := ffmpegCmd(); err != nil {
|
||||
return false
|
||||
}
|
||||
probeOnce.Do(func() {
|
||||
probePath := ffprobePath(ffmpegPath)
|
||||
if _, err := exec.LookPath(probePath); err == nil {
|
||||
probeAvail = true
|
||||
}
|
||||
})
|
||||
return probeAvail
|
||||
}
|
||||
|
||||
// Version executes ffmpeg -version and extracts the version from the output.
|
||||
// Sample output: ffmpeg version 6.0 Copyright (c) 2000-2023 the FFmpeg developers
|
||||
func (e *ffmpeg) Version() string {
|
||||
@ -533,4 +547,6 @@ var (
|
||||
ffOnce sync.Once
|
||||
ffmpegPath string
|
||||
ffmpegErr error
|
||||
probeOnce sync.Once
|
||||
probeAvail bool
|
||||
)
|
||||
|
||||
@ -44,10 +44,14 @@ func (s *deciderService) MakeDecision(ctx context.Context, mf *model.MediaFile,
|
||||
|
||||
var probe *ffmpeg.AudioProbeResult
|
||||
if !opts.SkipProbe {
|
||||
var err error
|
||||
probe, err = s.ensureProbed(ctx, mf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if !s.ff.IsProbeAvailable() {
|
||||
log.Debug(ctx, "ffprobe not available, using tag metadata for transcode decision", "mediaID", mf.ID)
|
||||
} else {
|
||||
var err error
|
||||
probe, err = s.ensureProbed(ctx, mf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1164,6 +1164,7 @@ var _ = Describe("Decider", func() {
|
||||
Expect(bitrate).To(Equal(fallbackBitrate))
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
Describe("ensureProbed", func() {
|
||||
|
||||
2
go.mod
2
go.mod
@ -3,7 +3,7 @@ module github.com/navidrome/navidrome
|
||||
go 1.25.0
|
||||
|
||||
// Fork to implement raw tags support
|
||||
replace go.senan.xyz/taglib => github.com/deluan/go-taglib v0.0.0-20260307161927-168f6e74ada7
|
||||
replace go.senan.xyz/taglib => github.com/deluan/go-taglib v0.0.0-20260407173416-cf47afbaa67a
|
||||
|
||||
require (
|
||||
github.com/Masterminds/squirrel v1.5.4
|
||||
|
||||
4
go.sum
4
go.sum
@ -34,8 +34,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1 h1:5RVFMOWjMyRy8cARdy79nAmgYw3hK/4HUq48LQ6Wwqo=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
|
||||
github.com/deluan/go-taglib v0.0.0-20260307161927-168f6e74ada7 h1:RpRSTEsAdLHx3Ci0d3M5wtpjcBZiKzhnGfnNAxGXrAE=
|
||||
github.com/deluan/go-taglib v0.0.0-20260307161927-168f6e74ada7/go.mod h1:sKDN0U4qXDlq6LFK+aOAkDH4Me5nDV1V/A4B+B69xBA=
|
||||
github.com/deluan/go-taglib v0.0.0-20260407173416-cf47afbaa67a h1:ZPwh87Xa08FCg5MU5e0Did5WgapEWGxb5d4Je0pLjJw=
|
||||
github.com/deluan/go-taglib v0.0.0-20260407173416-cf47afbaa67a/go.mod h1:sKDN0U4qXDlq6LFK+aOAkDH4Me5nDV1V/A4B+B69xBA=
|
||||
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-20241120162836-fdfd8fdfaa55 h1:wSCnggTs2f2ji6nFwQmfwgINcmSMj0xF0oHnoyRSPe4=
|
||||
|
||||
@ -9,6 +9,7 @@ import (
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/dustin/go-humanize"
|
||||
@ -35,6 +36,8 @@ type kvstoreServiceImpl struct {
|
||||
pluginName string
|
||||
db *sql.DB
|
||||
maxSize int64
|
||||
cancel context.CancelFunc
|
||||
wg sync.WaitGroup
|
||||
}
|
||||
|
||||
// newKVStoreService creates a new kvstoreServiceImpl instance with its own SQLite database.
|
||||
@ -74,12 +77,15 @@ func newKVStoreService(ctx context.Context, pluginName string, perm *KVStorePerm
|
||||
|
||||
log.Debug("Initialized plugin kvstore", "plugin", pluginName, "path", dbPath, "maxSize", humanize.Bytes(uint64(maxSize)))
|
||||
|
||||
cleanupCtx, cancel := context.WithCancel(ctx)
|
||||
svc := &kvstoreServiceImpl{
|
||||
pluginName: pluginName,
|
||||
db: db,
|
||||
maxSize: maxSize,
|
||||
cancel: cancel,
|
||||
}
|
||||
go svc.cleanupLoop(ctx)
|
||||
svc.wg.Add(1)
|
||||
go svc.cleanupLoop(cleanupCtx)
|
||||
return svc, nil
|
||||
}
|
||||
|
||||
@ -335,6 +341,7 @@ func (s *kvstoreServiceImpl) GetMany(ctx context.Context, keys []string) (map[st
|
||||
// cleanupLoop periodically removes expired keys from the database.
|
||||
// It stops when the provided context is cancelled.
|
||||
func (s *kvstoreServiceImpl) cleanupLoop(ctx context.Context) {
|
||||
defer s.wg.Done()
|
||||
ticker := time.NewTicker(cleanupInterval)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
@ -359,17 +366,12 @@ func (s *kvstoreServiceImpl) cleanupExpired(ctx context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
// Close runs a final cleanup and closes the SQLite database connection.
|
||||
// The cleanup goroutine is stopped by the context passed to newKVStoreService.
|
||||
// Close stops the cleanup goroutine and closes the SQLite database connection.
|
||||
func (s *kvstoreServiceImpl) Close() error {
|
||||
if s.db != nil {
|
||||
log.Debug("Closing plugin kvstore", "plugin", s.pluginName)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
s.cleanupExpired(ctx)
|
||||
return s.db.Close()
|
||||
}
|
||||
return nil
|
||||
log.Debug("Closing plugin kvstore", "plugin", s.pluginName)
|
||||
s.cancel()
|
||||
s.wg.Wait()
|
||||
return s.db.Close()
|
||||
}
|
||||
|
||||
// Compile-time verification
|
||||
|
||||
@ -445,6 +445,36 @@ var _ = Describe("KVStoreService", func() {
|
||||
})
|
||||
})
|
||||
|
||||
Describe("Close", func() {
|
||||
It("does not race with cleanupLoop goroutine", func() {
|
||||
// Create a service with a dedicated context so we can verify
|
||||
// that Close() properly waits for the cleanup goroutine.
|
||||
closeCtx, closeCancel := context.WithCancel(ctx)
|
||||
defer closeCancel()
|
||||
|
||||
maxSize := "1KB"
|
||||
svc, err := newKVStoreService(closeCtx, "test_close_race", &KVStorePermission{MaxSize: &maxSize})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
// Insert an expired key so cleanup has work to do
|
||||
_, err = svc.db.Exec(`
|
||||
INSERT INTO kvstore (key, value, size, expires_at)
|
||||
VALUES ('cleanup_race', 'old', 3, datetime('now', '-1 seconds'))
|
||||
`)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
// Close should not panic or produce "database is closed" errors.
|
||||
// Before the fix, the cleanup goroutine could race with db.Close().
|
||||
err = svc.Close()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
// Verify the database is actually closed (further queries should fail)
|
||||
_, err = svc.db.Exec(`SELECT 1`)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("database is closed"))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("SetWithTTL", func() {
|
||||
It("stores value that is retrievable before expiry", func() {
|
||||
err := service.SetWithTTL(ctx, "ttl_key", []byte("ttl_value"), 3600)
|
||||
|
||||
@ -43,8 +43,9 @@ FFMPEG_FILE="ffmpeg-n${FFMPEG_VERSION}-latest-${WIN_ARCH}-gpl-${FFMPEG_VERSION}"
|
||||
wget --quiet --output-document="${DOWNLOAD_FOLDER}/ffmpeg.zip" \
|
||||
"https://github.com/${FFMPEG_REPOSITORY}/releases/download/latest/${FFMPEG_FILE}.zip"
|
||||
rm -rf "${DOWNLOAD_FOLDER}/extracted_ffmpeg"
|
||||
unzip -d "${DOWNLOAD_FOLDER}/extracted_ffmpeg" "${DOWNLOAD_FOLDER}/ffmpeg.zip" "*/ffmpeg.exe"
|
||||
unzip -d "${DOWNLOAD_FOLDER}/extracted_ffmpeg" "${DOWNLOAD_FOLDER}/ffmpeg.zip" "*/ffmpeg.exe" "*/ffprobe.exe"
|
||||
cp "${DOWNLOAD_FOLDER}"/extracted_ffmpeg/${FFMPEG_FILE}/bin/ffmpeg.exe "$MSI_OUTPUT_DIR"
|
||||
cp "${DOWNLOAD_FOLDER}"/extracted_ffmpeg/${FFMPEG_FILE}/bin/ffprobe.exe "$MSI_OUTPUT_DIR"
|
||||
|
||||
cp "$WORKSPACE"/LICENSE "$WORKSPACE"/README.md "$MSI_OUTPUT_DIR"
|
||||
cp "$BINARY" "$MSI_OUTPUT_DIR"
|
||||
|
||||
@ -67,6 +67,10 @@
|
||||
<File Id='ffmpeg.exe' Name='ffmpeg.exe' DiskId='1' Source='ffmpeg.exe' KeyPath='yes' />
|
||||
</Component>
|
||||
|
||||
<Component Id='FFProbeExecutable' Guid='f8a3b2c1-5d4e-4f6a-9b8c-7e2d1a0f3c5b' Win64="$(var.Win64)">
|
||||
<File Id='ffprobe.exe' Name='ffprobe.exe' DiskId='1' Source='ffprobe.exe' KeyPath='yes' />
|
||||
</Component>
|
||||
|
||||
</Directory>
|
||||
</Directory>
|
||||
|
||||
@ -87,6 +91,7 @@
|
||||
<ComponentRef Id='Configuration'/>
|
||||
<ComponentRef Id='MainExecutable' />
|
||||
<ComponentRef Id='FFMpegExecutable' />
|
||||
<ComponentRef Id='FFProbeExecutable' />
|
||||
<ComponentRef Id='PackageFile' />
|
||||
</Feature>
|
||||
</Product>
|
||||
|
||||
@ -23,6 +23,7 @@
|
||||
"bitDepth": "位深度",
|
||||
"sampleRate": "采样率",
|
||||
"channels": "声道",
|
||||
"disc": "碟片 %{discNumber}",
|
||||
"discSubtitle": "碟片副标题",
|
||||
"starred": "收藏",
|
||||
"comment": "注释",
|
||||
@ -355,7 +356,8 @@
|
||||
"allUsers": "允许所有用户",
|
||||
"selectedUsers": "指定用户",
|
||||
"allLibraries": "允许所有媒体库",
|
||||
"selectedLibraries": "指定媒体库"
|
||||
"selectedLibraries": "指定媒体库",
|
||||
"allowWriteAccess": "允许写入权限"
|
||||
},
|
||||
"sections": {
|
||||
"status": "状态",
|
||||
@ -400,6 +402,7 @@
|
||||
"allLibrariesHelp": "启用时,插件将可以访问所有媒体库,包括将来创建的。",
|
||||
"noLibraries": "未选择媒体库",
|
||||
"librariesRequired": "此插件需要访问媒体库信息。请选择允许此插件访问的媒体库, 或启用 '允许所有媒体库'。",
|
||||
"allowWriteAccessHelp": "启用时,插件将可以修改媒体库目录中的文件。默认情况下,插件仅拥有只读权限。",
|
||||
"requiredHosts": "必需的主机"
|
||||
},
|
||||
"placeholders": {
|
||||
@ -554,6 +557,12 @@
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"uploadCover": "上传封面",
|
||||
"removeCover": "移除封面",
|
||||
"coverUploaded": "封面已上传",
|
||||
"coverRemoved": "封面已移除",
|
||||
"coverUploadError": "上传封面时出错",
|
||||
"coverRemoveError": "移除封面时出错",
|
||||
"note": "注意",
|
||||
"transcodingDisabled": "出于安全原因,从 Web 界面更改转码配置的功能已被禁用。要更改(编辑或新增)转码选项,请在启用 %{config} 选项的情况下重新启动服务器。",
|
||||
"transcodingEnabled": "Navidrome 当前与 %{config} 一起使用,可以通过从 Web 界面配置转码选项来执行任意命令。建议禁用此选项,并且仅在需要配置转码选项时启用此功能。",
|
||||
@ -673,6 +682,7 @@
|
||||
"currentValue": "当前值",
|
||||
"configurationFile": "配置文件",
|
||||
"exportToml": "导出配置(TOML)",
|
||||
"downloadToml": "下载配置(TOML)",
|
||||
"exportSuccess": "配置以 TOML 格式导出到剪贴板完成",
|
||||
"exportFailed": "复制配置失败",
|
||||
"devFlagsHeader": "开发标志(可能会更改/删除)",
|
||||
|
||||
@ -116,7 +116,7 @@ main:
|
||||
aliases: [ comm:description, comment, ©cmt, description, icmt ]
|
||||
maxLength: 4096
|
||||
originaldate:
|
||||
aliases: [ tdor, originaldate, ----:com.apple.itunes:originaldate, wm/originalreleasetime, tory, originalyear, ----:com.apple.itunes:originalyear, wm/originalreleaseyear ]
|
||||
aliases: [ tdor, originaldate, ----:com.apple.itunes:originaldate, wm/originalreleasetime, tory, originalyear, ----:com.apple.itunes:originalyear, wm/originalreleaseyear, origyear, ----:com.apple.itunes:origyear ]
|
||||
type: date
|
||||
recordingdate:
|
||||
aliases: [ tdrc, date, recordingdate, icrd, record date ]
|
||||
|
||||
@ -337,6 +337,7 @@ func (n noopFFmpeg) ConvertAnimatedImage(context.Context, io.Reader, int, int) (
|
||||
|
||||
func (n noopFFmpeg) CmdPath() (string, error) { return "", nil }
|
||||
func (n noopFFmpeg) IsAvailable() bool { return false }
|
||||
func (n noopFFmpeg) IsProbeAvailable() bool { return true }
|
||||
func (n noopFFmpeg) Version() string { return "noop" }
|
||||
|
||||
// noopArchiver implements core.Archiver
|
||||
|
||||
@ -68,13 +68,16 @@ func createInitialAdminUser(ds model.DataStore, initialPassword string) error {
|
||||
func checkFFmpegInstallation() {
|
||||
f := ffmpeg.New()
|
||||
_, err := f.CmdPath()
|
||||
if err == nil {
|
||||
if err != nil {
|
||||
log.Warn("Unable to find ffmpeg. Transcoding will fail if used", err)
|
||||
if conf.Server.Scanner.Extractor == "ffmpeg" {
|
||||
log.Warn("ffmpeg cannot be used for metadata extraction. Falling back to taglib")
|
||||
conf.Server.Scanner.Extractor = "taglib"
|
||||
}
|
||||
return
|
||||
}
|
||||
log.Warn("Unable to find ffmpeg. Transcoding will fail if used", err)
|
||||
if conf.Server.Scanner.Extractor == "ffmpeg" {
|
||||
log.Warn("ffmpeg cannot be used for metadata extraction. Falling back to taglib")
|
||||
conf.Server.Scanner.Extractor = "taglib"
|
||||
if !f.IsProbeAvailable() {
|
||||
log.Warn("Unable to find ffprobe. Transcoding decisions will be limited")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BIN
tests/fixtures/01 Invisible (RED) Edit Version.m4a
vendored
BIN
tests/fixtures/01 Invisible (RED) Edit Version.m4a
vendored
Binary file not shown.
BIN
tests/fixtures/test.aiff
vendored
BIN
tests/fixtures/test.aiff
vendored
Binary file not shown.
BIN
tests/fixtures/test.flac
vendored
BIN
tests/fixtures/test.flac
vendored
Binary file not shown.
BIN
tests/fixtures/test.m4a
vendored
BIN
tests/fixtures/test.m4a
vendored
Binary file not shown.
BIN
tests/fixtures/test.mp3
vendored
BIN
tests/fixtures/test.mp3
vendored
Binary file not shown.
BIN
tests/fixtures/test.ogg
vendored
BIN
tests/fixtures/test.ogg
vendored
Binary file not shown.
BIN
tests/fixtures/test.opus
vendored
BIN
tests/fixtures/test.opus
vendored
Binary file not shown.
BIN
tests/fixtures/test.wav
vendored
BIN
tests/fixtures/test.wav
vendored
Binary file not shown.
BIN
tests/fixtures/test.wma
vendored
BIN
tests/fixtures/test.wma
vendored
Binary file not shown.
BIN
tests/fixtures/test.wv
vendored
BIN
tests/fixtures/test.wv
vendored
Binary file not shown.
@ -12,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
func NewMockFFmpeg(data string) *MockFFmpeg {
|
||||
return &MockFFmpeg{Reader: strings.NewReader(data)}
|
||||
return &MockFFmpeg{Reader: strings.NewReader(data), ProbeAvailable: true}
|
||||
}
|
||||
|
||||
type MockFFmpeg struct {
|
||||
@ -21,12 +21,17 @@ type MockFFmpeg struct {
|
||||
closed atomic.Bool
|
||||
Error error
|
||||
ProbeAudioResult *ffmpeg.AudioProbeResult
|
||||
ProbeAvailable bool
|
||||
}
|
||||
|
||||
func (ff *MockFFmpeg) IsAvailable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (ff *MockFFmpeg) IsProbeAvailable() bool {
|
||||
return ff.ProbeAvailable
|
||||
}
|
||||
|
||||
func (ff *MockFFmpeg) Transcode(_ context.Context, _ ffmpeg.TranscodeOptions) (io.ReadCloser, error) {
|
||||
if ff.Error != nil {
|
||||
return nil, ff.Error
|
||||
|
||||
48
ui/package-lock.json
generated
48
ui/package-lock.json
generated
@ -68,7 +68,7 @@
|
||||
"prettier": "^3.6.2",
|
||||
"ra-test": "^3.19.12",
|
||||
"typescript": "^5.8.3",
|
||||
"vite": "^7.1.12",
|
||||
"vite": "^7.3.2",
|
||||
"vite-plugin-pwa": "^1.1.0",
|
||||
"vitest": "^4.0.3"
|
||||
}
|
||||
@ -129,7 +129,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz",
|
||||
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.29.0",
|
||||
"@babel/generator": "^7.29.0",
|
||||
@ -1744,7 +1743,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
@ -1768,7 +1766,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@ -2474,7 +2471,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@jsonforms/core/-/core-2.5.2.tgz",
|
||||
"integrity": "sha512-tl64cLC2dUrGvu2nTHRDEA5Yv3RfwzMCIlVaoSUSq44LakKLGJdkPl8j/fb07llpFqz0a7gEAmy/8gLdmwgaLQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/json-schema": "^7.0.3",
|
||||
"ajv": "^6.10.2",
|
||||
@ -2528,7 +2524,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@jsonforms/react/-/react-2.5.2.tgz",
|
||||
"integrity": "sha512-kZf2fq4urIBlFTCiBX95eKg8uojkyJj7FVDtIV739aVkJjE5+ihn1+kG1qLxYSxlGC7S24i12BZJzRetSRihBQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"lodash": "^4.17.15",
|
||||
"object-hash": "^2.0.0"
|
||||
@ -2544,7 +2539,6 @@
|
||||
"integrity": "sha512-tr7xekNlM9LjA6pagJmL8QCgZXaubWUwkJnoYcMKd4gw/t4XiyvnTkjdGrUVicyB2BsdaAv1tvow45bPM4sSwQ==",
|
||||
"deprecated": "Material UI v4 doesn't receive active development since September 2021. See the guide https://mui.com/material-ui/migration/migration-v4/ to upgrade to v5.",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.4.4",
|
||||
"@material-ui/styles": "^4.11.5",
|
||||
@ -2591,7 +2585,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/icons/-/icons-4.11.3.tgz",
|
||||
"integrity": "sha512-IKHlyx6LDh8n19vzwH5RtHIOHl9Tu90aAAxcbWME6kp4dmvODM3UvOHJeMIDzUbd4muuJKHmlNoBN+mDY4XkBA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.4.4"
|
||||
},
|
||||
@ -2684,7 +2677,6 @@
|
||||
"integrity": "sha512-o/41ot5JJiUsIETME9wVLAJrmIWL3j0R0Bj2kCOLbSfqEkKf0fmaPt+5vtblUh5eXr2S+J/8J3DaCb10+CzPGA==",
|
||||
"deprecated": "Material UI v4 doesn't receive active development since September 2021. See the guide https://mui.com/material-ui/migration/migration-v4/ to upgrade to v5.",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.4.4",
|
||||
"@emotion/hash": "^0.8.0",
|
||||
@ -3316,7 +3308,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.7.tgz",
|
||||
"integrity": "sha512-PQTyIulDkIDro8P+IHbKCsw7U2xxBYflVzW/FgWdCAePD9xGSidgA76/GeJ6lBKoblyhf9pBY763gbrN+1dI8g==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"hoist-non-react-statics": "^3.3.0"
|
||||
},
|
||||
@ -3369,7 +3360,6 @@
|
||||
"integrity": "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~7.16.0"
|
||||
}
|
||||
@ -3391,7 +3381,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.91.tgz",
|
||||
"integrity": "sha512-xauZca6qMeCU3Moy0KxCM9jtf1vyk6qRYK39Ryf3afUqwgNUjRIGoDdS9BcGWgAMGSg1hvP4XcmlYrM66PtqeA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/prop-types": "*",
|
||||
"@types/scheduler": "^0.16",
|
||||
@ -3561,7 +3550,6 @@
|
||||
"integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "6.21.0",
|
||||
"@typescript-eslint/types": "6.21.0",
|
||||
@ -3895,7 +3883,6 @@
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
|
||||
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
@ -4580,7 +4567,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"baseline-browser-mapping": "^2.10.12",
|
||||
"caniuse-lite": "^1.0.30001782",
|
||||
@ -4956,7 +4942,6 @@
|
||||
"resolved": "https://registry.npmjs.org/connected-react-router/-/connected-react-router-6.9.3.tgz",
|
||||
"integrity": "sha512-4ThxysOiv/R2Dc4Cke1eJwjKwH1Y51VDwlOrOfs1LjpdYOVvCNjNkZDayo7+sx42EeGJPQUNchWkjAIJdXGIOQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"lodash.isequalwith": "^4.4.0",
|
||||
"prop-types": "^15.7.2"
|
||||
@ -5837,7 +5822,6 @@
|
||||
"deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.2.0",
|
||||
"@eslint-community/regexpp": "^4.6.1",
|
||||
@ -6421,7 +6405,6 @@
|
||||
"resolved": "https://registry.npmjs.org/final-form/-/final-form-4.20.10.tgz",
|
||||
"integrity": "sha512-TL48Pi1oNHeMOHrKv1bCJUrWZDcD3DIG6AGYVNOnyZPr7Bd/pStN0pL+lfzF5BNoj/FclaoiaLenk4XUIFVYng==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.10.0"
|
||||
},
|
||||
@ -6438,7 +6421,6 @@
|
||||
"resolved": "https://registry.npmjs.org/final-form-arrays/-/final-form-arrays-3.1.0.tgz",
|
||||
"integrity": "sha512-TWBvun+AopgBLw9zfTFHBllnKMVNEwCEyDawphPuBGGqNsuhGzhT7yewHys64KFFwzIs6KEteGLpKOwvTQEscQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"peerDependencies": {
|
||||
"final-form": "^4.20.8"
|
||||
}
|
||||
@ -6853,7 +6835,6 @@
|
||||
"integrity": "sha512-Tz23LR9T9jOGVZm2x1EPdXqwA37G/owYMxRwU0E4miurAtFsPMQ1d2Jc2okUaSjZqAFz2oEn3FLXC5a0a+siyA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/node": ">=20.0.0",
|
||||
"@types/whatwg-mimetype": "^3.0.2",
|
||||
@ -6967,7 +6948,6 @@
|
||||
"resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz",
|
||||
"integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.1.2",
|
||||
"loose-envify": "^1.2.0",
|
||||
@ -8570,7 +8550,6 @@
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
|
||||
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
@ -9319,7 +9298,6 @@
|
||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
||||
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.4.0",
|
||||
"object-assign": "^4.1.1",
|
||||
@ -9411,7 +9389,6 @@
|
||||
"resolved": "https://registry.npmjs.org/ra-core/-/ra-core-3.19.12.tgz",
|
||||
"integrity": "sha512-E0cM6OjEUtccaR+dR5mL1MLiVVYML0Yf7aPhpLEq4iue73X3+CKcLztInoBhWgeevPbFQwgAtsXhlpedeyrNNg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"classnames": "~2.3.1",
|
||||
"date-fns": "^1.29.0",
|
||||
@ -9826,7 +9803,6 @@
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz",
|
||||
"integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.1.1"
|
||||
@ -9908,7 +9884,6 @@
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz",
|
||||
"integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.1.1",
|
||||
@ -9981,7 +9956,6 @@
|
||||
"resolved": "https://registry.npmjs.org/react-final-form/-/react-final-form-6.5.9.tgz",
|
||||
"integrity": "sha512-x3XYvozolECp3nIjly+4QqxdjSSWfcnpGEL5K8OBT6xmGrq5kBqbA6+/tOqoom9NwqIPPbxPNsOViFlbKgowbA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.15.4"
|
||||
},
|
||||
@ -9999,7 +9973,6 @@
|
||||
"resolved": "https://registry.npmjs.org/react-final-form-arrays/-/react-final-form-arrays-3.1.4.tgz",
|
||||
"integrity": "sha512-siVFAolUAe29rMR6u8VwepoysUcUdh6MLV2OWnCtKpsPRUdT9VUgECjAPaVMAH2GROZNiVB9On1H9MMrm9gdpg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.19.4"
|
||||
},
|
||||
@ -10105,7 +10078,6 @@
|
||||
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.9.tgz",
|
||||
"integrity": "sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.15.4",
|
||||
"@types/react-redux": "^7.1.20",
|
||||
@ -10141,7 +10113,6 @@
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-5.3.4.tgz",
|
||||
"integrity": "sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.13",
|
||||
"history": "^4.9.0",
|
||||
@ -10162,7 +10133,6 @@
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.3.4.tgz",
|
||||
"integrity": "sha512-m4EqFMHv/Ih4kpcBCONHbkT68KoAeHN4p3lAGoNryfHi0dMy0kCzEZakiKRsvg5wHZ/JLrLW8o8KomWiz/qbYQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.13",
|
||||
"history": "^4.9.0",
|
||||
@ -10344,7 +10314,6 @@
|
||||
"resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz",
|
||||
"integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.9.2"
|
||||
}
|
||||
@ -10354,7 +10323,6 @@
|
||||
"resolved": "https://registry.npmjs.org/redux-saga/-/redux-saga-1.4.2.tgz",
|
||||
"integrity": "sha512-QLIn/q+7MX/B+MkGJ/K6R3//60eJ4QNy65eqPsJrfGezbxdh1Jx+37VRKE2K4PsJnNET5JufJtgWdT30WBa+6w==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@redux-saga/core": "^1.4.2"
|
||||
}
|
||||
@ -10614,7 +10582,6 @@
|
||||
"integrity": "sha512-FAfGj5Ferzyna11iUwGdkYus/Y9d/H75PEpsseP5DZOsEsyPvP/Q7mJiSXhUYSEmyfHPaZyC8EsJCjqzDbtcfg==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/estree": "1.0.8"
|
||||
},
|
||||
@ -11544,7 +11511,6 @@
|
||||
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@ -11766,7 +11732,6 @@
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
@ -12006,12 +11971,11 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "7.3.1",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz",
|
||||
"integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==",
|
||||
"version": "7.3.2",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.3.2.tgz",
|
||||
"integrity": "sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.27.0",
|
||||
"fdir": "^6.5.0",
|
||||
@ -12136,7 +12100,6 @@
|
||||
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@ -12150,7 +12113,6 @@
|
||||
"integrity": "sha512-xjR1dMTVHlFLh98JE3i/f/WePqJsah4A0FK9cc8Ehp9Udk0AZk6ccpIZhh1qJ/yxVWRZ+Q54ocnD8TXmkhspGg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@vitest/expect": "4.1.2",
|
||||
"@vitest/mocker": "4.1.2",
|
||||
@ -12670,7 +12632,6 @@
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
|
||||
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fast-uri": "^3.0.1",
|
||||
@ -12780,7 +12741,6 @@
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.80.0.tgz",
|
||||
"integrity": "sha512-cIFJOD1DESzpjOBl763Kp1AH7UE/0fcdHe6rZXUdQ9c50uvgigvW97u3IcSeBwOkgqL/PXPBktBCh0KEu5L8XQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"rollup": "dist/bin/rollup"
|
||||
},
|
||||
|
||||
@ -77,7 +77,7 @@
|
||||
"prettier": "^3.6.2",
|
||||
"ra-test": "^3.19.12",
|
||||
"typescript": "^5.8.3",
|
||||
"vite": "^7.1.12",
|
||||
"vite": "^7.3.2",
|
||||
"vite-plugin-pwa": "^1.1.0",
|
||||
"vitest": "^4.0.3"
|
||||
},
|
||||
|
||||
@ -51,7 +51,6 @@ const useStyles = makeStyles({
|
||||
borderRadius: '4px',
|
||||
flexShrink: 0,
|
||||
cursor: 'pointer',
|
||||
filter: 'none !important',
|
||||
},
|
||||
row: {
|
||||
cursor: 'pointer',
|
||||
|
||||
@ -208,11 +208,11 @@ export default {
|
||||
borderBottom: `1px solid ${colors.gray[300]}`,
|
||||
padding: '10px !important',
|
||||
color: `${colors.gray[100]} !important`,
|
||||
'& img': {
|
||||
'& img[alt="playing"], & img[alt="paused"]': {
|
||||
filter:
|
||||
'brightness(0) saturate(100%) invert(36%) sepia(93%) saturate(7463%) hue-rotate(289deg) brightness(95%) contrast(102%);',
|
||||
'brightness(0) saturate(100%) invert(36%) sepia(93%) saturate(7463%) hue-rotate(289deg) brightness(95%) contrast(102%)',
|
||||
},
|
||||
'& img + span': {
|
||||
'& img[alt="playing"] + span, & img[alt="paused"] + span': {
|
||||
color: colors.pink[500],
|
||||
},
|
||||
},
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user