Customized Scheduler + Saved Kavita+ Details (#2644)
This commit is contained in:
parent
2092e120c3
commit
ad74871623
76 changed files with 6076 additions and 3370 deletions
|
@ -58,6 +58,10 @@ public sealed class DataContext : IdentityDbContext<AppUser, AppRole, int,
|
|||
public DbSet<AppUserDashboardStream> AppUserDashboardStream { get; set; } = null!;
|
||||
public DbSet<AppUserSideNavStream> AppUserSideNavStream { get; set; } = null!;
|
||||
public DbSet<AppUserExternalSource> AppUserExternalSource { get; set; } = null!;
|
||||
public DbSet<ExternalReview> ExternalReview { get; set; } = null!;
|
||||
public DbSet<ExternalRating> ExternalRating { get; set; } = null!;
|
||||
public DbSet<ExternalSeriesMetadata> ExternalSeriesMetadata { get; set; } = null!;
|
||||
public DbSet<ExternalRecommendation> ExternalRecommendation { get; set; } = null!;
|
||||
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder builder)
|
||||
|
|
2787
API/Data/Migrations/20240121223643_ExternalSeriesMetadata.Designer.cs
generated
Normal file
2787
API/Data/Migrations/20240121223643_ExternalSeriesMetadata.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
227
API/Data/Migrations/20240121223643_ExternalSeriesMetadata.cs
Normal file
227
API/Data/Migrations/20240121223643_ExternalSeriesMetadata.cs
Normal file
|
@ -0,0 +1,227 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace API.Data.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class ExternalSeriesMetadata : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ExternalRating",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
AverageScore = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
FavoriteCount = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
Provider = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
ProviderUrl = table.Column<string>(type: "TEXT", nullable: true),
|
||||
SeriesId = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ExternalRating", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ExternalRecommendation",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
Name = table.Column<string>(type: "TEXT", nullable: true),
|
||||
CoverUrl = table.Column<string>(type: "TEXT", nullable: true),
|
||||
Url = table.Column<string>(type: "TEXT", nullable: true),
|
||||
Summary = table.Column<string>(type: "TEXT", nullable: true),
|
||||
AniListId = table.Column<int>(type: "INTEGER", nullable: true),
|
||||
MalId = table.Column<long>(type: "INTEGER", nullable: true),
|
||||
Provider = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
SeriesId = table.Column<int>(type: "INTEGER", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ExternalRecommendation", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_ExternalRecommendation_Series_SeriesId",
|
||||
column: x => x.SeriesId,
|
||||
principalTable: "Series",
|
||||
principalColumn: "Id");
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ExternalReview",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
Tagline = table.Column<string>(type: "TEXT", nullable: true),
|
||||
Body = table.Column<string>(type: "TEXT", nullable: true),
|
||||
BodyJustText = table.Column<string>(type: "TEXT", nullable: true),
|
||||
RawBody = table.Column<string>(type: "TEXT", nullable: true),
|
||||
Provider = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
SiteUrl = table.Column<string>(type: "TEXT", nullable: true),
|
||||
Username = table.Column<string>(type: "TEXT", nullable: true),
|
||||
Rating = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
Score = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
TotalVotes = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
SeriesId = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ExternalReview", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ExternalSeriesMetadata",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
AverageExternalRating = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
AniListId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
MalId = table.Column<long>(type: "INTEGER", nullable: false),
|
||||
GoogleBooksId = table.Column<string>(type: "TEXT", nullable: true),
|
||||
LastUpdatedUtc = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||
SeriesId = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ExternalSeriesMetadata", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_ExternalSeriesMetadata_Series_SeriesId",
|
||||
column: x => x.SeriesId,
|
||||
principalTable: "Series",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ExternalRatingExternalSeriesMetadata",
|
||||
columns: table => new
|
||||
{
|
||||
ExternalRatingsId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
ExternalSeriesMetadatasId = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ExternalRatingExternalSeriesMetadata", x => new { x.ExternalRatingsId, x.ExternalSeriesMetadatasId });
|
||||
table.ForeignKey(
|
||||
name: "FK_ExternalRatingExternalSeriesMetadata_ExternalRating_ExternalRatingsId",
|
||||
column: x => x.ExternalRatingsId,
|
||||
principalTable: "ExternalRating",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_ExternalRatingExternalSeriesMetadata_ExternalSeriesMetadata_ExternalSeriesMetadatasId",
|
||||
column: x => x.ExternalSeriesMetadatasId,
|
||||
principalTable: "ExternalSeriesMetadata",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ExternalRecommendationExternalSeriesMetadata",
|
||||
columns: table => new
|
||||
{
|
||||
ExternalRecommendationsId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
ExternalSeriesMetadatasId = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ExternalRecommendationExternalSeriesMetadata", x => new { x.ExternalRecommendationsId, x.ExternalSeriesMetadatasId });
|
||||
table.ForeignKey(
|
||||
name: "FK_ExternalRecommendationExternalSeriesMetadata_ExternalRecommendation_ExternalRecommendationsId",
|
||||
column: x => x.ExternalRecommendationsId,
|
||||
principalTable: "ExternalRecommendation",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_ExternalRecommendationExternalSeriesMetadata_ExternalSeriesMetadata_ExternalSeriesMetadatasId",
|
||||
column: x => x.ExternalSeriesMetadatasId,
|
||||
principalTable: "ExternalSeriesMetadata",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ExternalReviewExternalSeriesMetadata",
|
||||
columns: table => new
|
||||
{
|
||||
ExternalReviewsId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
ExternalSeriesMetadatasId = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ExternalReviewExternalSeriesMetadata", x => new { x.ExternalReviewsId, x.ExternalSeriesMetadatasId });
|
||||
table.ForeignKey(
|
||||
name: "FK_ExternalReviewExternalSeriesMetadata_ExternalReview_ExternalReviewsId",
|
||||
column: x => x.ExternalReviewsId,
|
||||
principalTable: "ExternalReview",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_ExternalReviewExternalSeriesMetadata_ExternalSeriesMetadata_ExternalSeriesMetadatasId",
|
||||
column: x => x.ExternalSeriesMetadatasId,
|
||||
principalTable: "ExternalSeriesMetadata",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ExternalRatingExternalSeriesMetadata_ExternalSeriesMetadatasId",
|
||||
table: "ExternalRatingExternalSeriesMetadata",
|
||||
column: "ExternalSeriesMetadatasId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ExternalRecommendation_SeriesId",
|
||||
table: "ExternalRecommendation",
|
||||
column: "SeriesId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ExternalRecommendationExternalSeriesMetadata_ExternalSeriesMetadatasId",
|
||||
table: "ExternalRecommendationExternalSeriesMetadata",
|
||||
column: "ExternalSeriesMetadatasId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ExternalReviewExternalSeriesMetadata_ExternalSeriesMetadatasId",
|
||||
table: "ExternalReviewExternalSeriesMetadata",
|
||||
column: "ExternalSeriesMetadatasId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ExternalSeriesMetadata_SeriesId",
|
||||
table: "ExternalSeriesMetadata",
|
||||
column: "SeriesId",
|
||||
unique: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "ExternalRatingExternalSeriesMetadata");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ExternalRecommendationExternalSeriesMetadata");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ExternalReviewExternalSeriesMetadata");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ExternalRating");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ExternalRecommendation");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ExternalReview");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ExternalSeriesMetadata");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,7 +15,7 @@ namespace API.Data.Migrations
|
|||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "7.0.13");
|
||||
modelBuilder.HasAnnotation("ProductVersion", "8.0.1");
|
||||
|
||||
modelBuilder.Entity("API.Entities.AppRole", b =>
|
||||
{
|
||||
|
@ -1015,6 +1015,145 @@ namespace API.Data.Migrations
|
|||
b.ToTable("MediaError");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.Metadata.ExternalRating", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("AverageScore")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("FavoriteCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Provider")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ProviderUrl")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("SeriesId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("ExternalRating");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.Metadata.ExternalRecommendation", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("AniListId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("CoverUrl")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<long?>("MalId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Provider")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("SeriesId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Summary")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Url")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("SeriesId");
|
||||
|
||||
b.ToTable("ExternalRecommendation");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.Metadata.ExternalReview", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Body")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("BodyJustText")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Provider")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Rating")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("RawBody")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Score")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SeriesId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("SiteUrl")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Tagline")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("TotalVotes")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Username")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("ExternalReview");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.Metadata.ExternalSeriesMetadata", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("AniListId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("AverageExternalRating")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("GoogleBooksId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("LastUpdatedUtc")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<long>("MalId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SeriesId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("SeriesId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("ExternalSeriesMetadata");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.Metadata.SeriesMetadata", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
|
@ -1742,6 +1881,51 @@ namespace API.Data.Migrations
|
|||
b.ToTable("CollectionTagSeriesMetadata");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ExternalRatingExternalSeriesMetadata", b =>
|
||||
{
|
||||
b.Property<int>("ExternalRatingsId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ExternalSeriesMetadatasId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("ExternalRatingsId", "ExternalSeriesMetadatasId");
|
||||
|
||||
b.HasIndex("ExternalSeriesMetadatasId");
|
||||
|
||||
b.ToTable("ExternalRatingExternalSeriesMetadata");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ExternalRecommendationExternalSeriesMetadata", b =>
|
||||
{
|
||||
b.Property<int>("ExternalRecommendationsId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ExternalSeriesMetadatasId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("ExternalRecommendationsId", "ExternalSeriesMetadatasId");
|
||||
|
||||
b.HasIndex("ExternalSeriesMetadatasId");
|
||||
|
||||
b.ToTable("ExternalRecommendationExternalSeriesMetadata");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ExternalReviewExternalSeriesMetadata", b =>
|
||||
{
|
||||
b.Property<int>("ExternalReviewsId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ExternalSeriesMetadatasId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("ExternalReviewsId", "ExternalSeriesMetadatasId");
|
||||
|
||||
b.HasIndex("ExternalSeriesMetadatasId");
|
||||
|
||||
b.ToTable("ExternalReviewExternalSeriesMetadata");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GenreSeriesMetadata", b =>
|
||||
{
|
||||
b.Property<int>("GenresId")
|
||||
|
@ -2128,6 +2312,26 @@ namespace API.Data.Migrations
|
|||
b.Navigation("Chapter");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.Metadata.ExternalRecommendation", b =>
|
||||
{
|
||||
b.HasOne("API.Entities.Series", "Series")
|
||||
.WithMany()
|
||||
.HasForeignKey("SeriesId");
|
||||
|
||||
b.Navigation("Series");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.Metadata.ExternalSeriesMetadata", b =>
|
||||
{
|
||||
b.HasOne("API.Entities.Series", "Series")
|
||||
.WithOne("ExternalSeriesMetadata")
|
||||
.HasForeignKey("API.Entities.Metadata.ExternalSeriesMetadata", "SeriesId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Series");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.Metadata.SeriesMetadata", b =>
|
||||
{
|
||||
b.HasOne("API.Entities.Series", "Series")
|
||||
|
@ -2368,6 +2572,51 @@ namespace API.Data.Migrations
|
|||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ExternalRatingExternalSeriesMetadata", b =>
|
||||
{
|
||||
b.HasOne("API.Entities.Metadata.ExternalRating", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("ExternalRatingsId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("API.Entities.Metadata.ExternalSeriesMetadata", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("ExternalSeriesMetadatasId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ExternalRecommendationExternalSeriesMetadata", b =>
|
||||
{
|
||||
b.HasOne("API.Entities.Metadata.ExternalRecommendation", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("ExternalRecommendationsId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("API.Entities.Metadata.ExternalSeriesMetadata", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("ExternalSeriesMetadatasId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ExternalReviewExternalSeriesMetadata", b =>
|
||||
{
|
||||
b.HasOne("API.Entities.Metadata.ExternalReview", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("ExternalReviewsId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("API.Entities.Metadata.ExternalSeriesMetadata", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("ExternalSeriesMetadatasId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GenreSeriesMetadata", b =>
|
||||
{
|
||||
b.HasOne("API.Entities.Genre", null)
|
||||
|
@ -2510,6 +2759,8 @@ namespace API.Data.Migrations
|
|||
|
||||
modelBuilder.Entity("API.Entities.Series", b =>
|
||||
{
|
||||
b.Navigation("ExternalSeriesMetadata");
|
||||
|
||||
b.Navigation("Metadata");
|
||||
|
||||
b.Navigation("Progress");
|
||||
|
|
171
API/Data/Repositories/ExternalSeriesMetadataRepository.cs
Normal file
171
API/Data/Repositories/ExternalSeriesMetadataRepository.cs
Normal file
|
@ -0,0 +1,171 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.Constants;
|
||||
using API.DTOs;
|
||||
using API.DTOs.Recommendation;
|
||||
using API.DTOs.SeriesDetail;
|
||||
using API.Entities;
|
||||
using API.Entities.Enums;
|
||||
using API.Entities.Metadata;
|
||||
using API.Extensions;
|
||||
using API.Extensions.QueryExtensions;
|
||||
using AutoMapper;
|
||||
using AutoMapper.QueryableExtensions;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace API.Data.Repositories;
|
||||
|
||||
public interface IExternalSeriesMetadataRepository
|
||||
{
|
||||
void Attach(ExternalSeriesMetadata metadata);
|
||||
void Attach(ExternalRating rating);
|
||||
void Attach(ExternalReview review);
|
||||
void Remove(IEnumerable<ExternalReview>? reviews);
|
||||
void Remove(IEnumerable<ExternalRating>? ratings);
|
||||
void Remove(IEnumerable<ExternalRecommendation>? recommendations);
|
||||
Task<ExternalSeriesMetadata?> GetExternalSeriesMetadata(int seriesId, int limit = 25);
|
||||
Task<SeriesDetailPlusDto> GetSeriesDetailPlusDto(int seriesId, int libraryId, AppUser user);
|
||||
Task LinkRecommendationsToSeries(Series series);
|
||||
}
|
||||
|
||||
public class ExternalSeriesMetadataRepository : IExternalSeriesMetadataRepository
|
||||
{
|
||||
private readonly DataContext _context;
|
||||
private readonly IMapper _mapper;
|
||||
private readonly UserManager<AppUser> _userManager;
|
||||
|
||||
public ExternalSeriesMetadataRepository(DataContext context, IMapper mapper, UserManager<AppUser> userManager)
|
||||
{
|
||||
_context = context;
|
||||
_mapper = mapper;
|
||||
_userManager = userManager;
|
||||
}
|
||||
|
||||
public void Attach(ExternalSeriesMetadata metadata)
|
||||
{
|
||||
_context.ExternalSeriesMetadata.Attach(metadata);
|
||||
}
|
||||
|
||||
public void Attach(ExternalRating rating)
|
||||
{
|
||||
_context.ExternalRating.Attach(rating);
|
||||
}
|
||||
|
||||
public void Attach(ExternalReview review)
|
||||
{
|
||||
_context.ExternalReview.Attach(review);
|
||||
}
|
||||
|
||||
public void Remove(IEnumerable<ExternalReview>? reviews)
|
||||
{
|
||||
if (reviews == null) return;
|
||||
_context.ExternalReview.RemoveRange(reviews);
|
||||
}
|
||||
|
||||
public void Remove(IEnumerable<ExternalRating> ratings)
|
||||
{
|
||||
if (ratings == null) return;
|
||||
_context.ExternalRating.RemoveRange(ratings);
|
||||
}
|
||||
|
||||
public void Remove(IEnumerable<ExternalRecommendation> recommendations)
|
||||
{
|
||||
if (recommendations == null) return;
|
||||
_context.ExternalRecommendation.RemoveRange(recommendations);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the ExternalSeriesMetadata entity for the given Series including all linked tables
|
||||
/// </summary>
|
||||
/// <param name="seriesId"></param>
|
||||
/// <returns></returns>
|
||||
public Task<ExternalSeriesMetadata?> GetExternalSeriesMetadata(int seriesId, int limit = 25)
|
||||
{
|
||||
return _context.ExternalSeriesMetadata
|
||||
.Where(s => s.SeriesId == seriesId)
|
||||
.Include(s => s.ExternalReviews.Take(25))
|
||||
.Include(s => s.ExternalRatings.Take(25))
|
||||
.Include(s => s.ExternalRecommendations.Take(25))
|
||||
.AsSplitQuery()
|
||||
.FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task<SeriesDetailPlusDto> GetSeriesDetailPlusDto(int seriesId, int libraryId, AppUser user)
|
||||
{
|
||||
var canSeeExternalSeries = user is { AgeRestriction: AgeRating.NotApplicable } &&
|
||||
await _userManager.IsInRoleAsync(user, PolicyConstants.AdminRole);
|
||||
|
||||
var allowedLibraries = await _context.Library
|
||||
.Where(library => library.AppUsers.Any(x => x.Id == user.Id))
|
||||
.Select(l => l.Id)
|
||||
.ToListAsync();
|
||||
|
||||
var userRating = await _context.AppUser.GetUserAgeRestriction(user.Id);
|
||||
|
||||
var seriesDetailDto = await _context.ExternalSeriesMetadata
|
||||
.Where(m => m.SeriesId == seriesId)
|
||||
.Include(m => m.ExternalRatings)
|
||||
.Include(m => m.ExternalReviews)
|
||||
.Include(m => m.ExternalRecommendations)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
if (seriesDetailDto == null)
|
||||
{
|
||||
return null; // or handle the case when seriesDetailDto is not found
|
||||
}
|
||||
|
||||
var externalSeriesRecommendations = new List<ExternalSeriesDto>();
|
||||
if (!canSeeExternalSeries)
|
||||
{
|
||||
externalSeriesRecommendations = seriesDetailDto.ExternalRecommendations
|
||||
.Where(r => r.SeriesId is null or 0)
|
||||
.Select(r => _mapper.Map<ExternalSeriesDto>(r))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
|
||||
var ownedSeriesRecommendations = await _context.ExternalRecommendation
|
||||
.Where(r => r.SeriesId > 0 && allowedLibraries.Contains(r.Series.LibraryId))
|
||||
.Join(_context.Series, r => r.SeriesId, s => s.Id, (recommendation, series) => series)
|
||||
.RestrictAgainstAgeRestriction(userRating)
|
||||
.OrderBy(s => s.SortName.ToLower())
|
||||
.ProjectTo<SeriesDto>(_mapper.ConfigurationProvider)
|
||||
.AsNoTracking()
|
||||
.ToListAsync();
|
||||
|
||||
var seriesDetailPlusDto = new SeriesDetailPlusDto()
|
||||
{
|
||||
Ratings = seriesDetailDto.ExternalRatings.Select(r => _mapper.Map<RatingDto>(r)),
|
||||
Reviews = seriesDetailDto.ExternalReviews.OrderByDescending(r => r.Score).Select(r => _mapper.Map<UserReviewDto>(r)),
|
||||
Recommendations = new RecommendationDto()
|
||||
{
|
||||
ExternalSeries = externalSeriesRecommendations,
|
||||
OwnedSeries = ownedSeriesRecommendations
|
||||
}
|
||||
};
|
||||
|
||||
return seriesDetailPlusDto;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches Recommendations without a SeriesId on record and attempts to link based on Series Name/Localized Name
|
||||
/// </summary>
|
||||
/// <param name="series"></param>
|
||||
/// <returns></returns>
|
||||
public async Task LinkRecommendationsToSeries(Series series)
|
||||
{
|
||||
var recMatches = await _context.ExternalRecommendation
|
||||
.Where(r => r.SeriesId == null || r.SeriesId == 0)
|
||||
.Where(r => EF.Functions.Like(r.Name, series.Name) ||
|
||||
EF.Functions.Like(r.Name, series.LocalizedName))
|
||||
.ToListAsync();
|
||||
foreach (var rec in recMatches)
|
||||
{
|
||||
rec.SeriesId = series.Id;
|
||||
}
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
}
|
|
@ -47,7 +47,11 @@ public enum SeriesIncludes
|
|||
Metadata = 4,
|
||||
Related = 8,
|
||||
Library = 16,
|
||||
Chapters = 32
|
||||
Chapters = 32,
|
||||
ExternalReviews = 64,
|
||||
ExternalRatings = 128,
|
||||
ExternalRecommendations = 256,
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -133,6 +137,8 @@ public interface ISeriesRepository
|
|||
Task<IEnumerable<Series>> GetAllSeriesByNameAsync(IList<string> normalizedNames,
|
||||
int userId, SeriesIncludes includes = SeriesIncludes.None);
|
||||
Task<Series?> GetFullSeriesByAnyName(string seriesName, string localizedName, int libraryId, MangaFormat format, bool withFullIncludes = true);
|
||||
public Task<IList<Series>> GetAllSeriesByAnyName(string seriesName, string localizedName, int libraryId,
|
||||
MangaFormat format);
|
||||
Task<IList<Series>> RemoveSeriesNotInList(IList<ParsedSeries> seenSeries, int libraryId);
|
||||
Task<IDictionary<string, IList<SeriesModified>>> GetFolderPathMap(int libraryId);
|
||||
Task<AgeRating?> GetMaxAgeRatingFromSeriesAsync(IEnumerable<int> seriesIds);
|
||||
|
@ -148,6 +154,7 @@ public interface ISeriesRepository
|
|||
Task RemoveFromOnDeck(int seriesId, int userId);
|
||||
Task ClearOnDeckRemoval(int seriesId, int userId);
|
||||
Task<PagedList<SeriesDto>> GetSeriesDtoForLibraryIdV2Async(int userId, UserParams userParams, FilterV2Dto filterDto);
|
||||
|
||||
}
|
||||
|
||||
public class SeriesRepository : ISeriesRepository
|
||||
|
@ -176,6 +183,11 @@ public class SeriesRepository : ISeriesRepository
|
|||
_context.Series.Attach(series);
|
||||
}
|
||||
|
||||
public void Attach(ExternalSeriesMetadata metadata)
|
||||
{
|
||||
_context.ExternalSeriesMetadata.Attach(metadata);
|
||||
}
|
||||
|
||||
public void Update(Series series)
|
||||
{
|
||||
_context.Entry(series).State = EntityState.Modified;
|
||||
|
@ -669,7 +681,6 @@ public class SeriesRepository : ISeriesRepository
|
|||
return await PagedList<SeriesDto>.CreateAsync(retSeries, userParams.PageNumber, userParams.PageSize);
|
||||
}
|
||||
|
||||
|
||||
public async Task AddSeriesModifiers(int userId, IList<SeriesDto> series)
|
||||
{
|
||||
var userProgress = await _context.AppUserProgresses
|
||||
|
@ -1124,6 +1135,7 @@ public class SeriesRepository : ISeriesRepository
|
|||
FilterField.ReleaseYear => query.HasReleaseYear(true, statement.Comparison, (int) value),
|
||||
FilterField.ReadTime => query.HasAverageReadTime(true, statement.Comparison, (int) value),
|
||||
FilterField.ReadingDate => query.HasReadingDate(true, statement.Comparison, (DateTime) value, userId),
|
||||
FilterField.AverageRating => query.HasAverageRating(true, statement.Comparison, (float) value),
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
}
|
||||
|
@ -1231,8 +1243,10 @@ public class SeriesRepository : ISeriesRepository
|
|||
.Where(library => library.AppUsers.Any(x => x.Id == userId))
|
||||
.AsSplitQuery()
|
||||
.Select(l => l.Id);
|
||||
var userRating = await _context.AppUser.GetUserAgeRestriction(userId);
|
||||
|
||||
return await _context.Series
|
||||
.RestrictAgainstAgeRestriction(userRating)
|
||||
.Where(s => seriesIds.Contains(s.Id) && allowedLibraries.Contains(s.LibraryId))
|
||||
.OrderBy(s => s.SortName.ToLower())
|
||||
.ProjectTo<SeriesDto>(_mapper.ConfigurationProvider)
|
||||
|
@ -1571,6 +1585,27 @@ public class SeriesRepository : ISeriesRepository
|
|||
#nullable enable
|
||||
}
|
||||
|
||||
public async Task<IList<Series>> GetAllSeriesByAnyName(string seriesName, string localizedName, int libraryId,
|
||||
MangaFormat format)
|
||||
{
|
||||
var normalizedSeries = seriesName.ToNormalized();
|
||||
var normalizedLocalized = localizedName.ToNormalized();
|
||||
return await _context.Series
|
||||
.Where(s => s.LibraryId == libraryId)
|
||||
.Where(s => s.Format == format && format != MangaFormat.Unknown)
|
||||
.Where(s =>
|
||||
s.NormalizedName.Equals(normalizedSeries)
|
||||
|| s.NormalizedName.Equals(normalizedLocalized)
|
||||
|
||||
|| s.NormalizedLocalizedName.Equals(normalizedSeries)
|
||||
|| (!string.IsNullOrEmpty(normalizedLocalized) && s.NormalizedLocalizedName.Equals(normalizedLocalized))
|
||||
|
||||
|| (s.OriginalName != null && s.OriginalName.Equals(seriesName))
|
||||
)
|
||||
.AsSplitQuery()
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Removes series that are not in the seenSeries list. Does not commit.
|
||||
|
@ -1863,6 +1898,7 @@ public class SeriesRepository : ISeriesRepository
|
|||
SeriesDto? result = null;
|
||||
if (!string.IsNullOrEmpty(aniListUrl) || !string.IsNullOrEmpty(malUrl))
|
||||
{
|
||||
// TODO: I can likely work AniList and MalIds from ExternalSeriesMetadata in here
|
||||
result = await _context.Series
|
||||
.RestrictAgainstAgeRestriction(userRating)
|
||||
.Where(s => !string.IsNullOrEmpty(s.Metadata.WebLinks))
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.DTOs.SeriesDetail;
|
||||
using API.DTOs.Settings;
|
||||
using API.Entities;
|
||||
using API.Entities.Enums;
|
||||
using API.Entities.Metadata;
|
||||
using AutoMapper;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
|
@ -16,6 +18,7 @@ public interface ISettingsRepository
|
|||
Task<ServerSetting> GetSettingAsync(ServerSettingKey key);
|
||||
Task<IEnumerable<ServerSetting>> GetSettingsAsync();
|
||||
void Remove(ServerSetting setting);
|
||||
Task<ExternalSeriesMetadata?> GetExternalSeriesMetadata(int seriesId);
|
||||
}
|
||||
public class SettingsRepository : ISettingsRepository
|
||||
{
|
||||
|
@ -38,6 +41,13 @@ public class SettingsRepository : ISettingsRepository
|
|||
_context.Remove(setting);
|
||||
}
|
||||
|
||||
public async Task<ExternalSeriesMetadata?> GetExternalSeriesMetadata(int seriesId)
|
||||
{
|
||||
return await _context.ExternalSeriesMetadata
|
||||
.Where(s => s.SeriesId == seriesId)
|
||||
.FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task<ServerSettingDto> GetSettingsDtoAsync()
|
||||
{
|
||||
var settings = await _context.ServerSetting
|
||||
|
|
|
@ -208,8 +208,9 @@ public static class Seed
|
|||
{
|
||||
new() {Key = ServerSettingKey.CacheDirectory, Value = directoryService.CacheDirectory},
|
||||
new() {Key = ServerSettingKey.TaskScan, Value = "daily"},
|
||||
new() {Key = ServerSettingKey.LoggingLevel, Value = "Debug"},
|
||||
new() {Key = ServerSettingKey.TaskBackup, Value = "daily"},
|
||||
new() {Key = ServerSettingKey.TaskCleanup, Value = "daily"},
|
||||
new() {Key = ServerSettingKey.LoggingLevel, Value = "Debug"},
|
||||
new()
|
||||
{
|
||||
Key = ServerSettingKey.BackupDirectory, Value = Path.GetFullPath(DirectoryService.BackupDirectory)
|
||||
|
|
|
@ -30,6 +30,7 @@ public interface IUnitOfWork
|
|||
IUserTableOfContentRepository UserTableOfContentRepository { get; }
|
||||
IAppUserSmartFilterRepository AppUserSmartFilterRepository { get; }
|
||||
IAppUserExternalSourceRepository AppUserExternalSourceRepository { get; }
|
||||
IExternalSeriesMetadataRepository ExternalSeriesMetadataRepository { get; }
|
||||
bool Commit();
|
||||
Task<bool> CommitAsync();
|
||||
bool HasChanges();
|
||||
|
@ -72,6 +73,7 @@ public class UnitOfWork : IUnitOfWork
|
|||
public IUserTableOfContentRepository UserTableOfContentRepository => new UserTableOfContentRepository(_context, _mapper);
|
||||
public IAppUserSmartFilterRepository AppUserSmartFilterRepository => new AppUserSmartFilterRepository(_context, _mapper);
|
||||
public IAppUserExternalSourceRepository AppUserExternalSourceRepository => new AppUserExternalSourceRepository(_context, _mapper);
|
||||
public IExternalSeriesMetadataRepository ExternalSeriesMetadataRepository => new ExternalSeriesMetadataRepository(_context, _mapper, _userManager);
|
||||
|
||||
/// <summary>
|
||||
/// Commits changes to the DB. Completes the open transaction.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue