Compare commits

..

1 Commits

Author SHA1 Message Date
Senko-san c7e078d758 feat(config): derive MusicBrainz/AcoustID User-Agent from app name+version
Docker Build & Publish / build (push) Successful in 1m8s
Docker Build & Publish / push (push) Failing after 6s
Docker Build & Publish / Prune old image versions (push) Has been skipped
Replace the placeholder MUSICBRAINZ_USER_AGENT env var with
MUSICBRAINZ_OWNER_EMAIL. The User-Agent ("MCMA/<version> ( <contact> )")
is now composed from the fixed app name, the installed package version,
and the operator's contact email — falling back to the project URL when
no email is configured. Also use the same version for the FastAPI app.
2026-06-11 00:39:24 +03:00
3 changed files with 33 additions and 4 deletions
+2 -1
View File
@@ -37,5 +37,6 @@ MAX_PARALLEL_DOWNLOADS=2
# external services (all optional — backend degrades gracefully if unset)
# ML_SERVICE_URL=http://ml:9000
# ACOUSTID_API_KEY=
MUSICBRAINZ_USER_AGENT=mcma-backend/0.1.0 ( https://github.com/your/repo )
# Sent to MusicBrainz/AcoustID as part of the User-Agent (MCMA/<version> ( <email> )).
# MUSICBRAINZ_OWNER_EMAIL=you@example.com
# YOUTUBE_COOKIES_PATH=/data/cookies.txt
+29 -1
View File
@@ -5,12 +5,25 @@ development). Access the cached singleton via :func:`get_settings`.
"""
from functools import lru_cache
from importlib.metadata import PackageNotFoundError, version
from pathlib import Path
from typing import Literal
from pydantic import Field, SecretStr, field_validator
from pydantic_settings import BaseSettings, SettingsConfigDict
# App identity for outbound API calls (e.g. the MusicBrainz/AcoustID
# User-Agent). Name is fixed; version comes from the installed package.
APP_NAME = "MCMA"
_PROJECT_URL = "https://git.ollyhearn.ru/olly/mcma-backend"
def app_version() -> str:
try:
return version("mcma-backend")
except PackageNotFoundError:
return "0.0.0"
class Settings(BaseSettings):
model_config = SettingsConfigDict(
@@ -77,7 +90,12 @@ class Settings(BaseSettings):
ml_service_url: str | None = None
acoustid_api_key: SecretStr | None = None
acoustid_api_url: str = "https://api.acoustid.org/v2/lookup"
musicbrainz_user_agent: str = "mcma-backend/0.1.0 ( https://github.com/your/repo )"
# MusicBrainz/AcoustID require a meaningful User-Agent identifying the
# application and a way to contact its maintainer (see
# https://musicbrainz.org/doc/XML_Web_Service/Rate_Limiting). Self-hosted
# deployments should set their own contact email; see
# ``musicbrainz_user_agent`` below for how it's used.
musicbrainz_owner_email: str | None = None
youtube_cookies_path: Path | None = None
# -- enrichment -------------------------------------------------------
@@ -96,6 +114,16 @@ class Settings(BaseSettings):
def is_prod(self) -> bool:
return self.environment == "prod"
@property
def musicbrainz_user_agent(self) -> str:
"""User-Agent sent to MusicBrainz/AcoustID: ``MCMA/<version> ( <contact> )``.
Falls back to the project URL if the deployment hasn't set
``musicbrainz_owner_email``.
"""
contact = self.musicbrainz_owner_email or _PROJECT_URL
return f"{APP_NAME}/{app_version()} ( {contact} )"
@lru_cache
def get_settings() -> Settings:
+2 -2
View File
@@ -10,7 +10,7 @@ from app.api.health import router as health_router
from app.api.middleware import CorrelationIdMiddleware
from app.api.rest import subsonic_router
from app.api.v1 import api_v1_router
from app.core.config import get_settings
from app.core.config import app_version, get_settings
from app.core.logging import configure_logging, get_logger
from app.infrastructure.cache import close_redis
from app.infrastructure.db import dispose_engine
@@ -34,7 +34,7 @@ def create_app() -> FastAPI:
app = FastAPI(
title="mcma-backend",
version="0.1.0",
version=app_version(),
summary="Self-hosted, offline-first music service.",
lifespan=lifespan,
)