Browse Source

spike new scanner

pull/25/head
Jason Dove 6 years ago
parent
commit
868ac2bbc4
  1. 119
      ErsatzTV.Core.Tests/Metadata/TestMediaScannerTests.cs
  2. 1
      ErsatzTV.Core/Domain/MediaMetadata.cs
  3. 8
      ErsatzTV.Core/Domain/MetadataSource.cs
  4. 2
      ErsatzTV.Core/Metadata/FallbackMetadataProvider.cs
  5. 4
      ErsatzTV.Core/Metadata/ItemScanningPlan.cs
  6. 8
      ErsatzTV.Core/Metadata/LocalMediaItemScanningPlan.cs
  7. 3
      ErsatzTV.Core/Metadata/LocalMetadataProvider.cs
  8. 12
      ErsatzTV.Core/Metadata/ScanningAction.cs
  9. 98
      ErsatzTV.Core/Metadata/TestMediaScanner.cs

119
ErsatzTV.Core.Tests/Metadata/TestMediaScannerTests.cs

@ -0,0 +1,119 @@ @@ -0,0 +1,119 @@
using System.Collections.Generic;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Metadata;
using FluentAssertions;
using LanguageExt;
using NUnit.Framework;
namespace ErsatzTV.Core.Tests.Metadata
{
[TestFixture]
public class TestMediaScannerTests
{
private readonly TestMediaScanner _scanner = new();
[Test]
public void NewMovieFile_WithoutNfo_WithoutPoster()
{
// new movie file without nfo and without poster should have statistics and fallback metadata
var movieFileName = "/movies/test (2021)/test (2021).mkv";
string[] fileNames = { movieFileName };
Seq<LocalMediaItemScanningPlan> result = _scanner.DetermineActions(
MediaType.Movie,
Seq<MediaItem>.Empty,
fileNames.ToSeq());
result.Count.Should().Be(1);
(Either<string, MediaItem> source, List<ItemScanningPlan> itemScanningPlans) = result.Head();
source.IsLeft.Should().BeTrue();
source.LeftToSeq().Should().BeEquivalentTo(movieFileName);
itemScanningPlans.Should().BeEquivalentTo(
new ItemScanningPlan(movieFileName, ScanningAction.Statistics),
new ItemScanningPlan(movieFileName, ScanningAction.FallbackMetadata));
}
[Test]
public void NewMovieFile_WithNfo_WithoutPoster(
[Values("test (2021).nfo", "movie.nfo")]
string nfoFile)
{
// new movie file without nfo and without poster should have statistics and fallback metadata
var movieFileName = "/movies/test (2021)/test (2021).mkv";
var nfoFileName = $"/movies/test (2021)/{nfoFile}";
string[] fileNames = { movieFileName, nfoFileName };
Seq<LocalMediaItemScanningPlan> result = _scanner.DetermineActions(
MediaType.Movie,
Seq<MediaItem>.Empty,
fileNames.ToSeq());
result.Count.Should().Be(1);
(Either<string, MediaItem> source, List<ItemScanningPlan> itemScanningPlans) = result.Head();
source.IsLeft.Should().BeTrue();
source.LeftToSeq().Should().BeEquivalentTo(movieFileName);
itemScanningPlans.Should().BeEquivalentTo(
new ItemScanningPlan(movieFileName, ScanningAction.Statistics),
new ItemScanningPlan(nfoFileName, ScanningAction.SidecarMetadata));
}
[Test]
public void NewMovieFile_WithoutNfo_WithPoster(
[Values("", "test (2021)-")]
string basePosterName,
[Values("jpg", "jpeg", "png", "gif", "tbn")]
string posterExtension)
{
// new movie file without nfo and without poster should have statistics and fallback metadata
var movieFileName = "/movies/test (2021)/test (2021).mkv";
var posterFileName = $"/movies/test (2021)/{basePosterName}poster.{posterExtension}";
string[] fileNames = { movieFileName, posterFileName };
Seq<LocalMediaItemScanningPlan> result = _scanner.DetermineActions(
MediaType.Movie,
Seq<MediaItem>.Empty,
fileNames.ToSeq());
result.Count.Should().Be(1);
(Either<string, MediaItem> source, List<ItemScanningPlan> itemScanningPlans) = result.Head();
source.IsLeft.Should().BeTrue();
source.LeftToSeq().Should().BeEquivalentTo(movieFileName);
itemScanningPlans.Should().BeEquivalentTo(
new ItemScanningPlan(movieFileName, ScanningAction.Statistics),
new ItemScanningPlan(movieFileName, ScanningAction.FallbackMetadata),
new ItemScanningPlan(posterFileName, ScanningAction.Poster));
}
[Test]
public void NewMovieFile_WithNfo_WithPoster(
[Values("test (2021).nfo", "movie.nfo")]
string nfoFile,
[Values("", "test (2021)-")]
string basePosterName,
[Values("jpg", "jpeg", "png", "gif", "tbn")]
string posterExtension)
{
// new movie file without nfo and without poster should have statistics and fallback metadata
var movieFileName = "/movies/test (2021)/test (2021).mkv";
var nfoFileName = $"/movies/test (2021)/{nfoFile}";
var posterFileName = $"/movies/test (2021)/{basePosterName}poster.{posterExtension}";
string[] fileNames = { movieFileName, nfoFileName, posterFileName };
Seq<LocalMediaItemScanningPlan> result = _scanner.DetermineActions(
MediaType.Movie,
Seq<MediaItem>.Empty,
fileNames.ToSeq());
result.Count.Should().Be(1);
(Either<string, MediaItem> source, List<ItemScanningPlan> itemScanningPlans) = result.Head();
source.IsLeft.Should().BeTrue();
source.LeftToSeq().Should().BeEquivalentTo(movieFileName);
itemScanningPlans.Should().BeEquivalentTo(
new ItemScanningPlan(movieFileName, ScanningAction.Statistics),
new ItemScanningPlan(nfoFileName, ScanningAction.SidecarMetadata),
new ItemScanningPlan(posterFileName, ScanningAction.Poster));
}
}
}

