Compare commits

...

13 Commits

Author SHA1 Message Date
SenorSmartyPants f30633d43c
Merge a726629d94 into e2a22cec0e 2024-05-04 13:06:58 -04:00
Nyanmisaka e2a22cec0e Translated using Weblate (Chinese (Simplified))
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/zh_Hans/
2024-05-04 13:06:46 -04:00
queeup 067962ae2a Translated using Weblate (Turkish)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/tr/
2024-05-04 13:06:46 -04:00
Szilágyi Kristóf 8a65d239b7 Translated using Weblate (Hungarian)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/hu/
2024-05-04 13:06:46 -04:00
HiPotionQ8 518404cd1d Translated using Weblate (Arabic)
Translation: Jellyfin/Jellyfin
Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ar/
2024-05-04 13:06:46 -04:00
SenorSmartyPants a726629d94 Match extras based on clean file name 2024-03-12 18:00:30 -05:00
SenorSmartyPants d53a61cc48 Fix case when series name ends in special character
Add unit test
2023-03-30 18:17:40 -05:00
SenorSmartyPants 180e720d17 Fix extras test
Directory type extras are found before suffix types
2023-03-09 19:56:00 -06:00
SenorSmartyPants 881f1b7b40 Fix linting errors 2023-03-09 19:13:19 -06:00
SenorSmartyPants aa4b7c6547 use filename not video.name for extra suffix type comparison 2023-03-09 19:11:54 -06:00
SenorSmartyPants bc93f34ffa Determine isInMixedFolder after removing extras from consideration
This allows putting an episode in a folder by itself and having suffixed extras and extra subfolders
2023-03-09 19:11:54 -06:00
SenorSmartyPants bf9c528593 Test for episode extras with trailing number
Fix test typo
2023-03-09 19:11:54 -06:00
SenorSmartyPants 0569017b79 Parse episode extras - only suffix style is supported 2023-03-09 19:11:54 -06:00
9 changed files with 305 additions and 24 deletions

View File

@ -48,6 +48,8 @@ using Episode = MediaBrowser.Controller.Entities.TV.Episode;
using EpisodeInfo = Emby.Naming.TV.EpisodeInfo;
using Genre = MediaBrowser.Controller.Entities.Genre;
using Person = MediaBrowser.Controller.Entities.Person;
using Season = MediaBrowser.Controller.Entities.TV.Season;
using Series = MediaBrowser.Controller.Entities.TV.Series;
using VideoResolver = Emby.Naming.Video.VideoResolver;
namespace Emby.Server.Implementations.Library
@ -2654,11 +2656,12 @@ namespace Emby.Server.Implementations.Library
yield break;
}
var resolver = new EpisodeResolver(_namingOptions);
var count = fileSystemChildren.Count;
for (var i = 0; i < count; i++)
{
var current = fileSystemChildren[i];
if (current.IsDirectory && _namingOptions.AllExtrasTypesFolderNames.ContainsKey(current.Name))
if (!owner.IsInMixedFolder && current.IsDirectory && _namingOptions.AllExtrasTypesFolderNames.ContainsKey(current.Name))
{
var filesInSubFolder = _fileSystem.GetFiles(current.FullName, null, false, false);
foreach (var file in filesInSubFolder)
@ -2677,10 +2680,61 @@ namespace Emby.Server.Implementations.Library
}
else if (!current.IsDirectory && _extraResolver.TryGetExtraTypeForOwner(current.FullName, ownerVideoInfo, out var extraType))
{
var extra = GetExtra(current, extraType.Value);
if (extra is not null)
// if owner is dir, don't own episode extras
// test if extra filename is formatted like an episode
int? dashIndex = current.Name?.LastIndexOf('-');
string prefix = null;
if (dashIndex is int dashIndexValue)
{
yield return extra;
if (dashIndexValue >= 0)
{
prefix = current.Name.Substring(0, dashIndexValue); // possible episode name
prefix = ParseName(prefix).Name; // clean name - remove all things in brackets etc
string path = current.FullName;
path = string.Concat(path.AsSpan(0, path.LastIndexOf('-')), current.Extension);
var episodeInfo = resolver.Resolve(path, false);
string seriesName = null;
if (owner is Series series)
{
seriesName = series.Name;
}
if (owner is Season season)
{
seriesName = season.SeriesName;
}
if (seriesName is not null && episodeInfo?.SeriesName is not null)
{
// trim series names like episodepathparser does
seriesName = seriesName
.Trim()
.Trim('_', '.', '-')
.Trim();
var episodeInfoSeriesName = episodeInfo.SeriesName
.Trim()
.Trim('_', '.', '-')
.Trim();
if (seriesName.Equals(episodeInfoSeriesName, StringComparison.OrdinalIgnoreCase))
{
// don't attach episode extras to series or season
continue;
}
}
}
}
// if owner is Episode, only suffix type matches will be allowed, episode file cleaned name must match
if (owner is not Episode || (prefix is not null && prefix.Equals(ownerVideoInfo.Name, StringComparison.OrdinalIgnoreCase)))
{
var extra = GetExtra(current, extraType.Value);
if (extra is not null)
{
yield return extra;
}
}
}
}

