diff options
Diffstat (limited to 'MediaBrowser.Controller')
122 files changed, 1096 insertions, 429 deletions
diff --git a/MediaBrowser.Controller/Authentication/IAuthenticationProvider.cs b/MediaBrowser.Controller/Authentication/IAuthenticationProvider.cs index c0324a384..15c902777 100644 --- a/MediaBrowser.Controller/Authentication/IAuthenticationProvider.cs +++ b/MediaBrowser.Controller/Authentication/IAuthenticationProvider.cs @@ -7,12 +7,12 @@ namespace MediaBrowser.Controller.Authentication public interface IAuthenticationProvider { string Name { get; } + bool IsEnabled { get; } + Task<ProviderAuthenticationResult> Authenticate(string username, string password); bool HasPassword(User user); Task ChangePassword(User user, string newPassword); - void ChangeEasyPassword(User user, string newPassword, string newPasswordHash); - string GetEasyPasswordHash(User user); } public interface IRequiresResolvedUser @@ -28,6 +28,7 @@ namespace MediaBrowser.Controller.Authentication public class ProviderAuthenticationResult { public string Username { get; set; } + public string DisplayName { get; set; } } } diff --git a/MediaBrowser.Controller/Authentication/IPasswordResetProvider.cs b/MediaBrowser.Controller/Authentication/IPasswordResetProvider.cs index d9b814f69..693df80ac 100644 --- a/MediaBrowser.Controller/Authentication/IPasswordResetProvider.cs +++ b/MediaBrowser.Controller/Authentication/IPasswordResetProvider.cs @@ -8,7 +8,9 @@ namespace MediaBrowser.Controller.Authentication public interface IPasswordResetProvider { string Name { get; } + bool IsEnabled { get; } + Task<ForgotPasswordResult> StartForgotPasswordProcess(User user, bool isInNetwork); Task<PinRedeemResult> RedeemPasswordResetPin(string pin); } @@ -16,6 +18,7 @@ namespace MediaBrowser.Controller.Authentication public class PasswordPinCreationResult { public string PinFile { get; set; } + public DateTime ExpirationDate { get; set; } } } diff --git a/MediaBrowser.Controller/Channels/ChannelItemInfo.cs b/MediaBrowser.Controller/Channels/ChannelItemInfo.cs index aff68883b..00d4d9cb3 100644 --- a/MediaBrowser.Controller/Channels/ChannelItemInfo.cs +++ b/MediaBrowser.Controller/Channels/ChannelItemInfo.cs @@ -24,7 +24,9 @@ namespace MediaBrowser.Controller.Channels public string Overview { get; set; } public List<string> Genres { get; set; } + public List<string> Studios { get; set; } + public List<string> Tags { get; set; } public List<PersonInfo> People { get; set; } @@ -34,26 +36,33 @@ namespace MediaBrowser.Controller.Channels public long? RunTimeTicks { get; set; } public string ImageUrl { get; set; } + public string OriginalTitle { get; set; } public ChannelMediaType MediaType { get; set; } + public ChannelFolderType FolderType { get; set; } public ChannelMediaContentType ContentType { get; set; } + public ExtraType ExtraType { get; set; } + public List<TrailerType> TrailerTypes { get; set; } public Dictionary<string, string> ProviderIds { get; set; } public DateTime? PremiereDate { get; set; } + public int? ProductionYear { get; set; } public DateTime? DateCreated { get; set; } public DateTime? StartDate { get; set; } + public DateTime? EndDate { get; set; } public int? IndexNumber { get; set; } + public int? ParentIndexNumber { get; set; } public List<MediaSourceInfo> MediaSources { get; set; } @@ -63,7 +72,9 @@ namespace MediaBrowser.Controller.Channels public List<string> Artists { get; set; } public List<string> AlbumArtists { get; set; } + public bool IsLiveStream { get; set; } + public string Etag { get; set; } public ChannelItemInfo() diff --git a/MediaBrowser.Controller/Channels/ISearchableChannel.cs b/MediaBrowser.Controller/Channels/ISearchableChannel.cs index d5b76a160..48043ad7a 100644 --- a/MediaBrowser.Controller/Channels/ISearchableChannel.cs +++ b/MediaBrowser.Controller/Channels/ISearchableChannel.cs @@ -35,12 +35,10 @@ namespace MediaBrowser.Controller.Channels public interface IDisableMediaSourceDisplay { - } public interface ISupportsMediaProbe { - } public interface IHasFolderAttributes diff --git a/MediaBrowser.Controller/Channels/InternalChannelFeatures.cs b/MediaBrowser.Controller/Channels/InternalChannelFeatures.cs index 60455e68a..1f4a26064 100644 --- a/MediaBrowser.Controller/Channels/InternalChannelFeatures.cs +++ b/MediaBrowser.Controller/Channels/InternalChannelFeatures.cs @@ -18,7 +18,7 @@ namespace MediaBrowser.Controller.Channels public List<ChannelMediaContentType> ContentTypes { get; set; } /// <summary> - /// Represents the maximum number of records the channel allows retrieving at a time + /// Represents the maximum number of records the channel allows retrieving at a time. /// </summary> public int? MaxPageSize { get; set; } diff --git a/MediaBrowser.Controller/Collections/CollectionCreationOptions.cs b/MediaBrowser.Controller/Collections/CollectionCreationOptions.cs index 51fe4ce29..1e7549d2b 100644 --- a/MediaBrowser.Controller/Collections/CollectionCreationOptions.cs +++ b/MediaBrowser.Controller/Collections/CollectionCreationOptions.cs @@ -15,6 +15,7 @@ namespace MediaBrowser.Controller.Collections public Dictionary<string, string> ProviderIds { get; set; } public string[] ItemIdList { get; set; } + public Guid[] UserIds { get; set; } public CollectionCreationOptions() diff --git a/MediaBrowser.Controller/Configuration/IServerConfigurationManager.cs b/MediaBrowser.Controller/Configuration/IServerConfigurationManager.cs index 6660743e6..43ad04dba 100644 --- a/MediaBrowser.Controller/Configuration/IServerConfigurationManager.cs +++ b/MediaBrowser.Controller/Configuration/IServerConfigurationManager.cs @@ -4,7 +4,7 @@ using MediaBrowser.Model.Configuration; namespace MediaBrowser.Controller.Configuration { /// <summary> - /// Interface IServerConfigurationManager + /// Interface IServerConfigurationManager. /// </summary> public interface IServerConfigurationManager : IConfigurationManager { @@ -19,7 +19,5 @@ namespace MediaBrowser.Controller.Configuration /// </summary> /// <value>The configuration.</value> ServerConfiguration Configuration { get; } - - bool SetOptimalValues(); } } diff --git a/MediaBrowser.Controller/Drawing/IImageProcessor.cs b/MediaBrowser.Controller/Drawing/IImageProcessor.cs index f1873d539..69d799165 100644 --- a/MediaBrowser.Controller/Drawing/IImageProcessor.cs +++ b/MediaBrowser.Controller/Drawing/IImageProcessor.cs @@ -10,7 +10,7 @@ using MediaBrowser.Model.Entities; namespace MediaBrowser.Controller.Drawing { /// <summary> - /// Interface IImageProcessor + /// Interface IImageProcessor. /// </summary> public interface IImageProcessor { @@ -30,7 +30,7 @@ namespace MediaBrowser.Controller.Drawing /// Gets the dimensions of the image. /// </summary> /// <param name="path">Path to the image file.</param> - /// <returns>ImageDimensions</returns> + /// <returns>ImageDimensions.</returns> ImageDimensions GetImageDimensions(string path); /// <summary> @@ -38,14 +38,14 @@ namespace MediaBrowser.Controller.Drawing /// </summary> /// <param name="item">The base item.</param> /// <param name="info">The information.</param> - /// <returns>ImageDimensions</returns> + /// <returns>ImageDimensions.</returns> ImageDimensions GetImageDimensions(BaseItem item, ItemImageInfo info); /// <summary> /// Gets the blurhash of the image. /// </summary> /// <param name="path">Path to the image file.</param> - /// <returns>BlurHash</returns> + /// <returns>BlurHash.</returns> string GetImageBlurHash(string path); /// <summary> diff --git a/MediaBrowser.Controller/Drawing/ImageHelper.cs b/MediaBrowser.Controller/Drawing/ImageHelper.cs index c87a248b5..e1273fe7f 100644 --- a/MediaBrowser.Controller/Drawing/ImageHelper.cs +++ b/MediaBrowser.Controller/Drawing/ImageHelper.cs @@ -16,6 +16,7 @@ namespace MediaBrowser.Controller.Drawing return newSize; } + return GetSizeEstimate(options); } diff --git a/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs b/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs index 870e0278e..31d2c1bd4 100644 --- a/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs +++ b/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs @@ -15,6 +15,7 @@ namespace MediaBrowser.Controller.Drawing } public Guid ItemId { get; set; } + public BaseItem Item { get; set; } public ItemImageInfo Image { get; set; } @@ -38,12 +39,15 @@ namespace MediaBrowser.Controller.Drawing public bool AddPlayedIndicator { get; set; } public int? UnplayedCount { get; set; } + public int? Blur { get; set; } public double PercentPlayed { get; set; } public string BackgroundColor { get; set; } + public string ForegroundLayer { get; set; } + public bool RequiresAutoOrientation { get; set; } private bool HasDefaultOptions(string originalImagePath) @@ -73,14 +77,17 @@ namespace MediaBrowser.Controller.Drawing { return false; } + if (Height.HasValue && !sizeValue.Height.Equals(Height.Value)) { return false; } + if (MaxWidth.HasValue && sizeValue.Width > MaxWidth.Value) { return false; } + if (MaxHeight.HasValue && sizeValue.Height > MaxHeight.Value) { return false; diff --git a/MediaBrowser.Controller/Dto/DtoOptions.cs b/MediaBrowser.Controller/Dto/DtoOptions.cs index cdaf95f5c..cf301f1e4 100644 --- a/MediaBrowser.Controller/Dto/DtoOptions.cs +++ b/MediaBrowser.Controller/Dto/DtoOptions.cs @@ -14,11 +14,17 @@ namespace MediaBrowser.Controller.Dto }; public ItemFields[] Fields { get; set; } + public ImageType[] ImageTypes { get; set; } + public int ImageTypeLimit { get; set; } + public bool EnableImages { get; set; } + public bool AddProgramRecordingInfo { get; set; } + public bool EnableUserData { get; set; } + public bool AddCurrentProgram { get; set; } public DtoOptions() diff --git a/MediaBrowser.Controller/Dto/IDtoService.cs b/MediaBrowser.Controller/Dto/IDtoService.cs index 56e6c47c4..0dadc283e 100644 --- a/MediaBrowser.Controller/Dto/IDtoService.cs +++ b/MediaBrowser.Controller/Dto/IDtoService.cs @@ -7,7 +7,7 @@ using MediaBrowser.Model.Querying; namespace MediaBrowser.Controller.Dto { /// <summary> - /// Interface IDtoService + /// Interface IDtoService. /// </summary> public interface IDtoService { diff --git a/MediaBrowser.Controller/Entities/AggregateFolder.cs b/MediaBrowser.Controller/Entities/AggregateFolder.cs index 54540e892..e1c800e61 100644 --- a/MediaBrowser.Controller/Entities/AggregateFolder.cs +++ b/MediaBrowser.Controller/Entities/AggregateFolder.cs @@ -35,7 +35,7 @@ namespace MediaBrowser.Controller.Entities public override bool SupportsPlayedStatus => false; /// <summary> - /// The _virtual children + /// The _virtual children. /// </summary> private readonly ConcurrentBag<BaseItem> _virtualChildren = new ConcurrentBag<BaseItem>(); @@ -195,6 +195,7 @@ namespace MediaBrowser.Controller.Entities return child; } } + return null; } } diff --git a/MediaBrowser.Controller/Entities/Audio/Audio.cs b/MediaBrowser.Controller/Entities/Audio/Audio.cs index a8ea2157d..98f802b5d 100644 --- a/MediaBrowser.Controller/Entities/Audio/Audio.cs +++ b/MediaBrowser.Controller/Entities/Audio/Audio.cs @@ -11,7 +11,7 @@ using MediaBrowser.Model.Entities; namespace MediaBrowser.Controller.Entities.Audio { /// <summary> - /// Class Audio + /// Class Audio. /// </summary> public class Audio : BaseItem, IHasAlbumArtist, @@ -93,6 +93,7 @@ namespace MediaBrowser.Controller.Entities.Audio { songKey = ParentIndexNumber.Value.ToString("0000") + "-" + songKey; } + songKey += Name; if (!string.IsNullOrEmpty(Album)) @@ -117,6 +118,7 @@ namespace MediaBrowser.Controller.Entities.Audio { return UnratedItem.Music; } + return base.GetBlockUnratedType(); } diff --git a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs index f7b2f9549..5a1ddeece 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs @@ -15,7 +15,7 @@ using MetadataProvider = MediaBrowser.Model.Entities.MetadataProvider; namespace MediaBrowser.Controller.Entities.Audio { /// <summary> - /// Class MusicAlbum + /// Class MusicAlbum. /// </summary> public class MusicAlbum : Folder, IHasAlbumArtist, IHasArtist, IHasMusicGenres, IHasLookupInfo<AlbumInfo>, IMetadataContainer { @@ -56,6 +56,7 @@ namespace MediaBrowser.Controller.Entities.Audio { return LibraryManager.GetArtist(name, options); } + return null; } diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs index 63db3cfab..ea5c41caa 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs @@ -15,7 +15,7 @@ using MetadataProvider = MediaBrowser.Model.Entities.MetadataProvider; namespace MediaBrowser.Controller.Entities.Audio { /// <summary> - /// Class MusicArtist + /// Class MusicArtist. /// </summary> public class MusicArtist : Folder, IItemByName, IHasMusicGenres, IHasDualAccess, IHasLookupInfo<ArtistInfo> { @@ -111,7 +111,7 @@ namespace MediaBrowser.Controller.Entities.Audio /// <summary> /// Returns the folder containing the item. - /// If the item is a folder, it returns the folder itself + /// If the item is a folder, it returns the folder itself. /// </summary> /// <value>The containing folder path.</value> [JsonIgnore] @@ -135,6 +135,7 @@ namespace MediaBrowser.Controller.Entities.Audio list.Add("Artist-" + (item.Name ?? string.Empty).RemoveDiacritics()); return list; } + public override string CreatePresentationUniqueKey() { return "Artist-" + (Name ?? string.Empty).RemoveDiacritics(); @@ -201,7 +202,7 @@ namespace MediaBrowser.Controller.Entities.Audio } /// <summary> - /// This is called before any metadata refresh and returns true or false indicating if changes were made + /// This is called before any metadata refresh and returns true or false indicating if changes were made. /// </summary> public override bool BeforeMetadataRefresh(bool replaceAllMetdata) { diff --git a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs index 537e9630b..4f6aa0776 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs @@ -7,7 +7,7 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Controller.Entities.Audio { /// <summary> - /// Class MusicGenre + /// Class MusicGenre. /// </summary> public class MusicGenre : BaseItem, IItemByName { @@ -18,6 +18,7 @@ namespace MediaBrowser.Controller.Entities.Audio list.Insert(0, GetType().Name + "-" + (Name ?? string.Empty).RemoveDiacritics()); return list; } + public override string CreatePresentationUniqueKey() { return GetUserDataKeys()[0]; @@ -34,7 +35,7 @@ namespace MediaBrowser.Controller.Entities.Audio /// <summary> /// Returns the folder containing the item. - /// If the item is a folder, it returns the folder itself + /// If the item is a folder, it returns the folder itself. /// </summary> /// <value>The containing folder path.</value> [JsonIgnore] @@ -94,11 +95,12 @@ namespace MediaBrowser.Controller.Entities.Audio Logger.LogDebug("{0} path has changed from {1} to {2}", GetType().Name, Path, newPath); return true; } + return base.RequiresRefresh(); } /// <summary> - /// This is called before any metadata refresh and returns true or false indicating if changes were made + /// This is called before any metadata refresh and returns true or false indicating if changes were made. /// </summary> public override bool BeforeMetadataRefresh(bool replaceAllMetdata) { diff --git a/MediaBrowser.Controller/Entities/AudioBook.cs b/MediaBrowser.Controller/Entities/AudioBook.cs index 4adaf4c6e..11ff8a257 100644 --- a/MediaBrowser.Controller/Entities/AudioBook.cs +++ b/MediaBrowser.Controller/Entities/AudioBook.cs @@ -24,10 +24,12 @@ namespace MediaBrowser.Controller.Entities { return SeriesName; } + public string FindSeriesName() { return SeriesName; } + public string FindSeriesPresentationUniqueKey() { return SeriesPresentationUniqueKey; diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 0eabb4b7a..90f62e153 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -31,12 +31,12 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Controller.Entities { /// <summary> - /// Class BaseItem + /// Class BaseItem. /// </summary> public abstract class BaseItem : IHasProviderIds, IHasLookupInfo<ItemLookupInfo>, IEquatable<BaseItem> { /// <summary> - /// The supported image extensions + /// The supported image extensions. /// </summary> public static readonly string[] SupportedImageExtensions = new[] { ".png", ".jpg", ".jpeg", ".tbn", ".gif" }; @@ -75,7 +75,7 @@ namespace MediaBrowser.Controller.Entities public static char SlugChar = '-'; /// <summary> - /// The trailer folder name + /// The trailer folder name. /// </summary> public const string TrailerFolderName = "trailers"; public const string ThemeSongsFolderName = "theme-music"; @@ -108,6 +108,7 @@ namespace MediaBrowser.Controller.Entities public string PreferredMetadataLanguage { get; set; } public long? Size { get; set; } + public string Container { get; set; } [JsonIgnore] @@ -243,7 +244,7 @@ namespace MediaBrowser.Controller.Entities /// <summary> /// Returns the folder containing the item. - /// If the item is a folder, it returns the folder itself + /// If the item is a folder, it returns the folder itself. /// </summary> [JsonIgnore] public virtual string ContainingFolderPath @@ -267,7 +268,7 @@ namespace MediaBrowser.Controller.Entities public string ServiceName { get; set; } /// <summary> - /// If this content came from an external service, the id of the content on that service + /// If this content came from an external service, the id of the content on that service. /// </summary> [JsonIgnore] public string ExternalId { get; set; } @@ -300,7 +301,7 @@ namespace MediaBrowser.Controller.Entities { get { - //if (IsOffline) + // if (IsOffline) //{ // return LocationType.Offline; //} @@ -411,7 +412,7 @@ namespace MediaBrowser.Controller.Entities } /// <summary> - /// This is just a helper for convenience + /// This is just a helper for convenience. /// </summary> /// <value>The primary image path.</value> [JsonIgnore] @@ -448,6 +449,7 @@ namespace MediaBrowser.Controller.Entities // hack alert return true; } + if (SourceType == SourceType.Channel) { // hack alert @@ -556,18 +558,26 @@ namespace MediaBrowser.Controller.Entities public DateTime DateLastRefreshed { get; set; } /// <summary> - /// The logger + /// The logger. /// </summary> - public static ILoggerFactory LoggerFactory { get; set; } public static ILogger<BaseItem> Logger { get; set; } + public static ILibraryManager LibraryManager { get; set; } + public static IServerConfigurationManager ConfigurationManager { get; set; } + public static IProviderManager ProviderManager { get; set; } + public static ILocalizationManager LocalizationManager { get; set; } + public static IItemRepository ItemRepository { get; set; } + public static IFileSystem FileSystem { get; set; } + public static IUserDataManager UserDataManager { get; set; } + public static IChannelManager ChannelManager { get; set; } + public static IMediaSourceManager MediaSourceManager { get; set; } /// <summary> @@ -603,7 +613,7 @@ namespace MediaBrowser.Controller.Entities { if (!IsFileProtocol) { - return new string[] { }; + return Array.Empty<string>(); } return new[] { Path }; @@ -644,8 +654,10 @@ namespace MediaBrowser.Controller.Entities _sortName = CreateSortName(); } } + return _sortName; } + set => _sortName = value; } @@ -676,7 +688,10 @@ namespace MediaBrowser.Controller.Entities /// <returns>System.String.</returns> protected virtual string CreateSortName() { - if (Name == null) return null; //some items may not have name filled in properly + if (Name == null) + { + return null; // some items may not have name filled in properly + } if (!EnableAlphaNumericSorting) { @@ -736,7 +751,7 @@ namespace MediaBrowser.Controller.Entities builder.Append(chunkBuilder); } - //logger.LogDebug("ModifySortChunks Start: {0} End: {1}", name, builder.ToString()); + // logger.LogDebug("ModifySortChunks Start: {0} End: {1}", name, builder.ToString()); return builder.ToString().RemoveDiacritics(); } @@ -767,7 +782,6 @@ namespace MediaBrowser.Controller.Entities get => GetParent() as Folder; set { - } } @@ -800,7 +814,7 @@ namespace MediaBrowser.Controller.Entities } /// <summary> - /// Finds a parent of a given type + /// Finds a parent of a given type. /// </summary> /// <typeparam name="T"></typeparam> /// <returns>``0.</returns> @@ -815,6 +829,7 @@ namespace MediaBrowser.Controller.Entities return item; } } + return null; } @@ -838,6 +853,7 @@ namespace MediaBrowser.Controller.Entities { return null; } + return LibraryManager.GetItemById(id); } } @@ -1011,7 +1027,7 @@ namespace MediaBrowser.Controller.Entities return PlayAccess.None; } - //if (!user.IsParentalScheduleAllowed()) + // if (!user.IsParentalScheduleAllowed()) //{ // return PlayAccess.None; //} @@ -1064,7 +1080,6 @@ namespace MediaBrowser.Controller.Entities } return 1; - }).ThenBy(i => i.Video3DFormat.HasValue ? 1 : 0) .ThenByDescending(i => { @@ -1247,8 +1262,7 @@ namespace MediaBrowser.Controller.Entities // Support plex/xbmc convention files.AddRange(fileSystemChildren - .Where(i => !i.IsDirectory && string.Equals(FileSystem.GetFileNameWithoutExtension(i), ThemeSongFilename, StringComparison.OrdinalIgnoreCase)) - ); + .Where(i => !i.IsDirectory && string.Equals(FileSystem.GetFileNameWithoutExtension(i), ThemeSongFilename, StringComparison.OrdinalIgnoreCase))); return LibraryManager.ResolvePaths(files, directoryService, null, new LibraryOptions()) .OfType<Audio.Audio>() @@ -1347,20 +1361,18 @@ namespace MediaBrowser.Controller.Entities protected virtual void TriggerOnRefreshStart() { - } protected virtual void TriggerOnRefreshComplete() { - } /// <summary> - /// Overrides the base implementation to refresh metadata for local trailers + /// Overrides the base implementation to refresh metadata for local trailers. /// </summary> /// <param name="options">The options.</param> /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>true if a provider reports we changed</returns> + /// <returns>true if a provider reports we changed.</returns> public async Task<ItemUpdateType> RefreshMetadata(MetadataRefreshOptions options, CancellationToken cancellationToken) { TriggerOnRefreshStart(); @@ -1758,7 +1770,7 @@ namespace MediaBrowser.Controller.Entities } /// <summary> - /// Determines if a given user has access to this item + /// Determines if a given user has access to this item. /// </summary> /// <param name="user">The user.</param> /// <returns><c>true</c> if [is parental allowed] [the specified user]; otherwise, <c>false</c>.</returns> @@ -2064,7 +2076,7 @@ namespace MediaBrowser.Controller.Entities public virtual bool EnableRememberingTrackSelections => true; /// <summary> - /// Adds a studio to the item + /// Adds a studio to the item. /// </summary> /// <param name="name">The name.</param> /// <exception cref="ArgumentNullException"></exception> @@ -2100,7 +2112,7 @@ namespace MediaBrowser.Controller.Entities } /// <summary> - /// Adds a genre to the item + /// Adds a genre to the item. /// </summary> /// <param name="name">The name.</param> /// <exception cref="ArgumentNullException"></exception> @@ -2175,7 +2187,7 @@ namespace MediaBrowser.Controller.Entities var data = UserDataManager.GetUserData(user, this); - //I think it is okay to do this here. + // I think it is okay to do this here. // if this is only called when a user is manually forcing something to un-played // then it probably is what we want to do... data.PlayCount = 0; @@ -2195,7 +2207,7 @@ namespace MediaBrowser.Controller.Entities } /// <summary> - /// Gets an image + /// Gets an image. /// </summary> /// <param name="type">The type.</param> /// <param name="imageIndex">Index of the image.</param> @@ -2511,7 +2523,7 @@ namespace MediaBrowser.Controller.Entities } /// <summary> - /// Gets the file system path to delete when the item is to be deleted + /// Gets the file system path to delete when the item is to be deleted. /// </summary> /// <returns></returns> public virtual IEnumerable<FileSystemMetadata> GetDeletePaths() @@ -2760,8 +2772,8 @@ namespace MediaBrowser.Controller.Entities newOptions.ForceSave = true; } - //var parentId = Id; - //if (!video.IsOwnedItem || video.ParentId != parentId) + // var parentId = Id; + // if (!video.IsOwnedItem || video.ParentId != parentId) //{ // video.IsOwnedItem = true; // video.ParentId = parentId; @@ -2937,9 +2949,13 @@ namespace MediaBrowser.Controller.Entities 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 bool IsHD => Height >= 720; diff --git a/MediaBrowser.Controller/Entities/BasePluginFolder.cs b/MediaBrowser.Controller/Entities/BasePluginFolder.cs index 62d172fcc..106385bc6 100644 --- a/MediaBrowser.Controller/Entities/BasePluginFolder.cs +++ b/MediaBrowser.Controller/Entities/BasePluginFolder.cs @@ -27,7 +27,7 @@ namespace MediaBrowser.Controller.Entities [JsonIgnore] public override bool SupportsPeople => false; - //public override double? GetDefaultPrimaryImageAspectRatio() + // public override double? GetDefaultPrimaryImageAspectRatio() //{ // double value = 16; // value /= 9; diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs index e5adf88d1..5c6a9d2a2 100644 --- a/MediaBrowser.Controller/Entities/CollectionFolder.cs +++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs @@ -18,12 +18,14 @@ namespace MediaBrowser.Controller.Entities { /// <summary> /// Specialized Folder class that points to a subset of the physical folders in the system. - /// It is created from the user-specific folders within the system root + /// It is created from the user-specific folders within the system root. /// </summary> public class CollectionFolder : Folder, ICollectionFolder { public static IXmlSerializer XmlSerializer { get; set; } + public static IJsonSerializer JsonSerializer { get; set; } + public static IServerApplicationHost ApplicationHost { get; set; } public CollectionFolder() @@ -140,7 +142,7 @@ namespace MediaBrowser.Controller.Entities } /// <summary> - /// Allow different display preferences for each collection folder + /// Allow different display preferences for each collection folder. /// </summary> /// <value>The display prefs id.</value> [JsonIgnore] @@ -155,6 +157,7 @@ namespace MediaBrowser.Controller.Entities } public string[] PhysicalLocationsList { get; set; } + public Guid[] PhysicalFolderIds { get; set; } protected override FileSystemMetadata[] GetFileSystemChildren(IDirectoryService directoryService) @@ -222,7 +225,7 @@ namespace MediaBrowser.Controller.Entities return null; } - return (totalProgresses / foldersWithProgress); + return totalProgresses / foldersWithProgress; } protected override bool RefreshLinkedChildren(IEnumerable<FileSystemMetadata> fileSystemChildren) diff --git a/MediaBrowser.Controller/Entities/Extensions.cs b/MediaBrowser.Controller/Entities/Extensions.cs index d2ca11740..3a34c668c 100644 --- a/MediaBrowser.Controller/Entities/Extensions.cs +++ b/MediaBrowser.Controller/Entities/Extensions.cs @@ -6,7 +6,7 @@ using MediaBrowser.Model.Entities; namespace MediaBrowser.Controller.Entities { /// <summary> - /// Class Extensions + /// Class Extensions. /// </summary> public static class Extensions { diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index 4af74f9cd..6441340f9 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -31,7 +31,7 @@ using Series = MediaBrowser.Controller.Entities.TV.Series; namespace MediaBrowser.Controller.Entities { /// <summary> - /// Class Folder + /// Class Folder. /// </summary> public class Folder : BaseItem { @@ -126,10 +126,12 @@ namespace MediaBrowser.Controller.Entities { return false; } + if (this is UserView) { return false; } + return true; } @@ -156,6 +158,7 @@ namespace MediaBrowser.Controller.Entities { item.DateCreated = DateTime.UtcNow; } + if (item.DateModified == DateTime.MinValue) { item.DateModified = DateTime.UtcNow; @@ -172,7 +175,7 @@ namespace MediaBrowser.Controller.Entities public virtual IEnumerable<BaseItem> Children => LoadChildren(); /// <summary> - /// thread-safe access to all recursive children of this folder - without regard to user + /// thread-safe access to all recursive children of this folder - without regard to user. /// </summary> /// <value>The recursive children.</value> [JsonIgnore] @@ -213,8 +216,8 @@ namespace MediaBrowser.Controller.Entities /// </summary> protected virtual List<BaseItem> LoadChildren() { - //logger.LogDebug("Loading children from {0} {1} {2}", GetType().Name, Id, Path); - //just load our children from the repo - the library will be validated and maintained in other processes + // logger.LogDebug("Loading children from {0} {1} {2}", GetType().Name, Id, Path); + // just load our children from the repo - the library will be validated and maintained in other processes return GetCachedChildren(); } @@ -229,7 +232,7 @@ namespace MediaBrowser.Controller.Entities } /// <summary> - /// Validates that the children of the folder still exist + /// Validates that the children of the folder still exist. /// </summary> /// <param name="progress">The progress.</param> /// <param name="cancellationToken">The cancellation token.</param> @@ -477,7 +480,7 @@ namespace MediaBrowser.Controller.Entities innerProgress.RegisterAction(p => { double innerPercent = currentInnerPercent; - innerPercent += p / (count); + innerPercent += p / count; progress.Report(innerPercent); }); @@ -500,8 +503,8 @@ namespace MediaBrowser.Controller.Entities if (series != null) { await series.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false); - } + await container.RefreshAllMetadata(refreshOptions, progress, cancellationToken).ConfigureAwait(false); } @@ -553,7 +556,7 @@ namespace MediaBrowser.Controller.Entities innerProgress.RegisterAction(p => { double innerPercent = currentInnerPercent; - innerPercent += p / (count); + innerPercent += p / count; progress.Report(innerPercent); }); @@ -571,7 +574,7 @@ namespace MediaBrowser.Controller.Entities } /// <summary> - /// Get the children of this folder from the actual file system + /// Get the children of this folder from the actual file system. /// </summary> /// <returns>IEnumerable{BaseItem}.</returns> protected virtual IEnumerable<BaseItem> GetNonCachedChildren(IDirectoryService directoryService) @@ -583,7 +586,7 @@ namespace MediaBrowser.Controller.Entities } /// <summary> - /// Get our children from the repo - stubbed for now + /// Get our children from the repo - stubbed for now. /// </summary> /// <returns>IEnumerable{BaseItem}.</returns> protected List<BaseItem> GetCachedChildren() @@ -615,7 +618,6 @@ namespace MediaBrowser.Controller.Entities { EnableImages = false } - }); return result.TotalRecordCount; @@ -941,6 +943,7 @@ namespace MediaBrowser.Controller.Entities { items = items.Where(i => string.Compare(query.NameStartsWithOrGreater, i.SortName, StringComparison.CurrentCultureIgnoreCase) < 1); } + if (!string.IsNullOrEmpty(query.NameStartsWith)) { items = items.Where(i => i.SortName.StartsWith(query.NameStartsWith, StringComparison.OrdinalIgnoreCase)); @@ -991,18 +994,22 @@ namespace MediaBrowser.Controller.Entities { return false; } + if (queryParent is Series) { return false; } + if (queryParent is Season) { return false; } + if (queryParent is MusicAlbum) { return false; } + if (queryParent is MusicArtist) { return false; @@ -1032,22 +1039,27 @@ namespace MediaBrowser.Controller.Entities { return false; } + if (request.IsFavoriteOrLiked.HasValue) { return false; } + if (request.IsLiked.HasValue) { return false; } + if (request.IsPlayed.HasValue) { return false; } + if (request.IsResumable.HasValue) { return false; } + if (request.IsFolder.HasValue) { return false; @@ -1223,7 +1235,7 @@ namespace MediaBrowser.Controller.Entities throw new ArgumentNullException(nameof(user)); } - //the true root should return our users root folder children + // the true root should return our users root folder children if (IsPhysicalRoot) { return LibraryManager.GetUserRootFolder().GetChildren(user, includeLinkedChildren); @@ -1288,7 +1300,7 @@ namespace MediaBrowser.Controller.Entities } /// <summary> - /// Gets allowed recursive children of an item + /// Gets allowed recursive children of an item. /// </summary> /// <param name="user">The user.</param> /// <param name="includeLinkedChildren">if set to <c>true</c> [include linked children].</param> @@ -1393,6 +1405,7 @@ namespace MediaBrowser.Controller.Entities list.Add(child); } } + return list; } @@ -1415,6 +1428,7 @@ namespace MediaBrowser.Controller.Entities return true; } } + return false; } @@ -1629,7 +1643,6 @@ namespace MediaBrowser.Controller.Entities Recursive = true, IsFolder = false, EnableTotalRecordCount = false - }); // Sweep through recursively and update status @@ -1647,7 +1660,6 @@ namespace MediaBrowser.Controller.Entities IsFolder = false, IsVirtualItem = false, EnableTotalRecordCount = false - }); return itemsResult @@ -1669,22 +1681,27 @@ namespace MediaBrowser.Controller.Entities { return false; } + if (this is UserView) { return false; } + if (this is UserRootFolder) { return false; } + if (this is Channel) { return false; } + if (SourceType != SourceType.Library) { return false; } + var iItemByName = this as IItemByName; if (iItemByName != null) { diff --git a/MediaBrowser.Controller/Entities/Genre.cs b/MediaBrowser.Controller/Entities/Genre.cs index 773c7df34..437def532 100644 --- a/MediaBrowser.Controller/Entities/Genre.cs +++ b/MediaBrowser.Controller/Entities/Genre.cs @@ -8,7 +8,7 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Controller.Entities { /// <summary> - /// Class Genre + /// Class Genre. /// </summary> public class Genre : BaseItem, IItemByName { @@ -19,6 +19,7 @@ namespace MediaBrowser.Controller.Entities list.Insert(0, GetType().Name + "-" + (Name ?? string.Empty).RemoveDiacritics()); return list; } + public override string CreatePresentationUniqueKey() { return GetUserDataKeys()[0]; @@ -31,7 +32,7 @@ namespace MediaBrowser.Controller.Entities /// <summary> /// Returns the folder containing the item. - /// If the item is a folder, it returns the folder itself + /// If the item is a folder, it returns the folder itself. /// </summary> /// <value>The containing folder path.</value> [JsonIgnore] @@ -92,11 +93,12 @@ namespace MediaBrowser.Controller.Entities Logger.LogDebug("{0} path has changed from {1} to {2}", GetType().Name, Path, newPath); return true; } + return base.RequiresRefresh(); } /// <summary> - /// This is called before any metadata refresh and returns true or false indicating if changes were made + /// This is called before any metadata refresh and returns true or false indicating if changes were made. /// </summary> public override bool BeforeMetadataRefresh(bool replaceAllMetdata) { diff --git a/MediaBrowser.Controller/Entities/ICollectionFolder.cs b/MediaBrowser.Controller/Entities/ICollectionFolder.cs index 4f0760746..245b23ff0 100644 --- a/MediaBrowser.Controller/Entities/ICollectionFolder.cs +++ b/MediaBrowser.Controller/Entities/ICollectionFolder.cs @@ -3,7 +3,7 @@ using System; namespace MediaBrowser.Controller.Entities { /// <summary> - /// This is just a marker interface to denote top level folders + /// This is just a marker interface to denote top level folders. /// </summary> public interface ICollectionFolder : IHasCollectionType { diff --git a/MediaBrowser.Controller/Entities/IHasAspectRatio.cs b/MediaBrowser.Controller/Entities/IHasAspectRatio.cs index 149c1e5ab..d7d007668 100644 --- a/MediaBrowser.Controller/Entities/IHasAspectRatio.cs +++ b/MediaBrowser.Controller/Entities/IHasAspectRatio.cs @@ -1,7 +1,7 @@ namespace MediaBrowser.Controller.Entities { /// <summary> - /// Interface IHasAspectRatio + /// Interface IHasAspectRatio. /// </summary> public interface IHasAspectRatio { diff --git a/MediaBrowser.Controller/Entities/IHasDisplayOrder.cs b/MediaBrowser.Controller/Entities/IHasDisplayOrder.cs index abee75a28..13226b234 100644 --- a/MediaBrowser.Controller/Entities/IHasDisplayOrder.cs +++ b/MediaBrowser.Controller/Entities/IHasDisplayOrder.cs @@ -1,7 +1,7 @@ namespace MediaBrowser.Controller.Entities { /// <summary> - /// Interface IHasDisplayOrder + /// Interface IHasDisplayOrder. /// </summary> public interface IHasDisplayOrder { diff --git a/MediaBrowser.Controller/Entities/IHasMediaSources.cs b/MediaBrowser.Controller/Entities/IHasMediaSources.cs index 4635b9062..213c0a794 100644 --- a/MediaBrowser.Controller/Entities/IHasMediaSources.cs +++ b/MediaBrowser.Controller/Entities/IHasMediaSources.cs @@ -13,7 +13,9 @@ namespace MediaBrowser.Controller.Entities List<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution); List<MediaStream> GetMediaStreams(); Guid Id { get; set; } + long? RunTimeTicks { get; set; } + string Path { get; } } } diff --git a/MediaBrowser.Controller/Entities/IHasProgramAttributes.cs b/MediaBrowser.Controller/Entities/IHasProgramAttributes.cs index 777b40828..fd1c19c97 100644 --- a/MediaBrowser.Controller/Entities/IHasProgramAttributes.cs +++ b/MediaBrowser.Controller/Entities/IHasProgramAttributes.cs @@ -5,13 +5,21 @@ namespace MediaBrowser.Controller.Entities public interface IHasProgramAttributes { bool IsMovie { get; set; } + bool IsSports { get; } + bool IsNews { get; } + bool IsKids { get; } + bool IsRepeat { get; set; } + bool IsSeries { get; set; } + ProgramAudio? Audio { get; set; } + string EpisodeTitle { get; set; } + string ServiceName { get; set; } } } diff --git a/MediaBrowser.Controller/Entities/IHasScreenshots.cs b/MediaBrowser.Controller/Entities/IHasScreenshots.cs index 0975242f5..b027a0cb1 100644 --- a/MediaBrowser.Controller/Entities/IHasScreenshots.cs +++ b/MediaBrowser.Controller/Entities/IHasScreenshots.cs @@ -1,7 +1,7 @@ namespace MediaBrowser.Controller.Entities { /// <summary> - /// Interface IHasScreenshots + /// Interface IHasScreenshots. /// </summary> public interface IHasScreenshots { diff --git a/MediaBrowser.Controller/Entities/IHasSeries.cs b/MediaBrowser.Controller/Entities/IHasSeries.cs index 7da53f730..475a2ab85 100644 --- a/MediaBrowser.Controller/Entities/IHasSeries.cs +++ b/MediaBrowser.Controller/Entities/IHasSeries.cs @@ -9,11 +9,14 @@ namespace MediaBrowser.Controller.Entities /// </summary> /// <value>The name of the series.</value> string SeriesName { get; set; } + string FindSeriesName(); string FindSeriesSortName(); Guid SeriesId { get; set; } + Guid FindSeriesId(); string SeriesPresentationUniqueKey { get; set; } + string FindSeriesPresentationUniqueKey(); } } diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs index 496bee857..466cda67c 100644 --- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs +++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs @@ -21,100 +21,167 @@ namespace MediaBrowser.Controller.Entities public BaseItem SimilarTo { get; set; } public bool? IsFolder { get; set; } + public bool? IsFavorite { get; set; } + public bool? IsFavoriteOrLiked { get; set; } + public bool? IsLiked { get; set; } + public bool? IsPlayed { get; set; } + public bool? IsResumable { get; set; } + public bool? IncludeItemsByName { get; set; } public string[] MediaTypes { get; set; } + public string[] IncludeItemTypes { get; set; } + public string[] ExcludeItemTypes { get; set; } + public string[] ExcludeTags { get; set; } + public string[] ExcludeInheritedTags { get; set; } + public string[] Genres { get; set; } public bool? IsSpecialSeason { get; set; } + public bool? IsMissing { get; set; } + public bool? IsUnaired { get; set; } + public bool? CollapseBoxSetItems { get; set; } public string NameStartsWithOrGreater { get; set; } + public string NameStartsWith { get; set; } + public string NameLessThan { get; set; } + public string NameContains { get; set; } + public string MinSortName { get; set; } public string PresentationUniqueKey { get; set; } + public string Path { get; set; } + public string Name { get; set; } public string Person { get; set; } + public Guid[] PersonIds { get; set; } + public Guid[] ItemIds { get; set; } + public Guid[] ExcludeItemIds { get; set; } + public string AdjacentTo { get; set; } + public string[] PersonTypes { get; set; } public bool? Is3D { get; set; } + public bool? IsHD { get; set; } + public bool? IsLocked { get; set; } + public bool? IsPlaceHolder { get; set; } public bool? HasImdbId { get; set; } + public bool? HasOverview { get; set; } + public bool? HasTmdbId { get; set; } + public bool? HasOfficialRating { get; set; } + public bool? HasTvdbId { get; set; } + public bool? HasThemeSong { get; set; } + public bool? HasThemeVideo { get; set; } + public bool? HasSubtitles { get; set; } + public bool? HasSpecialFeature { get; set; } + public bool? HasTrailer { get; set; } + public bool? HasParentalRating { get; set; } public Guid[] StudioIds { get; set; } + public Guid[] GenreIds { get; set; } + public ImageType[] ImageTypes { get; set; } + public VideoType[] VideoTypes { get; set; } + public UnratedItem[] BlockUnratedItems { get; set; } + public int[] Years { get; set; } + public string[] Tags { get; set; } + public string[] OfficialRatings { get; set; } public DateTime? MinPremiereDate { get; set; } + public DateTime? MaxPremiereDate { get; set; } + public DateTime? MinStartDate { get; set; } + public DateTime? MaxStartDate { get; set; } + public DateTime? MinEndDate { get; set; } + public DateTime? MaxEndDate { get; set; } + public bool? IsAiring { get; set; } public bool? IsMovie { get; set; } + public bool? IsSports { get; set; } + public bool? IsKids { get; set; } + public bool? IsNews { get; set; } + public bool? IsSeries { get; set; } + public int? MinIndexNumber { get; set; } + public int? AiredDuringSeason { get; set; } + public double? MinCriticRating { get; set; } + public double? MinCommunityRating { get; set; } public Guid[] ChannelIds { get; set; } public int? ParentIndexNumber { get; set; } + public int? ParentIndexNumberNotEquals { get; set; } + public int? IndexNumber { get; set; } + public int? MinParentalRating { get; set; } + public int? MaxParentalRating { get; set; } public bool? HasDeadParentId { get; set; } + public bool? IsVirtualItem { get; set; } public Guid ParentId { get; set; } + public string ParentType { get; set; } + public Guid[] AncestorIds { get; set; } + public Guid[] TopParentIds { get; set; } public BaseItem Parent @@ -135,41 +202,65 @@ namespace MediaBrowser.Controller.Entities } public string[] PresetViews { get; set; } + public TrailerType[] TrailerTypes { get; set; } + public SourceType[] SourceTypes { get; set; } public SeriesStatus[] SeriesStatuses { get; set; } + public string ExternalSeriesId { get; set; } + public string ExternalId { get; set; } public Guid[] AlbumIds { get; set; } + public Guid[] ArtistIds { get; set; } + public Guid[] ExcludeArtistIds { get; set; } + public string AncestorWithPresentationUniqueKey { get; set; } + public string SeriesPresentationUniqueKey { get; set; } public bool GroupByPresentationUniqueKey { get; set; } + public bool GroupBySeriesPresentationUniqueKey { get; set; } + public bool EnableTotalRecordCount { get; set; } + public bool ForceDirect { get; set; } + public Dictionary<string, string> ExcludeProviderIds { get; set; } + public bool EnableGroupByMetadataKey { get; set; } + public bool? HasChapterImages { get; set; } public IReadOnlyList<(string, SortOrder)> OrderBy { get; set; } public DateTime? MinDateCreated { get; set; } + public DateTime? MinDateLastSaved { get; set; } + public DateTime? MinDateLastSavedForUser { get; set; } public DtoOptions DtoOptions { get; set; } + public int MinSimilarityScore { get; set; } + public string HasNoAudioTrackWithLanguage { get; set; } + public string HasNoInternalSubtitleTrackWithLanguage { get; set; } + public string HasNoExternalSubtitleTrackWithLanguage { get; set; } + public string HasNoSubtitleTrackWithLanguage { get; set; } + public bool? IsDeadArtist { get; set; } + public bool? IsDeadStudio { get; set; } + public bool? IsDeadPerson { get; set; } public InternalItemsQuery() @@ -240,17 +331,29 @@ namespace MediaBrowser.Controller.Entities } public Dictionary<string, string> HasAnyProviderId { get; set; } + public Guid[] AlbumArtistIds { get; set; } + public Guid[] BoxSetLibraryFolders { get; set; } + public Guid[] ContributingArtistIds { get; set; } + public bool? HasAired { get; set; } + public bool? HasOwnerId { get; set; } + public bool? Is4K { get; set; } + public int? MaxHeight { get; set; } + public int? MaxWidth { get; set; } + public int? MinHeight { get; set; } + public int? MinWidth { get; set; } + public string SearchTerm { get; set; } + public string SeriesTimerId { get; set; } } } diff --git a/MediaBrowser.Controller/Entities/LinkedChild.cs b/MediaBrowser.Controller/Entities/LinkedChild.cs index d88c31007..65753a26e 100644 --- a/MediaBrowser.Controller/Entities/LinkedChild.cs +++ b/MediaBrowser.Controller/Entities/LinkedChild.cs @@ -9,14 +9,16 @@ namespace MediaBrowser.Controller.Entities public class LinkedChild { public string Path { get; set; } + public LinkedChildType Type { get; set; } + public string LibraryItemId { get; set; } [JsonIgnore] public string Id { get; set; } /// <summary> - /// Serves as a cache + /// Serves as a cache. /// </summary> public Guid? ItemId { get; set; } @@ -63,6 +65,7 @@ namespace MediaBrowser.Controller.Entities { return _fileSystem.AreEqual(x.Path, y.Path); } + return false; } diff --git a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs index be71bcc3c..c131c5430 100644 --- a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs +++ b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs @@ -11,7 +11,7 @@ using MediaBrowser.Model.Querying; namespace MediaBrowser.Controller.Entities.Movies { /// <summary> - /// Class BoxSet + /// Class BoxSet. /// </summary> public class BoxSet : Folder, IHasTrailers, IHasDisplayOrder, IHasLookupInfo<BoxSetInfo> { @@ -198,7 +198,7 @@ namespace MediaBrowser.Controller.Entities.Movies public Guid[] GetLibraryFolderIds() { - var expandedFolders = new List<Guid>() { }; + var expandedFolders = new List<Guid>(); return FlattenItems(this, expandedFolders) .SelectMany(i => LibraryManager.GetCollectionFolders(i)) diff --git a/MediaBrowser.Controller/Entities/Movies/Movie.cs b/MediaBrowser.Controller/Entities/Movies/Movie.cs index 26a165025..53badac4d 100644 --- a/MediaBrowser.Controller/Entities/Movies/Movie.cs +++ b/MediaBrowser.Controller/Entities/Movies/Movie.cs @@ -13,7 +13,7 @@ using MediaBrowser.Model.Providers; namespace MediaBrowser.Controller.Entities.Movies { /// <summary> - /// Class Movie + /// Class Movie. /// </summary> public class Movie : Video, IHasSpecialFeatures, IHasTrailers, IHasLookupInfo<MovieInfo>, ISupportsBoxSetGrouping { diff --git a/MediaBrowser.Controller/Entities/PeopleHelper.cs b/MediaBrowser.Controller/Entities/PeopleHelper.cs index 2fb613768..c39495759 100644 --- a/MediaBrowser.Controller/Entities/PeopleHelper.cs +++ b/MediaBrowser.Controller/Entities/PeopleHelper.cs @@ -113,6 +113,7 @@ namespace MediaBrowser.Controller.Entities return true; } } + return false; } } diff --git a/MediaBrowser.Controller/Entities/Person.cs b/MediaBrowser.Controller/Entities/Person.cs index 9e4f9d47e..331d17fc8 100644 --- a/MediaBrowser.Controller/Entities/Person.cs +++ b/MediaBrowser.Controller/Entities/Person.cs @@ -19,6 +19,7 @@ namespace MediaBrowser.Controller.Entities list.Insert(0, GetType().Name + "-" + (Name ?? string.Empty).RemoveDiacritics()); return list; } + public override string CreatePresentationUniqueKey() { return GetUserDataKeys()[0]; @@ -46,7 +47,7 @@ namespace MediaBrowser.Controller.Entities /// <summary> /// Returns the folder containing the item. - /// If the item is a folder, it returns the folder itself + /// If the item is a folder, it returns the folder itself. /// </summary> /// <value>The containing folder path.</value> [JsonIgnore] @@ -114,11 +115,12 @@ namespace MediaBrowser.Controller.Entities Logger.LogDebug("{0} path has changed from {1} to {2}", GetType().Name, Path, newPath); return true; } + return base.RequiresRefresh(); } /// <summary> - /// This is called before any metadata refresh and returns true or false indicating if changes were made + /// This is called before any metadata refresh and returns true or false indicating if changes were made. /// </summary> public override bool BeforeMetadataRefresh(bool replaceAllMetdata) { diff --git a/MediaBrowser.Controller/Entities/Photo.cs b/MediaBrowser.Controller/Entities/Photo.cs index 5ebc9f16a..82d0826c5 100644 --- a/MediaBrowser.Controller/Entities/Photo.cs +++ b/MediaBrowser.Controller/Entities/Photo.cs @@ -29,6 +29,7 @@ namespace MediaBrowser.Controller.Entities return photoAlbum; } } + return null; } } @@ -68,17 +69,27 @@ namespace MediaBrowser.Controller.Entities } public string CameraMake { get; set; } + public string CameraModel { get; set; } + public string Software { get; set; } + public double? ExposureTime { get; set; } + public double? FocalLength { get; set; } + public ImageOrientation? Orientation { get; set; } + public double? Aperture { get; set; } + public double? ShutterSpeed { get; set; } public double? Latitude { get; set; } + public double? Longitude { get; set; } + public double? Altitude { get; set; } + public int? IsoSpeedRating { get; set; } } } diff --git a/MediaBrowser.Controller/Entities/Share.cs b/MediaBrowser.Controller/Entities/Share.cs index c17789ccc..a51f2b452 100644 --- a/MediaBrowser.Controller/Entities/Share.cs +++ b/MediaBrowser.Controller/Entities/Share.cs @@ -8,6 +8,7 @@ namespace MediaBrowser.Controller.Entities public class Share { public string UserId { get; set; } + public bool CanEdit { get; set; } } } diff --git a/MediaBrowser.Controller/Entities/Studio.cs b/MediaBrowser.Controller/Entities/Studio.cs index 068032317..1f64de6a4 100644 --- a/MediaBrowser.Controller/Entities/Studio.cs +++ b/MediaBrowser.Controller/Entities/Studio.cs @@ -7,7 +7,7 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Controller.Entities { /// <summary> - /// Class Studio + /// Class Studio. /// </summary> public class Studio : BaseItem, IItemByName { @@ -18,6 +18,7 @@ namespace MediaBrowser.Controller.Entities list.Insert(0, GetType().Name + "-" + (Name ?? string.Empty).RemoveDiacritics()); return list; } + public override string CreatePresentationUniqueKey() { return GetUserDataKeys()[0]; @@ -25,7 +26,7 @@ namespace MediaBrowser.Controller.Entities /// <summary> /// Returns the folder containing the item. - /// If the item is a folder, it returns the folder itself + /// If the item is a folder, it returns the folder itself. /// </summary> /// <value>The containing folder path.</value> [JsonIgnore] @@ -93,11 +94,12 @@ namespace MediaBrowser.Controller.Entities Logger.LogDebug("{0} path has changed from {1} to {2}", GetType().Name, Path, newPath); return true; } + return base.RequiresRefresh(); } /// <summary> - /// This is called before any metadata refresh and returns true or false indicating if changes were made + /// This is called before any metadata refresh and returns true or false indicating if changes were made. /// </summary> public override bool BeforeMetadataRefresh(bool replaceAllMetdata) { diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs index 4ec60e7cd..9a5f9097d 100644 --- a/MediaBrowser.Controller/Entities/TV/Episode.cs +++ b/MediaBrowser.Controller/Entities/TV/Episode.cs @@ -12,7 +12,7 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Controller.Entities.TV { /// <summary> - /// Class Episode + /// Class Episode. /// </summary> public class Episode : Video, IHasTrailers, IHasLookupInfo<EpisodeInfo>, IHasSeries { @@ -34,7 +34,9 @@ namespace MediaBrowser.Controller.Entities.TV /// </summary> /// <value>The aired season.</value> public int? AirsBeforeSeasonNumber { get; set; } + public int? AirsAfterSeasonNumber { get; set; } + public int? AirsBeforeEpisodeNumber { get; set; } /// <summary> @@ -94,6 +96,7 @@ namespace MediaBrowser.Controller.Entities.TV { take--; } + list.InsertRange(0, seriesUserDataKeys.Take(take).Select(i => i + ParentIndexNumber.Value.ToString("000") + IndexNumber.Value.ToString("000"))); } @@ -101,7 +104,7 @@ namespace MediaBrowser.Controller.Entities.TV } /// <summary> - /// This Episode's Series Instance + /// This Episode's Series Instance. /// </summary> /// <value>The series.</value> [JsonIgnore] @@ -114,6 +117,7 @@ namespace MediaBrowser.Controller.Entities.TV { seriesId = FindSeriesId(); } + return !seriesId.Equals(Guid.Empty) ? (LibraryManager.GetItemById(seriesId) as Series) : null; } } @@ -128,6 +132,7 @@ namespace MediaBrowser.Controller.Entities.TV { seasonId = FindSeasonId(); } + return !seasonId.Equals(Guid.Empty) ? (LibraryManager.GetItemById(seasonId) as Season) : null; } } @@ -160,6 +165,7 @@ namespace MediaBrowser.Controller.Entities.TV { return "Season " + ParentIndexNumber.Value.ToString(CultureInfo.InvariantCulture); } + return "Season Unknown"; } diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs index 7dfd1a759..2aba1d03d 100644 --- a/MediaBrowser.Controller/Entities/TV/Season.cs +++ b/MediaBrowser.Controller/Entities/TV/Season.cs @@ -11,7 +11,7 @@ using MediaBrowser.Model.Querying; namespace MediaBrowser.Controller.Entities.TV { /// <summary> - /// Class Season + /// Class Season. /// </summary> public class Season : Folder, IHasSeries, IHasLookupInfo<SeasonInfo> { @@ -68,7 +68,7 @@ namespace MediaBrowser.Controller.Entities.TV } /// <summary> - /// This Episode's Series Instance + /// This Episode's Series Instance. /// </summary> /// <value>The series.</value> [JsonIgnore] @@ -81,6 +81,7 @@ namespace MediaBrowser.Controller.Entities.TV { seriesId = FindSeriesId(); } + return seriesId == Guid.Empty ? null : (LibraryManager.GetItemById(seriesId) as Series); } } @@ -225,7 +226,7 @@ namespace MediaBrowser.Controller.Entities.TV } /// <summary> - /// This is called before any metadata refresh and returns true or false indicating if changes were made + /// This is called before any metadata refresh and returns true or false indicating if changes were made. /// </summary> /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns> public override bool BeforeMetadataRefresh(bool replaceAllMetdata) diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs index a519089b3..45daa8a53 100644 --- a/MediaBrowser.Controller/Entities/TV/Series.cs +++ b/MediaBrowser.Controller/Entities/TV/Series.cs @@ -17,7 +17,7 @@ using MetadataProvider = MediaBrowser.Model.Entities.MetadataProvider; namespace MediaBrowser.Controller.Entities.TV { /// <summary> - /// Class Series + /// Class Series. /// </summary> public class Series : Folder, IHasTrailers, IHasDisplayOrder, IHasLookupInfo<SeriesInfo>, IMetadataContainer { @@ -30,6 +30,7 @@ namespace MediaBrowser.Controller.Entities.TV } public DayOfWeek[] AirDays { get; set; } + public string AirTime { get; set; } [JsonIgnore] @@ -54,7 +55,7 @@ namespace MediaBrowser.Controller.Entities.TV public IReadOnlyList<Guid> RemoteTrailerIds { get; set; } /// <summary> - /// airdate, dvd or absolute + /// airdate, dvd or absolute. /// </summary> public string DisplayOrder { get; set; } @@ -150,6 +151,7 @@ namespace MediaBrowser.Controller.Entities.TV { query.IncludeItemTypes = new[] { typeof(Episode).Name }; } + query.IsVirtualItem = false; query.Limit = 0; var totalRecordCount = LibraryManager.GetCount(query); diff --git a/MediaBrowser.Controller/Entities/Trailer.cs b/MediaBrowser.Controller/Entities/Trailer.cs index c327d17c9..6b544afc6 100644 --- a/MediaBrowser.Controller/Entities/Trailer.cs +++ b/MediaBrowser.Controller/Entities/Trailer.cs @@ -9,7 +9,7 @@ using MediaBrowser.Model.Providers; namespace MediaBrowser.Controller.Entities { /// <summary> - /// Class Trailer + /// Class Trailer. /// </summary> public class Trailer : Video, IHasLookupInfo<TrailerInfo> { diff --git a/MediaBrowser.Controller/Entities/UserItemData.cs b/MediaBrowser.Controller/Entities/UserItemData.cs index ab425ee0f..3298fa2d3 100644 --- a/MediaBrowser.Controller/Entities/UserItemData.cs +++ b/MediaBrowser.Controller/Entities/UserItemData.cs @@ -4,7 +4,7 @@ using System.Text.Json.Serialization; namespace MediaBrowser.Controller.Entities { /// <summary> - /// Class UserItemData + /// Class UserItemData. /// </summary> public class UserItemData { @@ -21,11 +21,11 @@ namespace MediaBrowser.Controller.Entities public string Key { get; set; } /// <summary> - /// The _rating + /// The _rating. /// </summary> private double? _rating; /// <summary> - /// Gets or sets the users 0-10 rating + /// Gets or sets the users 0-10 rating. /// </summary> /// <value>The rating.</value> /// <exception cref="ArgumentOutOfRangeException">Rating;A 0 to 10 rating is required for UserItemData.</exception> @@ -105,6 +105,7 @@ namespace MediaBrowser.Controller.Entities return null; } + set { if (value.HasValue) diff --git a/MediaBrowser.Controller/Entities/UserView.cs b/MediaBrowser.Controller/Entities/UserView.cs index ceca77bc0..1fba8a30f 100644 --- a/MediaBrowser.Controller/Entities/UserView.cs +++ b/MediaBrowser.Controller/Entities/UserView.cs @@ -68,7 +68,7 @@ namespace MediaBrowser.Controller.Entities parent = LibraryManager.GetItemById(ParentId) as Folder ?? parent; } - return new UserViewBuilder(UserViewManager, LibraryManager, LoggerFactory.CreateLogger<UserViewBuilder>(), UserDataManager, TVSeriesManager, ConfigurationManager) + return new UserViewBuilder(UserViewManager, LibraryManager, Logger, UserDataManager, TVSeriesManager, ConfigurationManager) .GetUserItems(parent, this, CollectionType, query); } diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs index bf0e56cf7..cb35d6e32 100644 --- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs +++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs @@ -7,6 +7,7 @@ using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.TV; +using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Querying; using Microsoft.Extensions.Logging; @@ -22,7 +23,7 @@ namespace MediaBrowser.Controller.Entities { private readonly IUserViewManager _userViewManager; private readonly ILibraryManager _libraryManager; - private readonly ILogger<UserViewBuilder> _logger; + private readonly ILogger<BaseItem> _logger; private readonly IUserDataManager _userDataManager; private readonly ITVSeriesManager _tvSeriesManager; private readonly IServerConfigurationManager _config; @@ -30,7 +31,7 @@ namespace MediaBrowser.Controller.Entities public UserViewBuilder( IUserViewManager userViewManager, ILibraryManager libraryManager, - ILogger<UserViewBuilder> logger, + ILogger<BaseItem> logger, IUserDataManager userDataManager, ITVSeriesManager tvSeriesManager, IServerConfigurationManager config) @@ -47,7 +48,7 @@ namespace MediaBrowser.Controller.Entities { var user = query.User; - //if (query.IncludeItemTypes != null && + // if (query.IncludeItemTypes != null && // query.IncludeItemTypes.Length == 1 && // string.Equals(query.IncludeItemTypes[0], "Playlist", StringComparison.OrdinalIgnoreCase)) //{ @@ -270,7 +271,6 @@ namespace MediaBrowser.Controller.Entities _logger.LogError(ex, "Error getting genre"); return null; } - }) .Where(i => i != null) .Select(i => GetUserViewWithName(i.Name, SpecialFolder.MovieGenre, i.SortName, parent)); @@ -347,7 +347,6 @@ namespace MediaBrowser.Controller.Entities Limit = query.Limit, StartIndex = query.StartIndex, UserId = query.User.Id - }, parentFolders, query.DtoOptions); return result; @@ -384,7 +383,6 @@ namespace MediaBrowser.Controller.Entities IncludeItemTypes = new[] { typeof(Series).Name }, Recursive = true, EnableTotalRecordCount = false - }).Items .SelectMany(i => i.Genres) .DistinctNames() @@ -399,7 +397,6 @@ namespace MediaBrowser.Controller.Entities _logger.LogError(ex, "Error getting genre"); return null; } - }) .Where(i => i != null) .Select(i => GetUserViewWithName(i.Name, SpecialFolder.TvGenre, i.SortName, parent)); @@ -424,7 +421,7 @@ namespace MediaBrowser.Controller.Entities { return new QueryResult<BaseItem> { - Items = result.Items, //TODO Fix The co-variant conversion between T[] and BaseItem[], this can generate runtime issues if T is not BaseItem. + Items = result.Items, // TODO Fix The co-variant conversion between T[] and BaseItem[], this can generate runtime issues if T is not BaseItem. TotalRecordCount = result.TotalRecordCount }; } @@ -964,6 +961,7 @@ namespace MediaBrowser.Controller.Entities .OfType<Folder>() .Where(UserView.IsEligibleForGrouping); } + return _libraryManager.GetUserRootFolder() .GetChildren(user, true) .OfType<Folder>() @@ -982,6 +980,7 @@ namespace MediaBrowser.Controller.Entities return folder != null && viewTypes.Contains(folder.CollectionType ?? string.Empty, StringComparer.OrdinalIgnoreCase); }).ToArray(); } + return GetMediaFolders(user) .Where(i => { diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs index 72eb67a06..b7d7e8e1a 100644 --- a/MediaBrowser.Controller/Entities/Video.cs +++ b/MediaBrowser.Controller/Entities/Video.cs @@ -17,7 +17,7 @@ using MediaBrowser.Model.MediaInfo; namespace MediaBrowser.Controller.Entities { /// <summary> - /// Class Video + /// Class Video. /// </summary> public class Video : BaseItem, IHasAspectRatio, @@ -28,7 +28,9 @@ namespace MediaBrowser.Controller.Entities public string PrimaryVersionId { get; set; } public string[] AdditionalParts { get; set; } + public string[] LocalAlternateVersions { get; set; } + public LinkedChild[] LinkedAlternateVersions { get; set; } [JsonIgnore] @@ -52,15 +54,18 @@ namespace MediaBrowser.Controller.Entities { return false; } + if (extraType.Value == Model.Entities.ExtraType.ThemeVideo) { return false; } + if (extraType.Value == Model.Entities.ExtraType.Trailer) { return false; } } + return true; } } @@ -196,6 +201,7 @@ namespace MediaBrowser.Controller.Entities return video.MediaSourceCount; } } + return LinkedAlternateVersions.Length + LocalAlternateVersions.Length + 1; } } @@ -390,11 +396,13 @@ namespace MediaBrowser.Controller.Entities AdditionalParts = newVideo.AdditionalParts; updateType |= ItemUpdateType.MetadataImport; } + if (!LocalAlternateVersions.SequenceEqual(newVideo.LocalAlternateVersions, StringComparer.Ordinal)) { LocalAlternateVersions = newVideo.LocalAlternateVersions; updateType |= ItemUpdateType.MetadataImport; } + if (VideoType != newVideo.VideoType) { VideoType = newVideo.VideoType; @@ -416,6 +424,7 @@ namespace MediaBrowser.Controller.Entities .Select(i => i.FullName) .ToArray(); } + if (videoType == VideoType.BluRay) { return FileSystem.GetFiles(rootPath, new[] { ".m2ts" }, false, true) @@ -425,6 +434,7 @@ namespace MediaBrowser.Controller.Entities .Select(i => i.FullName) .ToArray(); } + return Array.Empty<string>(); } @@ -535,7 +545,6 @@ namespace MediaBrowser.Controller.Entities { ItemId = Id, Index = DefaultVideoStreamIndex.Value - }).FirstOrDefault(); } diff --git a/MediaBrowser.Controller/Entities/Year.cs b/MediaBrowser.Controller/Entities/Year.cs index a01ef5c31..c88498640 100644 --- a/MediaBrowser.Controller/Entities/Year.cs +++ b/MediaBrowser.Controller/Entities/Year.cs @@ -7,7 +7,7 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Controller.Entities { /// <summary> - /// Class Year + /// Class Year. /// </summary> public class Year : BaseItem, IItemByName { @@ -21,7 +21,7 @@ namespace MediaBrowser.Controller.Entities /// <summary> /// Returns the folder containing the item. - /// If the item is a folder, it returns the folder itself + /// If the item is a folder, it returns the folder itself. /// </summary> /// <value>The containing folder path.</value> [JsonIgnore] @@ -103,11 +103,12 @@ namespace MediaBrowser.Controller.Entities Logger.LogDebug("{0} path has changed from {1} to {2}", GetType().Name, Path, newPath); return true; } + return base.RequiresRefresh(); } /// <summary> - /// This is called before any metadata refresh and returns true or false indicating if changes were made + /// This is called before any metadata refresh and returns true or false indicating if changes were made. /// </summary> public override bool BeforeMetadataRefresh(bool replaceAllMetdata) { diff --git a/MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs b/MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs index c0043c0ef..c2932cc4c 100644 --- a/MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs +++ b/MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs @@ -24,6 +24,11 @@ namespace MediaBrowser.Controller.Extensions public const string FfmpegAnalyzeDurationKey = "FFmpeg:analyzeduration"; /// <summary> + /// The key for the FFmpeg path option. + /// </summary> + public const string FfmpegPathKey = "ffmpeg"; + + /// <summary> /// The key for a setting that indicates whether playlists should allow duplicate entries. /// </summary> public const string PlaylistsAllowDuplicatesKey = "playlists:allowDuplicates"; diff --git a/MediaBrowser.Controller/Extensions/StringExtensions.cs b/MediaBrowser.Controller/Extensions/StringExtensions.cs index b1aaf6534..e09543e14 100644 --- a/MediaBrowser.Controller/Extensions/StringExtensions.cs +++ b/MediaBrowser.Controller/Extensions/StringExtensions.cs @@ -7,7 +7,7 @@ using System.Text.RegularExpressions; namespace MediaBrowser.Controller.Extensions { /// <summary> - /// Class BaseExtensions + /// Class BaseExtensions. /// </summary> public static class StringExtensions { diff --git a/MediaBrowser.Controller/IO/FileData.cs b/MediaBrowser.Controller/IO/FileData.cs index 666a3f76b..e655f50eb 100644 --- a/MediaBrowser.Controller/IO/FileData.cs +++ b/MediaBrowser.Controller/IO/FileData.cs @@ -20,6 +20,7 @@ namespace MediaBrowser.Controller.IO { dict[file.FullName] = file; } + return dict; } @@ -49,6 +50,7 @@ namespace MediaBrowser.Controller.IO { throw new ArgumentNullException(nameof(path)); } + if (args == null) { throw new ArgumentNullException(nameof(args)); @@ -77,7 +79,7 @@ namespace MediaBrowser.Controller.IO if (string.IsNullOrEmpty(newPath)) { - //invalid shortcut - could be old or target could just be unavailable + // invalid shortcut - could be old or target could just be unavailable logger.LogWarning("Encountered invalid shortcut: " + fullName); continue; } @@ -116,9 +118,9 @@ namespace MediaBrowser.Controller.IO returnResult[index] = value; index++; } + return returnResult; } - } } diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs index d1d6c74b8..abdb0f695 100644 --- a/MediaBrowser.Controller/IServerApplicationHost.cs +++ b/MediaBrowser.Controller/IServerApplicationHost.cs @@ -10,7 +10,7 @@ using Microsoft.AspNetCore.Http; namespace MediaBrowser.Controller { /// <summary> - /// Interface IServerApplicationHost + /// Interface IServerApplicationHost. /// </summary> public interface IServerApplicationHost : IApplicationHost { diff --git a/MediaBrowser.Controller/IServerApplicationPaths.cs b/MediaBrowser.Controller/IServerApplicationPaths.cs index c35a22ac7..155bf9177 100644 --- a/MediaBrowser.Controller/IServerApplicationPaths.cs +++ b/MediaBrowser.Controller/IServerApplicationPaths.cs @@ -5,7 +5,7 @@ namespace MediaBrowser.Controller public interface IServerApplicationPaths : IApplicationPaths { /// <summary> - /// Gets the path to the base root media directory + /// Gets the path to the base root media directory. /// </summary> /// <value>The root folder path.</value> string RootFolderPath { get; } @@ -17,13 +17,13 @@ namespace MediaBrowser.Controller string DefaultUserViewsPath { get; } /// <summary> - /// Gets the path to the People directory + /// Gets the path to the People directory. /// </summary> /// <value>The people path.</value> string PeoplePath { get; } /// <summary> - /// Gets the path to the Genre directory + /// Gets the path to the Genre directory. /// </summary> /// <value>The genre path.</value> string GenrePath { get; } @@ -35,25 +35,25 @@ namespace MediaBrowser.Controller string MusicGenrePath { get; } /// <summary> - /// Gets the path to the Studio directory + /// Gets the path to the Studio directory. /// </summary> /// <value>The studio path.</value> string StudioPath { get; } /// <summary> - /// Gets the path to the Year directory + /// Gets the path to the Year directory. /// </summary> /// <value>The year path.</value> string YearPath { get; } /// <summary> - /// Gets the path to the General IBN directory + /// Gets the path to the General IBN directory. /// </summary> /// <value>The general path.</value> string GeneralPath { get; } /// <summary> - /// Gets the path to the Ratings IBN directory + /// Gets the path to the Ratings IBN directory. /// </summary> /// <value>The ratings path.</value> string RatingsPath { get; } @@ -65,7 +65,7 @@ namespace MediaBrowser.Controller string MediaInfoImagesPath { get; } /// <summary> - /// Gets the path to the user configuration directory + /// Gets the path to the user configuration directory. /// </summary> /// <value>The user configuration directory path.</value> string UserConfigurationDirectoryPath { get; } diff --git a/MediaBrowser.Controller/Library/DeleteOptions.cs b/MediaBrowser.Controller/Library/DeleteOptions.cs index 751b90481..2944d8259 100644 --- a/MediaBrowser.Controller/Library/DeleteOptions.cs +++ b/MediaBrowser.Controller/Library/DeleteOptions.cs @@ -3,6 +3,7 @@ namespace MediaBrowser.Controller.Library public class DeleteOptions { public bool DeleteFileLocation { get; set; } + public bool DeleteFromExternalProvider { get; set; } public DeleteOptions() diff --git a/MediaBrowser.Controller/Library/IIntroProvider.cs b/MediaBrowser.Controller/Library/IIntroProvider.cs index aa7001611..d45493d40 100644 --- a/MediaBrowser.Controller/Library/IIntroProvider.cs +++ b/MediaBrowser.Controller/Library/IIntroProvider.cs @@ -5,7 +5,7 @@ using MediaBrowser.Controller.Entities; namespace MediaBrowser.Controller.Library { /// <summary> - /// Class BaseIntroProvider + /// Class BaseIntroProvider. /// </summary> public interface IIntroProvider { diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index d7237039e..9d6646857 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -21,7 +21,7 @@ using Person = MediaBrowser.Controller.Entities.Person; namespace MediaBrowser.Controller.Library { /// <summary> - /// Interface ILibraryManager + /// Interface ILibraryManager. /// </summary> public interface ILibraryManager { @@ -30,15 +30,13 @@ namespace MediaBrowser.Controller.Library /// </summary> /// <param name="fileInfo">The file information.</param> /// <param name="parent">The parent.</param> - /// <param name="allowIgnorePath">Allow the path to be ignored.</param> /// <returns>BaseItem.</returns> BaseItem ResolvePath( FileSystemMetadata fileInfo, - Folder parent = null, - bool allowIgnorePath = true); + Folder parent = null); /// <summary> - /// Resolves a set of files into a list of BaseItem + /// Resolves a set of files into a list of BaseItem. /// </summary> IEnumerable<BaseItem> ResolvePaths( IEnumerable<FileSystemMetadata> files, @@ -54,7 +52,7 @@ namespace MediaBrowser.Controller.Library AggregateFolder RootFolder { get; } /// <summary> - /// Gets a Person + /// Gets a Person. /// </summary> /// <param name="name">The name.</param> /// <returns>Task{Person}.</returns> @@ -75,14 +73,14 @@ namespace MediaBrowser.Controller.Library MusicArtist GetArtist(string name); MusicArtist GetArtist(string name, DtoOptions options); /// <summary> - /// Gets a Studio + /// Gets a Studio. /// </summary> /// <param name="name">The name.</param> /// <returns>Task{Studio}.</returns> Studio GetStudio(string name); /// <summary> - /// Gets a Genre + /// Gets a Genre. /// </summary> /// <param name="name">The name.</param> /// <returns>Task{Genre}.</returns> @@ -96,7 +94,7 @@ namespace MediaBrowser.Controller.Library MusicGenre GetMusicGenre(string name); /// <summary> - /// Gets a Year + /// Gets a Year. /// </summary> /// <param name="value">The value.</param> /// <returns>Task{Year}.</returns> @@ -113,7 +111,7 @@ namespace MediaBrowser.Controller.Library Task ValidatePeople(CancellationToken cancellationToken, IProgress<double> progress); /// <summary> - /// Reloads the root media folder + /// Reloads the root media folder. /// </summary> /// <param name="progress">The progress.</param> /// <param name="cancellationToken">The cancellation token.</param> diff --git a/MediaBrowser.Controller/Library/ILibraryPostScanTask.cs b/MediaBrowser.Controller/Library/ILibraryPostScanTask.cs index cba5e8fd7..4032e9d83 100644 --- a/MediaBrowser.Controller/Library/ILibraryPostScanTask.cs +++ b/MediaBrowser.Controller/Library/ILibraryPostScanTask.cs @@ -5,7 +5,7 @@ using System.Threading.Tasks; namespace MediaBrowser.Controller.Library { /// <summary> - /// An interface for tasks that run after the media library scan + /// An interface for tasks that run after the media library scan. /// </summary> public interface ILibraryPostScanTask { diff --git a/MediaBrowser.Controller/Library/ILiveStream.cs b/MediaBrowser.Controller/Library/ILiveStream.cs index 734932f17..7c9a9b20e 100644 --- a/MediaBrowser.Controller/Library/ILiveStream.cs +++ b/MediaBrowser.Controller/Library/ILiveStream.cs @@ -9,10 +9,15 @@ namespace MediaBrowser.Controller.Library Task Open(CancellationToken openCancellationToken); Task Close(); int ConsumerCount { get; set; } + string OriginalStreamId { get; set; } + string TunerHostId { get; } + bool EnableStreamSharing { get; } + MediaSourceInfo MediaSource { get; set; } + string UniqueId { get; } } } diff --git a/MediaBrowser.Controller/Library/IMetadataSaver.cs b/MediaBrowser.Controller/Library/IMetadataSaver.cs index dd119984e..027cc5b40 100644 --- a/MediaBrowser.Controller/Library/IMetadataSaver.cs +++ b/MediaBrowser.Controller/Library/IMetadataSaver.cs @@ -4,7 +4,7 @@ using MediaBrowser.Controller.Entities; namespace MediaBrowser.Controller.Library { /// <summary> - /// Interface IMetadataSaver + /// Interface IMetadataSaver. /// </summary> public interface IMetadataSaver { diff --git a/MediaBrowser.Controller/Library/ISearchEngine.cs b/MediaBrowser.Controller/Library/ISearchEngine.cs index 8498b92ae..31dcbba5b 100644 --- a/MediaBrowser.Controller/Library/ISearchEngine.cs +++ b/MediaBrowser.Controller/Library/ISearchEngine.cs @@ -4,7 +4,7 @@ using MediaBrowser.Model.Search; namespace MediaBrowser.Controller.Library { /// <summary> - /// Interface ILibrarySearchEngine + /// Interface ILibrarySearchEngine. /// </summary> public interface ISearchEngine { diff --git a/MediaBrowser.Controller/Library/IUserDataManager.cs b/MediaBrowser.Controller/Library/IUserDataManager.cs index f5ccad671..d08ad4cac 100644 --- a/MediaBrowser.Controller/Library/IUserDataManager.cs +++ b/MediaBrowser.Controller/Library/IUserDataManager.cs @@ -42,14 +42,14 @@ namespace MediaBrowser.Controller.Library UserItemDataDto GetUserDataDto(BaseItem item, BaseItemDto itemDto, User user, DtoOptions dto_options); /// <summary> - /// Get all user data for the given user + /// Get all user data for the given user. /// </summary> /// <param name="userId"></param> /// <returns></returns> List<UserItemData> GetAllUserData(Guid userId); /// <summary> - /// Save the all provided user data for the given user + /// Save the all provided user data for the given user. /// </summary> /// <param name="userId"></param> /// <param name="userData"></param> @@ -58,7 +58,7 @@ namespace MediaBrowser.Controller.Library void SaveAllUserData(Guid userId, UserItemData[] userData, CancellationToken cancellationToken); /// <summary> - /// Updates playstate for an item and returns true or false indicating if it was played to completion + /// Updates playstate for an item and returns true or false indicating if it was played to completion. /// </summary> bool UpdatePlayState(BaseItem item, UserItemData data, long? positionTicks); } diff --git a/MediaBrowser.Controller/Library/IUserManager.cs b/MediaBrowser.Controller/Library/IUserManager.cs index b5b2e4729..e73fe7120 100644 --- a/MediaBrowser.Controller/Library/IUserManager.cs +++ b/MediaBrowser.Controller/Library/IUserManager.cs @@ -11,7 +11,7 @@ using MediaBrowser.Model.Users; namespace MediaBrowser.Controller.Library { /// <summary> - /// Interface IUserManager + /// Interface IUserManager. /// </summary> public interface IUserManager { @@ -111,8 +111,8 @@ namespace MediaBrowser.Controller.Library /// <summary> /// Deletes the specified user. /// </summary> - /// <param name="user">The user to be deleted.</param> - void DeleteUser(User user); + /// <param name="userId">The id of the user to be deleted.</param> + void DeleteUser(Guid userId); /// <summary> /// Resets the password. diff --git a/MediaBrowser.Controller/Library/ItemChangeEventArgs.cs b/MediaBrowser.Controller/Library/ItemChangeEventArgs.cs index c9671de47..b5c48321b 100644 --- a/MediaBrowser.Controller/Library/ItemChangeEventArgs.cs +++ b/MediaBrowser.Controller/Library/ItemChangeEventArgs.cs @@ -3,7 +3,7 @@ using MediaBrowser.Controller.Entities; namespace MediaBrowser.Controller.Library { /// <summary> - /// Class ItemChangeEventArgs + /// Class ItemChangeEventArgs. /// </summary> public class ItemChangeEventArgs { diff --git a/MediaBrowser.Controller/Library/ItemResolveArgs.cs b/MediaBrowser.Controller/Library/ItemResolveArgs.cs index 0222b926e..2e5dcc4c5 100644 --- a/MediaBrowser.Controller/Library/ItemResolveArgs.cs +++ b/MediaBrowser.Controller/Library/ItemResolveArgs.cs @@ -15,7 +15,7 @@ namespace MediaBrowser.Controller.Library public class ItemResolveArgs : EventArgs { /// <summary> - /// The _app paths + /// The _app paths. /// </summary> private readonly IServerApplicationPaths _appPaths; @@ -42,7 +42,7 @@ namespace MediaBrowser.Controller.Library public LibraryOptions GetLibraryOptions() { - return LibraryOptions ?? (LibraryOptions = (Parent == null ? new LibraryOptions() : BaseItem.LibraryManager.GetLibraryOptions(Parent))); + return LibraryOptions ?? (LibraryOptions = Parent == null ? new LibraryOptions() : BaseItem.LibraryManager.GetLibraryOptions(Parent)); } /// <summary> @@ -89,7 +89,6 @@ namespace MediaBrowser.Controller.Library return parentDir.Length > _appPaths.RootFolderPath.Length && parentDir.StartsWith(_appPaths.RootFolderPath, StringComparison.OrdinalIgnoreCase); - } } @@ -129,8 +128,8 @@ namespace MediaBrowser.Controller.Library } return item != null; - } + return false; } @@ -225,8 +224,6 @@ namespace MediaBrowser.Controller.Library public string CollectionType { get; set; } - #region Equality Overrides - /// <summary> /// Determines whether the specified <see cref="object" /> is equal to this instance. /// </summary> @@ -255,13 +252,15 @@ namespace MediaBrowser.Controller.Library { if (args != null) { - if (args.Path == null && Path == null) return true; + if (args.Path == null && Path == null) + { + return true; + } + return args.Path != null && BaseItem.FileSystem.AreEqual(args.Path, Path); } + return false; } - - #endregion } - } diff --git a/MediaBrowser.Controller/Library/PlaybackProgressEventArgs.cs b/MediaBrowser.Controller/Library/PlaybackProgressEventArgs.cs index b4e205184..08cfea3c3 100644 --- a/MediaBrowser.Controller/Library/PlaybackProgressEventArgs.cs +++ b/MediaBrowser.Controller/Library/PlaybackProgressEventArgs.cs @@ -8,23 +8,32 @@ using MediaBrowser.Model.Dto; namespace MediaBrowser.Controller.Library { /// <summary> - /// Holds information about a playback progress event + /// Holds information about a playback progress event. /// </summary> public class PlaybackProgressEventArgs : EventArgs { public List<User> Users { get; set; } + public long? PlaybackPositionTicks { get; set; } + public BaseItem Item { get; set; } + public BaseItemDto MediaInfo { get; set; } + public string MediaSourceId { get; set; } + public bool IsPaused { get; set; } + public bool IsAutomated { get; set; } public string DeviceId { get; set; } + public string DeviceName { get; set; } + public string ClientName { get; set; } public string PlaySessionId { get; set; } + public SessionInfo Session { get; set; } public PlaybackProgressEventArgs() diff --git a/MediaBrowser.Controller/Library/Profiler.cs b/MediaBrowser.Controller/Library/Profiler.cs index 0febef3d3..399378a09 100644 --- a/MediaBrowser.Controller/Library/Profiler.cs +++ b/MediaBrowser.Controller/Library/Profiler.cs @@ -5,21 +5,21 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Controller.Library { /// <summary> - /// Class Profiler + /// Class Profiler. /// </summary> public class Profiler : IDisposable { /// <summary> - /// The name + /// The name. /// </summary> readonly string _name; /// <summary> - /// The stopwatch + /// The stopwatch. /// </summary> readonly Stopwatch _stopwatch; /// <summary> - /// The _logger + /// The _logger. /// </summary> private readonly ILogger<Profiler> _logger; @@ -37,7 +37,6 @@ namespace MediaBrowser.Controller.Library _stopwatch = new Stopwatch(); _stopwatch.Start(); } - #region IDisposable Members /// <summary> /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. @@ -67,10 +66,9 @@ namespace MediaBrowser.Controller.Library message = string.Format("{0} took {1} seconds.", _name, ((float)_stopwatch.ElapsedMilliseconds / 1000).ToString("#0.000")); } + _logger.LogInformation(message); } } - - #endregion } } diff --git a/MediaBrowser.Controller/Library/SearchHintInfo.cs b/MediaBrowser.Controller/Library/SearchHintInfo.cs index 692431e34..897c2b7f4 100644 --- a/MediaBrowser.Controller/Library/SearchHintInfo.cs +++ b/MediaBrowser.Controller/Library/SearchHintInfo.cs @@ -3,7 +3,7 @@ using MediaBrowser.Controller.Entities; namespace MediaBrowser.Controller.Library { /// <summary> - /// Class SearchHintInfo + /// Class SearchHintInfo. /// </summary> public class SearchHintInfo { diff --git a/MediaBrowser.Controller/Library/TVUtils.cs b/MediaBrowser.Controller/Library/TVUtils.cs index fd5fb6748..a3aa6019e 100644 --- a/MediaBrowser.Controller/Library/TVUtils.cs +++ b/MediaBrowser.Controller/Library/TVUtils.cs @@ -3,7 +3,7 @@ using System; namespace MediaBrowser.Controller.Library { /// <summary> - /// Class TVUtils + /// Class TVUtils. /// </summary> public static class TVUtils { @@ -38,8 +38,9 @@ namespace MediaBrowser.Controller.Library }; } - return new DayOfWeek[] { }; + return Array.Empty<DayOfWeek>(); } + return null; } } diff --git a/MediaBrowser.Controller/Library/UserDataSaveEventArgs.cs b/MediaBrowser.Controller/Library/UserDataSaveEventArgs.cs index 3e7351b8b..fa0192784 100644 --- a/MediaBrowser.Controller/Library/UserDataSaveEventArgs.cs +++ b/MediaBrowser.Controller/Library/UserDataSaveEventArgs.cs @@ -6,7 +6,7 @@ using MediaBrowser.Model.Entities; namespace MediaBrowser.Controller.Library { /// <summary> - /// Class UserDataSaveEventArgs + /// Class UserDataSaveEventArgs. /// </summary> public class UserDataSaveEventArgs : EventArgs { diff --git a/MediaBrowser.Controller/LiveTv/ChannelInfo.cs b/MediaBrowser.Controller/LiveTv/ChannelInfo.cs index 70477fce7..67d0df4fd 100644 --- a/MediaBrowser.Controller/LiveTv/ChannelInfo.cs +++ b/MediaBrowser.Controller/LiveTv/ChannelInfo.cs @@ -3,7 +3,7 @@ using MediaBrowser.Model.LiveTv; namespace MediaBrowser.Controller.LiveTv { /// <summary> - /// Class ChannelInfo + /// Class ChannelInfo. /// </summary> public class ChannelInfo { @@ -44,13 +44,13 @@ namespace MediaBrowser.Controller.LiveTv public ChannelType ChannelType { get; set; } /// <summary> - /// Supply the image path if it can be accessed directly from the file system + /// Supply the image path if it can be accessed directly from the file system. /// </summary> /// <value>The image path.</value> public string ImagePath { get; set; } /// <summary> - /// Supply the image url if it can be downloaded + /// Supply the image url if it can be downloaded. /// </summary> /// <value>The image URL.</value> public string ImageUrl { get; set; } @@ -67,8 +67,11 @@ namespace MediaBrowser.Controller.LiveTv public bool? IsFavorite { get; set; } public bool? IsHD { get; set; } + public string AudioCodec { get; set; } + public string VideoCodec { get; set; } + public string[] Tags { get; set; } } } diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs index bc3bf78f0..f619b011b 100644 --- a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs +++ b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs @@ -14,7 +14,7 @@ using MediaBrowser.Model.Querying; namespace MediaBrowser.Controller.LiveTv { /// <summary> - /// Manages all live tv services installed on the server + /// Manages all live tv services installed on the server. /// </summary> public interface ILiveTvManager { @@ -286,8 +286,11 @@ namespace MediaBrowser.Controller.LiveTv public class ActiveRecordingInfo { public string Id { get; set; } + public string Path { get; set; } + public TimerInfo Timer { get; set; } + public CancellationTokenSource CancellationTokenSource { get; set; } } } diff --git a/MediaBrowser.Controller/LiveTv/ITunerHost.cs b/MediaBrowser.Controller/LiveTv/ITunerHost.cs index 240ba8c23..3679e4f78 100644 --- a/MediaBrowser.Controller/LiveTv/ITunerHost.cs +++ b/MediaBrowser.Controller/LiveTv/ITunerHost.cs @@ -50,6 +50,7 @@ namespace MediaBrowser.Controller.LiveTv get; } } + public interface IConfigurableTunerHost { /// <summary> diff --git a/MediaBrowser.Controller/LiveTv/LiveTvConflictException.cs b/MediaBrowser.Controller/LiveTv/LiveTvConflictException.cs index df8f91eec..0e09d1aeb 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvConflictException.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvConflictException.cs @@ -9,12 +9,11 @@ namespace MediaBrowser.Controller.LiveTv { public LiveTvConflictException() { - } + public LiveTvConflictException(string message) : base(message) { - } } } diff --git a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs index 5b901d223..472b061e6 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs @@ -140,14 +140,14 @@ namespace MediaBrowser.Controller.LiveTv /// <summary> /// Returns the folder containing the item. - /// If the item is a folder, it returns the folder itself + /// If the item is a folder, it returns the folder itself. /// </summary> /// <value>The containing folder path.</value> [JsonIgnore] public override string ContainingFolderPath => Path; //[JsonIgnore] - //public override string MediaType + // public override string MediaType //{ // get // { diff --git a/MediaBrowser.Controller/LiveTv/ProgramInfo.cs b/MediaBrowser.Controller/LiveTv/ProgramInfo.cs index 5d0f13192..d06a15323 100644 --- a/MediaBrowser.Controller/LiveTv/ProgramInfo.cs +++ b/MediaBrowser.Controller/LiveTv/ProgramInfo.cs @@ -18,7 +18,7 @@ namespace MediaBrowser.Controller.LiveTv public string ChannelId { get; set; } /// <summary> - /// Name of the program + /// Name of the program. /// </summary> public string Name { get; set; } @@ -95,13 +95,13 @@ namespace MediaBrowser.Controller.LiveTv public string EpisodeTitle { get; set; } /// <summary> - /// Supply the image path if it can be accessed directly from the file system + /// Supply the image path if it can be accessed directly from the file system. /// </summary> /// <value>The image path.</value> public string ImagePath { get; set; } /// <summary> - /// Supply the image url if it can be downloaded + /// Supply the image url if it can be downloaded. /// </summary> /// <value>The image URL.</value> public string ImageUrl { get; set; } @@ -199,6 +199,7 @@ namespace MediaBrowser.Controller.LiveTv public string Etag { get; set; } public Dictionary<string, string> ProviderIds { get; set; } + public Dictionary<string, string> SeriesProviderIds { get; set; } public ProgramInfo() diff --git a/MediaBrowser.Controller/LiveTv/RecordingInfo.cs b/MediaBrowser.Controller/LiveTv/RecordingInfo.cs index 432388d6b..b9e0218ab 100644 --- a/MediaBrowser.Controller/LiveTv/RecordingInfo.cs +++ b/MediaBrowser.Controller/LiveTv/RecordingInfo.cs @@ -169,13 +169,13 @@ namespace MediaBrowser.Controller.LiveTv public float? CommunityRating { get; set; } /// <summary> - /// Supply the image path if it can be accessed directly from the file system + /// Supply the image path if it can be accessed directly from the file system. /// </summary> /// <value>The image path.</value> public string ImagePath { get; set; } /// <summary> - /// Supply the image url if it can be downloaded + /// Supply the image url if it can be downloaded. /// </summary> /// <value>The image URL.</value> public string ImageUrl { get; set; } diff --git a/MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs b/MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs index 4fbd496c5..6e7acaae3 100644 --- a/MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs +++ b/MediaBrowser.Controller/LiveTv/SeriesTimerInfo.cs @@ -57,6 +57,7 @@ namespace MediaBrowser.Controller.LiveTv public bool RecordAnyChannel { get; set; } public int KeepUpTo { get; set; } + public KeepUntil KeepUntil { get; set; } public bool SkipEpisodesInLibrary { get; set; } diff --git a/MediaBrowser.Controller/LiveTv/TimerInfo.cs b/MediaBrowser.Controller/LiveTv/TimerInfo.cs index 46774b2b7..df98bb6af 100644 --- a/MediaBrowser.Controller/LiveTv/TimerInfo.cs +++ b/MediaBrowser.Controller/LiveTv/TimerInfo.cs @@ -18,7 +18,9 @@ namespace MediaBrowser.Controller.LiveTv } public Dictionary<string, string> ProviderIds { get; set; } + public Dictionary<string, string> SeriesProviderIds { get; set; } + public string[] Tags { get; set; } /// <summary> @@ -146,10 +148,15 @@ namespace MediaBrowser.Controller.LiveTv public bool IsRepeat { get; set; } public string HomePageUrl { get; set; } + public float? CommunityRating { get; set; } + public string OfficialRating { get; set; } + public string[] Genres { get; set; } + public string RecordingPath { get; set; } + public KeepUntil KeepUntil { get; set; } } } diff --git a/MediaBrowser.Controller/LiveTv/TunerChannelMapping.cs b/MediaBrowser.Controller/LiveTv/TunerChannelMapping.cs index cb02da635..df3f55c26 100644 --- a/MediaBrowser.Controller/LiveTv/TunerChannelMapping.cs +++ b/MediaBrowser.Controller/LiveTv/TunerChannelMapping.cs @@ -3,8 +3,11 @@ namespace MediaBrowser.Controller.LiveTv public class TunerChannelMapping { public string Name { get; set; } + public string ProviderChannelName { get; set; } + public string ProviderChannelId { get; set; } + public string Id { get; set; } } } diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 73e966344..67f17f7a5 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -13,8 +13,8 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.5" /> - <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="3.1.5" /> + <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.6" /> + <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="3.1.6" /> </ItemGroup> <ItemGroup> diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 8ce106469..ffbda42c3 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; +using System.Runtime.InteropServices; using System.Text; using System.Threading; using Jellyfin.Data.Enums; @@ -74,7 +75,8 @@ namespace MediaBrowser.Controller.MediaEncoding {"omx", hwEncoder + "_omx"}, {hwEncoder + "_v4l2m2m", hwEncoder + "_v4l2m2m"}, {"mediacodec", hwEncoder + "_mediacodec"}, - {"vaapi", hwEncoder + "_vaapi"} + {"vaapi", hwEncoder + "_vaapi"}, + {"videotoolbox", hwEncoder + "_videotoolbox"} }; if (!string.IsNullOrEmpty(hwType) @@ -104,11 +106,12 @@ namespace MediaBrowser.Controller.MediaEncoding return false; } - return true; + return _mediaEncoder.SupportsHwaccel("vaapi"); + } /// <summary> - /// Gets the name of the output video codec + /// Gets the name of the output video codec. /// </summary> public string GetVideoEncoder(EncodingJobInfo state, EncodingOptions encodingOptions) { @@ -285,7 +288,7 @@ namespace MediaBrowser.Controller.MediaEncoding } /// <summary> - /// Infers the audio codec based on the url + /// Infers the audio codec based on the url. /// </summary> public string InferAudioCodec(string container) { @@ -445,33 +448,61 @@ namespace MediaBrowser.Controller.MediaEncoding public string GetInputArgument(EncodingJobInfo state, EncodingOptions encodingOptions) { var arg = new StringBuilder(); + var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, encodingOptions) ?? string.Empty; + var outputVideoCodec = GetVideoEncoder(state, encodingOptions) ?? string.Empty; + var isVaapiDecoder = videoDecoder.IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1; + var isVaapiEncoder = outputVideoCodec.IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1; + var isQsvDecoder = videoDecoder.IndexOf("qsv", StringComparison.OrdinalIgnoreCase) != -1; + var isQsvEncoder = outputVideoCodec.IndexOf("qsv", StringComparison.OrdinalIgnoreCase) != -1; + var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + var isLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux); + + if (!IsCopyCodec(outputVideoCodec)) + { + if (state.IsVideoRequest + && IsVaapiSupported(state) + && string.Equals(encodingOptions.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase)) + { + if (isVaapiDecoder) + { + arg.Append("-hwaccel_output_format vaapi ") + .Append("-vaapi_device ") + .Append(encodingOptions.VaapiDevice) + .Append(" "); + } + else if (!isVaapiDecoder && isVaapiEncoder) + { + arg.Append("-vaapi_device ") + .Append(encodingOptions.VaapiDevice) + .Append(" "); + } + } - if (state.IsVideoRequest - && string.Equals(encodingOptions.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase)) - { - arg.Append("-hwaccel vaapi -hwaccel_output_format vaapi") - .Append(" -vaapi_device ") - .Append(encodingOptions.VaapiDevice) - .Append(' '); - } - - if (state.IsVideoRequest - && string.Equals(encodingOptions.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase)) - { - var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, encodingOptions); - var outputVideoCodec = GetVideoEncoder(state, encodingOptions); - - var hasTextSubs = state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; - - if (!hasTextSubs) + if (state.IsVideoRequest + && string.Equals(encodingOptions.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase)) { - // While using QSV encoder - if ((outputVideoCodec ?? string.Empty).IndexOf("qsv", StringComparison.OrdinalIgnoreCase) != -1) + var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; + + if (isQsvEncoder) { - // While using QSV decoder - if ((videoDecoder ?? string.Empty).IndexOf("qsv", StringComparison.OrdinalIgnoreCase) != -1) + if (isQsvDecoder) { - arg.Append("-hwaccel qsv "); + if (isLinux) + { + if (hasGraphicalSubs) + { + arg.Append("-init_hw_device qsv=hw -filter_hw_device hw "); + } + else + { + arg.Append("-hwaccel qsv -init_hw_device qsv=hw "); + } + } + + if (isWindows) + { + arg.Append("-hwaccel qsv -init_hw_device qsv=hw "); + } } // While using SW decoder else @@ -527,6 +558,8 @@ namespace MediaBrowser.Controller.MediaEncoding || codec.IndexOf("hevc", StringComparison.OrdinalIgnoreCase) != -1; } + // TODO This is auto inserted into the mpegts mux so it might not be needed + // https://www.ffmpeg.org/ffmpeg-bitstream-filters.html#h264_005fmp4toannexb public string GetBitStreamArgs(MediaStream stream) { if (IsH264(stream)) @@ -551,8 +584,8 @@ namespace MediaBrowser.Controller.MediaEncoding { if (string.Equals(videoCodec, "libvpx", StringComparison.OrdinalIgnoreCase)) { - // With vpx when crf is used, b:v becomes a max rate - // https://trac.ffmpeg.org/wiki/vpxEncodingGuide. + // When crf is used with vpx, b:v becomes a max rate + // https://trac.ffmpeg.org/wiki/Encode/VP9 return string.Format( CultureInfo.InvariantCulture, " -maxrate:v {0} -bufsize:v {1} -b:v {0}", @@ -703,7 +736,7 @@ namespace MediaBrowser.Controller.MediaEncoding } /// <summary> - /// Gets the video bitrate to specify on the command line + /// Gets the video bitrate to specify on the command line. /// </summary> public string GetVideoQualityParam(EncodingJobInfo state, string videoEncoder, EncodingOptions encodingOptions, string defaultPreset) { @@ -759,7 +792,6 @@ namespace MediaBrowser.Controller.MediaEncoding } param += " -look_ahead 0"; - } else if (string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase) // h264 (h264_nvenc) || string.Equals(videoEncoder, "hevc_nvenc", StringComparison.OrdinalIgnoreCase)) @@ -768,7 +800,7 @@ namespace MediaBrowser.Controller.MediaEncoding { case "veryslow": - param += "-preset slow"; //lossless is only supported on maxwell and newer(2014+) + param += "-preset slow"; // lossless is only supported on maxwell and newer(2014+) break; case "slow": @@ -793,6 +825,34 @@ namespace MediaBrowser.Controller.MediaEncoding break; } } + else if (string.Equals(videoEncoder, "h264_amf", StringComparison.OrdinalIgnoreCase) + || string.Equals(videoEncoder, "hevc_amf", StringComparison.OrdinalIgnoreCase)) + { + switch (encodingOptions.EncoderPreset) + { + case "veryslow": + case "slow": + case "slower": + param += "-quality quality"; + break; + + case "medium": + param += "-quality balanced"; + break; + + case "fast": + case "faster": + case "veryfast": + case "superfast": + case "ultrafast": + param += "-quality speed"; + break; + + default: + param += "-quality speed"; + break; + } + } else if (string.Equals(videoEncoder, "libvpx", StringComparison.OrdinalIgnoreCase)) // webm { // Values 0-3, 0 being highest quality but slower @@ -999,7 +1059,7 @@ namespace MediaBrowser.Controller.MediaEncoding { if (string.IsNullOrEmpty(videoStream.Profile)) { - //return false; + // return false; } var requestedProfile = requestedProfiles[0]; @@ -1072,7 +1132,7 @@ namespace MediaBrowser.Controller.MediaEncoding { if (!videoStream.Level.HasValue) { - //return false; + // return false; } if (videoStream.Level.HasValue && videoStream.Level.Value > requestLevel) @@ -1301,7 +1361,7 @@ namespace MediaBrowser.Controller.MediaEncoding } /// <summary> - /// Gets the number of audio channels to specify on the command line + /// Gets the number of audio channels to specify on the command line. /// </summary> /// <param name="state">The state.</param> /// <param name="audioStream">The audio stream.</param> @@ -1327,7 +1387,6 @@ namespace MediaBrowser.Controller.MediaEncoding // wmav2 currently only supports two channel output transcoderChannelLimit = 2; } - else if (codec.IndexOf("mp3", StringComparison.OrdinalIgnoreCase) != -1) { // libmp3lame currently only supports two channel output @@ -1339,7 +1398,7 @@ namespace MediaBrowser.Controller.MediaEncoding transcoderChannelLimit = 6; } - var isTranscodingAudio = !EncodingHelper.IsCopyCodec(codec); + var isTranscodingAudio = !IsCopyCodec(codec); int? resultChannels = state.GetRequestedAudioChannels(codec); if (isTranscodingAudio) @@ -1462,7 +1521,6 @@ namespace MediaBrowser.Controller.MediaEncoding " -map 0:{0}", state.AudioStream.Index); } - else { args += " -map -0:a"; @@ -1489,7 +1547,7 @@ namespace MediaBrowser.Controller.MediaEncoding } /// <summary> - /// Determines which stream will be used for playback + /// Determines which stream will be used for playback. /// </summary> /// <param name="allStream">All stream.</param> /// <param name="desiredIndex">Index of the desired.</param> @@ -1528,8 +1586,9 @@ namespace MediaBrowser.Controller.MediaEncoding EncodingOptions options, string outputVideoCodec) { - var outputSizeParam = string.Empty; + outputVideoCodec ??= string.Empty; + var outputSizeParam = string.Empty; var request = state.BaseRequest; // Add resolution params, if specified @@ -1543,28 +1602,28 @@ namespace MediaBrowser.Controller.MediaEncoding var index = outputSizeParam.IndexOf("hwdownload", StringComparison.OrdinalIgnoreCase); if (index != -1) { - outputSizeParam = "," + outputSizeParam.Substring(index); + outputSizeParam = outputSizeParam.Substring(index); } else { index = outputSizeParam.IndexOf("format", StringComparison.OrdinalIgnoreCase); if (index != -1) { - outputSizeParam = "," + outputSizeParam.Substring(index); + outputSizeParam = outputSizeParam.Substring(index); } else { index = outputSizeParam.IndexOf("yadif", StringComparison.OrdinalIgnoreCase); if (index != -1) { - outputSizeParam = "," + outputSizeParam.Substring(index); + outputSizeParam = outputSizeParam.Substring(index); } else { index = outputSizeParam.IndexOf("scale", StringComparison.OrdinalIgnoreCase); if (index != -1) { - outputSizeParam = "," + outputSizeParam.Substring(index); + outputSizeParam = outputSizeParam.Substring(index); } } } @@ -1572,44 +1631,31 @@ namespace MediaBrowser.Controller.MediaEncoding } var videoSizeParam = string.Empty; - var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, options); + var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, options) ?? string.Empty; + var isLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux); // Setup subtitle scaling if (state.VideoStream != null && state.VideoStream.Width.HasValue && state.VideoStream.Height.HasValue) { - // force_original_aspect_ratio=decrease - // Enable decreasing output video width or height if necessary to keep the original aspect ratio - videoSizeParam = string.Format( - CultureInfo.InvariantCulture, - "scale={0}:{1}:force_original_aspect_ratio=decrease", - state.VideoStream.Width.Value, - state.VideoStream.Height.Value); - - // For QSV, feed it into hardware encoder now - if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase)) - { - videoSizeParam += ",hwupload=extra_hw_frames=64"; - } + // Adjust the size of graphical subtitles to fit the video stream. + var videoStream = state.VideoStream; + var inputWidth = videoStream?.Width; + var inputHeight = videoStream?.Height; + var (width, height) = GetFixedOutputSize(inputWidth, inputHeight, request.Width, request.Height, request.MaxWidth, request.MaxHeight); - // For VAAPI and CUVID decoder - // these encoders cannot automatically adjust the size of graphical subtitles to fit the output video, - // thus needs to be manually adjusted. - if ((IsVaapiSupported(state) && string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase)) - || (videoDecoder ?? string.Empty).IndexOf("cuvid", StringComparison.OrdinalIgnoreCase) != -1) + if (width.HasValue && height.HasValue) { - var videoStream = state.VideoStream; - var inputWidth = videoStream?.Width; - var inputHeight = videoStream?.Height; - var (width, height) = GetFixedOutputSize(inputWidth, inputHeight, request.Width, request.Height, request.MaxWidth, request.MaxHeight); - - if (width.HasValue && height.HasValue) - { - videoSizeParam = string.Format( + videoSizeParam = string.Format( CultureInfo.InvariantCulture, - "scale={0}:{1}:force_original_aspect_ratio=decrease", + "scale={0}x{1}", width.Value, height.Value); - } + } + + // For QSV, feed it into hardware encoder now + if (isLinux && string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase)) + { + videoSizeParam += ",hwupload=extra_hw_frames=64"; } } @@ -1622,7 +1668,10 @@ namespace MediaBrowser.Controller.MediaEncoding : state.SubtitleStream.Index; // Setup default filtergraph utilizing FFMpeg overlay() and FFMpeg scale() (see the return of this function for index reference) - var retStr = " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay{3}\""; + // Always put the scaler before the overlay for better performance + var retStr = !string.IsNullOrEmpty(outputSizeParam) ? + " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay\"" : + " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay\""; // When the input may or may not be hardware VAAPI decodable if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase)) @@ -1632,12 +1681,11 @@ namespace MediaBrowser.Controller.MediaEncoding [sub]: SW scaling subtitle to FixedOutputSize [base][sub]: SW overlay */ - outputSizeParam = outputSizeParam.TrimStart(','); retStr = " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3},hwdownload[base];[base][sub]overlay,format=nv12,hwupload\""; } // If we're hardware VAAPI decoding and software encoding, download frames from the decoder first - else if (IsVaapiSupported(state) && string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase) + else if (IsVaapiSupported(state) && videoDecoder.IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1 && string.Equals(outputVideoCodec, "libx264", StringComparison.OrdinalIgnoreCase)) { /* @@ -1645,24 +1693,21 @@ namespace MediaBrowser.Controller.MediaEncoding [sub]: SW scaling subtitle to FixedOutputSize [base][sub]: SW overlay */ - outputSizeParam = outputSizeParam.TrimStart(','); retStr = " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay\""; } - else if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase)) { /* QSV in FFMpeg can now setup hardware overlay for transcodes. For software decoding and hardware encoding option, frames must be hwuploaded into hardware with fixed frame size. + Currently only supports linux. */ - if (!string.IsNullOrEmpty(videoDecoder) && videoDecoder.Contains("qsv", StringComparison.OrdinalIgnoreCase)) + if (isLinux) { - retStr = " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay_qsv=x=(W-w)/2:y=(H-h)/2{3}\""; - } - else - { - retStr = " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]hwupload=extra_hw_frames=64[v];[v][sub]overlay_qsv=x=(W-w)/2:y=(H-h)/2{3}\""; + retStr = !string.IsNullOrEmpty(outputSizeParam) ? + " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay_qsv\"" : + " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay_qsv\""; } } @@ -1688,6 +1733,7 @@ namespace MediaBrowser.Controller.MediaEncoding { return (null, null); } + if (!videoHeight.HasValue && !requestedHeight.HasValue) { return (null, null); @@ -1733,10 +1779,8 @@ namespace MediaBrowser.Controller.MediaEncoding requestedMaxWidth, requestedMaxHeight); - var hasTextSubs = state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; - if ((string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase) - || (string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) && !hasTextSubs)) + || string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase)) && width.HasValue && height.HasValue) { @@ -1745,7 +1789,7 @@ namespace MediaBrowser.Controller.MediaEncoding // output dimensions. Output dimensions are guaranteed to be even. var outputWidth = width.Value; var outputHeight = height.Value; - var vaapi_or_qsv = string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) ? "qsv" : "vaapi"; + var qsv_or_vaapi = string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase); if (!videoWidth.HasValue || outputWidth != videoWidth.Value @@ -1753,17 +1797,24 @@ namespace MediaBrowser.Controller.MediaEncoding || outputHeight != videoHeight.Value) { // Force nv12 pixel format to enable 10-bit to 8-bit colour conversion. + // use vpp_qsv filter to avoid green bar when the fixed output size is requested. filters.Add( string.Format( CultureInfo.InvariantCulture, - "scale_{0}=w={1}:h={2}:format=nv12", - vaapi_or_qsv, + "{0}=w={1}:h={2}:format=nv12{3}", + qsv_or_vaapi ? "vpp_qsv" : "scale_vaapi", outputWidth, - outputHeight)); + outputHeight, + (qsv_or_vaapi && state.DeInterlace("h264", true)) ? ":deinterlace=1" : string.Empty)); } else { - filters.Add(string.Format(CultureInfo.InvariantCulture, "scale_{0}=format=nv12", vaapi_or_qsv)); + filters.Add( + string.Format( + CultureInfo.InvariantCulture, + "{0}=format=nv12{1}", + qsv_or_vaapi ? "vpp_qsv" : "scale_vaapi", + (qsv_or_vaapi && state.DeInterlace("h264", true)) ? ":deinterlace=1" : string.Empty)); } } else if ((videoDecoder ?? string.Empty).IndexOf("cuvid", StringComparison.OrdinalIgnoreCase) != -1 @@ -1933,11 +1984,11 @@ namespace MediaBrowser.Controller.MediaEncoding break; case Video3DFormat.FullSideBySide: filter = "crop=iw/2:ih:0:0,setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1,scale={0}:trunc({0}/dar/2)*2"; - //fsbs crop width in half,set the display aspect,crop out any black bars we may have made the scale width to requestedWidth. + // fsbs crop width in half,set the display aspect,crop out any black bars we may have made the scale width to requestedWidth. break; case Video3DFormat.HalfTopAndBottom: filter = "crop=iw:ih/2:0:0,scale=(iw*2):ih),setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1,scale={0}:trunc({0}/dar/2)*2"; - //htab crop height in half,scale to correct size, set the display aspect,crop out any black bars we may have made the scale width to requestedWidth + // htab crop height in half,scale to correct size, set the display aspect,crop out any black bars we may have made the scale width to requestedWidth break; case Video3DFormat.FullTopAndBottom: filter = "crop=iw:ih/2:0:0,setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1,scale={0}:trunc({0}/dar/2)*2"; @@ -1965,7 +2016,7 @@ namespace MediaBrowser.Controller.MediaEncoding } /// <summary> - /// If we're going to put a fixed size on the command line, this will calculate it + /// If we're going to put a fixed size on the command line, this will calculate it. /// </summary> public string GetOutputSizeParam( EncodingJobInfo state, @@ -1979,13 +2030,11 @@ namespace MediaBrowser.Controller.MediaEncoding var videoStream = state.VideoStream; var filters = new List<string>(); - var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, options); + var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, options) ?? string.Empty; var inputWidth = videoStream?.Width; var inputHeight = videoStream?.Height; var threeDFormat = state.MediaSource.Video3DFormat; - var hasTextSubs = state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; - // When the input may or may not be hardware VAAPI decodable if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase)) { @@ -1996,20 +2045,16 @@ namespace MediaBrowser.Controller.MediaEncoding // When the input may or may not be hardware QSV decodable else if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase)) { - if (!hasTextSubs) - { - filters.Add("format=nv12|qsv"); - filters.Add("hwupload=extra_hw_frames=64"); - } + filters.Add("format=nv12|qsv"); + filters.Add("hwupload=extra_hw_frames=64"); } // If we're hardware VAAPI decoding and software encoding, download frames from the decoder first - else if (IsVaapiSupported(state) && string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase) + else if (IsVaapiSupported(state) && videoDecoder.IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1 && string.Equals(outputVideoCodec, "libx264", StringComparison.OrdinalIgnoreCase)) { var codec = videoStream.Codec.ToLowerInvariant(); - var isColorDepth10 = !string.IsNullOrEmpty(videoStream.Profile) && (videoStream.Profile.Contains("Main 10", StringComparison.OrdinalIgnoreCase) - || videoStream.Profile.Contains("High 10", StringComparison.OrdinalIgnoreCase)); + var isColorDepth10 = IsColorDepth10(state); // Assert 10-bit hardware VAAPI decodable if (isColorDepth10 && (string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase) @@ -2040,31 +2085,32 @@ namespace MediaBrowser.Controller.MediaEncoding { filters.Add(string.Format(CultureInfo.InvariantCulture, "deinterlace_vaapi")); } - else if (string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase)) - { - if (!hasTextSubs) - { - filters.Add(string.Format(CultureInfo.InvariantCulture, "deinterlace_qsv")); - } - } } // Add software deinterlace filter before scaling filter - if (((state.DeInterlace("h264", true) || state.DeInterlace("h265", true) || state.DeInterlace("hevc", true)) - && !string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase) - && !string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase)) - || (hasTextSubs && state.DeInterlace("h264", true) && string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase))) + if (state.DeInterlace("h264", true) || state.DeInterlace("h265", true) || state.DeInterlace("hevc", true)) { + var deintParam = string.Empty; var inputFramerate = videoStream?.RealFrameRate; // If it is already 60fps then it will create an output framerate that is much too high for roku and others to handle if (string.Equals(options.DeinterlaceMethod, "yadif_bob", StringComparison.OrdinalIgnoreCase) && (inputFramerate ?? 60) <= 30) { - filters.Add("yadif=1:-1:0"); + deintParam = "yadif=1:-1:0"; } else { - filters.Add("yadif=0:-1:0"); + deintParam = "yadif=0:-1:0"; + } + + if (!string.IsNullOrEmpty(deintParam)) + { + if (!string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase) + && !string.Equals(outputVideoCodec, "h264_qsv", StringComparison.OrdinalIgnoreCase) + && videoDecoder.IndexOf("h264_cuvid", StringComparison.OrdinalIgnoreCase) == -1) + { + filters.Add(deintParam); + } } } @@ -2250,7 +2296,7 @@ namespace MediaBrowser.Controller.MediaEncoding flags.Add("+ignidx"); } - if (state.GenPtsInput || EncodingHelper.IsCopyCodec(state.OutputVideoCodec)) + if (state.GenPtsInput || IsCopyCodec(state.OutputVideoCodec)) { flags.Add("+genpts"); } @@ -2276,7 +2322,8 @@ namespace MediaBrowser.Controller.MediaEncoding { inputModifier += " " + videoDecoder; - if ((videoDecoder ?? string.Empty).IndexOf("cuvid", StringComparison.OrdinalIgnoreCase) != -1) + if (!IsCopyCodec(state.OutputVideoCodec) + && (videoDecoder ?? string.Empty).IndexOf("cuvid", StringComparison.OrdinalIgnoreCase) != -1) { var videoStream = state.VideoStream; var inputWidth = videoStream?.Width; @@ -2289,11 +2336,19 @@ namespace MediaBrowser.Controller.MediaEncoding && width.HasValue && height.HasValue) { - inputModifier += string.Format( - CultureInfo.InvariantCulture, - " -resize {0}x{1}", - width.Value, - height.Value); + if (width.HasValue && height.HasValue) + { + inputModifier += string.Format( + CultureInfo.InvariantCulture, + " -resize {0}x{1}", + width.Value, + height.Value); + } + + if (state.DeInterlace("h264", true)) + { + inputModifier += " -deint 1"; + } } } } @@ -2509,20 +2564,21 @@ namespace MediaBrowser.Controller.MediaEncoding } /// <summary> - /// Gets the name of the output video codec + /// Gets the ffmpeg option string for the hardware accelerated video decoder. /// </summary> + /// <param name="state">The encoding job info.</param> + /// <param name="encodingOptions">The encoding options.</param> + /// <returns>The option string or null if none available.</returns> protected string GetHardwareAcceleratedVideoDecoder(EncodingJobInfo state, EncodingOptions encodingOptions) { - if (EncodingHelper.IsCopyCodec(state.OutputVideoCodec)) + var videoStream = state.VideoStream; + + if (videoStream == null) { return null; } - return GetHardwareAcceleratedVideoDecoder(state.MediaSource.VideoType ?? VideoType.VideoFile, state.VideoStream, encodingOptions); - } - - public string GetHardwareAcceleratedVideoDecoder(VideoType videoType, MediaStream videoStream, EncodingOptions encodingOptions) - { + var videoType = state.MediaSource.VideoType ?? VideoType.VideoFile; // Only use alternative encoders for video files. // When using concat with folder rips, if the mfx session fails to initialize, ffmpeg will be stuck retrying and will not exit gracefully // Since transcoding of folder rips is expiremental anyway, it's not worth adding additional variables such as this. @@ -2531,10 +2587,23 @@ namespace MediaBrowser.Controller.MediaEncoding return null; } - if (videoStream != null - && !string.IsNullOrEmpty(videoStream.Codec) - && !string.IsNullOrEmpty(encodingOptions.HardwareAccelerationType)) + if (IsCopyCodec(state.OutputVideoCodec)) { + return null; + } + + if (!string.IsNullOrEmpty(videoStream.Codec) && !string.IsNullOrEmpty(encodingOptions.HardwareAccelerationType)) + { + var isColorDepth10 = IsColorDepth10(state); + + // Only hevc and vp9 formats have 10-bit hardware decoder support now. + if (isColorDepth10 && !(string.Equals(videoStream.Codec, "hevc", StringComparison.OrdinalIgnoreCase) + || string.Equals(videoStream.Codec, "h265", StringComparison.OrdinalIgnoreCase) + || string.Equals(videoStream.Codec, "vp9", StringComparison.OrdinalIgnoreCase))) + { + return null; + } + if (string.Equals(encodingOptions.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase)) { switch (videoStream.Codec.ToLowerInvariant()) @@ -2549,32 +2618,51 @@ namespace MediaBrowser.Controller.MediaEncoding encodingOptions.HardwareDecodingCodecs = Array.Empty<string>(); return null; } + return "-c:v h264_qsv"; } + break; case "hevc": case "h265": if (_mediaEncoder.SupportsDecoder("hevc_qsv") && encodingOptions.HardwareDecodingCodecs.Contains("hevc", StringComparer.OrdinalIgnoreCase)) { - //return "-c:v hevc_qsv -load_plugin hevc_hw "; - return "-c:v hevc_qsv"; + return (isColorDepth10 && + !encodingOptions.EnableDecodingColorDepth10Hevc) ? null : "-c:v hevc_qsv"; } + break; case "mpeg2video": if (_mediaEncoder.SupportsDecoder("mpeg2_qsv") && encodingOptions.HardwareDecodingCodecs.Contains("mpeg2video", StringComparer.OrdinalIgnoreCase)) { return "-c:v mpeg2_qsv"; } + break; case "vc1": if (_mediaEncoder.SupportsDecoder("vc1_qsv") && encodingOptions.HardwareDecodingCodecs.Contains("vc1", StringComparer.OrdinalIgnoreCase)) { return "-c:v vc1_qsv"; } + + break; + case "vp8": + if (_mediaEncoder.SupportsDecoder("vp8_qsv") && encodingOptions.HardwareDecodingCodecs.Contains("vp8", StringComparer.OrdinalIgnoreCase)) + { + return "-c:v vp8_qsv"; + } + + break; + case "vp9": + if (_mediaEncoder.SupportsDecoder("vp9_qsv") && encodingOptions.HardwareDecodingCodecs.Contains("vp9", StringComparer.OrdinalIgnoreCase)) + { + return (isColorDepth10 && + !encodingOptions.EnableDecodingColorDepth10Vp9) ? null : "-c:v vp9_qsv"; + } + break; } } - else if (string.Equals(encodingOptions.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase)) { switch (videoStream.Codec.ToLowerInvariant()) @@ -2583,43 +2671,57 @@ namespace MediaBrowser.Controller.MediaEncoding case "h264": if (_mediaEncoder.SupportsDecoder("h264_cuvid") && encodingOptions.HardwareDecodingCodecs.Contains("h264", StringComparer.OrdinalIgnoreCase)) { - // cuvid decoder does not support 10-bit input - if ((videoStream.BitDepth ?? 8) > 8) - { - encodingOptions.HardwareDecodingCodecs = Array.Empty<string>(); - return null; - } return "-c:v h264_cuvid"; } + break; case "hevc": case "h265": if (_mediaEncoder.SupportsDecoder("hevc_cuvid") && encodingOptions.HardwareDecodingCodecs.Contains("hevc", StringComparer.OrdinalIgnoreCase)) { - return "-c:v hevc_cuvid"; + return (isColorDepth10 && + !encodingOptions.EnableDecodingColorDepth10Hevc) ? null : "-c:v hevc_cuvid"; } + break; case "mpeg2video": if (_mediaEncoder.SupportsDecoder("mpeg2_cuvid") && encodingOptions.HardwareDecodingCodecs.Contains("mpeg2video", StringComparer.OrdinalIgnoreCase)) { return "-c:v mpeg2_cuvid"; } + break; case "vc1": if (_mediaEncoder.SupportsDecoder("vc1_cuvid") && encodingOptions.HardwareDecodingCodecs.Contains("vc1", StringComparer.OrdinalIgnoreCase)) { return "-c:v vc1_cuvid"; } + break; case "mpeg4": if (_mediaEncoder.SupportsDecoder("mpeg4_cuvid") && encodingOptions.HardwareDecodingCodecs.Contains("mpeg4", StringComparer.OrdinalIgnoreCase)) { return "-c:v mpeg4_cuvid"; } + + break; + case "vp8": + if (_mediaEncoder.SupportsDecoder("vp8_cuvid") && encodingOptions.HardwareDecodingCodecs.Contains("vp8", StringComparer.OrdinalIgnoreCase)) + { + return "-c:v vp8_cuvid"; + } + + break; + case "vp9": + if (_mediaEncoder.SupportsDecoder("vp9_cuvid") && encodingOptions.HardwareDecodingCodecs.Contains("vp9", StringComparer.OrdinalIgnoreCase)) + { + return (isColorDepth10 && + !encodingOptions.EnableDecodingColorDepth10Vp9) ? null : "-c:v vp9_cuvid"; + } + break; } } - else if (string.Equals(encodingOptions.HardwareAccelerationType, "mediacodec", StringComparison.OrdinalIgnoreCase)) { switch (videoStream.Codec.ToLowerInvariant()) @@ -2630,41 +2732,48 @@ namespace MediaBrowser.Controller.MediaEncoding { return "-c:v h264_mediacodec"; } + break; case "hevc": case "h265": if (_mediaEncoder.SupportsDecoder("hevc_mediacodec") && encodingOptions.HardwareDecodingCodecs.Contains("hevc", StringComparer.OrdinalIgnoreCase)) { - return "-c:v hevc_mediacodec"; + return (isColorDepth10 && + !encodingOptions.EnableDecodingColorDepth10Hevc) ? null : "-c:v hevc_mediacodec"; } + break; case "mpeg2video": if (_mediaEncoder.SupportsDecoder("mpeg2_mediacodec") && encodingOptions.HardwareDecodingCodecs.Contains("mpeg2video", StringComparer.OrdinalIgnoreCase)) { return "-c:v mpeg2_mediacodec"; } + break; case "mpeg4": if (_mediaEncoder.SupportsDecoder("mpeg4_mediacodec") && encodingOptions.HardwareDecodingCodecs.Contains("mpeg4", StringComparer.OrdinalIgnoreCase)) { return "-c:v mpeg4_mediacodec"; } + break; case "vp8": if (_mediaEncoder.SupportsDecoder("vp8_mediacodec") && encodingOptions.HardwareDecodingCodecs.Contains("vp8", StringComparer.OrdinalIgnoreCase)) { return "-c:v vp8_mediacodec"; } + break; case "vp9": if (_mediaEncoder.SupportsDecoder("vp9_mediacodec") && encodingOptions.HardwareDecodingCodecs.Contains("vp9", StringComparer.OrdinalIgnoreCase)) { - return "-c:v vp9_mediacodec"; + return (isColorDepth10 && + !encodingOptions.EnableDecodingColorDepth10Vp9) ? null : "-c:v vp9_mediacodec"; } + break; } } - else if (string.Equals(encodingOptions.HardwareAccelerationType, "omx", StringComparison.OrdinalIgnoreCase)) { switch (videoStream.Codec.ToLowerInvariant()) @@ -2675,51 +2784,185 @@ namespace MediaBrowser.Controller.MediaEncoding { return "-c:v h264_mmal"; } + break; case "mpeg2video": if (_mediaEncoder.SupportsDecoder("mpeg2_mmal") && encodingOptions.HardwareDecodingCodecs.Contains("mpeg2video", StringComparer.OrdinalIgnoreCase)) { return "-c:v mpeg2_mmal"; } + break; case "mpeg4": if (_mediaEncoder.SupportsDecoder("mpeg4_mmal") && encodingOptions.HardwareDecodingCodecs.Contains("mpeg4", StringComparer.OrdinalIgnoreCase)) { return "-c:v mpeg4_mmal"; } + break; case "vc1": if (_mediaEncoder.SupportsDecoder("vc1_mmal") && encodingOptions.HardwareDecodingCodecs.Contains("vc1", StringComparer.OrdinalIgnoreCase)) { return "-c:v vc1_mmal"; } + break; } } - else if (string.Equals(encodingOptions.HardwareAccelerationType, "amf", StringComparison.OrdinalIgnoreCase)) { - if (Environment.OSVersion.Platform == PlatformID.Win32NT) + switch (videoStream.Codec.ToLowerInvariant()) { - if (Environment.OSVersion.Version.Major > 6 || (Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor > 1)) - return "-hwaccel d3d11va"; - else - return "-hwaccel dxva2"; + case "avc": + case "h264": + return GetHwaccelType(state, encodingOptions, "h264"); + case "hevc": + case "h265": + return (isColorDepth10 && + !encodingOptions.EnableDecodingColorDepth10Hevc) ? null : GetHwaccelType(state, encodingOptions, "hevc"); + case "mpeg2video": + return GetHwaccelType(state, encodingOptions, "mpeg2video"); + case "vc1": + return GetHwaccelType(state, encodingOptions, "vc1"); + case "mpeg4": + return GetHwaccelType(state, encodingOptions, "mpeg4"); + case "vp9": + return (isColorDepth10 && + !encodingOptions.EnableDecodingColorDepth10Vp9) ? null : GetHwaccelType(state, encodingOptions, "vp9"); } - else + } + else if (string.Equals(encodingOptions.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase)) + { + switch (videoStream.Codec.ToLowerInvariant()) { - return "-hwaccel vaapi"; + case "avc": + case "h264": + return GetHwaccelType(state, encodingOptions, "h264"); + case "hevc": + case "h265": + return (isColorDepth10 && + !encodingOptions.EnableDecodingColorDepth10Hevc) ? null : GetHwaccelType(state, encodingOptions, "hevc"); + case "mpeg2video": + return GetHwaccelType(state, encodingOptions, "mpeg2video"); + case "vc1": + return GetHwaccelType(state, encodingOptions, "vc1"); + case "vp8": + return GetHwaccelType(state, encodingOptions, "vp8"); + case "vp9": + return (isColorDepth10 && + !encodingOptions.EnableDecodingColorDepth10Vp9) ? null : GetHwaccelType(state, encodingOptions, "vp9"); + } + } + else if (string.Equals(encodingOptions.HardwareAccelerationType, "videotoolbox", StringComparison.OrdinalIgnoreCase)) + { + switch (videoStream.Codec.ToLowerInvariant()) + { + case "avc": + case "h264": + if (_mediaEncoder.SupportsDecoder("h264_opencl") && encodingOptions.HardwareDecodingCodecs.Contains("h264", StringComparer.OrdinalIgnoreCase)) + { + return "-c:v h264_opencl"; + } + + break; + case "hevc": + case "h265": + if (_mediaEncoder.SupportsDecoder("hevc_opencl") && encodingOptions.HardwareDecodingCodecs.Contains("h264", StringComparer.OrdinalIgnoreCase)) + { + return (isColorDepth10 && + !encodingOptions.EnableDecodingColorDepth10Hevc) ? null : "-c:v hevc_opencl"; + } + + break; + case "mpeg2video": + if (_mediaEncoder.SupportsDecoder("mpeg2_opencl") && encodingOptions.HardwareDecodingCodecs.Contains("mpeg2video", StringComparer.OrdinalIgnoreCase)) + { + return "-c:v mpeg2_opencl"; + } + + break; + case "mpeg4": + if (_mediaEncoder.SupportsDecoder("mpeg4_opencl") && encodingOptions.HardwareDecodingCodecs.Contains("mpeg4", StringComparer.OrdinalIgnoreCase)) + { + return "-c:v mpeg4_opencl"; + } + + break; + case "vc1": + if (_mediaEncoder.SupportsDecoder("vc1_opencl") && encodingOptions.HardwareDecodingCodecs.Contains("vc1", StringComparer.OrdinalIgnoreCase)) + { + return "-c:v vc1_opencl"; + } + + break; + case "vp8": + if (_mediaEncoder.SupportsDecoder("vp8_opencl") && encodingOptions.HardwareDecodingCodecs.Contains("vc1", StringComparer.OrdinalIgnoreCase)) + { + return "-c:v vp8_opencl"; + } + + break; + case "vp9": + if (_mediaEncoder.SupportsDecoder("vp9_opencl") && encodingOptions.HardwareDecodingCodecs.Contains("vc1", StringComparer.OrdinalIgnoreCase)) + { + return (isColorDepth10 && + !encodingOptions.EnableDecodingColorDepth10Vp9) ? null : "-c:v vp9_opencl"; + } + + break; } } } + var whichCodec = videoStream.Codec.ToLowerInvariant(); + switch (whichCodec) + { + case "avc": + whichCodec = "h264"; + break; + case "h265": + whichCodec = "hevc"; + break; + } + // Avoid a second attempt if no hardware acceleration is being used - encodingOptions.HardwareDecodingCodecs = Array.Empty<string>(); + encodingOptions.HardwareDecodingCodecs = encodingOptions.HardwareDecodingCodecs.Where(val => val != whichCodec).ToArray(); // leave blank so ffmpeg will decide return null; } + /// <summary> + /// Gets a hwaccel type to use as a hardware decoder(dxva/vaapi) depending on the system + /// </summary> + public string GetHwaccelType(EncodingJobInfo state, EncodingOptions options, string videoCodec) + { + var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + var isLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux); + var isWindows8orLater = Environment.OSVersion.Version.Major > 6 || (Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor > 1); + var isDxvaSupported = _mediaEncoder.SupportsHwaccel("dxva2") || _mediaEncoder.SupportsHwaccel("d3d11va"); + + if ((isDxvaSupported || IsVaapiSupported(state)) && options.HardwareDecodingCodecs.Contains(videoCodec, StringComparer.OrdinalIgnoreCase)) + { + if (isLinux) + { + return "-hwaccel vaapi"; + } + + if (isWindows && isWindows8orLater) + { + return "-hwaccel d3d11va"; + } + + if (isWindows && !isWindows8orLater) + { + return "-hwaccel dxva2"; + } + } + + return null; + } + public string GetSubtitleEmbedArguments(EncodingJobInfo state) { if (state.SubtitleStream == null || state.SubtitleDeliveryMethod != SubtitleDeliveryMethod.Embed) @@ -2801,7 +3044,7 @@ namespace MediaBrowser.Controller.MediaEncoding args += " -mpegts_m2ts_mode 1"; } - if (EncodingHelper.IsCopyCodec(videoCodec)) + if (IsCopyCodec(videoCodec)) { if (state.VideoStream != null && string.Equals(state.OutputContainer, "ts", StringComparison.OrdinalIgnoreCase) @@ -2903,7 +3146,7 @@ namespace MediaBrowser.Controller.MediaEncoding var args = "-codec:a:0 " + codec; - if (EncodingHelper.IsCopyCodec(codec)) + if (IsCopyCodec(codec)) { return args; } @@ -2980,5 +3223,42 @@ namespace MediaBrowser.Controller.MediaEncoding { return string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase); } + + public static bool IsColorDepth10(EncodingJobInfo state) + { + var result = false; + var videoStream = state.VideoStream; + + if (videoStream != null) + { + if (!string.IsNullOrEmpty(videoStream.PixelFormat)) + { + result = videoStream.PixelFormat.Contains("p10", StringComparison.OrdinalIgnoreCase); + if (result) + { + return true; + } + } + + if (!string.IsNullOrEmpty(videoStream.Profile)) + { + result = videoStream.Profile.Contains("Main 10", StringComparison.OrdinalIgnoreCase) + || videoStream.Profile.Contains("High 10", StringComparison.OrdinalIgnoreCase) + || videoStream.Profile.Contains("Profile 2", StringComparison.OrdinalIgnoreCase); + if (result) + { + return true; + } + } + + result = (videoStream.BitDepth ?? 8) == 10; + if (result) + { + return true; + } + } + + return result; + } } } diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs index acf1aae89..b971b7c4b 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs @@ -127,13 +127,19 @@ namespace MediaBrowser.Controller.MediaEncoding public string AlbumCoverPath { get; set; } public string InputAudioSync { get; set; } + public string InputVideoSync { get; set; } + public TransportStreamTimestamp InputTimestamp { get; set; } public MediaStream AudioStream { get; set; } + public string[] SupportedAudioCodecs { get; set; } + public string[] SupportedVideoCodecs { get; set; } + public string InputContainer { get; set; } + public IsoType? IsoType { get; set; } public BaseEncodingJobOptions BaseRequest { get; set; } @@ -293,6 +299,7 @@ namespace MediaBrowser.Controller.MediaEncoding } public bool IsVideoRequest { get; set; } + public TranscodingJobType TranscodingType { get; set; } public EncodingJobInfo(TranscodingJobType jobType) @@ -418,7 +425,7 @@ namespace MediaBrowser.Controller.MediaEncoding } /// <summary> - /// Predicts the audio sample rate that will be in the output stream + /// Predicts the audio sample rate that will be in the output stream. /// </summary> public double? TargetVideoLevel { @@ -441,7 +448,7 @@ namespace MediaBrowser.Controller.MediaEncoding } /// <summary> - /// Predicts the audio sample rate that will be in the output stream + /// Predicts the audio sample rate that will be in the output stream. /// </summary> public int? TargetVideoBitDepth { @@ -476,7 +483,7 @@ namespace MediaBrowser.Controller.MediaEncoding } /// <summary> - /// Predicts the audio sample rate that will be in the output stream + /// Predicts the audio sample rate that will be in the output stream. /// </summary> public float? TargetFramerate { @@ -508,7 +515,7 @@ namespace MediaBrowser.Controller.MediaEncoding } /// <summary> - /// Predicts the audio sample rate that will be in the output stream + /// Predicts the audio sample rate that will be in the output stream. /// </summary> public int? TargetPacketLength { @@ -524,7 +531,7 @@ namespace MediaBrowser.Controller.MediaEncoding } /// <summary> - /// Predicts the audio sample rate that will be in the output stream + /// Predicts the audio sample rate that will be in the output stream. /// </summary> public string TargetVideoProfile { @@ -672,6 +679,7 @@ namespace MediaBrowser.Controller.MediaEncoding } public IProgress<double> Progress { get; set; } + public virtual void ReportTranscodingProgress(TimeSpan? transcodingPosition, float? framerate, double? percentComplete, long? bytesTranscoded, int? bitRate) { Progress.Report(percentComplete.Value); @@ -679,20 +687,20 @@ namespace MediaBrowser.Controller.MediaEncoding } /// <summary> - /// Enum TranscodingJobType + /// Enum TranscodingJobType. /// </summary> public enum TranscodingJobType { /// <summary> - /// The progressive + /// The progressive. /// </summary> Progressive, /// <summary> - /// The HLS + /// The HLS. /// </summary> Hls, /// <summary> - /// The dash + /// The dash. /// </summary> Dash } diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs index addc88174..8f6fcb9ab 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs @@ -9,9 +9,11 @@ namespace MediaBrowser.Controller.MediaEncoding public class EncodingJobOptions : BaseEncodingJobOptions { public string OutputDirectory { get; set; } + public string ItemId { get; set; } public string TempDirectory { get; set; } + public bool ReadInputAtNativeFramerate { get; set; } /// <summary> @@ -47,6 +49,7 @@ namespace MediaBrowser.Controller.MediaEncoding { SubtitleStreamIndex = info.SubtitleStreamIndex; } + StreamOptions = info.StreamOptions; } } @@ -81,7 +84,9 @@ namespace MediaBrowser.Controller.MediaEncoding public bool EnableAutoStreamCopy { get; set; } public bool AllowVideoStreamCopy { get; set; } + public bool AllowAudioStreamCopy { get; set; } + public bool BreakOnNonKeyFrames { get; set; } /// <summary> @@ -197,10 +202,15 @@ namespace MediaBrowser.Controller.MediaEncoding [ApiMember(Name = "MaxVideoBitDepth", Description = "Optional.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] public int? MaxVideoBitDepth { get; set; } + public bool RequireAvc { get; set; } + public bool DeInterlace { get; set; } + public bool RequireNonAnamorphic { get; set; } + public int? TranscodingMaxAudioChannels { get; set; } + public int? CpuCoreLimit { get; set; } public string LiveStreamId { get; set; } diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs index 37f0b11a7..f60e70239 100644 --- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs +++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs @@ -11,7 +11,7 @@ using MediaBrowser.Model.System; namespace MediaBrowser.Controller.MediaEncoding { /// <summary> - /// Interface IMediaEncoder + /// Interface IMediaEncoder. /// </summary> public interface IMediaEncoder : ITranscoderSupport { @@ -27,13 +27,27 @@ namespace MediaBrowser.Controller.MediaEncoding string EncoderPath { get; } /// <summary> - /// Supportses the decoder. + /// Whether given encoder codec is supported. + /// </summary> + /// <param name="encoder">The encoder.</param> + /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns> + bool SupportsEncoder(string encoder); + + /// <summary> + /// Whether given decoder codec is supported. /// </summary> /// <param name="decoder">The decoder.</param> /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns> bool SupportsDecoder(string decoder); /// <summary> + /// Whether given hardware acceleration type is supported. + /// </summary> + /// <param name="hwaccel">The hwaccel.</param> + /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns> + bool SupportsHwaccel(string hwaccel); + + /// <summary> /// Extracts the audio image. /// </summary> /// <param name="path">The path.</param> @@ -98,7 +112,6 @@ namespace MediaBrowser.Controller.MediaEncoding void SetFFmpegPath(); void UpdateEncoderPath(string path, string pathType); - bool SupportsEncoder(string encoder); IEnumerable<string> GetPrimaryPlaylistVobFiles(string path, IIsoMount isoMount, uint? titleNumber); } diff --git a/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs b/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs index 5cedc3d57..6c9bbb043 100644 --- a/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs +++ b/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs @@ -7,7 +7,7 @@ using MediaBrowser.Model.IO; namespace MediaBrowser.Controller.MediaEncoding { /// <summary> - /// Class MediaEncoderHelpers + /// Class MediaEncoderHelpers. /// </summary> public static class MediaEncoderHelpers { diff --git a/MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs b/MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs index b78ef0b80..39a47792a 100644 --- a/MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs +++ b/MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs @@ -8,9 +8,13 @@ namespace MediaBrowser.Controller.MediaEncoding public class MediaInfoRequest { public MediaSourceInfo MediaSource { get; set; } + public bool ExtractChapters { get; set; } + public DlnaProfileType MediaType { get; set; } + public IIsoMount MountedIso { get; set; } + public string[] PlayableStreamFileNames { get; set; } public MediaInfoRequest() diff --git a/MediaBrowser.Controller/Net/AuthenticatedAttribute.cs b/MediaBrowser.Controller/Net/AuthenticatedAttribute.cs index 9f2743ea1..87a7f7e10 100644 --- a/MediaBrowser.Controller/Net/AuthenticatedAttribute.cs +++ b/MediaBrowser.Controller/Net/AuthenticatedAttribute.cs @@ -31,9 +31,9 @@ namespace MediaBrowser.Controller.Net /// <summary> /// The request filter is executed before the service. /// </summary> - /// <param name="request">The http request wrapper</param> - /// <param name="response">The http response wrapper</param> - /// <param name="requestDto">The request DTO</param> + /// <param name="request">The http request wrapper.</param> + /// <param name="response">The http response wrapper.</param> + /// <param name="requestDto">The request DTO.</param> public void RequestFilter(IRequest request, HttpResponse response, object requestDto) { AuthService.Authenticate(request, this); @@ -60,8 +60,11 @@ namespace MediaBrowser.Controller.Net public interface IAuthenticationAttributes { bool EscapeParentalControl { get; } + bool AllowBeforeStartupWizard { get; } + bool AllowLocal { get; } + bool AllowLocalOnly { get; } string[] GetRoles(); diff --git a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs index f09cb3705..a54f6d57b 100644 --- a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs +++ b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs @@ -11,7 +11,7 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Controller.Net { /// <summary> - /// Starts sending data over a web socket periodically when a message is received, and then stops when a corresponding stop message is received + /// Starts sending data over a web socket periodically when a message is received, and then stops when a corresponding stop message is received. /// </summary> /// <typeparam name="TReturnDataType">The type of the T return data type.</typeparam> /// <typeparam name="TStateType">The type of the T state type.</typeparam> @@ -20,7 +20,7 @@ namespace MediaBrowser.Controller.Net where TReturnDataType : class { /// <summary> - /// The _active connections + /// The _active connections. /// </summary> private readonly List<Tuple<IWebSocketConnection, CancellationTokenSource, TStateType>> _activeConnections = new List<Tuple<IWebSocketConnection, CancellationTokenSource, TStateType>>(); @@ -38,7 +38,7 @@ namespace MediaBrowser.Controller.Net protected abstract Task<TReturnDataType> GetDataToSend(); /// <summary> - /// The logger + /// The logger. /// </summary> protected ILogger<BasePeriodicWebSocketListener<TReturnDataType, TStateType>> Logger; @@ -78,7 +78,7 @@ namespace MediaBrowser.Controller.Net } /// <summary> - /// Starts sending messages over a web socket + /// Starts sending messages over a web socket. /// </summary> /// <param name="message">The message.</param> private void Start(WebSocketMessageInfo message) @@ -180,7 +180,7 @@ namespace MediaBrowser.Controller.Net } /// <summary> - /// Stops sending messages over a web socket + /// Stops sending messages over a web socket. /// </summary> /// <param name="message">The message.</param> private void Stop(WebSocketMessageInfo message) @@ -214,7 +214,7 @@ namespace MediaBrowser.Controller.Net } catch (ObjectDisposedException) { - //TODO Investigate and properly fix. + // TODO Investigate and properly fix. } lock (_activeConnections) @@ -254,7 +254,9 @@ namespace MediaBrowser.Controller.Net public class WebSocketListenerState { public DateTime DateLastSendUtc { get; set; } + public long InitialDelayMs { get; set; } + public long IntervalMs { get; set; } } } diff --git a/MediaBrowser.Controller/Net/IHttpResultFactory.cs b/MediaBrowser.Controller/Net/IHttpResultFactory.cs index 25404fa78..609bd5f59 100644 --- a/MediaBrowser.Controller/Net/IHttpResultFactory.cs +++ b/MediaBrowser.Controller/Net/IHttpResultFactory.cs @@ -7,7 +7,7 @@ using MediaBrowser.Model.Services; namespace MediaBrowser.Controller.Net { /// <summary> - /// Interface IHttpResultFactory + /// Interface IHttpResultFactory. /// </summary> public interface IHttpResultFactory { diff --git a/MediaBrowser.Controller/Net/IHttpServer.cs b/MediaBrowser.Controller/Net/IHttpServer.cs index efb5f4ac3..e6609fae3 100644 --- a/MediaBrowser.Controller/Net/IHttpServer.cs +++ b/MediaBrowser.Controller/Net/IHttpServer.cs @@ -29,19 +29,19 @@ namespace MediaBrowser.Controller.Net void Init(IEnumerable<Type> serviceTypes, IEnumerable<IWebSocketListener> listener, IEnumerable<string> urlPrefixes); /// <summary> - /// If set, all requests will respond with this message + /// If set, all requests will respond with this message. /// </summary> string GlobalResponse { get; set; } /// <summary> - /// The HTTP request handler + /// The HTTP request handler. /// </summary> /// <param name="context"></param> /// <returns></returns> Task RequestHandler(HttpContext context); /// <summary> - /// Get the default CORS headers + /// Get the default CORS headers. /// </summary> /// <param name="req"></param> /// <returns></returns> diff --git a/MediaBrowser.Controller/Net/IWebSocketListener.cs b/MediaBrowser.Controller/Net/IWebSocketListener.cs index 0f472a2bc..7250a57b0 100644 --- a/MediaBrowser.Controller/Net/IWebSocketListener.cs +++ b/MediaBrowser.Controller/Net/IWebSocketListener.cs @@ -3,7 +3,7 @@ using System.Threading.Tasks; namespace MediaBrowser.Controller.Net { /// <summary> - ///This is an interface for listening to messages coming through a web socket connection + ///This is an interface for listening to messages coming through a web socket connection. /// </summary> public interface IWebSocketListener { diff --git a/MediaBrowser.Controller/Net/SecurityException.cs b/MediaBrowser.Controller/Net/SecurityException.cs index a5b94ea5e..f0d0b45a0 100644 --- a/MediaBrowser.Controller/Net/SecurityException.cs +++ b/MediaBrowser.Controller/Net/SecurityException.cs @@ -27,7 +27,7 @@ namespace MediaBrowser.Controller.Net /// <summary> /// Initializes a new instance of the <see cref="SecurityException"/> class. /// </summary> - /// <param name="message">The message that describes the error</param> + /// <param name="message">The message that describes the error.</param> /// <param name="innerException">The exception that is the cause of the current exception, or a null reference if no inner exception is specified.</param> public SecurityException(string message, Exception innerException) : base(message, innerException) diff --git a/MediaBrowser.Controller/Net/StaticResultOptions.cs b/MediaBrowser.Controller/Net/StaticResultOptions.cs index 071beaed1..85772e036 100644 --- a/MediaBrowser.Controller/Net/StaticResultOptions.cs +++ b/MediaBrowser.Controller/Net/StaticResultOptions.cs @@ -8,8 +8,11 @@ namespace MediaBrowser.Controller.Net public class StaticResultOptions { public string ContentType { get; set; } + public TimeSpan? CacheDuration { get; set; } + public DateTime? DateLastModified { get; set; } + public Func<Task<Stream>> ContentFactory { get; set; } public bool IsHeadRequest { get; set; } @@ -17,9 +20,11 @@ namespace MediaBrowser.Controller.Net public IDictionary<string, string> ResponseHeaders { get; set; } public Action OnComplete { get; set; } + public Action OnError { get; set; } public string Path { get; set; } + public long? ContentLength { get; set; } public FileShare FileShare { get; set; } diff --git a/MediaBrowser.Controller/Net/WebSocketMessageInfo.cs b/MediaBrowser.Controller/Net/WebSocketMessageInfo.cs index 5bf39cae6..be0b3ddc3 100644 --- a/MediaBrowser.Controller/Net/WebSocketMessageInfo.cs +++ b/MediaBrowser.Controller/Net/WebSocketMessageInfo.cs @@ -3,7 +3,7 @@ using MediaBrowser.Model.Net; namespace MediaBrowser.Controller.Net { /// <summary> - /// Class WebSocketMessageInfo + /// Class WebSocketMessageInfo. /// </summary> public class WebSocketMessageInfo : WebSocketMessage<string> { diff --git a/MediaBrowser.Controller/Persistence/IItemRepository.cs b/MediaBrowser.Controller/Persistence/IItemRepository.cs index 75fc43a04..0ae1b8bbf 100644 --- a/MediaBrowser.Controller/Persistence/IItemRepository.cs +++ b/MediaBrowser.Controller/Persistence/IItemRepository.cs @@ -9,12 +9,12 @@ using MediaBrowser.Model.Querying; namespace MediaBrowser.Controller.Persistence { /// <summary> - /// Provides an interface to implement an Item repository + /// Provides an interface to implement an Item repository. /// </summary> public interface IItemRepository : IRepository { /// <summary> - /// Saves an item + /// Saves an item. /// </summary> /// <param name="item">The item.</param> /// <param name="cancellationToken">The cancellation token.</param> @@ -43,14 +43,14 @@ namespace MediaBrowser.Controller.Persistence BaseItem RetrieveItem(Guid id); /// <summary> - /// Gets chapters for an item + /// Gets chapters for an item. /// </summary> /// <param name="id"></param> /// <returns></returns> List<ChapterInfo> GetChapters(BaseItem id); /// <summary> - /// Gets a single chapter for an item + /// Gets a single chapter for an item. /// </summary> /// <param name="id"></param> /// <param name="index"></param> diff --git a/MediaBrowser.Controller/Persistence/IRepository.cs b/MediaBrowser.Controller/Persistence/IRepository.cs index 56bf1dd5a..42f285076 100644 --- a/MediaBrowser.Controller/Persistence/IRepository.cs +++ b/MediaBrowser.Controller/Persistence/IRepository.cs @@ -3,12 +3,12 @@ using System; namespace MediaBrowser.Controller.Persistence { /// <summary> - /// Provides a base interface for all the repository interfaces + /// Provides a base interface for all the repository interfaces. /// </summary> public interface IRepository : IDisposable { /// <summary> - /// Gets the name of the repository + /// Gets the name of the repository. /// </summary> /// <value>The name.</value> string Name { get; } diff --git a/MediaBrowser.Controller/Persistence/IUserDataRepository.cs b/MediaBrowser.Controller/Persistence/IUserDataRepository.cs index a4bdf60d7..ba7c9fd50 100644 --- a/MediaBrowser.Controller/Persistence/IUserDataRepository.cs +++ b/MediaBrowser.Controller/Persistence/IUserDataRepository.cs @@ -5,7 +5,7 @@ using MediaBrowser.Controller.Entities; namespace MediaBrowser.Controller.Persistence { /// <summary> - /// Provides an interface to implement a UserData repository + /// Provides an interface to implement a UserData repository. /// </summary> public interface IUserDataRepository : IRepository { @@ -30,20 +30,19 @@ namespace MediaBrowser.Controller.Persistence UserItemData GetUserData(long userId, List<string> keys); /// <summary> - /// Return all user data associated with the given user + /// Return all user data associated with the given user. /// </summary> /// <param name="userId"></param> /// <returns></returns> List<UserItemData> GetAllUserData(long userId); /// <summary> - /// Save all user data associated with the given user + /// Save all user data associated with the given user. /// </summary> /// <param name="userId"></param> /// <param name="userData"></param> /// <param name="cancellationToken"></param> /// <returns></returns> void SaveAllUserData(long userId, UserItemData[] userData, CancellationToken cancellationToken); - } } diff --git a/MediaBrowser.Controller/Plugins/IPluginConfigurationPage.cs b/MediaBrowser.Controller/Plugins/IPluginConfigurationPage.cs index c156da924..077f5ab63 100644 --- a/MediaBrowser.Controller/Plugins/IPluginConfigurationPage.cs +++ b/MediaBrowser.Controller/Plugins/IPluginConfigurationPage.cs @@ -4,7 +4,7 @@ using MediaBrowser.Common.Plugins; namespace MediaBrowser.Controller.Plugins { /// <summary> - /// Interface IConfigurationPage + /// Interface IConfigurationPage. /// </summary> public interface IPluginConfigurationPage { @@ -34,16 +34,16 @@ namespace MediaBrowser.Controller.Plugins } /// <summary> - /// Enum ConfigurationPageType + /// Enum ConfigurationPageType. /// </summary> public enum ConfigurationPageType { /// <summary> - /// The plugin configuration + /// The plugin configuration. /// </summary> PluginConfiguration, /// <summary> - /// The none + /// The none. /// </summary> None } diff --git a/MediaBrowser.Controller/Plugins/IServerEntryPoint.cs b/MediaBrowser.Controller/Plugins/IServerEntryPoint.cs index 1e8654c4d..b44e2531e 100644 --- a/MediaBrowser.Controller/Plugins/IServerEntryPoint.cs +++ b/MediaBrowser.Controller/Plugins/IServerEntryPoint.cs @@ -22,6 +22,5 @@ namespace MediaBrowser.Controller.Plugins /// </summary> public interface IRunBeforeStartup { - } } diff --git a/MediaBrowser.Controller/Providers/IExternalId.cs b/MediaBrowser.Controller/Providers/IExternalId.cs index d7e337bda..5e38446bc 100644 --- a/MediaBrowser.Controller/Providers/IExternalId.cs +++ b/MediaBrowser.Controller/Providers/IExternalId.cs @@ -1,15 +1,45 @@ using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Providers; namespace MediaBrowser.Controller.Providers { + /// <summary> + /// Represents an identifier for an external provider. + /// </summary> public interface IExternalId { - string Name { get; } + /// <summary> + /// Gets the display name of the provider associated with this ID type. + /// </summary> + string ProviderName { get; } + /// <summary> + /// Gets the unique key to distinguish this provider/type pair. This should be unique across providers. + /// </summary> + // TODO: This property is not actually unique across the concrete types at the moment. It should be updated to be unique. string Key { get; } + /// <summary> + /// Gets the specific media type for this id. This is used to distinguish between the different + /// external id types for providers with multiple ids. + /// A null value indicates there is no specific media type associated with the external id, or this is the + /// default id for the external provider so there is no need to specify a type. + /// </summary> + /// <remarks> + /// This can be used along with the <see cref="ProviderName"/> to localize the external id on the client. + /// </remarks> + ExternalIdMediaType? Type { get; } + + /// <summary> + /// Gets the URL format string for this id. + /// </summary> string UrlFormatString { get; } + /// <summary> + /// Determines whether this id supports a given item type. + /// </summary> + /// <param name="item">The item.</param> + /// <returns>True if this item is supported, otherwise false.</returns> bool Supports(IHasProviderIds item); } } diff --git a/MediaBrowser.Controller/Providers/IMetadataProvider.cs b/MediaBrowser.Controller/Providers/IMetadataProvider.cs index 3e595ff93..62b16dadd 100644 --- a/MediaBrowser.Controller/Providers/IMetadataProvider.cs +++ b/MediaBrowser.Controller/Providers/IMetadataProvider.cs @@ -3,7 +3,7 @@ using MediaBrowser.Controller.Entities; namespace MediaBrowser.Controller.Providers { /// <summary> - /// Marker interface + /// Marker interface. /// </summary> public interface IMetadataProvider { diff --git a/MediaBrowser.Controller/Providers/ImageRefreshOptions.cs b/MediaBrowser.Controller/Providers/ImageRefreshOptions.cs index aac41369c..3f8c409f5 100644 --- a/MediaBrowser.Controller/Providers/ImageRefreshOptions.cs +++ b/MediaBrowser.Controller/Providers/ImageRefreshOptions.cs @@ -7,11 +7,13 @@ namespace MediaBrowser.Controller.Providers public class ImageRefreshOptions { public MetadataRefreshMode ImageRefreshMode { get; set; } + public IDirectoryService DirectoryService { get; private set; } public bool ReplaceAllImages { get; set; } public ImageType[] ReplaceImages { get; set; } + public bool IsAutomated { get; set; } public ImageRefreshOptions(IDirectoryService directoryService) diff --git a/MediaBrowser.Controller/Providers/MetadataRefreshMode.cs b/MediaBrowser.Controller/Providers/MetadataRefreshMode.cs index 02152ee33..6d49b5510 100644 --- a/MediaBrowser.Controller/Providers/MetadataRefreshMode.cs +++ b/MediaBrowser.Controller/Providers/MetadataRefreshMode.cs @@ -3,22 +3,22 @@ namespace MediaBrowser.Controller.Providers public enum MetadataRefreshMode { /// <summary> - /// The none + /// The none. /// </summary> None = 0, /// <summary> - /// The validation only + /// The validation only. /// </summary> ValidationOnly = 1, /// <summary> - /// Providers will be executed based on default rules + /// Providers will be executed based on default rules. /// </summary> Default = 2, /// <summary> - /// All providers will be executed to search for new metadata + /// All providers will be executed to search for new metadata. /// </summary> FullRefresh = 3 } diff --git a/MediaBrowser.Controller/Providers/MetadataResult.cs b/MediaBrowser.Controller/Providers/MetadataResult.cs index 59adaedfa..270ea2444 100644 --- a/MediaBrowser.Controller/Providers/MetadataResult.cs +++ b/MediaBrowser.Controller/Providers/MetadataResult.cs @@ -40,7 +40,7 @@ namespace MediaBrowser.Controller.Providers } /// <summary> - /// Not only does this clear, but initializes the list so that services can differentiate between a null list and zero people + /// Not only does this clear, but initializes the list so that services can differentiate between a null list and zero people. /// </summary> public void ResetPeople() { @@ -48,6 +48,7 @@ namespace MediaBrowser.Controller.Providers { People = new List<PersonInfo>(); } + People.Clear(); } diff --git a/MediaBrowser.Controller/Providers/VideoContentType.cs b/MediaBrowser.Controller/Providers/VideoContentType.cs index c3b8964a3..49d587f6c 100644 --- a/MediaBrowser.Controller/Providers/VideoContentType.cs +++ b/MediaBrowser.Controller/Providers/VideoContentType.cs @@ -1,17 +1,17 @@ namespace MediaBrowser.Controller.Providers { /// <summary> - /// Enum VideoContentType + /// Enum VideoContentType. /// </summary> public enum VideoContentType { /// <summary> - /// The episode + /// The episode. /// </summary> Episode = 0, /// <summary> - /// The movie + /// The movie. /// </summary> Movie = 1 } diff --git a/MediaBrowser.Controller/Resolvers/BaseItemResolver.cs b/MediaBrowser.Controller/Resolvers/BaseItemResolver.cs index 637a7e3f0..67acdd9a3 100644 --- a/MediaBrowser.Controller/Resolvers/BaseItemResolver.cs +++ b/MediaBrowser.Controller/Resolvers/BaseItemResolver.cs @@ -4,7 +4,7 @@ using MediaBrowser.Controller.Library; namespace MediaBrowser.Controller.Resolvers { /// <summary> - /// Class ItemResolver + /// Class ItemResolver. /// </summary> /// <typeparam name="T"></typeparam> public abstract class ItemResolver<T> : IItemResolver @@ -27,7 +27,7 @@ namespace MediaBrowser.Controller.Resolvers public virtual ResolverPriority Priority => ResolverPriority.First; /// <summary> - /// Sets initial values on the newly resolved item + /// Sets initial values on the newly resolved item. /// </summary> /// <param name="item">The item.</param> /// <param name="args">The args.</param> diff --git a/MediaBrowser.Controller/Resolvers/IItemResolver.cs b/MediaBrowser.Controller/Resolvers/IItemResolver.cs index 16e37d249..a73937b3e 100644 --- a/MediaBrowser.Controller/Resolvers/IItemResolver.cs +++ b/MediaBrowser.Controller/Resolvers/IItemResolver.cs @@ -7,7 +7,7 @@ using MediaBrowser.Model.IO; namespace MediaBrowser.Controller.Resolvers { /// <summary> - /// Interface IItemResolver + /// Interface IItemResolver. /// </summary> public interface IItemResolver { @@ -35,6 +35,7 @@ namespace MediaBrowser.Controller.Resolvers public class MultiItemResolverResult { public List<BaseItem> Items { get; set; } + public List<FileSystemMetadata> ExtraFiles { get; set; } public MultiItemResolverResult() diff --git a/MediaBrowser.Controller/Resolvers/ResolverPriority.cs b/MediaBrowser.Controller/Resolvers/ResolverPriority.cs index e39310095..1911e5c1d 100644 --- a/MediaBrowser.Controller/Resolvers/ResolverPriority.cs +++ b/MediaBrowser.Controller/Resolvers/ResolverPriority.cs @@ -1,25 +1,25 @@ namespace MediaBrowser.Controller.Resolvers { /// <summary> - /// Enum ResolverPriority + /// Enum ResolverPriority. /// </summary> public enum ResolverPriority { /// <summary> - /// The first + /// The first. /// </summary> First = 1, /// <summary> - /// The second + /// The second. /// </summary> Second = 2, /// <summary> - /// The third + /// The third. /// </summary> Third = 3, Fourth = 4, /// <summary> - /// The last + /// The last. /// </summary> Last = 5 } diff --git a/MediaBrowser.Controller/Security/AuthenticationInfo.cs b/MediaBrowser.Controller/Security/AuthenticationInfo.cs index 828213588..1d0b959b7 100644 --- a/MediaBrowser.Controller/Security/AuthenticationInfo.cs +++ b/MediaBrowser.Controller/Security/AuthenticationInfo.cs @@ -65,6 +65,7 @@ namespace MediaBrowser.Controller.Security public DateTime? DateRevoked { get; set; } public DateTime DateLastActivity { get; set; } + public string UserName { get; set; } } } diff --git a/MediaBrowser.Controller/Session/AuthenticationRequest.cs b/MediaBrowser.Controller/Session/AuthenticationRequest.cs index a28f47a9c..685ca3bdd 100644 --- a/MediaBrowser.Controller/Session/AuthenticationRequest.cs +++ b/MediaBrowser.Controller/Session/AuthenticationRequest.cs @@ -5,13 +5,21 @@ namespace MediaBrowser.Controller.Session public class AuthenticationRequest { public string Username { get; set; } + public Guid UserId { get; set; } + public string Password { get; set; } + public string PasswordSha1 { get; set; } + public string App { get; set; } + public string AppVersion { get; set; } + public string DeviceId { get; set; } + public string DeviceName { get; set; } + public string RemoteEndPoint { get; set; } } } diff --git a/MediaBrowser.Controller/Session/ISessionManager.cs b/MediaBrowser.Controller/Session/ISessionManager.cs index 1fdb588eb..e54f21050 100644 --- a/MediaBrowser.Controller/Session/ISessionManager.cs +++ b/MediaBrowser.Controller/Session/ISessionManager.cs @@ -13,7 +13,7 @@ using MediaBrowser.Model.SyncPlay; namespace MediaBrowser.Controller.Session { /// <summary> - /// Interface ISessionManager + /// Interface ISessionManager. /// </summary> public interface ISessionManager { @@ -79,14 +79,14 @@ namespace MediaBrowser.Controller.Session void UpdateDeviceName(string sessionId, string reportedDeviceName); /// <summary> - /// Used to report that playback has started for an item + /// Used to report that playback has started for an item. /// </summary> /// <param name="info">The info.</param> /// <returns>Task.</returns> Task OnPlaybackStart(PlaybackStartInfo info); /// <summary> - /// Used to report playback progress for an item + /// Used to report playback progress for an item. /// </summary> /// <param name="info">The info.</param> /// <returns>Task.</returns> @@ -96,7 +96,7 @@ namespace MediaBrowser.Controller.Session Task OnPlaybackProgress(PlaybackProgressInfo info, bool isAutomated); /// <summary> - /// Used to report that playback has ended for an item + /// Used to report that playback has ended for an item. /// </summary> /// <param name="info">The info.</param> /// <returns>Task.</returns> diff --git a/MediaBrowser.Controller/Session/SessionInfo.cs b/MediaBrowser.Controller/Session/SessionInfo.cs index 2ba7c9fec..4b088998c 100644 --- a/MediaBrowser.Controller/Session/SessionInfo.cs +++ b/MediaBrowser.Controller/Session/SessionInfo.cs @@ -61,6 +61,7 @@ namespace MediaBrowser.Controller.Session { return Array.Empty<string>(); } + return Capabilities.PlayableMediaTypes; } } @@ -108,6 +109,12 @@ namespace MediaBrowser.Controller.Session public string DeviceName { get; set; } /// <summary> + /// Gets or sets the type of the device. + /// </summary> + /// <value>The type of the device.</value> + public string DeviceType { get; set; } + + /// <summary> /// Gets or sets the now playing item. /// </summary> /// <value>The now playing item.</value> @@ -154,6 +161,7 @@ namespace MediaBrowser.Controller.Session return true; } } + if (controllers.Length > 0) { return false; @@ -213,8 +221,17 @@ namespace MediaBrowser.Controller.Session public string PlaylistItemId { get; set; } + public string ServerId { get; set; } + public string UserPrimaryImageTag { get; set; } + /// <summary> + /// Gets or sets the supported commands. + /// </summary> + /// <value>The supported commands.</value> + public string[] SupportedCommands + => Capabilities == null ? Array.Empty<string>() : Capabilities.SupportedCommands; + public Tuple<ISessionController, bool> EnsureController<T>(Func<SessionInfo, ISessionController> factory) { var controllers = SessionControllers.ToList(); @@ -255,6 +272,7 @@ namespace MediaBrowser.Controller.Session return true; } } + return false; } @@ -292,6 +310,7 @@ namespace MediaBrowser.Controller.Session { return; } + if (progressInfo.IsPaused) { return; @@ -334,6 +353,7 @@ namespace MediaBrowser.Controller.Session _progressTimer.Dispose(); _progressTimer = null; } + _lastProgressInfo = null; } } diff --git a/MediaBrowser.Controller/Sorting/IBaseItemComparer.cs b/MediaBrowser.Controller/Sorting/IBaseItemComparer.cs index 31087edec..727cbe639 100644 --- a/MediaBrowser.Controller/Sorting/IBaseItemComparer.cs +++ b/MediaBrowser.Controller/Sorting/IBaseItemComparer.cs @@ -4,7 +4,7 @@ using MediaBrowser.Controller.Entities; namespace MediaBrowser.Controller.Sorting { /// <summary> - /// Interface IBaseItemComparer + /// Interface IBaseItemComparer. /// </summary> public interface IBaseItemComparer : IComparer<BaseItem> { diff --git a/MediaBrowser.Controller/Sorting/IUserBaseItemComparer.cs b/MediaBrowser.Controller/Sorting/IUserBaseItemComparer.cs index 6f75d16de..6d03d97ae 100644 --- a/MediaBrowser.Controller/Sorting/IUserBaseItemComparer.cs +++ b/MediaBrowser.Controller/Sorting/IUserBaseItemComparer.cs @@ -3,7 +3,7 @@ using MediaBrowser.Controller.Library; namespace MediaBrowser.Controller.Sorting { /// <summary> - /// Represents a BaseItem comparer that requires a User to perform it's comparison + /// Represents a BaseItem comparer that requires a User to perform it's comparison. /// </summary> public interface IUserBaseItemComparer : IBaseItemComparer { diff --git a/MediaBrowser.Controller/Subtitles/SubtitleResponse.cs b/MediaBrowser.Controller/Subtitles/SubtitleResponse.cs index b8ba35a5f..ad6025927 100644 --- a/MediaBrowser.Controller/Subtitles/SubtitleResponse.cs +++ b/MediaBrowser.Controller/Subtitles/SubtitleResponse.cs @@ -5,8 +5,11 @@ namespace MediaBrowser.Controller.Subtitles public class SubtitleResponse { public string Language { get; set; } + public string Format { get; set; } + public bool IsForced { get; set; } + public Stream Stream { get; set; } } } diff --git a/MediaBrowser.Controller/Subtitles/SubtitleSearchRequest.cs b/MediaBrowser.Controller/Subtitles/SubtitleSearchRequest.cs index 61dc72258..a202723b9 100644 --- a/MediaBrowser.Controller/Subtitles/SubtitleSearchRequest.cs +++ b/MediaBrowser.Controller/Subtitles/SubtitleSearchRequest.cs @@ -8,23 +8,35 @@ namespace MediaBrowser.Controller.Subtitles public class SubtitleSearchRequest : IHasProviderIds { public string Language { get; set; } + public string TwoLetterISOLanguageName { get; set; } public VideoContentType ContentType { get; set; } public string MediaPath { get; set; } + public string SeriesName { get; set; } + public string Name { get; set; } + public int? IndexNumber { get; set; } + public int? IndexNumberEnd { get; set; } + public int? ParentIndexNumber { get; set; } + public int? ProductionYear { get; set; } + public long? RuntimeTicks { get; set; } + public bool IsPerfectMatch { get; set; } + public Dictionary<string, string> ProviderIds { get; set; } public bool SearchAllProviders { get; set; } + public string[] DisabledSubtitleFetchers { get; set; } + public string[] SubtitleFetcherOrder { get; set; } public SubtitleSearchRequest() diff --git a/MediaBrowser.Controller/Sync/IRemoteSyncProvider.cs b/MediaBrowser.Controller/Sync/IRemoteSyncProvider.cs index c0b62b753..b2c53365c 100644 --- a/MediaBrowser.Controller/Sync/IRemoteSyncProvider.cs +++ b/MediaBrowser.Controller/Sync/IRemoteSyncProvider.cs @@ -1,7 +1,7 @@ namespace MediaBrowser.Controller.Sync { /// <summary> - /// A marker interface + /// A marker interface. /// </summary> public interface IRemoteSyncProvider { diff --git a/MediaBrowser.Controller/Sync/SyncedFileInfo.cs b/MediaBrowser.Controller/Sync/SyncedFileInfo.cs index 2ff40addb..687a46d78 100644 --- a/MediaBrowser.Controller/Sync/SyncedFileInfo.cs +++ b/MediaBrowser.Controller/Sync/SyncedFileInfo.cs @@ -10,6 +10,7 @@ namespace MediaBrowser.Controller.Sync /// </summary> /// <value>The path.</value> public string Path { get; set; } + public string[] PathParts { get; set; } /// <summary> /// Gets or sets the protocol. diff --git a/MediaBrowser.Controller/SyncPlay/GroupInfo.cs b/MediaBrowser.Controller/SyncPlay/GroupInfo.cs index 28a3ac505..e742df517 100644 --- a/MediaBrowser.Controller/SyncPlay/GroupInfo.cs +++ b/MediaBrowser.Controller/SyncPlay/GroupInfo.cs @@ -14,9 +14,9 @@ namespace MediaBrowser.Controller.SyncPlay public class GroupInfo { /// <summary> - /// Default ping value used for sessions. + /// Gets the default ping value used for sessions. /// </summary> - public long DefaulPing { get; } = 500; + public long DefaultPing { get; } = 500; /// <summary> /// Gets or sets the group identifier. @@ -31,13 +31,13 @@ namespace MediaBrowser.Controller.SyncPlay public BaseItem PlayingItem { get; set; } /// <summary> - /// Gets or sets whether playback is paused. + /// Gets or sets a value indicating whether playback is paused. /// </summary> /// <value>Playback is paused.</value> public bool IsPaused { get; set; } /// <summary> - /// Gets or sets the position ticks. + /// Gets or sets a value indicating whether there are position ticks. /// </summary> /// <value>The position ticks.</value> public long PositionTicks { get; set; } @@ -70,16 +70,16 @@ namespace MediaBrowser.Controller.SyncPlay /// <param name="session">The session.</param> public void AddSession(SessionInfo session) { - if (ContainsSession(session.Id.ToString())) + if (ContainsSession(session.Id)) { return; } var member = new GroupMember(); member.Session = session; - member.Ping = DefaulPing; + member.Ping = DefaultPing; member.IsBuffering = false; - Participants[session.Id.ToString()] = member; + Participants[session.Id] = member; } /// <summary> @@ -88,12 +88,12 @@ namespace MediaBrowser.Controller.SyncPlay /// <param name="session">The session.</param> public void RemoveSession(SessionInfo session) { - if (!ContainsSession(session.Id.ToString())) + if (!ContainsSession(session.Id)) { return; } - Participants.Remove(session.Id.ToString(), out _); + Participants.Remove(session.Id, out _); } /// <summary> @@ -103,12 +103,12 @@ namespace MediaBrowser.Controller.SyncPlay /// <param name="ping">The ping.</param> public void UpdatePing(SessionInfo session, long ping) { - if (!ContainsSession(session.Id.ToString())) + if (!ContainsSession(session.Id)) { return; } - Participants[session.Id.ToString()].Ping = ping; + Participants[session.Id].Ping = ping; } /// <summary> @@ -117,11 +117,12 @@ namespace MediaBrowser.Controller.SyncPlay /// <value name="session">The highest ping in the group.</value> public long GetHighestPing() { - long max = Int64.MinValue; + long max = long.MinValue; foreach (var session in Participants.Values) { max = Math.Max(max, session.Ping); } + return max; } @@ -132,12 +133,12 @@ namespace MediaBrowser.Controller.SyncPlay /// <param name="isBuffering">The state.</param> public void SetBuffering(SessionInfo session, bool isBuffering) { - if (!ContainsSession(session.Id.ToString())) + if (!ContainsSession(session.Id)) { return; } - Participants[session.Id.ToString()].IsBuffering = isBuffering; + Participants[session.Id].IsBuffering = isBuffering; } /// <summary> diff --git a/MediaBrowser.Controller/SyncPlay/GroupMember.cs b/MediaBrowser.Controller/SyncPlay/GroupMember.cs index a3975c334..cde6f8e8c 100644 --- a/MediaBrowser.Controller/SyncPlay/GroupMember.cs +++ b/MediaBrowser.Controller/SyncPlay/GroupMember.cs @@ -8,7 +8,7 @@ namespace MediaBrowser.Controller.SyncPlay public class GroupMember { /// <summary> - /// Gets or sets whether this member is buffering. + /// Gets or sets a value indicating whether this member is buffering. /// </summary> /// <value><c>true</c> if member is buffering; <c>false</c> otherwise.</value> public bool IsBuffering { get; set; } diff --git a/MediaBrowser.Controller/SyncPlay/ISyncPlayController.cs b/MediaBrowser.Controller/SyncPlay/ISyncPlayController.cs index de1fcd259..45c543806 100644 --- a/MediaBrowser.Controller/SyncPlay/ISyncPlayController.cs +++ b/MediaBrowser.Controller/SyncPlay/ISyncPlayController.cs @@ -33,7 +33,7 @@ namespace MediaBrowser.Controller.SyncPlay /// </summary> /// <param name="session">The session.</param> /// <param name="cancellationToken">The cancellation token.</param> - void InitGroup(SessionInfo session, CancellationToken cancellationToken); + void CreateGroup(SessionInfo session, CancellationToken cancellationToken); /// <summary> /// Adds the session to the group. |
