mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-07-09 03:04:20 -04:00
Cleaning up the configuration settings
This commit is contained in:
parent
8db47d62fd
commit
540a3c27de
@ -109,18 +109,18 @@ namespace Kyoo.Models
|
|||||||
if (!ep.Show.IsMovie)
|
if (!ep.Show.IsMovie)
|
||||||
{
|
{
|
||||||
if (ep.EpisodeNumber > 1)
|
if (ep.EpisodeNumber > 1)
|
||||||
previous = await library.Get(ep.ShowID, ep.SeasonNumber, ep.EpisodeNumber - 1);
|
previous = await library.GetOrDefault(ep.ShowID, ep.SeasonNumber, ep.EpisodeNumber - 1);
|
||||||
else if (ep.SeasonNumber > 1)
|
else if (ep.SeasonNumber > 1)
|
||||||
{
|
{
|
||||||
int count = await library.GetCount<Episode>(x => x.ShowID == ep.ShowID
|
int count = await library.GetCount<Episode>(x => x.ShowID == ep.ShowID
|
||||||
&& x.SeasonNumber == ep.SeasonNumber - 1);
|
&& x.SeasonNumber == ep.SeasonNumber - 1);
|
||||||
previous = await library.Get(ep.ShowID, ep.SeasonNumber - 1, count);
|
previous = await library.GetOrDefault(ep.ShowID, ep.SeasonNumber - 1, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ep.EpisodeNumber >= await library.GetCount<Episode>(x => x.SeasonID == ep.SeasonID))
|
if (ep.EpisodeNumber >= await library.GetCount<Episode>(x => x.SeasonID == ep.SeasonID))
|
||||||
next = await library.Get(ep.ShowID, ep.SeasonNumber + 1, 1);
|
next = await library.GetOrDefault(ep.ShowID, ep.SeasonNumber + 1, 1);
|
||||||
else
|
else
|
||||||
next = await library.Get(ep.ShowID, ep.SeasonNumber, ep.EpisodeNumber + 1);
|
next = await library.GetOrDefault(ep.ShowID, ep.SeasonNumber, ep.EpisodeNumber + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new WatchItem(ep.ID,
|
return new WatchItem(ep.ID,
|
||||||
|
@ -86,7 +86,7 @@ namespace Kyoo.Controllers
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override async Task<Episode> GetOrDefault(int id)
|
public override async Task<Episode> GetOrDefault(int id)
|
||||||
{
|
{
|
||||||
Episode ret = await base.Get(id);
|
Episode ret = await base.GetOrDefault(id);
|
||||||
if (ret != null)
|
if (ret != null)
|
||||||
ret.ShowSlug = await _shows.GetSlug(ret.ShowID);
|
ret.ShowSlug = await _shows.GetSlug(ret.ShowID);
|
||||||
return ret;
|
return ret;
|
||||||
@ -111,9 +111,9 @@ namespace Kyoo.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override async Task<Episode> GetOrDefault(Expression<Func<Episode, bool>> predicate)
|
public override async Task<Episode> GetOrDefault(Expression<Func<Episode, bool>> where)
|
||||||
{
|
{
|
||||||
Episode ret = await base.Get(predicate);
|
Episode ret = await base.GetOrDefault(where);
|
||||||
if (ret != null)
|
if (ret != null)
|
||||||
ret.ShowSlug = await _shows.GetSlug(ret.ShowID);
|
ret.ShowSlug = await _shows.GetSlug(ret.ShowID);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -92,15 +92,15 @@ namespace Kyoo.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override async Task<LibraryItem> Get(int id)
|
public override async Task<LibraryItem> GetOrDefault(int id)
|
||||||
{
|
{
|
||||||
return id > 0
|
return id > 0
|
||||||
? new LibraryItem(await _shows.Value.Get(id))
|
? new LibraryItem(await _shows.Value.GetOrDefault(id))
|
||||||
: new LibraryItem(await _collections.Value.Get(-id));
|
: new LibraryItem(await _collections.Value.GetOrDefault(-id));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override Task<LibraryItem> Get(string slug)
|
public override Task<LibraryItem> GetOrDefault(string slug)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("You can't get a library item by a slug.");
|
throw new InvalidOperationException("You can't get a library item by a slug.");
|
||||||
}
|
}
|
||||||
@ -189,7 +189,7 @@ namespace Kyoo.Controllers
|
|||||||
where,
|
where,
|
||||||
sort,
|
sort,
|
||||||
limit);
|
limit);
|
||||||
if (!items.Any() && await _libraries.Value.Get(id) == null)
|
if (!items.Any() && await _libraries.Value.GetOrDefault(id) == null)
|
||||||
throw new ItemNotFound();
|
throw new ItemNotFound();
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
@ -204,7 +204,7 @@ namespace Kyoo.Controllers
|
|||||||
where,
|
where,
|
||||||
sort,
|
sort,
|
||||||
limit);
|
limit);
|
||||||
if (!items.Any() && await _libraries.Value.Get(slug) == null)
|
if (!items.Any() && await _libraries.Value.GetOrDefault(slug) == null)
|
||||||
throw new ItemNotFound();
|
throw new ItemNotFound();
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
@ -98,9 +98,9 @@ namespace Kyoo.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override async Task<Season> Get(Expression<Func<Season, bool>> predicate)
|
public override async Task<Season> Get(Expression<Func<Season, bool>> where)
|
||||||
{
|
{
|
||||||
Season ret = await base.Get(predicate);
|
Season ret = await base.Get(where);
|
||||||
ret.ShowSlug = await _shows.GetSlug(ret.ShowID);
|
ret.ShowSlug = await _shows.GetSlug(ret.ShowID);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -36,13 +36,13 @@ namespace Kyoo.Controllers
|
|||||||
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override Task<Track> Get(string slug)
|
Task<Track> IRepository<Track>.Get(string slug)
|
||||||
{
|
{
|
||||||
return Get(slug, StreamType.Unknown);
|
return Get(slug);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async Task<Track> Get(string slug, StreamType type)
|
public async Task<Track> Get(string slug, StreamType type = StreamType.Unknown)
|
||||||
{
|
{
|
||||||
Track ret = await GetOrDefault(slug, type);
|
Track ret = await GetOrDefault(slug, type);
|
||||||
if (ret == null)
|
if (ret == null)
|
||||||
@ -51,7 +51,7 @@ namespace Kyoo.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public Task<Track> GetOrDefault(string slug, StreamType type)
|
public Task<Track> GetOrDefault(string slug, StreamType type = StreamType.Unknown)
|
||||||
{
|
{
|
||||||
Match match = Regex.Match(slug,
|
Match match = Regex.Match(slug,
|
||||||
@"(?<show>.*)-s(?<season>\d+)e(?<episode>\d+)(\.(?<type>\w*))?\.(?<language>.{0,3})(?<forced>-forced)?(\..*)?");
|
@"(?<show>.*)-s(?<season>\d+)e(?<episode>\d+)(\.(?<type>\w*))?\.(?<language>.{0,3})(?<forced>-forced)?(\..*)?");
|
||||||
|
@ -113,10 +113,8 @@
|
|||||||
</Target>
|
</Target>
|
||||||
|
|
||||||
<Target Name="BuildTranscoder" BeforeTargets="BeforeBuild" Condition="'$(SkipTranscoder)' != 'true'">
|
<Target Name="BuildTranscoder" BeforeTargets="BeforeBuild" Condition="'$(SkipTranscoder)' != 'true'">
|
||||||
<Exec WorkingDirectory="$(TranscoderRoot)" Condition="'$(IsWindows)' != 'true'"
|
<Exec WorkingDirectory="$(TranscoderRoot)" Condition="'$(IsWindows)' != 'true'" Command="mkdir -p build %26%26 cd build %26%26 cmake .. %26%26 make -j" />
|
||||||
Command="mkdir -p build %26%26 cd build %26%26 cmake .. %26%26 make -j" />
|
<Exec WorkingDirectory="$(TranscoderRoot)" Condition="'$(IsWindows)' == 'true'" Command="(if not exist build mkdir build) %26%26 cd build %26%26 cmake .. -G "NMake Makefiles" %26%26 nmake" />
|
||||||
<Exec WorkingDirectory="$(TranscoderRoot)" Condition="'$(IsWindows)' == 'true'"
|
|
||||||
Command='(if not exist build mkdir build) %26%26 cd build %26%26 cmake .. -G "NMake Makefiles" %26%26 nmake' />
|
|
||||||
<Copy SourceFiles="$(TranscoderRoot)/build/$(TranscoderBinary)" DestinationFolder="." />
|
<Copy SourceFiles="$(TranscoderRoot)/build/$(TranscoderBinary)" DestinationFolder="." />
|
||||||
</Target>
|
</Target>
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ using System;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Kyoo.Controllers;
|
||||||
using Kyoo.Models;
|
using Kyoo.Models;
|
||||||
using Kyoo.Models.Exceptions;
|
using Kyoo.Models.Exceptions;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
@ -10,28 +11,71 @@ using Npgsql;
|
|||||||
|
|
||||||
namespace Kyoo
|
namespace Kyoo
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The database handle used for all local repositories.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// It should not be used directly, to access the database use a <see cref="ILibraryManager"/> or repositories.
|
||||||
|
/// </remarks>
|
||||||
public class DatabaseContext : DbContext
|
public class DatabaseContext : DbContext
|
||||||
{
|
{
|
||||||
public DatabaseContext(DbContextOptions<DatabaseContext> options) : base(options)
|
/// <summary>
|
||||||
{
|
/// All libraries of Kyoo. See <see cref="Library"/>.
|
||||||
ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
|
/// </summary>
|
||||||
ChangeTracker.LazyLoadingEnabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DbSet<Library> Libraries { get; set; }
|
public DbSet<Library> Libraries { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// All collections of Kyoo. See <see cref="Collection"/>.
|
||||||
|
/// </summary>
|
||||||
public DbSet<Collection> Collections { get; set; }
|
public DbSet<Collection> Collections { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// All shows of Kyoo. See <see cref="Show"/>.
|
||||||
|
/// </summary>
|
||||||
public DbSet<Show> Shows { get; set; }
|
public DbSet<Show> Shows { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// All seasons of Kyoo. See <see cref="Season"/>.
|
||||||
|
/// </summary>
|
||||||
public DbSet<Season> Seasons { get; set; }
|
public DbSet<Season> Seasons { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// All episodes of Kyoo. See <see cref="Episode"/>.
|
||||||
|
/// </summary>
|
||||||
public DbSet<Episode> Episodes { get; set; }
|
public DbSet<Episode> Episodes { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// All tracks of Kyoo. See <see cref="Track"/>.
|
||||||
|
/// </summary>
|
||||||
public DbSet<Track> Tracks { get; set; }
|
public DbSet<Track> Tracks { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// All genres of Kyoo. See <see cref="Genres"/>.
|
||||||
|
/// </summary>
|
||||||
public DbSet<Genre> Genres { get; set; }
|
public DbSet<Genre> Genres { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// All people of Kyoo. See <see cref="People"/>.
|
||||||
|
/// </summary>
|
||||||
public DbSet<People> People { get; set; }
|
public DbSet<People> People { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// All studios of Kyoo. See <see cref="Studio"/>.
|
||||||
|
/// </summary>
|
||||||
public DbSet<Studio> Studios { get; set; }
|
public DbSet<Studio> Studios { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// All providers of Kyoo. See <see cref="Provider"/>.
|
||||||
|
/// </summary>
|
||||||
public DbSet<Provider> Providers { get; set; }
|
public DbSet<Provider> Providers { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// All metadataIDs (ExternalIDs) of Kyoo. See <see cref="MetadataID"/>.
|
||||||
|
/// </summary>
|
||||||
public DbSet<MetadataID> MetadataIds { get; set; }
|
public DbSet<MetadataID> MetadataIds { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// All people's role. See <see cref="PeopleRole"/>.
|
||||||
|
/// </summary>
|
||||||
public DbSet<PeopleRole> PeopleRoles { get; set; }
|
public DbSet<PeopleRole> PeopleRoles { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a generic link between two resource types.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Types are order dependant. You can't inverse the order. Please always put the owner first.</remarks>
|
||||||
|
/// <typeparam name="T1">The first resource type of the relation. It is the owner of the second</typeparam>
|
||||||
|
/// <typeparam name="T2">The second resource type of the relation. It is the contained resource.</typeparam>
|
||||||
|
/// <returns>All links between the two types.</returns>
|
||||||
public DbSet<Link<T1, T2>> Links<T1, T2>()
|
public DbSet<Link<T1, T2>> Links<T1, T2>()
|
||||||
where T1 : class, IResource
|
where T1 : class, IResource
|
||||||
where T2 : class, IResource
|
where T2 : class, IResource
|
||||||
@ -39,7 +83,10 @@ namespace Kyoo
|
|||||||
return Set<Link<T1, T2>>();
|
return Set<Link<T1, T2>>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A basic constructor that set default values (query tracker behaviors, mapping enums...)
|
||||||
|
/// </summary>
|
||||||
public DatabaseContext()
|
public DatabaseContext()
|
||||||
{
|
{
|
||||||
NpgsqlConnection.GlobalTypeMapper.MapEnum<Status>();
|
NpgsqlConnection.GlobalTypeMapper.MapEnum<Status>();
|
||||||
@ -50,6 +97,21 @@ namespace Kyoo
|
|||||||
ChangeTracker.LazyLoadingEnabled = false;
|
ChangeTracker.LazyLoadingEnabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new <see cref="DatabaseContext"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="options">Connection options to use (witch databse provider to use, connection strings...)</param>
|
||||||
|
public DatabaseContext(DbContextOptions<DatabaseContext> options)
|
||||||
|
: base(options)
|
||||||
|
{
|
||||||
|
ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
|
||||||
|
ChangeTracker.LazyLoadingEnabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set database parameters to support every types of Kyoo.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="modelBuilder">The database's model builder.</param>
|
||||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
{
|
{
|
||||||
base.OnModelCreating(modelBuilder);
|
base.OnModelCreating(modelBuilder);
|
||||||
@ -58,14 +120,6 @@ namespace Kyoo
|
|||||||
modelBuilder.HasPostgresEnum<ItemType>();
|
modelBuilder.HasPostgresEnum<ItemType>();
|
||||||
modelBuilder.HasPostgresEnum<StreamType>();
|
modelBuilder.HasPostgresEnum<StreamType>();
|
||||||
|
|
||||||
// modelBuilder.Entity<Library>()
|
|
||||||
// .Property(x => x.Paths)
|
|
||||||
// .HasColumnType("text[]");
|
|
||||||
//
|
|
||||||
// modelBuilder.Entity<Show>()
|
|
||||||
// .Property(x => x.Aliases)
|
|
||||||
// .HasColumnType("text[]");
|
|
||||||
|
|
||||||
modelBuilder.Entity<Track>()
|
modelBuilder.Entity<Track>()
|
||||||
.Property(t => t.IsDefault)
|
.Property(t => t.IsDefault)
|
||||||
.ValueGeneratedNever();
|
.ValueGeneratedNever();
|
||||||
@ -196,6 +250,13 @@ namespace Kyoo
|
|||||||
.IsUnique();
|
.IsUnique();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Return a new or an in cache temporary object wih the same ID as the one given
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="model">If a resource with the same ID is found in the database, it will be used.
|
||||||
|
/// <see cref="model"/> will be used overwise</param>
|
||||||
|
/// <typeparam name="T">The type of the resource</typeparam>
|
||||||
|
/// <returns>A resource that is now tracked by this context.</returns>
|
||||||
public T GetTemporaryObject<T>(T model)
|
public T GetTemporaryObject<T>(T model)
|
||||||
where T : class, IResource
|
where T : class, IResource
|
||||||
{
|
{
|
||||||
@ -206,6 +267,11 @@ namespace Kyoo
|
|||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Save changes that are applied to this context.
|
||||||
|
/// </summary>
|
||||||
|
/// <exception cref="DuplicatedItemException">A duplicated item has been found.</exception>
|
||||||
|
/// <returns>The number of state entries written to the database.</returns>
|
||||||
public override int SaveChanges()
|
public override int SaveChanges()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@ -221,6 +287,13 @@ namespace Kyoo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Save changes that are applied to this context.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="acceptAllChangesOnSuccess">Indicates whether AcceptAllChanges() is called after the changes
|
||||||
|
/// have been sent successfully to the database.</param>
|
||||||
|
/// <exception cref="DuplicatedItemException">A duplicated item has been found.</exception>
|
||||||
|
/// <returns>The number of state entries written to the database.</returns>
|
||||||
public override int SaveChanges(bool acceptAllChangesOnSuccess)
|
public override int SaveChanges(bool acceptAllChangesOnSuccess)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@ -236,6 +309,13 @@ namespace Kyoo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Save changes that are applied to this context.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="duplicateMessage">The message that will have the <see cref="DuplicatedItemException"/>
|
||||||
|
/// (if a duplicate is found).</param>
|
||||||
|
/// <exception cref="DuplicatedItemException">A duplicated item has been found.</exception>
|
||||||
|
/// <returns>The number of state entries written to the database.</returns>
|
||||||
public int SaveChanges(string duplicateMessage)
|
public int SaveChanges(string duplicateMessage)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@ -251,6 +331,14 @@ namespace Kyoo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Save changes that are applied to this context.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="acceptAllChangesOnSuccess">Indicates whether AcceptAllChanges() is called after the changes
|
||||||
|
/// have been sent successfully to the database.</param>
|
||||||
|
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to observe while waiting for the task to complete</param>
|
||||||
|
/// <exception cref="DuplicatedItemException">A duplicated item has been found.</exception>
|
||||||
|
/// <returns>The number of state entries written to the database.</returns>
|
||||||
public override async Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess,
|
public override async Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess,
|
||||||
CancellationToken cancellationToken = new())
|
CancellationToken cancellationToken = new())
|
||||||
{
|
{
|
||||||
@ -267,6 +355,12 @@ namespace Kyoo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Save changes that are applied to this context.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to observe while waiting for the task to complete</param>
|
||||||
|
/// <exception cref="DuplicatedItemException">A duplicated item has been found.</exception>
|
||||||
|
/// <returns>The number of state entries written to the database.</returns>
|
||||||
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = new())
|
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = new())
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@ -282,6 +376,14 @@ namespace Kyoo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Save changes that are applied to this context.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="duplicateMessage">The message that will have the <see cref="DuplicatedItemException"/>
|
||||||
|
/// (if a duplicate is found).</param>
|
||||||
|
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to observe while waiting for the task to complete</param>
|
||||||
|
/// <exception cref="DuplicatedItemException">A duplicated item has been found.</exception>
|
||||||
|
/// <returns>The number of state entries written to the database.</returns>
|
||||||
public async Task<int> SaveChangesAsync(string duplicateMessage,
|
public async Task<int> SaveChangesAsync(string duplicateMessage,
|
||||||
CancellationToken cancellationToken = new())
|
CancellationToken cancellationToken = new())
|
||||||
{
|
{
|
||||||
@ -298,6 +400,12 @@ namespace Kyoo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Save changes if no duplicates are found. If one is found, no change are saved but the current changes are no discarded.
|
||||||
|
/// The current context will still hold those invalid changes.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to observe while waiting for the task to complete</param>
|
||||||
|
/// <returns>The number of state entries written to the database or -1 if a duplicate exist.</returns>
|
||||||
public async Task<int> SaveIfNoDuplicates(CancellationToken cancellationToken = new())
|
public async Task<int> SaveIfNoDuplicates(CancellationToken cancellationToken = new())
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@ -310,12 +418,31 @@ namespace Kyoo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Save items or retry with a custom method if a duplicate is found.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The item to save (other changes of this context will also be saved)</param>
|
||||||
|
/// <param name="onFail">A function to run on fail, the <see cref="obj"/> param wil be mapped.
|
||||||
|
/// The second parameter is the current retry number.</param>
|
||||||
|
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to observe while waiting for the task to complete</param>
|
||||||
|
/// <typeparam name="T">The type of the item to save</typeparam>
|
||||||
|
/// <returns>The number of state entries written to the database.</returns>
|
||||||
public Task<T> SaveOrRetry<T>(T obj, Func<T, int, T> onFail, CancellationToken cancellationToken = new())
|
public Task<T> SaveOrRetry<T>(T obj, Func<T, int, T> onFail, CancellationToken cancellationToken = new())
|
||||||
{
|
{
|
||||||
return SaveOrRetry(obj, onFail, 0, cancellationToken);
|
return SaveOrRetry(obj, onFail, 0, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<T> SaveOrRetry<T>(T obj,
|
/// <summary>
|
||||||
|
/// Save items or retry with a custom method if a duplicate is found.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The item to save (other changes of this context will also be saved)</param>
|
||||||
|
/// <param name="onFail">A function to run on fail, the <see cref="obj"/> param wil be mapped.
|
||||||
|
/// The second parameter is the current retry number.</param>
|
||||||
|
/// <param name="recurse">The current retry number.</param>
|
||||||
|
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to observe while waiting for the task to complete</param>
|
||||||
|
/// <typeparam name="T">The type of the item to save</typeparam>
|
||||||
|
/// <returns>The number of state entries written to the database.</returns>
|
||||||
|
private async Task<T> SaveOrRetry<T>(T obj,
|
||||||
Func<T, int, T> onFail,
|
Func<T, int, T> onFail,
|
||||||
int recurse,
|
int recurse,
|
||||||
CancellationToken cancellationToken = new())
|
CancellationToken cancellationToken = new())
|
||||||
@ -337,11 +464,20 @@ namespace Kyoo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if the exception is a duplicated exception.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>WARNING: this only works for PostgreSQL</remarks>
|
||||||
|
/// <param name="ex">The exception to check</param>
|
||||||
|
/// <returns>True if the exception is a duplicate exception. False otherwise</returns>
|
||||||
private static bool IsDuplicateException(Exception ex)
|
private static bool IsDuplicateException(Exception ex)
|
||||||
{
|
{
|
||||||
return ex.InnerException is PostgresException {SqlState: PostgresErrorCodes.UniqueViolation};
|
return ex.InnerException is PostgresException {SqlState: PostgresErrorCodes.UniqueViolation};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Delete every changes that are on this context.
|
||||||
|
/// </summary>
|
||||||
private void DiscardChanges()
|
private void DiscardChanges()
|
||||||
{
|
{
|
||||||
foreach (EntityEntry entry in ChangeTracker.Entries().Where(x => x.State != EntityState.Unchanged
|
foreach (EntityEntry entry in ChangeTracker.Entries().Where(x => x.State != EntityState.Unchanged
|
||||||
|
@ -3,7 +3,11 @@ using System.IO;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore;
|
using Microsoft.AspNetCore;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.VisualBasic.FileIO;
|
using Microsoft.AspNetCore.Hosting.StaticWebAssets;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Kyoo
|
namespace Kyoo
|
||||||
{
|
{
|
||||||
@ -18,12 +22,9 @@ namespace Kyoo
|
|||||||
/// <param name="args">Command line arguments</param>
|
/// <param name="args">Command line arguments</param>
|
||||||
public static async Task Main(string[] args)
|
public static async Task Main(string[] args)
|
||||||
{
|
{
|
||||||
if (args.Length > 0)
|
if (!File.Exists("./settings.json"))
|
||||||
FileSystem.CurrentDirectory = args[0];
|
File.Copy(Path.Join(AppDomain.CurrentDomain.BaseDirectory, "settings.json"), "settings.json");
|
||||||
if (!File.Exists("./appsettings.json"))
|
|
||||||
File.Copy(Path.Join(AppDomain.CurrentDomain.BaseDirectory, "appsettings.json"), "appsettings.json");
|
|
||||||
|
|
||||||
|
|
||||||
bool? debug = Environment.GetEnvironmentVariable("ENVIRONMENT")?.ToLowerInvariant() switch
|
bool? debug = Environment.GetEnvironmentVariable("ENVIRONMENT")?.ToLowerInvariant() switch
|
||||||
{
|
{
|
||||||
"d" => true,
|
"d" => true,
|
||||||
@ -49,15 +50,50 @@ namespace Kyoo
|
|||||||
await host.Build().RunAsync();
|
await host.Build().RunAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Register settings.json, environment variables and command lines arguments as configuration.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="builder">The configuration builder to use</param>
|
||||||
|
/// <param name="args">The command line arguments</param>
|
||||||
|
/// <returns>The modified configuration builder</returns>
|
||||||
|
private static IConfigurationBuilder SetupConfig(IConfigurationBuilder builder, string[] args)
|
||||||
|
{
|
||||||
|
return builder.AddJsonFile("./settings.json", false, true)
|
||||||
|
.AddEnvironmentVariables()
|
||||||
|
.AddCommandLine(args);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Createa a web host
|
/// Createa a web host
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="args">Command line parameters that can be handled by kestrel</param>
|
/// <param name="args">Command line parameters that can be handled by kestrel</param>
|
||||||
/// <returns>A new web host instance</returns>
|
/// <returns>A new web host instance</returns>
|
||||||
private static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
|
private static IWebHostBuilder CreateWebHostBuilder(string[] args)
|
||||||
WebHost.CreateDefaultBuilder(args)
|
{
|
||||||
.UseKestrel(config => { config.AddServerHeader = false; })
|
WebHost.CreateDefaultBuilder(args);
|
||||||
.UseUrls("http://*:5000")
|
|
||||||
|
return new WebHostBuilder()
|
||||||
|
.UseContentRoot(AppDomain.CurrentDomain.BaseDirectory)
|
||||||
|
.UseConfiguration(SetupConfig(new ConfigurationBuilder(), args).Build())
|
||||||
|
.ConfigureAppConfiguration(x => SetupConfig(x, args))
|
||||||
|
.ConfigureLogging((context, builder) =>
|
||||||
|
{
|
||||||
|
builder.AddConfiguration(context.Configuration.GetSection("logging"))
|
||||||
|
.AddConsole()
|
||||||
|
.AddDebug()
|
||||||
|
.AddEventSourceLogger();
|
||||||
|
})
|
||||||
|
.UseDefaultServiceProvider((context, options) =>
|
||||||
|
{
|
||||||
|
options.ValidateScopes = context.HostingEnvironment.IsDevelopment();
|
||||||
|
if (context.HostingEnvironment.IsDevelopment())
|
||||||
|
StaticWebAssetsLoader.UseStaticWebAssets(context.HostingEnvironment, context.Configuration);
|
||||||
|
})
|
||||||
|
.ConfigureServices(x => x.AddRouting())
|
||||||
|
.UseKestrel(options => { options.AddServerHeader = false; })
|
||||||
|
.UseIIS()
|
||||||
|
.UseIISIntegration()
|
||||||
.UseStartup<Startup>();
|
.UseStartup<Startup>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,14 +64,28 @@ namespace Kyoo.Api
|
|||||||
[Authorize(Policy = "Read")]
|
[Authorize(Policy = "Read")]
|
||||||
public async Task<ActionResult<Season>> GetSeason(string showSlug, int seasonNumber, int episodeNumber)
|
public async Task<ActionResult<Season>> GetSeason(string showSlug, int seasonNumber, int episodeNumber)
|
||||||
{
|
{
|
||||||
return await _libraryManager.Get(showSlug, seasonNumber);
|
try
|
||||||
|
{
|
||||||
|
return await _libraryManager.Get(showSlug, seasonNumber);
|
||||||
|
}
|
||||||
|
catch (ItemNotFound)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("{showID:int}-{seasonNumber:int}e{episodeNumber:int}/season")]
|
[HttpGet("{showID:int}-{seasonNumber:int}e{episodeNumber:int}/season")]
|
||||||
[Authorize(Policy = "Read")]
|
[Authorize(Policy = "Read")]
|
||||||
public async Task<ActionResult<Season>> GetSeason(int showID, int seasonNumber, int episodeNumber)
|
public async Task<ActionResult<Season>> GetSeason(int showID, int seasonNumber, int episodeNumber)
|
||||||
{
|
{
|
||||||
return await _libraryManager.Get(showID, seasonNumber);
|
try
|
||||||
|
{
|
||||||
|
return await _libraryManager.Get(showID, seasonNumber);
|
||||||
|
}
|
||||||
|
catch (ItemNotFound)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("{episodeID:int}/track")]
|
[HttpGet("{episodeID:int}/track")]
|
||||||
@ -120,7 +134,7 @@ namespace Kyoo.Api
|
|||||||
new Sort<Track>(sortBy),
|
new Sort<Track>(sortBy),
|
||||||
new Pagination(limit, afterID));
|
new Pagination(limit, afterID));
|
||||||
|
|
||||||
if (!resources.Any() && await _libraryManager.Get(showID, seasonNumber, episodeNumber) == null)
|
if (!resources.Any() && await _libraryManager.GetOrDefault(showID, seasonNumber, episodeNumber) == null)
|
||||||
return NotFound();
|
return NotFound();
|
||||||
return Page(resources, limit);
|
return Page(resources, limit);
|
||||||
}
|
}
|
||||||
@ -130,10 +144,10 @@ namespace Kyoo.Api
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("{showSlug}-s{seasonNumber:int}e{episodeNumber:int}/track")]
|
[HttpGet("{slug}-s{seasonNumber:int}e{episodeNumber:int}/track")]
|
||||||
[HttpGet("{showSlug}-s{seasonNumber:int}e{episodeNumber:int}/tracks")]
|
[HttpGet("{slug}-s{seasonNumber:int}e{episodeNumber:int}/tracks")]
|
||||||
[Authorize(Policy = "Read")]
|
[Authorize(Policy = "Read")]
|
||||||
public async Task<ActionResult<Page<Track>>> GetEpisode(string showSlug,
|
public async Task<ActionResult<Page<Track>>> GetEpisode(string slug,
|
||||||
int seasonNumber,
|
int seasonNumber,
|
||||||
int episodeNumber,
|
int episodeNumber,
|
||||||
[FromQuery] string sortBy,
|
[FromQuery] string sortBy,
|
||||||
@ -144,13 +158,13 @@ namespace Kyoo.Api
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
ICollection<Track> resources = await _libraryManager.GetAll(
|
ICollection<Track> resources = await _libraryManager.GetAll(
|
||||||
ApiHelper.ParseWhere<Track>(where, x => x.Episode.Show.Slug == showSlug
|
ApiHelper.ParseWhere<Track>(where, x => x.Episode.Show.Slug == slug
|
||||||
&& x.Episode.SeasonNumber == seasonNumber
|
&& x.Episode.SeasonNumber == seasonNumber
|
||||||
&& x.Episode.EpisodeNumber == episodeNumber),
|
&& x.Episode.EpisodeNumber == episodeNumber),
|
||||||
new Sort<Track>(sortBy),
|
new Sort<Track>(sortBy),
|
||||||
new Pagination(limit, afterID));
|
new Pagination(limit, afterID));
|
||||||
|
|
||||||
if (!resources.Any() && await _libraryManager.Get(showSlug, seasonNumber, episodeNumber) == null)
|
if (!resources.Any() && await _libraryManager.GetOrDefault(slug, seasonNumber, episodeNumber) == null)
|
||||||
return NotFound();
|
return NotFound();
|
||||||
return Page(resources, limit);
|
return Page(resources, limit);
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ namespace Kyoo.Api
|
|||||||
new Sort<Episode>(sortBy),
|
new Sort<Episode>(sortBy),
|
||||||
new Pagination(limit, afterID));
|
new Pagination(limit, afterID));
|
||||||
|
|
||||||
if (!resources.Any() && await _libraryManager.Get(showSlug, seasonNumber) == null)
|
if (!resources.Any() && await _libraryManager.GetOrDefault(showSlug, seasonNumber) == null)
|
||||||
return NotFound();
|
return NotFound();
|
||||||
return Page(resources, limit);
|
return Page(resources, limit);
|
||||||
}
|
}
|
||||||
@ -102,7 +102,7 @@ namespace Kyoo.Api
|
|||||||
new Sort<Episode>(sortBy),
|
new Sort<Episode>(sortBy),
|
||||||
new Pagination(limit, afterID));
|
new Pagination(limit, afterID));
|
||||||
|
|
||||||
if (!resources.Any() && await _libraryManager.Get(showID, seasonNumber) == null)
|
if (!resources.Any() && await _libraryManager.GetOrDefault(showID, seasonNumber) == null)
|
||||||
return NotFound();
|
return NotFound();
|
||||||
return Page(resources, limit);
|
return Page(resources, limit);
|
||||||
}
|
}
|
||||||
|
@ -30,14 +30,14 @@ namespace Kyoo.Api
|
|||||||
Track subtitle;
|
Track subtitle;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
subtitle = await _libraryManager.Get(slug, StreamType.Subtitle);
|
subtitle = await _libraryManager.GetOrDefault(slug, StreamType.Subtitle);
|
||||||
}
|
}
|
||||||
catch (ArgumentException ex)
|
catch (ArgumentException ex)
|
||||||
{
|
{
|
||||||
return BadRequest(new {error = ex.Message});
|
return BadRequest(new {error = ex.Message});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (subtitle == null || subtitle.Type != StreamType.Subtitle)
|
if (subtitle is not {Type: StreamType.Subtitle})
|
||||||
return NotFound();
|
return NotFound();
|
||||||
|
|
||||||
if (subtitle.Codec == "subrip" && extension == "vtt")
|
if (subtitle.Codec == "subrip" && extension == "vtt")
|
||||||
|
@ -1,31 +1,25 @@
|
|||||||
{
|
{
|
||||||
"server.urls": "http://0.0.0.0:5000",
|
"server.urls": "http://*:5000",
|
||||||
"public_url": "http://localhost:5000/",
|
"public_url": "http://localhost:5000/",
|
||||||
"http_port": 5000,
|
|
||||||
"https_port": 44300,
|
|
||||||
|
|
||||||
"Database": {
|
"database": {
|
||||||
"Server": "127.0.0.1",
|
"server": "127.0.0.1",
|
||||||
"Port": "5432",
|
"port": "5432",
|
||||||
"Database": "kyooDB",
|
"database": "kyooDB",
|
||||||
"User Id": "kyoo",
|
"user ID": "kyoo",
|
||||||
"Password": "kyooPassword",
|
"password": "kyooPassword",
|
||||||
"Pooling": "true",
|
"pooling": "true",
|
||||||
"MaxPoolSize": "95",
|
"maxPoolSize": "95",
|
||||||
"Timeout": "30"
|
"timeout": "30"
|
||||||
},
|
},
|
||||||
|
|
||||||
"Logging": {
|
"logging": {
|
||||||
"LogLevel": {
|
"logLevel": {
|
||||||
"Default": "Warning",
|
"default": "Warning",
|
||||||
"Microsoft": "Warning",
|
"Microsoft": "Warning",
|
||||||
"Microsoft.Hosting.Lifetime": "Information",
|
"Microsoft.Hosting.Lifetime": "Information"
|
||||||
"Microsoft.EntityFrameworkCore.DbUpdateException": "None",
|
|
||||||
"Microsoft.EntityFrameworkCore.Update": "None",
|
|
||||||
"Microsoft.EntityFrameworkCore.Database.Command": "None"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"AllowedHosts": "*",
|
|
||||||
|
|
||||||
"parallelTasks": "1",
|
"parallelTasks": "1",
|
||||||
|
|
@ -5,7 +5,8 @@ After=network.target
|
|||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
User=kyoo
|
User=kyoo
|
||||||
ExecStart=/usr/lib/kyoo/Kyoo /var/lib/kyoo
|
WorkingDirectory=/var/lib/kyoo
|
||||||
|
ExecStart=/usr/lib/kyoo/Kyoo
|
||||||
Restart=on-abort
|
Restart=on-abort
|
||||||
TimeoutSec=20
|
TimeoutSec=20
|
||||||
|
|
||||||
|
42
settings.json
Normal file
42
settings.json
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
"server.urls": "http://*:5000",
|
||||||
|
"public_url": "http://localhost:5000/",
|
||||||
|
|
||||||
|
"database": {
|
||||||
|
"server": "127.0.0.1",
|
||||||
|
"port": "5432",
|
||||||
|
"database": "kyooDB",
|
||||||
|
"user ID": "kyoo",
|
||||||
|
"password": "kyooPassword",
|
||||||
|
"pooling": "true",
|
||||||
|
"maxPoolSize": "95",
|
||||||
|
"timeout": "30"
|
||||||
|
},
|
||||||
|
|
||||||
|
"logging": {
|
||||||
|
"logLevel": {
|
||||||
|
"default": "Warning",
|
||||||
|
"Microsoft": "Warning",
|
||||||
|
"Microsoft.Hosting.Lifetime": "Information"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"parallelTasks": "1",
|
||||||
|
|
||||||
|
"scheduledTasks": {
|
||||||
|
"scan": "24:00:00"
|
||||||
|
},
|
||||||
|
|
||||||
|
"certificatePassword": "passphrase",
|
||||||
|
|
||||||
|
"transmuxTempPath": "cached/kyoo/transmux",
|
||||||
|
"transcodeTempPath": "cached/kyoo/transcode",
|
||||||
|
"peoplePath": "people",
|
||||||
|
"providerPath": "providers",
|
||||||
|
"profilePicturePath": "users/",
|
||||||
|
"plugins": "plugins/",
|
||||||
|
"defaultPermissions": "read,play,write,admin",
|
||||||
|
"newUserPermissions": "read,play,write,admin",
|
||||||
|
"regex": "(?:\\/(?<Collection>.*?))?\\/(?<Show>.*?)(?: \\(\\d+\\))?\\/\\k<Show>(?: \\(\\d+\\))?(?:(?: S(?<Season>\\d+)E(?<Episode>\\d+))| (?<Absolute>\\d+))?.*$",
|
||||||
|
"subtitleRegex": "^(?<Episode>.*)\\.(?<Language>\\w{1,3})\\.(?<Default>default\\.)?(?<Forced>forced\\.)?.*$"
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user