View File

@ -281,8 +281,6 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
ExtraFiles = leftOver
};
var isInMixedFolder = resolverResult.Count > 1 || parent?.IsTopParent == true;
foreach (var video in resolverResult)
{
var firstVideo = video.Files[0];
@ -298,7 +296,6 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
var videoItem = new T
{
Path = path,
IsInMixedFolder = isInMixedFolder,
ProductionYear = video.Year,
Name = parseName ? video.Name : firstVideo.Name,
AdditionalParts = additionalParts,
@ -313,6 +310,13 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
result.ExtraFiles.AddRange(files.Where(i => !ContainsFile(resolverResult, i)));
// calculate if in mixed folder after extra files have been removed from count
var isInMixedFolder = result.Items.Count > 1 || parent?.IsTopParent == true;
foreach (var item in result.Items)
{
item.IsInMixedFolder = isInMixedFolder;
}
return result;
}

View File

@ -128,5 +128,7 @@
"TaskRefreshTrickplayImages": "توليد صور Trickplay",
"TaskRefreshTrickplayImagesDescription": "يُنشئ معاينات Trickplay لمقاطع الفيديو في المكتبات المُمكّنة.",
"TaskCleanCollectionsAndPlaylists": "حذف المجموعات وقوائم التشغيل",
"TaskCleanCollectionsAndPlaylistsDescription": "حذف عناصر من المجموعات وقوائم التشغيل التي لم تعد موجودة."
"TaskCleanCollectionsAndPlaylistsDescription": "حذف عناصر من المجموعات وقوائم التشغيل التي لم تعد موجودة.",
"TaskAudioNormalization": "تطبيع الصوت",
"TaskAudioNormalizationDescription": "مسح الملفات لتطبيع بيانات الصوت."
}

View File

