mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-01-24 03:58:06 +00:00
Merge branch 'master' into keyframe_extraction_v1
# Conflicts: # Jellyfin.Api/Controllers/DynamicHlsController.cs # MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs # MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
This commit is contained in:
@@ -55,12 +55,7 @@ namespace MediaBrowser.Controller.BaseItemManager
|
||||
return typeOptions.MetadataFetchers.Contains(name.AsSpan(), StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
if (!libraryOptions.EnableInternetProviders)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var itemConfig = _serverConfigurationManager.Configuration.MetadataOptions.FirstOrDefault(i => string.Equals(i.ItemType, GetType().Name, StringComparison.OrdinalIgnoreCase));
|
||||
var itemConfig = _serverConfigurationManager.Configuration.MetadataOptions.FirstOrDefault(i => string.Equals(i.ItemType, baseItem.GetType().Name, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
return itemConfig == null || !itemConfig.DisabledMetadataFetchers.Contains(name.AsSpan(), StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
@@ -86,12 +81,7 @@ namespace MediaBrowser.Controller.BaseItemManager
|
||||
return typeOptions.ImageFetchers.Contains(name.AsSpan(), StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
if (!libraryOptions.EnableInternetProviders)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var itemConfig = _serverConfigurationManager.Configuration.MetadataOptions.FirstOrDefault(i => string.Equals(i.ItemType, GetType().Name, StringComparison.OrdinalIgnoreCase));
|
||||
var itemConfig = _serverConfigurationManager.Configuration.MetadataOptions.FirstOrDefault(i => string.Equals(i.ItemType, baseItem.GetType().Name, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
return itemConfig == null || !itemConfig.DisabledImageFetchers.Contains(name.AsSpan(), StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
@@ -8,4 +8,4 @@ namespace MediaBrowser.Controller.Channels
|
||||
{
|
||||
public string UserId { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,4 +6,4 @@ namespace MediaBrowser.Controller.Channels
|
||||
{
|
||||
string[] Attributes { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,4 +12,4 @@ namespace MediaBrowser.Controller.Channels
|
||||
|
||||
Task DeleteItem(string id, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,4 +18,4 @@ namespace MediaBrowser.Controller.Channels
|
||||
/// <returns>The latest media.</returns>
|
||||
Task<IEnumerable<ChannelItemInfo>> GetLatestMedia(ChannelLatestMediaSearch request, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
31
MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs
Normal file
31
MediaBrowser.Controller/ClientEvent/ClientEventLogger.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Controller.ClientEvent
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public class ClientEventLogger : IClientEventLogger
|
||||
{
|
||||
private readonly IServerApplicationPaths _applicationPaths;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ClientEventLogger"/> class.
|
||||
/// </summary>
|
||||
/// <param name="applicationPaths">Instance of the <see cref="IServerApplicationPaths"/> interface.</param>
|
||||
public ClientEventLogger(IServerApplicationPaths applicationPaths)
|
||||
{
|
||||
_applicationPaths = applicationPaths;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<string> WriteDocumentAsync(string clientName, string clientVersion, Stream fileContents)
|
||||
{
|
||||
var fileName = $"upload_{clientName}_{clientVersion}_{DateTime.UtcNow:yyyyMMddHHmmss}_{Guid.NewGuid():N}.log";
|
||||
var logFilePath = Path.Combine(_applicationPaths.LogDirectoryPath, fileName);
|
||||
await using var fileStream = new FileStream(logFilePath, FileMode.CreateNew, FileAccess.Write, FileShare.None);
|
||||
await fileContents.CopyToAsync(fileStream).ConfigureAwait(false);
|
||||
return fileName;
|
||||
}
|
||||
}
|
||||
}
|
||||
23
MediaBrowser.Controller/ClientEvent/IClientEventLogger.cs
Normal file
23
MediaBrowser.Controller/ClientEvent/IClientEventLogger.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Controller.ClientEvent
|
||||
{
|
||||
/// <summary>
|
||||
/// The client event logger.
|
||||
/// </summary>
|
||||
public interface IClientEventLogger
|
||||
{
|
||||
/// <summary>
|
||||
/// Writes a file to the log directory.
|
||||
/// </summary>
|
||||
/// <param name="clientName">The client name writing the document.</param>
|
||||
/// <param name="clientVersion">The client version writing the document.</param>
|
||||
/// <param name="fileContents">The file contents to write.</param>
|
||||
/// <returns>The created file name.</returns>
|
||||
Task<string> WriteDocumentAsync(
|
||||
string clientName,
|
||||
string clientVersion,
|
||||
Stream fileContents);
|
||||
}
|
||||
}
|
||||
@@ -21,4 +21,4 @@ namespace MediaBrowser.Controller.Collections
|
||||
/// <value>The options.</value>
|
||||
public CollectionCreationOptions Options { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,8 +37,9 @@ namespace MediaBrowser.Controller.Dlna
|
||||
/// <summary>
|
||||
/// Updates the profile.
|
||||
/// </summary>
|
||||
/// <param name="profileId">The profile id.</param>
|
||||
/// <param name="profile">The profile.</param>
|
||||
void UpdateProfile(DeviceProfile profile);
|
||||
void UpdateProfile(string profileId, DeviceProfile profile);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the profile.
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace MediaBrowser.Controller.Drawing
|
||||
/// </summary>
|
||||
/// <param name="options">The options.</param>
|
||||
/// <returns>Task.</returns>
|
||||
Task<(string path, string? mimeType, DateTime dateModified)> ProcessImage(ImageProcessingOptions options);
|
||||
Task<(string Path, string? MimeType, DateTime DateModified)> ProcessImage(ImageProcessingOptions options);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the supported image output formats.
|
||||
|
||||
@@ -8,10 +8,8 @@ using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text.Json.Serialization;
|
||||
using Jellyfin.Data.Enums;
|
||||
using MediaBrowser.Controller.Persistence;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.Entities;
|
||||
|
||||
namespace MediaBrowser.Controller.Entities.Audio
|
||||
{
|
||||
@@ -126,15 +124,6 @@ namespace MediaBrowser.Controller.Entities.Audio
|
||||
return base.GetBlockUnratedType();
|
||||
}
|
||||
|
||||
public List<MediaStream> GetMediaStreams(MediaStreamType type)
|
||||
{
|
||||
return MediaSourceManager.GetMediaStreams(new MediaStreamQuery
|
||||
{
|
||||
ItemId = Id,
|
||||
Type = type
|
||||
});
|
||||
}
|
||||
|
||||
public SongInfo GetLookupInfo()
|
||||
{
|
||||
var info = GetItemLookupInfo<SongInfo>();
|
||||
@@ -146,11 +135,7 @@ namespace MediaBrowser.Controller.Entities.Audio
|
||||
return info;
|
||||
}
|
||||
|
||||
protected override List<Tuple<BaseItem, MediaSourceType>> GetAllItemsForMediaSources()
|
||||
{
|
||||
var list = new List<Tuple<BaseItem, MediaSourceType>>();
|
||||
list.Add(new Tuple<BaseItem, MediaSourceType>(this, MediaSourceType.Default));
|
||||
return list;
|
||||
}
|
||||
protected override IEnumerable<(BaseItem Item, MediaSourceType MediaSourceType)> GetAllItemsForMediaSources()
|
||||
=> new[] { ((BaseItem)this, MediaSourceType.Default) };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ namespace MediaBrowser.Controller.Entities.Audio
|
||||
{
|
||||
if (query.IncludeItemTypes.Length == 0)
|
||||
{
|
||||
query.IncludeItemTypes = new[] { nameof(Audio), nameof(MusicVideo), nameof(MusicAlbum) };
|
||||
query.IncludeItemTypes = new[] { BaseItemKind.Audio, BaseItemKind.MusicVideo, BaseItemKind.MusicAlbum };
|
||||
query.ArtistIds = new[] { Id };
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
using Diacritics.Extensions;
|
||||
using Jellyfin.Data.Enums;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace MediaBrowser.Controller.Entities.Audio
|
||||
@@ -66,7 +67,7 @@ namespace MediaBrowser.Controller.Entities.Audio
|
||||
public IList<BaseItem> GetTaggedItems(InternalItemsQuery query)
|
||||
{
|
||||
query.GenreIds = new[] { Id };
|
||||
query.IncludeItemTypes = new[] { nameof(MusicVideo), nameof(Audio), nameof(MusicAlbum), nameof(MusicArtist) };
|
||||
query.IncludeItemTypes = new[] { BaseItemKind.MusicVideo, BaseItemKind.Audio, BaseItemKind.MusicAlbum, BaseItemKind.MusicArtist };
|
||||
|
||||
return LibraryManager.GetItemList(query);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.Json.Serialization;
|
||||
@@ -23,7 +22,6 @@ using MediaBrowser.Controller.Entities.Audio;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Persistence;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Model.Configuration;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Globalization;
|
||||
@@ -41,13 +39,9 @@ namespace MediaBrowser.Controller.Entities
|
||||
/// </summary>
|
||||
public abstract class BaseItem : IHasProviderIds, IHasLookupInfo<ItemLookupInfo>, IEquatable<BaseItem>
|
||||
{
|
||||
/// <summary>
|
||||
/// The trailer folder name.
|
||||
/// </summary>
|
||||
public const string TrailersFolderName = "trailers";
|
||||
public const string ThemeSongsFolderName = "theme-music";
|
||||
private BaseItemKind? _baseItemKind;
|
||||
|
||||
public const string ThemeSongFileName = "theme";
|
||||
public const string ThemeVideosFolderName = "backdrops";
|
||||
|
||||
/// <summary>
|
||||
/// The supported image extensions.
|
||||
@@ -84,26 +78,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
Model.Entities.ExtraType.Scene
|
||||
};
|
||||
|
||||
public static readonly char[] SlugReplaceChars = { '?', '/', '&' };
|
||||
|
||||
/// <summary>
|
||||
/// The supported extra folder names and types. See <see cref="Emby.Naming.Common.NamingOptions" />.
|
||||
/// </summary>
|
||||
public static readonly Dictionary<string, ExtraType> AllExtrasTypesFolderNames = new Dictionary<string, ExtraType>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
["extras"] = MediaBrowser.Model.Entities.ExtraType.Unknown,
|
||||
["behind the scenes"] = MediaBrowser.Model.Entities.ExtraType.BehindTheScenes,
|
||||
["deleted scenes"] = MediaBrowser.Model.Entities.ExtraType.DeletedScene,
|
||||
["interviews"] = MediaBrowser.Model.Entities.ExtraType.Interview,
|
||||
["scenes"] = MediaBrowser.Model.Entities.ExtraType.Scene,
|
||||
["samples"] = MediaBrowser.Model.Entities.ExtraType.Sample,
|
||||
["shorts"] = MediaBrowser.Model.Entities.ExtraType.Clip,
|
||||
["featurettes"] = MediaBrowser.Model.Entities.ExtraType.Clip
|
||||
};
|
||||
|
||||
private string _sortName;
|
||||
private Guid[] _themeSongIds;
|
||||
private Guid[] _themeVideoIds;
|
||||
|
||||
private string _forcedSortName;
|
||||
|
||||
@@ -124,40 +99,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; }
|
||||
|
||||
@@ -335,13 +276,6 @@ namespace MediaBrowser.Controller.Entities
|
||||
[JsonIgnore]
|
||||
public string ExternalSeriesId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the etag.
|
||||
/// </summary>
|
||||
/// <value>The etag.</value>
|
||||
[JsonIgnore]
|
||||
public string ExternalEtag { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public virtual bool IsHidden => false;
|
||||
|
||||
@@ -354,11 +288,6 @@ namespace MediaBrowser.Controller.Entities
|
||||
{
|
||||
get
|
||||
{
|
||||
// if (IsOffline)
|
||||
// {
|
||||
// return LocationType.Offline;
|
||||
// }
|
||||
|
||||
var path = Path;
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
@@ -391,7 +320,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public bool IsFileProtocol => IsPathProtocol(MediaProtocol.File);
|
||||
public bool IsFileProtocol => PathProtocol == MediaProtocol.File;
|
||||
|
||||
[JsonIgnore]
|
||||
public bool HasPathProtocol => PathProtocol.HasValue;
|
||||
@@ -583,14 +512,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public virtual Guid DisplayParentId
|
||||
{
|
||||
get
|
||||
{
|
||||
var parentId = ParentId;
|
||||
return parentId;
|
||||
}
|
||||
}
|
||||
public virtual Guid DisplayParentId => ParentId;
|
||||
|
||||
[JsonIgnore]
|
||||
public BaseItem DisplayParent
|
||||
@@ -853,13 +775,6 @@ namespace MediaBrowser.Controller.Entities
|
||||
return Id.ToString("N", CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
public bool IsPathProtocol(MediaProtocol protocol)
|
||||
{
|
||||
var current = PathProtocol;
|
||||
|
||||
return current.HasValue && current.Value == protocol;
|
||||
}
|
||||
|
||||
private List<Tuple<StringBuilder, bool>> GetSortChunks(string s1)
|
||||
{
|
||||
var list = new List<Tuple<StringBuilder, bool>>();
|
||||
@@ -987,7 +902,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
|
||||
ReadOnlySpan<char> idString = Id.ToString("N", CultureInfo.InvariantCulture);
|
||||
|
||||
return System.IO.Path.Join(basePath, "library", idString.Slice(0, 2), idString);
|
||||
return System.IO.Path.Join(basePath, "library", idString[..2], idString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1154,7 +1069,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
}
|
||||
|
||||
var list = GetAllItemsForMediaSources();
|
||||
var result = list.Select(i => GetVersionInfo(enablePathSubstitution, i.Item1, i.Item2)).ToList();
|
||||
var result = list.Select(i => GetVersionInfo(enablePathSubstitution, i.Item, i.MediaSourceType)).ToList();
|
||||
|
||||
if (IsActiveRecording())
|
||||
{
|
||||
@@ -1182,9 +1097,9 @@ namespace MediaBrowser.Controller.Entities
|
||||
.ToList();
|
||||
}
|
||||
|
||||
protected virtual List<Tuple<BaseItem, MediaSourceType>> GetAllItemsForMediaSources()
|
||||
protected virtual IEnumerable<(BaseItem Item, MediaSourceType MediaSourceType)> GetAllItemsForMediaSources()
|
||||
{
|
||||
return new List<Tuple<BaseItem, MediaSourceType>>();
|
||||
return Enumerable.Empty<(BaseItem, MediaSourceType)>();
|
||||
}
|
||||
|
||||
private MediaSourceInfo GetVersionInfo(bool enablePathSubstitution, BaseItem item, MediaSourceType type)
|
||||
@@ -1302,8 +1217,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
terms.Add(item.Name);
|
||||
}
|
||||
|
||||
var video = item as Video;
|
||||
if (video != null)
|
||||
if (item is Video video)
|
||||
{
|
||||
if (video.Video3DFormat.HasValue)
|
||||
{
|
||||
@@ -1338,99 +1252,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
}
|
||||
}
|
||||
|
||||
return string.Join('/', terms.ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the theme songs.
|
||||
/// </summary>
|
||||
/// <returns>List{Audio.Audio}.</returns>
|
||||
private static Audio.Audio[] LoadThemeSongs(List<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService)
|
||||
{
|
||||
var files = fileSystemChildren.Where(i => i.IsDirectory)
|
||||
.Where(i => string.Equals(i.Name, ThemeSongsFolderName, StringComparison.OrdinalIgnoreCase))
|
||||
.SelectMany(i => FileSystem.GetFiles(i.FullName))
|
||||
.ToList();
|
||||
|
||||
// Support plex/xbmc convention
|
||||
files.AddRange(fileSystemChildren
|
||||
.Where(i => !i.IsDirectory && System.IO.Path.GetFileNameWithoutExtension(i.FullName.AsSpan()).Equals(ThemeSongFileName, StringComparison.OrdinalIgnoreCase)));
|
||||
|
||||
return LibraryManager.ResolvePaths(files, directoryService, null, new LibraryOptions())
|
||||
.OfType<Audio.Audio>()
|
||||
.Select(audio =>
|
||||
{
|
||||
// Try to retrieve it from the db. If we don't find it, use the resolved version
|
||||
var dbItem = LibraryManager.GetItemById(audio.Id) as Audio.Audio;
|
||||
|
||||
if (dbItem != null)
|
||||
{
|
||||
audio = dbItem;
|
||||
}
|
||||
else
|
||||
{
|
||||
// item is new
|
||||
audio.ExtraType = MediaBrowser.Model.Entities.ExtraType.ThemeSong;
|
||||
}
|
||||
|
||||
return audio;
|
||||
|
||||
// Sort them so that the list can be easily compared for changes
|
||||
}).OrderBy(i => i.Path).ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the video backdrops.
|
||||
/// </summary>
|
||||
/// <returns>List{Video}.</returns>
|
||||
private static Video[] LoadThemeVideos(IEnumerable<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService)
|
||||
{
|
||||
var files = fileSystemChildren.Where(i => i.IsDirectory)
|
||||
.Where(i => string.Equals(i.Name, ThemeVideosFolderName, StringComparison.OrdinalIgnoreCase))
|
||||
.SelectMany(i => FileSystem.GetFiles(i.FullName));
|
||||
|
||||
return LibraryManager.ResolvePaths(files, directoryService, null, new LibraryOptions())
|
||||
.OfType<Video>()
|
||||
.Select(item =>
|
||||
{
|
||||
// Try to retrieve it from the db. If we don't find it, use the resolved version
|
||||
|
||||
if (LibraryManager.GetItemById(item.Id) is Video dbItem)
|
||||
{
|
||||
item = dbItem;
|
||||
}
|
||||
else
|
||||
{
|
||||
// item is new
|
||||
item.ExtraType = Model.Entities.ExtraType.ThemeVideo;
|
||||
}
|
||||
|
||||
return item;
|
||||
|
||||
// Sort them so that the list can be easily compared for changes
|
||||
}).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();
|
||||
return string.Join('/', terms);
|
||||
}
|
||||
|
||||
public Task RefreshMetadata(CancellationToken cancellationToken)
|
||||
@@ -1462,21 +1284,16 @@ namespace MediaBrowser.Controller.Entities
|
||||
{
|
||||
try
|
||||
{
|
||||
var files = IsFileProtocol ?
|
||||
GetFileSystemChildren(options.DirectoryService).ToList() :
|
||||
new List<FileSystemMetadata>();
|
||||
|
||||
var ownedItemsChanged = await RefreshedOwnedItems(options, files, cancellationToken).ConfigureAwait(false);
|
||||
await LibraryManager.UpdateImagesAsync(this).ConfigureAwait(false); // ensure all image properties in DB are fresh
|
||||
|
||||
if (ownedItemsChanged)
|
||||
if (IsFileProtocol)
|
||||
{
|
||||
requiresSave = true;
|
||||
requiresSave = await RefreshedOwnedItems(options, GetFileSystemChildren(options.DirectoryService).ToList(), cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await LibraryManager.UpdateImagesAsync(this).ConfigureAwait(false); // ensure all image properties in DB are fresh
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex, "Error refreshing owned items for {path}", Path ?? Name);
|
||||
Logger.LogError(ex, "Error refreshing owned items for {Path}", Path ?? Name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1548,36 +1365,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 or UserRootFolder or AggregateFolder || this.GetType() == typeof(Folder))
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
var hasTrailers = this as IHasTrailers;
|
||||
if (hasTrailers != null)
|
||||
{
|
||||
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)
|
||||
@@ -1587,98 +1380,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, options.DirectoryService).ToArray();
|
||||
var newExtraIds = extras.Select(i => i.Id).ToArray();
|
||||
var extrasChanged = !item.ExtraIds.SequenceEqual(newExtraIds);
|
||||
|
||||
if (extrasChanged)
|
||||
if (!extrasChanged && !options.ReplaceAllMetadata && options.MetadataRefreshMode != MetadataRefreshMode.FullRefresh)
|
||||
{
|
||||
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;
|
||||
@@ -1689,48 +1408,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()
|
||||
@@ -1963,7 +1643,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
|
||||
private bool IsVisibleViaTags(User user)
|
||||
{
|
||||
if (user.GetPreference(PreferenceKind.BlockedTags).Any(i => Tags.Contains(i, StringComparer.OrdinalIgnoreCase)))
|
||||
if (user.GetPreference(PreferenceKind.BlockedTags).Any(i => Tags.Contains(i, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -2042,7 +1722,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
|
||||
public BaseItemKind GetBaseItemKind()
|
||||
{
|
||||
return Enum.Parse<BaseItemKind>(GetClientTypeName());
|
||||
return _baseItemKind ??= Enum.Parse<BaseItemKind>(GetClientTypeName());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -2132,7 +1812,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
|
||||
var current = Studios;
|
||||
|
||||
if (!current.Contains(name, StringComparer.OrdinalIgnoreCase))
|
||||
if (!current.Contains(name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
int curLen = current.Length;
|
||||
if (curLen == 0)
|
||||
@@ -2167,7 +1847,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
}
|
||||
|
||||
var genres = Genres;
|
||||
if (!genres.Contains(name, StringComparer.OrdinalIgnoreCase))
|
||||
if (!genres.Contains(name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var list = genres.ToList();
|
||||
list.Add(name);
|
||||
@@ -2268,7 +1948,11 @@ namespace MediaBrowser.Controller.Entities
|
||||
|
||||
var existingImage = GetImageInfo(image.Type, index);
|
||||
|
||||
if (existingImage != null)
|
||||
if (existingImage == null)
|
||||
{
|
||||
AddImage(image);
|
||||
}
|
||||
else
|
||||
{
|
||||
existingImage.Path = image.Path;
|
||||
existingImage.DateModified = image.DateModified;
|
||||
@@ -2276,15 +1960,6 @@ namespace MediaBrowser.Controller.Entities
|
||||
existingImage.Height = image.Height;
|
||||
existingImage.BlurHash = image.BlurHash;
|
||||
}
|
||||
else
|
||||
{
|
||||
var current = ImageInfos;
|
||||
var currentCount = current.Length;
|
||||
var newArr = new ItemImageInfo[currentCount + 1];
|
||||
current.CopyTo(newArr, 0);
|
||||
newArr[currentCount] = image;
|
||||
ImageInfos = newArr;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetImagePath(ImageType type, int index, FileSystemMetadata file)
|
||||
@@ -2298,7 +1973,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
|
||||
if (image == null)
|
||||
{
|
||||
ImageInfos = ImageInfos.Concat(new[] { GetImageInfo(file, type) }).ToArray();
|
||||
AddImage(GetImageInfo(file, type));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2342,14 +2017,24 @@ namespace MediaBrowser.Controller.Entities
|
||||
|
||||
public void RemoveImage(ItemImageInfo image)
|
||||
{
|
||||
RemoveImages(new List<ItemImageInfo> { image });
|
||||
RemoveImages(new[] { image });
|
||||
}
|
||||
|
||||
public void RemoveImages(List<ItemImageInfo> deletedImages)
|
||||
public void RemoveImages(IEnumerable<ItemImageInfo> deletedImages)
|
||||
{
|
||||
ImageInfos = ImageInfos.Except(deletedImages).ToArray();
|
||||
}
|
||||
|
||||
public void AddImage(ItemImageInfo image)
|
||||
{
|
||||
var current = ImageInfos;
|
||||
var currentCount = current.Length;
|
||||
var newArr = new ItemImageInfo[currentCount + 1];
|
||||
current.CopyTo(newArr, 0);
|
||||
newArr[currentCount] = image;
|
||||
ImageInfos = newArr;
|
||||
}
|
||||
|
||||
public virtual Task UpdateToRepositoryAsync(ItemUpdateType updateReason, CancellationToken cancellationToken)
|
||||
=> LibraryManager.UpdateItemAsync(this, GetParent(), updateReason, cancellationToken);
|
||||
|
||||
@@ -2368,12 +2053,12 @@ namespace MediaBrowser.Controller.Entities
|
||||
.ToList();
|
||||
|
||||
var deletedImages = ImageInfos
|
||||
.Where(image => image.IsLocalFile && !allFiles.Contains(image.Path, StringComparer.OrdinalIgnoreCase))
|
||||
.Where(image => image.IsLocalFile && !allFiles.Contains(image.Path, StringComparison.OrdinalIgnoreCase))
|
||||
.ToList();
|
||||
|
||||
if (deletedImages.Count > 0)
|
||||
{
|
||||
ImageInfos = ImageInfos.Except(deletedImages).ToArray();
|
||||
RemoveImages(deletedImages);
|
||||
}
|
||||
|
||||
return deletedImages.Count > 0;
|
||||
@@ -2495,11 +2180,11 @@ namespace MediaBrowser.Controller.Entities
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the images.
|
||||
/// Adds the images, updating metadata if they already are part of this item.
|
||||
/// </summary>
|
||||
/// <param name="imageType">Type of the image.</param>
|
||||
/// <param name="images">The images.</param>
|
||||
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
|
||||
/// <returns><c>true</c> if images were added or updated, <c>false</c> otherwise.</returns>
|
||||
/// <exception cref="ArgumentException">Cannot call AddImages with chapter images.</exception>
|
||||
public bool AddImages(ImageType imageType, List<FileSystemMetadata> images)
|
||||
{
|
||||
@@ -2512,7 +2197,6 @@ namespace MediaBrowser.Controller.Entities
|
||||
.ToList();
|
||||
|
||||
var newImageList = new List<FileSystemMetadata>();
|
||||
var imageAdded = false;
|
||||
var imageUpdated = false;
|
||||
|
||||
foreach (var newImage in images)
|
||||
@@ -2528,7 +2212,6 @@ namespace MediaBrowser.Controller.Entities
|
||||
if (existing == null)
|
||||
{
|
||||
newImageList.Add(newImage);
|
||||
imageAdded = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2549,19 +2232,6 @@ namespace MediaBrowser.Controller.Entities
|
||||
}
|
||||
}
|
||||
|
||||
if (imageAdded || images.Count != existingImages.Count)
|
||||
{
|
||||
var newImagePaths = images.Select(i => i.FullName).ToList();
|
||||
|
||||
var deleted = existingImages
|
||||
.FindAll(i => i.IsLocalFile && !newImagePaths.Contains(i.Path.AsSpan(), StringComparison.OrdinalIgnoreCase) && !File.Exists(i.Path));
|
||||
|
||||
if (deleted.Count > 0)
|
||||
{
|
||||
ImageInfos = ImageInfos.Except(deleted).ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
if (newImageList.Count > 0)
|
||||
{
|
||||
ImageInfos = ImageInfos.Concat(newImageList.Select(i => GetImageInfo(i, imageType))).ToArray();
|
||||
@@ -2612,7 +2282,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
|
||||
public bool AllowsMultipleImages(ImageType type)
|
||||
{
|
||||
return type == ImageType.Backdrop || type == ImageType.Screenshot || type == ImageType.Chapter;
|
||||
return type == ImageType.Backdrop || type == ImageType.Chapter;
|
||||
}
|
||||
|
||||
public Task SwapImagesAsync(ImageType type, int index1, int index2)
|
||||
@@ -2730,7 +2400,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
|
||||
protected static string GetMappedPath(BaseItem item, string path, MediaProtocol? protocol)
|
||||
{
|
||||
if (protocol.HasValue && protocol.Value == MediaProtocol.File)
|
||||
if (protocol == MediaProtocol.File)
|
||||
{
|
||||
return LibraryManager.GetPathAfterNetworkSubstitution(path, item);
|
||||
}
|
||||
@@ -2758,8 +2428,10 @@ namespace MediaBrowser.Controller.Entities
|
||||
|
||||
protected Task RefreshMetadataForOwnedItem(BaseItem ownedItem, bool copyTitleMetadata, MetadataRefreshOptions options, CancellationToken cancellationToken)
|
||||
{
|
||||
var newOptions = new MetadataRefreshOptions(options);
|
||||
newOptions.SearchResult = null;
|
||||
var newOptions = new MetadataRefreshOptions(options)
|
||||
{
|
||||
SearchResult = null
|
||||
};
|
||||
|
||||
var item = this;
|
||||
|
||||
@@ -2820,8 +2492,10 @@ namespace MediaBrowser.Controller.Entities
|
||||
|
||||
protected Task RefreshMetadataForOwnedVideo(MetadataRefreshOptions options, bool copyTitleMetadata, string path, CancellationToken cancellationToken)
|
||||
{
|
||||
var newOptions = new MetadataRefreshOptions(options);
|
||||
newOptions.SearchResult = null;
|
||||
var newOptions = new MetadataRefreshOptions(options)
|
||||
{
|
||||
SearchResult = null
|
||||
};
|
||||
|
||||
var id = LibraryManager.GetNewItemId(path, typeof(Video));
|
||||
|
||||
@@ -2835,14 +2509,6 @@ namespace MediaBrowser.Controller.Entities
|
||||
newOptions.ForceSave = true;
|
||||
}
|
||||
|
||||
// var parentId = Id;
|
||||
// if (!video.IsOwnedItem || video.ParentId != parentId)
|
||||
// {
|
||||
// video.IsOwnedItem = true;
|
||||
// video.ParentId = parentId;
|
||||
// newOptions.ForceSave = true;
|
||||
// }
|
||||
|
||||
if (video == null)
|
||||
{
|
||||
return Task.FromResult(true);
|
||||
@@ -2926,9 +2592,9 @@ namespace MediaBrowser.Controller.Entities
|
||||
.Select(i => i.OfficialRating)
|
||||
.Where(i => !string.IsNullOrEmpty(i))
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.Select(i => new Tuple<string, int?>(i, LocalizationManager.GetRatingLevel(i)))
|
||||
.Select(rating => (rating, LocalizationManager.GetRatingLevel(rating)))
|
||||
.OrderBy(i => i.Item2 ?? 1000)
|
||||
.Select(i => i.Item1);
|
||||
.Select(i => i.rating);
|
||||
|
||||
OfficialRating = ratings.FirstOrDefault() ?? currentOfficialRating;
|
||||
|
||||
@@ -2938,14 +2604,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>
|
||||
@@ -2973,18 +2639,6 @@ namespace MediaBrowser.Controller.Entities
|
||||
.Where(i => i.ExtraType.HasValue && extraTypes.Contains(i.ExtraType.Value));
|
||||
}
|
||||
|
||||
public IEnumerable<BaseItem> GetTrailers()
|
||||
{
|
||||
if (this is IHasTrailers)
|
||||
{
|
||||
return ((IHasTrailers)this).LocalTrailerIds.Select(LibraryManager.GetItemById).Where(i => i != null).OrderBy(i => i.SortName);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Array.Empty<BaseItem>();
|
||||
}
|
||||
}
|
||||
|
||||
public virtual long GetRunTimeTicksForPlayState()
|
||||
{
|
||||
return RunTimeTicks ?? 0;
|
||||
@@ -2997,7 +2651,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Equals(BaseItem other) => object.Equals(Id, other?.Id);
|
||||
public bool Equals(BaseItem other) => Id == other?.Id;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode() => HashCode.Combine(Id);
|
||||
|
||||
@@ -44,14 +44,15 @@ namespace MediaBrowser.Controller.Entities
|
||||
/// <param name="file">The file.</param>
|
||||
public static void SetImagePath(this BaseItem item, ImageType imageType, string file)
|
||||
{
|
||||
if (file.StartsWith("http", System.StringComparison.OrdinalIgnoreCase))
|
||||
if (file.StartsWith("http", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
item.SetImage(
|
||||
new ItemImageInfo
|
||||
{
|
||||
Path = file,
|
||||
Type = imageType
|
||||
}, 0);
|
||||
new ItemImageInfo
|
||||
{
|
||||
Path = file,
|
||||
Type = imageType
|
||||
},
|
||||
0);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -303,7 +303,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
if (dictionary.ContainsKey(id))
|
||||
{
|
||||
Logger.LogError(
|
||||
"Found folder containing items with duplicate id. Path: {path}, Child Name: {ChildName}",
|
||||
"Found folder containing items with duplicate id. Path: {Path}, Child Name: {ChildName}",
|
||||
Path ?? Name,
|
||||
child.Path ?? child.Name);
|
||||
}
|
||||
@@ -425,7 +425,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
{
|
||||
if (item.IsFileProtocol)
|
||||
{
|
||||
Logger.LogDebug("Removed item: " + item.Path);
|
||||
Logger.LogDebug("Removed item: {Path}", item.Path);
|
||||
|
||||
item.SetParent(null);
|
||||
LibraryManager.DeleteItem(item, new DeleteOptions { DeleteFileLocation = false }, this, false);
|
||||
@@ -792,7 +792,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
|
||||
private bool RequiresPostFiltering2(InternalItemsQuery query)
|
||||
{
|
||||
if (query.IncludeItemTypes.Length == 1 && string.Equals(query.IncludeItemTypes[0], nameof(BoxSet), StringComparison.OrdinalIgnoreCase))
|
||||
if (query.IncludeItemTypes.Length == 1 && query.IncludeItemTypes[0] == BaseItemKind.BoxSet)
|
||||
{
|
||||
Logger.LogDebug("Query requires post-filtering due to BoxSet query");
|
||||
return true;
|
||||
@@ -807,7 +807,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
{
|
||||
if (this is not ICollectionFolder)
|
||||
{
|
||||
Logger.LogDebug("Query requires post-filtering due to LinkedChildren. Type: " + GetType().Name);
|
||||
Logger.LogDebug("{Type}: Query requires post-filtering due to LinkedChildren.", GetType().Name);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -882,7 +882,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
|
||||
if (query.IsPlayed.HasValue)
|
||||
{
|
||||
if (query.IncludeItemTypes.Length == 1 && query.IncludeItemTypes.Contains(nameof(Series)))
|
||||
if (query.IncludeItemTypes.Length == 1 && query.IncludeItemTypes.Contains(BaseItemKind.Series))
|
||||
{
|
||||
Logger.LogDebug("Query requires post-filtering due to IsPlayed");
|
||||
return true;
|
||||
@@ -1013,6 +1013,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
items = CollapseBoxSetItemsIfNeeded(items, query, this, user, ConfigurationManager, CollectionManager);
|
||||
}
|
||||
|
||||
#pragma warning disable CA1309
|
||||
if (!string.IsNullOrEmpty(query.NameStartsWithOrGreater))
|
||||
{
|
||||
items = items.Where(i => string.Compare(query.NameStartsWithOrGreater, i.SortName, StringComparison.InvariantCultureIgnoreCase) < 1);
|
||||
@@ -1027,6 +1028,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
{
|
||||
items = items.Where(i => string.Compare(query.NameLessThan, i.SortName, StringComparison.InvariantCultureIgnoreCase) == 1);
|
||||
}
|
||||
#pragma warning restore CA1309
|
||||
|
||||
// This must be the last filter
|
||||
if (!string.IsNullOrEmpty(query.AdjacentTo))
|
||||
@@ -1099,7 +1101,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
return false;
|
||||
}
|
||||
|
||||
if (query.IncludeItemTypes.Length == 0 || query.IncludeItemTypes.Contains("Movie", StringComparer.OrdinalIgnoreCase))
|
||||
if (query.IncludeItemTypes.Length == 0 || query.IncludeItemTypes.Contains(BaseItemKind.Movie))
|
||||
{
|
||||
param = true;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
using Diacritics.Extensions;
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
using Jellyfin.Data.Enums;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace MediaBrowser.Controller.Entities
|
||||
@@ -66,10 +66,10 @@ namespace MediaBrowser.Controller.Entities
|
||||
query.GenreIds = new[] { Id };
|
||||
query.ExcludeItemTypes = new[]
|
||||
{
|
||||
nameof(MusicVideo),
|
||||
nameof(Entities.Audio.Audio),
|
||||
nameof(MusicAlbum),
|
||||
nameof(MusicArtist)
|
||||
BaseItemKind.MusicVideo,
|
||||
BaseItemKind.Audio,
|
||||
BaseItemKind.MusicAlbum,
|
||||
BaseItemKind.MusicArtist
|
||||
};
|
||||
|
||||
return LibraryManager.GetItemList(query);
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
namespace MediaBrowser.Controller.Entities
|
||||
{
|
||||
/// <summary>
|
||||
/// The item has screenshots.
|
||||
/// </summary>
|
||||
public interface IHasScreenshots
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -8,4 +8,4 @@ namespace MediaBrowser.Controller.Entities
|
||||
{
|
||||
Share[] Shares { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,9 +10,9 @@ namespace MediaBrowser.Controller.Entities
|
||||
public interface IHasSpecialFeatures
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the special feature ids.
|
||||
/// Gets the special feature ids.
|
||||
/// </summary>
|
||||
/// <value>The special feature ids.</value>
|
||||
IReadOnlyList<Guid> SpecialFeatureIds { get; set; }
|
||||
IReadOnlyList<Guid> SpecialFeatureIds { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,18 +27,18 @@ namespace MediaBrowser.Controller.Entities
|
||||
ExcludeArtistIds = Array.Empty<Guid>();
|
||||
ExcludeInheritedTags = Array.Empty<string>();
|
||||
ExcludeItemIds = Array.Empty<Guid>();
|
||||
ExcludeItemTypes = Array.Empty<string>();
|
||||
ExcludeItemTypes = Array.Empty<BaseItemKind>();
|
||||
ExcludeTags = Array.Empty<string>();
|
||||
GenreIds = Array.Empty<Guid>();
|
||||
Genres = Array.Empty<string>();
|
||||
GroupByPresentationUniqueKey = true;
|
||||
ImageTypes = Array.Empty<ImageType>();
|
||||
IncludeItemTypes = Array.Empty<string>();
|
||||
IncludeItemTypes = Array.Empty<BaseItemKind>();
|
||||
ItemIds = Array.Empty<Guid>();
|
||||
MediaTypes = Array.Empty<string>();
|
||||
MinSimilarityScore = 20;
|
||||
OfficialRatings = Array.Empty<string>();
|
||||
OrderBy = Array.Empty<ValueTuple<string, SortOrder>>();
|
||||
OrderBy = Array.Empty<(string, SortOrder)>();
|
||||
PersonIds = Array.Empty<Guid>();
|
||||
PersonTypes = Array.Empty<string>();
|
||||
PresetViews = Array.Empty<string>();
|
||||
@@ -87,9 +87,9 @@ namespace MediaBrowser.Controller.Entities
|
||||
|
||||
public string[] MediaTypes { get; set; }
|
||||
|
||||
public string[] IncludeItemTypes { get; set; }
|
||||
public BaseItemKind[] IncludeItemTypes { get; set; }
|
||||
|
||||
public string[] ExcludeItemTypes { get; set; }
|
||||
public BaseItemKind[] ExcludeItemTypes { get; set; }
|
||||
|
||||
public string[] ExcludeTags { get; set; }
|
||||
|
||||
@@ -229,7 +229,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
|
||||
public Guid ParentId { get; set; }
|
||||
|
||||
public string? ParentType { get; set; }
|
||||
public BaseItemKind? ParentType { get; set; }
|
||||
|
||||
public Guid[] AncestorIds { get; set; }
|
||||
|
||||
@@ -271,7 +271,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
|
||||
public bool? HasChapterImages { get; set; }
|
||||
|
||||
public IReadOnlyList<(string, SortOrder)> OrderBy { get; set; }
|
||||
public IReadOnlyList<(string OrderBy, SortOrder SortOrder)> OrderBy { get; set; }
|
||||
|
||||
public DateTime? MinDateCreated { get; set; }
|
||||
|
||||
@@ -314,7 +314,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
else
|
||||
{
|
||||
ParentId = value.Id;
|
||||
ParentType = value.GetType().Name;
|
||||
ParentType = value.GetBaseItemKind();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,4 +32,4 @@ namespace MediaBrowser.Controller.Entities
|
||||
return ((obj.Path ?? string.Empty) + (obj.LibraryItemId ?? string.Empty) + obj.Type).GetHashCode(StringComparison.Ordinal);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,4 +15,4 @@
|
||||
/// </summary>
|
||||
Shortcut = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,16 @@ namespace MediaBrowser.Controller.Entities.Movies
|
||||
/// </summary>
|
||||
public class Movie : Video, IHasSpecialFeatures, IHasTrailers, IHasLookupInfo<MovieInfo>, ISupportsBoxSetGrouping
|
||||
{
|
||||
public Movie()
|
||||
{
|
||||
SpecialFeatureIds = Array.Empty<Guid>();
|
||||
RemoteTrailers = Array.Empty<MediaUrl>();
|
||||
LocalTrailerIds = Array.Empty<Guid>();
|
||||
RemoteTrailerIds = Array.Empty<Guid>();
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyList<Guid> SpecialFeatureIds => GetExtras()
|
||||
.Where(extra => extra.ExtraType != null && extra is Video)
|
||||
.Select(extra => extra.Id)
|
||||
.ToArray();
|
||||
|
||||
/// <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 +57,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()
|
||||
{
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
@@ -131,7 +127,7 @@ namespace MediaBrowser.Controller.Entities.TV
|
||||
{
|
||||
AncestorWithPresentationUniqueKey = null,
|
||||
SeriesPresentationUniqueKey = seriesKey,
|
||||
IncludeItemTypes = new[] { nameof(Season) },
|
||||
IncludeItemTypes = new[] { BaseItemKind.Season },
|
||||
IsVirtualItem = false,
|
||||
Limit = 0,
|
||||
DtoOptions = new DtoOptions(false)
|
||||
@@ -159,7 +155,7 @@ namespace MediaBrowser.Controller.Entities.TV
|
||||
|
||||
if (query.IncludeItemTypes.Length == 0)
|
||||
{
|
||||
query.IncludeItemTypes = new[] { nameof(Episode) };
|
||||
query.IncludeItemTypes = new[] { BaseItemKind.Episode };
|
||||
}
|
||||
|
||||
query.IsVirtualItem = false;
|
||||
@@ -213,7 +209,7 @@ namespace MediaBrowser.Controller.Entities.TV
|
||||
|
||||
query.AncestorWithPresentationUniqueKey = null;
|
||||
query.SeriesPresentationUniqueKey = seriesKey;
|
||||
query.IncludeItemTypes = new[] { nameof(Season) };
|
||||
query.IncludeItemTypes = new[] { BaseItemKind.Season };
|
||||
query.OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) };
|
||||
|
||||
if (user != null && !user.DisplayMissingEpisodes)
|
||||
@@ -239,7 +235,7 @@ namespace MediaBrowser.Controller.Entities.TV
|
||||
|
||||
if (query.IncludeItemTypes.Length == 0)
|
||||
{
|
||||
query.IncludeItemTypes = new[] { nameof(Episode), nameof(Season) };
|
||||
query.IncludeItemTypes = new[] { BaseItemKind.Episode, BaseItemKind.Season };
|
||||
}
|
||||
|
||||
query.IsVirtualItem = false;
|
||||
@@ -259,7 +255,7 @@ namespace MediaBrowser.Controller.Entities.TV
|
||||
{
|
||||
AncestorWithPresentationUniqueKey = null,
|
||||
SeriesPresentationUniqueKey = seriesKey,
|
||||
IncludeItemTypes = new[] { nameof(Episode), nameof(Season) },
|
||||
IncludeItemTypes = new[] { BaseItemKind.Episode, BaseItemKind.Season },
|
||||
OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) },
|
||||
DtoOptions = options
|
||||
};
|
||||
@@ -363,7 +359,7 @@ namespace MediaBrowser.Controller.Entities.TV
|
||||
{
|
||||
AncestorWithPresentationUniqueKey = queryFromSeries ? null : seriesKey,
|
||||
SeriesPresentationUniqueKey = queryFromSeries ? seriesKey : null,
|
||||
IncludeItemTypes = new[] { nameof(Episode) },
|
||||
IncludeItemTypes = new[] { BaseItemKind.Episode },
|
||||
OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) },
|
||||
DtoOptions = options
|
||||
};
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Jellyfin.Extensions;
|
||||
|
||||
namespace MediaBrowser.Controller.Entities
|
||||
{
|
||||
@@ -16,7 +17,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
|
||||
var current = item.Tags;
|
||||
|
||||
if (!current.Contains(name, StringComparer.OrdinalIgnoreCase))
|
||||
if (!current.Contains(name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (current.Length == 0)
|
||||
{
|
||||
|
||||
@@ -8,6 +8,7 @@ using System.Linq;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading.Tasks;
|
||||
using Jellyfin.Data.Entities;
|
||||
using Jellyfin.Extensions;
|
||||
using MediaBrowser.Controller.TV;
|
||||
using MediaBrowser.Model.Querying;
|
||||
|
||||
@@ -102,7 +103,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
parent = LibraryManager.GetItemById(ParentId) as Folder ?? parent;
|
||||
}
|
||||
|
||||
return new UserViewBuilder(UserViewManager, LibraryManager, Logger, UserDataManager, TVSeriesManager, ConfigurationManager)
|
||||
return new UserViewBuilder(UserViewManager, LibraryManager, Logger, UserDataManager, TVSeriesManager)
|
||||
.GetUserItems(parent, this, CollectionType, query);
|
||||
}
|
||||
|
||||
@@ -170,12 +171,12 @@ namespace MediaBrowser.Controller.Entities
|
||||
|
||||
public static bool IsEligibleForGrouping(string viewType)
|
||||
{
|
||||
return _viewTypesEligibleForGrouping.Contains(viewType ?? string.Empty, StringComparer.OrdinalIgnoreCase);
|
||||
return _viewTypesEligibleForGrouping.Contains(viewType ?? string.Empty, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public static bool EnableOriginalFolder(string viewType)
|
||||
{
|
||||
return _originalFolderViewTypes.Contains(viewType ?? string.Empty, StringComparer.OrdinalIgnoreCase);
|
||||
return _originalFolderViewTypes.Contains(viewType ?? string.Empty, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
protected override Task ValidateChildrenInternal(IProgress<double> progress, bool recursive, bool refreshChildMetadata, Providers.MetadataRefreshOptions refreshOptions, Providers.IDirectoryService directoryService, System.Threading.CancellationToken cancellationToken)
|
||||
|
||||
@@ -8,8 +8,7 @@ using System.Globalization;
|
||||
using System.Linq;
|
||||
using Jellyfin.Data.Entities;
|
||||
using Jellyfin.Data.Enums;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Entities.Movies;
|
||||
using Jellyfin.Extensions;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.TV;
|
||||
using MediaBrowser.Model.Entities;
|
||||
@@ -17,8 +16,6 @@ using MediaBrowser.Model.Querying;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Episode = MediaBrowser.Controller.Entities.TV.Episode;
|
||||
using MetadataProvider = MediaBrowser.Model.Entities.MetadataProvider;
|
||||
using Movie = MediaBrowser.Controller.Entities.Movies.Movie;
|
||||
using Season = MediaBrowser.Controller.Entities.TV.Season;
|
||||
using Series = MediaBrowser.Controller.Entities.TV.Series;
|
||||
|
||||
namespace MediaBrowser.Controller.Entities
|
||||
@@ -30,22 +27,19 @@ namespace MediaBrowser.Controller.Entities
|
||||
private readonly ILogger<BaseItem> _logger;
|
||||
private readonly IUserDataManager _userDataManager;
|
||||
private readonly ITVSeriesManager _tvSeriesManager;
|
||||
private readonly IServerConfigurationManager _config;
|
||||
|
||||
public UserViewBuilder(
|
||||
IUserViewManager userViewManager,
|
||||
ILibraryManager libraryManager,
|
||||
ILogger<BaseItem> logger,
|
||||
IUserDataManager userDataManager,
|
||||
ITVSeriesManager tvSeriesManager,
|
||||
IServerConfigurationManager config)
|
||||
ITVSeriesManager tvSeriesManager)
|
||||
{
|
||||
_userViewManager = userViewManager;
|
||||
_libraryManager = libraryManager;
|
||||
_logger = logger;
|
||||
_userDataManager = userDataManager;
|
||||
_tvSeriesManager = tvSeriesManager;
|
||||
_config = config;
|
||||
}
|
||||
|
||||
public QueryResult<BaseItem> GetUserItems(Folder queryParent, Folder displayParent, string viewType, InternalItemsQuery query)
|
||||
@@ -144,7 +138,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
|
||||
if (query.IncludeItemTypes.Length == 0)
|
||||
{
|
||||
query.IncludeItemTypes = new[] { nameof(Movie) };
|
||||
query.IncludeItemTypes = new[] { BaseItemKind.Movie };
|
||||
}
|
||||
|
||||
return parent.QueryRecursive(query);
|
||||
@@ -169,7 +163,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
query.Parent = parent;
|
||||
query.SetUser(user);
|
||||
query.IsFavorite = true;
|
||||
query.IncludeItemTypes = new[] { nameof(Movie) };
|
||||
query.IncludeItemTypes = new[] { BaseItemKind.Movie };
|
||||
|
||||
return _libraryManager.GetItemsResult(query);
|
||||
}
|
||||
@@ -180,7 +174,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
query.Parent = parent;
|
||||
query.SetUser(user);
|
||||
query.IsFavorite = true;
|
||||
query.IncludeItemTypes = new[] { nameof(Series) };
|
||||
query.IncludeItemTypes = new[] { BaseItemKind.Series };
|
||||
|
||||
return _libraryManager.GetItemsResult(query);
|
||||
}
|
||||
@@ -191,7 +185,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
query.Parent = parent;
|
||||
query.SetUser(user);
|
||||
query.IsFavorite = true;
|
||||
query.IncludeItemTypes = new[] { nameof(Episode) };
|
||||
query.IncludeItemTypes = new[] { BaseItemKind.Episode };
|
||||
|
||||
return _libraryManager.GetItemsResult(query);
|
||||
}
|
||||
@@ -202,7 +196,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
query.Parent = parent;
|
||||
query.SetUser(user);
|
||||
|
||||
query.IncludeItemTypes = new[] { nameof(Movie) };
|
||||
query.IncludeItemTypes = new[] { BaseItemKind.Movie };
|
||||
|
||||
return _libraryManager.GetItemsResult(query);
|
||||
}
|
||||
@@ -210,7 +204,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
private QueryResult<BaseItem> GetMovieCollections(User user, InternalItemsQuery query)
|
||||
{
|
||||
query.Parent = null;
|
||||
query.IncludeItemTypes = new[] { nameof(BoxSet) };
|
||||
query.IncludeItemTypes = new[] { BaseItemKind.BoxSet };
|
||||
query.SetUser(user);
|
||||
query.Recursive = true;
|
||||
|
||||
@@ -224,7 +218,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
query.Parent = parent;
|
||||
query.SetUser(user);
|
||||
query.Limit = GetSpecialItemsLimit();
|
||||
query.IncludeItemTypes = new[] { nameof(Movie) };
|
||||
query.IncludeItemTypes = new[] { BaseItemKind.Movie };
|
||||
|
||||
return ConvertToResult(_libraryManager.GetItemList(query));
|
||||
}
|
||||
@@ -237,7 +231,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
query.Parent = parent;
|
||||
query.SetUser(user);
|
||||
query.Limit = GetSpecialItemsLimit();
|
||||
query.IncludeItemTypes = new[] { nameof(Movie) };
|
||||
query.IncludeItemTypes = new[] { BaseItemKind.Movie };
|
||||
|
||||
return ConvertToResult(_libraryManager.GetItemList(query));
|
||||
}
|
||||
@@ -256,7 +250,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
{
|
||||
var genres = parent.QueryRecursive(new InternalItemsQuery(user)
|
||||
{
|
||||
IncludeItemTypes = new[] { nameof(Movie) },
|
||||
IncludeItemTypes = new[] { BaseItemKind.Movie },
|
||||
Recursive = true,
|
||||
EnableTotalRecordCount = false
|
||||
}).Items
|
||||
@@ -287,7 +281,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
query.GenreIds = new[] { displayParent.Id };
|
||||
query.SetUser(user);
|
||||
|
||||
query.IncludeItemTypes = new[] { nameof(Movie) };
|
||||
query.IncludeItemTypes = new[] { BaseItemKind.Movie };
|
||||
|
||||
return _libraryManager.GetItemsResult(query);
|
||||
}
|
||||
@@ -303,9 +297,9 @@ namespace MediaBrowser.Controller.Entities
|
||||
{
|
||||
query.IncludeItemTypes = new[]
|
||||
{
|
||||
nameof(Series),
|
||||
nameof(Season),
|
||||
nameof(Episode)
|
||||
BaseItemKind.Series,
|
||||
BaseItemKind.Season,
|
||||
BaseItemKind.Episode
|
||||
};
|
||||
}
|
||||
|
||||
@@ -333,7 +327,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
query.Parent = parent;
|
||||
query.SetUser(user);
|
||||
query.Limit = GetSpecialItemsLimit();
|
||||
query.IncludeItemTypes = new[] { nameof(Episode) };
|
||||
query.IncludeItemTypes = new[] { BaseItemKind.Episode };
|
||||
query.IsVirtualItem = false;
|
||||
|
||||
return ConvertToResult(_libraryManager.GetItemList(query));
|
||||
@@ -364,7 +358,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
query.Parent = parent;
|
||||
query.SetUser(user);
|
||||
query.Limit = GetSpecialItemsLimit();
|
||||
query.IncludeItemTypes = new[] { nameof(Episode) };
|
||||
query.IncludeItemTypes = new[] { BaseItemKind.Episode };
|
||||
|
||||
return ConvertToResult(_libraryManager.GetItemList(query));
|
||||
}
|
||||
@@ -375,7 +369,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
query.Parent = parent;
|
||||
query.SetUser(user);
|
||||
|
||||
query.IncludeItemTypes = new[] { nameof(Series) };
|
||||
query.IncludeItemTypes = new[] { BaseItemKind.Series };
|
||||
|
||||
return _libraryManager.GetItemsResult(query);
|
||||
}
|
||||
@@ -384,7 +378,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
{
|
||||
var genres = parent.QueryRecursive(new InternalItemsQuery(user)
|
||||
{
|
||||
IncludeItemTypes = new[] { nameof(Series) },
|
||||
IncludeItemTypes = new[] { BaseItemKind.Series },
|
||||
Recursive = true,
|
||||
EnableTotalRecordCount = false
|
||||
}).Items
|
||||
@@ -415,7 +409,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
query.GenreIds = new[] { displayParent.Id };
|
||||
query.SetUser(user);
|
||||
|
||||
query.IncludeItemTypes = new[] { nameof(Series) };
|
||||
query.IncludeItemTypes = new[] { BaseItemKind.Series };
|
||||
|
||||
return _libraryManager.GetItemsResult(query);
|
||||
}
|
||||
@@ -498,17 +492,17 @@ namespace MediaBrowser.Controller.Entities
|
||||
|
||||
public static bool Filter(BaseItem item, User user, InternalItemsQuery query, IUserDataManager userDataManager, ILibraryManager libraryManager)
|
||||
{
|
||||
if (query.MediaTypes.Length > 0 && !query.MediaTypes.Contains(item.MediaType ?? string.Empty, StringComparer.OrdinalIgnoreCase))
|
||||
if (query.MediaTypes.Length > 0 && !query.MediaTypes.Contains(item.MediaType ?? string.Empty, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (query.IncludeItemTypes.Length > 0 && !query.IncludeItemTypes.Contains(item.GetClientTypeName(), StringComparer.OrdinalIgnoreCase))
|
||||
if (query.IncludeItemTypes.Length > 0 && !query.IncludeItemTypes.Contains(item.GetBaseItemKind()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (query.ExcludeItemTypes.Length > 0 && query.ExcludeItemTypes.Contains(item.GetClientTypeName(), StringComparer.OrdinalIgnoreCase))
|
||||
if (query.ExcludeItemTypes.Length > 0 && query.ExcludeItemTypes.Contains(item.GetBaseItemKind()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -749,10 +743,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;
|
||||
@@ -767,7 +760,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)
|
||||
@@ -780,7 +773,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)
|
||||
@@ -790,7 +783,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
}
|
||||
|
||||
// Apply genre filter
|
||||
if (query.Genres.Count > 0 && !query.Genres.Any(v => item.Genres.Contains(v, StringComparer.OrdinalIgnoreCase)))
|
||||
if (query.Genres.Count > 0 && !query.Genres.Any(v => item.Genres.Contains(v, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -814,7 +807,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
if (query.StudioIds.Length > 0 && !query.StudioIds.Any(id =>
|
||||
{
|
||||
var studioItem = libraryManager.GetItemById(id);
|
||||
return studioItem != null && item.Studios.Contains(studioItem.Name, StringComparer.OrdinalIgnoreCase);
|
||||
return studioItem != null && item.Studios.Contains(studioItem.Name, StringComparison.OrdinalIgnoreCase);
|
||||
}))
|
||||
{
|
||||
return false;
|
||||
@@ -824,7 +817,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
if (query.GenreIds.Count > 0 && !query.GenreIds.Any(id =>
|
||||
{
|
||||
var genreItem = libraryManager.GetItemById(id);
|
||||
return genreItem != null && item.Genres.Contains(genreItem.Name, StringComparer.OrdinalIgnoreCase);
|
||||
return genreItem != null && item.Genres.Contains(genreItem.Name, StringComparison.OrdinalIgnoreCase);
|
||||
}))
|
||||
{
|
||||
return false;
|
||||
@@ -857,7 +850,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
var tags = query.Tags;
|
||||
if (tags.Length > 0)
|
||||
{
|
||||
if (!tags.Any(v => item.Tags.Contains(v, StringComparer.OrdinalIgnoreCase)))
|
||||
if (!tags.Any(v => item.Tags.Contains(v, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -975,7 +968,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
{
|
||||
var folder = i as ICollectionFolder;
|
||||
|
||||
return folder != null && viewTypes.Contains(folder.CollectionType ?? string.Empty, StringComparer.OrdinalIgnoreCase);
|
||||
return folder != null && viewTypes.Contains(folder.CollectionType ?? string.Empty, StringComparison.OrdinalIgnoreCase);
|
||||
}).ToArray();
|
||||
}
|
||||
|
||||
@@ -984,7 +977,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
{
|
||||
var folder = i as ICollectionFolder;
|
||||
|
||||
return folder != null && viewTypes.Contains(folder.CollectionType ?? string.Empty, StringComparer.OrdinalIgnoreCase);
|
||||
return folder != null && viewTypes.Contains(folder.CollectionType ?? string.Empty, StringComparison.OrdinalIgnoreCase);
|
||||
}).ToArray();
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ using System.Linq;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Jellyfin.Extensions;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.LiveTv;
|
||||
using MediaBrowser.Controller.Persistence;
|
||||
@@ -33,6 +34,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
AdditionalParts = Array.Empty<string>();
|
||||
LocalAlternateVersions = Array.Empty<string>();
|
||||
SubtitleFiles = Array.Empty<string>();
|
||||
AudioFiles = Array.Empty<string>();
|
||||
LinkedAlternateVersions = Array.Empty<LinkedChild>();
|
||||
}
|
||||
|
||||
@@ -97,6 +99,12 @@ namespace MediaBrowser.Controller.Entities
|
||||
/// <value>The subtitle paths.</value>
|
||||
public string[] SubtitleFiles { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the audio paths.
|
||||
/// </summary>
|
||||
/// <value>The audio paths.</value>
|
||||
public string[] AudioFiles { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this instance has subtitles.
|
||||
/// </summary>
|
||||
@@ -185,7 +193,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
{
|
||||
if (SourceType == SourceType.Channel)
|
||||
{
|
||||
return !Tags.Contains("livestream", StringComparer.OrdinalIgnoreCase);
|
||||
return !Tags.Contains("livestream", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
return !IsActiveRecording();
|
||||
@@ -509,35 +517,35 @@ namespace MediaBrowser.Controller.Entities
|
||||
}).FirstOrDefault();
|
||||
}
|
||||
|
||||
protected override List<Tuple<BaseItem, MediaSourceType>> GetAllItemsForMediaSources()
|
||||
protected override IEnumerable<(BaseItem Item, MediaSourceType MediaSourceType)> GetAllItemsForMediaSources()
|
||||
{
|
||||
var list = new List<Tuple<BaseItem, MediaSourceType>>();
|
||||
var list = new List<(BaseItem, MediaSourceType)>
|
||||
{
|
||||
(this, MediaSourceType.Default)
|
||||
};
|
||||
|
||||
list.Add(new Tuple<BaseItem, MediaSourceType>(this, MediaSourceType.Default));
|
||||
list.AddRange(GetLinkedAlternateVersions().Select(i => new Tuple<BaseItem, MediaSourceType>(i, MediaSourceType.Grouping)));
|
||||
list.AddRange(GetLinkedAlternateVersions().Select(i => ((BaseItem)i, MediaSourceType.Grouping)));
|
||||
|
||||
if (!string.IsNullOrEmpty(PrimaryVersionId))
|
||||
{
|
||||
var primary = LibraryManager.GetItemById(PrimaryVersionId) as Video;
|
||||
if (primary != null)
|
||||
if (LibraryManager.GetItemById(PrimaryVersionId) is Video primary)
|
||||
{
|
||||
var existingIds = list.Select(i => i.Item1.Id).ToList();
|
||||
list.Add(new Tuple<BaseItem, MediaSourceType>(primary, MediaSourceType.Grouping));
|
||||
list.AddRange(primary.GetLinkedAlternateVersions().Where(i => !existingIds.Contains(i.Id)).Select(i => new Tuple<BaseItem, MediaSourceType>(i, MediaSourceType.Grouping)));
|
||||
list.Add((primary, MediaSourceType.Grouping));
|
||||
list.AddRange(primary.GetLinkedAlternateVersions().Where(i => !existingIds.Contains(i.Id)).Select(i => ((BaseItem)i, MediaSourceType.Grouping)));
|
||||
}
|
||||
}
|
||||
|
||||
var localAlternates = list
|
||||
.SelectMany(i =>
|
||||
{
|
||||
var video = i.Item1 as Video;
|
||||
return video == null ? new List<Guid>() : video.GetLocalAlternateVersionIds();
|
||||
return i.Item1 is Video video ? video.GetLocalAlternateVersionIds() : Enumerable.Empty<Guid>();
|
||||
})
|
||||
.Select(LibraryManager.GetItemById)
|
||||
.Where(i => i != null)
|
||||
.ToList();
|
||||
|
||||
list.AddRange(localAlternates.Select(i => new Tuple<BaseItem, MediaSourceType>(i, MediaSourceType.Default)));
|
||||
list.AddRange(localAlternates.Select(i => (i, MediaSourceType.Default)));
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
@@ -57,9 +57,7 @@ namespace MediaBrowser.Controller.Entities
|
||||
|
||||
public IList<BaseItem> GetTaggedItems(InternalItemsQuery query)
|
||||
{
|
||||
var usCulture = new CultureInfo("en-US");
|
||||
|
||||
if (!int.TryParse(Name, NumberStyles.Integer, usCulture, out var year))
|
||||
if (!int.TryParse(Name, NumberStyles.Integer, CultureInfo.InvariantCulture, out var year))
|
||||
{
|
||||
return new List<BaseItem>();
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace MediaBrowser.Controller.IO
|
||||
if (string.IsNullOrEmpty(newPath))
|
||||
{
|
||||
// invalid shortcut - could be old or target could just be unavailable
|
||||
logger.LogWarning("Encountered invalid shortcut: " + fullName);
|
||||
logger.LogWarning("Encountered invalid shortcut: {Path}", fullName);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ namespace MediaBrowser.Controller.IO
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "Error resolving shortcut from {path}", fullName);
|
||||
logger.LogError(ex, "Error resolving shortcut from {Path}", fullName);
|
||||
}
|
||||
}
|
||||
else if (flattenFolderDepth > 0 && isDirectory)
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using MediaBrowser.Common;
|
||||
using MediaBrowser.Model.System;
|
||||
@@ -42,50 +41,42 @@ namespace MediaBrowser.Controller
|
||||
/// <value>The name of the friendly.</value>
|
||||
string FriendlyName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the configured published server url.
|
||||
/// </summary>
|
||||
string PublishedServerUrl { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the system info.
|
||||
/// </summary>
|
||||
/// <param name="source">The originator of the request.</param>
|
||||
/// <param name="request">The HTTP request.</param>
|
||||
/// <returns>SystemInfo.</returns>
|
||||
SystemInfo GetSystemInfo(IPAddress source);
|
||||
SystemInfo GetSystemInfo(HttpRequest request);
|
||||
|
||||
PublicSystemInfo GetPublicSystemInfo(IPAddress address);
|
||||
PublicSystemInfo GetPublicSystemInfo(HttpRequest request);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a URL specific for the request.
|
||||
/// </summary>
|
||||
/// <param name="request">The <see cref="HttpRequest"/> instance.</param>
|
||||
/// <param name="port">Optional port number.</param>
|
||||
/// <returns>An accessible URL.</returns>
|
||||
string GetSmartApiUrl(HttpRequest request, int? port = null);
|
||||
string GetSmartApiUrl(HttpRequest request);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a URL specific for the request.
|
||||
/// </summary>
|
||||
/// <param name="remoteAddr">The remote <see cref="IPAddress"/> of the connection.</param>
|
||||
/// <param name="port">Optional port number.</param>
|
||||
/// <returns>An accessible URL.</returns>
|
||||
string GetSmartApiUrl(IPAddress remoteAddr, int? port = null);
|
||||
string GetSmartApiUrl(IPAddress remoteAddr);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a URL specific for the request.
|
||||
/// </summary>
|
||||
/// <param name="hostname">The hostname used in the connection.</param>
|
||||
/// <param name="port">Optional port number.</param>
|
||||
/// <returns>An accessible URL.</returns>
|
||||
string GetSmartApiUrl(string hostname, int? port = null);
|
||||
string GetSmartApiUrl(string hostname);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a localhost URL that can be used to access the API using the loop-back IP address.
|
||||
/// over HTTP (not HTTPS).
|
||||
/// Gets an URL that can be used to access the API over LAN.
|
||||
/// </summary>
|
||||
/// <param name="allowHttps">A value indicating whether to allow HTTPS.</param>
|
||||
/// <returns>The API URL.</returns>
|
||||
string GetLoopbackHttpApiUrl();
|
||||
string GetApiUrlForLocalAccess(bool allowHttps = true);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a local (LAN) URL that can be used to access the API.
|
||||
@@ -103,8 +94,6 @@ namespace MediaBrowser.Controller
|
||||
/// <returns>The API URL.</returns>
|
||||
string GetLocalApiUrl(string hostname, string scheme = null, int? port = null);
|
||||
|
||||
IEnumerable<WakeOnLanInfo> GetWakeOnLanInfo();
|
||||
|
||||
string ExpandVirtualPath(string path);
|
||||
|
||||
string ReverseVirtualPath(string path);
|
||||
|
||||
@@ -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;
|
||||
@@ -59,10 +58,12 @@ namespace MediaBrowser.Controller.Library
|
||||
/// </summary>
|
||||
/// <param name="fileInfo">The file information.</param>
|
||||
/// <param name="parent">The parent.</param>
|
||||
/// <param name="directoryService">An instance of <see cref="IDirectoryService"/>.</param>
|
||||
/// <returns>BaseItem.</returns>
|
||||
BaseItem ResolvePath(
|
||||
FileSystemMetadata fileInfo,
|
||||
Folder parent = null);
|
||||
Folder parent = null,
|
||||
IDirectoryService directoryService = null);
|
||||
|
||||
/// <summary>
|
||||
/// Resolves a set of files into a list of BaseItem.
|
||||
@@ -211,7 +212,7 @@ namespace MediaBrowser.Controller.Library
|
||||
/// <returns>IEnumerable{BaseItem}.</returns>
|
||||
IEnumerable<BaseItem> Sort(IEnumerable<BaseItem> items, User user, IEnumerable<string> sortBy, SortOrder sortOrder);
|
||||
|
||||
IEnumerable<BaseItem> Sort(IEnumerable<BaseItem> items, User user, IEnumerable<ValueTuple<string, SortOrder>> orderBy);
|
||||
IEnumerable<BaseItem> Sort(IEnumerable<BaseItem> items, User user, IEnumerable<(string OrderBy, SortOrder SortOrder)> orderBy);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user root folder.
|
||||
@@ -396,20 +397,6 @@ namespace MediaBrowser.Controller.Library
|
||||
string viewType,
|
||||
string sortName);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether [is video file] [the specified path].
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <returns><c>true</c> if [is video file] [the specified path]; otherwise, <c>false</c>.</returns>
|
||||
bool IsVideoFile(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether [is audio file] [the specified path].
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <returns><c>true</c> if [is audio file] [the specified path]; otherwise, <c>false</c>.</returns>
|
||||
bool IsAudioFile(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the season number from path.
|
||||
/// </summary>
|
||||
@@ -440,29 +427,14 @@ 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<Trailer>.</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<Video>.</returns>
|
||||
IEnumerable<Video> FindExtras(
|
||||
BaseItem owner,
|
||||
List<FileSystemMetadata> fileSystemChildren,
|
||||
IDirectoryService directoryService);
|
||||
/// <param name="directoryService">An instance of <see cref="IDirectoryService"/>.</param>
|
||||
/// <returns>IEnumerable<BaseItem>.</returns>
|
||||
IEnumerable<BaseItem> FindExtras(BaseItem owner, List<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the collection folders.
|
||||
@@ -601,17 +573,17 @@ namespace MediaBrowser.Controller.Library
|
||||
|
||||
void RemoveMediaPath(string virtualFolderName, string mediaPath);
|
||||
|
||||
QueryResult<(BaseItem, ItemCounts)> GetGenres(InternalItemsQuery query);
|
||||
QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetGenres(InternalItemsQuery query);
|
||||
|
||||
QueryResult<(BaseItem, ItemCounts)> GetMusicGenres(InternalItemsQuery query);
|
||||
QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetMusicGenres(InternalItemsQuery query);
|
||||
|
||||
QueryResult<(BaseItem, ItemCounts)> GetStudios(InternalItemsQuery query);
|
||||
QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetStudios(InternalItemsQuery query);
|
||||
|
||||
QueryResult<(BaseItem, ItemCounts)> GetArtists(InternalItemsQuery query);
|
||||
QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetArtists(InternalItemsQuery query);
|
||||
|
||||
QueryResult<(BaseItem, ItemCounts)> GetAlbumArtists(InternalItemsQuery query);
|
||||
QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetAlbumArtists(InternalItemsQuery query);
|
||||
|
||||
QueryResult<(BaseItem, ItemCounts)> GetAllArtists(InternalItemsQuery query);
|
||||
QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetAllArtists(InternalItemsQuery query);
|
||||
|
||||
int GetCount(InternalItemsQuery query);
|
||||
|
||||
@@ -625,11 +597,5 @@ namespace MediaBrowser.Controller.Library
|
||||
BaseItem GetParentItem(string parentId, Guid? userId);
|
||||
|
||||
BaseItem GetParentItem(Guid? parentId, Guid? userId);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or creates a static instance of <see cref="NamingOptions"/>.
|
||||
/// </summary>
|
||||
/// <returns>An instance of the <see cref="NamingOptions"/> class.</returns>
|
||||
NamingOptions GetNamingOptions();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,13 +30,6 @@ namespace MediaBrowser.Controller.Library
|
||||
/// <returns>IEnumerable<MediaStream>.</returns>
|
||||
List<MediaStream> GetMediaStreams(Guid itemId);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the media streams.
|
||||
/// </summary>
|
||||
/// <param name="mediaSourceId">The media source identifier.</param>
|
||||
/// <returns>IEnumerable<MediaStream>.</returns>
|
||||
List<MediaStream> GetMediaStreams(string mediaSourceId);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the media streams.
|
||||
/// </summary>
|
||||
|
||||
@@ -36,6 +36,7 @@ namespace MediaBrowser.Controller.Library
|
||||
DirectoryService = directoryService;
|
||||
}
|
||||
|
||||
// TODO remove dependencies as properties, they should be injected where it makes sense
|
||||
public IDirectoryService DirectoryService { get; }
|
||||
|
||||
/// <summary>
|
||||
@@ -236,6 +237,40 @@ namespace MediaBrowser.Controller.Library
|
||||
return CollectionType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the configured content type for the path.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is subject to future refactoring as it relies on a static property in BaseItem.
|
||||
/// </remarks>
|
||||
/// <returns>The configured content type.</returns>
|
||||
public string GetConfiguredContentType()
|
||||
{
|
||||
return BaseItem.LibraryManager.GetConfiguredContentType(Path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the file system children that do not hit the ignore file check.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is subject to future refactoring as it relies on a static property in BaseItem.
|
||||
/// </remarks>
|
||||
/// <returns>The file system children that are not ignored.</returns>
|
||||
public IEnumerable<FileSystemMetadata> GetActualFileSystemChildren()
|
||||
{
|
||||
var numberOfChildren = FileSystemChildren.Length;
|
||||
for (var i = 0; i < numberOfChildren; i++)
|
||||
{
|
||||
var child = FileSystemChildren[i];
|
||||
if (BaseItem.LibraryManager.IgnoreFile(child, Parent))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
yield return child;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a hash code for this instance.
|
||||
/// </summary>
|
||||
|
||||
@@ -16,4 +16,4 @@ namespace MediaBrowser.Controller.LiveTv
|
||||
|
||||
public CancellationTokenSource CancellationTokenSource { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -251,7 +251,7 @@ namespace MediaBrowser.Controller.LiveTv
|
||||
/// <param name="fields">The fields.</param>
|
||||
/// <param name="user">The user.</param>
|
||||
/// <returns>Task.</returns>
|
||||
Task AddInfoToProgramDto(IReadOnlyCollection<(BaseItem, BaseItemDto)> programs, IReadOnlyList<ItemFields> fields, User user = null);
|
||||
Task AddInfoToProgramDto(IReadOnlyCollection<(BaseItem Item, BaseItemDto ItemDto)> programs, IReadOnlyList<ItemFields> fields, User user = null);
|
||||
|
||||
/// <summary>
|
||||
/// Saves the tuner host.
|
||||
@@ -292,7 +292,7 @@ namespace MediaBrowser.Controller.LiveTv
|
||||
/// <param name="items">The items.</param>
|
||||
/// <param name="options">The options.</param>
|
||||
/// <param name="user">The user.</param>
|
||||
void AddChannelInfo(IReadOnlyCollection<(BaseItemDto, LiveTvChannel)> items, DtoOptions options, User user);
|
||||
void AddChannelInfo(IReadOnlyCollection<(BaseItemDto ItemDto, LiveTvChannel Channel)> items, DtoOptions options, User user);
|
||||
|
||||
Task<List<ChannelInfo>> GetChannelsForListingsProvider(string id, CancellationToken cancellationToken);
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text.Json.Serialization;
|
||||
using Jellyfin.Data.Enums;
|
||||
using Jellyfin.Extensions;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.Entities;
|
||||
@@ -74,7 +74,7 @@ namespace MediaBrowser.Controller.LiveTv
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if this instance is kids; otherwise, <c>false</c>.</value>
|
||||
[JsonIgnore]
|
||||
public bool IsKids => Tags.Contains("Kids", StringComparer.OrdinalIgnoreCase);
|
||||
public bool IsKids => Tags.Contains("Kids", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
[JsonIgnore]
|
||||
public bool IsRepeat { get; set; }
|
||||
@@ -107,9 +107,7 @@ namespace MediaBrowser.Controller.LiveTv
|
||||
{
|
||||
if (!string.IsNullOrEmpty(Number))
|
||||
{
|
||||
double number = 0;
|
||||
|
||||
if (double.TryParse(Number, NumberStyles.Any, CultureInfo.InvariantCulture, out number))
|
||||
if (double.TryParse(Number, NumberStyles.Any, CultureInfo.InvariantCulture, out double number))
|
||||
{
|
||||
return string.Format(CultureInfo.InvariantCulture, "{0:00000.0}", number) + "-" + (Name ?? string.Empty);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text.Json.Serialization;
|
||||
using Jellyfin.Data.Enums;
|
||||
using Jellyfin.Extensions;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
@@ -66,7 +67,7 @@ namespace MediaBrowser.Controller.LiveTv
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if this instance is sports; otherwise, <c>false</c>.</value>
|
||||
[JsonIgnore]
|
||||
public bool IsSports => Tags.Contains("Sports", StringComparer.OrdinalIgnoreCase);
|
||||
public bool IsSports => Tags.Contains("Sports", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this instance is series.
|
||||
@@ -80,28 +81,28 @@ namespace MediaBrowser.Controller.LiveTv
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if this instance is live; otherwise, <c>false</c>.</value>
|
||||
[JsonIgnore]
|
||||
public bool IsLive => Tags.Contains("Live", StringComparer.OrdinalIgnoreCase);
|
||||
public bool IsLive => Tags.Contains("Live", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance is news.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if this instance is news; otherwise, <c>false</c>.</value>
|
||||
[JsonIgnore]
|
||||
public bool IsNews => Tags.Contains("News", StringComparer.OrdinalIgnoreCase);
|
||||
public bool IsNews => Tags.Contains("News", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance is kids.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if this instance is kids; otherwise, <c>false</c>.</value>
|
||||
[JsonIgnore]
|
||||
public bool IsKids => Tags.Contains("Kids", StringComparer.OrdinalIgnoreCase);
|
||||
public bool IsKids => Tags.Contains("Kids", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance is premiere.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if this instance is premiere; otherwise, <c>false</c>.</value>
|
||||
[JsonIgnore]
|
||||
public bool IsPremiere => Tags.Contains("Premiere", StringComparer.OrdinalIgnoreCase);
|
||||
public bool IsPremiere => Tags.Contains("Premiere", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the folder containing the item.
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json.Serialization;
|
||||
using Jellyfin.Extensions;
|
||||
using MediaBrowser.Model.LiveTv;
|
||||
|
||||
namespace MediaBrowser.Controller.LiveTv
|
||||
@@ -123,11 +123,11 @@ namespace MediaBrowser.Controller.LiveTv
|
||||
|
||||
public bool IsMovie { get; set; }
|
||||
|
||||
public bool IsKids => Tags.Contains("Kids", StringComparer.OrdinalIgnoreCase);
|
||||
public bool IsKids => Tags.Contains("Kids", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
public bool IsSports => Tags.Contains("Sports", StringComparer.OrdinalIgnoreCase);
|
||||
public bool IsSports => Tags.Contains("Sports", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
public bool IsNews => Tags.Contains("News", StringComparer.OrdinalIgnoreCase);
|
||||
public bool IsNews => Tags.Contains("News", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
public bool IsSeries { get; set; }
|
||||
|
||||
@@ -136,10 +136,10 @@ namespace MediaBrowser.Controller.LiveTv
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if this instance is live; otherwise, <c>false</c>.</value>
|
||||
[JsonIgnore]
|
||||
public bool IsLive => Tags.Contains("Live", StringComparer.OrdinalIgnoreCase);
|
||||
public bool IsLive => Tags.Contains("Live", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
[JsonIgnore]
|
||||
public bool IsPremiere => Tags.Contains("Premiere", StringComparer.OrdinalIgnoreCase);
|
||||
public bool IsPremiere => Tags.Contains("Premiere", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
public int? ProductionYear { get; set; }
|
||||
|
||||
|
||||
@@ -13,12 +13,16 @@
|
||||
<PackageLicenseExpression>GPL-3.0-only</PackageLicenseExpression>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Diacritics" Version="2.1.20036.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
|
||||
<PackageReference Include="System.Threading.Tasks.Dataflow" Version="5.0.0" />
|
||||
<PackageReference Include="Diacritics" Version="3.3.10" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
|
||||
<PackageReference Include="System.Threading.Tasks.Dataflow" Version="6.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -39,11 +43,6 @@
|
||||
<EmbedUntrackedSources>true</EmbedUntrackedSources>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release'">
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Stability)'=='Unstable'">
|
||||
@@ -54,7 +53,7 @@
|
||||
<!-- Code Analyzers-->
|
||||
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.376" PrivateAssets="All" />
|
||||
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -201,4 +201,4 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -110,23 +110,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
|
||||
public string OutputContainer { get; set; }
|
||||
|
||||
public string OutputVideoSync
|
||||
{
|
||||
get
|
||||
{
|
||||
// For live tv + in progress recordings
|
||||
if (string.Equals(InputContainer, "mpegts", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(InputContainer, "ts", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (!MediaSource.RunTimeTicks.HasValue)
|
||||
{
|
||||
return "cfr";
|
||||
}
|
||||
}
|
||||
|
||||
return "-1";
|
||||
}
|
||||
}
|
||||
public string OutputVideoSync { get; set; }
|
||||
|
||||
public string AlbumCoverPath { get; set; }
|
||||
|
||||
|
||||
@@ -18,6 +18,16 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
/// <summary>
|
||||
/// The tonemap_opencl_bt2390.
|
||||
/// </summary>
|
||||
TonemapOpenclBt2390 = 2
|
||||
TonemapOpenclBt2390 = 2,
|
||||
|
||||
/// <summary>
|
||||
/// The overlay_opencl_framesync.
|
||||
/// </summary>
|
||||
OverlayOpenclFrameSync = 3,
|
||||
|
||||
/// <summary>
|
||||
/// The overlay_vaapi_framesync.
|
||||
/// </summary>
|
||||
OverlayVaapiFrameSync = 4
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
{
|
||||
public interface IAttachmentExtractor
|
||||
{
|
||||
Task<(MediaAttachment attachment, Stream stream)> GetAttachment(
|
||||
Task<(MediaAttachment Attachment, Stream Stream)> GetAttachment(
|
||||
BaseItem item,
|
||||
string mediaSourceId,
|
||||
int attachmentStreamIndex,
|
||||
|
||||
@@ -7,6 +7,7 @@ using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
using MediaBrowser.Model.Drawing;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.MediaInfo;
|
||||
@@ -30,6 +31,30 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
/// <value>The probe path.</value>
|
||||
string ProbePath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the version of encoder.
|
||||
/// </summary>
|
||||
/// <returns>The version of encoder.</returns>
|
||||
Version EncoderVersion { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the configured Vaapi device is from AMD(radeonsi/r600 Mesa driver).
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if the Vaapi device is an AMD(radeonsi/r600 Mesa driver) GPU, <c>false</c> otherwise.</value>
|
||||
bool IsVaapiDeviceAmd { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the configured Vaapi device is from Intel(iHD driver).
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if the Vaapi device is an Intel(iHD driver) GPU, <c>false</c> otherwise.</value>
|
||||
bool IsVaapiDeviceInteliHD { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the configured Vaapi device is from Intel(legacy i965 driver).
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if the Vaapi device is an Intel(legacy i965 driver) GPU, <c>false</c> otherwise.</value>
|
||||
bool IsVaapiDeviceInteli965 { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether given encoder codec is supported.
|
||||
/// </summary>
|
||||
@@ -65,12 +90,6 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
/// <returns><c>true</c> if the filter is supported, <c>false</c> otherwise.</returns>
|
||||
bool SupportsFilterWithOption(FilterOptionType option);
|
||||
|
||||
/// <summary>
|
||||
/// Get the version of media encoder.
|
||||
/// </summary>
|
||||
/// <returns>The version of media encoder.</returns>
|
||||
Version GetMediaEncoderVersion();
|
||||
|
||||
/// <summary>
|
||||
/// Extracts the audio image.
|
||||
/// </summary>
|
||||
@@ -101,35 +120,10 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
/// <param name="mediaSource">Media source information.</param>
|
||||
/// <param name="imageStream">Media stream information.</param>
|
||||
/// <param name="imageStreamIndex">Index of the stream to extract from.</param>
|
||||
/// <param name="targetFormat">The format of the file to write.</param>
|
||||
/// <param name="cancellationToken">CancellationToken to use for operation.</param>
|
||||
/// <returns>Location of video image.</returns>
|
||||
Task<string> ExtractVideoImage(string inputFile, string container, MediaSourceInfo mediaSource, MediaStream imageStream, int? imageStreamIndex, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Extracts the video images on interval.
|
||||
/// </summary>
|
||||
/// <param name="inputFile">Input file.</param>
|
||||
/// <param name="container">Video container type.</param>
|
||||
/// <param name="videoStream">Media stream information.</param>
|
||||
/// <param name="mediaSource">Media source information.</param>
|
||||
/// <param name="threedFormat">Video 3D format.</param>
|
||||
/// <param name="interval">Time interval.</param>
|
||||
/// <param name="targetDirectory">Directory to write images.</param>
|
||||
/// <param name="filenamePrefix">Filename prefix to use.</param>
|
||||
/// <param name="maxWidth">Maximum width of image.</param>
|
||||
/// <param name="cancellationToken">CancellationToken to use for operation.</param>
|
||||
/// <returns>A task.</returns>
|
||||
Task ExtractVideoImagesOnInterval(
|
||||
string inputFile,
|
||||
string container,
|
||||
MediaStream videoStream,
|
||||
MediaSourceInfo mediaSource,
|
||||
Video3DFormat? threedFormat,
|
||||
TimeSpan interval,
|
||||
string targetDirectory,
|
||||
string filenamePrefix,
|
||||
int? maxWidth,
|
||||
CancellationToken cancellationToken);
|
||||
Task<string> ExtractVideoImage(string inputFile, string container, MediaSourceInfo mediaSource, MediaStream imageStream, int? imageStreamIndex, ImageFormat? targetFormat, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the media info.
|
||||
|
||||
@@ -13,7 +13,6 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
{
|
||||
public class JobLogger
|
||||
{
|
||||
private static readonly CultureInfo _usCulture = CultureInfo.ReadOnly(new CultureInfo("en-US"));
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public JobLogger(ILogger logger)
|
||||
@@ -42,7 +41,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
break;
|
||||
}
|
||||
|
||||
await target.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
|
||||
await target.WriteAsync(bytes).ConfigureAwait(false);
|
||||
|
||||
// Check again, the stream could have been closed
|
||||
if (!target.CanWrite)
|
||||
@@ -87,7 +86,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
{
|
||||
var rate = parts[i + 1];
|
||||
|
||||
if (float.TryParse(rate, NumberStyles.Any, _usCulture, out var val))
|
||||
if (float.TryParse(rate, NumberStyles.Any, CultureInfo.InvariantCulture, out var val))
|
||||
{
|
||||
framerate = val;
|
||||
}
|
||||
@@ -96,7 +95,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
{
|
||||
var rate = part.Split('=', 2)[^1];
|
||||
|
||||
if (float.TryParse(rate, NumberStyles.Any, _usCulture, out var val))
|
||||
if (float.TryParse(rate, NumberStyles.Any, CultureInfo.InvariantCulture, out var val))
|
||||
{
|
||||
framerate = val;
|
||||
}
|
||||
@@ -106,7 +105,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
{
|
||||
var time = part.Split('=', 2)[^1];
|
||||
|
||||
if (TimeSpan.TryParse(time, _usCulture, out var val))
|
||||
if (TimeSpan.TryParse(time, CultureInfo.InvariantCulture, out var val))
|
||||
{
|
||||
var currentMs = startMs + val.TotalMilliseconds;
|
||||
|
||||
@@ -128,7 +127,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
|
||||
if (scale.HasValue)
|
||||
{
|
||||
if (long.TryParse(size, NumberStyles.Any, _usCulture, out var val))
|
||||
if (long.TryParse(size, NumberStyles.Any, CultureInfo.InvariantCulture, out var val))
|
||||
{
|
||||
bytesTranscoded = val * scale.Value;
|
||||
}
|
||||
@@ -147,7 +146,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||
|
||||
if (scale.HasValue)
|
||||
{
|
||||
if (float.TryParse(rate, NumberStyles.Any, _usCulture, out var val))
|
||||
if (float.TryParse(rate, NumberStyles.Any, CultureInfo.InvariantCulture, out var val))
|
||||
{
|
||||
bitRate = (int)Math.Ceiling(val * scale.Value);
|
||||
}
|
||||
|
||||
@@ -20,4 +20,4 @@
|
||||
/// </summary>
|
||||
Dash
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Model.Net;
|
||||
using MediaBrowser.Model.Session;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace MediaBrowser.Controller.Net
|
||||
@@ -95,7 +96,7 @@ namespace MediaBrowser.Controller.Net
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task ProcessWebSocketConnectedAsync(IWebSocketConnection connection) => Task.CompletedTask;
|
||||
public Task ProcessWebSocketConnectedAsync(IWebSocketConnection connection, HttpContext httpContext) => Task.CompletedTask;
|
||||
|
||||
/// <summary>
|
||||
/// Starts sending messages over a web socket.
|
||||
|
||||
@@ -29,12 +29,6 @@ namespace MediaBrowser.Controller.Net
|
||||
/// <value>The date of last Keeplive received.</value>
|
||||
DateTime LastKeepAliveDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the query string.
|
||||
/// </summary>
|
||||
/// <value>The query string.</value>
|
||||
IQueryCollection QueryString { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the receive action.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace MediaBrowser.Controller.Net
|
||||
{
|
||||
@@ -18,7 +19,8 @@ namespace MediaBrowser.Controller.Net
|
||||
/// Processes a new web socket connection.
|
||||
/// </summary>
|
||||
/// <param name="connection">An instance of the <see cref="IWebSocketConnection"/> interface.</param>
|
||||
/// <param name="httpContext">The current http context.</param>
|
||||
/// <returns>Task.</returns>
|
||||
Task ProcessWebSocketConnectedAsync(IWebSocketConnection connection);
|
||||
Task ProcessWebSocketConnectedAsync(IWebSocketConnection connection, HttpContext httpContext);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,4 +14,4 @@ namespace MediaBrowser.Controller.Net
|
||||
|
||||
public long IntervalMs { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,17 +161,17 @@ namespace MediaBrowser.Controller.Persistence
|
||||
|
||||
int GetCount(InternalItemsQuery query);
|
||||
|
||||
QueryResult<(BaseItem, ItemCounts)> GetGenres(InternalItemsQuery query);
|
||||
QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetGenres(InternalItemsQuery query);
|
||||
|
||||
QueryResult<(BaseItem, ItemCounts)> GetMusicGenres(InternalItemsQuery query);
|
||||
QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetMusicGenres(InternalItemsQuery query);
|
||||
|
||||
QueryResult<(BaseItem, ItemCounts)> GetStudios(InternalItemsQuery query);
|
||||
QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetStudios(InternalItemsQuery query);
|
||||
|
||||
QueryResult<(BaseItem, ItemCounts)> GetArtists(InternalItemsQuery query);
|
||||
QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetArtists(InternalItemsQuery query);
|
||||
|
||||
QueryResult<(BaseItem, ItemCounts)> GetAlbumArtists(InternalItemsQuery query);
|
||||
QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetAlbumArtists(InternalItemsQuery query);
|
||||
|
||||
QueryResult<(BaseItem, ItemCounts)> GetAllArtists(InternalItemsQuery query);
|
||||
QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetAllArtists(InternalItemsQuery query);
|
||||
|
||||
List<string> GetMusicGenreNames();
|
||||
|
||||
|
||||
@@ -189,7 +189,7 @@ namespace MediaBrowser.Controller.Playlists
|
||||
return LibraryManager.GetItemList(new InternalItemsQuery(user)
|
||||
{
|
||||
Recursive = true,
|
||||
IncludeItemTypes = new[] { nameof(Audio) },
|
||||
IncludeItemTypes = new[] { BaseItemKind.Audio },
|
||||
GenreIds = new[] { musicGenre.Id },
|
||||
OrderBy = new[] { (ItemSortBy.AlbumArtist, SortOrder.Ascending), (ItemSortBy.Album, SortOrder.Ascending), (ItemSortBy.SortName, SortOrder.Ascending) },
|
||||
DtoOptions = options
|
||||
@@ -201,7 +201,7 @@ namespace MediaBrowser.Controller.Playlists
|
||||
return LibraryManager.GetItemList(new InternalItemsQuery(user)
|
||||
{
|
||||
Recursive = true,
|
||||
IncludeItemTypes = new[] { nameof(Audio) },
|
||||
IncludeItemTypes = new[] { BaseItemKind.Audio },
|
||||
ArtistIds = new[] { musicArtist.Id },
|
||||
OrderBy = new[] { (ItemSortBy.AlbumArtist, SortOrder.Ascending), (ItemSortBy.Album, SortOrder.Ascending), (ItemSortBy.SortName, SortOrder.Ascending) },
|
||||
DtoOptions = options
|
||||
|
||||
@@ -12,11 +12,11 @@ namespace MediaBrowser.Controller.Providers
|
||||
{
|
||||
private readonly IFileSystem _fileSystem;
|
||||
|
||||
private readonly ConcurrentDictionary<string, FileSystemMetadata[]> _cache = new (StringComparer.Ordinal);
|
||||
private readonly ConcurrentDictionary<string, FileSystemMetadata[]> _cache = new(StringComparer.Ordinal);
|
||||
|
||||
private readonly ConcurrentDictionary<string, FileSystemMetadata> _fileCache = new (StringComparer.Ordinal);
|
||||
private readonly ConcurrentDictionary<string, FileSystemMetadata> _fileCache = new(StringComparer.Ordinal);
|
||||
|
||||
private readonly ConcurrentDictionary<string, List<string>> _filePathCache = new (StringComparer.Ordinal);
|
||||
private readonly ConcurrentDictionary<string, List<string>> _filePathCache = new(StringComparer.Ordinal);
|
||||
|
||||
public DirectoryService(IFileSystem fileSystem)
|
||||
{
|
||||
@@ -25,7 +25,7 @@ namespace MediaBrowser.Controller.Providers
|
||||
|
||||
public FileSystemMetadata[] GetFileSystemEntries(string path)
|
||||
{
|
||||
return _cache.GetOrAdd(path, (p, fileSystem) => fileSystem.GetFileSystemEntries(p).ToArray(), _fileSystem);
|
||||
return _cache.GetOrAdd(path, static (p, fileSystem) => fileSystem.GetFileSystemEntries(p).ToArray(), _fileSystem);
|
||||
}
|
||||
|
||||
public List<FileSystemMetadata> GetFiles(string path)
|
||||
@@ -69,7 +69,7 @@ namespace MediaBrowser.Controller.Providers
|
||||
_filePathCache.TryRemove(path, out _);
|
||||
}
|
||||
|
||||
var filePaths = _filePathCache.GetOrAdd(path, (p, fileSystem) => fileSystem.GetFilePaths(p).ToList(), _fileSystem);
|
||||
var filePaths = _filePathCache.GetOrAdd(path, static (p, fileSystem) => fileSystem.GetFilePaths(p).ToList(), _fileSystem);
|
||||
|
||||
if (sort)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#nullable disable
|
||||
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Providers;
|
||||
|
||||
@@ -35,7 +33,7 @@ namespace MediaBrowser.Controller.Providers
|
||||
/// <summary>
|
||||
/// Gets the URL format string for this id.
|
||||
/// </summary>
|
||||
string UrlFormatString { get; }
|
||||
string? UrlFormatString { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this id supports a given item type.
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#nullable disable
|
||||
|
||||
#pragma warning disable CA1819, CS1591
|
||||
|
||||
using System;
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#nullable disable
|
||||
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Jellyfin.Extensions;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Model.Providers;
|
||||
|
||||
@@ -58,7 +59,7 @@ namespace MediaBrowser.Controller.Providers
|
||||
{
|
||||
if (RefreshPaths != null && RefreshPaths.Length > 0)
|
||||
{
|
||||
return RefreshPaths.Contains(item.Path ?? string.Empty, StringComparer.OrdinalIgnoreCase);
|
||||
return RefreshPaths.Contains(item.Path ?? string.Empty, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace MediaBrowser.Controller.Providers
|
||||
{
|
||||
// Images aren't always used so the allocation is a waste a lot of the time
|
||||
private List<LocalImageInfo> _images;
|
||||
private List<(string url, ImageType type)> _remoteImages;
|
||||
private List<(string Url, ImageType Type)> _remoteImages;
|
||||
|
||||
public MetadataResult()
|
||||
{
|
||||
@@ -27,9 +27,9 @@ namespace MediaBrowser.Controller.Providers
|
||||
set => _images = value;
|
||||
}
|
||||
|
||||
public List<(string url, ImageType type)> RemoteImages
|
||||
public List<(string Url, ImageType Type)> RemoteImages
|
||||
{
|
||||
get => _remoteImages ??= new List<(string url, ImageType type)>();
|
||||
get => _remoteImages ??= new List<(string Url, ImageType Type)>();
|
||||
set => _remoteImages = value;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,4 +20,4 @@
|
||||
/// </summary>
|
||||
Low = 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,12 +31,14 @@ namespace MediaBrowser.Controller.Subtitles
|
||||
/// <param name="video">The video.</param>
|
||||
/// <param name="language">Subtitle language.</param>
|
||||
/// <param name="isPerfectMatch">Require perfect match.</param>
|
||||
/// <param name="isAutomated">Request is automated.</param>
|
||||
/// <param name="cancellationToken">CancellationToken to use for the operation.</param>
|
||||
/// <returns>Subtitles, wrapped in task.</returns>
|
||||
Task<RemoteSubtitleInfo[]> SearchSubtitles(
|
||||
Video video,
|
||||
string language,
|
||||
bool? isPerfectMatch,
|
||||
bool isAutomated,
|
||||
CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -51,5 +51,7 @@ namespace MediaBrowser.Controller.Subtitles
|
||||
public string[] DisabledSubtitleFetchers { get; set; }
|
||||
|
||||
public string[] SubtitleFetcherOrder { get; set; }
|
||||
|
||||
public bool IsAutomated { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,11 +17,6 @@ namespace MediaBrowser.Controller.SyncPlay.GroupStates
|
||||
/// </remarks>
|
||||
public class PausedGroupState : AbstractGroupState
|
||||
{
|
||||
/// <summary>
|
||||
/// The logger.
|
||||
/// </summary>
|
||||
private readonly ILogger<PausedGroupState> _logger;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PausedGroupState"/> class.
|
||||
/// </summary>
|
||||
@@ -29,7 +24,6 @@ namespace MediaBrowser.Controller.SyncPlay.GroupStates
|
||||
public PausedGroupState(ILoggerFactory loggerFactory)
|
||||
: base(loggerFactory)
|
||||
{
|
||||
_logger = LoggerFactory.CreateLogger<PausedGroupState>();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -17,11 +17,6 @@ namespace MediaBrowser.Controller.SyncPlay.GroupStates
|
||||
/// </remarks>
|
||||
public class PlayingGroupState : AbstractGroupState
|
||||
{
|
||||
/// <summary>
|
||||
/// The logger.
|
||||
/// </summary>
|
||||
private readonly ILogger<PlayingGroupState> _logger;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PlayingGroupState"/> class.
|
||||
/// </summary>
|
||||
@@ -29,7 +24,6 @@ namespace MediaBrowser.Controller.SyncPlay.GroupStates
|
||||
public PlayingGroupState(ILoggerFactory loggerFactory)
|
||||
: base(loggerFactory)
|
||||
{
|
||||
_logger = LoggerFactory.CreateLogger<PlayingGroupState>();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Jellyfin.Extensions;
|
||||
using MediaBrowser.Model.SyncPlay;
|
||||
|
||||
namespace MediaBrowser.Controller.SyncPlay.Queue
|
||||
@@ -19,10 +20,16 @@ namespace MediaBrowser.Controller.SyncPlay.Queue
|
||||
private const int NoPlayingItemIndex = -1;
|
||||
|
||||
/// <summary>
|
||||
/// Random number generator used to shuffle lists.
|
||||
/// The sorted playlist.
|
||||
/// </summary>
|
||||
/// <value>The random number generator.</value>
|
||||
private readonly Random _randomNumberGenerator = new Random();
|
||||
/// <value>The sorted playlist, or play queue of the group.</value>
|
||||
private List<QueueItem> _sortedPlaylist = new List<QueueItem>();
|
||||
|
||||
/// <summary>
|
||||
/// The shuffled playlist.
|
||||
/// </summary>
|
||||
/// <value>The shuffled playlist, or play queue of the group.</value>
|
||||
private List<QueueItem> _shuffledPlaylist = new List<QueueItem>();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PlayQueueManager" /> class.
|
||||
@@ -56,18 +63,6 @@ namespace MediaBrowser.Controller.SyncPlay.Queue
|
||||
/// <value>The repeat mode.</value>
|
||||
public GroupRepeatMode RepeatMode { get; private set; } = GroupRepeatMode.RepeatNone;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the sorted playlist.
|
||||
/// </summary>
|
||||
/// <value>The sorted playlist, or play queue of the group.</value>
|
||||
private List<QueueItem> SortedPlaylist { get; set; } = new List<QueueItem>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the shuffled playlist.
|
||||
/// </summary>
|
||||
/// <value>The shuffled playlist, or play queue of the group.</value>
|
||||
private List<QueueItem> ShuffledPlaylist { get; set; } = new List<QueueItem>();
|
||||
|
||||
/// <summary>
|
||||
/// Checks if an item is playing.
|
||||
/// </summary>
|
||||
@@ -92,14 +87,14 @@ namespace MediaBrowser.Controller.SyncPlay.Queue
|
||||
/// <param name="items">The new items of the playlist.</param>
|
||||
public void SetPlaylist(IReadOnlyList<Guid> items)
|
||||
{
|
||||
SortedPlaylist.Clear();
|
||||
ShuffledPlaylist.Clear();
|
||||
_sortedPlaylist.Clear();
|
||||
_shuffledPlaylist.Clear();
|
||||
|
||||
SortedPlaylist = CreateQueueItemsFromArray(items);
|
||||
_sortedPlaylist = CreateQueueItemsFromArray(items);
|
||||
if (ShuffleMode.Equals(GroupShuffleMode.Shuffle))
|
||||
{
|
||||
ShuffledPlaylist = new List<QueueItem>(SortedPlaylist);
|
||||
Shuffle(ShuffledPlaylist);
|
||||
_shuffledPlaylist = new List<QueueItem>(_sortedPlaylist);
|
||||
_shuffledPlaylist.Shuffle();
|
||||
}
|
||||
|
||||
PlayingItemIndex = NoPlayingItemIndex;
|
||||
@@ -114,10 +109,10 @@ namespace MediaBrowser.Controller.SyncPlay.Queue
|
||||
{
|
||||
var newItems = CreateQueueItemsFromArray(items);
|
||||
|
||||
SortedPlaylist.AddRange(newItems);
|
||||
_sortedPlaylist.AddRange(newItems);
|
||||
if (ShuffleMode.Equals(GroupShuffleMode.Shuffle))
|
||||
{
|
||||
ShuffledPlaylist.AddRange(newItems);
|
||||
_shuffledPlaylist.AddRange(newItems);
|
||||
}
|
||||
|
||||
LastChange = DateTime.UtcNow;
|
||||
@@ -130,26 +125,26 @@ namespace MediaBrowser.Controller.SyncPlay.Queue
|
||||
{
|
||||
if (PlayingItemIndex == NoPlayingItemIndex)
|
||||
{
|
||||
ShuffledPlaylist = new List<QueueItem>(SortedPlaylist);
|
||||
Shuffle(ShuffledPlaylist);
|
||||
_shuffledPlaylist = new List<QueueItem>(_sortedPlaylist);
|
||||
_shuffledPlaylist.Shuffle();
|
||||
}
|
||||
else if (ShuffleMode.Equals(GroupShuffleMode.Sorted))
|
||||
{
|
||||
// First time shuffle.
|
||||
var playingItem = SortedPlaylist[PlayingItemIndex];
|
||||
ShuffledPlaylist = new List<QueueItem>(SortedPlaylist);
|
||||
ShuffledPlaylist.RemoveAt(PlayingItemIndex);
|
||||
Shuffle(ShuffledPlaylist);
|
||||
ShuffledPlaylist.Insert(0, playingItem);
|
||||
var playingItem = _sortedPlaylist[PlayingItemIndex];
|
||||
_shuffledPlaylist = new List<QueueItem>(_sortedPlaylist);
|
||||
_shuffledPlaylist.RemoveAt(PlayingItemIndex);
|
||||
_shuffledPlaylist.Shuffle();
|
||||
_shuffledPlaylist.Insert(0, playingItem);
|
||||
PlayingItemIndex = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Re-shuffle playlist.
|
||||
var playingItem = ShuffledPlaylist[PlayingItemIndex];
|
||||
ShuffledPlaylist.RemoveAt(PlayingItemIndex);
|
||||
Shuffle(ShuffledPlaylist);
|
||||
ShuffledPlaylist.Insert(0, playingItem);
|
||||
var playingItem = _shuffledPlaylist[PlayingItemIndex];
|
||||
_shuffledPlaylist.RemoveAt(PlayingItemIndex);
|
||||
_shuffledPlaylist.Shuffle();
|
||||
_shuffledPlaylist.Insert(0, playingItem);
|
||||
PlayingItemIndex = 0;
|
||||
}
|
||||
|
||||
@@ -164,11 +159,11 @@ namespace MediaBrowser.Controller.SyncPlay.Queue
|
||||
{
|
||||
if (PlayingItemIndex != NoPlayingItemIndex)
|
||||
{
|
||||
var playingItem = ShuffledPlaylist[PlayingItemIndex];
|
||||
PlayingItemIndex = SortedPlaylist.IndexOf(playingItem);
|
||||
var playingItem = _shuffledPlaylist[PlayingItemIndex];
|
||||
PlayingItemIndex = _sortedPlaylist.IndexOf(playingItem);
|
||||
}
|
||||
|
||||
ShuffledPlaylist.Clear();
|
||||
_shuffledPlaylist.Clear();
|
||||
|
||||
ShuffleMode = GroupShuffleMode.Sorted;
|
||||
LastChange = DateTime.UtcNow;
|
||||
@@ -181,16 +176,16 @@ namespace MediaBrowser.Controller.SyncPlay.Queue
|
||||
public void ClearPlaylist(bool clearPlayingItem)
|
||||
{
|
||||
var playingItem = GetPlayingItem();
|
||||
SortedPlaylist.Clear();
|
||||
ShuffledPlaylist.Clear();
|
||||
_sortedPlaylist.Clear();
|
||||
_shuffledPlaylist.Clear();
|
||||
LastChange = DateTime.UtcNow;
|
||||
|
||||
if (!clearPlayingItem && playingItem != null)
|
||||
{
|
||||
SortedPlaylist.Add(playingItem);
|
||||
_sortedPlaylist.Add(playingItem);
|
||||
if (ShuffleMode.Equals(GroupShuffleMode.Shuffle))
|
||||
{
|
||||
ShuffledPlaylist.Add(playingItem);
|
||||
_shuffledPlaylist.Add(playingItem);
|
||||
}
|
||||
|
||||
PlayingItemIndex = 0;
|
||||
@@ -212,14 +207,14 @@ namespace MediaBrowser.Controller.SyncPlay.Queue
|
||||
if (ShuffleMode.Equals(GroupShuffleMode.Shuffle))
|
||||
{
|
||||
var playingItem = GetPlayingItem();
|
||||
var sortedPlayingItemIndex = SortedPlaylist.IndexOf(playingItem);
|
||||
var sortedPlayingItemIndex = _sortedPlaylist.IndexOf(playingItem);
|
||||
// Append items to sorted and shuffled playlist as they are.
|
||||
SortedPlaylist.InsertRange(sortedPlayingItemIndex + 1, newItems);
|
||||
ShuffledPlaylist.InsertRange(PlayingItemIndex + 1, newItems);
|
||||
_sortedPlaylist.InsertRange(sortedPlayingItemIndex + 1, newItems);
|
||||
_shuffledPlaylist.InsertRange(PlayingItemIndex + 1, newItems);
|
||||
}
|
||||
else
|
||||
{
|
||||
SortedPlaylist.InsertRange(PlayingItemIndex + 1, newItems);
|
||||
_sortedPlaylist.InsertRange(PlayingItemIndex + 1, newItems);
|
||||
}
|
||||
|
||||
LastChange = DateTime.UtcNow;
|
||||
@@ -298,8 +293,8 @@ namespace MediaBrowser.Controller.SyncPlay.Queue
|
||||
{
|
||||
var playingItem = GetPlayingItem();
|
||||
|
||||
SortedPlaylist.RemoveAll(item => playlistItemIds.Contains(item.PlaylistItemId));
|
||||
ShuffledPlaylist.RemoveAll(item => playlistItemIds.Contains(item.PlaylistItemId));
|
||||
_sortedPlaylist.RemoveAll(item => playlistItemIds.Contains(item.PlaylistItemId));
|
||||
_shuffledPlaylist.RemoveAll(item => playlistItemIds.Contains(item.PlaylistItemId));
|
||||
|
||||
LastChange = DateTime.UtcNow;
|
||||
|
||||
@@ -313,7 +308,7 @@ namespace MediaBrowser.Controller.SyncPlay.Queue
|
||||
{
|
||||
// Was first element, picking next if available.
|
||||
// Default to no playing item otherwise.
|
||||
PlayingItemIndex = SortedPlaylist.Count > 0 ? 0 : NoPlayingItemIndex;
|
||||
PlayingItemIndex = _sortedPlaylist.Count > 0 ? 0 : NoPlayingItemIndex;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -363,8 +358,8 @@ namespace MediaBrowser.Controller.SyncPlay.Queue
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
SortedPlaylist.Clear();
|
||||
ShuffledPlaylist.Clear();
|
||||
_sortedPlaylist.Clear();
|
||||
_shuffledPlaylist.Clear();
|
||||
PlayingItemIndex = NoPlayingItemIndex;
|
||||
ShuffleMode = GroupShuffleMode.Sorted;
|
||||
RepeatMode = GroupRepeatMode.RepeatNone;
|
||||
@@ -460,7 +455,7 @@ namespace MediaBrowser.Controller.SyncPlay.Queue
|
||||
}
|
||||
|
||||
PlayingItemIndex++;
|
||||
if (PlayingItemIndex >= SortedPlaylist.Count)
|
||||
if (PlayingItemIndex >= _sortedPlaylist.Count)
|
||||
{
|
||||
if (RepeatMode.Equals(GroupRepeatMode.RepeatAll))
|
||||
{
|
||||
@@ -468,7 +463,7 @@ namespace MediaBrowser.Controller.SyncPlay.Queue
|
||||
}
|
||||
else
|
||||
{
|
||||
PlayingItemIndex = SortedPlaylist.Count - 1;
|
||||
PlayingItemIndex = _sortedPlaylist.Count - 1;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -494,7 +489,7 @@ namespace MediaBrowser.Controller.SyncPlay.Queue
|
||||
{
|
||||
if (RepeatMode.Equals(GroupRepeatMode.RepeatAll))
|
||||
{
|
||||
PlayingItemIndex = SortedPlaylist.Count - 1;
|
||||
PlayingItemIndex = _sortedPlaylist.Count - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -507,23 +502,6 @@ namespace MediaBrowser.Controller.SyncPlay.Queue
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shuffles a given list.
|
||||
/// </summary>
|
||||
/// <param name="list">The list to shuffle.</param>
|
||||
private void Shuffle<T>(IList<T> list)
|
||||
{
|
||||
int n = list.Count;
|
||||
while (n > 1)
|
||||
{
|
||||
n--;
|
||||
int k = _randomNumberGenerator.Next(n + 1);
|
||||
T value = list[k];
|
||||
list[k] = list[n];
|
||||
list[n] = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a list from the array of items. Each item is given an unique playlist identifier.
|
||||
/// </summary>
|
||||
@@ -548,11 +526,11 @@ namespace MediaBrowser.Controller.SyncPlay.Queue
|
||||
{
|
||||
if (ShuffleMode.Equals(GroupShuffleMode.Shuffle))
|
||||
{
|
||||
return ShuffledPlaylist;
|
||||
return _shuffledPlaylist;
|
||||
}
|
||||
else
|
||||
{
|
||||
return SortedPlaylist;
|
||||
return _sortedPlaylist;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -568,11 +546,11 @@ namespace MediaBrowser.Controller.SyncPlay.Queue
|
||||
}
|
||||
else if (ShuffleMode.Equals(GroupShuffleMode.Shuffle))
|
||||
{
|
||||
return ShuffledPlaylist[PlayingItemIndex];
|
||||
return _shuffledPlaylist[PlayingItemIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
return SortedPlaylist[PlayingItemIndex];
|
||||
return _sortedPlaylist[PlayingItemIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user