diff options
| author | Mark Monteiro <marknr.monteiro@protonmail.com> | 2020-05-15 15:23:44 -0400 |
|---|---|---|
| committer | Mark Monteiro <marknr.monteiro@protonmail.com> | 2020-05-15 15:23:44 -0400 |
| commit | 757643e3262abbe2d4144b38e9f8e95a85a1fd51 (patch) | |
| tree | 7c1bf7be6bffb2a6a8fb6f54ec3487699e111830 /Jellyfin.Server.Implementations | |
| parent | 6e68702799b2b3de9660babad6a66493d16fec72 (diff) | |
| parent | d21feb2b188b0b317f0d12ff2aea9f48f4a4c2e0 (diff) | |
Merge remote-tracking branch 'upstream/master' into no-more-doppelgangers
Diffstat (limited to 'Jellyfin.Server.Implementations')
8 files changed, 354 insertions, 2 deletions
diff --git a/Jellyfin.Server.Implementations/Activity/ActivityManager.cs b/Jellyfin.Server.Implementations/Activity/ActivityManager.cs new file mode 100644 index 0000000000..65ceee32bf --- /dev/null +++ b/Jellyfin.Server.Implementations/Activity/ActivityManager.cs @@ -0,0 +1,102 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Jellyfin.Data.Entities; +using MediaBrowser.Model.Activity; +using MediaBrowser.Model.Events; +using MediaBrowser.Model.Querying; + +namespace Jellyfin.Server.Implementations.Activity +{ + /// <summary> + /// Manages the storage and retrieval of <see cref="ActivityLog"/> instances. + /// </summary> + public class ActivityManager : IActivityManager + { + private readonly JellyfinDbProvider _provider; + + /// <summary> + /// Initializes a new instance of the <see cref="ActivityManager"/> class. + /// </summary> + /// <param name="provider">The Jellyfin database provider.</param> + public ActivityManager(JellyfinDbProvider provider) + { + _provider = provider; + } + + /// <inheritdoc/> + public event EventHandler<GenericEventArgs<ActivityLogEntry>> EntryCreated; + + /// <inheritdoc/> + public void Create(ActivityLog entry) + { + using var dbContext = _provider.CreateContext(); + dbContext.ActivityLogs.Add(entry); + dbContext.SaveChanges(); + + EntryCreated?.Invoke(this, new GenericEventArgs<ActivityLogEntry>(ConvertToOldModel(entry))); + } + + /// <inheritdoc/> + public async Task CreateAsync(ActivityLog entry) + { + using var dbContext = _provider.CreateContext(); + await dbContext.ActivityLogs.AddAsync(entry); + await dbContext.SaveChangesAsync().ConfigureAwait(false); + + EntryCreated?.Invoke(this, new GenericEventArgs<ActivityLogEntry>(ConvertToOldModel(entry))); + } + + /// <inheritdoc/> + public QueryResult<ActivityLogEntry> GetPagedResult( + Func<IQueryable<ActivityLog>, IQueryable<ActivityLog>> func, + int? startIndex, + int? limit) + { + using var dbContext = _provider.CreateContext(); + + var query = func(dbContext.ActivityLogs.OrderByDescending(entry => entry.DateCreated)); + + if (startIndex.HasValue) + { + query = query.Skip(startIndex.Value); + } + + if (limit.HasValue) + { + query = query.Take(limit.Value); + } + + // This converts the objects from the new database model to the old for compatibility with the existing API. + var list = query.Select(ConvertToOldModel).ToList(); + + return new QueryResult<ActivityLogEntry> + { + Items = list, + TotalRecordCount = func(dbContext.ActivityLogs).Count() + }; + } + + /// <inheritdoc/> + public QueryResult<ActivityLogEntry> GetPagedResult(int? startIndex, int? limit) + { + return GetPagedResult(logs => logs, startIndex, limit); + } + + private static ActivityLogEntry ConvertToOldModel(ActivityLog entry) + { + return new ActivityLogEntry + { + Id = entry.Id, + Name = entry.Name, + Overview = entry.Overview, + ShortOverview = entry.ShortOverview, + Type = entry.Type, + ItemId = entry.ItemId, + UserId = entry.UserId, + Date = entry.DateCreated, + Severity = entry.LogSeverity + }; + } + } +} diff --git a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj index a31f28f64a..149ca50209 100644 --- a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj +++ b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj @@ -26,6 +26,14 @@ </ItemGroup> <ItemGroup> + <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.1.3" /> + <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.3"> + <PrivateAssets>all</PrivateAssets> + <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> + </PackageReference> + </ItemGroup> + + <ItemGroup> <ProjectReference Include="..\Jellyfin.Data\Jellyfin.Data.csproj" /> <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" /> <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" /> diff --git a/Jellyfin.Server.Implementations/JellyfinDb.cs b/Jellyfin.Server.Implementations/JellyfinDb.cs index 76343edf9d..ec09a619f2 100644 --- a/Jellyfin.Server.Implementations/JellyfinDb.cs +++ b/Jellyfin.Server.Implementations/JellyfinDb.cs @@ -15,6 +15,7 @@ namespace Jellyfin.Server.Implementations /// <inheritdoc/> public partial class JellyfinDb : DbContext { + public virtual DbSet<ActivityLog> ActivityLogs { get; set; } /*public virtual DbSet<Artwork> Artwork { get; set; } public virtual DbSet<Book> Books { get; set; } public virtual DbSet<BookMetadata> BookMetadata { get; set; } @@ -49,6 +50,7 @@ namespace Jellyfin.Server.Implementations public virtual DbSet<Preference> Preferences { get; set; } public virtual DbSet<ProviderMapping> ProviderMappings { get; set; } public virtual DbSet<Rating> Ratings { get; set; } + /// <summary> /// Repository for global::Jellyfin.Data.Entities.RatingSource - This is the entity to /// store review ratings, not age ratings @@ -93,8 +95,10 @@ namespace Jellyfin.Server.Implementations modelBuilder.HasDefaultSchema("jellyfin"); /*modelBuilder.Entity<Artwork>().HasIndex(t => t.Kind); + modelBuilder.Entity<Genre>().HasIndex(t => t.Name) .IsUnique(); + modelBuilder.Entity<LibraryItem>().HasIndex(t => t.UrlId) .IsUnique();*/ @@ -103,9 +107,10 @@ namespace Jellyfin.Server.Implementations public override int SaveChanges() { - foreach (var entity in ChangeTracker.Entries().Where(e => e.State == EntityState.Modified)) + foreach (var saveEntity in ChangeTracker.Entries() + .Where(e => e.State == EntityState.Modified) + .OfType<ISavingChanges>()) { - var saveEntity = entity.Entity as ISavingChanges; saveEntity.OnSavingChanges(); } diff --git a/Jellyfin.Server.Implementations/JellyfinDbProvider.cs b/Jellyfin.Server.Implementations/JellyfinDbProvider.cs new file mode 100644 index 0000000000..eab531d386 --- /dev/null +++ b/Jellyfin.Server.Implementations/JellyfinDbProvider.cs @@ -0,0 +1,33 @@ +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; + +namespace Jellyfin.Server.Implementations +{ + /// <summary> + /// Factory class for generating new <see cref="JellyfinDb"/> instances. + /// </summary> + public class JellyfinDbProvider + { + private readonly IServiceProvider _serviceProvider; + + /// <summary> + /// Initializes a new instance of the <see cref="JellyfinDbProvider"/> class. + /// </summary> + /// <param name="serviceProvider">The application's service provider.</param> + public JellyfinDbProvider(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + serviceProvider.GetService<JellyfinDb>().Database.Migrate(); + } + + /// <summary> + /// Creates a new <see cref="JellyfinDb"/> context. + /// </summary> + /// <returns>The newly created context.</returns> + public JellyfinDb CreateContext() + { + return _serviceProvider.GetRequiredService<JellyfinDb>(); + } + } +} diff --git a/Jellyfin.Server.Implementations/Migrations/20200514181226_AddActivityLog.Designer.cs b/Jellyfin.Server.Implementations/Migrations/20200514181226_AddActivityLog.Designer.cs new file mode 100644 index 0000000000..98a83b7450 --- /dev/null +++ b/Jellyfin.Server.Implementations/Migrations/20200514181226_AddActivityLog.Designer.cs @@ -0,0 +1,72 @@ +#pragma warning disable CS1591 + +// <auto-generated /> +using System; +using Jellyfin.Server.Implementations; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace Jellyfin.Server.Implementations.Migrations +{ + [DbContext(typeof(JellyfinDb))] + [Migration("20200514181226_AddActivityLog")] + partial class AddActivityLog + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("jellyfin") + .HasAnnotation("ProductVersion", "3.1.3"); + + modelBuilder.Entity("Jellyfin.Data.Entities.ActivityLog", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<DateTime>("DateCreated") + .HasColumnType("TEXT"); + + b.Property<string>("ItemId") + .HasColumnType("TEXT") + .HasMaxLength(256); + + b.Property<int>("LogSeverity") + .HasColumnType("INTEGER"); + + b.Property<string>("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(512); + + b.Property<string>("Overview") + .HasColumnType("TEXT") + .HasMaxLength(512); + + b.Property<uint>("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property<string>("ShortOverview") + .HasColumnType("TEXT") + .HasMaxLength(512); + + b.Property<string>("Type") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(256); + + b.Property<Guid>("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("ActivityLogs"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Jellyfin.Server.Implementations/Migrations/20200514181226_AddActivityLog.cs b/Jellyfin.Server.Implementations/Migrations/20200514181226_AddActivityLog.cs new file mode 100644 index 0000000000..5e0b454d8b --- /dev/null +++ b/Jellyfin.Server.Implementations/Migrations/20200514181226_AddActivityLog.cs @@ -0,0 +1,46 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1601 + +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Jellyfin.Server.Implementations.Migrations +{ + public partial class AddActivityLog : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.EnsureSchema( + name: "jellyfin"); + + migrationBuilder.CreateTable( + name: "ActivityLogs", + schema: "jellyfin", + columns: table => new + { + Id = table.Column<int>(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Name = table.Column<string>(maxLength: 512, nullable: false), + Overview = table.Column<string>(maxLength: 512, nullable: true), + ShortOverview = table.Column<string>(maxLength: 512, nullable: true), + Type = table.Column<string>(maxLength: 256, nullable: false), + UserId = table.Column<Guid>(nullable: false), + ItemId = table.Column<string>(maxLength: 256, nullable: true), + DateCreated = table.Column<DateTime>(nullable: false), + LogSeverity = table.Column<int>(nullable: false), + RowVersion = table.Column<uint>(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ActivityLogs", x => x.Id); + }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "ActivityLogs", + schema: "jellyfin"); + } + } +} diff --git a/Jellyfin.Server.Implementations/Migrations/DesignTimeJellyfinDbFactory.cs b/Jellyfin.Server.Implementations/Migrations/DesignTimeJellyfinDbFactory.cs new file mode 100644 index 0000000000..72a4a8c3b6 --- /dev/null +++ b/Jellyfin.Server.Implementations/Migrations/DesignTimeJellyfinDbFactory.cs @@ -0,0 +1,20 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; + +namespace Jellyfin.Server.Implementations.Migrations +{ + /// <summary> + /// The design time factory for <see cref="JellyfinDb"/>. + /// This is only used for the creation of migrations and not during runtime. + /// </summary> + internal class DesignTimeJellyfinDbFactory : IDesignTimeDbContextFactory<JellyfinDb> + { + public JellyfinDb CreateDbContext(string[] args) + { + var optionsBuilder = new DbContextOptionsBuilder<JellyfinDb>(); + optionsBuilder.UseSqlite("Data Source=jellyfin.db"); + + return new JellyfinDb(optionsBuilder.Options); + } + } +} diff --git a/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs b/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs new file mode 100644 index 0000000000..1e7ffd2359 --- /dev/null +++ b/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs @@ -0,0 +1,66 @@ +// <auto-generated /> +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; + +namespace Jellyfin.Server.Implementations.Migrations +{ + [DbContext(typeof(JellyfinDb))] + partial class JellyfinDbModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("jellyfin") + .HasAnnotation("ProductVersion", "3.1.3"); + + modelBuilder.Entity("Jellyfin.Data.Entities.ActivityLog", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<DateTime>("DateCreated") + .HasColumnType("TEXT"); + + b.Property<string>("ItemId") + .HasColumnType("TEXT") + .HasMaxLength(256); + + b.Property<int>("LogSeverity") + .HasColumnType("INTEGER"); + + b.Property<string>("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(512); + + b.Property<string>("Overview") + .HasColumnType("TEXT") + .HasMaxLength(512); + + b.Property<uint>("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property<string>("ShortOverview") + .HasColumnType("TEXT") + .HasMaxLength(512); + + b.Property<string>("Type") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(256); + + b.Property<Guid>("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("ActivityLogs"); + }); +#pragma warning restore 612, 618 + } + } +} |
