mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-24 02:02:36 -04:00
Add next update date in db
This commit is contained in:
parent
c1ecdad916
commit
5a9b846f7c
@ -24,8 +24,8 @@ namespace Kyoo.Abstractions.Models;
|
||||
/// <summary>
|
||||
/// A show, a movie or a collection.
|
||||
/// </summary>
|
||||
[OneOf(Types = new[] { typeof(Episode), typeof(Movie) })]
|
||||
public interface INews : IResource, IThumbnails, IMetadata, IAddedDate, IQuery
|
||||
[OneOf(Types = [typeof(Episode), typeof(Movie)])]
|
||||
public interface INews : IResource, IThumbnails, IAddedDate, IQuery
|
||||
{
|
||||
static Sort IQuery.DefaultSort => new Sort<INews>.By(nameof(AddedDate), true);
|
||||
}
|
||||
|
@ -33,3 +33,29 @@ public class MetadataId
|
||||
/// </summary>
|
||||
public string? Link { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ID informations about an episode.
|
||||
/// </summary>
|
||||
public class EpisodeId
|
||||
{
|
||||
/// <summary>
|
||||
/// The Id of the show on the metadata database.
|
||||
/// </summary>
|
||||
public string ShowId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The season number or null if absolute numbering is used in this database.
|
||||
/// </summary>
|
||||
public int? Season { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The episode number or absolute number if Season is null.
|
||||
/// </summary>
|
||||
public int Episode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The URL of the resource on the external provider.
|
||||
/// </summary>
|
||||
public string? Link { get; set; }
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ namespace Kyoo.Abstractions.Models;
|
||||
/// <summary>
|
||||
/// A class representing collections of <see cref="Show"/>.
|
||||
/// </summary>
|
||||
public class Collection : IQuery, IResource, IMetadata, IThumbnails, IAddedDate, ILibraryItem
|
||||
public class Collection : IQuery, IResource, IMetadata, IThumbnails, IAddedDate, IRefreshable, ILibraryItem
|
||||
{
|
||||
public static Sort DefaultSort => new Sort<Collection>.By(nameof(Collection.Name));
|
||||
|
||||
@ -76,6 +76,9 @@ public class Collection : IQuery, IResource, IMetadata, IThumbnails, IAddedDate,
|
||||
/// <inheritdoc />
|
||||
public Dictionary<string, MetadataId> ExternalId { get; set; } = new();
|
||||
|
||||
/// <inheritdoc />
|
||||
public DateTime? NextMetadataRefresh { get; set; }
|
||||
|
||||
public Collection() { }
|
||||
|
||||
[JsonConstructor]
|
||||
|
@ -31,7 +31,7 @@ namespace Kyoo.Abstractions.Models;
|
||||
/// <summary>
|
||||
/// A class to represent a single show's episode.
|
||||
/// </summary>
|
||||
public class Episode : IQuery, IResource, IMetadata, IThumbnails, IAddedDate, INews
|
||||
public class Episode : IQuery, IResource, IThumbnails, IAddedDate, IRefreshable, INews
|
||||
{
|
||||
// Use absolute numbers by default and fallback to season/episodes if it does not exists.
|
||||
public static Sort DefaultSort =>
|
||||
@ -166,7 +166,10 @@ public class Episode : IQuery, IResource, IMetadata, IThumbnails, IAddedDate, IN
|
||||
public Image? Logo { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Dictionary<string, MetadataId> ExternalId { get; set; } = new();
|
||||
public Dictionary<string, EpisodeId> ExternalId { get; set; } = [];
|
||||
|
||||
/// <inheritdoc />
|
||||
public DateTime? NextMetadataRefresh { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The previous episode that should be seen before viewing this one.
|
||||
|
@ -0,0 +1,29 @@
|
||||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Kyoo.Abstractions.Models;
|
||||
|
||||
public interface IRefreshable
|
||||
{
|
||||
/// <summary>
|
||||
/// The date of the next metadata refresh. Null if auto-refresh is disabled.
|
||||
/// </summary>
|
||||
public DateTime? NextMetadataRefresh { get; set; }
|
||||
}
|
@ -38,6 +38,7 @@ public class Movie
|
||||
IMetadata,
|
||||
IThumbnails,
|
||||
IAddedDate,
|
||||
IRefreshable,
|
||||
ILibraryItem,
|
||||
INews,
|
||||
IWatchlist
|
||||
@ -134,6 +135,9 @@ public class Movie
|
||||
/// <inheritdoc />
|
||||
public Dictionary<string, MetadataId> ExternalId { get; set; } = new();
|
||||
|
||||
/// <inheritdoc />
|
||||
public DateTime? NextMetadataRefresh { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The ID of the Studio that made this show.
|
||||
/// </summary>
|
||||
|
@ -31,7 +31,7 @@ namespace Kyoo.Abstractions.Models;
|
||||
/// <summary>
|
||||
/// A season of a <see cref="Show"/>.
|
||||
/// </summary>
|
||||
public class Season : IQuery, IResource, IMetadata, IThumbnails, IAddedDate
|
||||
public class Season : IQuery, IResource, IMetadata, IThumbnails, IAddedDate, IRefreshable
|
||||
{
|
||||
public static Sort DefaultSort => new Sort<Season>.By(x => x.SeasonNumber);
|
||||
|
||||
@ -119,6 +119,9 @@ public class Season : IQuery, IResource, IMetadata, IThumbnails, IAddedDate
|
||||
/// <inheritdoc />
|
||||
public Dictionary<string, MetadataId> ExternalId { get; set; } = new();
|
||||
|
||||
/// <inheritdoc />
|
||||
public DateTime? NextMetadataRefresh { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of episodes that this season contains.
|
||||
/// </summary>
|
||||
|
@ -39,6 +39,7 @@ public class Show
|
||||
IOnMerge,
|
||||
IThumbnails,
|
||||
IAddedDate,
|
||||
IRefreshable,
|
||||
ILibraryItem,
|
||||
IWatchlist
|
||||
{
|
||||
@ -126,6 +127,9 @@ public class Show
|
||||
/// <inheritdoc />
|
||||
public Dictionary<string, MetadataId> ExternalId { get; set; } = new();
|
||||
|
||||
/// <inheritdoc />
|
||||
public DateTime? NextMetadataRefresh { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The ID of the Studio that made this show.
|
||||
/// </summary>
|
||||
|
@ -157,21 +157,11 @@ public abstract class DatabaseContext : DbContext
|
||||
optionsBuilder.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
|
||||
}
|
||||
|
||||
private static ValueComparer<Dictionary<string, T>> _GetComparer<T>()
|
||||
{
|
||||
return new(
|
||||
(c1, c2) => c1!.SequenceEqual(c2!),
|
||||
c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode()))
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Build the metadata model for the given type.
|
||||
/// </summary>
|
||||
/// <param name="modelBuilder">The database model builder</param>
|
||||
/// <typeparam name="T">The type to add metadata to.</typeparam>
|
||||
private static void _HasMetadata<T>(ModelBuilder modelBuilder)
|
||||
where T : class, IMetadata
|
||||
private static void _HasJson<T, TVal>(
|
||||
ModelBuilder builder,
|
||||
Expression<Func<T, Dictionary<string, TVal>>> property
|
||||
)
|
||||
where T : class
|
||||
{
|
||||
// TODO: Waiting for https://github.com/dotnet/efcore/issues/29825
|
||||
// modelBuilder.Entity<T>()
|
||||
@ -179,22 +169,33 @@ public abstract class DatabaseContext : DbContext
|
||||
// {
|
||||
// x.ToJson();
|
||||
// });
|
||||
modelBuilder
|
||||
builder
|
||||
.Entity<T>()
|
||||
.Property(x => x.ExternalId)
|
||||
.Property(property)
|
||||
.HasConversion(
|
||||
v => JsonSerializer.Serialize(v, (JsonSerializerOptions?)null),
|
||||
v =>
|
||||
JsonSerializer.Deserialize<Dictionary<string, MetadataId>>(
|
||||
JsonSerializer.Deserialize<Dictionary<string, TVal>>(
|
||||
v,
|
||||
(JsonSerializerOptions?)null
|
||||
)!
|
||||
)
|
||||
.HasColumnType("json");
|
||||
modelBuilder
|
||||
builder
|
||||
.Entity<T>()
|
||||
.Property(x => x.ExternalId)
|
||||
.Metadata.SetValueComparer(_GetComparer<MetadataId>());
|
||||
.Property(property)
|
||||
.Metadata.SetValueComparer(
|
||||
new ValueComparer<Dictionary<string, TVal>>(
|
||||
(c1, c2) => c1!.SequenceEqual(c2!),
|
||||
c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode()))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private static void _HasMetadata<T>(ModelBuilder modelBuilder)
|
||||
where T : class, IMetadata
|
||||
{
|
||||
_HasJson<T, MetadataId>(modelBuilder, x => x.ExternalId);
|
||||
}
|
||||
|
||||
private static void _HasImages<T>(ModelBuilder modelBuilder)
|
||||
@ -215,6 +216,16 @@ public abstract class DatabaseContext : DbContext
|
||||
.ValueGeneratedOnAdd();
|
||||
}
|
||||
|
||||
private static void _HasRefreshDate<T>(ModelBuilder builder)
|
||||
where T : class, IRefreshable
|
||||
{
|
||||
// schedule a refresh soon since metadata can change frequently for recently added items ond online databases
|
||||
builder.Entity<T>()
|
||||
.Property(x => x.NextMetadataRefresh)
|
||||
.HasDefaultValueSql("now() at time zone 'utc' + interval '2 hours'")
|
||||
.ValueGeneratedOnAdd();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a many to many relationship between the two entities.
|
||||
/// The resulting relationship will have an available <see cref="AddLinks{T1,T2}"/> method.
|
||||
@ -296,8 +307,8 @@ public abstract class DatabaseContext : DbContext
|
||||
_HasMetadata<Movie>(modelBuilder);
|
||||
_HasMetadata<Show>(modelBuilder);
|
||||
_HasMetadata<Season>(modelBuilder);
|
||||
_HasMetadata<Episode>(modelBuilder);
|
||||
_HasMetadata<Studio>(modelBuilder);
|
||||
_HasJson<Episode, EpisodeId>(modelBuilder, x => x.ExternalId);
|
||||
|
||||
_HasImages<Collection>(modelBuilder);
|
||||
_HasImages<Movie>(modelBuilder);
|
||||
@ -313,6 +324,12 @@ public abstract class DatabaseContext : DbContext
|
||||
_HasAddedDate<User>(modelBuilder);
|
||||
_HasAddedDate<Issue>(modelBuilder);
|
||||
|
||||
_HasRefreshDate<Collection>(modelBuilder);
|
||||
_HasRefreshDate<Movie>(modelBuilder);
|
||||
_HasRefreshDate<Show>(modelBuilder);
|
||||
_HasRefreshDate<Season>(modelBuilder);
|
||||
_HasRefreshDate<Episode>(modelBuilder);
|
||||
|
||||
modelBuilder
|
||||
.Entity<MovieWatchStatus>()
|
||||
.HasKey(x => new { User = x.UserId, Movie = x.MovieId });
|
||||
@ -389,62 +406,9 @@ public abstract class DatabaseContext : DbContext
|
||||
|
||||
modelBuilder.Entity<Issue>().HasKey(x => new { x.Domain, x.Cause });
|
||||
|
||||
// TODO: Waiting for https://github.com/dotnet/efcore/issues/29825
|
||||
// modelBuilder.Entity<T>()
|
||||
// .OwnsOne(x => x.ExternalId, x =>
|
||||
// {
|
||||
// x.ToJson();
|
||||
// });
|
||||
modelBuilder
|
||||
.Entity<User>()
|
||||
.Property(x => x.Settings)
|
||||
.HasConversion(
|
||||
v => JsonSerializer.Serialize(v, (JsonSerializerOptions?)null),
|
||||
v =>
|
||||
JsonSerializer.Deserialize<Dictionary<string, string>>(
|
||||
v,
|
||||
(JsonSerializerOptions?)null
|
||||
)!
|
||||
)
|
||||
.HasColumnType("json");
|
||||
modelBuilder
|
||||
.Entity<User>()
|
||||
.Property(x => x.Settings)
|
||||
.Metadata.SetValueComparer(_GetComparer<string>());
|
||||
|
||||
modelBuilder
|
||||
.Entity<User>()
|
||||
.Property(x => x.ExternalId)
|
||||
.HasConversion(
|
||||
v => JsonSerializer.Serialize(v, (JsonSerializerOptions?)null),
|
||||
v =>
|
||||
JsonSerializer.Deserialize<Dictionary<string, ExternalToken>>(
|
||||
v,
|
||||
(JsonSerializerOptions?)null
|
||||
)!
|
||||
)
|
||||
.HasColumnType("json");
|
||||
modelBuilder
|
||||
.Entity<User>()
|
||||
.Property(x => x.ExternalId)
|
||||
.Metadata.SetValueComparer(_GetComparer<ExternalToken>());
|
||||
|
||||
modelBuilder
|
||||
.Entity<Issue>()
|
||||
.Property(x => x.Extra)
|
||||
.HasConversion(
|
||||
v => JsonSerializer.Serialize(v, (JsonSerializerOptions?)null),
|
||||
v =>
|
||||
JsonSerializer.Deserialize<Dictionary<string, object>>(
|
||||
v,
|
||||
(JsonSerializerOptions?)null
|
||||
)!
|
||||
)
|
||||
.HasColumnType("json");
|
||||
modelBuilder
|
||||
.Entity<Issue>()
|
||||
.Property(x => x.Extra)
|
||||
.Metadata.SetValueComparer(_GetComparer<object>());
|
||||
_HasJson<User, string>(modelBuilder, x => x.Settings);
|
||||
_HasJson<User, ExternalToken>(modelBuilder, x => x.ExternalId);
|
||||
_HasJson<Issue, object>(modelBuilder, x => x.Extra);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
Loading…
x
Reference in New Issue
Block a user