diff --git a/Kyoo.Authentication/Views/AccountApi.cs b/Kyoo.Authentication/Views/AccountApi.cs
index 59d964f0..4288250e 100644
--- a/Kyoo.Authentication/Views/AccountApi.cs
+++ b/Kyoo.Authentication/Views/AccountApi.cs
@@ -36,7 +36,7 @@ namespace Kyoo.Authentication.Views
///
/// A file manager to send profile pictures
///
- private readonly IFileManager _files;
+ private readonly IFileSystem _files;
///
/// Options about authentication. Those options are monitored and reloads are supported.
///
@@ -50,7 +50,7 @@ namespace Kyoo.Authentication.Views
/// A file manager to send profile pictures
/// Authentication options (this may be hot reloaded)
public AccountApi(IUserRepository users,
- IFileManager files,
+ IFileSystem files,
IOptions options)
{
_users = users;
@@ -205,8 +205,8 @@ namespace Kyoo.Authentication.Views
user.Username = data.Username;
if (data.Picture?.Length > 0)
{
- string path = Path.Combine(_options.Value.ProfilePicturePath, user.ID.ToString());
- await using Stream file = _files.NewFile(path);
+ string path = _files.Combine(_options.Value.ProfilePicturePath, user.ID.ToString());
+ await using Stream file = await _files.NewFile(path);
await data.Picture.CopyToAsync(file);
}
return await _users.Edit(user, false);
diff --git a/Kyoo.Common/Controllers/IFileManager.cs b/Kyoo.Common/Controllers/IFileSystem.cs
similarity index 78%
rename from Kyoo.Common/Controllers/IFileManager.cs
rename to Kyoo.Common/Controllers/IFileSystem.cs
index d39cff6e..2fb1c6d7 100644
--- a/Kyoo.Common/Controllers/IFileManager.cs
+++ b/Kyoo.Common/Controllers/IFileSystem.cs
@@ -10,7 +10,7 @@ namespace Kyoo.Controllers
///
/// A service to abstract the file system to allow custom file systems (like distant file systems or external providers)
///
- public interface IFileManager
+ public interface IFileSystem
{
// TODO find a way to handle Transmux/Transcode with this system.
@@ -41,14 +41,14 @@ namespace Kyoo.Controllers
/// The path of the file
/// If the file could not be found.
/// A reader to read the file.
- public Stream GetReader([NotNull] string path);
+ public Task GetReader([NotNull] string path);
///
/// Create a new file at .
///
/// The path of the new file.
/// A writer to write to the new file.
- public Stream NewFile([NotNull] string path);
+ public Task NewFile([NotNull] string path);
///
/// Create a new directory at the given path
@@ -87,24 +87,6 @@ namespace Kyoo.Controllers
///
/// The show to proceed
/// The extra directory of the show
- public string GetExtraDirectory(Show show);
-
- ///
- /// Get the extra directory of a season.
- /// This method is in this system to allow a filesystem to use a different metadata policy for one.
- /// It can be useful if the filesystem is readonly.
- ///
- /// The season to proceed
- /// The extra directory of the season
- public string GetExtraDirectory(Season season);
-
- ///
- /// Get the extra directory of an episode.
- /// This method is in this system to allow a filesystem to use a different metadata policy for one.
- /// It can be useful if the filesystem is readonly.
- ///
- /// The episode to proceed
- /// The extra directory of the episode
- public string GetExtraDirectory(Episode episode);
+ public string GetExtraDirectory([NotNull] Show show);
}
}
\ No newline at end of file
diff --git a/Kyoo.Common/Controllers/ITask.cs b/Kyoo.Common/Controllers/ITask.cs
index 3267837b..1896bbcd 100644
--- a/Kyoo.Common/Controllers/ITask.cs
+++ b/Kyoo.Common/Controllers/ITask.cs
@@ -5,6 +5,7 @@ using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Kyoo.Models.Attributes;
+using Kyoo.Models.Exceptions;
namespace Kyoo.Controllers
{
@@ -210,6 +211,12 @@ namespace Kyoo.Controllers
/// they will be set to an available service from the service container before calling this method.
/// They also will be removed after this method return (or throw) to prevent dangling services.
///
+ ///
+ /// An exception meaning that the task has failed for handled reasons like invalid arguments,
+ /// invalid environment, missing plugins or failures not related to a default in the code.
+ /// This exception allow the task to display a failure message to the end user while others exceptions
+ /// will be displayed as unhandled exceptions and display a stack trace.
+ ///
public Task Run([NotNull] TaskParameters arguments,
[NotNull] IProgress progress,
CancellationToken cancellationToken);
diff --git a/Kyoo.Common/Kyoo.Common.csproj b/Kyoo.Common/Kyoo.Common.csproj
index ada2ed60..844be997 100644
--- a/Kyoo.Common/Kyoo.Common.csproj
+++ b/Kyoo.Common/Kyoo.Common.csproj
@@ -27,6 +27,7 @@
+
diff --git a/Kyoo.Common/Models/Attributes/FileSystemMetadataAttribute.cs b/Kyoo.Common/Models/Attributes/FileSystemMetadataAttribute.cs
new file mode 100644
index 00000000..464c44d0
--- /dev/null
+++ b/Kyoo.Common/Models/Attributes/FileSystemMetadataAttribute.cs
@@ -0,0 +1,35 @@
+using System;
+using System.ComponentModel.Composition;
+using Kyoo.Controllers;
+
+namespace Kyoo.Common.Models.Attributes
+{
+ ///
+ /// An attribute to inform how a works.
+ ///
+ [MetadataAttribute]
+ [AttributeUsage(AttributeTargets.Class)]
+ public class FileSystemMetadataAttribute : Attribute
+ {
+ ///
+ /// The scheme(s) used to identify this path.
+ /// It can be something like http, https, ftp, file and so on.
+ ///
+ ///
+ /// If multiples files with the same schemes exists, an exception will be thrown.
+ ///
+ public string[] Scheme { get; }
+
+ ///
+ /// true if the scheme should be removed from the path before calling
+ /// methods of this , false otherwise.
+ ///
+ public bool StripScheme { get; set; }
+
+
+ public FileSystemMetadataAttribute(string[] schemes)
+ {
+ Scheme = schemes;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Kyoo.Common/Models/Resources/Episode.cs b/Kyoo.Common/Models/Resources/Episode.cs
index e7cf056a..7f76a8a4 100644
--- a/Kyoo.Common/Models/Resources/Episode.cs
+++ b/Kyoo.Common/Models/Resources/Episode.cs
@@ -95,7 +95,7 @@ namespace Kyoo.Models
public int? AbsoluteNumber { get; set; }
///
- /// The path of the video file for this episode. Any format supported by a is allowed.
+ /// The path of the video file for this episode. Any format supported by a is allowed.
///
[SerializeIgnore] public string Path { get; set; }
diff --git a/Kyoo.Common/Models/Resources/Show.cs b/Kyoo.Common/Models/Resources/Show.cs
index 656d6ffb..c9d5ca8e 100644
--- a/Kyoo.Common/Models/Resources/Show.cs
+++ b/Kyoo.Common/Models/Resources/Show.cs
@@ -31,7 +31,7 @@ namespace Kyoo.Models
///
/// The path of the root directory of this show.
- /// This can be any kind of path supported by
+ /// This can be any kind of path supported by
///
[SerializeIgnore] public string Path { get; set; }
@@ -46,7 +46,7 @@ namespace Kyoo.Models
public Status? Status { get; set; }
///
- /// An URL to a trailer. This could be any path supported by the .
+ /// An URL to a trailer. This could be any path supported by the .
///
/// TODO for now, this is set to a youtube url. It should be cached and converted to a local file.
public string TrailerUrl { get; set; }
diff --git a/Kyoo.Common/Models/WatchItem.cs b/Kyoo.Common/Models/WatchItem.cs
index eaf437c7..241ed9a9 100644
--- a/Kyoo.Common/Models/WatchItem.cs
+++ b/Kyoo.Common/Models/WatchItem.cs
@@ -62,7 +62,7 @@ namespace Kyoo.Models
public DateTime? ReleaseDate { get; set; }
///
- /// The path of the video file for this episode. Any format supported by a is allowed.
+ /// The path of the video file for this episode. Any format supported by a is allowed.
///
[SerializeIgnore] public string Path { get; set; }
diff --git a/Kyoo.Common/Utility/Utility.cs b/Kyoo.Common/Utility/Utility.cs
index 6b6dc472..2f7e14ad 100644
--- a/Kyoo.Common/Utility/Utility.cs
+++ b/Kyoo.Common/Utility/Utility.cs
@@ -254,7 +254,7 @@ namespace Kyoo
///
/// To run for a List where you don't know the type at compile type,
/// you could do:
- ///
+ ///
/// Utility.RunGenericMethod<object>(
/// typeof(Utility),
/// nameof(MergeLists),
diff --git a/Kyoo.Tests/Library/RepositoryActivator.cs b/Kyoo.Tests/Database/RepositoryActivator.cs
similarity index 100%
rename from Kyoo.Tests/Library/RepositoryActivator.cs
rename to Kyoo.Tests/Database/RepositoryActivator.cs
diff --git a/Kyoo.Tests/Library/RepositoryTests.cs b/Kyoo.Tests/Database/RepositoryTests.cs
similarity index 100%
rename from Kyoo.Tests/Library/RepositoryTests.cs
rename to Kyoo.Tests/Database/RepositoryTests.cs
diff --git a/Kyoo.Tests/Library/SpecificTests/CollectionsTests.cs b/Kyoo.Tests/Database/SpecificTests/CollectionsTests.cs
similarity index 100%
rename from Kyoo.Tests/Library/SpecificTests/CollectionsTests.cs
rename to Kyoo.Tests/Database/SpecificTests/CollectionsTests.cs
diff --git a/Kyoo.Tests/Library/SpecificTests/EpisodeTests.cs b/Kyoo.Tests/Database/SpecificTests/EpisodeTests.cs
similarity index 100%
rename from Kyoo.Tests/Library/SpecificTests/EpisodeTests.cs
rename to Kyoo.Tests/Database/SpecificTests/EpisodeTests.cs
diff --git a/Kyoo.Tests/Library/SpecificTests/GenreTests.cs b/Kyoo.Tests/Database/SpecificTests/GenreTests.cs
similarity index 100%
rename from Kyoo.Tests/Library/SpecificTests/GenreTests.cs
rename to Kyoo.Tests/Database/SpecificTests/GenreTests.cs
diff --git a/Kyoo.Tests/Library/SpecificTests/LibraryItemTest.cs b/Kyoo.Tests/Database/SpecificTests/LibraryItemTest.cs
similarity index 100%
rename from Kyoo.Tests/Library/SpecificTests/LibraryItemTest.cs
rename to Kyoo.Tests/Database/SpecificTests/LibraryItemTest.cs
diff --git a/Kyoo.Tests/Library/SpecificTests/LibraryTests.cs b/Kyoo.Tests/Database/SpecificTests/LibraryTests.cs
similarity index 100%
rename from Kyoo.Tests/Library/SpecificTests/LibraryTests.cs
rename to Kyoo.Tests/Database/SpecificTests/LibraryTests.cs
diff --git a/Kyoo.Tests/Library/SpecificTests/PeopleTests.cs b/Kyoo.Tests/Database/SpecificTests/PeopleTests.cs
similarity index 100%
rename from Kyoo.Tests/Library/SpecificTests/PeopleTests.cs
rename to Kyoo.Tests/Database/SpecificTests/PeopleTests.cs
diff --git a/Kyoo.Tests/Library/SpecificTests/ProviderTests.cs b/Kyoo.Tests/Database/SpecificTests/ProviderTests.cs
similarity index 100%
rename from Kyoo.Tests/Library/SpecificTests/ProviderTests.cs
rename to Kyoo.Tests/Database/SpecificTests/ProviderTests.cs
diff --git a/Kyoo.Tests/Library/SpecificTests/SanityTests.cs b/Kyoo.Tests/Database/SpecificTests/SanityTests.cs
similarity index 100%
rename from Kyoo.Tests/Library/SpecificTests/SanityTests.cs
rename to Kyoo.Tests/Database/SpecificTests/SanityTests.cs
diff --git a/Kyoo.Tests/Library/SpecificTests/SeasonTests.cs b/Kyoo.Tests/Database/SpecificTests/SeasonTests.cs
similarity index 100%
rename from Kyoo.Tests/Library/SpecificTests/SeasonTests.cs
rename to Kyoo.Tests/Database/SpecificTests/SeasonTests.cs
diff --git a/Kyoo.Tests/Library/SpecificTests/ShowTests.cs b/Kyoo.Tests/Database/SpecificTests/ShowTests.cs
similarity index 100%
rename from Kyoo.Tests/Library/SpecificTests/ShowTests.cs
rename to Kyoo.Tests/Database/SpecificTests/ShowTests.cs
diff --git a/Kyoo.Tests/Library/SpecificTests/StudioTests.cs b/Kyoo.Tests/Database/SpecificTests/StudioTests.cs
similarity index 100%
rename from Kyoo.Tests/Library/SpecificTests/StudioTests.cs
rename to Kyoo.Tests/Database/SpecificTests/StudioTests.cs
diff --git a/Kyoo.Tests/Library/SpecificTests/TrackTests.cs b/Kyoo.Tests/Database/SpecificTests/TrackTests.cs
similarity index 100%
rename from Kyoo.Tests/Library/SpecificTests/TrackTests.cs
rename to Kyoo.Tests/Database/SpecificTests/TrackTests.cs
diff --git a/Kyoo.Tests/Library/SpecificTests/UserTests.cs b/Kyoo.Tests/Database/SpecificTests/UserTests.cs
similarity index 100%
rename from Kyoo.Tests/Library/SpecificTests/UserTests.cs
rename to Kyoo.Tests/Database/SpecificTests/UserTests.cs
diff --git a/Kyoo.Tests/Library/TestContext.cs b/Kyoo.Tests/Database/TestContext.cs
similarity index 100%
rename from Kyoo.Tests/Library/TestContext.cs
rename to Kyoo.Tests/Database/TestContext.cs
diff --git a/Kyoo.Tests/Library/TestSample.cs b/Kyoo.Tests/Database/TestSample.cs
similarity index 100%
rename from Kyoo.Tests/Library/TestSample.cs
rename to Kyoo.Tests/Database/TestSample.cs
diff --git a/Kyoo/Controllers/FileSystems/FileSystemComposite.cs b/Kyoo/Controllers/FileSystems/FileSystemComposite.cs
new file mode 100644
index 00000000..f2f87a02
--- /dev/null
+++ b/Kyoo/Controllers/FileSystems/FileSystemComposite.cs
@@ -0,0 +1,143 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using Autofac.Features.Metadata;
+using JetBrains.Annotations;
+using Kyoo.Common.Models.Attributes;
+using Kyoo.Models;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Kyoo.Controllers
+{
+ ///
+ /// A composite that merge every available
+ /// using .
+ ///
+ public class FileSystemComposite : IFileSystem
+ {
+ ///
+ /// The list of mapped to their metadata.
+ ///
+ private readonly ICollection, FileSystemMetadataAttribute>> _fileSystems;
+
+ ///
+ /// Create a new from a list of mapped to their
+ /// metadata.
+ ///
+ /// The list of filesystem mapped to their metadata.
+ public FileSystemComposite(ICollection, FileSystemMetadataAttribute>> fileSystems)
+ {
+ _fileSystems = fileSystems;
+ }
+
+
+ ///
+ /// Retrieve the file system that should be used for a given path.
+ ///
+ ///
+ /// The path that was requested.
+ ///
+ ///
+ /// The path that the returned file system wants
+ /// (respecting ).
+ ///
+ /// No file system was registered for the given path.
+ /// The file system that should be used for a given path
+ [NotNull]
+ private IFileSystem _GetFileSystemForPath([NotNull] string path, [NotNull] out string usablePath)
+ {
+ Regex schemeMatcher = new(@"(.+)://(.*)", RegexOptions.Compiled);
+ Match match = schemeMatcher.Match(path);
+
+ if (!match.Success)
+ {
+ usablePath = path;
+ Meta, FileSystemMetadataAttribute> defaultFs = _fileSystems
+ .SingleOrDefault(x => x.Metadata.Scheme.Contains(""));
+ if (defaultFs == null)
+ throw new ArgumentException($"No file system registered for the default scheme.");
+ return defaultFs.Value.Invoke();
+ }
+ string scheme = match.Groups[1].Value;
+ Meta, FileSystemMetadataAttribute> ret = _fileSystems
+ .SingleOrDefault(x => x.Metadata.Scheme.Contains(scheme));
+ if (ret == null)
+ throw new ArgumentException($"No file system registered for the scheme: {scheme}.");
+ usablePath = ret.Metadata.StripScheme ? match.Groups[2].Value : path;
+ return ret.Value.Invoke();
+ }
+
+ ///
+ public IActionResult FileResult(string path, bool rangeSupport = false, string type = null)
+ {
+ if (path == null)
+ return new NotFoundResult();
+ return _GetFileSystemForPath(path, out string relativePath)
+ .FileResult(relativePath);
+ }
+
+ ///
+ public Task GetReader(string path)
+ {
+ if (path == null)
+ throw new ArgumentNullException(nameof(path));
+ return _GetFileSystemForPath(path, out string relativePath)
+ .GetReader(relativePath);
+ }
+
+ ///
+ public Task NewFile(string path)
+ {
+ if (path == null)
+ throw new ArgumentNullException(nameof(path));
+ return _GetFileSystemForPath(path, out string relativePath)
+ .NewFile(relativePath);
+ }
+
+ ///
+ public Task CreateDirectory(string path)
+ {
+ if (path == null)
+ throw new ArgumentNullException(nameof(path));
+ return _GetFileSystemForPath(path, out string relativePath)
+ .CreateDirectory(relativePath);
+ }
+
+ ///
+ public string Combine(params string[] paths)
+ {
+ return _GetFileSystemForPath(paths[0], out string relativePath)
+ .Combine(paths[1..].Prepend(relativePath).ToArray());
+ }
+
+ ///
+ public Task> ListFiles(string path, SearchOption options = SearchOption.TopDirectoryOnly)
+ {
+ if (path == null)
+ throw new ArgumentNullException(nameof(path));
+ return _GetFileSystemForPath(path, out string relativePath)
+ .ListFiles(relativePath, options);
+ }
+
+ ///
+ public Task Exists(string path)
+ {
+ if (path == null)
+ throw new ArgumentNullException(nameof(path));
+ return _GetFileSystemForPath(path, out string relativePath)
+ .Exists(relativePath);
+ }
+
+ ///
+ public string GetExtraDirectory(Show show)
+ {
+ if (show == null)
+ throw new ArgumentNullException(nameof(show));
+ return _GetFileSystemForPath(show.Path, out string _)
+ .GetExtraDirectory(show);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Kyoo/Controllers/FileSystems/HttpFileSystem.cs b/Kyoo/Controllers/FileSystems/HttpFileSystem.cs
new file mode 100644
index 00000000..3d2ea991
--- /dev/null
+++ b/Kyoo/Controllers/FileSystems/HttpFileSystem.cs
@@ -0,0 +1,123 @@
+using System;
+using System.Collections.Generic;
+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
+{
+ ///
+ /// A for http/https links.
+ ///
+ [FileSystemMetadata(new [] {"http", "https"})]
+ public class HttpFileSystem : IFileSystem
+ {
+ ///
+ /// The http client factory used to create clients.
+ ///
+ private readonly IHttpClientFactory _clientFactory;
+
+ ///
+ /// Create a using the given client factory.
+ ///
+ /// The http client factory used to create clients.
+ public HttpFileSystem(IHttpClientFactory factory)
+ {
+ _clientFactory = factory;
+ }
+
+
+ ///
+ public IActionResult FileResult(string path, bool rangeSupport = false, string type = null)
+ {
+ if (path == null)
+ return new NotFoundResult();
+ return new HttpForwardResult(new Uri(path), rangeSupport, type);
+ }
+
+ ///
+ public Task GetReader(string path)
+ {
+ HttpClient client = _clientFactory.CreateClient();
+ return client.GetStreamAsync(path);
+ }
+
+ ///
+ public Task NewFile(string path)
+ {
+ throw new NotSupportedException("An http filesystem is readonly, a new file can't be created.");
+ }
+
+ ///
+ public Task CreateDirectory(string path)
+ {
+ throw new NotSupportedException("An http filesystem is readonly, a directory can't be created.");
+ }
+
+ ///
+ public string Combine(params string[] paths)
+ {
+ return Path.Combine(paths);
+ }
+
+ ///
+ public Task> ListFiles(string path, SearchOption options = SearchOption.TopDirectoryOnly)
+ {
+ throw new NotSupportedException("Listing files is not supported on an http filesystem.");
+ }
+
+ ///
+ public Task Exists(string path)
+ {
+ throw new NotSupportedException("Checking if a file exists is not supported on an http filesystem.");
+ }
+
+ ///
+ public string GetExtraDirectory(Show show)
+ {
+ throw new NotSupportedException("Extras can not be stored inside an http filesystem.");
+ }
+ }
+
+ ///
+ /// An to proxy an http request.
+ ///
+ public class HttpForwardResult : IActionResult
+ {
+ ///
+ /// The path of the request to forward.
+ ///
+ private readonly Uri _path;
+ ///
+ /// Should the proxied result support ranges requests?
+ ///
+ private readonly bool _rangeSupport;
+ ///
+ /// If not null, override the content type of the resulting request.
+ ///
+ private readonly string _type;
+
+ ///
+ /// Create a new .
+ ///
+ /// The path of the request to forward.
+ /// Should the proxied result support ranges requests?
+ /// If not null, override the content type of the resulting request.
+ public HttpForwardResult(Uri path, bool rangeSupport, string type = null)
+ {
+ _path = path;
+ _rangeSupport = rangeSupport;
+ _type = type;
+ }
+
+ ///
+ public Task ExecuteResultAsync(ActionContext context)
+ {
+ // TODO implement that, example: https://github.com/twitchax/AspNetCore.Proxy/blob/14dd0f212d7abb43ca1bf8c890d5efb95db66acb/src/Core/Extensions/Http.cs#L15
+ throw new NotImplementedException();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Kyoo/Controllers/FileManager.cs b/Kyoo/Controllers/FileSystems/LocalFileSystem.cs
similarity index 77%
rename from Kyoo/Controllers/FileManager.cs
rename to Kyoo/Controllers/FileSystems/LocalFileSystem.cs
index 4aa89467..dc1c4b9d 100644
--- a/Kyoo/Controllers/FileManager.cs
+++ b/Kyoo/Controllers/FileSystems/LocalFileSystem.cs
@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
+using Kyoo.Common.Models.Attributes;
using Kyoo.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.StaticFiles;
@@ -9,9 +10,10 @@ using Microsoft.AspNetCore.StaticFiles;
namespace Kyoo.Controllers
{
///
- /// A for the local filesystem (using System.IO).
+ /// A for the local filesystem (using System.IO).
///
- public class FileManager : IFileManager
+ [FileSystemMetadata(new [] {"", "file"}, StripScheme = true)]
+ public class LocalFileSystem : IFileSystem
{
///
/// An extension provider to get content types from files extensions.
@@ -54,19 +56,19 @@ namespace Kyoo.Controllers
}
///
- public Stream GetReader(string path)
+ public Task GetReader(string path)
{
if (path == null)
throw new ArgumentNullException(nameof(path));
- return File.OpenRead(path);
+ return Task.FromResult(File.OpenRead(path));
}
///
- public Stream NewFile(string path)
+ public Task NewFile(string path)
{
if (path == null)
throw new ArgumentNullException(nameof(path));
- return File.Create(path);
+ return Task.FromResult(File.Create(path));
}
///
@@ -108,24 +110,5 @@ namespace Kyoo.Controllers
Directory.CreateDirectory(path);
return path;
}
-
- ///
- public string GetExtraDirectory(Season season)
- {
- if (season.Show == null)
- throw new NotImplementedException("Can't get season's extra directory when season.Show == null.");
- // TODO use a season.Path here.
- string path = Path.Combine(season.Show.Path, "Extra");
- Directory.CreateDirectory(path);
- return path;
- }
-
- ///
- public string GetExtraDirectory(Episode episode)
- {
- string path = Path.Combine(Path.GetDirectoryName(episode.Path)!, "Extra");
- Directory.CreateDirectory(path);
- return path;
- }
}
}
\ No newline at end of file
diff --git a/Kyoo/Controllers/ThumbnailsManager.cs b/Kyoo/Controllers/ThumbnailsManager.cs
index f76123ee..389891b3 100644
--- a/Kyoo/Controllers/ThumbnailsManager.cs
+++ b/Kyoo/Controllers/ThumbnailsManager.cs
@@ -17,7 +17,7 @@ namespace Kyoo.Controllers
///
/// The file manager used to download the image if the file is distant
///
- private readonly IFileManager _files;
+ private readonly IFileSystem _files;
///
/// A logger to report errors.
///
@@ -26,6 +26,10 @@ namespace Kyoo.Controllers
/// The options containing the base path of people images and provider logos.
///
private readonly IOptionsMonitor _options;
+ ///
+ /// A library manager used to load episode and seasons shows if they are not loaded.
+ ///
+ private readonly Lazy _library;
///
/// Create a new .
@@ -33,13 +37,16 @@ namespace Kyoo.Controllers
/// The file manager to use.
/// A logger to report errors
/// The options to use.
- public ThumbnailsManager(IFileManager files,
+ /// A library manager used to load shows if they are not loaded.
+ public ThumbnailsManager(IFileSystem files,
ILogger logger,
- IOptionsMonitor options)
+ IOptionsMonitor options,
+ Lazy library)
{
_files = files;
_logger = logger;
_options = options;
+ _library = library;
options.OnChange(x =>
{
@@ -66,7 +73,7 @@ namespace Kyoo.Controllers
}
///
- /// An helper function to download an image using a .
+ /// An helper function to download an image using a .
///
/// The distant url of the image
/// The local path of the image
@@ -79,8 +86,8 @@ namespace Kyoo.Controllers
try
{
- await using Stream reader = _files.GetReader(url);
- await using Stream local = _files.NewFile(localPath);
+ await using Stream reader = await _files.GetReader(url);
+ await using Stream local = await _files.NewFile(localPath);
await reader.CopyToAsync(local);
return true;
}
@@ -185,7 +192,7 @@ namespace Kyoo.Controllers
if (episode.Thumb == null)
return false;
- string localPath = await GetEpisodeThumb(episode);
+ string localPath = await _GetEpisodeThumb(episode);
if (alwaysDownload || !await _files.Exists(localPath))
return await _DownloadImage(episode.Thumb, localPath, $"The thumbnail of {episode.Slug}");
return false;
@@ -218,13 +225,25 @@ namespace Kyoo.Controllers
{
if (item == null)
throw new ArgumentNullException(nameof(item));
- return Task.FromResult(item switch
+ return item switch
{
- Show show => _files.Combine(_files.GetExtraDirectory(show), "poster.jpg"),
- Season season => _files.Combine(_files.GetExtraDirectory(season), $"season-{season.SeasonNumber}.jpg"),
- People people => _files.Combine(_options.CurrentValue.PeoplePath, $"{people.Slug}.jpg"),
+ Show show => Task.FromResult(_files.Combine(_files.GetExtraDirectory(show), "poster.jpg")),
+ Season season => _GetSeasonPoster(season),
+ People actor => Task.FromResult(_files.Combine(_options.CurrentValue.PeoplePath, $"{actor.Slug}.jpg")),
_ => throw new NotSupportedException($"The type {typeof(T).Name} does not have a poster.")
- });
+ };
+ }
+
+ ///
+ /// Retrieve the path of a season's poster.
+ ///
+ /// The season to retrieve the poster from.
+ /// The path of the season's poster.
+ private async Task _GetSeasonPoster(Season season)
+ {
+ if (season.Show == null)
+ await _library.Value.Load(season, x => x.Show);
+ return _files.Combine(_files.GetExtraDirectory(season.Show), $"season-{season.SeasonNumber}.jpg");
}
///
@@ -236,14 +255,21 @@ namespace Kyoo.Controllers
return item switch
{
Show show => Task.FromResult(_files.Combine(_files.GetExtraDirectory(show), "backdrop.jpg")),
- Episode episode => GetEpisodeThumb(episode),
+ Episode episode => _GetEpisodeThumb(episode),
_ => throw new NotSupportedException($"The type {typeof(T).Name} does not have a thumbnail.")
};
}
- private async Task GetEpisodeThumb(Episode episode)
+ ///
+ /// Get the path for an episode's thumbnail.
+ ///
+ /// The episode to retrieve the thumbnail from
+ /// The path of the given episode's thumbnail.
+ private async Task _GetEpisodeThumb(Episode episode)
{
- string dir = _files.Combine(_files.GetExtraDirectory(episode), "Thumbnails");
+ if (episode.Show == null)
+ await _library.Value.Load(episode, x => x.Show);
+ string dir = _files.Combine(_files.GetExtraDirectory(episode.Show), "Thumbnails");
await _files.CreateDirectory(dir);
return _files.Combine(dir, $"{Path.GetFileNameWithoutExtension(episode.Path)}.jpg");
}
diff --git a/Kyoo/Controllers/Transcoder.cs b/Kyoo/Controllers/Transcoder.cs
index 8f6e006e..47291753 100644
--- a/Kyoo/Controllers/Transcoder.cs
+++ b/Kyoo/Controllers/Transcoder.cs
@@ -72,24 +72,29 @@ namespace Kyoo.Controllers
}
}
- private readonly IFileManager _files;
+ private readonly IFileSystem _files;
private readonly IOptions _options;
+ private readonly Lazy _library;
- public Transcoder(IFileManager files, IOptions options)
+ public Transcoder(IFileSystem files, IOptions options, Lazy library)
{
_files = files;
_options = options;
+ _library = library;
if (TranscoderAPI.init() != Marshal.SizeOf())
throw new BadTranscoderException();
}
- public Task