Refactor extras parsing

This commit is contained in:
cvium
2021-12-07 15:18:17 +01:00
parent 9cafa2cab4
commit fde84a1e00
28 changed files with 689 additions and 1005 deletions

View File

@@ -40,9 +40,7 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
public abstract class BaseItem : IHasProviderIds, IHasLookupInfo<ItemLookupInfo>, IEquatable<BaseItem>
{
/// <summary>
/// The trailer folder name.
/// </summary>
public const string TrailerFileName = "trailer";
public const string TrailersFolderName = "trailers";
public const string ThemeSongsFolderName = "theme-music";
public const string ThemeSongFileName = "theme";
@@ -99,8 +97,6 @@ namespace MediaBrowser.Controller.Entities
};
private string _sortName;
private Guid[] _themeSongIds;
private Guid[] _themeVideoIds;
private string _forcedSortName;
@@ -121,40 +117,6 @@ namespace MediaBrowser.Controller.Entities
ExtraIds = Array.Empty<Guid>();
}
[JsonIgnore]
public Guid[] ThemeSongIds
{
get
{
return _themeSongIds ??= GetExtras()
.Where(extra => extra.ExtraType == Model.Entities.ExtraType.ThemeSong)
.Select(song => song.Id)
.ToArray();
}
private set
{
_themeSongIds = value;
}
}
[JsonIgnore]
public Guid[] ThemeVideoIds
{
get
{
return _themeVideoIds ??= GetExtras()
.Where(extra => extra.ExtraType == Model.Entities.ExtraType.ThemeVideo)
.Select(song => song.Id)
.ToArray();
}
private set
{
_themeVideoIds = value;
}
}
[JsonIgnore]
public string PreferredMetadataCountryCode { get; set; }
@@ -1379,28 +1341,6 @@ namespace MediaBrowser.Controller.Entities
}).OrderBy(i => i.Path).ToArray();
}
protected virtual BaseItem[] LoadExtras(List<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService)
{
return fileSystemChildren
.Where(child => child.IsDirectory && AllExtrasTypesFolderNames.ContainsKey(child.Name))
.SelectMany(folder => LibraryManager
.ResolvePaths(FileSystem.GetFiles(folder.FullName), directoryService, null, new LibraryOptions())
.OfType<Video>()
.Select(video =>
{
// Try to retrieve it from the db. If we don't find it, use the resolved version
if (LibraryManager.GetItemById(video.Id) is Video dbItem)
{
video = dbItem;
}
video.ExtraType = AllExtrasTypesFolderNames[folder.Name];
return video;
})
.OrderBy(video => video.Path)) // Sort them so that the list can be easily compared for changes
.ToArray();
}
public Task RefreshMetadata(CancellationToken cancellationToken)
{
return RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(FileSystem)), cancellationToken);
@@ -1434,13 +1374,8 @@ namespace MediaBrowser.Controller.Entities
GetFileSystemChildren(options.DirectoryService).ToList() :
new List<FileSystemMetadata>();
var ownedItemsChanged = await RefreshedOwnedItems(options, files, cancellationToken).ConfigureAwait(false);
requiresSave = await RefreshedOwnedItems(options, files, cancellationToken).ConfigureAwait(false);
await LibraryManager.UpdateImagesAsync(this).ConfigureAwait(false); // ensure all image properties in DB are fresh
if (ownedItemsChanged)
{
requiresSave = true;
}
}
catch (Exception ex)
{
@@ -1516,35 +1451,12 @@ 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, List<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken)
{
var themeSongsChanged = false;
var themeVideosChanged = false;
var extrasChanged = false;
var localTrailersChanged = false;
if (IsFileProtocol && SupportsOwnedItems)
if (!IsFileProtocol || !SupportsOwnedItems || IsInMixedFolder || this is ICollectionFolder)
{
if (SupportsThemeMedia)
{
if (!IsInMixedFolder)
{
themeSongsChanged = await RefreshThemeSongs(this, options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
themeVideosChanged = await RefreshThemeVideos(this, options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
extrasChanged = await RefreshExtras(this, options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
}
}
if (this is IHasTrailers hasTrailers)
{
localTrailersChanged = await RefreshLocalTrailers(hasTrailers, options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
}
return false;
}
return themeSongsChanged || themeVideosChanged || extrasChanged || localTrailersChanged;
return await RefreshExtras(this, options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
}
protected virtual FileSystemMetadata[] GetFileSystemChildren(IDirectoryService directoryService)
@@ -1554,98 +1466,24 @@ namespace MediaBrowser.Controller.Entities
return directoryService.GetFileSystemEntries(path);
}
private async Task<bool> RefreshLocalTrailers(IHasTrailers item, MetadataRefreshOptions options, List<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken)
{
var newItems = LibraryManager.FindTrailers(this, fileSystemChildren, options.DirectoryService);
var newItemIds = newItems.Select(i => i.Id);
var itemsChanged = !item.LocalTrailerIds.SequenceEqual(newItemIds);
var ownerId = item.Id;
var tasks = newItems.Select(i =>
{
var subOptions = new MetadataRefreshOptions(options);
if (i.ExtraType != Model.Entities.ExtraType.Trailer ||
i.OwnerId != ownerId ||
!i.ParentId.Equals(Guid.Empty))
{
i.ExtraType = Model.Entities.ExtraType.Trailer;
i.OwnerId = ownerId;
i.ParentId = Guid.Empty;
subOptions.ForceSave = true;
}
return RefreshMetadataForOwnedItem(i, true, subOptions, cancellationToken);
});
await Task.WhenAll(tasks).ConfigureAwait(false);
item.LocalTrailerIds = newItemIds.ToArray();
return itemsChanged;
}
private async Task<bool> RefreshExtras(BaseItem item, MetadataRefreshOptions options, List<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken)
{
var extras = LoadExtras(fileSystemChildren, options.DirectoryService);
var themeVideos = LoadThemeVideos(fileSystemChildren, options.DirectoryService);
var themeSongs = LoadThemeSongs(fileSystemChildren, options.DirectoryService);
var newExtras = new BaseItem[extras.Length + themeVideos.Length + themeSongs.Length];
extras.CopyTo(newExtras, 0);
themeVideos.CopyTo(newExtras, extras.Length);
themeSongs.CopyTo(newExtras, extras.Length + themeVideos.Length);
var newExtraIds = newExtras.Select(i => i.Id).ToArray();
var extras = LibraryManager.FindExtras(item, fileSystemChildren).ToArray();
var newExtraIds = extras.Select(i => i.Id).ToArray();
var extrasChanged = !item.ExtraIds.SequenceEqual(newExtraIds);
if (extrasChanged)
if (!extrasChanged)
{
var ownerId = item.Id;
var tasks = newExtras.Select(i =>
{
var subOptions = new MetadataRefreshOptions(options);
if (i.OwnerId != ownerId || i.ParentId != Guid.Empty)
{
i.OwnerId = ownerId;
i.ParentId = Guid.Empty;
subOptions.ForceSave = true;
}
return RefreshMetadataForOwnedItem(i, true, subOptions, cancellationToken);
});
await Task.WhenAll(tasks).ConfigureAwait(false);
item.ExtraIds = newExtraIds;
return false;
}
return extrasChanged;
}
private async Task<bool> RefreshThemeVideos(BaseItem item, MetadataRefreshOptions options, IEnumerable<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken)
{
var newThemeVideos = LoadThemeVideos(fileSystemChildren, options.DirectoryService);
var newThemeVideoIds = newThemeVideos.Select(i => i.Id).ToArray();
var themeVideosChanged = !item.ThemeVideoIds.SequenceEqual(newThemeVideoIds);
var ownerId = item.Id;
var tasks = newThemeVideos.Select(i =>
var tasks = extras.Select(i =>
{
var subOptions = new MetadataRefreshOptions(options);
if (!i.ExtraType.HasValue ||
i.ExtraType.Value != Model.Entities.ExtraType.ThemeVideo ||
i.OwnerId != ownerId ||
!i.ParentId.Equals(Guid.Empty))
if (i.OwnerId != ownerId || i.ParentId != Guid.Empty)
{
i.ExtraType = Model.Entities.ExtraType.ThemeVideo;
i.OwnerId = ownerId;
i.ParentId = Guid.Empty;
subOptions.ForceSave = true;
@@ -1656,48 +1494,9 @@ namespace MediaBrowser.Controller.Entities
await Task.WhenAll(tasks).ConfigureAwait(false);
// They are expected to be sorted by SortName
item.ThemeVideoIds = newThemeVideos.OrderBy(i => i.SortName).Select(i => i.Id).ToArray();
item.ExtraIds = newExtraIds;
return themeVideosChanged;
}
/// <summary>
/// Refreshes the theme songs.
/// </summary>
private async Task<bool> RefreshThemeSongs(BaseItem item, MetadataRefreshOptions options, List<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken)
{
var newThemeSongs = LoadThemeSongs(fileSystemChildren, options.DirectoryService);
var newThemeSongIds = newThemeSongs.Select(i => i.Id).ToArray();
var themeSongsChanged = !item.ThemeSongIds.SequenceEqual(newThemeSongIds);
var ownerId = item.Id;
var tasks = newThemeSongs.Select(i =>
{
var subOptions = new MetadataRefreshOptions(options);
if (!i.ExtraType.HasValue ||
i.ExtraType.Value != Model.Entities.ExtraType.ThemeSong ||
i.OwnerId != ownerId ||
!i.ParentId.Equals(Guid.Empty))
{
i.ExtraType = Model.Entities.ExtraType.ThemeSong;
i.OwnerId = ownerId;
i.ParentId = Guid.Empty;
subOptions.ForceSave = true;
}
return RefreshMetadataForOwnedItem(i, true, subOptions, cancellationToken);
});
await Task.WhenAll(tasks).ConfigureAwait(false);
// They are expected to be sorted by SortName
item.ThemeSongIds = newThemeSongs.OrderBy(i => i.SortName).Select(i => i.Id).ToArray();
return themeSongsChanged;
return true;
}
public string GetPresentationUniqueKey()
@@ -2891,14 +2690,14 @@ namespace MediaBrowser.Controller.Entities
StringComparison.OrdinalIgnoreCase);
}
public IEnumerable<BaseItem> GetThemeSongs()
public IReadOnlyList<BaseItem> GetThemeSongs()
{
return ThemeSongIds.Select(LibraryManager.GetItemById);
return GetExtras().Where(e => e.ExtraType == Model.Entities.ExtraType.ThemeSong).ToArray();
}
public IEnumerable<BaseItem> GetThemeVideos()
public IReadOnlyList<BaseItem> GetThemeVideos()
{
return ThemeVideoIds.Select(LibraryManager.GetItemById);
return GetExtras().Where(e => e.ExtraType == Model.Entities.ExtraType.ThemeVideo).ToArray();
}
/// <summary>

View File

@@ -2,7 +2,6 @@
#pragma warning disable CS1591
using System;
using System.Collections.Generic;
using MediaBrowser.Model.Entities;
@@ -17,18 +16,10 @@ namespace MediaBrowser.Controller.Entities
IReadOnlyList<MediaUrl> RemoteTrailers { get; set; }
/// <summary>
/// Gets or sets the local trailer ids.
/// Gets the local trailers.
/// </summary>
/// <value>The local trailer ids.</value>
IReadOnlyList<Guid> LocalTrailerIds { get; set; }
/// <summary>
/// Gets or sets the remote trailer ids.
/// </summary>
/// <value>The remote trailer ids.</value>
IReadOnlyList<Guid> RemoteTrailerIds { get; set; }
Guid Id { get; set; }
/// <value>The local trailers.</value>
IReadOnlyList<BaseItem> LocalTrailers { get; }
}
/// <summary>
@@ -42,57 +33,6 @@ namespace MediaBrowser.Controller.Entities
/// <param name="item">Media item.</param>
/// <returns><see cref="IReadOnlyList{Guid}" />.</returns>
public static int GetTrailerCount(this IHasTrailers item)
=> item.LocalTrailerIds.Count + item.RemoteTrailerIds.Count;
/// <summary>
/// Gets the trailer ids.
/// </summary>
/// <param name="item">Media item.</param>
/// <returns><see cref="IReadOnlyList{Guid}" />.</returns>
public static IReadOnlyList<Guid> GetTrailerIds(this IHasTrailers item)
{
var localIds = item.LocalTrailerIds;
var remoteIds = item.RemoteTrailerIds;
var all = new Guid[localIds.Count + remoteIds.Count];
var index = 0;
foreach (var id in localIds)
{
all[index++] = id;
}
foreach (var id in remoteIds)
{
all[index++] = id;
}
return all;
}
/// <summary>
/// Gets the trailers.
/// </summary>
/// <param name="item">Media item.</param>
/// <returns><see cref="IReadOnlyList{BaseItem}" />.</returns>
public static IReadOnlyList<BaseItem> GetTrailers(this IHasTrailers item)
{
var localIds = item.LocalTrailerIds;
var remoteIds = item.RemoteTrailerIds;
var libraryManager = BaseItem.LibraryManager;
var all = new BaseItem[localIds.Count + remoteIds.Count];
var index = 0;
foreach (var id in localIds)
{
all[index++] = libraryManager.GetItemById(id);
}
foreach (var id in remoteIds)
{
all[index++] = libraryManager.GetItemById(id);
}
return all;
}
=> item.LocalTrailers.Count + item.RemoteTrailers.Count;
}
}

View File

@@ -9,7 +9,6 @@ using System.Text.Json.Serialization;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
namespace MediaBrowser.Controller.Entities.Movies
@@ -21,10 +20,6 @@ namespace MediaBrowser.Controller.Entities.Movies
{
public BoxSet()
{
RemoteTrailers = Array.Empty<MediaUrl>();
LocalTrailerIds = Array.Empty<Guid>();
RemoteTrailerIds = Array.Empty<Guid>();
DisplayOrder = ItemSortBy.PremiereDate;
}
@@ -38,10 +33,9 @@ namespace MediaBrowser.Controller.Entities.Movies
public override bool SupportsPeople => true;
/// <inheritdoc />
public IReadOnlyList<Guid> LocalTrailerIds { get; set; }
/// <inheritdoc />
public IReadOnlyList<Guid> RemoteTrailerIds { get; set; }
public IReadOnlyList<BaseItem> LocalTrailers => GetExtras()
.Where(extra => extra.ExtraType == Model.Entities.ExtraType.Trailer)
.ToArray();
/// <summary>
/// Gets or sets the display order.

View File

@@ -7,12 +7,9 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Providers;
namespace MediaBrowser.Controller.Entities.Movies
@@ -22,22 +19,29 @@ namespace MediaBrowser.Controller.Entities.Movies
/// </summary>
public class Movie : Video, IHasSpecialFeatures, IHasTrailers, IHasLookupInfo<MovieInfo>, ISupportsBoxSetGrouping
{
public Movie()
private IReadOnlyList<Guid> _specialFeatureIds;
/// <inheritdoc />
public IReadOnlyList<Guid> SpecialFeatureIds
{
SpecialFeatureIds = Array.Empty<Guid>();
RemoteTrailers = Array.Empty<MediaUrl>();
LocalTrailerIds = Array.Empty<Guid>();
RemoteTrailerIds = Array.Empty<Guid>();
get
{
return _specialFeatureIds ??= GetExtras()
.Where(extra => extra.ExtraType != Model.Entities.ExtraType.Trailer)
.Select(song => song.Id)
.ToArray();
}
set
{
_specialFeatureIds = value;
}
}
/// <inheritdoc />
public IReadOnlyList<Guid> SpecialFeatureIds { get; set; }
/// <inheritdoc />
public IReadOnlyList<Guid> LocalTrailerIds { get; set; }
/// <inheritdoc />
public IReadOnlyList<Guid> RemoteTrailerIds { get; set; }
public IReadOnlyList<BaseItem> LocalTrailers => GetExtras()
.Where(extra => extra.ExtraType == Model.Entities.ExtraType.Trailer)
.ToArray();
/// <summary>
/// Gets or sets the name of the TMDB collection.
@@ -66,54 +70,6 @@ namespace MediaBrowser.Controller.Entities.Movies
return 2.0 / 3;
}
protected override async Task<bool> RefreshedOwnedItems(MetadataRefreshOptions options, List<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken)
{
var hasChanges = await base.RefreshedOwnedItems(options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
// Must have a parent to have special features
// In other words, it must be part of the Parent/Child tree
if (IsFileProtocol && SupportsOwnedItems && !IsInMixedFolder)
{
var specialFeaturesChanged = await RefreshSpecialFeatures(options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
if (specialFeaturesChanged)
{
hasChanges = true;
}
}
return hasChanges;
}
private async Task<bool> RefreshSpecialFeatures(MetadataRefreshOptions options, List<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken)
{
var newItems = LibraryManager.FindExtras(this, fileSystemChildren, options.DirectoryService).ToList();
var newItemIds = newItems.Select(i => i.Id).ToArray();
var itemsChanged = !SpecialFeatureIds.SequenceEqual(newItemIds);
var ownerId = Id;
var tasks = newItems.Select(i =>
{
var subOptions = new MetadataRefreshOptions(options);
if (i.OwnerId != ownerId)
{
i.OwnerId = ownerId;
subOptions.ForceSave = true;
}
return RefreshMetadataForOwnedItem(i, false, subOptions, cancellationToken);
});
await Task.WhenAll(tasks).ConfigureAwait(false);
SpecialFeatureIds = newItemIds;
return itemsChanged;
}
/// <inheritdoc />
public override UnratedItem GetBlockUnratedType()
{

View File

@@ -20,18 +20,10 @@ namespace MediaBrowser.Controller.Entities.TV
/// </summary>
public class Episode : Video, IHasTrailers, IHasLookupInfo<EpisodeInfo>, IHasSeries
{
public Episode()
{
RemoteTrailers = Array.Empty<MediaUrl>();
LocalTrailerIds = Array.Empty<Guid>();
RemoteTrailerIds = Array.Empty<Guid>();
}
/// <inheritdoc />
public IReadOnlyList<Guid> LocalTrailerIds { get; set; }
/// <inheritdoc />
public IReadOnlyList<Guid> RemoteTrailerIds { get; set; }
public IReadOnlyList<BaseItem> LocalTrailers => GetExtras()
.Where(extra => extra.ExtraType == Model.Entities.ExtraType.Trailer)
.ToArray();
/// <summary>
/// Gets or sets the season in which it aired.

View File

@@ -27,9 +27,6 @@ namespace MediaBrowser.Controller.Entities.TV
{
public Series()
{
RemoteTrailers = Array.Empty<MediaUrl>();
LocalTrailerIds = Array.Empty<Guid>();
RemoteTrailerIds = Array.Empty<Guid>();
AirDays = Array.Empty<DayOfWeek>();
}
@@ -53,10 +50,9 @@ namespace MediaBrowser.Controller.Entities.TV
public override bool SupportsPeople => true;
/// <inheritdoc />
public IReadOnlyList<Guid> LocalTrailerIds { get; set; }
/// <inheritdoc />
public IReadOnlyList<Guid> RemoteTrailerIds { get; set; }
public IReadOnlyList<BaseItem> LocalTrailers => GetExtras()
.Where(extra => extra.ExtraType == Model.Entities.ExtraType.Trailer)
.ToArray();
/// <summary>
/// Gets or sets the display order.

View File

@@ -745,10 +745,9 @@ namespace MediaBrowser.Controller.Entities
var val = query.HasTrailer.Value;
var trailerCount = 0;
var hasTrailers = item as IHasTrailers;
if (hasTrailers != null)
if (item is IHasTrailers hasTrailers)
{
trailerCount = hasTrailers.GetTrailerIds().Count;
trailerCount = hasTrailers.GetTrailerCount();
}
var ok = val ? trailerCount > 0 : trailerCount == 0;
@@ -763,7 +762,7 @@ namespace MediaBrowser.Controller.Entities
{
var filterValue = query.HasThemeSong.Value;
var themeCount = item.ThemeSongIds.Length;
var themeCount = item.GetThemeSongs().Count;
var ok = filterValue ? themeCount > 0 : themeCount == 0;
if (!ok)
@@ -776,7 +775,7 @@ namespace MediaBrowser.Controller.Entities
{
var filterValue = query.HasThemeVideo.Value;
var themeCount = item.ThemeVideoIds.Length;
var themeCount = item.GetThemeVideos().Count;
var ok = filterValue ? themeCount > 0 : themeCount == 0;
if (!ok)

View File

@@ -6,7 +6,6 @@ using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Emby.Naming.Common;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Dto;
@@ -426,29 +425,15 @@ namespace MediaBrowser.Controller.Library
/// <returns>Guid.</returns>
Guid GetNewItemId(string key, Type type);
/// <summary>
/// Finds the trailers.
/// </summary>
/// <param name="owner">The owner.</param>
/// <param name="fileSystemChildren">The file system children.</param>
/// <param name="directoryService">The directory service.</param>
/// <returns>IEnumerable&lt;Trailer&gt;.</returns>
IEnumerable<Video> FindTrailers(
BaseItem owner,
List<FileSystemMetadata> fileSystemChildren,
IDirectoryService directoryService);
/// <summary>
/// Finds the extras.
/// </summary>
/// <param name="owner">The owner.</param>
/// <param name="fileSystemChildren">The file system children.</param>
/// <param name="directoryService">The directory service.</param>
/// <returns>IEnumerable&lt;Video&gt;.</returns>
IEnumerable<Video> FindExtras(
BaseItem owner,
List<FileSystemMetadata> fileSystemChildren,
IDirectoryService directoryService);
List<FileSystemMetadata> fileSystemChildren);
/// <summary>
/// Gets the collection folders.