@ -1,13 +1,13 @@
{
"Albums": "Albumok",
"AppDeviceValues": "Program: {0}, eszköz: {1}",
"AppDeviceValues": "Program: {0}, Eszköz: {1}",
"Application": "Alkalmazás",
"Artists": "Előadók",
"AuthenticationSucceededWithUserName": "{0} sikeresen hitelesítve",
"Books": "Könyvek",
"CameraImageUploadedFrom": "Új kamerakép feltöltve innen: {0}",
"CameraImageUploadedFrom": "Új kamerakép lett feltöltve innen: {0}",
"Channels": "Csatornák",
"ChapterNameValue": "{0}. jelenet",
"ChapterNameValue": "Jelenet {0}",
"Collections": "Gyűjtemények",
"DeviceOfflineWithName": "{0} kijelentkezett",
"DeviceOnlineWithName": "{0} belépett",
@ -15,27 +15,27 @@
"Favorites": "Kedvencek",
"Folders": "Könyvtárak",
"Genres": "Műfajok",
"HeaderAlbumArtists": "Albumelőadók",
"HeaderAlbumArtists": "Album előadók",
"HeaderContinueWatching": "Megtekintés folytatása",
"HeaderFavoriteAlbums": "Kedvenc albumok",
"HeaderFavoriteAlbums": "Kedvenc Albumok",
"HeaderFavoriteArtists": "Kedvenc előadók",
"HeaderFavoriteEpisodes": "Kedvenc epizódok",
"HeaderFavoriteShows": "Kedvenc sorozatok",
"HeaderFavoriteSongs": "Kedvenc számok",
"HeaderFavoriteSongs": "Kedvenc dalok",
"HeaderLiveTV": "Élő TV",
"HeaderNextUp": "Következik",
"HeaderRecordingGroups": "Felvételi csoportok",
"HomeVideos": "Házi videók",
"HomeVideos": "Otthoni videók",
"Inherit": "Örökölt",
"ItemAddedWithName": "{0} hozzáadva a könyvtárhoz",
"ItemRemovedWithName": "{0} eltávolítva a könyvtárból",
"LabelIpAddressValue": "IP-cím: {0}",
"LabelRunningTimeValue": "Lejátszási idő: {0}",
"Latest": "Legújabb",
"MessageApplicationUpdated": "A Jellyfin kiszolgáló frissítve",
"MessageApplicationUpdated": "A Jellyfin kiszolgáló frissítve lett",
"MessageApplicationUpdatedTo": "A Jellyfin kiszolgáló frissítve lett a következőre: {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "A kiszolgálókonfigurációs rész frissítve: {0}",
"MessageServerConfigurationUpdated": "Kiszolgálókonfiguráció frissítve",
"MessageNamedServerConfigurationUpdatedWithValue": "A kiszolgálókonfigurációs rész frissítve lett: {0}",
"MessageServerConfigurationUpdated": "Kiszolgálókonfiguráció frissítve lett",
"MixedContent": "Vegyes tartalom",
"Movies": "Filmek",
"Music": "Zenék",
@ -46,7 +46,7 @@
"NewVersionIsAvailable": "Letölthető a Jellyfin kiszolgáló új verziója.",
"NotificationOptionApplicationUpdateAvailable": "Frissítés érhető el az alkalmazáshoz",
"NotificationOptionApplicationUpdateInstalled": "Alkalmazásfrissítés telepítve",
"NotificationOptionAudioPlayback": "Hanglejátszás elkezdve",
"NotificationOptionAudioPlayback": "Hanglejátszás elkezdődött",
"NotificationOptionAudioPlaybackStopped": "Hanglejátszás leállítva",
"NotificationOptionCameraImageUploaded": "Kamerakép feltöltve",
"NotificationOptionInstallationFailed": "Telepítési hiba",
@ -126,5 +126,9 @@
"External": "Külső",
"HearingImpaired": "Hallássérült",
"TaskRefreshTrickplayImages": "Trickplay képek generálása",
"TaskRefreshTrickplayImagesDescription": "Trickplay előnézetet készít az engedélyezett könyvtárakban lévő videókhoz."
"TaskRefreshTrickplayImagesDescription": "Trickplay előnézetet készít az engedélyezett könyvtárakban lévő videókhoz.",
"TaskAudioNormalization": "Hangerő Normalizáció",
"TaskCleanCollectionsAndPlaylistsDescription": "Nem létező elemek törlése a gyűjteményekből és lejátszási listákról.",
"TaskAudioNormalizationDescription": "Hangerő normalizációs adatok keresése.",
"TaskCleanCollectionsAndPlaylists": "Gyűjtemények és lejátszási listák optimalizálása"
}

View File

