Browse Source

fix duplicate smart collection names (#2720)

* fix duplicate smart collection names

* fix update error
pull/2721/head
Jason Dove 3 weeks ago committed by GitHub
parent
commit
d8122edad6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 1
      CHANGELOG.md
  2. 1
      Directory.Build.props
  3. 26
      ErsatzTV.Application/MediaCollections/Commands/UpdateSmartCollectionHandler.cs
  4. 6882
      ErsatzTV.Infrastructure.MySql/Migrations/20251213202531_Fix_DuplicateSmartCollectionName.Designer.cs
  5. 33
      ErsatzTV.Infrastructure.MySql/Migrations/20251213202531_Fix_DuplicateSmartCollectionName.cs
  6. 6885
      ErsatzTV.Infrastructure.MySql/Migrations/20251213203336_Add_SmartCollectionNameUnique.Designer.cs
  7. 50
      ErsatzTV.Infrastructure.MySql/Migrations/20251213203336_Add_SmartCollectionNameUnique.cs
  8. 5
      ErsatzTV.Infrastructure.MySql/Migrations/TvContextModelSnapshot.cs
  9. 6709
      ErsatzTV.Infrastructure.Sqlite/Migrations/20251213202449_Fix_DuplicateSmartCollectionName.Designer.cs
  10. 34
      ErsatzTV.Infrastructure.Sqlite/Migrations/20251213202449_Fix_DuplicateSmartCollectionName.cs
  11. 6712
      ErsatzTV.Infrastructure.Sqlite/Migrations/20251213203256_Add_SmartCollectionNameUnique.Designer.cs
  12. 28
      ErsatzTV.Infrastructure.Sqlite/Migrations/20251213203256_Add_SmartCollectionNameUnique.cs
  13. 3
      ErsatzTV.Infrastructure.Sqlite/Migrations/TvContextModelSnapshot.cs
  14. 8
      ErsatzTV.Infrastructure/Data/Configurations/Collection/SmartCollectionConfiguration.cs

1
CHANGELOG.md

@ -56,6 +56,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). @@ -56,6 +56,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- NVIDIA: fix stream failure with certain content that should decode in hardware but falls back to software
- Fix stream failure when configured fallback filler collection is empty
- Fix high CPU when errors are displayed; errors will now work ahead before throttling to realtime, similar to primary content
- Fix startup error caused by duplicate smart collection names (and no longer allow duplicate smart collection names)
### Changed
- No longer round framerate to nearest integer when normalizing framerate

1
Directory.Build.props

@ -2,5 +2,6 @@ @@ -2,5 +2,6 @@
<PropertyGroup>
<InformationalVersion>develop</InformationalVersion>
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
<AllowMissingPrunePackageData>true</AllowMissingPrunePackageData>
</PropertyGroup>
</Project>

26
ErsatzTV.Application/MediaCollections/Commands/UpdateSmartCollectionHandler.cs

@ -12,7 +12,9 @@ using Microsoft.EntityFrameworkCore; @@ -12,7 +12,9 @@ using Microsoft.EntityFrameworkCore;
namespace ErsatzTV.Application.MediaCollections;
public class UpdateSmartCollectionHandler : IRequestHandler<UpdateSmartCollection, Either<BaseError, UpdateSmartCollectionResult>>
public class
UpdateSmartCollectionHandler : IRequestHandler<UpdateSmartCollection,
Either<BaseError, UpdateSmartCollectionResult>>
{
private readonly ChannelWriter<IBackgroundServiceRequest> _channel;
private readonly IDbContextFactory<TvContext> _dbContextFactory;
@ -71,7 +73,8 @@ public class UpdateSmartCollectionHandler : IRequestHandler<UpdateSmartCollectio @@ -71,7 +73,8 @@ public class UpdateSmartCollectionHandler : IRequestHandler<UpdateSmartCollectio
private static Task<Validation<BaseError, SmartCollection>> Validate(
TvContext dbContext,
UpdateSmartCollection request,
CancellationToken cancellationToken) => SmartCollectionMustExist(dbContext, request, cancellationToken);
CancellationToken cancellationToken) => ValidateName(dbContext, request)
.BindT(_ => SmartCollectionMustExist(dbContext, request, cancellationToken));
private static Task<Validation<BaseError, SmartCollection>> SmartCollectionMustExist(
TvContext dbContext,
@ -80,4 +83,23 @@ public class UpdateSmartCollectionHandler : IRequestHandler<UpdateSmartCollectio @@ -80,4 +83,23 @@ public class UpdateSmartCollectionHandler : IRequestHandler<UpdateSmartCollectio
dbContext.SmartCollections
.SelectOneAsync(c => c.Id, c => c.Id == updateCollection.Id, cancellationToken)
.Map(o => o.ToValidation<BaseError>("SmartCollection does not exist."));
private static async Task<Validation<BaseError, string>> ValidateName(
TvContext dbContext,
UpdateSmartCollection updateCollection)
{
List<string> allNames = await dbContext.SmartCollections
.Where(c => c.Id != updateCollection.Id)
.Map(c => c.Name)
.ToListAsync();
Validation<BaseError, string> result1 = updateCollection.NotEmpty(c => c.Name)
.Bind(_ => updateCollection.NotLongerThan(50)(c => c.Name));
var result2 = Optional(updateCollection.Name)
.Where(name => !allNames.Contains(name))
.ToValidation<BaseError>("SmartCollection name must be unique");
return (result1, result2).Apply((_, _) => updateCollection.Name);
}
}

