Browse Source

other video library scanner improvements (#1655)

* improve scanner detection when developing on macos

* support ogv files in local libraries

* improve health check wording

* try to properly restore other videos that are no longer missing
pull/1656/head
Jason Dove 2 years ago committed by GitHub
parent
commit
476fe991b6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 1
      CHANGELOG.md
  2. 9
      ErsatzTV.Application/Libraries/Commands/CallLibraryScannerHandler.cs
  3. 1
      ErsatzTV.Core/Interfaces/Repositories/ILibraryRepository.cs
  4. 36
      ErsatzTV.Infrastructure/Data/Repositories/LibraryRepository.cs
  5. 2
      ErsatzTV.Infrastructure/Health/Checks/HardwareAccelerationHealthCheck.cs
  6. 2
      ErsatzTV.Scanner/Core/Metadata/LocalFolderScanner.cs
  7. 32
      ErsatzTV.Scanner/Core/Metadata/OtherVideoFolderScanner.cs

1
CHANGELOG.md

@ -31,6 +31,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- If it tests well for others, it *may* replace the current `HLS Segmenter` in a future release - If it tests well for others, it *may* replace the current `HLS Segmenter` in a future release
- Add setting to change XMLTV data from `Local` time zone to `UTC` - Add setting to change XMLTV data from `Local` time zone to `UTC`
- This is needed because some clients (incorrectly) ignore time zone specifier and require UTC times - This is needed because some clients (incorrectly) ignore time zone specifier and require UTC times
- Support `.ogv` video files in local libraries
### Fixed ### Fixed
- Fix antiforgery error caused by reusing existing browser tabs across docker container restarts - Fix antiforgery error caused by reusing existing browser tabs across docker container restarts

9
ErsatzTV.Application/Libraries/Commands/CallLibraryScannerHandler.cs

@ -193,9 +193,14 @@ public abstract class CallLibraryScannerHandler<TRequest>
: "ErsatzTV.Scanner"; : "ErsatzTV.Scanner";
string processFileName = Environment.ProcessPath ?? string.Empty; string processFileName = Environment.ProcessPath ?? string.Empty;
if (!string.IsNullOrWhiteSpace(processFileName)) string processExecutable = Path.GetFileNameWithoutExtension(processFileName);
string folderName = Path.GetDirectoryName(
"dotnet".Equals(processExecutable, StringComparison.OrdinalIgnoreCase)
? typeof(EntityIdResult).Assembly.Location
: processFileName);
if (!string.IsNullOrWhiteSpace(folderName))
{ {
string localFileName = Path.Combine(Path.GetDirectoryName(processFileName) ?? string.Empty, executable); string localFileName = Path.Combine(folderName, executable);
if (File.Exists(localFileName)) if (File.Exists(localFileName))
{ {
return localFileName; return localFileName;

1
ErsatzTV.Core/Interfaces/Repositories/ILibraryRepository.cs

@ -18,4 +18,5 @@ public interface ILibraryRepository
Task<LibraryFolder> GetOrAddFolder(LibraryPath libraryPath, Option<int> maybeParentFolder, string folder); Task<LibraryFolder> GetOrAddFolder(LibraryPath libraryPath, Option<int> maybeParentFolder, string folder);
Task UpdateLibraryFolderId(MediaFile mediaFile, int libraryFolderId); Task UpdateLibraryFolderId(MediaFile mediaFile, int libraryFolderId);
Task UpdatePath(LibraryPath libraryPath, string normalizedLibraryPath); Task UpdatePath(LibraryPath libraryPath, string normalizedLibraryPath);
Task<System.Collections.Generic.HashSet<string>> FindAllMissingFiles(LibraryPath libraryPath);
} }

36
ErsatzTV.Infrastructure/Data/Repositories/LibraryRepository.cs

@ -1,5 +1,6 @@
using Dapper; using Dapper;
using ErsatzTV.Core.Domain; using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Extensions;
using ErsatzTV.Core.Interfaces.Metadata; using ErsatzTV.Core.Interfaces.Metadata;
using ErsatzTV.Core.Interfaces.Repositories; using ErsatzTV.Core.Interfaces.Repositories;
using ErsatzTV.Infrastructure.Extensions; using ErsatzTV.Infrastructure.Extensions;
@ -211,6 +212,41 @@ public class LibraryRepository : ILibraryRepository
new { Path = normalizedLibraryPath, libraryPath.Id }); new { Path = normalizedLibraryPath, libraryPath.Id });
} }
public async Task<System.Collections.Generic.HashSet<string>> FindAllMissingFiles(LibraryPath libraryPath)
{
var result = new System.Collections.Generic.HashSet<string>();
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync();
IAsyncEnumerable<MediaItem> items = dbContext.MediaItems
.AsNoTracking()
.Filter(mi => mi.LibraryPathId == libraryPath.Id)
.Filter(mi => mi.State == MediaItemState.FileNotFound)
.Include(mi => (mi as Movie).MediaVersions)
.ThenInclude(mv => mv.MediaFiles)
.Include(mi => (mi as Episode).MediaVersions)
.ThenInclude(mv => mv.MediaFiles)
.Include(mi => (mi as Song).MediaVersions)
.ThenInclude(mv => mv.MediaFiles)
.Include(mi => (mi as MusicVideo).MediaVersions)
.ThenInclude(mv => mv.MediaFiles)
.Include(mi => (mi as OtherVideo).MediaVersions)
.ThenInclude(mv => mv.MediaFiles)
.Include(mi => (mi as Image).MediaVersions)
.ThenInclude(mv => mv.MediaFiles)
.AsAsyncEnumerable();
await foreach (MediaItem item in items)
{
MediaVersion version = item.GetHeadVersion();
foreach (MediaFile file in version.MediaFiles)
{
result.Add(file.Path);
}
}
return result;
}
private static LibraryFolder CreateNewFolder(LibraryPath libraryPath, Option<int> maybeParentFolder, string folder) private static LibraryFolder CreateNewFolder(LibraryPath libraryPath, Option<int> maybeParentFolder, string folder)
{ {
int? parentId = null; int? parentId = null;

2
ErsatzTV.Infrastructure/Health/Checks/HardwareAccelerationHealthCheck.cs

@ -94,7 +94,7 @@ public class HardwareAccelerationHealthCheck : BaseHealthCheck, IHardwareAcceler
var accel = string.Join(", ", accelerationKinds); var accel = string.Join(", ", accelerationKinds);
var channels = string.Join(", ", badChannels.Map(c => $"{c.Number} - {c.Name}")); var channels = string.Join(", ", badChannels.Map(c => $"{c.Number} - {c.Name}"));
return WarningResult( return WarningResult(
$"The following channels are transcoding without hardware acceleration ({accel}): {channels}"); $"The following channels use ffmpeg profiles that are not configured for hardware acceleration ({accel}): {channels}");
} }
return None; return None;

