diff --git a/config/slskd.example.yml b/config/slskd.example.yml
index 9cd9584fd..c73118e57 100644
--- a/config/slskd.example.yml
+++ b/config/slskd.example.yml
@@ -192,6 +192,7 @@
# password: ~
# description: |
# A slskd user. https://github.com/slskd/slskd
+# picture: path/to/slsk-profile-picture.jpg
# listen_ip_address: 0.0.0.0
# listen_port: 50300
# diagnostic_level: Info
diff --git a/docs/config.md b/docs/config.md
index 92798196d..c83e26c77 100644
--- a/docs/config.md
+++ b/docs/config.md
@@ -680,14 +680,19 @@ soulseek:
## Other
+Users can configure "profile" information for other users on the network to view, including a description and a photo. Note that Soulseek NS doesn't support .PNG,
+so formats .JPG/.JPEG, .GIF, and .BMP are advised.
+
| Command-Line | Environment Variable | Description |
| -------------------- | -------------------------- | --------------------------------------------- |
| `--slsk-description` | `SLSKD_SLSK_DESCRIPTION` | The user description for the Soulseek network |
+| `--slsk-picture` | `SLSKD_SLSK_PICTURE` | The user picture for the Soulseek network |
#### **YAML**
```yaml
soulseek:
description: A slskd user. https://github.com/slskd/slskd
+ picture: path/to/slsk-profile-picture.jpg
```
## Connection Options
diff --git a/src/slskd/Application.cs b/src/slskd/Application.cs
index e56814424..9179987ea 100644
--- a/src/slskd/Application.cs
+++ b/src/slskd/Application.cs
@@ -1662,13 +1662,32 @@ private void State_OnChange((State Previous, State Current) state)
/// A Task resolving the UserInfo instance.
private async Task UserInfoResolver(string username, IPEndPoint endpoint)
{
+ byte[] pictureBytes = null;
+
+ if (!string.IsNullOrWhiteSpace(Options.Soulseek.Picture))
+ {
+ try
+ {
+ // note: the Picture setting is validated at startup to ensure it exists and that
+ // it is readable
+ pictureBytes = await System.IO.File.ReadAllBytesAsync(Options.Soulseek.Picture);
+ }
+ catch (Exception ex)
+ {
+ // this isn't a serious enough problem to prevent us from continuing, so we'll just
+ // log a warning and continue, omitting the picture
+ Log.Warning("Failed to read Soulseek picture {Picture}: {Message}", Options.Soulseek.Picture, ex.Message);
+ }
+ }
+
if (Users.IsBlacklisted(username, endpoint.Address))
{
return new UserInfo(
description: Options.Soulseek.Description,
uploadSlots: 0,
queueLength: int.MaxValue,
- hasFreeUploadSlot: false);
+ hasFreeUploadSlot: false,
+ picture: pictureBytes);
}
try
@@ -1688,11 +1707,13 @@ private async Task UserInfoResolver(string username, IPEndPoint endpoi
// i want to know how many slots they have, which gives me an idea of how fast their
// queue moves, and the length of the queue *ahead of me*, meaning how long i'd have to
// wait until my first download starts.
+ // revisited 3 years later: why was it important to leave this comment??
var info = new UserInfo(
description: Options.Soulseek.Description,
uploadSlots: group.Slots,
queueLength: forecastedPosition,
- hasFreeUploadSlot: forecastedPosition == 0);
+ hasFreeUploadSlot: forecastedPosition == 0,
+ picture: pictureBytes);
return info;
}
diff --git a/src/slskd/Core/Options.cs b/src/slskd/Core/Options.cs
index 266d04aa0..cc6624443 100644
--- a/src/slskd/Core/Options.cs
+++ b/src/slskd/Core/Options.cs
@@ -1531,6 +1531,15 @@ public class SoulseekOptions
[Description("user description for the Soulseek network")]
public string Description { get; init; } = "A slskd user. https://github.com/slskd/slskd";
+ ///
+ /// Gets the file path for the user's profile picture.
+ ///
+ [Argument(default, "slsk-picture")]
+ [EnvironmentVariable("SLSK_PICTURE")]
+ [Description("user picture for the Soulseek network")]
+ [FileExists(FileAccess.Read)]
+ public string Picture { get; init; } = null;
+
///
/// Gets the local IP address on which to listen for incoming connections.
///
diff --git a/tests/slskd.Tests.Unit/Users/UserServiceTests.cs b/tests/slskd.Tests.Unit/Users/UserServiceTests.cs
index 1fd21c325..ca45d5b9d 100644
--- a/tests/slskd.Tests.Unit/Users/UserServiceTests.cs
+++ b/tests/slskd.Tests.Unit/Users/UserServiceTests.cs
@@ -2,7 +2,6 @@
{
using System.Collections.Generic;
using AutoFixture.Xunit2;
- using Microsoft.EntityFrameworkCore;
using Moq;
using slskd.Users;
using Soulseek;
@@ -120,7 +119,7 @@ public void Gives_Lowest_Priority_Group_To_Users_Appearing_In_Multiple_Groups(st
}
}
- private static (UserService governor, Mocks mocks) GetFixture(Options options = null)
+ private static (UserService service, Mocks mocks) GetFixture(Options options = null)
{
var mocks = new Mocks(options);
var service = new UserService(