6882
ErsatzTV.Infrastructure.MySql/Migrations/20251213202531_Fix_DuplicateSmartCollectionName.Designer.cs generated

File diff suppressed because it is too large Load Diff

33
ErsatzTV.Infrastructure.MySql/Migrations/20251213202531_Fix_DuplicateSmartCollectionName.cs

@ -0,0 +1,33 @@ @@ -0,0 +1,33 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ErsatzTV.Infrastructure.MySql.Migrations
{
/// <inheritdoc />
public partial class Fix_DuplicateSmartCollectionName : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql(
@"
WITH Numbered AS (
SELECT
Id,
ROW_NUMBER() OVER (PARTITION BY Name ORDER BY Id) as RowNum
FROM SmartCollection
)
UPDATE SmartCollection sc
JOIN Numbered n ON sc.Id = n.Id
SET sc.Name = CONCAT(sc.Name, ' (', n.RowNum - 1, ')')
WHERE n.RowNum > 1;
");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
}
}
}

6885
ErsatzTV.Infrastructure.MySql/Migrations/20251213203336_Add_SmartCollectionNameUnique.Designer.cs generated

File diff suppressed because it is too large Load Diff

50
ErsatzTV.Infrastructure.MySql/Migrations/20251213203336_Add_SmartCollectionNameUnique.cs

@ -0,0 +1,50 @@ @@ -0,0 +1,50 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ErsatzTV.Infrastructure.MySql.Migrations
{
/// <inheritdoc />
public partial class Add_SmartCollectionNameUnique : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<string>(
name: "Name",
table: "SmartCollection",
type: "varchar(255)",
nullable: true,
oldClrType: typeof(string),
oldType: "longtext",
oldNullable: true)
.Annotation("MySql:CharSet", "utf8mb4")
.OldAnnotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateIndex(
name: "IX_SmartCollection_Name",
table: "SmartCollection",
column: "Name",
unique: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_SmartCollection_Name",
table: "SmartCollection");
migrationBuilder.AlterColumn<string>(
name: "Name",
table: "SmartCollection",
type: "longtext",
nullable: true,
oldClrType: typeof(string),
oldType: "varchar(255)",
oldNullable: true)
.Annotation("MySql:CharSet", "utf8mb4")
.OldAnnotation("MySql:CharSet", "utf8mb4");
}
}
}

5
ErsatzTV.Infrastructure.MySql/Migrations/TvContextModelSnapshot.cs

@ -3235,13 +3235,16 @@ namespace ErsatzTV.Infrastructure.MySql.Migrations @@ -3235,13 +3235,16 @@ namespace ErsatzTV.Infrastructure.MySql.Migrations
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
b.Property<string>("Name")
.HasColumnType("longtext");
.HasColumnType("varchar(255)");
b.Property<string>("Query")
.HasColumnType("longtext");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("SmartCollection", (string)null);
});

6709
ErsatzTV.Infrastructure.Sqlite/Migrations/20251213202449_Fix_DuplicateSmartCollectionName.Designer.cs generated

File diff suppressed because it is too large Load Diff

34
ErsatzTV.Infrastructure.Sqlite/Migrations/20251213202449_Fix_DuplicateSmartCollectionName.cs

@ -0,0 +1,34 @@ @@ -0,0 +1,34 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ErsatzTV.Infrastructure.Sqlite.Migrations
{
/// <inheritdoc />
public partial class Fix_DuplicateSmartCollectionName : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql(
@"
WITH Numbered AS (
SELECT
Id,
ROW_NUMBER() OVER (PARTITION BY Name ORDER BY Id) AS RowNum
FROM SmartCollection
)
UPDATE SmartCollection
SET Name = Name || ' (' || (Numbered.RowNum - 1) || ')'
FROM Numbered
WHERE SmartCollection.Id = Numbered.Id
AND Numbered.RowNum > 1;
");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
}
}
}

6712
ErsatzTV.Infrastructure.Sqlite/Migrations/20251213203256_Add_SmartCollectionNameUnique.Designer.cs generated

File diff suppressed because it is too large Load Diff

28
ErsatzTV.Infrastructure.Sqlite/Migrations/20251213203256_Add_SmartCollectionNameUnique.cs

@ -0,0 +1,28 @@ @@ -0,0 +1,28 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ErsatzTV.Infrastructure.Sqlite.Migrations
{
/// <inheritdoc />
public partial class Add_SmartCollectionNameUnique : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateIndex(
name: "IX_SmartCollection_Name",
table: "SmartCollection",
column: "Name",
unique: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_SmartCollection_Name",
table: "SmartCollection");
}
}
}

3
ErsatzTV.Infrastructure.Sqlite/Migrations/TvContextModelSnapshot.cs

@ -3091,6 +3091,9 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations @@ -3091,6 +3091,9 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("SmartCollection", (string)null);
});

8
ErsatzTV.Infrastructure/Data/Configurations/Collection/SmartCollectionConfiguration.cs

@ -6,5 +6,11 @@ namespace ErsatzTV.Infrastructure.Data.Configurations; @@ -6,5 +6,11 @@ namespace ErsatzTV.Infrastructure.Data.Configurations;
public class SmartCollectionConfiguration : IEntityTypeConfiguration<SmartCollection>
{
public void Configure(EntityTypeBuilder<SmartCollection> builder) => builder.ToTable("SmartCollection");
public void Configure(EntityTypeBuilder<SmartCollection> builder)
{
builder.ToTable("SmartCollection");
builder.HasIndex(sc => sc.Name)
.IsUnique();
}
}

Loading…
Cancel
Save