Add next update date in db

This commit is contained in:
Zoe Roux 2024-04-14 23:04:56 +02:00
parent c1ecdad916
commit 5a9b846f7c
No known key found for this signature in database
9 changed files with 120 additions and 84 deletions

View File

@ -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);
}

View File

@ -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; }
}

View File

@ -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]

View File

@ -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.

View File

@ -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; }
}

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>