Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions DiscordBee.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ public partial class Plugin
private SettingsWindow _settingsWindow;
private readonly Timer _updateTimer = new Timer(300);
private string _imgurAssetCachePath;
private string _imgurAlbum;

public Plugin()
{
Expand Down Expand Up @@ -78,15 +77,14 @@ public PluginInfo Initialise(IntPtr apiInterfacePtr)

var workingDir = _mbApiInterface.Setting_GetPersistentStoragePath() + _about.Name;
var settingsFilePath = $"{workingDir}\\{_about.Name}.settings";
_imgurAssetCachePath = $"{workingDir}\\{_about.Name}-Imgur.cache";
_imgurAlbum = $"{workingDir}\\{_about.Name}-Imgur.album";
_imgurAssetCachePath = $"{workingDir}\\{_about.Name}-ImgBB.cache";

_settings = Settings.GetInstance(settingsFilePath);
_settings.SettingChanged += SettingChangedCallback;

_discordClient.ArtworkUploadEnabled = _settings.UploadArtwork;
_discordClient.DiscordId = _settings.DiscordAppId;
UpdateAssetManager(_imgurAssetCachePath, new ImgurUploader(_imgurAlbum, _settings.ImgurClientId));
UpdateAssetManager(_imgurAssetCachePath, new ImgBBUploader(_settings.ImgBBApiKey));
ToolStripMenuItem mainMenuItem = (ToolStripMenuItem)_mbApiInterface.MB_AddMenuItem($"mnuTools/{_about.Name}", null, null);
mainMenuItem.DropDown.Items.Add("Uploader Health", null, ShowUploaderHealth);

Expand Down Expand Up @@ -138,9 +136,9 @@ private void SettingChangedCallback(object sender, Settings.SettingChangedEventA
{
_discordClient.ArtworkUploadEnabled = _settings.UploadArtwork;
}
if (e.SettingProperty.Equals("ImgurClientId"))
if (e.SettingProperty.Equals("ImgBBApiKey"))
{
UpdateAssetManager(_imgurAssetCachePath, new ImgurUploader(_imgurAlbum, _settings.ImgurClientId));
UpdateAssetManager(_imgurAssetCachePath, new ImgBBUploader(_settings.ImgBBApiKey));
}
}

Expand Down
16 changes: 3 additions & 13 deletions DiscordBee.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -96,16 +96,11 @@
<Compile Include="DiscordTools\Assets\UploaderHealthInfo.cs" />
<Compile Include="DiscordTools\Assets\Uploader\CachingUploader.cs" />
<Compile Include="DiscordTools\Assets\Uploader\DelegatingUploader.cs" />
<Compile Include="DiscordTools\Assets\Uploader\ImgurUploader.cs" />
<Compile Include="DiscordTools\Assets\Uploader\ImgBBUploader.cs" />
<Compile Include="DiscordTools\Assets\Uploader\ResizingUploader.cs" />
<Compile Include="DiscordTools\Assets\UploadResult.cs" />
<Compile Include="DiscordTools\DiscordClient.cs" />
<Compile Include="ImgurClient\RateLimitHandler.cs" />
<Compile Include="ImgurClient\Types\ImgurImage.cs" />
<Compile Include="ImgurClient\Types\ImgurAlbum.cs" />
<Compile Include="ImgurClient\ImgurAuthenticator.cs" />
<Compile Include="ImgurClient\ImgurClient.cs" />
<Compile Include="ImgurClient\Types\ImgurResponse.cs" />

<Compile Include="SortableBindingList.cs" />
<Compile Include="LayoutHandler.cs" />
<Compile Include="MusicBeeInterface.cs" />
Expand Down Expand Up @@ -177,12 +172,7 @@
<PackageReference Include="Newtonsoft.Json">
<Version>13.0.3</Version>
</PackageReference>
<PackageReference Include="RestSharp">
<Version>109.0.1</Version>
</PackageReference>
<PackageReference Include="RestSharp.Serializers.NewtonsoftJson">
<Version>109.0.1</Version>
</PackageReference>

</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\Icons\MusicBee_Logo.png" />
Expand Down
162 changes: 162 additions & 0 deletions DiscordTools/Assets/Uploader/ImgBBUploader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
namespace MusicBeePlugin.DiscordTools.Assets.Uploader
{
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net.Http;
using System.Threading.Tasks;

public class ImgBBUploader : IAssetUploader
{
private const string UploadEndpoint = "https://api.imgbb.com/1/upload";

private static readonly HttpClient _httpClient = new HttpClient();

private string _apiKey;
private bool _isRateLimited;
private string _rateLimitInfo = "";

public ImgBBUploader(string apiKey)
{
_apiKey = apiKey ?? "";
}

public void UpdateApiKey(string apiKey)
{
_apiKey = apiKey ?? "";
_isRateLimited = false;
_rateLimitInfo = "";
}

public Task<bool> Init()
{
// ImgBB has no album setup step needed - just return true
return Task.FromResult(true);
}

public bool IsAssetCached(AlbumCoverData assetData)
{
return false;
}

public async Task<UploadResult> UploadAsset(AlbumCoverData assetData)
{
var link = await UploadAsync(assetData.ImageB64, assetData.Hash).ConfigureAwait(false);
return new UploadResult { Hash = assetData.Hash, Link = link };
}

public Task<bool> DeleteAsset(AlbumCoverData assetData)
{
// ImgBB anonymous uploads cannot be deleted via the API without the delete URL
// stored at upload time - not implemented for now
throw new NotImplementedException();
}

public Task<Dictionary<string, string>> GetAssets()
{
// ImgBB has no album/listing concept for anonymous uploads
return Task.FromResult(new Dictionary<string, string>());
}

public UploaderHealthInfo GetHealth()
{
var health = new UploaderHealthInfo();

if (string.IsNullOrWhiteSpace(_apiKey))
{
health.IsHealthy = false;
health.AddInfo("No ImgBB API key set. Get a free key at https://api.imgbb.com");
}
else if (_isRateLimited)
{
health.IsHealthy = false;
health.AddInfo(_rateLimitInfo);
}
else
{
health.IsHealthy = true;
health.AddInfo("OK");
}

return health;
}

public void Dispose()
{
// HttpClient is static/shared - nothing to dispose
}

// ── Internal upload logic ────────────────────────────────────────────────

private async Task<string> UploadAsync(string base64ImageData, string name = null)
{
if (string.IsNullOrWhiteSpace(_apiKey))
{
Debug.WriteLine("ImgBBUploader: No API key configured.", "DiscordBee");
return null;
}

try
{
using (var content = new MultipartFormDataContent())
{
content.Add(new StringContent(StripDataUriPrefix(base64ImageData)), "image");

if (!string.IsNullOrWhiteSpace(name))
{
content.Add(new StringContent(name), "name");
}

var requestUri = $"{UploadEndpoint}?key={Uri.EscapeDataString(_apiKey)}";
var response = await _httpClient.PostAsync(requestUri, content).ConfigureAwait(false);
var responseBody = await response.Content.ReadAsStringAsync().ConfigureAwait(false);

Debug.WriteLine($"ImgBBUploader: HTTP {(int)response.StatusCode} – {responseBody}", "DiscordBee");

// ImgBB returns 429 when rate limited
if ((int)response.StatusCode == 429)
{
_isRateLimited = true;
_rateLimitInfo = "ImgBB rate limit reached. Uploads will resume automatically.";
return null;
}

if (!response.IsSuccessStatusCode)
{
_isRateLimited = true;
_rateLimitInfo = $"ImgBB upload failed: HTTP {(int)response.StatusCode} {response.ReasonPhrase}";
return null;
}

var json = JObject.Parse(responseBody);
var url = json["data"]?["url"]?.ToString();

if (string.IsNullOrEmpty(url))
{
Debug.WriteLine("ImgBBUploader: Response did not contain an image URL.", "DiscordBee");
return null;
}

_isRateLimited = false;
_rateLimitInfo = "";
return url;
}
}
catch (Exception ex)
{
_isRateLimited = true;
_rateLimitInfo = $"ImgBB upload exception: {ex.Message}";
Debug.WriteLine($"ImgBBUploader: Exception – {ex}", "DiscordBee");
return null;
}
}

private static string StripDataUriPrefix(string data)
{
if (data == null) return data;
var idx = data.IndexOf(',');
return idx >= 0 && data.StartsWith("data:") ? data.Substring(idx + 1) : data;
}
}
}
149 changes: 0 additions & 149 deletions DiscordTools/Assets/Uploader/ImgurUploader.cs

This file was deleted.

15 changes: 0 additions & 15 deletions ImgurClient/ImgurAuthenticator.cs

This file was deleted.

Loading