@ -128,5 +128,7 @@
"TaskRefreshTrickplayImages": "Trickplay Görselleri Oluştur",
"TaskRefreshTrickplayImagesDescription": "Etkin kütüphanelerdeki videolar için trickplay önizlemeleri oluşturur.",
"TaskCleanCollectionsAndPlaylistsDescription": "Artık var olmayan koleksiyon ve çalma listelerindeki ögeleri kaldırır.",
"TaskCleanCollectionsAndPlaylists": "Koleksiyonları ve çalma listelerini temizleyin"
"TaskCleanCollectionsAndPlaylists": "Koleksiyonları ve çalma listelerini temizleyin",
"TaskAudioNormalizationDescription": "Ses normalleştirme verileri için dosyaları tarar.",
"TaskAudioNormalization": "Ses Normalleştirme"
}

View File

@ -128,5 +128,7 @@
"TaskRefreshTrickplayImages": "生成时间轴缩略图",
"TaskRefreshTrickplayImagesDescription": "为启用的媒体库中的视频生成时间轴缩略图。",
"TaskCleanCollectionsAndPlaylists": "清理合集和播放列表",
"TaskCleanCollectionsAndPlaylistsDescription": "清理合集和播放列表中已不存在的项目。"
"TaskCleanCollectionsAndPlaylistsDescription": "清理合集和播放列表中已不存在的项目。",
"TaskAudioNormalization": "音频标准化",
"TaskAudioNormalizationDescription": "扫描文件以寻找音频标准化数据。"
}

View File

