A profile picture cannot be set: the legacy user-image routes are missing #165
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Setting a profile picture from the pinned web bundle does nothing: the file
dialog takes the image, the preview updates, and the avatar is never stored.
Nothing appears in the log either, because a request that matches no route is
not traced — the miss is invisible from both ends.
The bundle uploads to a spelling we do not answer.
jellyfin-apiclient'suploadUserImageposts the base64 body toUsers/{userId}/Images/{imageType}with the file's content type, anddeleteUserImagesendsDELETEto the same path.user-userprofile's imagepicker calls both. We declare
/UserImagealone —crates/ephyra-http/src/routes/library/images/mod.rs:133.Upstream answers both spellings.
Jellyfin.Api/Controllers/ImageController.csdeclares the legacy user-image routes at 159 and 182 (
POST), 251 and 274(
DELETE), and 1554 and 1610 (GET/HEAD) — each one[Obsolete("Kept for backwards compatibility")]and each one[ApiExplorerSettings(IgnoreApi = true)]. They forward to the/UserImageaction, so the behaviour is already written here; only the spellings are
missing.
The spec cannot see this class of route, so nothing here counts it.
IgnoreApikeeps them out of upstream's own generator, socrates/ephyra-dto/openapi.jsonhas noUsers/{userId}/Imagesat all, andENDPOINTS.md— which is generated from that spec — neither lists them asanswered nor as unanswered. This is the gap
ENDPOINTS.mdalready names inprose: the legacy
/Users/{userId}/…spellings the pinned bundle still calls.Sixteen of that family are served (
crates/ephyra-http/src/canonical.rs:293),and the four image ones are the hole in it.
Where to start
The three legacy paths route to the handlers already in
routes/library/images/account.rs; the user is the route parameter rather thanthe query.
canonical.rsneeds the entries too, or the methods it refuses ontheir behalf will be
axum's rather than upstream's.Worth doing in the same pass: sweep the pinned bundle for the other paths it
asks for that the spec never declared, so the next one of these is found by a
test rather than by somebody clicking.
ENDPOINTS.mdcounts 37 answered butundocumented, and that number came from reading upstream rather than from
asking the client what it calls.
How to know it is done
Choosing an image on the user profile page stores it, the avatar comes back on
the next load, and deleting it removes it — all with the pinned bundle and no
other client. A test that asks the legacy path and fails today.
House rules
CLAUDE.mdis the short version and these are the parts that bite: tests rununder
cargo nextest; upstream's source is the checkout inupstream/at thepin; generated files are regenerated rather than edited; a comment says what
the code does, not how it got there.
A test that cannot fail is worse than none.
Landed in
73e5d4e.The three legacy paths route to the handlers that were already in
routes/library/images/account.rs— the account comes off the path rather thanthe query, and the type and index are bound and ignored, as they are upstream.
canonical.rscarries both spellings with their methods, or the ones refused ontheir behalf would be axum's rather than upstream's.
ENDPOINTS.mdcounts eightmore answered-but-undocumented, which is exactly this family (37 → 45).
A test covers the round trip at the legacy spelling: nothing stored is a 404,
a POST writes the same file
/UserImagewrites, the GET serves it back, theindexed spelling answers the same picture, and a DELETE removes it.
On the sweep
The suggestion to sweep the bundle for other undeclared paths in the same pass
does not come out mechanically, and it is worth recording why rather than
leaving it looking undone:
"Users/" + userId + "/Images/" + type.grepover the minified chunks finds"Users/"29 times and nothing that resembles a whole route, so a static scancannot follow it.
bundle.json's 40distinct paths is already declared in
canonical.rs. That scenario is homescreen traffic, which is why it never carried the user-image routes.
So finding the next one of these needs more recorded traffic — the profile page,
the library dialog, the picker — rather than a scan of what is in the tree. That
is a scenario-authoring job, and it is the shape
bundle.jsonalready has.