// Kyoo - A portable and vast media library solution. // Copyright (c) Kyoo. // // See AUTHORS.md and LICENSE file in the project root for full license information. // // Kyoo is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // any later version. // // Kyoo is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Kyoo. If not, see . using System; using System.IO; using System.Linq; using System.Net.Http; using System.Threading.Tasks; using Kyoo.Abstractions.Controllers; using Kyoo.Abstractions.Models; using Microsoft.AspNetCore.StaticFiles; using Microsoft.Extensions.Logging; #nullable enable namespace Kyoo.Core.Controllers { /// /// Download images and retrieve the path of those images for a resource. /// public class ThumbnailsManager : IThumbnailsManager { /// /// A logger to report errors. /// private readonly ILogger _logger; private readonly IHttpClientFactory _clientFactory; /// /// Create a new . /// /// Client factory /// A logger to report errors public ThumbnailsManager(IHttpClientFactory clientFactory, ILogger logger) { _clientFactory = clientFactory; _logger = logger; } /// /// An helper function to download an image. /// /// The distant url of the image /// The local path of the image /// What is currently downloaded (used for errors) /// true if an image has been downloaded, false otherwise. private async Task _DownloadImage(string url, string localPath, string what) { if (url == localPath) return false; try { _logger.LogInformation("Downloading image {What}", what); HttpClient client = _clientFactory.CreateClient(); HttpResponseMessage response = await client.GetAsync(url); response.EnsureSuccessStatusCode(); string mime = response.Content.Headers.ContentType?.MediaType!; await using Stream reader = await response.Content.ReadAsStreamAsync(); string extension = new FileExtensionContentTypeProvider() .Mappings.FirstOrDefault(x => x.Value == mime) .Key; await using Stream local = File.Create(localPath + extension); await reader.CopyToAsync(local); return true; } catch (Exception ex) { _logger.LogError(ex, "{What} could not be downloaded", what); return false; } } /// public async Task DownloadImages(T item, bool alwaysDownload = false) where T : IThumbnails { if (item == null) throw new ArgumentNullException(nameof(item)); if (item.Images == null) return false; string name = item is IResource res ? res.Slug : "???"; bool ret = false; foreach ((int id, string image) in item.Images.Where(x => x.Value != null)) { string localPath = _GetPrivateImagePath(item, id); if (alwaysDownload || !Path.Exists(localPath)) ret |= await _DownloadImage(image, localPath, $"The image n {id} of {name}"); } return ret; } /// /// Retrieve the local path of an image of the given item without an extension. /// /// The item to retrieve the poster from. /// The ID of the image. See for values. /// The type of the item /// The path of the image for the given resource, even if it does not exists private static string _GetPrivateImagePath(T item, int imageId) { if (item == null) throw new ArgumentNullException(nameof(item)); string directory = item switch { IResource res => Path.Combine("/metadata", typeof(T).Name.ToLowerInvariant(), res.Slug), _ => Path.Combine("/metadata", typeof(T).Name.ToLowerInvariant()) }; Directory.CreateDirectory(directory); string imageName = imageId switch { Images.Poster => "poster", Images.Logo => "logo", Images.Thumbnail => "thumbnail", Images.Trailer => "trailer", _ => $"{imageId}" }; return Path.Combine(directory, imageName); } /// public string? GetImagePath(T item, int imageId) where T : IThumbnails { string basePath = _GetPrivateImagePath(item, imageId); string directory = Path.GetDirectoryName(basePath)!; string baseFile = Path.GetFileName(basePath); if (!Directory.Exists(directory)) return null; return Directory.GetFiles(directory, "*", SearchOption.TopDirectoryOnly) .FirstOrDefault(x => Path.GetFileNameWithoutExtension(x) == baseFile); } } }