@ -19,6 +19,7 @@ using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers;
@ -1345,7 +1346,7 @@ namespace MediaBrowser.Controller.Entities
/// <returns><c>true</c> if any items have changed, else <c>false</c>.</returns>
protected virtual async Task<bool> RefreshedOwnedItems(MetadataRefreshOptions options, IReadOnlyList<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken)
{
if (!IsFileProtocol || !SupportsOwnedItems || IsInMixedFolder || this is ICollectionFolder or UserRootFolder or AggregateFolder || this.GetType() == typeof(Folder))
if (!IsFileProtocol || !SupportsOwnedItems || (IsInMixedFolder && this is not Episode) || this is ICollectionFolder or UserRootFolder or AggregateFolder || this.GetType() == typeof(Folder))
{
return false;
}

View File

@ -45,7 +45,7 @@ namespace MediaBrowser.Controller.Entities.TV
public int? IndexNumberEnd { get; set; }
[JsonIgnore]
protected override bool SupportsOwnedItems => IsStacked || MediaSourceCount > 1;
protected override bool SupportsOwnedItems => true;
[JsonIgnore]
public override bool SupportsInheritedParentImages => true;

View File

@ -331,4 +331,216 @@ public class FindExtrasTests
Assert.Equal("/series/Dexter/trailer.mkv", extras[0].Path);
Assert.Equal("/series/Dexter/trailers/trailer2.mkv", extras[1].Path);
}
[Fact]
public void FindExtras_SeriesWithExtras_FindsCorrectExtras()
{
var owner = new Series { Name = "Dexter", Path = "/series/Dexter" };
var paths = new List<string>
{
"/series/Dexter/Season 1/Dexter - S01E01.mkv",
"/series/Dexter/Season 1/Dexter - S01E01-deleted.mkv",
"/series/Dexter/Season 1/Dexter - S01E01 [WEBDL-1080p AVC][AAC 2.0][YouTube]-deleted.mkv",
"/series/Dexter/Season 1/Dexter - S01E02 - Second Epi.mkv",
"/series/Dexter/Season 1/Dexter - S01E02 - Second Epi-interview.mkv",
"/series/Dexter/Season 1/Dexter - S01E02 - Second Epi-scene.mkv",
"/series/Dexter/Season 1/It's a begining-behindthescenes.mkv",
"/series/Dexter/Season 1/interviews/The Cast.mkv",
"/series/Dexter/Funny-behindthescenes.mkv",
"/series/Dexter/interviews/The Director.mkv",
"/series/Dexter/Dexter - S02E05.mkv",
"/series/Dexter/Dexter - S02E05-clip.mkv",
"/series/Dexter/Dexter - S03E05/Dexter - S03E05 - Fifth.mkv",
"/series/Dexter/Dexter - S03E05/Dexter - S03E05 - Fifth-featurette.mkv",
};
var files = paths.Select(p => new FileSystemMetadata
{
FullName = p,
Name = Path.GetFileName(p),
Extension = Path.GetExtension(p),
IsDirectory = string.IsNullOrEmpty(Path.GetExtension(p))
}).ToList();
var extras = _libraryManager.FindExtras(owner, files, new DirectoryService(_fileSystemMock.Object)).OrderBy(e => e.ExtraType).ToList();
Assert.Equal(2, extras.Count);
Assert.Equal(ExtraType.BehindTheScenes, extras[0].ExtraType);
Assert.Equal(typeof(Video), extras[0].GetType());
Assert.Equal("Funny-behindthescenes", extras[0].FileNameWithoutExtension);
Assert.Equal("/series/Dexter/Funny-behindthescenes.mkv", extras[0].Path);
Assert.Equal("/series/Dexter/interviews/The Director.mkv", extras[1].Path);
}
[Fact]
public void FindExtras_SeasonWithExtras_FindsCorrectExtras()
{
var owner = new Season { Name = "Season 1", SeriesName = "Dexter", Path = "/series/Dexter/Season 1" };
var paths = new List<string>
{
"/series/Dexter/Season 1/Dexter 1x01 [Bluray-1080p x264][AC3 5.1][-reward] - Northwest Passage.mkv",
"/series/Dexter/Season 1/Dexter 1x01-deleted.mkv",
"/series/Dexter/Season 1/Dexter 1x01 [WEBDL-1080p AVC][AAC 2.0][YouTube]-deleted.mkv",
"/series/Dexter/Season 1/Dexter 1x01 [WEBDL-1080p AVC][AAC 2.0][YouTube][-MrC] - Log Lady Introduction 1-extra.mkv",
"/series/Dexter/Season 1/Dexter - S01E02 - Second Epi.mkv",
"/series/Dexter/Season 1/Dexter - S01E02 - Second Epi-interview.mkv",
"/series/Dexter/Season 1/Dexter - S01E02 - Second Epi-scene.mkv",
"/series/Dexter/Season 1/It's a begining-behindthescenes.mkv",
"/series/Dexter/Season 1/interviews/The Cast.mkv",
"/series/Dexter/Funny-behindthescenes.mkv",
"/series/Dexter/interviews/The Director.mkv",
"/series/Dexter/Dexter - S02E05.mkv",
"/series/Dexter/Dexter - S02E05-clip.mkv",
"/series/Dexter/Dexter - S03E05/Dexter - S03E05 - Fifth.mkv",
"/series/Dexter/Dexter - S03E05/Dexter - S03E05 - Fifth-featurette.mkv",
};
var files = paths.Select(p => new FileSystemMetadata
{
FullName = p,
Name = Path.GetFileName(p),
Extension = Path.GetExtension(p),
IsDirectory = string.IsNullOrEmpty(Path.GetExtension(p))
}).ToList();
var extras = _libraryManager.FindExtras(owner, files, new DirectoryService(_fileSystemMock.Object)).OrderBy(e => e.ExtraType).ToList();
Assert.Equal(2, extras.Count);
Assert.Equal(ExtraType.BehindTheScenes, extras[0].ExtraType);
Assert.Equal(typeof(Video), extras[0].GetType());
Assert.Equal("It's a begining-behindthescenes", extras[0].FileNameWithoutExtension);
Assert.Equal("/series/Dexter/Season 1/It's a begining-behindthescenes.mkv", extras[0].Path);
Assert.Equal("/series/Dexter/Season 1/interviews/The Cast.mkv", extras[1].Path);
}
[Fact]
public void FindExtras_SeasonWithExtras_FindsCorrectExtras2()
{
// Series name directory has special characters stripped that episodes do not
var owner = new Season { Name = "Season 1", SeriesName = "The Venture Bros.", Path = "/series/The Venture Bros/Season 1" };
var paths = new List<string>
{
"/series/The Venture Bros/Season 1/The Venture Bros. S01E01.mkv",
"/series/The Venture Bros/Season 1/The Venture Bros. S01E01-deleted.mkv",
"/series/The Venture Bros/Season 1/The Venture Bros. - S01E02 - Second Epi.mkv",
"/series/The Venture Bros/Season 1/The Venture Bros. - S01E02 - Second Epi-interview.mkv",
"/series/The Venture Bros/Season 1/The Venture Bros. - S01E02 - Second Epi-scene.mkv",
"/series/The Venture Bros/Season 1/It's a begining-behindthescenes.mkv",
"/series/The Venture Bros/Season 1/interviews/The Cast.mkv",
};
var files = paths.Select(p => new FileSystemMetadata
{
FullName = p,
Name = Path.GetFileName(p),
Extension = Path.GetExtension(p),
IsDirectory = string.IsNullOrEmpty(Path.GetExtension(p))
}).ToList();
var extras = _libraryManager.FindExtras(owner, files, new DirectoryService(_fileSystemMock.Object)).OrderBy(e => e.ExtraType).ToList();
Assert.Equal(2, extras.Count);
Assert.Equal(ExtraType.BehindTheScenes, extras[0].ExtraType);
Assert.Equal(typeof(Video), extras[0].GetType());
Assert.Equal("It's a begining-behindthescenes", extras[0].FileNameWithoutExtension);
Assert.Equal("/series/The Venture Bros/Season 1/It's a begining-behindthescenes.mkv", extras[0].Path);
Assert.Equal("/series/The Venture Bros/Season 1/interviews/The Cast.mkv", extras[1].Path);
}
[Fact]
public void FindExtras_EpisodeWithExtras_FindsCorrectExtras()
{
var paths = new List<string>
{
"/series/Dexter/Season 1/Dexter - S01E01.mkv",
"/series/Dexter/Season 1/Dexter - S01E01-deleted.mkv",
"/series/Dexter/Season 1/Dexter - S01E02 - Second Epi.mkv",
"/series/Dexter/Season 1/Dexter - S01E02 - Second Epi-interview.mkv",
"/series/Dexter/Season 1/Dexter - S01E02 - Second Epi-scene.mkv",
"/series/Dexter/Season 1/It's a begining-behindthescenes.mkv",
"/series/Dexter/Season 1/interviews/The Cast.mkv",
"/series/Dexter/Funny-behindthescenes.mkv",
"/series/Dexter/interviews/The Director.mkv",
"/series/Dexter/Dexter - S02E05.mkv",
"/series/Dexter/Dexter - S02E05-clip.mkv",
"/series/Dexter/Dexter - S03E05/Dexter - S03E05 - Fifth.mkv",
"/series/Dexter/Dexter - S03E05/Dexter - S03E05 - Fifth-featurette.mkv",
"/series/Dexter/Dexter - S03E05/Dexter - S03E05 - Fifth-featurette2.mkv",
"/series/Dexter/Dexter - S03E05/Deleted Scenes/Meet Friends.mkv",
};
var files = paths.Select(p => new FileSystemMetadata
{
FullName = p,
Name = Path.GetFileName(p),
Extension = Path.GetExtension(p),
IsDirectory = string.IsNullOrEmpty(Path.GetExtension(p))
}).ToList();
var owner = new Episode { Name = "Dexter - S01E01", Path = "/series/Dexter/Season 1/Dexter - S01E01.mkv", IsInMixedFolder = true };
var extras = _libraryManager.FindExtras(owner, files, new DirectoryService(_fileSystemMock.Object)).OrderBy(e => e.ExtraType).ToList();
Assert.Single(extras);
Assert.Equal(ExtraType.DeletedScene, extras[0].ExtraType);
Assert.Equal(typeof(Video), extras[0].GetType());
Assert.Equal("/series/Dexter/Season 1/Dexter - S01E01-deleted.mkv", extras[0].Path);
owner = new Episode { Name = "Dexter - S01E02 - Second Epi", Path = "/series/Dexter/Season 1/Dexter - S01E02 - Second Epi.mkv", IsInMixedFolder = true };
extras = _libraryManager.FindExtras(owner, files, new DirectoryService(_fileSystemMock.Object)).OrderBy(e => e.ExtraType).ToList();
Assert.Equal(2, extras.Count);
Assert.Equal(ExtraType.Interview, extras[0].ExtraType);
Assert.Equal(ExtraType.Scene, extras[1].ExtraType);
Assert.Equal(typeof(Video), extras[0].GetType());
Assert.Equal("/series/Dexter/Season 1/Dexter - S01E02 - Second Epi-interview.mkv", extras[0].Path);
Assert.Equal("/series/Dexter/Season 1/Dexter - S01E02 - Second Epi-scene.mkv", extras[1].Path);
owner = new Episode { Name = "Dexter - S02E05", Path = "/series/Dexter/Dexter - S02E05.mkv", IsInMixedFolder = true };
extras = _libraryManager.FindExtras(owner, files, new DirectoryService(_fileSystemMock.Object)).OrderBy(e => e.ExtraType).ToList();
Assert.Single(extras);
Assert.Equal(ExtraType.Clip, extras[0].ExtraType);
Assert.Equal(typeof(Video), extras[0].GetType());
Assert.Equal("/series/Dexter/Dexter - S02E05-clip.mkv", extras[0].Path);
// episode folder with special feature subfolders are not supported yet, but it should be considered as not mixed, but current is marked as mixed
Folder folderOwner = new Folder { Name = "Dexter - S03E05", Path = "/series/Dexter/Dexter - S03E05", IsInMixedFolder = false };
extras = _libraryManager.FindExtras(folderOwner, files, new DirectoryService(_fileSystemMock.Object)).OrderBy(e => e.ExtraType).ToList();
Assert.Equal(3, extras.Count);
// directory type extras are found before suffix type
Assert.Equal(ExtraType.DeletedScene, extras[0].ExtraType);
Assert.Equal("/series/Dexter/Dexter - S03E05/Deleted Scenes/Meet Friends.mkv", extras[0].Path);
Assert.Equal(ExtraType.Featurette, extras[1].ExtraType);
Assert.Equal(typeof(Video), extras[1].GetType());
Assert.Equal("/series/Dexter/Dexter - S03E05/Dexter - S03E05 - Fifth-featurette.mkv", extras[1].Path);
Assert.Equal("/series/Dexter/Dexter - S03E05/Dexter - S03E05 - Fifth-featurette2.mkv", extras[2].Path);
}
[Fact]
public void FindExtras_EpisodeWithExtras_CleanNameTest()
{
var paths = new List<string>
{
"/series/Dexter/Season 1/Dexter - S01E01[Bluray-1080p x264][AC3 5.1].mkv",
"/series/Dexter/Season 1/Dexter - S01E01 [WEBDL-1080p AVC][AAC 2.0][YouTube]-deleted.mkv",
"/series/Dexter/Season 1/Dexter - S01E01[Bluray-1080p x264][AC3 5.1] - Some crazy deleted scene -deleted.mkv"
};
var files = paths.Select(p => new FileSystemMetadata
{
FullName = p,
Name = Path.GetFileName(p),
Extension = Path.GetExtension(p),
IsDirectory = string.IsNullOrEmpty(Path.GetExtension(p))
}).ToList();
var owner = new Episode { Name = "Dexter - S01E01", Path = "/series/Dexter/Season 1/Dexter - S01E01[Bluray-1080p x264][AC3 5.1].mkv", IsInMixedFolder = true };
var extras = _libraryManager.FindExtras(owner, files, new DirectoryService(_fileSystemMock.Object)).OrderBy(e => e.ExtraType).ToList();
Assert.Equal(2, extras.Count);
Assert.Equal(ExtraType.DeletedScene, extras[0].ExtraType);
Assert.Equal(typeof(Video), extras[0].GetType());
Assert.Equal("/series/Dexter/Season 1/Dexter - S01E01 [WEBDL-1080p AVC][AAC 2.0][YouTube]-deleted.mkv", extras[0].Path);
}
}