105 lines
2.5 KiB
Go
105 lines
2.5 KiB
Go
// Package library manages the music library and its configuration.
|
|
package library
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
)
|
|
|
|
var (
|
|
errNotDirectory = errors.New("path is not a directory")
|
|
errUnknownScanConcurrency = errors.New("unknown scan concurrency mode")
|
|
)
|
|
|
|
// ScanConcurrency controls how many parallel workers the scanner
|
|
// uses for metadata extraction. The choice directly affects I/O
|
|
// throughput on spinning disks vs SSDs.
|
|
type ScanConcurrency string
|
|
|
|
// Valid ScanConcurrency modes.
|
|
const (
|
|
// ScanConcurrencyAuto detects whether the library resides on
|
|
// a rotational disk and chooses workers accordingly.
|
|
ScanConcurrencyAuto ScanConcurrency = "auto"
|
|
|
|
// ScanConcurrencySSD uses runtime.NumCPU() workers, maximising
|
|
// throughput on solid-state storage.
|
|
ScanConcurrencySSD ScanConcurrency = "ssd"
|
|
|
|
// ScanConcurrencyHDD uses a small number of workers to limit
|
|
// I/O contention on spinning disks.
|
|
ScanConcurrencyHDD ScanConcurrency = "hdd"
|
|
)
|
|
|
|
// DefaultScanConcurrency is the mode used when no value is
|
|
// configured.
|
|
const DefaultScanConcurrency = ScanConcurrencyAuto
|
|
|
|
// Config holds Library config data.
|
|
type Config struct {
|
|
DirectoryPath Directory `toml:"DirectoryPath"`
|
|
ScanConcurrency ScanConcurrency `toml:"ScanConcurrency"`
|
|
}
|
|
|
|
// Directory represents a filesystem path to a music directory.
|
|
type Directory string
|
|
|
|
// NewConfig creates a validated library configuration.
|
|
func NewConfig(dir string) (*Config, error) {
|
|
config := &Config{
|
|
DirectoryPath: Directory(dir),
|
|
}
|
|
if err := config.Validate(); err != nil {
|
|
return nil, fmt.Errorf(
|
|
"validation error for new library config: %w",
|
|
err,
|
|
)
|
|
}
|
|
|
|
return config, nil
|
|
}
|
|
|
|
// ApplyDefaults fills zero-value fields with sensible defaults.
|
|
func (c *Config) ApplyDefaults() {
|
|
if c.ScanConcurrency == "" {
|
|
c.ScanConcurrency = DefaultScanConcurrency
|
|
}
|
|
}
|
|
|
|
// Validate checks that the configured directory exists and that
|
|
// the scan concurrency mode is recognised.
|
|
func (c *Config) Validate() error {
|
|
c.ApplyDefaults()
|
|
|
|
if len(c.DirectoryPath) != 0 {
|
|
dirInfo, err := os.Stat(string(c.DirectoryPath))
|
|
if err != nil {
|
|
return fmt.Errorf(
|
|
"problem getting info on library dir (%s): %w",
|
|
c.DirectoryPath, err,
|
|
)
|
|
}
|
|
|
|
if !dirInfo.IsDir() {
|
|
return fmt.Errorf(
|
|
"%s: %w", c.DirectoryPath, errNotDirectory,
|
|
)
|
|
}
|
|
}
|
|
|
|
switch c.ScanConcurrency {
|
|
case ScanConcurrencyAuto,
|
|
ScanConcurrencySSD,
|
|
ScanConcurrencyHDD:
|
|
// Valid.
|
|
default:
|
|
return fmt.Errorf(
|
|
"%w: %q", errUnknownScanConcurrency,
|
|
c.ScanConcurrency,
|
|
)
|
|
}
|
|
|
|
return nil
|
|
}
|