Browse Source

optimize song artwork scanning (#527)

pull/534/head
Jason Dove 5 years ago committed by GitHub
parent
commit
abc103308b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      CHANGELOG.md
  2. 1
      ErsatzTV.Core/Domain/Metadata/Artwork.cs
  3. 6
      ErsatzTV.Core/Interfaces/Repositories/IMetadataRepository.cs
  4. 61
      ErsatzTV.Core/Metadata/LocalFolderScanner.cs
  5. 81
      ErsatzTV.Infrastructure/Data/Repositories/MetadataRepository.cs
  6. 1
      ErsatzTV.Infrastructure/Data/TvContext.cs
  7. 3855
      ErsatzTV.Infrastructure/Migrations/20211203191336_Add_ArtworkSourcePath.Designer.cs
  8. 25
      ErsatzTV.Infrastructure/Migrations/20211203191336_Add_ArtworkSourcePath.cs
  9. 3
      ErsatzTV.Infrastructure/Migrations/TvContextModelSnapshot.cs

1
CHANGELOG.md

@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). @@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Fix unicode console output on Windows
- Fix TV Show NFO metadata processing when `year` is missing
- Fix song detail outline to help legibility on white backgrounds
- Optimize song artwork scanning to prevent re-processing album artwork for each song
### Changed
- Use custom log database backend which should be more portable (i.e. work in osx-arm64)

1
ErsatzTV.Core/Domain/Metadata/Artwork.cs