2
ErsatzTV.Scanner/Core/Metadata/LocalFolderScanner.cs

@ -19,7 +19,7 @@ public abstract class LocalFolderScanner
{ {
public static readonly ImmutableHashSet<string> VideoFileExtensions = new[] public static readonly ImmutableHashSet<string> VideoFileExtensions = new[]
{ {
".mpg", ".mp2", ".mpeg", ".mpe", ".mpv", ".ogg", ".mp4", ".mpg", ".mp2", ".mpeg", ".mpe", ".mpv", ".ogg", ".ogv", ".mp4",
".m4p", ".m4v", ".avi", ".wmv", ".mov", ".mkv", ".m2ts", ".ts", ".webm" ".m4p", ".m4v", ".avi", ".wmv", ".mov", ".mkv", ".m2ts", ".ts", ".webm"
}.ToImmutableHashSet(StringComparer.OrdinalIgnoreCase); }.ToImmutableHashSet(StringComparer.OrdinalIgnoreCase);

32
ErsatzTV.Scanner/Core/Metadata/OtherVideoFolderScanner.cs

@ -78,16 +78,32 @@ public class OtherVideoFolderScanner : LocalFolderScanner, IOtherVideoFolderScan
var allFolders = new System.Collections.Generic.HashSet<string>(); var allFolders = new System.Collections.Generic.HashSet<string>();
var folderQueue = new Queue<string>(); var folderQueue = new Queue<string>();
System.Collections.Generic.HashSet<string> allMissingFiles =
await _libraryRepository.FindAllMissingFiles(libraryPath);
if (allMissingFiles.Count > 0)
{
_logger.LogDebug(
"Library path {Path} has {Count} missing files",
libraryPath.Path,
allMissingFiles.Count);
}
string normalizedLibraryPath = libraryPath.Path.TrimEnd( string normalizedLibraryPath = libraryPath.Path.TrimEnd(
Path.DirectorySeparatorChar, Path.DirectorySeparatorChar,
Path.AltDirectorySeparatorChar); Path.AltDirectorySeparatorChar);
if (libraryPath.Path != normalizedLibraryPath) if (libraryPath.Path != normalizedLibraryPath)
{ {
_logger.LogDebug(
"Normalizing library path from {Original} to {Normalized}",
libraryPath.Path,
normalizedLibraryPath);
await _libraryRepository.UpdatePath(libraryPath, normalizedLibraryPath); await _libraryRepository.UpdatePath(libraryPath, normalizedLibraryPath);
} }
if (ShouldIncludeFolder(libraryPath.Path) && allFolders.Add(libraryPath.Path)) if (ShouldIncludeFolder(libraryPath.Path) && allFolders.Add(libraryPath.Path))
{ {
_logger.LogDebug("Adding folder to scanner queue: {Folder}", libraryPath.Path);
folderQueue.Enqueue(libraryPath.Path); folderQueue.Enqueue(libraryPath.Path);
} }
@ -96,6 +112,7 @@ public class OtherVideoFolderScanner : LocalFolderScanner, IOtherVideoFolderScan
.Filter(allFolders.Add) .Filter(allFolders.Add)
.OrderBy(identity)) .OrderBy(identity))
{ {
_logger.LogDebug("Adding folder to scanner queue: {Folder}", folder);
folderQueue.Enqueue(folder); folderQueue.Enqueue(folder);
} }
@ -133,6 +150,7 @@ public class OtherVideoFolderScanner : LocalFolderScanner, IOtherVideoFolderScan
.Filter(allFolders.Add) .Filter(allFolders.Add)
.OrderBy(identity)) .OrderBy(identity))
{ {
_logger.LogDebug("Adding folder to scanner queue: {Folder}", subdirectory);
folderQueue.Enqueue(subdirectory); folderQueue.Enqueue(subdirectory);
} }
@ -142,8 +160,18 @@ public class OtherVideoFolderScanner : LocalFolderScanner, IOtherVideoFolderScan
maybeParentFolder, maybeParentFolder,
otherVideoFolder); otherVideoFolder);
bool hasMissingFiles = allFiles.Any(allMissingFiles.Contains);
bool isSameEtag = knownFolder.Etag == etag && !hasMissingFiles;
_logger.LogDebug(
"Scanning other video folder {Folder}; etag {Etag}; last etag {LastEtag}; has missing files {HasMissingFiles}",
otherVideoFolder,
etag,
knownFolder.Etag,
hasMissingFiles);
// skip folder if etag matches // skip folder if etag matches
if (allFiles.Count == 0 || knownFolder.Etag == etag) if (allFiles.Count == 0 || isSameEtag)
{ {
continue; continue;
} }
@ -156,6 +184,8 @@ public class OtherVideoFolderScanner : LocalFolderScanner, IOtherVideoFolderScan
foreach (string file in allFiles.OrderBy(identity)) foreach (string file in allFiles.OrderBy(identity))
{ {
_logger.LogDebug("Processing other video file {File}", file);
Either<BaseError, MediaItemScanResult<OtherVideo>> maybeVideo = await _otherVideoRepository Either<BaseError, MediaItemScanResult<OtherVideo>> maybeVideo = await _otherVideoRepository
.GetOrAdd(libraryPath, knownFolder, file) .GetOrAdd(libraryPath, knownFolder, file)
.BindT(video => UpdateStatistics(video, ffmpegPath, ffprobePath)) .BindT(video => UpdateStatistics(video, ffmpegPath, ffprobePath))

Loading…
Cancel
Save