Browse Source

Fix fallback metadata parsing for TV (#3)

* fix yml formatting

* test build action

* move ci workflow

* fix unit tests from dotnet test command

* add release workflow

* try another matrix syntax

* fix path to csproj

* fix target framework

* more release fixes

* port fixes for running outside of docker

* more search fixes

* improve fallback metadata parsing, add tests

* fix odd behavior when searching collections

* more fallback metadata fixes

* refresh "other" metadata on startup
pull/4/head
Jason Dove 5 years ago committed by GitHub
parent
commit
d04f3d574b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      ErsatzTV.Application/MediaItems/Queries/GetAggregateMediaItemsHandler.cs
  2. 1
      ErsatzTV.Core.Tests/FFmpeg/FFmpegPlaybackSettingsServiceTests.cs
  3. 40
      ErsatzTV.Core.Tests/Metadata/FallbackMetadataProviderTests.cs
  4. 1
      ErsatzTV.Core.Tests/Scheduling/ChronologicalContentTests.cs
  5. 1
      ErsatzTV.Core.Tests/Scheduling/PlayoutBuilderTests.cs
  6. 1
      ErsatzTV.Core.Tests/Scheduling/RandomizedContentTests.cs
  7. 1
      ErsatzTV.Core.Tests/Scheduling/ShuffledContentTests.cs
  8. 30
      ErsatzTV.Core/Metadata/FallbackMetadataProvider.cs
  9. 3
      ErsatzTV.Core/Metadata/LocalMediaScanner.cs
  10. 24
      ErsatzTV.Core/Metadata/LocalMetadataProvider.cs
  11. 6
      ErsatzTV/Pages/MediaCollections.razor
  12. 4
      ErsatzTV/Pages/PlayoutEditor.razor
  13. 2
      ErsatzTV/Pages/ScheduleItemsEditor.razor

5
ErsatzTV.Application/MediaItems/Queries/GetAggregateMediaItemsHandler.cs

@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@ -25,7 +26,7 @@ namespace ErsatzTV.Application.MediaItems.Queries @@ -25,7 +26,7 @@ namespace ErsatzTV.Application.MediaItems.Queries
if (!string.IsNullOrEmpty(request.SearchString))
{
allItems = allItems.Filter(
i => i.Metadata?.Title.ToLowerInvariant().Contains(request.SearchString.ToLowerInvariant()) ==
i => i.Metadata?.Title.Contains(request.SearchString, StringComparison.OrdinalIgnoreCase) ==
true);
}

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

@ -6,6 +6,7 @@ using NUnit.Framework; @@ -6,6 +6,7 @@ using NUnit.Framework;
namespace ErsatzTV.Core.Tests.FFmpeg
{
[TestFixture]
public class FFmpegPlaybackSettingsCalculatorTests
{
public class CalculateSettings

40
ErsatzTV.Core.Tests/Metadata/FallbackMetadataProviderTests.cs

@ -0,0 +1,40 @@ @@ -0,0 +1,40 @@
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Metadata;
using FluentAssertions;
using NUnit.Framework;
namespace ErsatzTV.Core.Tests.Metadata
{
[TestFixture]
public class FallbackMetadataProviderTests
{
[Test]
[TestCase("Awesome Show - s01e02.mkv", "Awesome Show", 1, 2)]
[TestCase("Awesome Show - S01E02.mkv", "Awesome Show", 1, 2)]
[TestCase("Awesome Show - s1e2.mkv", "Awesome Show", 1, 2)]
[TestCase("Awesome Show - S1E2.mkv", "Awesome Show", 1, 2)]
[TestCase("Awesome Show - s01e02 - Episode Title.mkv", "Awesome Show", 1, 2)]
[TestCase("Awesome Show - S01E02 - Episode Title.mkv", "Awesome Show", 1, 2)]
[TestCase("Awesome Show - s1e2 - Episode Title.mkv", "Awesome Show", 1, 2)]
[TestCase("Awesome Show - S1E2 - Episode Title.mkv", "Awesome Show", 1, 2)]
[TestCase("Awesome Show (2021) - s01e02 - Episode Title.mkv", "Awesome Show (2021)", 1, 2)]
[TestCase("Awesome Show (2021) - S01E02 - Episode Title.mkv", "Awesome Show (2021)", 1, 2)]
[TestCase("Awesome Show (2021) - s1e2 - Episode Title.mkv", "Awesome Show (2021)", 1, 2)]
[TestCase("Awesome Show (2021) - S1E2 - Episode Title.mkv", "Awesome Show (2021)", 1, 2)]
[TestCase("Awesome Show - s01e02 - Episode Title-720p.mkv", "Awesome Show", 1, 2)]
[TestCase("Awesome Show - S01E02 - Episode Title-720p.mkv", "Awesome Show", 1, 2)]
[TestCase("Awesome Show - s1e2 - Episode Title-720p.mkv", "Awesome Show", 1, 2)]
[TestCase("Awesome Show - S1E2 - Episode Title-720p.mkv", "Awesome Show", 1, 2)]
[TestCase("Awesome Show (2021) - S01E02 - Description; More Description (1080p QUALITY codec GROUP).mkv", "Awesome Show (2021)", 1, 2)]
[TestCase("Awesome.Show.S01E02.Description.more.Description.QUAlity.codec.CODEC-GROUP.mkv", "Awesome.Show", 1, 2)]
public void GetFallbackMetadata_ShouldHandleVariousFormats(string path, string title, int season, int episode)
{
var metadata = FallbackMetadataProvider.GetFallbackMetadata(path);
metadata.MediaType.Should().Be(MediaType.TvShow);
metadata.Title.Should().Be(title);
metadata.SeasonNumber.Should().Be(season);
metadata.EpisodeNumber.Should().Be(episode);
}
}
}

1
ErsatzTV.Core.Tests/Scheduling/ChronologicalContentTests.cs

@ -9,6 +9,7 @@ using static LanguageExt.Prelude; @@ -9,6 +9,7 @@ using static LanguageExt.Prelude;
namespace ErsatzTV.Core.Tests.Scheduling
{
[TestFixture]
public class ChronologicalContentTests
{
[Test]

1
ErsatzTV.Core.Tests/Scheduling/PlayoutBuilderTests.cs

@ -14,6 +14,7 @@ using static LanguageExt.Prelude; @@ -14,6 +14,7 @@ using static LanguageExt.Prelude;
namespace ErsatzTV.Core.Tests.Scheduling
{
[TestFixture]
public class PlayoutBuilderTests
{
private readonly ILogger<PlayoutBuilder> _logger;

1
ErsatzTV.Core.Tests/Scheduling/RandomizedContentTests.cs

@ -9,6 +9,7 @@ using static LanguageExt.Prelude; @@ -9,6 +9,7 @@ using static LanguageExt.Prelude;
namespace ErsatzTV.Core.Tests.Scheduling
{
[TestFixture]
public class RandomizedContentTests
{
private const int KnownSeed = 22295;

1
ErsatzTV.Core.Tests/Scheduling/ShuffledContentTests.cs

@ -9,6 +9,7 @@ using static LanguageExt.Prelude; @@ -9,6 +9,7 @@ using static LanguageExt.Prelude;
namespace ErsatzTV.Core.Tests.Scheduling
{
[TestFixture]
public class ShuffledContentTests
{
// this seed will produce (shuffle) 1-10 in order

30
ErsatzTV.Core/Metadata/FallbackMetadataProvider.cs

@ -0,0 +1,30 @@ @@ -0,0 +1,30 @@
using System.IO;
using System.Text.RegularExpressions;
using ErsatzTV.Core.Domain;
namespace ErsatzTV.Core.Metadata
{
public static class FallbackMetadataProvider
{
public static MediaMetadata GetFallbackMetadata(string path)
{
string fileName = Path.GetFileName(path);
var metadata = new MediaMetadata { Title = fileName ?? path };
if (fileName != null)
{
const string PATTERN = @"^(.*?)[.\s-]+[sS](\d+)[eE](\d+).*\.\w+$";
Match match = Regex.Match(fileName, PATTERN);
if (match.Success)
{
metadata.MediaType = MediaType.TvShow;
metadata.Title = match.Groups[1].Value;
metadata.SeasonNumber = int.Parse(match.Groups[2].Value);
metadata.EpisodeNumber = int.Parse(match.Groups[3].Value);
}
}
return metadata;
}
}
}

3
ErsatzTV.Core/Metadata/LocalMediaScanner.cs

@ -82,12 +82,13 @@ namespace ErsatzTV.Core.Metadata @@ -82,12 +82,13 @@ namespace ErsatzTV.Core.Metadata
}
// if exists, check if the file was modified
// also, try to re-categorize "other" by refreshing metadata
Seq<MediaItem> modifiedMediaItems = existingMediaItems.Filter(
mediaItem =>
{
DateTime lastWrite = File.GetLastWriteTimeUtc(mediaItem.Path);
bool modified = lastWrite > mediaItem.LastWriteTime.IfNone(DateTime.MinValue);
return modified || mediaItem.Metadata == null;
return modified || mediaItem.Metadata == null || mediaItem.Metadata.MediaType == MediaType.Other;
});
modifiedPlayoutIds.AddRange(await _playoutRepository.GetPlayoutIdsForMediaItems(modifiedMediaItems));
foreach (MediaItem mediaItem in modifiedMediaItems)

24
ErsatzTV.Core/Metadata/LocalMetadataProvider.cs

@ -21,7 +21,8 @@ namespace ErsatzTV.Core.Metadata @@ -21,7 +21,8 @@ namespace ErsatzTV.Core.Metadata
public async Task RefreshMetadata(MediaItem mediaItem)
{
Option<MediaMetadata> maybeMetadata = await LoadMetadata(mediaItem);
MediaMetadata metadata = maybeMetadata.IfNone(() => GetFallbackMetadata(mediaItem));
MediaMetadata metadata =
maybeMetadata.IfNone(() => FallbackMetadataProvider.GetFallbackMetadata(mediaItem.Path));
await ApplyMetadataUpdate(mediaItem, metadata);
}
@ -99,27 +100,6 @@ namespace ErsatzTV.Core.Metadata @@ -99,27 +100,6 @@ namespace ErsatzTV.Core.Metadata
None);
}
private MediaMetadata GetFallbackMetadata(MediaItem mediaItem)
{
string fileName = Path.GetFileName(mediaItem.Path);
var metadata = new MediaMetadata { Title = fileName ?? mediaItem.Path };
if (fileName != null)
{
const string PATTERN = @"^(.*?)[\s-]+[sS](\d+)[eE](\d+)\.\w+$";
Match match = Regex.Match(fileName, PATTERN);
if (match.Success)
{
metadata.MediaType = MediaType.TvShow;
metadata.Title = match.Groups[1].Value;
metadata.SeasonNumber = int.Parse(match.Groups[2].Value);
metadata.EpisodeNumber = int.Parse(match.Groups[3].Value);
}
}
return metadata;
}
private static DateTime? GetAired(string aired)
{
if (string.IsNullOrWhiteSpace(aired))

6
ErsatzTV/Pages/MediaCollections.razor

@ -91,7 +91,11 @@ @@ -91,7 +91,11 @@
_totalItems = aggregateData.Count;
_pagedData = aggregateData.Skip(state.Page * state.PageSize).Take(state.PageSize);
_pagedData = aggregateData
.Skip(_totalItems <= state.PageSize ? 0 : state.Page * state.PageSize)
.Take(state.PageSize)
.OrderBy(c => c.Name);
return new TableData<MediaCollectionSummaryViewModel> { TotalItems = _totalItems, Items = _pagedData };
}

4
ErsatzTV/Pages/PlayoutEditor.razor

@ -49,10 +49,10 @@ @@ -49,10 +49,10 @@
}
private Task<IEnumerable<ChannelViewModel>> SearchChannels(string value) =>
_channels.Filter(c => $"{c.Number} - {c.Name}".ToLowerInvariant().Contains(value ?? string.Empty)).AsTask();
_channels.Filter(c => $"{c.Number} - {c.Name}".Contains(value ?? string.Empty, StringComparison.OrdinalIgnoreCase)).AsTask();
private Task<IEnumerable<ProgramScheduleViewModel>> SearchProgramSchedules(string value) =>
_programSchedules.Filter(c => c.Name.ToLowerInvariant().StartsWith(value ?? string.Empty)).AsTask();
_programSchedules.Filter(c => c.Name.Contains(value ?? string.Empty, StringComparison.OrdinalIgnoreCase)).AsTask();
private async Task HandleSubmitAsync()

2
ErsatzTV/Pages/ScheduleItemsEditor.razor

@ -170,7 +170,7 @@ @@ -170,7 +170,7 @@
}
private Task<IEnumerable<MediaCollectionViewModel>> SearchMediaCollections(string value) =>
_mediaCollections.Filter(c => c.Name.ToLowerInvariant().Contains((value ?? string.Empty).ToLowerInvariant())).AsTask();
_mediaCollections.Filter(c => c.Name.Contains(value ?? string.Empty, StringComparison.OrdinalIgnoreCase)).AsTask();
private async Task SaveChanges()
{

Loading…
Cancel
Save