feat: replace OpenSSL with native Blowfish decryption and add library rescan

- Replace OpenSSL subprocess with BouncyCastle native Blowfish CBC decryption
- Add automatic Subsonic library scan trigger after downloads (with 30s debounce)
- Improve error handling in DeezerMetadataService search methods
- Add comprehensive tests for download service, metadata service, and library service
- Increase test coverage from 13 to 32 tests
This commit is contained in:
V1ck3s
2025-12-08 22:12:08 +01:00
committed by Vickes
parent ad15e10ea6
commit 9b398c7484
8 changed files with 513 additions and 69 deletions

View File

@@ -18,65 +18,89 @@ public class DeezerMetadataService : IMusicMetadataService
public async Task<List<Song>> SearchSongsAsync(string query, int limit = 20)
{
var url = $"{BaseUrl}/search/track?q={Uri.EscapeDataString(query)}&limit={limit}";
var response = await _httpClient.GetAsync(url);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
var result = JsonDocument.Parse(json);
var songs = new List<Song>();
if (result.RootElement.TryGetProperty("data", out var data))
try
{
foreach (var track in data.EnumerateArray())
var url = $"{BaseUrl}/search/track?q={Uri.EscapeDataString(query)}&limit={limit}";
var response = await _httpClient.GetAsync(url);
if (!response.IsSuccessStatusCode) return new List<Song>();
var json = await response.Content.ReadAsStringAsync();
var result = JsonDocument.Parse(json);
var songs = new List<Song>();
if (result.RootElement.TryGetProperty("data", out var data))
{
songs.Add(ParseDeezerTrack(track));
foreach (var track in data.EnumerateArray())
{
songs.Add(ParseDeezerTrack(track));
}
}
return songs;
}
catch
{
return new List<Song>();
}
return songs;
}
public async Task<List<Album>> SearchAlbumsAsync(string query, int limit = 20)
{
var url = $"{BaseUrl}/search/album?q={Uri.EscapeDataString(query)}&limit={limit}";
var response = await _httpClient.GetAsync(url);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
var result = JsonDocument.Parse(json);
var albums = new List<Album>();
if (result.RootElement.TryGetProperty("data", out var data))
try
{
foreach (var album in data.EnumerateArray())
var url = $"{BaseUrl}/search/album?q={Uri.EscapeDataString(query)}&limit={limit}";
var response = await _httpClient.GetAsync(url);
if (!response.IsSuccessStatusCode) return new List<Album>();
var json = await response.Content.ReadAsStringAsync();
var result = JsonDocument.Parse(json);
var albums = new List<Album>();
if (result.RootElement.TryGetProperty("data", out var data))
{
albums.Add(ParseDeezerAlbum(album));
foreach (var album in data.EnumerateArray())
{
albums.Add(ParseDeezerAlbum(album));
}
}
return albums;
}
catch
{
return new List<Album>();
}
return albums;
}
public async Task<List<Artist>> SearchArtistsAsync(string query, int limit = 20)
{
var url = $"{BaseUrl}/search/artist?q={Uri.EscapeDataString(query)}&limit={limit}";
var response = await _httpClient.GetAsync(url);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
var result = JsonDocument.Parse(json);
var artists = new List<Artist>();
if (result.RootElement.TryGetProperty("data", out var data))
try
{
foreach (var artist in data.EnumerateArray())
var url = $"{BaseUrl}/search/artist?q={Uri.EscapeDataString(query)}&limit={limit}";
var response = await _httpClient.GetAsync(url);
if (!response.IsSuccessStatusCode) return new List<Artist>();
var json = await response.Content.ReadAsStringAsync();
var result = JsonDocument.Parse(json);
var artists = new List<Artist>();
if (result.RootElement.TryGetProperty("data", out var data))
{
artists.Add(ParseDeezerArtist(artist));
foreach (var artist in data.EnumerateArray())
{
artists.Add(ParseDeezerArtist(artist));
}
}
return artists;
}
catch
{
return new List<Artist>();
}
return artists;
}
public async Task<SearchResult> SearchAllAsync(string query, int songLimit = 20, int albumLimit = 20, int artistLimit = 20)