diff --git a/backend/src/TeleWave.Infrastructure/Media/ImageDownloader.cs b/backend/src/TeleWave.Infrastructure/Media/ImageDownloader.cs index 1c2b90c..aa41fa5 100644 --- a/backend/src/TeleWave.Infrastructure/Media/ImageDownloader.cs +++ b/backend/src/TeleWave.Infrastructure/Media/ImageDownloader.cs @@ -1,9 +1,18 @@ +using Microsoft.Extensions.Logging; using TeleWave.Application.Common.Interfaces; namespace TeleWave.Infrastructure.Media; -/// Скачивает изображение по URL через HTTP-клиент «metadata» и определяет расширение. -public sealed class ImageDownloader(IHttpClientFactory httpFactory) : IImageDownloader +/// +/// Скачивает изображение по URL через HTTP-клиент «metadata» и определяет расширение. +/// +/// Любая неудача — это null, а не исключение: постер не тот повод, чтобы ронять применение +/// метаданных. Но молчать о ней нельзя — снаружи «постера нет» неотличимо от «постера не было +/// в источнике», а причина обычно сетевая: у TMDb картинки лежат на отдельном хосте +/// (image.tmdb.org), и он бывает недоступен там, где сам API работает. +/// +public sealed class ImageDownloader(IHttpClientFactory httpFactory, ILogger logger) + : IImageDownloader { public async Task DownloadAsync( string url, @@ -17,22 +26,40 @@ public sealed class ImageDownloader(IHttpClientFactory httpFactory) : IImageDown !Uri.TryCreate(url, UriKind.Absolute, out var uri) || (uri.Scheme != Uri.UriSchemeHttp && uri.Scheme != Uri.UriSchemeHttps) ) + { + logger.LogWarning("Картинка не скачана: недопустимый адрес {Url}", url); return null; + } try { var client = httpFactory.CreateClient("metadata"); using var response = await client.GetAsync(uri, cancellationToken); if (!response.IsSuccessStatusCode) + { + logger.LogWarning( + "Картинка не скачана: {Url} ответил {Status}", + url, + (int)response.StatusCode + ); return null; + } var ext = ExtensionFor(url, response.Content.Headers.ContentType?.MediaType); var bytes = await response.Content.ReadAsByteArrayAsync(cancellationToken); - return bytes.Length == 0 ? null : new DownloadedImage(bytes, ext); + if (bytes.Length == 0) + { + logger.LogWarning("Картинка не скачана: {Url} вернул пустой ответ", url); + return null; + } + + return new DownloadedImage(bytes, ext); } catch (Exception ex) when (ex is HttpRequestException or TaskCanceledException or IOException) { + // Самая частая причина — недоступный хост картинок: он отдельный от хоста API. + logger.LogWarning(ex, "Картинка не скачана: {Url} недоступен", url); return null; } }