From 1a33f38384d9c075822569fd5a9d7d818091e12f Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Wed, 4 Aug 2021 23:43:17 +0200 Subject: [PATCH] FileSystem: Creating an api to retrieve a file mime type --- Kyoo.Common/Controllers/IFileSystem.cs | 26 ++++++++++- Kyoo.Common/Controllers/IThumbnailsManager.cs | 8 ++-- .../FileSystems/FileSystemComposite.cs | 16 +++++-- .../Controllers/FileSystems/HttpFileSystem.cs | 11 ++++- .../FileSystems/LocalFileSystem.cs | 10 +++++ Kyoo/Controllers/ThumbnailsManager.cs | 43 +++++++++++++------ 6 files changed, 91 insertions(+), 23 deletions(-) diff --git a/Kyoo.Common/Controllers/IFileSystem.cs b/Kyoo.Common/Controllers/IFileSystem.cs index f0702e04..92e951b7 100644 --- a/Kyoo.Common/Controllers/IFileSystem.cs +++ b/Kyoo.Common/Controllers/IFileSystem.cs @@ -2,11 +2,25 @@ using System.Collections.Generic; using System.IO; using System.Threading.Tasks; using JetBrains.Annotations; -using Kyoo.Models; using Microsoft.AspNetCore.Mvc; namespace Kyoo.Controllers { + /// + /// A class wrapping a value that will be set after the completion of the task it is related to. + /// + /// + /// This class replace the use of an out parameter on a task since tasks and out can't be combined. + /// + /// The type of the value + public class AsyncRef + { + /// + /// The value that will be set before the completion of the task. + /// + public T Value { get; set; } + } + /// /// A service to abstract the file system to allow custom file systems (like distant file systems or external providers) /// @@ -42,6 +56,16 @@ namespace Kyoo.Controllers /// If the file could not be found. /// A reader to read the file. public Task GetReader([NotNull] string path); + + /// + /// Read a file present at . The reader can be used in an arbitrary context. + /// To return files from an http endpoint, use . + /// + /// The path of the file + /// The mime type of the opened file. + /// If the file could not be found. + /// A reader to read the file. + public Task GetReader([NotNull] string path, AsyncRef mime); /// /// Create a new file at . diff --git a/Kyoo.Common/Controllers/IThumbnailsManager.cs b/Kyoo.Common/Controllers/IThumbnailsManager.cs index aac09210..61fe5345 100644 --- a/Kyoo.Common/Controllers/IThumbnailsManager.cs +++ b/Kyoo.Common/Controllers/IThumbnailsManager.cs @@ -1,5 +1,4 @@ -using System; -using Kyoo.Models; +using Kyoo.Models; using System.Threading.Tasks; using JetBrains.Annotations; @@ -27,13 +26,12 @@ namespace Kyoo.Controllers /// - /// Retrieve the local path of the poster of the given item. + /// Retrieve the local path of an image of the given item. /// /// The item to retrieve the poster from. /// The ID of the image. See for values. /// The type of the item - /// If the type does not have a poster - /// The path of the poster for the given resource (it might or might not exists). + /// The path of the image for the given resource or null if it does not exists. Task GetImagePath([NotNull] T item, int imageID) where T : IThumbnails; } diff --git a/Kyoo/Controllers/FileSystems/FileSystemComposite.cs b/Kyoo/Controllers/FileSystems/FileSystemComposite.cs index 20a7f67d..b3f4f66a 100644 --- a/Kyoo/Controllers/FileSystems/FileSystemComposite.cs +++ b/Kyoo/Controllers/FileSystems/FileSystemComposite.cs @@ -107,6 +107,15 @@ namespace Kyoo.Controllers .GetReader(relativePath); } + /// + public Task GetReader(string path, AsyncRef mime) + { + if (path == null) + throw new ArgumentNullException(nameof(path)); + return _GetFileSystemForPath(path, out string relativePath) + .GetReader(relativePath, mime); + } + /// public Task NewFile(string path) { @@ -181,10 +190,11 @@ namespace Kyoo.Controllers Season season => await GetExtraDirectory(season.Show), Episode episode => await GetExtraDirectory(episode.Show), Track track => await GetExtraDirectory(track.Episode), - IResource res => Combine(_options.CurrentValue.MetadataPath, typeof(T).Name, res.Slug), - _ => Combine(_options.CurrentValue.MetadataPath, typeof(T).Name) + IResource res => Combine(_options.CurrentValue.MetadataPath, + typeof(T).Name.ToLowerInvariant(), res.Slug), + _ => Combine(_options.CurrentValue.MetadataPath, typeof(T).Name.ToLowerInvariant()) }; - return path; + return await CreateDirectory(path); } } } \ No newline at end of file diff --git a/Kyoo/Controllers/FileSystems/HttpFileSystem.cs b/Kyoo/Controllers/FileSystems/HttpFileSystem.cs index 5eeebbc2..82409cdc 100644 --- a/Kyoo/Controllers/FileSystems/HttpFileSystem.cs +++ b/Kyoo/Controllers/FileSystems/HttpFileSystem.cs @@ -4,7 +4,6 @@ using System.IO; using System.Net.Http; using System.Threading.Tasks; using Kyoo.Common.Models.Attributes; -using Kyoo.Models; using Microsoft.AspNetCore.Mvc; namespace Kyoo.Controllers @@ -44,6 +43,16 @@ namespace Kyoo.Controllers HttpClient client = _clientFactory.CreateClient(); return client.GetStreamAsync(path); } + + /// + public async Task GetReader(string path, AsyncRef mime) + { + HttpClient client = _clientFactory.CreateClient(); + HttpResponseMessage response = await client.GetAsync(path); + response.EnsureSuccessStatusCode(); + mime.Value = response.Content.Headers.ContentType?.MediaType; + return await response.Content.ReadAsStreamAsync(); + } /// public Task NewFile(string path) diff --git a/Kyoo/Controllers/FileSystems/LocalFileSystem.cs b/Kyoo/Controllers/FileSystems/LocalFileSystem.cs index 71dac41d..18d887a0 100644 --- a/Kyoo/Controllers/FileSystems/LocalFileSystem.cs +++ b/Kyoo/Controllers/FileSystems/LocalFileSystem.cs @@ -78,6 +78,16 @@ namespace Kyoo.Controllers throw new ArgumentNullException(nameof(path)); return Task.FromResult(File.OpenRead(path)); } + + /// + public Task GetReader(string path, AsyncRef mime) + { + if (path == null) + throw new ArgumentNullException(nameof(path)); + _provider.TryGetContentType(path, out string mimeValue); + mime.Value = mimeValue; + return Task.FromResult(File.OpenRead(path)); + } /// public Task NewFile(string path) diff --git a/Kyoo/Controllers/ThumbnailsManager.cs b/Kyoo/Controllers/ThumbnailsManager.cs index 142ee236..b56625bd 100644 --- a/Kyoo/Controllers/ThumbnailsManager.cs +++ b/Kyoo/Controllers/ThumbnailsManager.cs @@ -47,7 +47,9 @@ namespace Kyoo.Controllers try { - await using Stream reader = await _files.GetReader(url); + AsyncRef mime = new(); + await using Stream reader = await _files.GetReader(url, mime); + // TODO use this mime type to guess the file extension. await using Stream local = await _files.NewFile(localPath); await reader.CopyToAsync(local); return true; @@ -58,7 +60,7 @@ namespace Kyoo.Controllers return false; } } - + /// public async Task DownloadImages(T item, bool alwaysDownload = false) where T : IThumbnails @@ -74,28 +76,32 @@ namespace Kyoo.Controllers foreach ((int id, string image) in item.Images.Where(x => x.Value != null)) { - string localPath = await GetImagePath(item, id); + 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; } - - /// - public async Task GetImagePath(T item, int imageID) - where T : IThumbnails + + /// + /// 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)); - // TODO handle extensions string imageName = imageID switch { - Images.Poster => "poster.jpg", - Images.Logo => "logo.jpg", - Images.Thumbnail => "thumbnail.jpg", - Images.Trailer => "trailer.mp4", - _ => $"{imageID}.jpg" + Images.Poster => "poster", + Images.Logo => "logo", + Images.Thumbnail => "thumbnail", + Images.Trailer => "trailer", + _ => $"{imageID}" }; imageName = item switch @@ -108,5 +114,16 @@ namespace Kyoo.Controllers return _files.Combine(await _files.GetExtraDirectory(item), 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); + } } }