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. 11
      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/). @@ -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
- 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
- Support `.ogv` video files in local libraries
### Fixed
- Fix antiforgery error caused by reusing existing browser tabs across docker container restarts

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

@ -193,9 +193,14 @@ public abstract class CallLibraryScannerHandler<TRequest> @@ -193,9 +193,14 @@ public abstract class CallLibraryScannerHandler<TRequest>
: "ErsatzTV.Scanner";
string processFileName = Environment.ProcessPath ?? string.Empty;
if (!string.IsNullOrWhiteSpace(processFileName))
{
string localFileName = Path.Combine(Path.GetDirectoryName(processFileName) ?? string.Empty, executable);
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(folderName, executable);
if (File.Exists(localFileName))
{
return localFileName;

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

@ -18,4 +18,5 @@ public interface ILibraryRepository @@ -18,4 +18,5 @@ public interface ILibraryRepository
Task<LibraryFolder> GetOrAddFolder(LibraryPath libraryPath, Option<int> maybeParentFolder, string folder);
Task UpdateLibraryFolderId(MediaFile mediaFile, int libraryFolderId);
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 @@ @@ -1,5 +1,6 @@
using Dapper;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Extensions;
using ErsatzTV.Core.Interfaces.Metadata;
using ErsatzTV.Core.Interfaces.Repositories;
using ErsatzTV.Infrastructure.Extensions;
@ -211,6 +212,41 @@ public class LibraryRepository : ILibraryRepository @@ -211,6 +212,41 @@ public class LibraryRepository : ILibraryRepository
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)
{
int? parentId = null;

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

@ -94,7 +94,7 @@ public class HardwareAccelerationHealthCheck : BaseHealthCheck, IHardwareAcceler @@ -94,7 +94,7 @@ public class HardwareAccelerationHealthCheck : BaseHealthCheck, IHardwareAcceler
var accel = string.Join(", ", accelerationKinds);
var channels = string.Join(", ", badChannels.Map(c => $"{c.Number} - {c.Name}"));
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;

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

@ -19,7 +19,7 @@ public abstract class LocalFolderScanner @@ -19,7 +19,7 @@ public abstract class LocalFolderScanner
{
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"
}.ToImmutableHashSet(StringComparer.OrdinalIgnoreCase);

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

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

Loading…
Cancel
Save