mirror of https://github.com/ErsatzTV/ErsatzTV.git
34 changed files with 28118 additions and 39 deletions
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,37 @@
@@ -0,0 +1,37 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations; |
||||
|
||||
#nullable disable |
||||
|
||||
namespace ErsatzTV.Infrastructure.MySql.Migrations |
||||
{ |
||||
/// <inheritdoc />
|
||||
public partial class Fix_RemoteStreamMetadataTitle : Migration |
||||
{ |
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder) |
||||
{ |
||||
// mark the file as needing an update
|
||||
migrationBuilder.Sql( |
||||
"UPDATE `RemoteStreamMetadata` SET `DateUpdated` = '0001-01-01 00:00:00' WHERE `Title` IS NULL"); |
||||
|
||||
// mark the folder as needing an update
|
||||
migrationBuilder.Sql( |
||||
"""
|
||||
UPDATE `LibraryFolder` SET `Etag` = NULL WHERE `Id` IN ( |
||||
SELECT `MF`.`LibraryFolderId` |
||||
FROM `MediaFile` `MF` |
||||
INNER JOIN `MediaVersion` `MV` ON `MV`.`Id` = `MF`.`MediaVersionId` |
||||
INNER JOIN `RemoteStream` `RS` ON `RS`.`Id` = `MV`.`RemoteStreamId` |
||||
INNER JOIN `RemoteStreamMetadata` `RSM` ON `RSM`.`RemoteStreamId` = `RS`.`Id` |
||||
WHERE `RSM`.`Title` IS NULL |
||||
) |
||||
""");
|
||||
} |
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder) |
||||
{ |
||||
|
||||
} |
||||
} |
||||
} |
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,358 @@
@@ -0,0 +1,358 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations; |
||||
|
||||
#nullable disable |
||||
|
||||
namespace ErsatzTV.Infrastructure.MySql.Migrations |
||||
{ |
||||
/// <inheritdoc />
|
||||
public partial class Optimize_JellyfinItemIdEtagColumns : Migration |
||||
{ |
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder) |
||||
{ |
||||
migrationBuilder.AlterColumn<string>( |
||||
name: "ItemId", |
||||
table: "JellyfinShow", |
||||
type: "varchar(36)", |
||||
unicode: false, |
||||
maxLength: 36, |
||||
nullable: true, |
||||
oldClrType: typeof(string), |
||||
oldType: "longtext", |
||||
oldNullable: true) |
||||
.Annotation("MySql:CharSet", "utf8mb4") |
||||
.OldAnnotation("MySql:CharSet", "utf8mb4"); |
||||
|
||||
migrationBuilder.AlterColumn<string>( |
||||
name: "Etag", |
||||
table: "JellyfinShow", |
||||
type: "varchar(36)", |
||||
unicode: false, |
||||
maxLength: 36, |
||||
nullable: true, |
||||
oldClrType: typeof(string), |
||||
oldType: "longtext", |
||||
oldNullable: true) |
||||
.Annotation("MySql:CharSet", "utf8mb4") |
||||
.OldAnnotation("MySql:CharSet", "utf8mb4"); |
||||
|
||||
migrationBuilder.AlterColumn<string>( |
||||
name: "ItemId", |
||||
table: "JellyfinSeason", |
||||
type: "varchar(36)", |
||||
unicode: false, |
||||
maxLength: 36, |
||||
nullable: true, |
||||
oldClrType: typeof(string), |
||||
oldType: "longtext", |
||||
oldNullable: true) |
||||
.Annotation("MySql:CharSet", "utf8mb4") |
||||
.OldAnnotation("MySql:CharSet", "utf8mb4"); |
||||
|
||||
migrationBuilder.AlterColumn<string>( |
||||
name: "Etag", |
||||
table: "JellyfinSeason", |
||||
type: "varchar(36)", |
||||
unicode: false, |
||||
maxLength: 36, |
||||
nullable: true, |
||||
oldClrType: typeof(string), |
||||
oldType: "longtext", |
||||
oldNullable: true) |
||||
.Annotation("MySql:CharSet", "utf8mb4") |
||||
.OldAnnotation("MySql:CharSet", "utf8mb4"); |
||||
|
||||
migrationBuilder.AlterColumn<string>( |
||||
name: "ItemId", |
||||
table: "JellyfinMovie", |
||||
type: "varchar(36)", |
||||
unicode: false, |
||||
maxLength: 36, |
||||
nullable: true, |
||||
oldClrType: typeof(string), |
||||
oldType: "longtext", |
||||
oldNullable: true) |
||||
.Annotation("MySql:CharSet", "utf8mb4") |
||||
.OldAnnotation("MySql:CharSet", "utf8mb4"); |
||||
|
||||
migrationBuilder.AlterColumn<string>( |
||||
name: "Etag", |
||||
table: "JellyfinMovie", |
||||
type: "varchar(36)", |
||||
unicode: false, |
||||
maxLength: 36, |
||||
nullable: true, |
||||
oldClrType: typeof(string), |
||||
oldType: "longtext", |
||||
oldNullable: true) |
||||
.Annotation("MySql:CharSet", "utf8mb4") |
||||
.OldAnnotation("MySql:CharSet", "utf8mb4"); |
||||
|
||||
migrationBuilder.AlterColumn<string>( |
||||
name: "ItemId", |
||||
table: "JellyfinLibrary", |
||||
type: "varchar(36)", |
||||
unicode: false, |
||||
maxLength: 36, |
||||
nullable: true, |
||||
oldClrType: typeof(string), |
||||
oldType: "longtext", |
||||
oldNullable: true) |
||||
.Annotation("MySql:CharSet", "utf8mb4") |
||||
.OldAnnotation("MySql:CharSet", "utf8mb4"); |
||||
|
||||
migrationBuilder.AlterColumn<string>( |
||||
name: "ItemId", |
||||
table: "JellyfinEpisode", |
||||
type: "varchar(36)", |
||||
unicode: false, |
||||
maxLength: 36, |
||||
nullable: true, |
||||
oldClrType: typeof(string), |
||||
oldType: "longtext", |
||||
oldNullable: true) |
||||
.Annotation("MySql:CharSet", "utf8mb4") |
||||
.OldAnnotation("MySql:CharSet", "utf8mb4"); |
||||
|
||||
migrationBuilder.AlterColumn<string>( |
||||
name: "Etag", |
||||
table: "JellyfinEpisode", |
||||
type: "varchar(36)", |
||||
unicode: false, |
||||
maxLength: 36, |
||||
nullable: true, |
||||
oldClrType: typeof(string), |
||||
oldType: "longtext", |
||||
oldNullable: true) |
||||
.Annotation("MySql:CharSet", "utf8mb4") |
||||
.OldAnnotation("MySql:CharSet", "utf8mb4"); |
||||
|
||||
migrationBuilder.AlterColumn<string>( |
||||
name: "ItemId", |
||||
table: "JellyfinCollection", |
||||
type: "varchar(36)", |
||||
unicode: false, |
||||
maxLength: 36, |
||||
nullable: true, |
||||
oldClrType: typeof(string), |
||||
oldType: "longtext", |
||||
oldNullable: true) |
||||
.Annotation("MySql:CharSet", "utf8mb4") |
||||
.OldAnnotation("MySql:CharSet", "utf8mb4"); |
||||
|
||||
migrationBuilder.AlterColumn<string>( |
||||
name: "Etag", |
||||
table: "JellyfinCollection", |
||||
type: "varchar(36)", |
||||
unicode: false, |
||||
maxLength: 36, |
||||
nullable: true, |
||||
oldClrType: typeof(string), |
||||
oldType: "longtext", |
||||
oldNullable: true) |
||||
.Annotation("MySql:CharSet", "utf8mb4") |
||||
.OldAnnotation("MySql:CharSet", "utf8mb4"); |
||||
|
||||
migrationBuilder.CreateIndex( |
||||
name: "IX_JellyfinShow_ItemId", |
||||
table: "JellyfinShow", |
||||
column: "ItemId"); |
||||
|
||||
migrationBuilder.CreateIndex( |
||||
name: "IX_JellyfinSeason_ItemId", |
||||
table: "JellyfinSeason", |
||||
column: "ItemId"); |
||||
|
||||
migrationBuilder.CreateIndex( |
||||
name: "IX_JellyfinMovie_ItemId", |
||||
table: "JellyfinMovie", |
||||
column: "ItemId"); |
||||
|
||||
migrationBuilder.CreateIndex( |
||||
name: "IX_JellyfinLibrary_ItemId", |
||||
table: "JellyfinLibrary", |
||||
column: "ItemId"); |
||||
|
||||
migrationBuilder.CreateIndex( |
||||
name: "IX_JellyfinEpisode_ItemId", |
||||
table: "JellyfinEpisode", |
||||
column: "ItemId"); |
||||
|
||||
migrationBuilder.CreateIndex( |
||||
name: "IX_JellyfinCollection_ItemId", |
||||
table: "JellyfinCollection", |
||||
column: "ItemId"); |
||||
} |
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder) |
||||
{ |
||||
migrationBuilder.DropIndex( |
||||
name: "IX_JellyfinShow_ItemId", |
||||
table: "JellyfinShow"); |
||||
|
||||
migrationBuilder.DropIndex( |
||||
name: "IX_JellyfinSeason_ItemId", |
||||
table: "JellyfinSeason"); |
||||
|
||||
migrationBuilder.DropIndex( |
||||
name: "IX_JellyfinMovie_ItemId", |
||||
table: "JellyfinMovie"); |
||||
|
||||
migrationBuilder.DropIndex( |
||||
name: "IX_JellyfinLibrary_ItemId", |
||||
table: "JellyfinLibrary"); |
||||
|
||||
migrationBuilder.DropIndex( |
||||
name: "IX_JellyfinEpisode_ItemId", |
||||
table: "JellyfinEpisode"); |
||||
|
||||
migrationBuilder.DropIndex( |
||||
name: "IX_JellyfinCollection_ItemId", |
||||
table: "JellyfinCollection"); |
||||
|
||||
migrationBuilder.AlterColumn<string>( |
||||
name: "ItemId", |
||||
table: "JellyfinShow", |
||||
type: "longtext", |
||||
nullable: true, |
||||
oldClrType: typeof(string), |
||||
oldType: "varchar(36)", |
||||
oldUnicode: false, |
||||
oldMaxLength: 36, |
||||
oldNullable: true) |
||||
.Annotation("MySql:CharSet", "utf8mb4") |
||||
.OldAnnotation("MySql:CharSet", "utf8mb4"); |
||||
|
||||
migrationBuilder.AlterColumn<string>( |
||||
name: "Etag", |
||||
table: "JellyfinShow", |
||||
type: "longtext", |
||||
nullable: true, |
||||
oldClrType: typeof(string), |
||||
oldType: "varchar(36)", |
||||
oldUnicode: false, |
||||
oldMaxLength: 36, |
||||
oldNullable: true) |
||||
.Annotation("MySql:CharSet", "utf8mb4") |
||||
.OldAnnotation("MySql:CharSet", "utf8mb4"); |
||||
|
||||
migrationBuilder.AlterColumn<string>( |
||||
name: "ItemId", |
||||
table: "JellyfinSeason", |
||||
type: "longtext", |
||||
nullable: true, |
||||
oldClrType: typeof(string), |
||||
oldType: "varchar(36)", |
||||
oldUnicode: false, |
||||
oldMaxLength: 36, |
||||
oldNullable: true) |
||||
.Annotation("MySql:CharSet", "utf8mb4") |
||||
.OldAnnotation("MySql:CharSet", "utf8mb4"); |
||||
|
||||
migrationBuilder.AlterColumn<string>( |
||||
name: "Etag", |
||||
table: "JellyfinSeason", |
||||
type: "longtext", |
||||
nullable: true, |
||||
oldClrType: typeof(string), |
||||
oldType: "varchar(36)", |
||||
oldUnicode: false, |
||||
oldMaxLength: 36, |
||||
oldNullable: true) |
||||
.Annotation("MySql:CharSet", "utf8mb4") |
||||
.OldAnnotation("MySql:CharSet", "utf8mb4"); |
||||
|
||||
migrationBuilder.AlterColumn<string>( |
||||
name: "ItemId", |
||||
table: "JellyfinMovie", |
||||
type: "longtext", |
||||
nullable: true, |
||||
oldClrType: typeof(string), |
||||
oldType: "varchar(36)", |
||||
oldUnicode: false, |
||||
oldMaxLength: 36, |
||||
oldNullable: true) |
||||
.Annotation("MySql:CharSet", "utf8mb4") |
||||
.OldAnnotation("MySql:CharSet", "utf8mb4"); |
||||
|
||||
migrationBuilder.AlterColumn<string>( |
||||
name: "Etag", |
||||
table: "JellyfinMovie", |
||||
type: "longtext", |
||||
nullable: true, |
||||
oldClrType: typeof(string), |
||||
oldType: "varchar(36)", |
||||
oldUnicode: false, |
||||
oldMaxLength: 36, |
||||
oldNullable: true) |
||||
.Annotation("MySql:CharSet", "utf8mb4") |
||||
.OldAnnotation("MySql:CharSet", "utf8mb4"); |
||||
|
||||
migrationBuilder.AlterColumn<string>( |
||||
name: "ItemId", |
||||
table: "JellyfinLibrary", |
||||
type: "longtext", |
||||
nullable: true, |
||||
oldClrType: typeof(string), |
||||
oldType: "varchar(36)", |
||||
oldUnicode: false, |
||||
oldMaxLength: 36, |
||||
oldNullable: true) |
||||
.Annotation("MySql:CharSet", "utf8mb4") |
||||
.OldAnnotation("MySql:CharSet", "utf8mb4"); |
||||
|
||||
migrationBuilder.AlterColumn<string>( |
||||
name: "ItemId", |
||||
table: "JellyfinEpisode", |
||||
type: "longtext", |
||||
nullable: true, |
||||
oldClrType: typeof(string), |
||||
oldType: "varchar(36)", |
||||
oldUnicode: false, |
||||
oldMaxLength: 36, |
||||
oldNullable: true) |
||||
.Annotation("MySql:CharSet", "utf8mb4") |
||||
.OldAnnotation("MySql:CharSet", "utf8mb4"); |
||||
|
||||
migrationBuilder.AlterColumn<string>( |
||||
name: "Etag", |
||||
table: "JellyfinEpisode", |
||||
type: "longtext", |
||||
nullable: true, |
||||
oldClrType: typeof(string), |
||||
oldType: "varchar(36)", |
||||
oldUnicode: false, |
||||
oldMaxLength: 36, |
||||
oldNullable: true) |
||||
.Annotation("MySql:CharSet", "utf8mb4") |
||||
.OldAnnotation("MySql:CharSet", "utf8mb4"); |
||||
|
||||
migrationBuilder.AlterColumn<string>( |
||||
name: "ItemId", |
||||
table: "JellyfinCollection", |
||||
type: "longtext", |
||||
nullable: true, |
||||
oldClrType: typeof(string), |
||||
oldType: "varchar(36)", |
||||
oldUnicode: false, |
||||
oldMaxLength: 36, |
||||
oldNullable: true) |
||||
.Annotation("MySql:CharSet", "utf8mb4") |
||||
.OldAnnotation("MySql:CharSet", "utf8mb4"); |
||||
|
||||
migrationBuilder.AlterColumn<string>( |
||||
name: "Etag", |
||||
table: "JellyfinCollection", |
||||
type: "longtext", |
||||
nullable: true, |
||||
oldClrType: typeof(string), |
||||
oldType: "varchar(36)", |
||||
oldUnicode: false, |
||||
oldMaxLength: 36, |
||||
oldNullable: true) |
||||
.Annotation("MySql:CharSet", "utf8mb4") |
||||
.OldAnnotation("MySql:CharSet", "utf8mb4"); |
||||
} |
||||
} |
||||
} |
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,37 @@
@@ -0,0 +1,37 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations; |
||||
|
||||
#nullable disable |
||||
|
||||
namespace ErsatzTV.Infrastructure.Sqlite.Migrations |
||||
{ |
||||
/// <inheritdoc />
|
||||
public partial class Fix_RemoteStreamMetadataTitle : Migration |
||||
{ |
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder) |
||||
{ |
||||
// mark the file as needing an update
|
||||
migrationBuilder.Sql( |
||||
"UPDATE `RemoteStreamMetadata` SET `DateUpdated` = '0001-01-01 00:00:00' WHERE `Title` IS NULL"); |
||||
|
||||
// mark the folder as needing an update
|
||||
migrationBuilder.Sql( |
||||
"""
|
||||
UPDATE `LibraryFolder` SET `Etag` = NULL WHERE `Id` IN ( |
||||
SELECT `MF`.`LibraryFolderId` |
||||
FROM `MediaFile` `MF` |
||||
INNER JOIN `MediaVersion` `MV` ON `MV`.`Id` = `MF`.`MediaVersionId` |
||||
INNER JOIN `RemoteStream` `RS` ON `RS`.`Id` = `MV`.`RemoteStreamId` |
||||
INNER JOIN `RemoteStreamMetadata` `RSM` ON `RSM`.`RemoteStreamId` = `RS`.`Id` |
||||
WHERE `RSM`.`Title` IS NULL |
||||
) |
||||
""");
|
||||
} |
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder) |
||||
{ |
||||
|
||||
} |
||||
} |
||||
} |
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,72 @@
@@ -0,0 +1,72 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations; |
||||
|
||||
#nullable disable |
||||
|
||||
namespace ErsatzTV.Infrastructure.Sqlite.Migrations |
||||
{ |
||||
/// <inheritdoc />
|
||||
public partial class Optimize_JellyfinItemIdEtagColumns : Migration |
||||
{ |
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder) |
||||
{ |
||||
migrationBuilder.CreateIndex( |
||||
name: "IX_JellyfinShow_ItemId", |
||||
table: "JellyfinShow", |
||||
column: "ItemId"); |
||||
|
||||
migrationBuilder.CreateIndex( |
||||
name: "IX_JellyfinSeason_ItemId", |
||||
table: "JellyfinSeason", |
||||
column: "ItemId"); |
||||
|
||||
migrationBuilder.CreateIndex( |
||||
name: "IX_JellyfinMovie_ItemId", |
||||
table: "JellyfinMovie", |
||||
column: "ItemId"); |
||||
|
||||
migrationBuilder.CreateIndex( |
||||
name: "IX_JellyfinLibrary_ItemId", |
||||
table: "JellyfinLibrary", |
||||
column: "ItemId"); |
||||
|
||||
migrationBuilder.CreateIndex( |
||||
name: "IX_JellyfinEpisode_ItemId", |
||||
table: "JellyfinEpisode", |
||||
column: "ItemId"); |
||||
|
||||
migrationBuilder.CreateIndex( |
||||
name: "IX_JellyfinCollection_ItemId", |
||||
table: "JellyfinCollection", |
||||
column: "ItemId"); |
||||
} |
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder) |
||||
{ |
||||
migrationBuilder.DropIndex( |
||||
name: "IX_JellyfinShow_ItemId", |
||||
table: "JellyfinShow"); |
||||
|
||||
migrationBuilder.DropIndex( |
||||
name: "IX_JellyfinSeason_ItemId", |
||||
table: "JellyfinSeason"); |
||||
|
||||
migrationBuilder.DropIndex( |
||||
name: "IX_JellyfinMovie_ItemId", |
||||
table: "JellyfinMovie"); |
||||
|
||||
migrationBuilder.DropIndex( |
||||
name: "IX_JellyfinLibrary_ItemId", |
||||
table: "JellyfinLibrary"); |
||||
|
||||
migrationBuilder.DropIndex( |
||||
name: "IX_JellyfinEpisode_ItemId", |
||||
table: "JellyfinEpisode"); |
||||
|
||||
migrationBuilder.DropIndex( |
||||
name: "IX_JellyfinCollection_ItemId", |
||||
table: "JellyfinCollection"); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,38 @@
@@ -0,0 +1,38 @@
|
||||
using ErsatzTV.Core; |
||||
|
||||
namespace ErsatzTV; |
||||
|
||||
using System.Diagnostics; |
||||
|
||||
public class SlowApiHandler(ILogger<SlowApiHandler> logger) : DelegatingHandler |
||||
{ |
||||
protected override async Task<HttpResponseMessage> SendAsync( |
||||
HttpRequestMessage request, |
||||
CancellationToken cancellationToken) |
||||
{ |
||||
if (SystemEnvironment.SlowApiMs > 0) |
||||
{ |
||||
var stopwatch = Stopwatch.StartNew(); |
||||
|
||||
var response = await base.SendAsync(request, cancellationToken); |
||||
|
||||
stopwatch.Stop(); |
||||
|
||||
if (stopwatch.ElapsedMilliseconds > SystemEnvironment.SlowApiMs.Value) |
||||
{ |
||||
string uri = request.RequestUri?.ToString() ?? "Unknown URI"; |
||||
string method = request.Method.Method; |
||||
|
||||
logger.LogDebug( |
||||
"[SLOW API] {Method} {Uri} took {Milliseconds}ms", |
||||
method, |
||||
uri, |
||||
stopwatch.ElapsedMilliseconds); |
||||
} |
||||
|
||||
return response; |
||||
} |
||||
|
||||
return await base.SendAsync(request, cancellationToken); |
||||
} |
||||
} |
||||
@ -0,0 +1,24 @@
@@ -0,0 +1,24 @@
|
||||
using System.Data.Common; |
||||
using Microsoft.EntityFrameworkCore.Diagnostics; |
||||
|
||||
namespace ErsatzTV; |
||||
|
||||
public class SlowQueryInterceptor(int threshold) : DbCommandInterceptor |
||||
{ |
||||
public override ValueTask<DbDataReader> ReaderExecutedAsync( |
||||
DbCommand command, |
||||
CommandExecutedEventData eventData, |
||||
DbDataReader result, |
||||
CancellationToken cancellationToken = default) |
||||
{ |
||||
if (eventData.Duration.TotalMilliseconds > threshold) |
||||
{ |
||||
Serilog.Log.Logger.Debug( |
||||
"[SLOW QUERY] ({Milliseconds}ms): {Command}", |
||||
eventData.Duration.TotalMilliseconds, |
||||
command.CommandText); |
||||
} |
||||
|
||||
return base.ReaderExecutedAsync(command, eventData, result, cancellationToken); |
||||
} |
||||
} |
||||
Loading…
Reference in new issue