diff --git a/sample.env b/sample.env index 360232b..68511ba 100644 --- a/sample.env +++ b/sample.env @@ -8,6 +8,8 @@ # DISCOVERY_SERVICE=listenbrainz # Your ListenBrainz username LISTENBRAINZ_USER= +# User token from ListenBrainz +LISTENBRAINZ_USER_TOKEN= # 'playlist' to fetch weekly playlist (50 songs), 'api' for fewer songs (good for testing) (default: playlist) # LISTENBRAINZ_DISCOVERY=playlist # Size of the cover art downloaded from coverartarchive (250 or 500) (default: 250) diff --git a/src/config/config.go b/src/config/config.go index 8eabc58..512c6a9 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -159,6 +159,7 @@ type DiscoveryConfig struct { type Listenbrainz struct { Discovery string `env:"LISTENBRAINZ_DISCOVERY" env-default:"playlist"` User string `env:"LISTENBRAINZ_USER"` + UserToken string `env:"LISTENBRAINZ_USER_TOKEN"` ImportPlaylist string SingleArtist bool `env:"SINGLE_ARTIST" env-default:"true"` CoverArtSize string `env:"COVER_ART_SIZE" env-default:"250"` @@ -261,6 +262,10 @@ func (cfg *Config) HandleDeprecation() { // slog.Warn("--persist flag now only handles playlist deletion, use toggle in UI or --clean-downloads to delete tracks") } + if cfg.DiscoveryCfg.Listenbrainz.UserToken == "" { + slog.Warn("Provide a ListenBrainz user token via Wizard or env (LISTENBRAINZ_USER_TOKEN)") + } + if cfg.Flags.CleanDownloads && !cfg.DownloadCfg.UseSubDir { slog.Warn("Deleting tracks requires 'USE_SUBDIRECTORY' to be true") } diff --git a/src/discovery/listenbrainz.go b/src/discovery/listenbrainz.go index f164806..c7f3509 100644 --- a/src/discovery/listenbrainz.go +++ b/src/discovery/listenbrainz.go @@ -182,13 +182,20 @@ type MBRecording struct { type ListenBrainz struct { HttpClient *util.HttpClient + Headers map[string]string cfg cfg.Listenbrainz Separator string } func NewListenBrainz(cfg cfg.DiscoveryConfig, httpClient *util.HttpClient) *ListenBrainz { + authHeader := make(map[string]string) + if cfg.Listenbrainz.UserToken != "" { + authHeader["Authorization"] = fmt.Sprintf("Token %s", cfg.Listenbrainz.UserToken) + } + return &ListenBrainz{ cfg: cfg.Listenbrainz, + Headers: authHeader, HttpClient: httpClient, } } @@ -725,7 +732,7 @@ func (c *ListenBrainz) parsePlaylist(identifier string, singleArtist bool) (stri func (c *ListenBrainz) lbRequest(path string) ([]byte, error) { reqURL := fmt.Sprintf("https://api.listenbrainz.org/1/%s", path) - body, err := c.HttpClient.MakeRequest("GET", reqURL, nil, nil) + body, err := c.HttpClient.MakeRequest("GET", reqURL, nil, c.Headers) if err != nil { return nil, fmt.Errorf("failed to make request to ListenBrainz API: %s", err) } diff --git a/src/web/backend/defs/defs.go b/src/web/backend/defs/defs.go index 84fe591..5b4756d 100644 --- a/src/web/backend/defs/defs.go +++ b/src/web/backend/defs/defs.go @@ -181,5 +181,5 @@ var AllConfigKeys = []string{ "DOWNLOAD_DIR", "USE_SUBDIRECTORY", "PATH_TEMPLATE", "ENRICH_TRACK_METADATA", "DOWNLOAD_SERVICES", "YOUTUBE_API_KEY", "TRACK_EXTENSION", "FILTER_LIST", "SLSKD_URL", "SLSKD_API_KEY", - "WIZARD_COMPLETE", "MIGRATE_DOWNLOADS", "EXTENSIONS", + "WIZARD_COMPLETE", "MIGRATE_DOWNLOADS", "EXTENSIONS", "LISTENBRAINZ_USER_TOKEN", } \ No newline at end of file diff --git a/src/web/backend/settings/handlers.go b/src/web/backend/settings/handlers.go index 3225673..6ca56f9 100644 --- a/src/web/backend/settings/handlers.go +++ b/src/web/backend/settings/handlers.go @@ -195,6 +195,7 @@ func (s *Settings) HandleSaveEnrichMetadata(w http.ResponseWriter, r *http.Reque func (s *Settings) HandleWizardStep1(w http.ResponseWriter, r *http.Request) { var body struct { User string `json:"user"` + UserToken string `json:"userToken"` Playlists []string `json:"playlists"` DiscoveryMode string `json:"discovery_mode"` } @@ -214,6 +215,7 @@ func (s *Settings) HandleWizardStep1(w http.ResponseWriter, r *http.Request) { updates := map[string]string{ "LISTENBRAINZ_USER": body.User, + "LISTENBRAINZ_USER_TOKEN": body.UserToken, "LISTENBRAINZ_DISCOVERY": body.DiscoveryMode, } for name, def := range defs.PlaylistDefs { diff --git a/src/web/frontend/src/components/Wizard.jsx b/src/web/frontend/src/components/Wizard.jsx index ab9fe82..2744d8c 100644 --- a/src/web/frontend/src/components/Wizard.jsx +++ b/src/web/frontend/src/components/Wizard.jsx @@ -46,9 +46,9 @@ const PLAYLISTS = [ ]; function Step1({ fields, setField, envSources, onNext, saving }) { - const { user, discoveryMode, checked } = fields + const { user, userToken, discoveryMode, checked } = fields const isLocked = key => envSources[key] === 'env' - const valid = user.trim() !== '' && (discoveryMode !== 'playlist' || Object.values(checked).some(Boolean)) + const valid = user.trim() !== '' && userToken.trim() !== '' && (discoveryMode !== 'playlist' || Object.values(checked).some(Boolean)) return (