mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-06-30 19:32:57 +01:00
Merge pull request #16828 from Shadowghost/episode-multiple-versions
Implement multiple versions for episodes.
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -10,6 +10,12 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
public class VideoListResolverTests
|
||||
{
|
||||
private readonly NamingOptions _namingOptions = new NamingOptions();
|
||||
private readonly VideoListResolver _videoListResolver;
|
||||
|
||||
public VideoListResolverTests()
|
||||
{
|
||||
_videoListResolver = new VideoListResolver(_namingOptions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestStackAndExtras()
|
||||
@@ -40,9 +46,8 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
"WillyWonka-trailer.mkv"
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
var result = _videoListResolver.Resolve(
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList()).ToList();
|
||||
|
||||
Assert.Equal(11, result.Count);
|
||||
var batman = result.FirstOrDefault(x => string.Equals(x.Name, "Batman", StringComparison.Ordinal));
|
||||
@@ -74,9 +79,8 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
"300.nfo"
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
var result = _videoListResolver.Resolve(
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList()).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
}
|
||||
@@ -90,9 +94,8 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
"300 - trailer.mkv"
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
var result = _videoListResolver.Resolve(
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList()).ToList();
|
||||
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.False(result[0].ExtraType.HasValue);
|
||||
@@ -108,9 +111,8 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
"X-Men Days of Future Past-trailer.mp4"
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
var result = _videoListResolver.Resolve(
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList()).ToList();
|
||||
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.False(result[0].ExtraType.HasValue);
|
||||
@@ -127,9 +129,8 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
"X-Men Days of Future Past-trailer2.mp4"
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
var result = _videoListResolver.Resolve(
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList()).ToList();
|
||||
|
||||
Assert.Equal(3, result.Count);
|
||||
Assert.False(result[0].ExtraType.HasValue);
|
||||
@@ -147,9 +148,8 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
"Looper.2012.bluray.720p.x264.mkv"
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
var result = _videoListResolver.Resolve(
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList()).ToList();
|
||||
|
||||
Assert.Equal(3, result.Count);
|
||||
Assert.False(result[0].ExtraType.HasValue);
|
||||
@@ -166,9 +166,8 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
"/movies/Looper (2012)/Looper.bluray.720p.x264.mkv"
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
var result = _videoListResolver.Resolve(
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList()).ToList();
|
||||
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.False(result[0].ExtraType.HasValue);
|
||||
@@ -188,9 +187,8 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
"My video 5.mkv"
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
var result = _videoListResolver.Resolve(
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList()).ToList();
|
||||
|
||||
Assert.Equal(5, result.Count);
|
||||
}
|
||||
@@ -204,9 +202,8 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
"M:/Movies (DVD)/Movies (Musical)/Sound of Music (1965)/Sound of Music Disc 2"
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => VideoResolver.Resolve(i, true, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
var result = _videoListResolver.Resolve(
|
||||
files.Select(i => VideoResolver.Resolve(i, true, _namingOptions)).OfType<VideoFileInfo>().ToList()).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
}
|
||||
@@ -221,9 +218,8 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
"My movie #2.mp4"
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => VideoResolver.Resolve(i, true, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
var result = _videoListResolver.Resolve(
|
||||
files.Select(i => VideoResolver.Resolve(i, true, _namingOptions)).OfType<VideoFileInfo>().ToList()).ToList();
|
||||
|
||||
Assert.Equal(2, result.Count);
|
||||
}
|
||||
@@ -239,9 +235,8 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
"No (2012)-trailer.mp4"
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
var result = _videoListResolver.Resolve(
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList()).ToList();
|
||||
|
||||
Assert.Equal(3, result.Count);
|
||||
Assert.False(result[0].ExtraType.HasValue);
|
||||
@@ -260,9 +255,8 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
"/Movies/trailer.mp4"
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
var result = _videoListResolver.Resolve(
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList()).ToList();
|
||||
|
||||
Assert.Equal(4, result.Count);
|
||||
Assert.False(result[0].ExtraType.HasValue);
|
||||
@@ -282,9 +276,8 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
"/MCFAMILY-PC/Private3$/Heterosexual/Breast In Class 2 Counterfeit Racks (2011)/Breast In Class 2 Disc 2 cd2.avi"
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
var result = _videoListResolver.Resolve(
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList()).ToList();
|
||||
|
||||
Assert.Equal(2, result.Count);
|
||||
}
|
||||
@@ -297,9 +290,8 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
"/nas-markrobbo78/Videos/INDEX HTPC/Movies/Watched/3 - ACTION/Argo (2012)/movie.mkv"
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
var result = _videoListResolver.Resolve(
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList()).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
}
|
||||
@@ -312,9 +304,8 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
"The Colony.mkv"
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
var result = _videoListResolver.Resolve(
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList()).ToList();
|
||||
|
||||
Assert.Single(result);
|
||||
}
|
||||
@@ -328,9 +319,8 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
"Four Sisters and a Wedding - B.avi"
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
var result = _videoListResolver.Resolve(
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList()).ToList();
|
||||
|
||||
// The result should contain two individual movies
|
||||
// Version grouping should not work here, because the files are not in a directory with the name 'Four Sisters and a Wedding'
|
||||
@@ -346,9 +336,8 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
"Four Rooms - A.mp4"
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
var result = _videoListResolver.Resolve(
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList()).ToList();
|
||||
|
||||
Assert.Equal(2, result.Count);
|
||||
}
|
||||
@@ -362,9 +351,8 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
"/Server/Despicable Me/trailer.mkv"
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
var result = _videoListResolver.Resolve(
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList()).ToList();
|
||||
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.False(result[0].ExtraType.HasValue);
|
||||
@@ -380,9 +368,8 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
"/Server/Despicable Me/trailers/some title.mkv"
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
var result = _videoListResolver.Resolve(
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList()).ToList();
|
||||
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.False(result[0].ExtraType.HasValue);
|
||||
@@ -398,9 +385,8 @@ namespace Jellyfin.Naming.Tests.Video
|
||||
"/Movies/Despicable Me/trailers/trailer.mkv"
|
||||
};
|
||||
|
||||
var result = VideoListResolver.Resolve(
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList(),
|
||||
_namingOptions).ToList();
|
||||
var result = _videoListResolver.Resolve(
|
||||
files.Select(i => VideoResolver.Resolve(i, false, _namingOptions)).OfType<VideoFileInfo>().ToList()).ToList();
|
||||
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.False(result[0].ExtraType.HasValue);
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
using System.Collections.Generic;
|
||||
using Emby.Naming.Common;
|
||||
using Emby.Naming.Video;
|
||||
using Emby.Server.Implementations.Library.Resolvers.Movies;
|
||||
using Jellyfin.Data.Enums;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Drawing;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Movies;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Model.IO;
|
||||
@@ -14,11 +20,12 @@ namespace Jellyfin.Server.Implementations.Tests.Library;
|
||||
public class MovieResolverTests
|
||||
{
|
||||
private static readonly NamingOptions _namingOptions = new();
|
||||
private static readonly VideoListResolver _videoListResolver = new(_namingOptions);
|
||||
|
||||
[Fact]
|
||||
public void Resolve_GivenLocalAlternateVersion_ResolvesToVideo()
|
||||
{
|
||||
var movieResolver = new MovieResolver(Mock.Of<IImageProcessor>(), Mock.Of<ILogger<MovieResolver>>(), _namingOptions, Mock.Of<IDirectoryService>());
|
||||
var movieResolver = new MovieResolver(Mock.Of<IImageProcessor>(), Mock.Of<ILogger<MovieResolver>>(), _namingOptions, Mock.Of<IDirectoryService>(), _videoListResolver);
|
||||
var itemResolveArgs = new ItemResolveArgs(
|
||||
Mock.Of<IServerApplicationPaths>(),
|
||||
null)
|
||||
@@ -32,4 +39,54 @@ public class MovieResolverTests
|
||||
|
||||
Assert.NotNull(movieResolver.Resolve(itemResolveArgs));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResolveMultiple_GivenTvShowsCollection_CreatesEpisodeItems()
|
||||
{
|
||||
// For a tvshows collection, the multi-version grouping must still produce
|
||||
// Episode BaseItems (not generic Video) so downstream metadata fetching
|
||||
// and series-aware logic apply.
|
||||
var movieResolver = new MovieResolver(Mock.Of<IImageProcessor>(), Mock.Of<ILogger<MovieResolver>>(), _namingOptions, Mock.Of<IDirectoryService>(), _videoListResolver);
|
||||
|
||||
var parent = new Folder { Path = "/TV/Show/Season 1" };
|
||||
var files = new List<FileSystemMetadata>
|
||||
{
|
||||
new() { FullName = "/TV/Show/Season 1/Show - S01E01 - 1080p.mkv", Name = "Show - S01E01 - 1080p.mkv", IsDirectory = false },
|
||||
new() { FullName = "/TV/Show/Season 1/Show - S01E01 - 720p.mkv", Name = "Show - S01E01 - 720p.mkv", IsDirectory = false },
|
||||
new() { FullName = "/TV/Show/Season 1/Show - S01E02.mkv", Name = "Show - S01E02.mkv", IsDirectory = false }
|
||||
};
|
||||
|
||||
var result = movieResolver.ResolveMultiple(parent, files, CollectionType.tvshows, Mock.Of<IDirectoryService>());
|
||||
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(2, result.Items.Count);
|
||||
Assert.All(result.Items, item => Assert.IsType<Episode>(item));
|
||||
|
||||
// The S01E01 item should have one alternate version
|
||||
var s01e01 = result.Items.Find(i => i.Path.Contains("S01E01", System.StringComparison.Ordinal));
|
||||
Assert.NotNull(s01e01);
|
||||
Assert.Single(((Video)s01e01).LocalAlternateVersions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResolveMultiple_GivenMoviesCollection_CreatesMovieItems()
|
||||
{
|
||||
// For a movies collection, the multi-version grouping must produce Movie
|
||||
// BaseItems (not generic Video) so downstream movie-specific logic applies.
|
||||
var movieResolver = new MovieResolver(Mock.Of<IImageProcessor>(), Mock.Of<ILogger<MovieResolver>>(), _namingOptions, Mock.Of<IDirectoryService>(), _videoListResolver);
|
||||
|
||||
var parent = new Folder { Path = "/movies/Inception (2010)" };
|
||||
var files = new List<FileSystemMetadata>
|
||||
{
|
||||
new() { FullName = "/movies/Inception (2010)/Inception (2010) - 1080p.mkv", Name = "Inception (2010) - 1080p.mkv", IsDirectory = false },
|
||||
new() { FullName = "/movies/Inception (2010)/Inception (2010) - 720p.mkv", Name = "Inception (2010) - 720p.mkv", IsDirectory = false }
|
||||
};
|
||||
|
||||
var result = movieResolver.ResolveMultiple(parent, files, CollectionType.movies, Mock.Of<IDirectoryService>());
|
||||
|
||||
Assert.NotNull(result);
|
||||
Assert.Single(result.Items);
|
||||
Assert.All(result.Items, item => Assert.IsType<Movie>(item));
|
||||
Assert.Single(((Video)result.Items[0]).LocalAlternateVersions);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user