A background Fingerprinter runs after every scan and at startup, one file at a time, filling media_files.sha256 and track_fingerprints (fpcalc raw, first 120 s). Without fpcalc only SHA-256 is computed and files stay queued for Chromaprint. Changing a file's content resets its prints. Similarity() compares raw prints with a ±80-point alignment search; it is the basis for duplicate detection in the upload queue. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
84 lines
4.6 KiB
Go
84 lines
4.6 KiB
Go
// Package config читает конфигурацию сервиса из переменных окружения.
|
||
package config
|
||
|
||
import (
|
||
"fmt"
|
||
"time"
|
||
|
||
"github.com/caarlos0/env/v11"
|
||
)
|
||
|
||
// API — конфигурация публичного сервиса api.
|
||
type API struct {
|
||
HTTPAddr string `env:"HTTP_ADDR" envDefault:":8080"`
|
||
DatabaseURL string `env:"DATABASE_URL,required"`
|
||
RedisURL string `env:"REDIS_URL,required"`
|
||
LogLevel string `env:"LOG_LEVEL" envDefault:"info"`
|
||
MigrateOnStart bool `env:"MIGRATE_ON_START" envDefault:"true"`
|
||
ShutdownTimeout time.Duration `env:"SHUTDOWN_TIMEOUT" envDefault:"15s"`
|
||
|
||
SupportedLocales []string `env:"SUPPORTED_LOCALES" envDefault:"ru,en" envSeparator:","`
|
||
MinAndroidVersionCode int `env:"MIN_ANDROID_VERSION_CODE" envDefault:"0"`
|
||
|
||
// JWTPrivateKey — Ed25519 (base64: 32-байтовый seed или 64-байтовый ключ). Пусто — случайный ключ
|
||
// на время жизни процесса: после перезапуска всем придётся обновить access-токен (refresh остаётся).
|
||
JWTPrivateKey string `env:"JWT_PRIVATE_KEY"`
|
||
// JWTPreviousKeys — прежние ключи через запятую: токены, подписанные ими, ещё принимаются.
|
||
JWTPreviousKeys []string `env:"JWT_PREVIOUS_KEYS" envSeparator:","`
|
||
AccessTokenTTL time.Duration `env:"ACCESS_TOKEN_TTL" envDefault:"15m"`
|
||
// CookieSecure — флаг Secure у refresh-cookie. Отключать только для разработки по http не на localhost.
|
||
CookieSecure bool `env:"COOKIE_SECURE" envDefault:"true"`
|
||
|
||
BootstrapAdminUsername string `env:"BOOTSTRAP_ADMIN_USERNAME" envDefault:"admin"`
|
||
BootstrapAdminPassword string `env:"BOOTSTRAP_ADMIN_PASSWORD"`
|
||
|
||
// LibraryRoot — точки монтирования NFS-хранилищ (общий каталог с storage-agent).
|
||
LibraryRoot string `env:"LIBRARY_ROOT" envDefault:"/library"`
|
||
// LibraryHostRoot — LIBRARY_HOST внутри контейнера: local-хранилища — его подпапки.
|
||
LibraryHostRoot string `env:"LIBRARY_HOST_ROOT" envDefault:"/library-host"`
|
||
StorageAgentURL string `env:"STORAGE_AGENT_URL" envDefault:"http://storage-agent:8090"`
|
||
InternalToken string `env:"INTERNAL_TOKEN"`
|
||
FFprobePath string `env:"FFPROBE_PATH" envDefault:"ffprobe"`
|
||
// FPCalcPath — fpcalc (Chromaprint) для акустических отпечатков; нет бинарника — считается только SHA-256.
|
||
FPCalcPath string `env:"FPCALC_PATH" envDefault:"fpcalc"`
|
||
|
||
// SigningKey — ключ подписи ссылок на обложки и стрим (base64, ≥ 16 байт). Пусто — случайный до перезапуска.
|
||
SigningKey string `env:"STREAM_SIGNING_KEY"`
|
||
CacheDir string `env:"CACHE_DIR" envDefault:"/cache"`
|
||
FFmpegPath string `env:"FFMPEG_PATH" envDefault:"ffmpeg"`
|
||
// XAccel — файлы отдаёт nginx по X-Accel-Redirect; false — для запуска api без nginx.
|
||
XAccel bool `env:"X_ACCEL" envDefault:"true"`
|
||
// TranscodeConcurrency — параллельных ffmpeg; 0 — половина ядер.
|
||
TranscodeConcurrency int `env:"TRANSCODE_CONCURRENCY" envDefault:"0"`
|
||
TranscodeCacheLimitGB int `env:"TRANSCODE_CACHE_LIMIT_GB" envDefault:"50"`
|
||
|
||
RecommenderURL string `env:"RECOMMENDER_URL" envDefault:"http://recommender:8000"`
|
||
|
||
MeiliURL string `env:"MEILI_URL" envDefault:"http://meilisearch:7700"`
|
||
MeiliMasterKey string `env:"MEILI_MASTER_KEY"`
|
||
|
||
// Самообновление Android: сервер читает релизы Gitea своим токеном и кэширует APK в UpdatesDir.
|
||
GiteaURL string `env:"GITEA_URL"`
|
||
GiteaRepo string `env:"GITEA_REPO"`
|
||
GiteaToken string `env:"GITEA_TOKEN"`
|
||
UpdatesDir string `env:"UPDATES_DIR" envDefault:"/updates"`
|
||
}
|
||
|
||
// StorageAgent — конфигурация внутреннего сервиса storage-agent.
|
||
type StorageAgent struct {
|
||
HTTPAddr string `env:"HTTP_ADDR" envDefault:":8090"`
|
||
InternalToken string `env:"INTERNAL_TOKEN,required,notEmpty"`
|
||
LibraryRoot string `env:"LIBRARY_ROOT" envDefault:"/library"`
|
||
LogLevel string `env:"LOG_LEVEL" envDefault:"info"`
|
||
ShutdownTimeout time.Duration `env:"SHUTDOWN_TIMEOUT" envDefault:"15s"`
|
||
}
|
||
|
||
// Load заполняет структуру конфигурации T из окружения.
|
||
func Load[T any]() (T, error) {
|
||
var cfg T
|
||
if err := env.Parse(&cfg); err != nil {
|
||
return cfg, fmt.Errorf("config: %w", err)
|
||
}
|
||
return cfg, nil
|
||
}
|