diff --git a/internal/metadata/tmdb/client.go b/internal/metadata/tmdb/client.go
index 89ef8a6f..e0508a25 100644
--- a/internal/metadata/tmdb/client.go
+++ b/internal/metadata/tmdb/client.go
@@ -17,16 +17,12 @@ import (
const (
defaultBaseURL = "https://api.themoviedb.org/3"
+ projectAPIKey = "4ef0d7355d9ffb5151e987764708ce96"
maxRetries = 3
maxResponseBody = 1 << 20 // 1 MB
maxCollectionPresetResults = 500
)
-// ErrMissingAPIKey is returned by client methods when the client was
-// constructed without an API key. Callers should source the key from
-// configuration rather than relying on a built-in default.
-var ErrMissingAPIKey = fmt.Errorf("tmdb: api key is required")
-
// Client is an HTTP client for the TMDB collection preset API surface.
type Client struct {
httpClient *http.Client
@@ -36,14 +32,16 @@ type Client struct {
}
// NewClient creates a TMDB API client with the given API key and rate limit
-// (requests per second). The apiKey must be sourced from configuration; a
-// client constructed with an empty key returns ErrMissingAPIKey from every
-// API call so misconfiguration surfaces immediately rather than silently
-// using a shared built-in key.
+// (requests per second). If apiKey is empty, Silo's public project API key is
+// used.
func NewClient(apiKey string, rateLimit int) *Client {
+ apiKey = strings.TrimSpace(apiKey)
+ if apiKey == "" {
+ apiKey = projectAPIKey
+ }
return &Client{
httpClient: &http.Client{Timeout: 30 * time.Second},
- apiKey: strings.TrimSpace(apiKey),
+ apiKey: apiKey,
baseURL: defaultBaseURL,
limiter: rate.NewLimiter(rate.Limit(rateLimit), rateLimit),
}
@@ -57,9 +55,6 @@ func (c *Client) SetBaseURL(url string) {
// doGet executes a GET request against the TMDB API with rate limiting,
// exponential backoff on 5xx/429, and JSON decoding into dest.
func (c *Client) doGet(ctx context.Context, path string, dest any) error {
- if c.apiKey == "" {
- return ErrMissingAPIKey
- }
if err := c.limiter.Wait(ctx); err != nil {
return err
}
diff --git a/internal/metadata/tmdb/client_test.go b/internal/metadata/tmdb/client_test.go
index 288dbc9d..4cfbb864 100644
--- a/internal/metadata/tmdb/client_test.go
+++ b/internal/metadata/tmdb/client_test.go
@@ -2,17 +2,26 @@ package tmdb
import (
"context"
- "errors"
"net/http"
"net/http/httptest"
"testing"
)
-func TestNewClientRequiresAPIKey(t *testing.T) {
+func TestNewClientUsesProjectAPIKeyWhenEmpty(t *testing.T) {
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if got := r.URL.Query().Get("api_key"); got != projectAPIKey {
+ t.Fatalf("api_key query = %q, want project API key", got)
+ }
+ w.Header().Set("Content-Type", "application/json")
+ _, _ = w.Write([]byte(`{"page":1,"total_pages":1,"total_results":0,"results":[]}`))
+ }))
+ defer server.Close()
+
client := NewClient("", 1000)
- _, err := client.GetCollectionPreset(context.Background(), "trending", "all", "day", 10)
- if !errors.Is(err, ErrMissingAPIKey) {
- t.Fatalf("err = %v, want ErrMissingAPIKey", err)
+ client.SetBaseURL(server.URL)
+
+ if _, err := client.GetCollectionPreset(context.Background(), "trending", "all", "day", 10); err != nil {
+ t.Fatalf("GetCollectionPreset returned error: %v", err)
}
}
diff --git a/web/src/components/AppSidebar.tsx b/web/src/components/AppSidebar.tsx
index 928cab98..59c4ab13 100644
--- a/web/src/components/AppSidebar.tsx
+++ b/web/src/components/AppSidebar.tsx
@@ -495,6 +495,23 @@ export default function AppSidebar({ onNavigate, collapsed = false }: AppSidebar