* rework television media * refactor poster saving * television and movie views are working again * remove dead code * use paper styling for all cards * add show poster, plot to seasons page * remove missing shows; cleanup interfaces * fix split show display (same show in different folders/sources) * add placeholder "add to schedule" button * no more duplicate television shows, even with the same show split across sources * stop releasing CLI for now * use season number as season placeholder * add television shows to collections * add television seasons to collections * add television episodes to collections * add movies to collections * remove movies, shows, seasons, episodes from collections * fix page width and menus * fix buffer size defaults * fix chronological episode ordering * allow deleting media collections * don't get stuck building a playout with an empty collection * schedule editing and playouts work again * minor cleanup * remove dead code * fix bugs with viewing movies as they are loading * add scanner tests; support nested movie folders * update collections docs * rearrange order of schedule items * add show and season to schedule * delete schedules that use legacy collections, reset all posters * move cleanup to new migration * load fallback metadata when nfo fails; don't require metadata in ui * update readme and screenshots
109 lines
3.9 KiB
C#
109 lines
3.9 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using ErsatzTV.Core.Domain;
|
|
using ErsatzTV.Core.Interfaces.Domain;
|
|
using ErsatzTV.Core.Interfaces.Images;
|
|
using ErsatzTV.Core.Interfaces.Metadata;
|
|
using LanguageExt;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
namespace ErsatzTV.Core.Metadata
|
|
{
|
|
public abstract class LocalFolderScanner
|
|
{
|
|
public static readonly List<string> VideoFileExtensions = new()
|
|
{
|
|
".mpg", ".mp2", ".mpeg", ".mpe", ".mpv", ".ogg", ".mp4",
|
|
".m4p", ".m4v", ".avi", ".wmv", ".mov", ".mkv", ".ts"
|
|
};
|
|
|
|
public static readonly List<string> ImageFileExtensions = new()
|
|
{
|
|
"jpg", "jpeg", "png", "gif", "tbn"
|
|
};
|
|
|
|
public static readonly List<string> ExtraFiles = new()
|
|
{
|
|
"behindthescenes", "deleted", "featurette",
|
|
"interview", "scene", "short", "trailer", "other"
|
|
};
|
|
|
|
public static readonly List<string> ExtraDirectories = new List<string>
|
|
{
|
|
"behind the scenes", "deleted scenes", "featurettes",
|
|
"interviews", "scenes", "shorts", "trailers", "other",
|
|
"extras", "specials"
|
|
}
|
|
.Map(s => $"{Path.DirectorySeparatorChar}{s}{Path.DirectorySeparatorChar}")
|
|
.ToList();
|
|
|
|
private readonly IImageCache _imageCache;
|
|
|
|
private readonly ILocalFileSystem _localFileSystem;
|
|
private readonly ILocalStatisticsProvider _localStatisticsProvider;
|
|
private readonly ILogger _logger;
|
|
|
|
protected LocalFolderScanner(
|
|
ILocalFileSystem localFileSystem,
|
|
ILocalStatisticsProvider localStatisticsProvider,
|
|
IImageCache imageCache,
|
|
ILogger logger)
|
|
{
|
|
_localFileSystem = localFileSystem;
|
|
_localStatisticsProvider = localStatisticsProvider;
|
|
_imageCache = imageCache;
|
|
_logger = logger;
|
|
}
|
|
|
|
protected async Task<Either<BaseError, T>> UpdateStatistics<T>(T mediaItem, string ffprobePath)
|
|
where T : MediaItem
|
|
{
|
|
try
|
|
{
|
|
if (mediaItem.Statistics is null ||
|
|
(mediaItem.Statistics.LastWriteTime ?? DateTime.MinValue) <
|
|
_localFileSystem.GetLastWriteTime(mediaItem.Path))
|
|
{
|
|
_logger.LogDebug("Refreshing {Attribute} for {Path}", "Statistics", mediaItem.Path);
|
|
await _localStatisticsProvider.RefreshStatistics(ffprobePath, mediaItem);
|
|
}
|
|
|
|
return mediaItem;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return BaseError.New(ex.Message);
|
|
}
|
|
}
|
|
|
|
protected async Task SavePosterToDisk<T>(
|
|
T show,
|
|
string posterPath,
|
|
Func<T, Task<bool>> update,
|
|
int height = 220) where T : IHasAPoster
|
|
{
|
|
byte[] originalBytes = await _localFileSystem.ReadAllBytes(posterPath);
|
|
Either<BaseError, string> maybeHash = await _imageCache.ResizeAndSaveImage(originalBytes, height, null);
|
|
await maybeHash.Match(
|
|
hash =>
|
|
{
|
|
show.Poster = hash;
|
|
show.PosterLastWriteTime = _localFileSystem.GetLastWriteTime(posterPath);
|
|
return update(show);
|
|
},
|
|
error =>
|
|
{
|
|
_logger.LogWarning("Unable to save poster to disk from {Path}: {Error}", posterPath, error.Value);
|
|
return Task.CompletedTask;
|
|
});
|
|
}
|
|
|
|
protected Task<Either<BaseError, string>> SavePosterToDisk(string posterPath, int height = 220) =>
|
|
_localFileSystem.ReadAllBytes(posterPath)
|
|
.Bind(bytes => _imageCache.ResizeAndSaveImage(bytes, height, null));
|
|
}
|
|
}
|