Browse Source

limit disk use and keep cover art aspect ratio (#502)

* use temp file pool to limit disk use

* keep aspect ratio and crop when scaling cover art for blurred background

* fix typo
pull/504/head
Jason Dove 4 years ago committed by GitHub
parent
commit
0edfb71f8d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      ErsatzTV.Application/Streaming/Queries/GetPlayoutItemProcessByChannelNumberHandler.cs
  2. 1
      ErsatzTV.Core.Tests/FFmpeg/TranscodingTests.cs
  3. 2
      ErsatzTV.Core.Tests/Metadata/MovieFolderScannerTests.cs
  4. 2
      ErsatzTV.Core/FFmpeg/FFmpegComplexFilterBuilder.cs
  5. 6
      ErsatzTV.Core/FFmpeg/FFmpegProcessService.cs
  6. 9
      ErsatzTV.Core/FFmpeg/TempFileCategory.cs
  7. 35
      ErsatzTV.Core/FFmpeg/TempFilePool.cs
  8. 1
      ErsatzTV.Core/FileSystemLayout.cs
  9. 9
      ErsatzTV.Core/Interfaces/FFmpeg/ITempFilePool.cs
  10. 6
      ErsatzTV.Core/Metadata/LocalFolderScanner.cs
  11. 10
      ErsatzTV.Core/Metadata/MovieFolderScanner.cs
  12. 2
      ErsatzTV.Core/Metadata/MusicVideoFolderScanner.cs
  13. 2
      ErsatzTV.Core/Metadata/OtherVideoFolderScanner.cs
  14. 2
      ErsatzTV.Core/Metadata/SongFolderScanner.cs
  15. 2
      ErsatzTV.Core/Metadata/TelevisionFolderScanner.cs
  16. 6
      ErsatzTV/Startup.cs

8
ErsatzTV.Application/Streaming/Queries/GetPlayoutItemProcessByChannelNumberHandler.cs

@ -11,6 +11,7 @@ using ErsatzTV.Core.Domain; @@ -11,6 +11,7 @@ using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Domain.Filler;
using ErsatzTV.Core.Errors;
using ErsatzTV.Core.Extensions;
using ErsatzTV.Core.FFmpeg;
using ErsatzTV.Core.Interfaces.Emby;
using ErsatzTV.Core.Interfaces.FFmpeg;
using ErsatzTV.Core.Interfaces.Images;
@ -41,6 +42,7 @@ namespace ErsatzTV.Application.Streaming.Queries @@ -41,6 +42,7 @@ namespace ErsatzTV.Application.Streaming.Queries
private readonly IPlexPathReplacementService _plexPathReplacementService;
private readonly IRuntimeInfo _runtimeInfo;
private readonly IImageCache _imageCache;
private readonly ITempFilePool _tempFilePool;
public GetPlayoutItemProcessByChannelNumberHandler(
IDbContextFactory<TvContext> dbContextFactory,
@ -53,7 +55,8 @@ namespace ErsatzTV.Application.Streaming.Queries @@ -53,7 +55,8 @@ namespace ErsatzTV.Application.Streaming.Queries
ITelevisionRepository televisionRepository,
IArtistRepository artistRepository,
IRuntimeInfo runtimeInfo,
IImageCache imageCache)
IImageCache imageCache,
ITempFilePool tempFilePool)
: base(dbContextFactory)
{
_ffmpegProcessService = ffmpegProcessService;
@ -66,6 +69,7 @@ namespace ErsatzTV.Application.Streaming.Queries @@ -66,6 +69,7 @@ namespace ErsatzTV.Application.Streaming.Queries
_artistRepository = artistRepository;
_runtimeInfo = runtimeInfo;
_imageCache = imageCache;
_tempFilePool = tempFilePool;
}
protected override async Task<Either<BaseError, PlayoutItemProcessModel>> GetProcess(
@ -159,7 +163,7 @@ namespace ErsatzTV.Application.Streaming.Queries @@ -159,7 +163,7 @@ namespace ErsatzTV.Application.Streaming.Queries
// use thumbnail (cover art) if present
foreach (SongMetadata metadata in song.SongMetadata)
{
string fileName = Path.GetTempFileName();
string fileName = _tempFilePool.GetNextTempFile(TempFileCategory.DrawText);
drawtextFile = fileName;
var sb = new StringBuilder();

1
ErsatzTV.Core.Tests/FFmpeg/TranscodingTests.cs

@ -139,6 +139,7 @@ namespace ErsatzTV.Core.Tests.FFmpeg @@ -139,6 +139,7 @@ namespace ErsatzTV.Core.Tests.FFmpeg
new FFmpegPlaybackSettingsCalculator(),
new FakeStreamSelector(),
new Mock<IImageCache>().Object,
new Mock<ITempFilePool>().Object,
new Mock<ILogger<FFmpegProcessService>>().Object);
MediaVersion v = new MediaVersion();

2
ErsatzTV.Core.Tests/Metadata/MovieFolderScannerTests.cs

@ -7,6 +7,7 @@ using System.Threading.Tasks; @@ -7,6 +7,7 @@ using System.Threading.Tasks;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Errors;
using ErsatzTV.Core.FFmpeg;
using ErsatzTV.Core.Interfaces.FFmpeg;
using ErsatzTV.Core.Interfaces.Images;
using ErsatzTV.Core.Interfaces.Metadata;
using ErsatzTV.Core.Interfaces.Repositories;
@ -603,6 +604,7 @@ namespace ErsatzTV.Core.Tests.Metadata @@ -603,6 +604,7 @@ namespace ErsatzTV.Core.Tests.Metadata
new Mock<ILibraryRepository>().Object,
new Mock<IMediator>().Object,
null,
new Mock<ITempFilePool>().Object,
new Mock<ILogger<MovieFolderScanner>>().Object
);
}