1
ErsatzTV.Core/Domain/MediaMetadata.cs

@ -5,6 +5,7 @@ namespace ErsatzTV.Core.Domain @@ -5,6 +5,7 @@ namespace ErsatzTV.Core.Domain
{
public record MediaMetadata : IDisplaySize
{
public MetadataSource Source { get; set; }
public TimeSpan Duration { get; set; }
public string SampleAspectRatio { get; set; }
public string DisplayAspectRatio { get; set; }

8
ErsatzTV.Core/Domain/MetadataSource.cs

@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
namespace ErsatzTV.Core.Domain
{
public enum MetadataSource
{
Sidecar = 1,
Fallback = 2
}
}

2
ErsatzTV.Core/Metadata/FallbackMetadataProvider.cs

@ -10,7 +10,7 @@ namespace ErsatzTV.Core.Metadata @@ -10,7 +10,7 @@ namespace ErsatzTV.Core.Metadata
public static MediaMetadata GetFallbackMetadata(MediaItem mediaItem)
{
string fileName = Path.GetFileName(mediaItem.Path);
var metadata = new MediaMetadata { Title = fileName ?? mediaItem.Path };
var metadata = new MediaMetadata { Source = MetadataSource.Fallback, Title = fileName ?? mediaItem.Path };
if (fileName != null)
{

4
ErsatzTV.Core/Metadata/ItemScanningPlan.cs

@ -0,0 +1,4 @@ @@ -0,0 +1,4 @@
namespace ErsatzTV.Core.Metadata
{
public record ItemScanningPlan(string TargetPath, ScanningAction TargetAction);
}

8
ErsatzTV.Core/Metadata/LocalMediaItemScanningPlan.cs

@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
using System.Collections.Generic;
using ErsatzTV.Core.Domain;
using LanguageExt;
namespace ErsatzTV.Core.Metadata
{
public record LocalMediaItemScanningPlan(Either<string, MediaItem> Source, List<ItemScanningPlan> ActionPlans);
}

3
ErsatzTV.Core/Metadata/LocalMetadataProvider.cs

@ -40,6 +40,7 @@ namespace ErsatzTV.Core.Metadata @@ -40,6 +40,7 @@ namespace ErsatzTV.Core.Metadata
mediaItem.Metadata = new MediaMetadata();
}
mediaItem.Metadata.Source = metadata.Source;
mediaItem.Metadata.MediaType = metadata.MediaType;
mediaItem.Metadata.Title = metadata.Title;
mediaItem.Metadata.Subtitle = metadata.Subtitle;
@ -88,6 +89,7 @@ namespace ErsatzTV.Core.Metadata @@ -88,6 +89,7 @@ namespace ErsatzTV.Core.Metadata
return maybeNfo.Match<Option<MediaMetadata>>(
nfo => new MediaMetadata
{
Source = MetadataSource.Sidecar,
MediaType = MediaType.TvShow,
Title = nfo.ShowTitle,
Subtitle = nfo.Title,
@ -114,6 +116,7 @@ namespace ErsatzTV.Core.Metadata @@ -114,6 +116,7 @@ namespace ErsatzTV.Core.Metadata
return maybeNfo.Match<Option<MediaMetadata>>(
nfo => new MediaMetadata
{
Source = MetadataSource.Sidecar,
MediaType = MediaType.Movie,
Title = nfo.Title,
Description = nfo.Outline,

12
ErsatzTV.Core/Metadata/ScanningAction.cs

@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
namespace ErsatzTV.Core.Metadata
{
public enum ScanningAction
{
None = 0,
Statistics = 1,
SidecarMetadata = 2,
FallbackMetadata = 3,
Collections = 4,
Poster = 5
}
}

98
ErsatzTV.Core/Metadata/TestMediaScanner.cs

@ -0,0 +1,98 @@ @@ -0,0 +1,98 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using ErsatzTV.Core.Domain;
using LanguageExt;
using static LanguageExt.Prelude;
namespace ErsatzTV.Core.Metadata
{
public class TestMediaScanner
{
// @formatter:off
private static readonly Seq<string> VideoFileExtensions = Seq(
".mpg", ".mp2", ".mpeg", ".mpe", ".mpv", ".ogg", ".mp4",
".m4p", ".m4v", ".avi", ".wmv", ".mov", ".mkv", ".ts");
// @formatter:on
private static readonly Seq<string> ImageFileExtensions = Seq("jpg", "jpeg", "png", "gif", "tbn");
public Seq<LocalMediaItemScanningPlan> DetermineActions(
MediaType mediaType,
Seq<MediaItem> mediaItems,
Seq<string> files)
{
var results = new IntermediateResults();
var videoFiles = files.Filter(f => VideoFileExtensions.Contains(Path.GetExtension(f)));
(Seq<string> newFiles, Seq<MediaItem> existingMediaItems) = videoFiles.Map(
s => mediaItems.Find(i => i.Path == s).ToEither(s))
.Partition();
// new files
foreach (string file in newFiles)
{
results.Add(file, new ItemScanningPlan(file, ScanningAction.Statistics));
Option<string> maybeNfoFile = LocateNfoFile(mediaType, files, file);
maybeNfoFile.BiIter(
nfoFile => results.Add(file, new ItemScanningPlan(nfoFile, ScanningAction.SidecarMetadata)),
() => results.Add(file, new ItemScanningPlan(file, ScanningAction.FallbackMetadata)));
Option<string> maybePoster = LocatePoster(mediaType, files, file);
maybePoster.IfSome(
posterFile => results.Add(file, new ItemScanningPlan(posterFile, ScanningAction.Poster)));
}
// existing media items
return results.Summarize();
}
private static Option<string> LocateNfoFile(MediaType mediaType, Seq<string> files, string file)
{
switch (mediaType)
{
case MediaType.Movie:
string movieAsNfo = Path.ChangeExtension(file, "nfo");
string movieNfo = Path.Combine(Path.GetDirectoryName(file) ?? string.Empty, "movie.nfo");
return Seq(movieAsNfo, movieNfo)
.Filter(s => files.Contains(s))
.HeadOrNone();
}
return None;
}
private static Option<string> LocatePoster(MediaType mediaType, Seq<string> files, string file)
{
string folder = Path.GetDirectoryName(file) ?? string.Empty;
switch (mediaType)
{
case MediaType.Movie:
IEnumerable<string> possiblePosters = ImageFileExtensions.Collect(
ext => new[] { $"poster.{ext}", Path.GetFileNameWithoutExtension(file) + $"-poster.{ext}" })
.Map(f => Path.Combine(folder, f));
return possiblePosters.Filter(s => files.Contains(s)).HeadOrNone();
}
return None;
}
private class IntermediateResults
{
private readonly List<Tuple<Either<string, MediaItem>, ItemScanningPlan>> _rawResults = new();
public void Add(Either<string, MediaItem> source, ItemScanningPlan plan) =>
_rawResults.Add(Tuple(source, plan));
public Seq<LocalMediaItemScanningPlan> Summarize() =>
_rawResults
.GroupBy(t => t.Item1)
.Select(g => new LocalMediaItemScanningPlan(g.Key, g.Select(g2 => g2.Item2).ToList()))
.ToSeq();
}
}
}
Loading…
Cancel
Save