using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Kyoo.Abstractions.Controllers;
using Kyoo.Abstractions.Models;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.Logging;
namespace Kyoo.Core.Controllers
{
///
/// Download images and retrieve the path of those images for a resource.
///
public class ThumbnailsManager : IThumbnailsManager
{
///
/// The file manager used to download the image if the file is distant
///
private readonly IFileSystem _files;
///
/// A logger to report errors.
///
private readonly ILogger _logger;
///
/// Create a new .
///
/// The file manager to use.
/// A logger to report errors
public ThumbnailsManager(IFileSystem files,
ILogger logger)
{
_files = files;
_logger = logger;
}
///
/// An helper function to download an image using a .
///
/// 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
{
AsyncRef mime = new();
await using Stream reader = await _files.GetReader(url, mime);
string extension = new FileExtensionContentTypeProvider()
.Mappings.FirstOrDefault(x => x.Value == mime.Value)
.Key;
await using Stream local = await _files.NewFile(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 = await _GetPrivateImagePath(item, id);
if (alwaysDownload || !await _files.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 async Task _GetPrivateImagePath(T item, int imageID)
{
if (item == null)
throw new ArgumentNullException(nameof(item));
string directory = await _files.GetExtraDirectory(item);
string imageName = imageID switch
{
Images.Poster => "poster",
Images.Logo => "logo",
Images.Thumbnail => "thumbnail",
Images.Trailer => "trailer",
_ => $"{imageID}"
};
switch (item)
{
case Season season:
imageName = $"season-{season.SeasonNumber}-{imageName}";
break;
case Episode episode:
directory = await _files.CreateDirectory(_files.Combine(directory, "Thumbnails"));
imageName = $"{Path.GetFileNameWithoutExtension(episode.Path)}-{imageName}";
break;
}
return _files.Combine(directory, imageName);
}
///
public async Task GetImagePath(T item, int imageID)
where T : IThumbnails
{
string basePath = await _GetPrivateImagePath(item, imageID);
string directory = Path.GetDirectoryName(basePath);
string baseFile = Path.GetFileName(basePath);
return (await _files.ListFiles(directory!))
.FirstOrDefault(x => Path.GetFileNameWithoutExtension(x) == baseFile);
}
}
}