Enhance metadata handling and UI feedback: add support for jingles in channel queries, implement error handling for metadata provider requests, and improve search functionality in ShowMetadataCard with user feedback for no results found. Update translations for new UI messages.

This commit is contained in:
Leonid Pershin
2026-07-25 09:58:59 +03:00
parent 7fb46b5e0d
commit 8ca585a5be
8 changed files with 66 additions and 22 deletions
@@ -21,7 +21,7 @@ public sealed class OmdbMetadataProvider(IHttpClientFactory httpFactory, IOption
{
var url = $"{_omdb.BaseUrl}/?apikey={_omdb.ApiKey}&type=series&s={Uri.EscapeDataString(query)}";
using var doc = await GetJsonAsync(url, cancellationToken);
if (doc is null || !doc.RootElement.TryGetProperty("Search", out var search))
if (!doc.RootElement.TryGetProperty("Search", out var search))
return [];
var list = new List<MetadataCandidate>();
@@ -46,7 +46,7 @@ public sealed class OmdbMetadataProvider(IHttpClientFactory httpFactory, IOption
public async Task<ShowMetadata?> GetShowAsync(string externalId, CancellationToken cancellationToken)
{
var url = $"{_omdb.BaseUrl}/?apikey={_omdb.ApiKey}&i={Uri.EscapeDataString(externalId)}";
using var doc = await GetJsonAsync(url, cancellationToken);
using var doc = await TryGetJsonAsync(url, cancellationToken);
if (doc is null || !IsResponseTrue(doc.RootElement))
return null;
var root = doc.RootElement;
@@ -69,7 +69,7 @@ public sealed class OmdbMetadataProvider(IHttpClientFactory httpFactory, IOption
var url =
$"{_omdb.BaseUrl}/?apikey={_omdb.ApiKey}&i={Uri.EscapeDataString(externalId)}"
+ $"&Season={season}&Episode={episode}";
using var doc = await GetJsonAsync(url, cancellationToken);
using var doc = await TryGetJsonAsync(url, cancellationToken);
if (doc is null || !IsResponseTrue(doc.RootElement))
return null;
var root = doc.RootElement;
@@ -81,16 +81,22 @@ public sealed class OmdbMetadataProvider(IHttpClientFactory httpFactory, IOption
);
}
private async Task<JsonDocument?> GetJsonAsync(string url, CancellationToken cancellationToken)
/// <summary>GET+parse; бросает при не-2xx/сетевой ошибке (для поиска — чтобы показать сбой).</summary>
private async Task<JsonDocument> GetJsonAsync(string url, CancellationToken cancellationToken)
{
var client = httpFactory.CreateClient("metadata");
using var response = await client.GetAsync(url, cancellationToken);
response.EnsureSuccessStatusCode();
var stream = await response.Content.ReadAsStreamAsync(cancellationToken);
return await JsonDocument.ParseAsync(stream, cancellationToken: cancellationToken);
}
/// <summary>Как GetJsonAsync, но глотает ошибки в null (для get/episode — деградируем мягко).</summary>
private async Task<JsonDocument?> TryGetJsonAsync(string url, CancellationToken cancellationToken)
{
try
{
using var response = await client.GetAsync(url, cancellationToken);
if (!response.IsSuccessStatusCode)
return null;
var stream = await response.Content.ReadAsStreamAsync(cancellationToken);
return await JsonDocument.ParseAsync(stream, cancellationToken: cancellationToken);
return await GetJsonAsync(url, cancellationToken);
}
catch (Exception ex) when (ex is HttpRequestException or JsonException or TaskCanceledException)
{
@@ -25,7 +25,7 @@ public sealed class TmdbMetadataProvider(IHttpClientFactory httpFactory, IOption
$"{Tmdb.BaseUrl}/search/tv?api_key={Tmdb.ApiKey}&language={_options.Language}"
+ $"&include_adult=false&query={Uri.EscapeDataString(query)}";
using var doc = await GetJsonAsync(url, cancellationToken);
if (doc is null || !doc.RootElement.TryGetProperty("results", out var results))
if (!doc.RootElement.TryGetProperty("results", out var results))
return [];
var list = new List<MetadataCandidate>();
@@ -50,7 +50,7 @@ public sealed class TmdbMetadataProvider(IHttpClientFactory httpFactory, IOption
public async Task<ShowMetadata?> GetShowAsync(string externalId, CancellationToken cancellationToken)
{
var url = $"{Tmdb.BaseUrl}/tv/{externalId}?api_key={Tmdb.ApiKey}&language={_options.Language}";
using var doc = await GetJsonAsync(url, cancellationToken);
using var doc = await TryGetJsonAsync(url, cancellationToken);
if (doc is null)
return null;
var root = doc.RootElement;
@@ -73,7 +73,7 @@ public sealed class TmdbMetadataProvider(IHttpClientFactory httpFactory, IOption
var url =
$"{Tmdb.BaseUrl}/tv/{externalId}/season/{season}/episode/{episode}"
+ $"?api_key={Tmdb.ApiKey}&language={_options.Language}";
using var doc = await GetJsonAsync(url, cancellationToken);
using var doc = await TryGetJsonAsync(url, cancellationToken);
if (doc is null)
return null;
var root = doc.RootElement;
@@ -91,16 +91,22 @@ public sealed class TmdbMetadataProvider(IHttpClientFactory httpFactory, IOption
private string? StillUrl(string? path) =>
string.IsNullOrEmpty(path) ? null : $"{Tmdb.ImageBaseUrl}/{Tmdb.StillSize}{path}";
private async Task<JsonDocument?> GetJsonAsync(string url, CancellationToken cancellationToken)
/// <summary>GET+parse; бросает при не-2xx/сетевой ошибке (для поиска — чтобы показать сбой).</summary>
private async Task<JsonDocument> GetJsonAsync(string url, CancellationToken cancellationToken)
{
var client = httpFactory.CreateClient("metadata");
using var response = await client.GetAsync(url, cancellationToken);
response.EnsureSuccessStatusCode();
var stream = await response.Content.ReadAsStreamAsync(cancellationToken);
return await JsonDocument.ParseAsync(stream, cancellationToken: cancellationToken);
}
/// <summary>Как GetJsonAsync, но глотает ошибки в null (для get/episode — деградируем мягко).</summary>
private async Task<JsonDocument?> TryGetJsonAsync(string url, CancellationToken cancellationToken)
{
try
{
using var response = await client.GetAsync(url, cancellationToken);
if (!response.IsSuccessStatusCode)
return null;
var stream = await response.Content.ReadAsStreamAsync(cancellationToken);
return await JsonDocument.ParseAsync(stream, cancellationToken: cancellationToken);
return await GetJsonAsync(url, cancellationToken);
}
catch (Exception ex) when (ex is HttpRequestException or JsonException or TaskCanceledException)
{