2
ErsatzTV.Core/FFmpeg/FFmpegComplexFilterBuilder.cs

@ -239,9 +239,9 @@ namespace ErsatzTV.Core.FFmpeg @@ -239,9 +239,9 @@ namespace ErsatzTV.Core.FFmpeg
HardwareAccelerationKind.Qsv => $"scale_qsv=w={size.Width}:h={size.Height}",
HardwareAccelerationKind.Nvenc when _pixelFormat is "yuv420p10le" =>
$"hwupload_cuda,scale_cuda={size.Width}:{size.Height}",
HardwareAccelerationKind.Nvenc when isSong => $"scale_cuda={size.Width}:{size.Height}:format=yuv420p",
HardwareAccelerationKind.Nvenc => $"scale_cuda={size.Width}:{size.Height}",
HardwareAccelerationKind.Vaapi => $"scale_vaapi=format=nv12:w={size.Width}:h={size.Height}",
_ when videoOnly => $"scale={size.Width}:{size.Height}:force_original_aspect_ratio=increase,crop={size.Width}:{size.Height}",
_ => $"scale={size.Width}:{size.Height}:flags=fast_bilinear"
};

6
ErsatzTV.Core/FFmpeg/FFmpegProcessService.cs

@ -16,6 +16,7 @@ namespace ErsatzTV.Core.FFmpeg @@ -16,6 +16,7 @@ namespace ErsatzTV.Core.FFmpeg
{
private readonly IFFmpegStreamSelector _ffmpegStreamSelector;
private readonly IImageCache _imageCache;
private readonly ITempFilePool _tempFilePool;
private readonly ILogger<FFmpegProcessService> _logger;
private readonly FFmpegPlaybackSettingsCalculator _playbackSettingsCalculator;
@ -23,11 +24,13 @@ namespace ErsatzTV.Core.FFmpeg @@ -23,11 +24,13 @@ namespace ErsatzTV.Core.FFmpeg
FFmpegPlaybackSettingsCalculator ffmpegPlaybackSettingsService,
IFFmpegStreamSelector ffmpegStreamSelector,
IImageCache imageCache,
ITempFilePool tempFilePool,
ILogger<FFmpegProcessService> logger)
{
_playbackSettingsCalculator = ffmpegPlaybackSettingsService;
_ffmpegStreamSelector = ffmpegStreamSelector;
_imageCache = imageCache;
_tempFilePool = tempFilePool;
_logger = logger;
}
@ -242,8 +245,7 @@ namespace ErsatzTV.Core.FFmpeg @@ -242,8 +245,7 @@ namespace ErsatzTV.Core.FFmpeg
{
try
{
// todo: generate name by channel? clean up old images?
string outputFile = Path.GetTempFileName();
string outputFile = _tempFilePool.GetNextTempFile(TempFileCategory.SongBackground);
MediaStream videoStream = await _ffmpegStreamSelector.SelectVideoStream(channel, videoVersion);

9
ErsatzTV.Core/FFmpeg/TempFileCategory.cs

@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
namespace ErsatzTV.Core.FFmpeg
{
public enum TempFileCategory
{
DrawText = 0,
SongBackground = 1,
CoverArt = 2
}
}

35
ErsatzTV.Core/FFmpeg/TempFilePool.cs

@ -0,0 +1,35 @@ @@ -0,0 +1,35 @@
using System.Collections.Generic;
using System.IO;
using ErsatzTV.Core.Interfaces.FFmpeg;
namespace ErsatzTV.Core.FFmpeg
{
public class TempFilePool : ITempFilePool
{
private const int ItemLimit = 10;
private readonly Dictionary<TempFileCategory, int> _state = new();
private readonly object _lock = new();
public string GetNextTempFile(TempFileCategory category)
{
lock (_lock)
{
var index = 0;
if (_state.TryGetValue(category, out int current))
{
index = (current + 1) % ItemLimit;
}
_state[category] = index;
return GetFileName(category, index);
}
}
private static string GetFileName(TempFileCategory category, int index)
{
return Path.Combine(FileSystemLayout.TempFilePoolFolder, $"{category}_{index}".ToLowerInvariant());
}
}
}

1
ErsatzTV.Core/FileSystemLayout.cs

@ -31,6 +31,7 @@ namespace ErsatzTV.Core @@ -31,6 +31,7 @@ namespace ErsatzTV.Core
public static readonly string FFmpegReportsFolder = Path.Combine(AppDataFolder, "ffmpeg-reports");
public static readonly string SearchIndexFolder = Path.Combine(AppDataFolder, "search-index");
public static readonly string TempFilePoolFolder = Path.Combine(AppDataFolder, "temp-pool");
public static readonly string ArtworkCacheFolder = Path.Combine(AppDataFolder, "cache", "artwork");

9
ErsatzTV.Core/Interfaces/FFmpeg/ITempFilePool.cs

@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
using ErsatzTV.Core.FFmpeg;
namespace ErsatzTV.Core.Interfaces.FFmpeg
{
public interface ITempFilePool
{
string GetNextTempFile(TempFileCategory category);
}
}

6
ErsatzTV.Core/Metadata/LocalFolderScanner.cs

@ -6,6 +6,7 @@ using System.Linq; @@ -6,6 +6,7 @@ using System.Linq;
using System.Threading.Tasks;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Extensions;
using ErsatzTV.Core.FFmpeg;
using ErsatzTV.Core.Interfaces.FFmpeg;
using ErsatzTV.Core.Interfaces.Images;
using ErsatzTV.Core.Interfaces.Metadata;
@ -50,6 +51,7 @@ namespace ErsatzTV.Core.Metadata @@ -50,6 +51,7 @@ namespace ErsatzTV.Core.Metadata
private readonly IImageCache _imageCache;
private readonly IFFmpegProcessService _ffmpegProcessService;
private readonly ITempFilePool _tempFilePool;
private readonly ILocalFileSystem _localFileSystem;
private readonly ILocalStatisticsProvider _localStatisticsProvider;
@ -62,6 +64,7 @@ namespace ErsatzTV.Core.Metadata @@ -62,6 +64,7 @@ namespace ErsatzTV.Core.Metadata
IMetadataRepository metadataRepository,
IImageCache imageCache,
IFFmpegProcessService ffmpegProcessService,
ITempFilePool tempFilePool,
ILogger logger)
{
_localFileSystem = localFileSystem;
@ -69,6 +72,7 @@ namespace ErsatzTV.Core.Metadata @@ -69,6 +72,7 @@ namespace ErsatzTV.Core.Metadata
_metadataRepository = metadataRepository;
_imageCache = imageCache;
_ffmpegProcessService = ffmpegProcessService;
_tempFilePool = tempFilePool;
_logger = logger;
}
@ -133,7 +137,7 @@ namespace ErsatzTV.Core.Metadata @@ -133,7 +137,7 @@ namespace ErsatzTV.Core.Metadata
// if ffmpeg path is passed, we want to convert to png
foreach (string path in ffmpegPath)
{
string tempName = Path.GetTempFileName();
string tempName = _tempFilePool.GetNextTempFile(TempFileCategory.CoverArt);
using Process process = _ffmpegProcessService.ConvertToPng(path, artworkFile, tempName);
process.Start();
await process.WaitForExitAsync();

10
ErsatzTV.Core/Metadata/MovieFolderScanner.cs

@ -42,8 +42,16 @@ namespace ErsatzTV.Core.Metadata @@ -42,8 +42,16 @@ namespace ErsatzTV.Core.Metadata
ILibraryRepository libraryRepository,
IMediator mediator,
IFFmpegProcessService ffmpegProcessService,
ITempFilePool tempFilePool,
ILogger<MovieFolderScanner> logger)
: base(localFileSystem, localStatisticsProvider, metadataRepository, imageCache, ffmpegProcessService, logger)
: base(
localFileSystem,
localStatisticsProvider,
metadataRepository,
imageCache,
ffmpegProcessService,
tempFilePool,
logger)
{
_localFileSystem = localFileSystem;
_movieRepository = movieRepository;

2
ErsatzTV.Core/Metadata/MusicVideoFolderScanner.cs

@ -43,12 +43,14 @@ namespace ErsatzTV.Core.Metadata @@ -43,12 +43,14 @@ namespace ErsatzTV.Core.Metadata
ILibraryRepository libraryRepository,
IMediator mediator,
IFFmpegProcessService ffmpegProcessService,
ITempFilePool tempFilePool,
ILogger<MusicVideoFolderScanner> logger) : base(
localFileSystem,
localStatisticsProvider,
metadataRepository,
imageCache,
ffmpegProcessService,
tempFilePool,
logger)
{
_localFileSystem = localFileSystem;

2
ErsatzTV.Core/Metadata/OtherVideoFolderScanner.cs

@ -41,12 +41,14 @@ namespace ErsatzTV.Core.Metadata @@ -41,12 +41,14 @@ namespace ErsatzTV.Core.Metadata
IOtherVideoRepository otherVideoRepository,
ILibraryRepository libraryRepository,
IFFmpegProcessService ffmpegProcessService,
ITempFilePool tempFilePool,
ILogger<OtherVideoFolderScanner> logger) : base(
localFileSystem,
localStatisticsProvider,
metadataRepository,
imageCache,
ffmpegProcessService,
tempFilePool,
logger)
{
_localFileSystem = localFileSystem;

2
ErsatzTV.Core/Metadata/SongFolderScanner.cs

@ -42,12 +42,14 @@ namespace ErsatzTV.Core.Metadata @@ -42,12 +42,14 @@ namespace ErsatzTV.Core.Metadata
ISongRepository songRepository,
ILibraryRepository libraryRepository,
IFFmpegProcessService ffmpegProcessService,
ITempFilePool tempFilePool,
ILogger<SongFolderScanner> logger) : base(
localFileSystem,
localStatisticsProvider,
metadataRepository,
imageCache,
ffmpegProcessService,
tempFilePool,
logger)
{
_localFileSystem = localFileSystem;

2
ErsatzTV.Core/Metadata/TelevisionFolderScanner.cs

@ -42,12 +42,14 @@ namespace ErsatzTV.Core.Metadata @@ -42,12 +42,14 @@ namespace ErsatzTV.Core.Metadata
ILibraryRepository libraryRepository,
IMediator mediator,
IFFmpegProcessService ffmpegProcessService,
ITempFilePool tempFilePool,
ILogger<TelevisionFolderScanner> logger) : base(
localFileSystem,
localStatisticsProvider,
metadataRepository,
imageCache,
ffmpegProcessService,
tempFilePool,
logger)
{
_localFileSystem = localFileSystem;

6
ErsatzTV/Startup.cs

@ -141,6 +141,11 @@ namespace ErsatzTV @@ -141,6 +141,11 @@ namespace ErsatzTV
Directory.CreateDirectory(FileSystemLayout.TranscodeFolder);
}
if (!Directory.Exists(FileSystemLayout.TempFilePoolFolder))
{
Directory.CreateDirectory(FileSystemLayout.TempFilePoolFolder);
}
Log.Logger.Information("Database is at {DatabasePath}", FileSystemLayout.DatabasePath);
// until we add a setting for a file-specific scheme://host:port to access
@ -245,6 +250,7 @@ namespace ErsatzTV @@ -245,6 +250,7 @@ namespace ErsatzTV
services.AddSingleton<IEntityLocker, EntityLocker>();
services.AddSingleton<ISearchIndex, SearchIndex>();
services.AddSingleton<IFFmpegSegmenterService, FFmpegSegmenterService>();
services.AddSingleton<ITempFilePool, TempFilePool>();
AddChannel<IBackgroundServiceRequest>(services);
AddChannel<IPlexBackgroundServiceRequest>(services);
AddChannel<IJellyfinBackgroundServiceRequest>(services);

Loading…
Cancel
Save