feat(ui): add quick and full scan options for individual libraries

Signed-off-by: Deluan <deluan@navidrome.org>
This commit is contained in:
Deluan 2025-11-12 16:09:35 -05:00
parent acae163b0f
commit 8cebf82590
6 changed files with 107 additions and 2 deletions

View File

@ -300,6 +300,8 @@
},
"actions": {
"scan": "Scanear Biblioteca",
"quickScan": "Scan Rápido",
"fullScan": "Scan Completo",
"manageUsers": "Gerenciar Acesso do Usuário",
"viewDetails": "Ver Detalhes"
},
@ -308,6 +310,9 @@
"updated": "Biblioteca atualizada com sucesso",
"deleted": "Biblioteca excluída com sucesso",
"scanStarted": "Scan da biblioteca iniciada",
"quickScanStarted": "Scan rápido iniciado",
"fullScanStarted": "Scan completo iniciado",
"scanError": "Erro ao iniciar o scan. Verifique os logs",
"scanCompleted": "Scan da biblioteca concluída"
},
"validation": {

View File

@ -302,6 +302,8 @@
},
"actions": {
"scan": "Scan Library",
"quickScan": "Quick Scan",
"fullScan": "Full Scan",
"manageUsers": "Manage User Access",
"viewDetails": "View Details"
},
@ -310,6 +312,9 @@
"updated": "Library updated successfully",
"deleted": "Library deleted successfully",
"scanStarted": "Library scan started",
"quickScanStarted": "Quick scan started",
"fullScanStarted": "Full scan started",
"scanError": "Error starting scan. Check logs",
"scanCompleted": "Library scan completed"
},
"validation": {

View File

@ -10,6 +10,7 @@ import {
} from 'react-admin'
import { useMediaQuery } from '@material-ui/core'
import { List, DateField, useResourceRefresh, SizeField } from '../common'
import LibraryListBulkActions from './LibraryListBulkActions'
const LibraryFilter = (props) => (
<Filter {...props} variant={'outlined'}>
@ -26,7 +27,7 @@ const LibraryList = (props) => {
{...props}
sort={{ field: 'name', order: 'ASC' }}
exporter={false}
bulkActionButtons={false}
bulkActionButtons={!isXsmall && <LibraryListBulkActions />}
filters={<LibraryFilter />}
>
{isXsmall ? (

View File

@ -0,0 +1,11 @@
import React from 'react'
import LibraryScanButton from './LibraryScanButton'
const LibraryListBulkActions = (props) => (
<>
<LibraryScanButton fullScan={false} {...props} />
<LibraryScanButton fullScan={true} {...props} />
</>
)
export default LibraryListBulkActions

View File

@ -0,0 +1,77 @@
import React, { useState } from 'react'
import PropTypes from 'prop-types'
import {
Button,
useNotify,
useRefresh,
useTranslate,
useUnselectAll,
} from 'react-admin'
import { useSelector } from 'react-redux'
import SyncIcon from '@material-ui/icons/Sync'
import CachedIcon from '@material-ui/icons/Cached'
import subsonic from '../subsonic'
const LibraryScanButton = ({ fullScan, selectedIds, className }) => {
const [loading, setLoading] = useState(false)
const notify = useNotify()
const refresh = useRefresh()
const translate = useTranslate()
const unselectAll = useUnselectAll()
const scanStatus = useSelector((state) => state.activity.scanStatus)
const handleClick = async () => {
setLoading(true)
try {
// Build scan options
const options = { fullScan }
// If specific libraries are selected, scan only those
// Format: "libraryID:" to scan entire library (no folder path specified)
if (selectedIds && selectedIds.length > 0) {
options.path = selectedIds.map((id) => `${id}:`)
}
await subsonic.startScan(options)
const notificationKey = fullScan
? 'resources.library.notifications.fullScanStarted'
: 'resources.library.notifications.quickScanStarted'
notify(notificationKey, 'info')
refresh()
// Unselect all items after successful scan
unselectAll('library')
} catch (error) {
notify('resources.library.notifications.scanError', 'warning')
} finally {
setLoading(false)
}
}
const isDisabled = loading || scanStatus.scanning
const label = fullScan
? translate('resources.library.actions.fullScan')
: translate('resources.library.actions.quickScan')
const icon = fullScan ? <CachedIcon /> : <SyncIcon />
return (
<Button
onClick={handleClick}
disabled={isDisabled}
label={label}
className={className}
>
{icon}
</Button>
)
}
LibraryScanButton.propTypes = {
fullScan: PropTypes.bool.isRequired,
selectedIds: PropTypes.array,
className: PropTypes.string,
}
export default LibraryScanButton

View File

@ -23,7 +23,13 @@ const url = (command, id, options) => {
delete options.ts
}
Object.keys(options).forEach((k) => {
params.append(k, options[k])
const value = options[k]
// Handle array parameters by appending each value separately
if (Array.isArray(value)) {
value.forEach((v) => params.append(k, v))
} else {
params.append(k, value)
}
})
}
return `/rest/${command}?${params.toString()}`