@ -6,6 +6,7 @@ namespace ErsatzTV.Core.Domain @@ -6,6 +6,7 @@ namespace ErsatzTV.Core.Domain
{
public int Id { get; set; }
public string Path { get; set; }
public string SourcePath { get; set; }
public string BlurHash43 { get; set; }
public string BlurHash54 { get; set; }
public string BlurHash64 { get; set; }

6
ErsatzTV.Core/Interfaces/Repositories/IMetadataRepository.cs

@ -20,6 +20,12 @@ namespace ErsatzTV.Core.Interfaces.Repositories @@ -20,6 +20,12 @@ namespace ErsatzTV.Core.Interfaces.Repositories
Task<Unit> UpdateArtworkPath(Artwork artwork);
Task<Unit> AddArtwork(Domain.Metadata metadata, Artwork artwork);
Task<Unit> RemoveArtwork(Domain.Metadata metadata, ArtworkKind artworkKind);
Task<bool> CloneArtwork(
Domain.Metadata metadata,
Option<Artwork> maybeArtwork,
ArtworkKind artworkKind,
string sourcePath,
DateTime lastWriteTime);
Task<Unit> MarkAsUpdated(ShowMetadata metadata, DateTime dateUpdated);
Task<Unit> MarkAsUpdated(SeasonMetadata metadata, DateTime dateUpdated);
Task<Unit> MarkAsUpdated(MovieMetadata metadata, DateTime dateUpdated);

61
ErsatzTV.Core/Metadata/LocalFolderScanner.cs

@ -139,6 +139,17 @@ namespace ErsatzTV.Core.Metadata @@ -139,6 +139,17 @@ namespace ErsatzTV.Core.Metadata
{
_logger.LogDebug("Refreshing {Attribute} from {Path}", artworkKind, artworkFile);
string sourcePath = artworkFile;
if (await _metadataRepository.CloneArtwork(
metadata,
maybeArtwork,
artworkKind,
sourcePath,
lastWriteTime))
{
return true;
}
// if ffmpeg path is passed, we need pre-processing
foreach (string path in ffmpegPath)
{
@ -179,10 +190,28 @@ namespace ErsatzTV.Core.Metadata @@ -179,10 +190,28 @@ namespace ErsatzTV.Core.Metadata
async artwork =>
{
artwork.Path = cacheName;
artwork.SourcePath = sourcePath;
artwork.DateUpdated = lastWriteTime;
artwork.BlurHash43 = await _imageCache.CalculateBlurHash(cacheName, artworkKind, 4, 3);
artwork.BlurHash54 = await _imageCache.CalculateBlurHash(cacheName, artworkKind, 5, 4);
artwork.BlurHash64 = await _imageCache.CalculateBlurHash(cacheName, artworkKind, 6, 4);
if (metadata is SongMetadata)
{
artwork.BlurHash43 = await _imageCache.CalculateBlurHash(
cacheName,
artworkKind,
4,
3);
artwork.BlurHash54 = await _imageCache.CalculateBlurHash(
cacheName,
artworkKind,
5,
4);
artwork.BlurHash64 = await _imageCache.CalculateBlurHash(
cacheName,
artworkKind,
6,
4);
}
await _metadataRepository.UpdateArtworkPath(artwork);
},
async () =>
@ -190,13 +219,31 @@ namespace ErsatzTV.Core.Metadata @@ -190,13 +219,31 @@ namespace ErsatzTV.Core.Metadata
var artwork = new Artwork
{
Path = cacheName,
SourcePath = sourcePath,
DateAdded = DateTime.UtcNow,
DateUpdated = lastWriteTime,
ArtworkKind = artworkKind,
BlurHash43 = await _imageCache.CalculateBlurHash(cacheName, artworkKind, 4, 3),
BlurHash54 = await _imageCache.CalculateBlurHash(cacheName, artworkKind, 5, 4),
BlurHash64 = await _imageCache.CalculateBlurHash(cacheName, artworkKind, 6, 4)
ArtworkKind = artworkKind
};
if (metadata is SongMetadata)
{
artwork.BlurHash43 = await _imageCache.CalculateBlurHash(
cacheName,
artworkKind,
4,
3);
artwork.BlurHash54 = await _imageCache.CalculateBlurHash(
cacheName,
artworkKind,
5,
4);
artwork.BlurHash64 = await _imageCache.CalculateBlurHash(
cacheName,
artworkKind,
6,
4);
}
metadata.Artwork.Add(artwork);
await _metadataRepository.AddArtwork(metadata, artwork);
});

81
ErsatzTV.Infrastructure/Data/Repositories/MetadataRepository.cs

@ -209,51 +209,52 @@ namespace ErsatzTV.Infrastructure.Data.Repositories @@ -209,51 +209,52 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
public Task<Unit> UpdateArtworkPath(Artwork artwork) =>
_dbConnection.ExecuteAsync(
"UPDATE Artwork SET Path = @Path, DateUpdated = @DateUpdated, BlurHash43 = @BlurHash43, BlurHash43 = @BlurHash54, BlurHash43 = @BlurHash64 WHERE Id = @Id",
new { artwork.Path, artwork.DateUpdated, artwork.BlurHash43, artwork.BlurHash54, artwork.BlurHash64, artwork.Id }).ToUnit();
"UPDATE Artwork SET Path = @Path, SourcePath = @SourcePath, DateUpdated = @DateUpdated, BlurHash43 = @BlurHash43, BlurHash43 = @BlurHash54, BlurHash43 = @BlurHash64 WHERE Id = @Id",
new { artwork.Path, artwork.SourcePath, artwork.DateUpdated, artwork.BlurHash43, artwork.BlurHash54, artwork.BlurHash64, artwork.Id }).ToUnit();
public Task<Unit> AddArtwork(Metadata metadata, Artwork artwork)
{
var parameters = new
{
artwork.ArtworkKind, metadata.Id, artwork.DateAdded, artwork.DateUpdated, artwork.Path, artwork.BlurHash43, artwork.BlurHash54, artwork.BlurHash64
artwork.ArtworkKind, metadata.Id, artwork.DateAdded, artwork.DateUpdated, artwork.Path,
artwork.SourcePath, artwork.BlurHash43, artwork.BlurHash54, artwork.BlurHash64
};
return metadata switch
{
MovieMetadata => _dbConnection.ExecuteAsync(
@"INSERT INTO Artwork (ArtworkKind, MovieMetadataId, DateAdded, DateUpdated, Path, BlurHash43, BlurHash54, BlurHash64)
VALUES (@ArtworkKind, @Id, @DateAdded, @DateUpdated, @Path, @BlurHash43, @BlurHash54, @BlurHash64)",
@"INSERT INTO Artwork (ArtworkKind, MovieMetadataId, DateAdded, DateUpdated, Path, SourcePath, BlurHash43, BlurHash54, BlurHash64)
VALUES (@ArtworkKind, @Id, @DateAdded, @DateUpdated, @Path, @SourcePath, @BlurHash43, @BlurHash54, @BlurHash64)",
parameters)
.ToUnit(),
ShowMetadata => _dbConnection.ExecuteAsync(
@"INSERT INTO Artwork (ArtworkKind, ShowMetadataId, DateAdded, DateUpdated, Path, BlurHash43, BlurHash54, BlurHash64)
VALUES (@ArtworkKind, @Id, @DateAdded, @DateUpdated, @Path, @BlurHash43, @BlurHash54, @BlurHash64)",
@"INSERT INTO Artwork (ArtworkKind, ShowMetadataId, DateAdded, DateUpdated, Path, SourcePath, BlurHash43, BlurHash54, BlurHash64)
VALUES (@ArtworkKind, @Id, @DateAdded, @DateUpdated, @Path, @SourcePath, @BlurHash43, @BlurHash54, @BlurHash64)",
parameters)
.ToUnit(),
SeasonMetadata => _dbConnection.ExecuteAsync(
@"INSERT INTO Artwork (ArtworkKind, SeasonMetadataId, DateAdded, DateUpdated, Path, BlurHash43, BlurHash54, BlurHash64)
VALUES (@ArtworkKind, @Id, @DateAdded, @DateUpdated, @Path, @BlurHash43, @BlurHash54, @BlurHash64)",
@"INSERT INTO Artwork (ArtworkKind, SeasonMetadataId, DateAdded, DateUpdated, Path, SourcePath, BlurHash43, BlurHash54, BlurHash64)
VALUES (@ArtworkKind, @Id, @DateAdded, @DateUpdated, @Path, @SourcePath, @BlurHash43, @BlurHash54, @BlurHash64)",
parameters)
.ToUnit(),
EpisodeMetadata => _dbConnection.ExecuteAsync(
@"INSERT INTO Artwork (ArtworkKind, EpisodeMetadataId, DateAdded, DateUpdated, Path, BlurHash43, BlurHash54, BlurHash64)
VALUES (@ArtworkKind, @Id, @DateAdded, @DateUpdated, @Path, @BlurHash43, @BlurHash54, @BlurHash64)",
@"INSERT INTO Artwork (ArtworkKind, EpisodeMetadataId, DateAdded, DateUpdated, Path, SourcePath, BlurHash43, BlurHash54, BlurHash64)
VALUES (@ArtworkKind, @Id, @DateAdded, @DateUpdated, @Path, @SourcePath, @BlurHash43, @BlurHash54, @BlurHash64)",
parameters)
.ToUnit(),
ArtistMetadata => _dbConnection.ExecuteAsync(
@"INSERT INTO Artwork (ArtworkKind, ArtistMetadataId, DateAdded, DateUpdated, Path, BlurHash43, BlurHash54, BlurHash64)
Values (@ArtworkKind, @Id, @DateAdded, @DateUpdated, @Path, @BlurHash43, @BlurHash54, @BlurHash64)",
@"INSERT INTO Artwork (ArtworkKind, ArtistMetadataId, DateAdded, DateUpdated, Path, SourcePath, BlurHash43, BlurHash54, BlurHash64)
Values (@ArtworkKind, @Id, @DateAdded, @DateUpdated, @Path, @SourcePath, @BlurHash43, @BlurHash54, @BlurHash64)",
parameters)
.ToUnit(),
MusicVideoMetadata => _dbConnection.ExecuteAsync(
@"INSERT INTO Artwork (ArtworkKind, MusicVideoMetadataId, DateAdded, DateUpdated, Path, BlurHash43, BlurHash54, BlurHash64)
VALUES (@ArtworkKind, @Id, @DateAdded, @DateUpdated, @Path, @BlurHash43, @BlurHash54, @BlurHash64)",
@"INSERT INTO Artwork (ArtworkKind, MusicVideoMetadataId, DateAdded, DateUpdated, Path, SourcePath, BlurHash43, BlurHash54, BlurHash64)
VALUES (@ArtworkKind, @Id, @DateAdded, @DateUpdated, @Path, @SourcePath, @BlurHash43, @BlurHash54, @BlurHash64)",
parameters)
.ToUnit(),
SongMetadata => _dbConnection.ExecuteAsync(
@"INSERT INTO Artwork (ArtworkKind, SongMetadataId, DateAdded, DateUpdated, Path, BlurHash43, BlurHash54, BlurHash64)
VALUES (@ArtworkKind, @Id, @DateAdded, @DateUpdated, @Path, @BlurHash43, @BlurHash54, @BlurHash64)",
@"INSERT INTO Artwork (ArtworkKind, SongMetadataId, DateAdded, DateUpdated, Path, SourcePath, BlurHash43, BlurHash54, BlurHash64)
VALUES (@ArtworkKind, @Id, @DateAdded, @DateUpdated, @Path, @SourcePath, @BlurHash43, @BlurHash54, @BlurHash64)",
parameters)
.ToUnit(),
_ => Task.FromResult(Unit.Default)
@ -266,6 +267,52 @@ namespace ErsatzTV.Infrastructure.Data.Repositories @@ -266,6 +267,52 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
OR ShowMetadataId = @Id OR SeasonMetadataId = @Id OR EpisodeMetadataId = @Id)",
new { ArtworkKind = artworkKind, metadata.Id }).ToUnit();
public async Task<bool> CloneArtwork(
Metadata metadata,
Option<Artwork> maybeArtwork,
ArtworkKind artworkKind,
string sourcePath,
DateTime lastWriteTime)
{
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync();
Option<Artwork> maybeExisting = await dbContext.Artwork
.AsNoTracking()
.Filter(
a => a.SourcePath == sourcePath && a.ArtworkKind == artworkKind && a.DateUpdated == lastWriteTime)
.FirstOrDefaultAsync()
.Map(Optional);
foreach (Artwork existing in maybeExisting)
{
Artwork artwork = await maybeArtwork.IfNoneAsync(new Artwork());
if (maybeArtwork.IsNone)
{
metadata.Artwork.Add(artwork);
}
artwork.Path = existing.Path;
artwork.SourcePath = existing.SourcePath;
artwork.ArtworkKind = artworkKind;
artwork.BlurHash43 = existing.BlurHash43;
artwork.BlurHash54 = existing.BlurHash54;
artwork.BlurHash64 = existing.BlurHash64;
artwork.DateAdded = existing.DateAdded;
artwork.DateUpdated = existing.DateUpdated;
if (maybeArtwork.IsNone)
{
await AddArtwork(metadata, artwork);
}
else
{
await UpdateArtworkPath(artwork);
}
return true;
}
return false;
}
public Task<Unit> MarkAsUpdated(ShowMetadata metadata, DateTime dateUpdated) =>
_dbConnection.ExecuteAsync(
@"UPDATE ShowMetadata SET DateUpdated = @DateUpdated WHERE Id = @Id",

1
ErsatzTV.Infrastructure/Data/TvContext.cs

@ -37,6 +37,7 @@ namespace ErsatzTV.Infrastructure.Data @@ -37,6 +37,7 @@ namespace ErsatzTV.Infrastructure.Data
public DbSet<MediaStream> MediaStreams { get; set; }
public DbSet<Movie> Movies { get; set; }
public DbSet<MovieMetadata> MovieMetadata { get; set; }
public DbSet<Artwork> Artwork { get; set; }
public DbSet<Artist> Artists { get; set; }
public DbSet<ArtistMetadata> ArtistMetadata { get; set; }
public DbSet<MusicVideo> MusicVideos { get; set; }

3855
ErsatzTV.Infrastructure/Migrations/20211203191336_Add_ArtworkSourcePath.Designer.cs generated

File diff suppressed because it is too large Load Diff

25
ErsatzTV.Infrastructure/Migrations/20211203191336_Add_ArtworkSourcePath.cs

@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ErsatzTV.Infrastructure.Migrations
{
public partial class Add_ArtworkSourcePath : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "SourcePath",
table: "Artwork",
type: "TEXT",
nullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "SourcePath",
table: "Artwork");
}
}
}

3
ErsatzTV.Infrastructure/Migrations/TvContextModelSnapshot.cs

@ -186,6 +186,9 @@ namespace ErsatzTV.Infrastructure.Migrations @@ -186,6 +186,9 @@ namespace ErsatzTV.Infrastructure.Migrations
b.Property<int?>("SongMetadataId")
.HasColumnType("INTEGER");
b.Property<string>("SourcePath")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("ArtistMetadataId");

Loading…
Cancel
Save