mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-07-09 03:04:24 -04:00
Merge pull request #2696 from mark-monteiro/fix-extras
Display Extras With Unknown Types
This commit is contained in:
commit
5b59cd2eb2
@ -505,7 +505,63 @@ namespace Emby.Naming.Common
|
|||||||
RuleType = ExtraRuleType.Suffix,
|
RuleType = ExtraRuleType.Suffix,
|
||||||
Token = "-short",
|
Token = "-short",
|
||||||
MediaType = MediaType.Video
|
MediaType = MediaType.Video
|
||||||
}
|
},
|
||||||
|
new ExtraRule
|
||||||
|
{
|
||||||
|
ExtraType = ExtraType.BehindTheScenes,
|
||||||
|
RuleType = ExtraRuleType.DirectoryName,
|
||||||
|
Token = "behind the scenes",
|
||||||
|
MediaType = MediaType.Video,
|
||||||
|
},
|
||||||
|
new ExtraRule
|
||||||
|
{
|
||||||
|
ExtraType = ExtraType.DeletedScene,
|
||||||
|
RuleType = ExtraRuleType.DirectoryName,
|
||||||
|
Token = "deleted scenes",
|
||||||
|
MediaType = MediaType.Video,
|
||||||
|
},
|
||||||
|
new ExtraRule
|
||||||
|
{
|
||||||
|
ExtraType = ExtraType.Interview,
|
||||||
|
RuleType = ExtraRuleType.DirectoryName,
|
||||||
|
Token = "interviews",
|
||||||
|
MediaType = MediaType.Video,
|
||||||
|
},
|
||||||
|
new ExtraRule
|
||||||
|
{
|
||||||
|
ExtraType = ExtraType.Scene,
|
||||||
|
RuleType = ExtraRuleType.DirectoryName,
|
||||||
|
Token = "scenes",
|
||||||
|
MediaType = MediaType.Video,
|
||||||
|
},
|
||||||
|
new ExtraRule
|
||||||
|
{
|
||||||
|
ExtraType = ExtraType.Sample,
|
||||||
|
RuleType = ExtraRuleType.DirectoryName,
|
||||||
|
Token = "samples",
|
||||||
|
MediaType = MediaType.Video,
|
||||||
|
},
|
||||||
|
new ExtraRule
|
||||||
|
{
|
||||||
|
ExtraType = ExtraType.Clip,
|
||||||
|
RuleType = ExtraRuleType.DirectoryName,
|
||||||
|
Token = "shorts",
|
||||||
|
MediaType = MediaType.Video,
|
||||||
|
},
|
||||||
|
new ExtraRule
|
||||||
|
{
|
||||||
|
ExtraType = ExtraType.Clip,
|
||||||
|
RuleType = ExtraRuleType.DirectoryName,
|
||||||
|
Token = "featurettes",
|
||||||
|
MediaType = MediaType.Video,
|
||||||
|
},
|
||||||
|
new ExtraRule
|
||||||
|
{
|
||||||
|
ExtraType = ExtraType.Unknown,
|
||||||
|
RuleType = ExtraRuleType.DirectoryName,
|
||||||
|
Token = "extras",
|
||||||
|
MediaType = MediaType.Video,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
Format3DRules = new[]
|
Format3DRules = new[]
|
||||||
|
@ -80,6 +80,15 @@ namespace Emby.Naming.Video
|
|||||||
result.Rule = rule;
|
result.Rule = rule;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (rule.RuleType == ExtraRuleType.DirectoryName)
|
||||||
|
{
|
||||||
|
var directoryName = Path.GetFileName(Path.GetDirectoryName(path));
|
||||||
|
if (string.Equals(directoryName, rule.Token, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
result.ExtraType = rule.ExtraType;
|
||||||
|
result.Rule = rule;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -5,30 +5,29 @@ using MediaType = Emby.Naming.Common.MediaType;
|
|||||||
|
|
||||||
namespace Emby.Naming.Video
|
namespace Emby.Naming.Video
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A rule used to match a file path with an <see cref="MediaBrowser.Model.Entities.ExtraType"/>.
|
||||||
|
/// </summary>
|
||||||
public class ExtraRule
|
public class ExtraRule
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the token.
|
/// Gets or sets the token to use for matching against the file path.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The token.</value>
|
|
||||||
public string Token { get; set; }
|
public string Token { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the type of the extra.
|
/// Gets or sets the type of the extra to return when matched.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The type of the extra.</value>
|
|
||||||
public ExtraType ExtraType { get; set; }
|
public ExtraType ExtraType { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the type of the rule.
|
/// Gets or sets the type of the rule.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The type of the rule.</value>
|
|
||||||
public ExtraRuleType RuleType { get; set; }
|
public ExtraRuleType RuleType { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the type of the media.
|
/// Gets or sets the type of the media to return when matched.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The type of the media.</value>
|
|
||||||
public MediaType MediaType { get; set; }
|
public MediaType MediaType { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,18 +5,23 @@ namespace Emby.Naming.Video
|
|||||||
public enum ExtraRuleType
|
public enum ExtraRuleType
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The suffix
|
/// Match <see cref="ExtraRule.Token"/> against a suffix in the file name.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Suffix = 0,
|
Suffix = 0,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The filename
|
/// Match <see cref="ExtraRule.Token"/> against the file name, excluding the file extension.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Filename = 1,
|
Filename = 1,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The regex
|
/// Match <see cref="ExtraRule.Token"/> against the file name, including the file extension.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Regex = 2
|
Regex = 2,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Match <see cref="ExtraRule.Token"/> against the name of the directory containing the file.
|
||||||
|
/// </summary>
|
||||||
|
DirectoryName = 3,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1055,31 +1055,20 @@ namespace Emby.Server.Implementations.Dto
|
|||||||
BaseItem[] allExtras = null;
|
BaseItem[] allExtras = null;
|
||||||
|
|
||||||
if (options.ContainsField(ItemFields.SpecialFeatureCount))
|
if (options.ContainsField(ItemFields.SpecialFeatureCount))
|
||||||
{
|
|
||||||
if (allExtras == null)
|
|
||||||
{
|
{
|
||||||
allExtras = item.GetExtras().ToArray();
|
allExtras = item.GetExtras().ToArray();
|
||||||
}
|
|
||||||
|
|
||||||
dto.SpecialFeatureCount = allExtras.Count(i => i.ExtraType.HasValue && BaseItem.DisplayExtraTypes.Contains(i.ExtraType.Value));
|
dto.SpecialFeatureCount = allExtras.Count(i => i.ExtraType.HasValue && BaseItem.DisplayExtraTypes.Contains(i.ExtraType.Value));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.ContainsField(ItemFields.LocalTrailerCount))
|
if (options.ContainsField(ItemFields.LocalTrailerCount))
|
||||||
{
|
{
|
||||||
int trailerCount = 0;
|
allExtras ??= item.GetExtras().ToArray();
|
||||||
if (allExtras == null)
|
dto.LocalTrailerCount = allExtras.Count(i => i.ExtraType == ExtraType.Trailer);
|
||||||
{
|
|
||||||
allExtras = item.GetExtras().ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
trailerCount += allExtras.Count(i => i.ExtraType.HasValue && i.ExtraType.Value == ExtraType.Trailer);
|
|
||||||
|
|
||||||
if (item is IHasTrailers hasTrailers)
|
if (item is IHasTrailers hasTrailers)
|
||||||
{
|
{
|
||||||
trailerCount += hasTrailers.GetTrailerCount();
|
dto.LocalTrailerCount += hasTrailers.GetTrailerCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
dto.LocalTrailerCount = trailerCount;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add EpisodeInfo
|
// Add EpisodeInfo
|
||||||
|
@ -2609,14 +2609,12 @@ namespace Emby.Server.Implementations.Library
|
|||||||
}).OrderBy(i => i.Path);
|
}).OrderBy(i => i.Path);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly string[] ExtrasSubfolderNames = new[] { "extras", "specials", "shorts", "scenes", "featurettes", "behind the scenes", "deleted scenes", "interviews" };
|
|
||||||
|
|
||||||
public IEnumerable<Video> FindExtras(BaseItem owner, List<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService)
|
public IEnumerable<Video> FindExtras(BaseItem owner, List<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService)
|
||||||
{
|
{
|
||||||
var namingOptions = GetNamingOptions();
|
var namingOptions = GetNamingOptions();
|
||||||
|
|
||||||
var files = owner.IsInMixedFolder ? new List<FileSystemMetadata>() : fileSystemChildren.Where(i => i.IsDirectory)
|
var files = owner.IsInMixedFolder ? new List<FileSystemMetadata>() : fileSystemChildren.Where(i => i.IsDirectory)
|
||||||
.Where(i => ExtrasSubfolderNames.Contains(i.Name ?? string.Empty, StringComparer.OrdinalIgnoreCase))
|
.Where(i => BaseItem.AllExtrasTypesFolderNames.Contains(i.Name ?? string.Empty, StringComparer.OrdinalIgnoreCase))
|
||||||
.SelectMany(i => _fileSystem.GetFiles(i.FullName, _videoFileExtensions, false, false))
|
.SelectMany(i => _fileSystem.GetFiles(i.FullName, _videoFileExtensions, false, false))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
|
@ -361,7 +361,8 @@ namespace MediaBrowser.Api.UserLibrary
|
|||||||
|
|
||||||
var dtoOptions = GetDtoOptions(_authContext, request);
|
var dtoOptions = GetDtoOptions(_authContext, request);
|
||||||
|
|
||||||
var dtos = item.GetDisplayExtras()
|
var dtos = item
|
||||||
|
.GetExtras(BaseItem.DisplayExtraTypes)
|
||||||
.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item));
|
.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item));
|
||||||
|
|
||||||
return dtos.ToArray();
|
return dtos.ToArray();
|
||||||
|
@ -1326,8 +1326,9 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Use some hackery to get the extra type based on foldername
|
// Use some hackery to get the extra type based on foldername
|
||||||
Enum.TryParse(extraFolderName.Replace(" ", ""), true, out ExtraType extraType);
|
item.ExtraType = Enum.TryParse(extraFolderName.Replace(" ", string.Empty), true, out ExtraType extraType)
|
||||||
item.ExtraType = extraType;
|
? extraType
|
||||||
|
: Model.Entities.ExtraType.Unknown;
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
|
|
||||||
@ -2877,14 +2878,29 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// <value>The remote trailers.</value>
|
/// <value>The remote trailers.</value>
|
||||||
public IReadOnlyList<MediaUrl> RemoteTrailers { get; set; }
|
public IReadOnlyList<MediaUrl> RemoteTrailers { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get all extras associated with this item, sorted by <see cref="SortName"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>An enumerable containing the items.</returns>
|
||||||
public IEnumerable<BaseItem> GetExtras()
|
public IEnumerable<BaseItem> GetExtras()
|
||||||
{
|
{
|
||||||
return ExtraIds.Select(LibraryManager.GetItemById).Where(i => i != null).OrderBy(i => i.SortName);
|
return ExtraIds
|
||||||
|
.Select(LibraryManager.GetItemById)
|
||||||
|
.Where(i => i != null)
|
||||||
|
.OrderBy(i => i.SortName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get all extras with specific types that are associated with this item.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="extraTypes">The types of extras to retrieve.</param>
|
||||||
|
/// <returns>An enumerable containing the extras.</returns>
|
||||||
public IEnumerable<BaseItem> GetExtras(IReadOnlyCollection<ExtraType> extraTypes)
|
public IEnumerable<BaseItem> GetExtras(IReadOnlyCollection<ExtraType> extraTypes)
|
||||||
{
|
{
|
||||||
return ExtraIds.Select(LibraryManager.GetItemById).Where(i => i?.ExtraType != null && extraTypes.Contains(i.ExtraType.Value));
|
return ExtraIds
|
||||||
|
.Select(LibraryManager.GetItemById)
|
||||||
|
.Where(i => i != null)
|
||||||
|
.Where(i => i.ExtraType.HasValue && extraTypes.Contains(i.ExtraType.Value));
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<BaseItem> GetTrailers()
|
public IEnumerable<BaseItem> GetTrailers()
|
||||||
@ -2895,11 +2911,6 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
return Array.Empty<BaseItem>();
|
return Array.Empty<BaseItem>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<BaseItem> GetDisplayExtras()
|
|
||||||
{
|
|
||||||
return GetExtras(DisplayExtraTypes);
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual bool IsHD => Height >= 720;
|
public virtual bool IsHD => Height >= 720;
|
||||||
|
|
||||||
public bool IsShortcut { get; set; }
|
public bool IsShortcut { get; set; }
|
||||||
@ -2917,8 +2928,19 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
return RunTimeTicks ?? 0;
|
return RunTimeTicks ?? 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Possible types of extra videos
|
/// <summary>
|
||||||
public static readonly IReadOnlyCollection<ExtraType> DisplayExtraTypes = new[] { Model.Entities.ExtraType.BehindTheScenes, Model.Entities.ExtraType.Clip, Model.Entities.ExtraType.DeletedScene, Model.Entities.ExtraType.Interview, Model.Entities.ExtraType.Sample, Model.Entities.ExtraType.Scene };
|
/// Extra types that should be counted and displayed as "Special Features" in the UI.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly IReadOnlyCollection<ExtraType> DisplayExtraTypes = new HashSet<ExtraType>
|
||||||
|
{
|
||||||
|
Model.Entities.ExtraType.Unknown,
|
||||||
|
Model.Entities.ExtraType.BehindTheScenes,
|
||||||
|
Model.Entities.ExtraType.Clip,
|
||||||
|
Model.Entities.ExtraType.DeletedScene,
|
||||||
|
Model.Entities.ExtraType.Interview,
|
||||||
|
Model.Entities.ExtraType.Sample,
|
||||||
|
Model.Entities.ExtraType.Scene
|
||||||
|
};
|
||||||
|
|
||||||
public virtual bool SupportsExternalTransfer => false;
|
public virtual bool SupportsExternalTransfer => false;
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ namespace MediaBrowser.Model.Entities
|
|||||||
{
|
{
|
||||||
public enum ExtraType
|
public enum ExtraType
|
||||||
{
|
{
|
||||||
|
Unknown = 0,
|
||||||
Clip = 1,
|
Clip = 1,
|
||||||
Trailer = 2,
|
Trailer = 2,
|
||||||
BehindTheScenes = 3,
|
BehindTheScenes = 3,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user