fix(historyimport): use standard Jellyfin authorization header (#572)
Co-authored-by: OpenAI Codex (GPT-5) <codex@openai.com>
This commit is contained in:
co-authored by
OpenAI Codex
parent
383a2cce71
commit
ceb3393992
@@ -69,7 +69,7 @@ func (c *JellyfinClient) AuthenticateServerUser(ctx context.Context, baseURL, us
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("X-Emby-Authorization", jellyfinAuthorizationHeader())
|
||||
setJellyfinAuthorizationHeader(req, jellyfinAuthorizationHeader())
|
||||
var resp jellyfinServerAuthResponse
|
||||
err = c.doJSON(req, &resp)
|
||||
if err == nil {
|
||||
@@ -128,7 +128,7 @@ func (c *JellyfinClient) FetchItemsByIDs(ctx context.Context, auth jellyfinLocal
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("X-Emby-Authorization", jellyfinAuthorizationHeaderWithToken(auth.AccessToken))
|
||||
setJellyfinAuthorizationHeader(req, jellyfinAuthorizationHeaderWithToken(auth.AccessToken))
|
||||
|
||||
var payload jellyfinItemsResponse
|
||||
if err := c.doJSON(req, &payload); err != nil {
|
||||
@@ -156,7 +156,7 @@ func (c *JellyfinClient) FetchResumableItems(ctx context.Context, auth jellyfinL
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("X-Emby-Authorization", jellyfinAuthorizationHeaderWithToken(auth.AccessToken))
|
||||
setJellyfinAuthorizationHeader(req, jellyfinAuthorizationHeaderWithToken(auth.AccessToken))
|
||||
|
||||
var payload jellyfinItemsResponse
|
||||
if err := c.doJSON(req, &payload); err != nil {
|
||||
@@ -191,7 +191,7 @@ func (c *JellyfinClient) fetchPagedItems(ctx context.Context, auth jellyfinLocal
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("X-Emby-Authorization", jellyfinAuthorizationHeaderWithToken(auth.AccessToken))
|
||||
setJellyfinAuthorizationHeader(req, jellyfinAuthorizationHeaderWithToken(auth.AccessToken))
|
||||
|
||||
var payload jellyfinItemsResponse
|
||||
if err := c.doJSON(req, &payload); err != nil {
|
||||
@@ -244,13 +244,17 @@ func jellyfinAuthorizationHeaderWithToken(token string) string {
|
||||
return jellyfinAuthorizationHeader() + `, Token="` + token + `"`
|
||||
}
|
||||
|
||||
func setJellyfinAuthorizationHeader(req *http.Request, value string) {
|
||||
req.Header.Set("Authorization", value)
|
||||
}
|
||||
|
||||
// ListUsers returns all user accounts on the Jellyfin server using an admin API token.
|
||||
func (c *JellyfinClient) ListUsers(ctx context.Context, baseURL, adminToken string) ([]ExternalUser, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, strings.TrimRight(baseURL, "/")+"/Users", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("X-Emby-Authorization", jellyfinAuthorizationHeaderWithToken(adminToken))
|
||||
setJellyfinAuthorizationHeader(req, jellyfinAuthorizationHeaderWithToken(adminToken))
|
||||
var users []struct {
|
||||
ID string `json:"Id"`
|
||||
Name string `json:"Name"`
|
||||
|
||||
@@ -9,12 +9,74 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func assertJellyfinAuthorization(t *testing.T, r *http.Request, token string) {
|
||||
t.Helper()
|
||||
|
||||
want := `MediaBrowser Client="watch-importer", Device="Silo", DeviceId="silo-history-import", Version="1.0.0"`
|
||||
if token != "" {
|
||||
want += `, Token="` + token + `"`
|
||||
}
|
||||
if got := r.Header.Get("Authorization"); got != want {
|
||||
t.Fatalf("Authorization = %q, want %q", got, want)
|
||||
}
|
||||
if got := r.Header.Get("X-Emby-Authorization"); got != "" {
|
||||
t.Fatalf("X-Emby-Authorization = %q, want empty", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJellyfinAuthenticateServerUser_UsesStandardAuthorizationHeader(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if got := r.URL.Path; got != "/Users/AuthenticateByName" {
|
||||
t.Fatalf("path = %q, want /Users/AuthenticateByName", got)
|
||||
}
|
||||
assertJellyfinAuthorization(t, r, "")
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, _ = w.Write([]byte(`{"AccessToken":"user-token","User":{"Id":"user-1"}}`))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
client := NewJellyfinClient()
|
||||
auth, err := client.AuthenticateServerUser(context.Background(), server.URL, "alice", "password")
|
||||
if err != nil {
|
||||
t.Fatalf("AuthenticateServerUser returned error: %v", err)
|
||||
}
|
||||
if auth.UserID != "user-1" || auth.AccessToken != "user-token" {
|
||||
t.Fatalf("auth = %+v, want user-1 with user-token", auth)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJellyfinListUsers_UsesStandardAuthorizationHeader(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if got := r.URL.Path; got != "/Users" {
|
||||
t.Fatalf("path = %q, want /Users", got)
|
||||
}
|
||||
assertJellyfinAuthorization(t, r, "admin-token")
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, _ = w.Write([]byte(`[{"Id":"user-1","Name":"Alice"},{"Id":"","Name":"Missing ID"}]`))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
client := NewJellyfinClient()
|
||||
users, err := client.ListUsers(context.Background(), server.URL, "admin-token")
|
||||
if err != nil {
|
||||
t.Fatalf("ListUsers returned error: %v", err)
|
||||
}
|
||||
if len(users) != 1 || users[0].ID != "user-1" || users[0].Name != "Alice" {
|
||||
t.Fatalf("users = %+v, want Alice (user-1)", users)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJellyfinFetchResumableItems_IncludesExpectedQueryAndPaginates(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
requests := 0
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
requests++
|
||||
assertJellyfinAuthorization(t, r, "token-1")
|
||||
|
||||
if got := r.URL.Path; got != "/UserItems/Resume" {
|
||||
t.Fatalf("path = %q, want /UserItems/Resume", got)
|
||||
@@ -71,6 +133,7 @@ func TestJellyfinFetchItems_PaginatesPlayedItems(t *testing.T) {
|
||||
requests := 0
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
requests++
|
||||
assertJellyfinAuthorization(t, r, "token-1")
|
||||
|
||||
if got := r.URL.Path; got != "/Users/user-1/Items" {
|
||||
t.Fatalf("path = %q, want /Users/user-1/Items", got)
|
||||
|
||||
Reference in New Issue
Block a user