Skip to content

Added ProfileImageFormat support#174

Merged
crobibero merged 4 commits into
jellyfin:masterfrom
CEbbinghaus:feat/ExpandProfilePictureSupport
Mar 14, 2025
Merged

Added ProfileImageFormat support#174
crobibero merged 4 commits into
jellyfin:masterfrom
CEbbinghaus:feat/ExpandProfilePictureSupport

Conversation

@CEbbinghaus

Copy link
Copy Markdown
Contributor

This PR adds a enum ProfileImageFormat allowing the admin to specify whichever format the profile image should be in.

It also has a default value which will try and automatically determine which format the ProfileImage is in to simplify setup and maximize "Just Works" factor

There is an existing PR #166 with similar albeit incomplete functionality which the author has neglected to update for quite some time. Prompting me to raise my own.

return ProfileImageFormat.Url;
}

throw new InvalidFormatException($"Image attribute was a valid URI but had an invalid Scheme");

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the part I am least confident in. We shouldn't support any url with non http/https schema given we are making a request via a HTTP client. If however there are other schemas that the HttpClient supports (ftp & co), this default mode will fail even if the request might have succeeded. I am content given that if they have a non http url they can just manually change the format to Url which ignores this logic altogether and just lets the HttpClient fail with whatever it got given but i don't know is that is behavior the maintainers want.

Some feedback from both maintainers and users would be greatly appreciated.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am content given that if they have a non http url they can just manually change the format to Url

I feel like that's honestly the best way to do it. There would also need to be a change in documentation to let the user's know that though. Maybe added to the footer of the input?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can do 👍

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should give some context to what failed, this error message itself isn't very helpful

{
_logger = logger;
_applicationHost = applicationHost;
_httpClient = new(() => httpClientFactory.CreateClient());

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Create the HttpClient when needed, not once. This class's lifetime is the Jellyfin server's lifetime.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh so we want to create 1 client per login? I thought it smarter to preserve the same client throughout the plugin lifetime.

Happy to change just wanted to utilize the least resources required

return ProfileImageFormat.Url;
}

throw new InvalidFormatException($"Image attribute was a valid URI but had an invalid Scheme");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should give some context to what failed, this error message itself isn't very helpful

@CEbbinghaus

Copy link
Copy Markdown
Contributor Author

Done @crobibero, Would you mind re-reviewing?

@CEbbinghaus CEbbinghaus requested a review from crobibero March 7, 2025 05:53
}

private HttpClient HttpClient => _httpClient.Value;
private HttpClient HttpClient => _httpClientFactory.CreateClient();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private HttpClient HttpClient => _httpClientFactory.CreateClient();
private HttpClient HttpClient => _httpClientFactory.CreateClient(NamedClient.Default);

Might need a new using statement at the top

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added it. Did need a using statement.

@CEbbinghaus CEbbinghaus requested a review from crobibero March 9, 2025 00:19
@CEbbinghaus

Copy link
Copy Markdown
Contributor Author

done and ready for re-review @crobibero. Sorry for the delay I didn't see the review until it was already time for bed

@crobibero crobibero merged commit 222cbb7 into jellyfin:master Mar 14, 2025
@DuvelCorp

DuvelCorp commented Mar 19, 2025

Copy link
Copy Markdown

Any guidance on how to configure this with an Authentik LDAP? Would that even work in the actual state?

Tried a few things but nothing work.

First I am not sure what I have to specify on LDAP Profile Image Attribute
Tried with attributes.avatar and avatar but it doesnt work (this is the fields names I see in Authentik)

[11:07:31] [WRN] [76] Jellyfin.Plugin.LDAP_Auth.LdapAuthenticationProviderPlugin: LDAP attribute attributes.avatar not found for user cn=tomtest,ou=users,dc=ldap,dc=goauthentik,dc=io

And I really dont know if specific config should be done on Authentik LDAP configuration to deal with it. We are missing doc or I did not find it (and I searched!)

Txs

@nanospearing

Copy link
Copy Markdown

Any guidance on how to configure this with an Authentik LDAP? Would that even work in the actual state?

Tried a few things but nothing work.

First I am not sure what I have to specify on LDAP Profile Image Attribute Tried with attributes.avatar and avatar but it doesnt work (this is the fields names I see in Authentik)

[11:07:31] [WRN] [76] Jellyfin.Plugin.LDAP_Auth.LdapAuthenticationProviderPlugin: LDAP attribute attributes.avatar not found for user cn=tomtest,ou=users,dc=ldap,dc=goauthentik,dc=io

And I really dont know if specific config should be done on Authentik LDAP configuration to deal with it. We are missing doc or I did not find it (and I searched!)

Txs

Do you have a discord? I can help you out since I currently use authentik :3

@epireyn

epireyn commented Mar 31, 2025

Copy link
Copy Markdown

Sorry for bumping this PR, but I believe context can help.

While checking the merged commits, I noticed that the synchronization tasked hasn't been updated. Tests on my end with a url demonstrated the lack of implementation (it saves a .jpg containing the url as text).

@CEbbinghaus

Copy link
Copy Markdown
Contributor Author

While checking the merged commits, I noticed that the synchronization tasked hasn't been updated. Tests on my end with a url demonstrated the lack of implementation (it saves a .jpg containing the url as text).

I am not quite sure what you mean with this? Are you trying to say that when setting the mode to URL and providing a url it actually saves the url itself into a file ending with the .jpg extension?

@epireyn

epireyn commented Apr 1, 2025

Copy link
Copy Markdown

Yes, exactly. And I think it may be because the implementation of the synchronization task (there is a dedicated file and object) has not been updated (it does not adapt to the type, but treats everything as bytes)

@CEbbinghaus

Copy link
Copy Markdown
Contributor Author

Very strange According to the implementation I wrote it should make a http request and write whatever it responds with to the image:

ProfileImageFormat.Url => await HttpClient.GetByteArrayAsync(profileImageAttr.StringValue).ConfigureAwait(false),

GetByteArrayAsync docs say that it makes a GET request and returns the response body as a byte stream.

This would indicate that your http url returns as its response body a url. Can you confirm that this is the case?

I suspect it may be a N+1 problem where GET URL_A returns URL_B and GET URL_B returns the image. (think S3 or co). But this is a highly convoluted setup.

@epireyn can you confirm for me what the url returns?

@epireyn

epireyn commented Apr 1, 2025

Copy link
Copy Markdown

It returns the image in the body. Actually, the url is the image name (and probably relative path).

According to the implementation I wrote it should make a http request and write whatever it responds with to the image:

You implemented the URL for the authentification provider, when a new user is created in Jellyfin (because inexistent). My user already exists, and thus the only way to update the profile picture is through the dedicated scheduled task, defined here. As you can see in the following snippet, the code was not updated accordingly with your changes.

https://github.com/jellyfin/jellyfin-plugin-ldapauth/blob/222cbb71e153913fdfb64ebbb4d5a33be30bcfa3/LDAP-Auth/LdapProfileImageSyncTask.cs#L102C17-L123C18

@CEbbinghaus

Copy link
Copy Markdown
Contributor Author

Omg. You are so right. I knew that the code I had written was specifically for the first login but I hadn't realized that there was more. Let me fix that. My bad.

@CEbbinghaus CEbbinghaus deleted the feat/ExpandProfilePictureSupport branch April 19, 2025 02:58
@CEbbinghaus CEbbinghaus restored the feat/ExpandProfilePictureSupport branch May 27, 2026 14:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants