diff --git a/src/Kyoo.Abstractions/Controllers/ILibraryManager.cs b/src/Kyoo.Abstractions/Controllers/ILibraryManager.cs
index f4218760..1bbe147f 100644
--- a/src/Kyoo.Abstractions/Controllers/ILibraryManager.cs
+++ b/src/Kyoo.Abstractions/Controllers/ILibraryManager.cs
@@ -200,10 +200,11 @@ namespace Kyoo.Abstractions.Controllers
/// Get the resource by a filter function or null if it is not found.
///
/// The filter function.
+ /// A custom sort method to handle cases where multiples items match the filters.
/// The type of the resource
/// The first resource found that match the where function
[ItemCanBeNull]
- Task GetOrDefault(Expression> where)
+ Task GetOrDefault(Expression> where, Sort sortBy = default)
where T : class, IResource;
///
diff --git a/src/Kyoo.Abstractions/Controllers/IRepository.cs b/src/Kyoo.Abstractions/Controllers/IRepository.cs
index 21a65c12..4deae6e8 100644
--- a/src/Kyoo.Abstractions/Controllers/IRepository.cs
+++ b/src/Kyoo.Abstractions/Controllers/IRepository.cs
@@ -81,9 +81,10 @@ namespace Kyoo.Abstractions.Controllers
/// Get the first resource that match the predicate or null if it is not found.
///
/// A predicate to filter the resource.
+ /// A custom sort method to handle cases where multiples items match the filters.
/// The resource found
[ItemCanBeNull]
- Task GetOrDefault(Expression> where);
+ Task GetOrDefault(Expression> where, Sort sortBy = default);
///
/// Search for resources.
@@ -263,6 +264,8 @@ namespace Kyoo.Abstractions.Controllers
///
public interface IEpisodeRepository : IRepository
{
+ // TODO replace the next methods with extension methods.
+
///
/// Get a episode from it's showID, it's seasonNumber and it's episode number.
///
diff --git a/src/Kyoo.Abstractions/Models/WatchItem.cs b/src/Kyoo.Abstractions/Models/WatchItem.cs
index 8c4daab9..5bbf0fb8 100644
--- a/src/Kyoo.Abstractions/Models/WatchItem.cs
+++ b/src/Kyoo.Abstractions/Models/WatchItem.cs
@@ -158,36 +158,50 @@ namespace Kyoo.Abstractions.Models
/// A new WatchItem representing the given episode.
public static async Task FromEpisode(Episode ep, ILibraryManager library)
{
- Episode previous = null;
- Episode next = null;
-
await library.Load(ep, x => x.Show);
await library.Load(ep, x => x.Tracks);
- if (!ep.Show.IsMovie && ep.SeasonNumber != null && ep.EpisodeNumber != null)
+ Episode previous = null;
+ Episode next = null;
+ if (!ep.Show.IsMovie)
{
- if (ep.EpisodeNumber > 1)
- previous = await library.GetOrDefault(ep.ShowID, ep.SeasonNumber.Value, ep.EpisodeNumber.Value - 1);
- else if (ep.SeasonNumber > 1)
+ if (ep.AbsoluteNumber != null)
{
- previous = (await library.GetAll(x => x.ShowID == ep.ShowID
- && x.SeasonNumber == ep.SeasonNumber.Value - 1,
- limit: 1,
- sort: new Sort(x => x.EpisodeNumber, true))
- ).FirstOrDefault();
+ previous = await library.GetOrDefault(
+ x => x.ShowID == ep.ShowID && x.AbsoluteNumber <= ep.AbsoluteNumber,
+ new Sort(x => x.AbsoluteNumber, true)
+ );
+ next = await library.GetOrDefault(
+ x => x.ShowID == ep.ShowID && x.AbsoluteNumber >= ep.AbsoluteNumber,
+ new Sort(x => x.AbsoluteNumber)
+ );
}
+ else if (ep.SeasonNumber != null && ep.EpisodeNumber != null)
+ {
+ previous = await library.GetOrDefault(
+ x => x.ShowID == ep.ShowID
+ && x.SeasonNumber == ep.SeasonNumber
+ && x.EpisodeNumber < ep.EpisodeNumber,
+ new Sort(x => x.EpisodeNumber, true)
+ );
+ previous ??= await library.GetOrDefault(
+ x => x.ShowID == ep.ShowID
+ && x.SeasonNumber == ep.SeasonNumber - 1,
+ new Sort(x => x.EpisodeNumber, true)
+ );
- if (ep.EpisodeNumber >= await library.GetCount(x => x.SeasonID == ep.SeasonID))
- next = await library.GetOrDefault(ep.ShowID, ep.SeasonNumber.Value + 1, 1);
- else
- next = await library.GetOrDefault(ep.ShowID, ep.SeasonNumber.Value, ep.EpisodeNumber.Value + 1);
- }
- else if (!ep.Show.IsMovie && ep.AbsoluteNumber != null)
- {
- previous = await library.GetOrDefault(x => x.ShowID == ep.ShowID
- && x.AbsoluteNumber == ep.EpisodeNumber + 1);
- next = await library.GetOrDefault(x => x.ShowID == ep.ShowID
- && x.AbsoluteNumber == ep.AbsoluteNumber + 1);
+ next = await library.GetOrDefault(
+ x => x.ShowID == ep.ShowID
+ && x.SeasonNumber == ep.SeasonNumber
+ && x.EpisodeNumber > ep.EpisodeNumber,
+ new Sort(x => x.EpisodeNumber)
+ );
+ next ??= await library.GetOrDefault(
+ x => x.ShowID == ep.ShowID
+ && x.SeasonNumber == ep.SeasonNumber + 1,
+ new Sort(x => x.EpisodeNumber)
+ );
+ }
}
return new WatchItem
diff --git a/src/Kyoo.Core/Controllers/LibraryManager.cs b/src/Kyoo.Core/Controllers/LibraryManager.cs
index a6cd9f44..5243ee36 100644
--- a/src/Kyoo.Core/Controllers/LibraryManager.cs
+++ b/src/Kyoo.Core/Controllers/LibraryManager.cs
@@ -163,10 +163,10 @@ namespace Kyoo.Core.Controllers
}
///
- public async Task GetOrDefault(Expression> where)
+ public async Task GetOrDefault(Expression> where, Sort sortBy)
where T : class, IResource
{
- return await GetRepository().GetOrDefault(where);
+ return await GetRepository().GetOrDefault(where, sortBy);
}
///
diff --git a/src/Kyoo.Core/Controllers/Repositories/LocalRepository.cs b/src/Kyoo.Core/Controllers/Repositories/LocalRepository.cs
index 3cc80036..59928d02 100644
--- a/src/Kyoo.Core/Controllers/Repositories/LocalRepository.cs
+++ b/src/Kyoo.Core/Controllers/Repositories/LocalRepository.cs
@@ -115,9 +115,12 @@ namespace Kyoo.Core.Controllers
}
///
- public virtual Task GetOrDefault(Expression> where)
+ public virtual Task GetOrDefault(Expression> where, Sort sortBy = default)
{
- return Database.Set().FirstOrDefaultAsync(where);
+ IQueryable query = Database.Set();
+ Expression> sortKey = sortBy.Key ?? DefaultSort;
+ query = sortBy.Descendant ? query.OrderByDescending(sortKey) : query.OrderBy(sortKey);
+ return query.FirstOrDefaultAsync(where);
}
///
diff --git a/src/Kyoo.Core/Views/Helper/ApiHelper.cs b/src/Kyoo.Core/Views/Helper/ApiHelper.cs
index d2e926fa..7365c5a7 100644
--- a/src/Kyoo.Core/Views/Helper/ApiHelper.cs
+++ b/src/Kyoo.Core/Views/Helper/ApiHelper.cs
@@ -23,10 +23,6 @@ using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Kyoo.Abstractions.Models;
-using Kyoo.Core.Models.Options;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Options;
namespace Kyoo.Core.Api
{
diff --git a/src/Kyoo.Core/Views/Helper/Serializers/JsonPropertyIgnorer.cs b/src/Kyoo.Core/Views/Helper/Serializers/JsonPropertyIgnorer.cs
index 65c00f7f..40a682a9 100644
--- a/src/Kyoo.Core/Views/Helper/Serializers/JsonPropertyIgnorer.cs
+++ b/src/Kyoo.Core/Views/Helper/Serializers/JsonPropertyIgnorer.cs
@@ -29,8 +29,8 @@ namespace Kyoo.Core.Api
{
public class JsonPropertyIgnorer : CamelCasePropertyNamesContractResolver
{
- private int _depth = -1;
private readonly Uri _host;
+ private int _depth = -1;
public JsonPropertyIgnorer(Uri host)
{
diff --git a/src/Kyoo.Core/Views/CollectionApi.cs b/src/Kyoo.Core/Views/Resources/CollectionApi.cs
similarity index 100%
rename from src/Kyoo.Core/Views/CollectionApi.cs
rename to src/Kyoo.Core/Views/Resources/CollectionApi.cs
diff --git a/src/Kyoo.Core/Views/EpisodeApi.cs b/src/Kyoo.Core/Views/Resources/EpisodeApi.cs
similarity index 100%
rename from src/Kyoo.Core/Views/EpisodeApi.cs
rename to src/Kyoo.Core/Views/Resources/EpisodeApi.cs
diff --git a/src/Kyoo.Core/Views/LibraryApi.cs b/src/Kyoo.Core/Views/Resources/LibraryApi.cs
similarity index 100%
rename from src/Kyoo.Core/Views/LibraryApi.cs
rename to src/Kyoo.Core/Views/Resources/LibraryApi.cs
diff --git a/src/Kyoo.Core/Views/LibraryItemApi.cs b/src/Kyoo.Core/Views/Resources/LibraryItemApi.cs
similarity index 100%
rename from src/Kyoo.Core/Views/LibraryItemApi.cs
rename to src/Kyoo.Core/Views/Resources/LibraryItemApi.cs
diff --git a/src/Kyoo.Core/Views/SeasonApi.cs b/src/Kyoo.Core/Views/Resources/SeasonApi.cs
similarity index 100%
rename from src/Kyoo.Core/Views/SeasonApi.cs
rename to src/Kyoo.Core/Views/Resources/SeasonApi.cs
diff --git a/src/Kyoo.Core/Views/ShowApi.cs b/src/Kyoo.Core/Views/Resources/ShowApi.cs
similarity index 100%
rename from src/Kyoo.Core/Views/ShowApi.cs
rename to src/Kyoo.Core/Views/Resources/ShowApi.cs
diff --git a/src/Kyoo.Core/Views/TrackApi.cs b/src/Kyoo.Core/Views/Watch/TrackApi.cs
similarity index 100%
rename from src/Kyoo.Core/Views/TrackApi.cs
rename to src/Kyoo.Core/Views/Watch/TrackApi.cs
diff --git a/src/Kyoo.Core/Views/Watch/WatchApi.cs b/src/Kyoo.Core/Views/Watch/WatchApi.cs
new file mode 100644
index 00000000..f72b1901
--- /dev/null
+++ b/src/Kyoo.Core/Views/Watch/WatchApi.cs
@@ -0,0 +1,83 @@
+// 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.Threading.Tasks;
+using Kyoo.Abstractions.Controllers;
+using Kyoo.Abstractions.Models;
+using Kyoo.Abstractions.Models.Attributes;
+using Kyoo.Abstractions.Models.Permissions;
+using Kyoo.Abstractions.Models.Utils;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using static Kyoo.Abstractions.Models.Utils.Constants;
+
+namespace Kyoo.Core.Api
+{
+ ///
+ /// Retrieve information of an as a .
+ /// A watch item is another representation of an episode in a form easier to read and display for playback.
+ /// It contains streams (video, audio, subtitles) information, chapters, next and previous episodes and a bit of
+ /// information of the show.
+ ///
+ [Route("api/watch")]
+ [Route("api/watchitem", Order = AlternativeRoute)]
+ [ApiController]
+ [ApiDefinition("Watch Items", Group = WatchGroup)]
+ public class WatchApi : ControllerBase
+ {
+ ///
+ /// The library manager used to modify or retrieve information in the data store.
+ ///
+ private readonly ILibraryManager _libraryManager;
+
+ ///
+ /// Create a new .
+ ///
+ ///
+ /// The library manager used to modify or retrieve information in the data store.
+ ///
+ public WatchApi(ILibraryManager libraryManager)
+ {
+ _libraryManager = libraryManager;
+ }
+
+ ///
+ /// Get a watch item
+ ///
+ ///
+ /// Retrieve a watch item of an episode.
+ ///
+ /// The ID or slug of the .
+ /// A page of items.
+ /// No episode with the given ID or slug could be found.
+ [HttpGet("{identifier:id}")]
+ [Permission("watch", Kind.Read)]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status404NotFound)]
+ public async Task> GetWatchItem(Identifier identifier)
+ {
+ Episode item = await identifier.Match(
+ id => _libraryManager.GetOrDefault(id),
+ slug => _libraryManager.GetOrDefault(slug)
+ );
+ if (item == null)
+ return NotFound();
+ return await WatchItem.FromEpisode(item, _libraryManager);
+ }
+ }
+}
diff --git a/src/Kyoo.Core/Views/WatchApi.cs b/src/Kyoo.Core/Views/WatchApi.cs
deleted file mode 100644
index 74dba487..00000000
--- a/src/Kyoo.Core/Views/WatchApi.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-// 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.Threading.Tasks;
-using Kyoo.Abstractions.Controllers;
-using Kyoo.Abstractions.Models;
-using Kyoo.Abstractions.Models.Exceptions;
-using Kyoo.Abstractions.Models.Permissions;
-using Microsoft.AspNetCore.Mvc;
-
-namespace Kyoo.Core.Api
-{
- [Route("api/watch")]
- [ApiController]
- public class WatchApi : ControllerBase
- {
- private readonly ILibraryManager _libraryManager;
-
- public WatchApi(ILibraryManager libraryManager)
- {
- _libraryManager = libraryManager;
- }
-
- [HttpGet("{slug}")]
- [Permission("video", Kind.Read)]
- public async Task> GetWatchItem(string slug)
- {
- try
- {
- Episode item = await _libraryManager.Get(slug);
- return await WatchItem.FromEpisode(item, _libraryManager);
- }
- catch (ItemNotFoundException)
- {
- return NotFound();
- }
- }
- }
-}