mirror of
				https://github.com/jellyfin/jellyfin.git
				synced 2025-11-04 03:27:21 -05:00 
			
		
		
		
	Merge branch 'master' into tests11
This commit is contained in:
		
						commit
						e09e67deae
					
				@ -1,5 +1,4 @@
 | 
			
		||||
#pragma warning disable CS1591
 | 
			
		||||
#pragma warning disable SA1602
 | 
			
		||||
 | 
			
		||||
namespace Emby.Dlna.ContentDirectory
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
@ -553,7 +553,7 @@ namespace Emby.Dlna
 | 
			
		||||
 | 
			
		||||
        private void DumpProfiles()
 | 
			
		||||
        {
 | 
			
		||||
            DeviceProfile[] list = new []
 | 
			
		||||
            DeviceProfile[] list = new[]
 | 
			
		||||
            {
 | 
			
		||||
                new SamsungSmartTvProfile(),
 | 
			
		||||
                new XboxOneProfile(),
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,4 @@
 | 
			
		||||
#pragma warning disable CS1591
 | 
			
		||||
#pragma warning disable SA1602
 | 
			
		||||
 | 
			
		||||
namespace Emby.Dlna.PlayTo
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
@ -374,7 +374,7 @@ namespace Emby.Server.Implementations
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Creates an instance of type and resolves all constructor dependencies.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// /// <typeparam name="T">The type.</typeparam>
 | 
			
		||||
        /// <typeparam name="T">The type.</typeparam>
 | 
			
		||||
        /// <returns>T.</returns>
 | 
			
		||||
        public T CreateInstance<T>()
 | 
			
		||||
            => ActivatorUtilities.CreateInstance<T>(ServiceProvider);
 | 
			
		||||
 | 
			
		||||
@ -6207,9 +6207,9 @@ AND Type = @InternalPersonType)");
 | 
			
		||||
 | 
			
		||||
            if (item.Type == MediaStreamType.Subtitle)
 | 
			
		||||
            {
 | 
			
		||||
                item.localizedUndefined = _localization.GetLocalizedString("Undefined");
 | 
			
		||||
                item.localizedDefault = _localization.GetLocalizedString("Default");
 | 
			
		||||
                item.localizedForced = _localization.GetLocalizedString("Forced");
 | 
			
		||||
                item.LocalizedUndefined = _localization.GetLocalizedString("Undefined");
 | 
			
		||||
                item.LocalizedDefault = _localization.GetLocalizedString("Default");
 | 
			
		||||
                item.LocalizedForced = _localization.GetLocalizedString("Forced");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return item;
 | 
			
		||||
 | 
			
		||||
@ -29,7 +29,7 @@
 | 
			
		||||
    <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="5.0.0" />
 | 
			
		||||
    <PackageReference Include="Mono.Nat" Version="3.0.1" />
 | 
			
		||||
    <PackageReference Include="prometheus-net.DotNetRuntime" Version="3.4.1" />
 | 
			
		||||
    <PackageReference Include="sharpcompress" Version="0.28.0" />
 | 
			
		||||
    <PackageReference Include="sharpcompress" Version="0.28.1" />
 | 
			
		||||
    <PackageReference Include="SQLitePCL.pretty.netstandard" Version="2.1.0" />
 | 
			
		||||
    <PackageReference Include="DotNet.Glob" Version="3.1.2" />
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
 | 
			
		||||
@ -1,3 +1,5 @@
 | 
			
		||||
#nullable enable
 | 
			
		||||
 | 
			
		||||
using System.Net.Sockets;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
@ -29,7 +31,7 @@ namespace Emby.Server.Implementations.EntryPoints
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// The UDP server.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        private UdpServer _udpServer;
 | 
			
		||||
        private UdpServer? _udpServer;
 | 
			
		||||
        private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
 | 
			
		||||
        private bool _disposed = false;
 | 
			
		||||
 | 
			
		||||
@ -71,9 +73,8 @@ namespace Emby.Server.Implementations.EntryPoints
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            _cancellationTokenSource.Cancel();
 | 
			
		||||
            _udpServer.Dispose();
 | 
			
		||||
            _cancellationTokenSource.Dispose();
 | 
			
		||||
            _cancellationTokenSource = null;
 | 
			
		||||
            _udpServer?.Dispose();
 | 
			
		||||
            _udpServer = null;
 | 
			
		||||
 | 
			
		||||
            _disposed = true;
 | 
			
		||||
 | 
			
		||||
@ -10,7 +10,6 @@ using System.Net.Http;
 | 
			
		||||
using System.Net.Mime;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Text.Json;
 | 
			
		||||
using System.Text.Json.Serialization;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using MediaBrowser.Common;
 | 
			
		||||
 | 
			
		||||
@ -335,11 +335,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
 | 
			
		||||
            return new Uri(url).AbsoluteUri.TrimEnd('/');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected EncodingOptions GetEncodingOptions()
 | 
			
		||||
        {
 | 
			
		||||
            return Config.GetConfiguration<EncodingOptions>("encoding");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private static string GetHdHrIdFromChannelId(string channelId)
 | 
			
		||||
        {
 | 
			
		||||
            return channelId.Split('_')[1];
 | 
			
		||||
 | 
			
		||||
@ -8,7 +8,6 @@ using System.Linq;
 | 
			
		||||
using System.Net.WebSockets;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using MediaBrowser.Common.Extensions;
 | 
			
		||||
using MediaBrowser.Controller.Net;
 | 
			
		||||
using MediaBrowser.Controller.Session;
 | 
			
		||||
using MediaBrowser.Model.Net;
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,6 @@ using System.Net.Mime;
 | 
			
		||||
using Jellyfin.Api.Attributes;
 | 
			
		||||
using Jellyfin.Api.Models;
 | 
			
		||||
using MediaBrowser.Common.Plugins;
 | 
			
		||||
using MediaBrowser.Controller;
 | 
			
		||||
using MediaBrowser.Model.Net;
 | 
			
		||||
using MediaBrowser.Model.Plugins;
 | 
			
		||||
using Microsoft.AspNetCore.Http;
 | 
			
		||||
@ -22,22 +21,18 @@ namespace Jellyfin.Api.Controllers
 | 
			
		||||
    public class DashboardController : BaseJellyfinApiController
 | 
			
		||||
    {
 | 
			
		||||
        private readonly ILogger<DashboardController> _logger;
 | 
			
		||||
        private readonly IServerApplicationHost _appHost;
 | 
			
		||||
        private readonly IPluginManager _pluginManager;
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Initializes a new instance of the <see cref="DashboardController"/> class.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="logger">Instance of <see cref="ILogger{DashboardController}"/> interface.</param>
 | 
			
		||||
        /// <param name="appHost">Instance of <see cref="IServerApplicationHost"/> interface.</param>
 | 
			
		||||
        /// <param name="pluginManager">Instance of <see cref="IPluginManager"/> interface.</param>
 | 
			
		||||
        public DashboardController(
 | 
			
		||||
            ILogger<DashboardController> logger,
 | 
			
		||||
            IServerApplicationHost appHost,
 | 
			
		||||
            IPluginManager pluginManager)
 | 
			
		||||
        {
 | 
			
		||||
            _logger = logger;
 | 
			
		||||
            _appHost = appHost;
 | 
			
		||||
            _pluginManager = pluginManager;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -51,7 +46,7 @@ namespace Jellyfin.Api.Controllers
 | 
			
		||||
        [HttpGet("web/ConfigurationPages")]
 | 
			
		||||
        [ProducesResponseType(StatusCodes.Status200OK)]
 | 
			
		||||
        [ProducesResponseType(StatusCodes.Status404NotFound)]
 | 
			
		||||
        public ActionResult<IEnumerable<ConfigurationPageInfo?>> GetConfigurationPages(
 | 
			
		||||
        public ActionResult<IEnumerable<ConfigurationPageInfo>> GetConfigurationPages(
 | 
			
		||||
            [FromQuery] bool? enableInMainMenu)
 | 
			
		||||
        {
 | 
			
		||||
            var configPages = _pluginManager.Plugins.SelectMany(GetConfigPages).ToList();
 | 
			
		||||
@ -77,40 +72,24 @@ namespace Jellyfin.Api.Controllers
 | 
			
		||||
        [ProducesFile(MediaTypeNames.Text.Html, "application/x-javascript")]
 | 
			
		||||
        public ActionResult GetDashboardConfigurationPage([FromQuery] string? name)
 | 
			
		||||
        {
 | 
			
		||||
            IPlugin? plugin = null;
 | 
			
		||||
            Stream? stream = null;
 | 
			
		||||
 | 
			
		||||
            var isJs = false;
 | 
			
		||||
            var isTemplate = false;
 | 
			
		||||
 | 
			
		||||
            var altPage = GetPluginPages().FirstOrDefault(p => string.Equals(p.Item1.Name, name, StringComparison.OrdinalIgnoreCase));
 | 
			
		||||
            if (altPage != null)
 | 
			
		||||
            if (altPage == null)
 | 
			
		||||
            {
 | 
			
		||||
                plugin = altPage.Item2;
 | 
			
		||||
                stream = plugin.GetType().Assembly.GetManifestResourceStream(altPage.Item1.EmbeddedResourcePath);
 | 
			
		||||
 | 
			
		||||
                isJs = string.Equals(Path.GetExtension(altPage.Item1.EmbeddedResourcePath), ".js", StringComparison.OrdinalIgnoreCase);
 | 
			
		||||
                isTemplate = altPage.Item1.EmbeddedResourcePath.EndsWith(".template.html", StringComparison.Ordinal);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (plugin != null && stream != null)
 | 
			
		||||
            {
 | 
			
		||||
                if (isJs)
 | 
			
		||||
                {
 | 
			
		||||
                    return File(stream, MimeTypes.GetMimeType("page.js"));
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (isTemplate)
 | 
			
		||||
                {
 | 
			
		||||
                    return File(stream, MimeTypes.GetMimeType("page.html"));
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return File(stream, MimeTypes.GetMimeType("page.html"));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
                return NotFound();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            IPlugin plugin = altPage.Item2;
 | 
			
		||||
            string resourcePath = altPage.Item1.EmbeddedResourcePath;
 | 
			
		||||
            Stream? stream = plugin.GetType().Assembly.GetManifestResourceStream(resourcePath);
 | 
			
		||||
            if (stream == null)
 | 
			
		||||
            {
 | 
			
		||||
                _logger.LogError("Failed to get resource {Resource} from plugin {Plugin}", resourcePath, plugin.Name);
 | 
			
		||||
                return NotFound();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return File(stream, MimeTypes.GetMimeType(resourcePath));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private IEnumerable<ConfigurationPageInfo> GetConfigPages(LocalPlugin plugin)
 | 
			
		||||
        {
 | 
			
		||||
            return GetPluginPages(plugin).Select(i => new ConfigurationPageInfo(plugin.Instance, i.Item1));
 | 
			
		||||
@ -120,7 +99,7 @@ namespace Jellyfin.Api.Controllers
 | 
			
		||||
        {
 | 
			
		||||
            if (plugin?.Instance is not IHasWebPages hasWebPages)
 | 
			
		||||
            {
 | 
			
		||||
                return new List<Tuple<PluginPageInfo, IPlugin>>();
 | 
			
		||||
                return Enumerable.Empty<Tuple<PluginPageInfo, IPlugin>>();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return hasWebPages.GetPages().Select(i => new Tuple<PluginPageInfo, IPlugin>(i, plugin.Instance));
 | 
			
		||||
 | 
			
		||||
@ -113,7 +113,7 @@ namespace Jellyfin.Api.Controllers
 | 
			
		||||
                await _userManager.ClearProfileImageAsync(user).ConfigureAwait(false);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            user.ProfileImage = new Data.Entities.ImageInfo(Path.Combine(userDataPath, "profile" + MimeTypes.ToExtension(mimeType)));
 | 
			
		||||
            user.ProfileImage = new Data.Entities.ImageInfo(Path.Combine(userDataPath, "profile" + MimeTypes.ToExtension(mimeType ?? string.Empty)));
 | 
			
		||||
 | 
			
		||||
            await _providerManager
 | 
			
		||||
                .SaveImage(memoryStream, mimeType, user.ProfileImage.Path)
 | 
			
		||||
@ -160,7 +160,7 @@ namespace Jellyfin.Api.Controllers
 | 
			
		||||
                await _userManager.ClearProfileImageAsync(user).ConfigureAwait(false);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            user.ProfileImage = new Data.Entities.ImageInfo(Path.Combine(userDataPath, "profile" + MimeTypes.ToExtension(mimeType)));
 | 
			
		||||
            user.ProfileImage = new Data.Entities.ImageInfo(Path.Combine(userDataPath, "profile" + MimeTypes.ToExtension(mimeType ?? string.Empty)));
 | 
			
		||||
 | 
			
		||||
            await _providerManager
 | 
			
		||||
                .SaveImage(memoryStream, mimeType, user.ProfileImage.Path)
 | 
			
		||||
 | 
			
		||||
@ -113,14 +113,5 @@ namespace Jellyfin.Api.Extensions
 | 
			
		||||
 | 
			
		||||
            return dtoOptions;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Check if DtoOptions contains field.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="dtoOptions">DtoOptions object.</param>
 | 
			
		||||
        /// <param name="field">Field to check.</param>
 | 
			
		||||
        /// <returns>Field existence.</returns>
 | 
			
		||||
        internal static bool ContainsField(this DtoOptions dtoOptions, ItemFields field)
 | 
			
		||||
            => dtoOptions.Fields != null && dtoOptions.Fields.Contains(field);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -3,6 +3,7 @@ using System.Collections.Generic;
 | 
			
		||||
using System.Globalization;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Net;
 | 
			
		||||
using System.Net.Mime;
 | 
			
		||||
using System.Security.Claims;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
@ -171,13 +172,15 @@ namespace Jellyfin.Api.Helpers
 | 
			
		||||
            var queryString = _httpContextAccessor.HttpContext.Request.QueryString.ToString();
 | 
			
		||||
 | 
			
		||||
            // from universal audio service
 | 
			
		||||
            if (queryString.IndexOf("SegmentContainer", StringComparison.OrdinalIgnoreCase) == -1 && !string.IsNullOrWhiteSpace(state.Request.SegmentContainer))
 | 
			
		||||
            if (!string.IsNullOrWhiteSpace(state.Request.SegmentContainer)
 | 
			
		||||
                && !queryString.Contains("SegmentContainer", StringComparison.OrdinalIgnoreCase))
 | 
			
		||||
            {
 | 
			
		||||
                queryString += "&SegmentContainer=" + state.Request.SegmentContainer;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // from universal audio service
 | 
			
		||||
            if (!string.IsNullOrWhiteSpace(state.Request.TranscodeReasons) && queryString.IndexOf("TranscodeReasons=", StringComparison.OrdinalIgnoreCase) == -1)
 | 
			
		||||
            if (!string.IsNullOrWhiteSpace(state.Request.TranscodeReasons)
 | 
			
		||||
                && !queryString.Contains("TranscodeReasons=", StringComparison.OrdinalIgnoreCase))
 | 
			
		||||
            {
 | 
			
		||||
                queryString += "&TranscodeReasons=" + state.Request.TranscodeReasons;
 | 
			
		||||
            }
 | 
			
		||||
@ -560,13 +563,13 @@ namespace Jellyfin.Api.Helpers
 | 
			
		||||
                profileString = state.GetRequestedProfiles(codec).FirstOrDefault() ?? string.Empty;
 | 
			
		||||
                if (string.Equals(state.ActualOutputVideoCodec, "h264", StringComparison.OrdinalIgnoreCase))
 | 
			
		||||
                {
 | 
			
		||||
                    profileString = profileString ?? "high";
 | 
			
		||||
                    profileString ??= "high";
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (string.Equals(state.ActualOutputVideoCodec, "h265", StringComparison.OrdinalIgnoreCase)
 | 
			
		||||
                    || string.Equals(state.ActualOutputVideoCodec, "hevc", StringComparison.OrdinalIgnoreCase))
 | 
			
		||||
                {
 | 
			
		||||
                    profileString = profileString ?? "main";
 | 
			
		||||
                    profileString ??= "main";
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -523,7 +523,7 @@ namespace Jellyfin.Api.Helpers
 | 
			
		||||
        /// <param name="type">Dlna profile type.</param>
 | 
			
		||||
        public void NormalizeMediaSourceContainer(MediaSourceInfo mediaSource, DeviceProfile profile, DlnaProfileType type)
 | 
			
		||||
        {
 | 
			
		||||
            mediaSource.Container = StreamBuilder.NormalizeMediaSourceFormatIntoSingleContainer(mediaSource.Container, mediaSource.Path, profile, type);
 | 
			
		||||
            mediaSource.Container = StreamBuilder.NormalizeMediaSourceFormatIntoSingleContainer(mediaSource.Container, profile, type);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void SetDeviceSpecificSubtitleInfo(StreamInfo info, MediaSourceInfo mediaSource, string accessToken)
 | 
			
		||||
 | 
			
		||||
@ -183,7 +183,7 @@ namespace Jellyfin.Api.Helpers
 | 
			
		||||
            if (string.IsNullOrEmpty(containerInternal))
 | 
			
		||||
            {
 | 
			
		||||
                containerInternal = streamingRequest.Static ?
 | 
			
		||||
                    StreamBuilder.NormalizeMediaSourceFormatIntoSingleContainer(state.InputContainer, state.MediaPath, null, DlnaProfileType.Audio)
 | 
			
		||||
                    StreamBuilder.NormalizeMediaSourceFormatIntoSingleContainer(state.InputContainer, null, DlnaProfileType.Audio)
 | 
			
		||||
                    : GetOutputFileExtension(state);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@ -245,7 +245,7 @@ namespace Jellyfin.Api.Helpers
 | 
			
		||||
 | 
			
		||||
            var ext = string.IsNullOrWhiteSpace(state.OutputContainer)
 | 
			
		||||
                ? GetOutputFileExtension(state)
 | 
			
		||||
                : ('.' + state.OutputContainer);
 | 
			
		||||
                : ("." + state.OutputContainer);
 | 
			
		||||
 | 
			
		||||
            state.OutputFilePath = GetOutputFilePath(state, ext!, serverConfigurationManager, streamingRequest.DeviceId, streamingRequest.PlaySessionId);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -17,8 +17,8 @@
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
    <PackageReference Include="Microsoft.AspNetCore.Authorization" Version="5.0.3" />
 | 
			
		||||
    <PackageReference Include="Microsoft.Extensions.Http" Version="5.0.0" />
 | 
			
		||||
    <PackageReference Include="Swashbuckle.AspNetCore" Version="6.0.5" />
 | 
			
		||||
    <PackageReference Include="Swashbuckle.AspNetCore.ReDoc" Version="6.0.5" />
 | 
			
		||||
    <PackageReference Include="Swashbuckle.AspNetCore" Version="6.0.7" />
 | 
			
		||||
    <PackageReference Include="Swashbuckle.AspNetCore.ReDoc" Version="6.0.7" />
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,5 @@
 | 
			
		||||
using System;
 | 
			
		||||
using MediaBrowser.Common.Plugins;
 | 
			
		||||
using MediaBrowser.Controller.Plugins;
 | 
			
		||||
using MediaBrowser.Model.Plugins;
 | 
			
		||||
 | 
			
		||||
namespace Jellyfin.Api.Models
 | 
			
		||||
@ -25,6 +24,14 @@ namespace Jellyfin.Api.Models
 | 
			
		||||
            PluginId = plugin?.Id;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Initializes a new instance of the <see cref="ConfigurationPageInfo"/> class.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public ConfigurationPageInfo()
 | 
			
		||||
        {
 | 
			
		||||
            Name = string.Empty;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets the name.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
 | 
			
		||||
@ -98,7 +98,7 @@ namespace Jellyfin.Api.Models.PlaybackDtos
 | 
			
		||||
 | 
			
		||||
        private EncodingOptions GetOptions()
 | 
			
		||||
        {
 | 
			
		||||
            return _config.GetConfiguration<EncodingOptions>("encoding");
 | 
			
		||||
            return _config.GetEncodingOptions();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private async void TimerCallback(object? state)
 | 
			
		||||
 | 
			
		||||
@ -11,7 +11,6 @@ using Jellyfin.Server.Implementations;
 | 
			
		||||
using Jellyfin.Server.Implementations.Activity;
 | 
			
		||||
using Jellyfin.Server.Implementations.Events;
 | 
			
		||||
using Jellyfin.Server.Implementations.Users;
 | 
			
		||||
using MediaBrowser.Common.Net;
 | 
			
		||||
using MediaBrowser.Controller;
 | 
			
		||||
using MediaBrowser.Controller.BaseItemManager;
 | 
			
		||||
using MediaBrowser.Controller.Drawing;
 | 
			
		||||
 | 
			
		||||
@ -32,7 +32,7 @@ namespace Jellyfin.Server.Migrations.Routines
 | 
			
		||||
        public void Perform()
 | 
			
		||||
        {
 | 
			
		||||
            // Set EnableThrottling to false since it wasn't used before and may introduce issues
 | 
			
		||||
            var encoding = _configManager.GetConfiguration<EncodingOptions>("encoding");
 | 
			
		||||
            var encoding = _configManager.GetEncodingOptions();
 | 
			
		||||
            if (encoding.EnableThrottling)
 | 
			
		||||
            {
 | 
			
		||||
                _logger.LogInformation("Disabling transcoding throttling during migration");
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										51
									
								
								MediaBrowser.Common/Extensions/StreamExtensions.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								MediaBrowser.Common/Extensions/StreamExtensions.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,51 @@
 | 
			
		||||
#nullable enable
 | 
			
		||||
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace MediaBrowser.Common.Extensions
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Class BaseExtensions.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public static class StreamExtensions
 | 
			
		||||
    {
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Reads all lines in the <see cref="Stream" />.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="stream">The <see cref="Stream" /> to read from.</param>
 | 
			
		||||
        /// <returns>All lines in the stream.</returns>
 | 
			
		||||
        public static string[] ReadAllLines(this Stream stream)
 | 
			
		||||
            => ReadAllLines(stream, Encoding.UTF8);
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Reads all lines in the <see cref="Stream" />.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="stream">The <see cref="Stream" /> to read from.</param>
 | 
			
		||||
        /// <param name="encoding">The character encoding to use.</param>
 | 
			
		||||
        /// <returns>All lines in the stream.</returns>
 | 
			
		||||
        public static string[] ReadAllLines(this Stream stream, Encoding encoding)
 | 
			
		||||
        {
 | 
			
		||||
            using (StreamReader reader = new StreamReader(stream, encoding))
 | 
			
		||||
            {
 | 
			
		||||
                return ReadAllLines(reader).ToArray();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Reads all lines in the <see cref="StreamReader" />.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="reader">The <see cref="StreamReader" /> to read from.</param>
 | 
			
		||||
        /// <returns>All lines in the stream.</returns>
 | 
			
		||||
        public static IEnumerable<string> ReadAllLines(this StreamReader reader)
 | 
			
		||||
        {
 | 
			
		||||
            string? line;
 | 
			
		||||
            while ((line = reader.ReadLine()) != null)
 | 
			
		||||
            {
 | 
			
		||||
                yield return line;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,37 +0,0 @@
 | 
			
		||||
#nullable enable
 | 
			
		||||
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
namespace MediaBrowser.Common.Extensions
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Extensions methods to simplify string operations.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public static class StringExtensions
 | 
			
		||||
    {
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Returns the part on the left of the <c>needle</c>.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="haystack">The string to seek.</param>
 | 
			
		||||
        /// <param name="needle">The needle to find.</param>
 | 
			
		||||
        /// <returns>The part left of the <paramref name="needle" />.</returns>
 | 
			
		||||
        public static ReadOnlySpan<char> LeftPart(this ReadOnlySpan<char> haystack, char needle)
 | 
			
		||||
        {
 | 
			
		||||
            var pos = haystack.IndexOf(needle);
 | 
			
		||||
            return pos == -1 ? haystack : haystack[..pos];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Returns the part on the left of the <c>needle</c>.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="haystack">The string to seek.</param>
 | 
			
		||||
        /// <param name="needle">The needle to find.</param>
 | 
			
		||||
        /// <param name="stringComparison">One of the enumeration values that specifies the rules for the search.</param>
 | 
			
		||||
        /// <returns>The part left of the <c>needle</c>.</returns>
 | 
			
		||||
        public static ReadOnlySpan<char> LeftPart(this ReadOnlySpan<char> haystack, ReadOnlySpan<char> needle, StringComparison stringComparison = default)
 | 
			
		||||
        {
 | 
			
		||||
            var pos = haystack.IndexOf(needle, stringComparison);
 | 
			
		||||
            return pos == -1 ? haystack : haystack[..pos];
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,11 +1,6 @@
 | 
			
		||||
#pragma warning disable CA1062 // Validate arguments of public methods
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Collections.ObjectModel;
 | 
			
		||||
using System.Net;
 | 
			
		||||
using System.Runtime.CompilerServices;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace MediaBrowser.Common.Net
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
@ -1,10 +1,7 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
using System.Runtime.InteropServices;
 | 
			
		||||
using MediaBrowser.Common.Configuration;
 | 
			
		||||
using MediaBrowser.Model.Plugins;
 | 
			
		||||
using MediaBrowser.Model.Serialization;
 | 
			
		||||
 | 
			
		||||
namespace MediaBrowser.Common.Plugins
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
@ -103,7 +103,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
 | 
			
		||||
        public void SetFFmpegPath()
 | 
			
		||||
        {
 | 
			
		||||
            // 1) Custom path stored in config/encoding xml file under tag <EncoderAppPath> takes precedence
 | 
			
		||||
            if (!ValidatePath(_configurationManager.GetConfiguration<EncodingOptions>("encoding").EncoderAppPath, FFmpegLocation.Custom))
 | 
			
		||||
            if (!ValidatePath(_configurationManager.GetEncodingOptions().EncoderAppPath, FFmpegLocation.Custom))
 | 
			
		||||
            {
 | 
			
		||||
                // 2) Check if the --ffmpeg CLI switch has been given
 | 
			
		||||
                if (!ValidatePath(_startupOptionFFmpegPath, FFmpegLocation.SetByArgument))
 | 
			
		||||
@ -118,7 +118,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Write the FFmpeg path to the config/encoding.xml file as <EncoderAppPathDisplay> so it appears in UI
 | 
			
		||||
            var config = _configurationManager.GetConfiguration<EncodingOptions>("encoding");
 | 
			
		||||
            var config = _configurationManager.GetEncodingOptions();
 | 
			
		||||
            config.EncoderAppPathDisplay = _ffmpegPath ?? string.Empty;
 | 
			
		||||
            _configurationManager.SaveConfiguration("encoding", config);
 | 
			
		||||
 | 
			
		||||
@ -177,7 +177,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
 | 
			
		||||
 | 
			
		||||
            // Write the new ffmpeg path to the xml as <EncoderAppPath>
 | 
			
		||||
            // This ensures its not lost on next startup
 | 
			
		||||
            var config = _configurationManager.GetConfiguration<EncodingOptions>("encoding");
 | 
			
		||||
            var config = _configurationManager.GetEncodingOptions();
 | 
			
		||||
            config.EncoderAppPath = newPath;
 | 
			
		||||
            _configurationManager.SaveConfiguration("encoding", config);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -24,6 +24,7 @@
 | 
			
		||||
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
    <PackageReference Include="BDInfo" Version="0.7.6.1" />
 | 
			
		||||
    <PackageReference Include="libse" Version="3.5.8" />
 | 
			
		||||
    <PackageReference Include="Microsoft.Extensions.Http" Version="5.0.0" />
 | 
			
		||||
    <PackageReference Include="System.Text.Encoding.CodePages" Version="5.0.0" />
 | 
			
		||||
    <PackageReference Include="UTF.Unknown" Version="2.3.0" />
 | 
			
		||||
 | 
			
		||||
@ -681,9 +681,9 @@ namespace MediaBrowser.MediaEncoding.Probing
 | 
			
		||||
            {
 | 
			
		||||
                stream.Type = MediaStreamType.Subtitle;
 | 
			
		||||
                stream.Codec = NormalizeSubtitleCodec(stream.Codec);
 | 
			
		||||
                stream.localizedUndefined = _localization.GetLocalizedString("Undefined");
 | 
			
		||||
                stream.localizedDefault = _localization.GetLocalizedString("Default");
 | 
			
		||||
                stream.localizedForced = _localization.GetLocalizedString("Forced");
 | 
			
		||||
                stream.LocalizedUndefined = _localization.GetLocalizedString("Undefined");
 | 
			
		||||
                stream.LocalizedDefault = _localization.GetLocalizedString("Default");
 | 
			
		||||
                stream.LocalizedForced = _localization.GetLocalizedString("Forced");
 | 
			
		||||
            }
 | 
			
		||||
            else if (string.Equals(streamInfo.CodecType, "video", StringComparison.OrdinalIgnoreCase))
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
@ -1,130 +1,21 @@
 | 
			
		||||
#pragma warning disable CS1591
 | 
			
		||||
#nullable enable
 | 
			
		||||
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Globalization;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text.RegularExpressions;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
using MediaBrowser.Model.MediaInfo;
 | 
			
		||||
using Microsoft.Extensions.Logging;
 | 
			
		||||
using Nikse.SubtitleEdit.Core.SubtitleFormats;
 | 
			
		||||
 | 
			
		||||
namespace MediaBrowser.MediaEncoding.Subtitles
 | 
			
		||||
{
 | 
			
		||||
    public class AssParser : ISubtitleParser
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Advanced SubStation Alpha subtitle parser.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public class AssParser : SubtitleEditParser<AdvancedSubStationAlpha>
 | 
			
		||||
    {
 | 
			
		||||
        private readonly CultureInfo _usCulture = new CultureInfo("en-US");
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc />
 | 
			
		||||
        public SubtitleTrackInfo Parse(Stream stream, CancellationToken cancellationToken)
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Initializes a new instance of the <see cref="AssParser"/> class.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="logger">The logger.</param>
 | 
			
		||||
        public AssParser(ILogger logger) : base(logger)
 | 
			
		||||
        {
 | 
			
		||||
            var trackInfo = new SubtitleTrackInfo();
 | 
			
		||||
            var trackEvents = new List<SubtitleTrackEvent>();
 | 
			
		||||
            var eventIndex = 1;
 | 
			
		||||
            using (var reader = new StreamReader(stream))
 | 
			
		||||
            {
 | 
			
		||||
                string line;
 | 
			
		||||
                while (!string.Equals(reader.ReadLine(), "[Events]", StringComparison.Ordinal))
 | 
			
		||||
                {
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                var headers = ParseFieldHeaders(reader.ReadLine());
 | 
			
		||||
 | 
			
		||||
                while ((line = reader.ReadLine()) != null)
 | 
			
		||||
                {
 | 
			
		||||
                    cancellationToken.ThrowIfCancellationRequested();
 | 
			
		||||
 | 
			
		||||
                    if (string.IsNullOrWhiteSpace(line))
 | 
			
		||||
                    {
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if (line[0] == '[')
 | 
			
		||||
                    {
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    var subEvent = new SubtitleTrackEvent { Id = eventIndex.ToString(_usCulture) };
 | 
			
		||||
                    eventIndex++;
 | 
			
		||||
                    const string Dialogue = "Dialogue: ";
 | 
			
		||||
                    var sections = line.Substring(Dialogue.Length).Split(',');
 | 
			
		||||
 | 
			
		||||
                    subEvent.StartPositionTicks = GetTicks(sections[headers["Start"]]);
 | 
			
		||||
                    subEvent.EndPositionTicks = GetTicks(sections[headers["End"]]);
 | 
			
		||||
 | 
			
		||||
                    subEvent.Text = string.Join(',', sections[headers["Text"]..]);
 | 
			
		||||
                    RemoteNativeFormatting(subEvent);
 | 
			
		||||
 | 
			
		||||
                    subEvent.Text = subEvent.Text.Replace("\\n", ParserValues.NewLine, StringComparison.OrdinalIgnoreCase);
 | 
			
		||||
 | 
			
		||||
                    subEvent.Text = Regex.Replace(subEvent.Text, @"\{(\\[\w]+\(?([\w0-9]+,?)+\)?)+\}", string.Empty, RegexOptions.IgnoreCase);
 | 
			
		||||
 | 
			
		||||
                    trackEvents.Add(subEvent);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            trackInfo.TrackEvents = trackEvents;
 | 
			
		||||
            return trackInfo;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private long GetTicks(ReadOnlySpan<char> time)
 | 
			
		||||
        {
 | 
			
		||||
            return TimeSpan.TryParseExact(time, @"h\:mm\:ss\.ff", _usCulture, out var span)
 | 
			
		||||
                ? span.Ticks : 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        internal static Dictionary<string, int> ParseFieldHeaders(string line)
 | 
			
		||||
        {
 | 
			
		||||
            const string Format = "Format: ";
 | 
			
		||||
            var fields = line.Substring(Format.Length).Split(',').Select(x => x.Trim()).ToList();
 | 
			
		||||
 | 
			
		||||
            return new Dictionary<string, int>
 | 
			
		||||
            {
 | 
			
		||||
                { "Start", fields.IndexOf("Start") },
 | 
			
		||||
                { "End", fields.IndexOf("End") },
 | 
			
		||||
                { "Text", fields.IndexOf("Text") }
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void RemoteNativeFormatting(SubtitleTrackEvent p)
 | 
			
		||||
        {
 | 
			
		||||
            int indexOfBegin = p.Text.IndexOf('{', StringComparison.Ordinal);
 | 
			
		||||
            string pre = string.Empty;
 | 
			
		||||
            while (indexOfBegin >= 0 && p.Text.IndexOf('}', StringComparison.Ordinal) > indexOfBegin)
 | 
			
		||||
            {
 | 
			
		||||
                string s = p.Text.Substring(indexOfBegin);
 | 
			
		||||
                if (s.StartsWith("{\\an1}", StringComparison.Ordinal) ||
 | 
			
		||||
                    s.StartsWith("{\\an2}", StringComparison.Ordinal) ||
 | 
			
		||||
                    s.StartsWith("{\\an3}", StringComparison.Ordinal) ||
 | 
			
		||||
                    s.StartsWith("{\\an4}", StringComparison.Ordinal) ||
 | 
			
		||||
                    s.StartsWith("{\\an5}", StringComparison.Ordinal) ||
 | 
			
		||||
                    s.StartsWith("{\\an6}", StringComparison.Ordinal) ||
 | 
			
		||||
                    s.StartsWith("{\\an7}", StringComparison.Ordinal) ||
 | 
			
		||||
                    s.StartsWith("{\\an8}", StringComparison.Ordinal) ||
 | 
			
		||||
                    s.StartsWith("{\\an9}", StringComparison.Ordinal))
 | 
			
		||||
                {
 | 
			
		||||
                    pre = s.Substring(0, 6);
 | 
			
		||||
                }
 | 
			
		||||
                else if (s.StartsWith("{\\an1\\", StringComparison.Ordinal) ||
 | 
			
		||||
                    s.StartsWith("{\\an2\\", StringComparison.Ordinal) ||
 | 
			
		||||
                    s.StartsWith("{\\an3\\", StringComparison.Ordinal) ||
 | 
			
		||||
                    s.StartsWith("{\\an4\\", StringComparison.Ordinal) ||
 | 
			
		||||
                    s.StartsWith("{\\an5\\", StringComparison.Ordinal) ||
 | 
			
		||||
                    s.StartsWith("{\\an6\\", StringComparison.Ordinal) ||
 | 
			
		||||
                    s.StartsWith("{\\an7\\", StringComparison.Ordinal) ||
 | 
			
		||||
                    s.StartsWith("{\\an8\\", StringComparison.Ordinal) ||
 | 
			
		||||
                    s.StartsWith("{\\an9\\", StringComparison.Ordinal))
 | 
			
		||||
                {
 | 
			
		||||
                    pre = s.Substring(0, 5) + "}";
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                int indexOfEnd = p.Text.IndexOf('}', StringComparison.Ordinal);
 | 
			
		||||
                p.Text = p.Text.Remove(indexOfBegin, (indexOfEnd - indexOfBegin) + 1);
 | 
			
		||||
 | 
			
		||||
                indexOfBegin = p.Text.IndexOf('{', StringComparison.Ordinal);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            p.Text = pre + p.Text;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,102 +1,21 @@
 | 
			
		||||
#pragma warning disable CS1591
 | 
			
		||||
#nullable enable
 | 
			
		||||
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Globalization;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Text.RegularExpressions;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
using MediaBrowser.Model.MediaInfo;
 | 
			
		||||
using Microsoft.Extensions.Logging;
 | 
			
		||||
using Nikse.SubtitleEdit.Core.SubtitleFormats;
 | 
			
		||||
 | 
			
		||||
namespace MediaBrowser.MediaEncoding.Subtitles
 | 
			
		||||
{
 | 
			
		||||
    public class SrtParser : ISubtitleParser
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// SubRip subtitle parser.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public class SrtParser : SubtitleEditParser<SubRip>
 | 
			
		||||
    {
 | 
			
		||||
        private readonly ILogger _logger;
 | 
			
		||||
 | 
			
		||||
        private readonly CultureInfo _usCulture = new CultureInfo("en-US");
 | 
			
		||||
 | 
			
		||||
        public SrtParser(ILogger logger)
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Initializes a new instance of the <see cref="SrtParser"/> class.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="logger">The logger.</param>
 | 
			
		||||
        public SrtParser(ILogger logger) : base(logger)
 | 
			
		||||
        {
 | 
			
		||||
            _logger = logger;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc />
 | 
			
		||||
        public SubtitleTrackInfo Parse(Stream stream, CancellationToken cancellationToken)
 | 
			
		||||
        {
 | 
			
		||||
            var trackInfo = new SubtitleTrackInfo();
 | 
			
		||||
            var trackEvents = new List<SubtitleTrackEvent>();
 | 
			
		||||
            using (var reader = new StreamReader(stream))
 | 
			
		||||
            {
 | 
			
		||||
                string line;
 | 
			
		||||
                while ((line = reader.ReadLine()) != null)
 | 
			
		||||
                {
 | 
			
		||||
                    cancellationToken.ThrowIfCancellationRequested();
 | 
			
		||||
 | 
			
		||||
                    if (string.IsNullOrWhiteSpace(line))
 | 
			
		||||
                    {
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    var subEvent = new SubtitleTrackEvent { Id = line };
 | 
			
		||||
                    line = reader.ReadLine();
 | 
			
		||||
 | 
			
		||||
                    if (string.IsNullOrWhiteSpace(line))
 | 
			
		||||
                    {
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    var time = Regex.Split(line, @"[\t ]*-->[\t ]*");
 | 
			
		||||
 | 
			
		||||
                    if (time.Length < 2)
 | 
			
		||||
                    {
 | 
			
		||||
                        // This occurs when subtitle text has an empty line as part of the text.
 | 
			
		||||
                        // Need to adjust the break statement below to resolve this.
 | 
			
		||||
                        _logger.LogWarning("Unrecognized line in srt: {0}", line);
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    subEvent.StartPositionTicks = GetTicks(time[0]);
 | 
			
		||||
                    var endTime = time[1].AsSpan();
 | 
			
		||||
                    var idx = endTime.IndexOf(' ');
 | 
			
		||||
                    if (idx > 0)
 | 
			
		||||
                    {
 | 
			
		||||
                        endTime = endTime.Slice(0, idx);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    subEvent.EndPositionTicks = GetTicks(endTime);
 | 
			
		||||
                    var multiline = new List<string>();
 | 
			
		||||
                    while ((line = reader.ReadLine()) != null)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (line.Length == 0)
 | 
			
		||||
                        {
 | 
			
		||||
                            break;
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        multiline.Add(line);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    subEvent.Text = string.Join(ParserValues.NewLine, multiline);
 | 
			
		||||
                    subEvent.Text = subEvent.Text.Replace(@"\N", ParserValues.NewLine, StringComparison.OrdinalIgnoreCase);
 | 
			
		||||
                    subEvent.Text = Regex.Replace(subEvent.Text, @"\{(?:\\[0-9]?[\w.-]+(?:\([^\)]*\)|&H?[0-9A-Fa-f]+&|))+\}", string.Empty, RegexOptions.IgnoreCase);
 | 
			
		||||
                    subEvent.Text = Regex.Replace(subEvent.Text, "<", "<", RegexOptions.IgnoreCase);
 | 
			
		||||
                    subEvent.Text = Regex.Replace(subEvent.Text, ">", ">", RegexOptions.IgnoreCase);
 | 
			
		||||
                    subEvent.Text = Regex.Replace(subEvent.Text, "<(\\/?(font|b|u|i|s))((\\s+(\\w|\\w[\\w\\-]*\\w)(\\s*=\\s*(?:\\\".*?\\\"|'.*?'|[^'\\\">\\s]+))?)+\\s*|\\s*)(\\/?)>", "<$1$3$7>", RegexOptions.IgnoreCase);
 | 
			
		||||
                    trackEvents.Add(subEvent);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            trackInfo.TrackEvents = trackEvents;
 | 
			
		||||
            return trackInfo;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private long GetTicks(ReadOnlySpan<char> time)
 | 
			
		||||
        {
 | 
			
		||||
            return TimeSpan.TryParseExact(time, @"hh\:mm\:ss\.fff", _usCulture, out var span)
 | 
			
		||||
                ? span.Ticks
 | 
			
		||||
                : (TimeSpan.TryParseExact(time, @"hh\:mm\:ss\,fff", _usCulture, out span)
 | 
			
		||||
                ? span.Ticks : 0);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,477 +1,21 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Globalization;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
using MediaBrowser.Model.MediaInfo;
 | 
			
		||||
#nullable enable
 | 
			
		||||
 | 
			
		||||
using Microsoft.Extensions.Logging;
 | 
			
		||||
using Nikse.SubtitleEdit.Core.SubtitleFormats;
 | 
			
		||||
 | 
			
		||||
namespace MediaBrowser.MediaEncoding.Subtitles
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// <see href="https://github.com/SubtitleEdit/subtitleedit/blob/a299dc4407a31796364cc6ad83f0d3786194ba22/src/Logic/SubtitleFormats/SubStationAlpha.cs">Credit</see>.
 | 
			
		||||
    /// SubStation Alpha subtitle parser.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public class SsaParser : ISubtitleParser
 | 
			
		||||
    public class SsaParser : SubtitleEditParser<SubStationAlpha>
 | 
			
		||||
    {
 | 
			
		||||
        /// <inheritdoc />
 | 
			
		||||
        public SubtitleTrackInfo Parse(Stream stream, CancellationToken cancellationToken)
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Initializes a new instance of the <see cref="SsaParser"/> class.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="logger">The logger.</param>
 | 
			
		||||
        public SsaParser(ILogger logger) : base(logger)
 | 
			
		||||
        {
 | 
			
		||||
            var trackInfo = new SubtitleTrackInfo();
 | 
			
		||||
            var trackEvents = new List<SubtitleTrackEvent>();
 | 
			
		||||
 | 
			
		||||
            using (var reader = new StreamReader(stream))
 | 
			
		||||
            {
 | 
			
		||||
                bool eventsStarted = false;
 | 
			
		||||
 | 
			
		||||
                string[] format = "Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text".Split(',');
 | 
			
		||||
                int indexLayer = 0;
 | 
			
		||||
                int indexStart = 1;
 | 
			
		||||
                int indexEnd = 2;
 | 
			
		||||
                int indexStyle = 3;
 | 
			
		||||
                int indexName = 4;
 | 
			
		||||
                int indexEffect = 8;
 | 
			
		||||
                int indexText = 9;
 | 
			
		||||
                int lineNumber = 0;
 | 
			
		||||
 | 
			
		||||
                var header = new StringBuilder();
 | 
			
		||||
 | 
			
		||||
                string line;
 | 
			
		||||
 | 
			
		||||
                while ((line = reader.ReadLine()) != null)
 | 
			
		||||
                {
 | 
			
		||||
                    cancellationToken.ThrowIfCancellationRequested();
 | 
			
		||||
 | 
			
		||||
                    lineNumber++;
 | 
			
		||||
                    if (!eventsStarted)
 | 
			
		||||
                    {
 | 
			
		||||
                        header.AppendLine(line);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if (string.Equals(line.Trim(), "[events]", StringComparison.OrdinalIgnoreCase))
 | 
			
		||||
                    {
 | 
			
		||||
                        eventsStarted = true;
 | 
			
		||||
                    }
 | 
			
		||||
                    else if (!string.IsNullOrEmpty(line) && line.Trim().StartsWith(';'))
 | 
			
		||||
                    {
 | 
			
		||||
                        // skip comment lines
 | 
			
		||||
                    }
 | 
			
		||||
                    else if (eventsStarted && line.Trim().Length > 0)
 | 
			
		||||
                    {
 | 
			
		||||
                        string s = line.Trim().ToLowerInvariant();
 | 
			
		||||
                        if (s.StartsWith("format:", StringComparison.Ordinal))
 | 
			
		||||
                        {
 | 
			
		||||
                            if (line.Length > 10)
 | 
			
		||||
                            {
 | 
			
		||||
                                format = line.ToLowerInvariant().Substring(8).Split(',');
 | 
			
		||||
                                for (int i = 0; i < format.Length; i++)
 | 
			
		||||
                                {
 | 
			
		||||
                                    if (string.Equals(format[i].Trim(), "layer", StringComparison.OrdinalIgnoreCase))
 | 
			
		||||
                                    {
 | 
			
		||||
                                        indexLayer = i;
 | 
			
		||||
                                    }
 | 
			
		||||
                                    else if (string.Equals(format[i].Trim(), "start", StringComparison.OrdinalIgnoreCase))
 | 
			
		||||
                                    {
 | 
			
		||||
                                        indexStart = i;
 | 
			
		||||
                                    }
 | 
			
		||||
                                    else if (string.Equals(format[i].Trim(), "end", StringComparison.OrdinalIgnoreCase))
 | 
			
		||||
                                    {
 | 
			
		||||
                                        indexEnd = i;
 | 
			
		||||
                                    }
 | 
			
		||||
                                    else if (string.Equals(format[i].Trim(), "text", StringComparison.OrdinalIgnoreCase))
 | 
			
		||||
                                    {
 | 
			
		||||
                                        indexText = i;
 | 
			
		||||
                                    }
 | 
			
		||||
                                    else if (string.Equals(format[i].Trim(), "effect", StringComparison.OrdinalIgnoreCase))
 | 
			
		||||
                                    {
 | 
			
		||||
                                        indexEffect = i;
 | 
			
		||||
                                    }
 | 
			
		||||
                                    else if (string.Equals(format[i].Trim(), "style", StringComparison.OrdinalIgnoreCase))
 | 
			
		||||
                                    {
 | 
			
		||||
                                        indexStyle = i;
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        else if (!string.IsNullOrEmpty(s))
 | 
			
		||||
                        {
 | 
			
		||||
                            string text = string.Empty;
 | 
			
		||||
                            string start = string.Empty;
 | 
			
		||||
                            string end = string.Empty;
 | 
			
		||||
                            string style = string.Empty;
 | 
			
		||||
                            string layer = string.Empty;
 | 
			
		||||
                            string effect = string.Empty;
 | 
			
		||||
                            string name = string.Empty;
 | 
			
		||||
 | 
			
		||||
                            string[] splittedLine;
 | 
			
		||||
 | 
			
		||||
                            if (s.StartsWith("dialogue:", StringComparison.Ordinal))
 | 
			
		||||
                            {
 | 
			
		||||
                                splittedLine = line.Substring(10).Split(',');
 | 
			
		||||
                            }
 | 
			
		||||
                            else
 | 
			
		||||
                            {
 | 
			
		||||
                                splittedLine = line.Split(',');
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            for (int i = 0; i < splittedLine.Length; i++)
 | 
			
		||||
                            {
 | 
			
		||||
                                if (i == indexStart)
 | 
			
		||||
                                {
 | 
			
		||||
                                    start = splittedLine[i].Trim();
 | 
			
		||||
                                }
 | 
			
		||||
                                else if (i == indexEnd)
 | 
			
		||||
                                {
 | 
			
		||||
                                    end = splittedLine[i].Trim();
 | 
			
		||||
                                }
 | 
			
		||||
                                else if (i == indexLayer)
 | 
			
		||||
                                {
 | 
			
		||||
                                    layer = splittedLine[i];
 | 
			
		||||
                                }
 | 
			
		||||
                                else if (i == indexEffect)
 | 
			
		||||
                                {
 | 
			
		||||
                                    effect = splittedLine[i];
 | 
			
		||||
                                }
 | 
			
		||||
                                else if (i == indexText)
 | 
			
		||||
                                {
 | 
			
		||||
                                    text = splittedLine[i];
 | 
			
		||||
                                }
 | 
			
		||||
                                else if (i == indexStyle)
 | 
			
		||||
                                {
 | 
			
		||||
                                    style = splittedLine[i];
 | 
			
		||||
                                }
 | 
			
		||||
                                else if (i == indexName)
 | 
			
		||||
                                {
 | 
			
		||||
                                    name = splittedLine[i];
 | 
			
		||||
                                }
 | 
			
		||||
                                else if (i > indexText)
 | 
			
		||||
                                {
 | 
			
		||||
                                    text += "," + splittedLine[i];
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            try
 | 
			
		||||
                            {
 | 
			
		||||
                                trackEvents.Add(
 | 
			
		||||
                                    new SubtitleTrackEvent
 | 
			
		||||
                                    {
 | 
			
		||||
                                        StartPositionTicks = GetTimeCodeFromString(start),
 | 
			
		||||
                                        EndPositionTicks = GetTimeCodeFromString(end),
 | 
			
		||||
                                        Text = GetFormattedText(text)
 | 
			
		||||
                                    });
 | 
			
		||||
                            }
 | 
			
		||||
                            catch
 | 
			
		||||
                            {
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // if (header.Length > 0)
 | 
			
		||||
                // subtitle.Header = header.ToString();
 | 
			
		||||
 | 
			
		||||
                // subtitle.Renumber(1);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            trackInfo.TrackEvents = trackEvents.ToArray();
 | 
			
		||||
            return trackInfo;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private static long GetTimeCodeFromString(string time)
 | 
			
		||||
        {
 | 
			
		||||
            // h:mm:ss.cc
 | 
			
		||||
            string[] timeCode = time.Split(':', '.');
 | 
			
		||||
            return new TimeSpan(
 | 
			
		||||
                0,
 | 
			
		||||
                int.Parse(timeCode[0], CultureInfo.InvariantCulture),
 | 
			
		||||
                int.Parse(timeCode[1], CultureInfo.InvariantCulture),
 | 
			
		||||
                int.Parse(timeCode[2], CultureInfo.InvariantCulture),
 | 
			
		||||
                int.Parse(timeCode[3], CultureInfo.InvariantCulture) * 10).Ticks;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private static string GetFormattedText(string text)
 | 
			
		||||
        {
 | 
			
		||||
            text = text.Replace("\\n", ParserValues.NewLine, StringComparison.OrdinalIgnoreCase);
 | 
			
		||||
 | 
			
		||||
            for (int i = 0; i < 10; i++) // just look ten times...
 | 
			
		||||
            {
 | 
			
		||||
                if (text.Contains(@"{\fn", StringComparison.Ordinal))
 | 
			
		||||
                {
 | 
			
		||||
                    int start = text.IndexOf(@"{\fn", StringComparison.Ordinal);
 | 
			
		||||
                    int end = text.IndexOf('}', start);
 | 
			
		||||
                    if (end > 0 && !text.Substring(start).StartsWith("{\\fn}", StringComparison.Ordinal))
 | 
			
		||||
                    {
 | 
			
		||||
                        string fontName = text.Substring(start + 4, end - (start + 4));
 | 
			
		||||
                        string extraTags = string.Empty;
 | 
			
		||||
                        CheckAndAddSubTags(ref fontName, ref extraTags, out bool italic);
 | 
			
		||||
                        text = text.Remove(start, end - start + 1);
 | 
			
		||||
                        if (italic)
 | 
			
		||||
                        {
 | 
			
		||||
                            text = text.Insert(start, "<font face=\"" + fontName + "\"" + extraTags + "><i>");
 | 
			
		||||
                        }
 | 
			
		||||
                        else
 | 
			
		||||
                        {
 | 
			
		||||
                            text = text.Insert(start, "<font face=\"" + fontName + "\"" + extraTags + ">");
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        int indexOfEndTag = text.IndexOf("{\\fn}", start, StringComparison.Ordinal);
 | 
			
		||||
                        if (indexOfEndTag > 0)
 | 
			
		||||
                        {
 | 
			
		||||
                            text = text.Remove(indexOfEndTag, "{\\fn}".Length).Insert(indexOfEndTag, "</font>");
 | 
			
		||||
                        }
 | 
			
		||||
                        else
 | 
			
		||||
                        {
 | 
			
		||||
                            text += "</font>";
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (text.Contains(@"{\fs", StringComparison.Ordinal))
 | 
			
		||||
                {
 | 
			
		||||
                    int start = text.IndexOf(@"{\fs", StringComparison.Ordinal);
 | 
			
		||||
                    int end = text.IndexOf('}', start);
 | 
			
		||||
                    if (end > 0 && !text.Substring(start).StartsWith("{\\fs}", StringComparison.Ordinal))
 | 
			
		||||
                    {
 | 
			
		||||
                        string fontSize = text.Substring(start + 4, end - (start + 4));
 | 
			
		||||
                        string extraTags = string.Empty;
 | 
			
		||||
                        CheckAndAddSubTags(ref fontSize, ref extraTags, out bool italic);
 | 
			
		||||
                        if (IsInteger(fontSize))
 | 
			
		||||
                        {
 | 
			
		||||
                            text = text.Remove(start, end - start + 1);
 | 
			
		||||
                            if (italic)
 | 
			
		||||
                            {
 | 
			
		||||
                                text = text.Insert(start, "<font size=\"" + fontSize + "\"" + extraTags + "><i>");
 | 
			
		||||
                            }
 | 
			
		||||
                            else
 | 
			
		||||
                            {
 | 
			
		||||
                                text = text.Insert(start, "<font size=\"" + fontSize + "\"" + extraTags + ">");
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            int indexOfEndTag = text.IndexOf("{\\fs}", start, StringComparison.Ordinal);
 | 
			
		||||
                            if (indexOfEndTag > 0)
 | 
			
		||||
                            {
 | 
			
		||||
                                text = text.Remove(indexOfEndTag, "{\\fs}".Length).Insert(indexOfEndTag, "</font>");
 | 
			
		||||
                            }
 | 
			
		||||
                            else
 | 
			
		||||
                            {
 | 
			
		||||
                                text += "</font>";
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (text.Contains(@"{\c", StringComparison.Ordinal))
 | 
			
		||||
                {
 | 
			
		||||
                    int start = text.IndexOf(@"{\c", StringComparison.Ordinal);
 | 
			
		||||
                    int end = text.IndexOf('}', start);
 | 
			
		||||
                    if (end > 0 && !text.Substring(start).StartsWith("{\\c}", StringComparison.Ordinal))
 | 
			
		||||
                    {
 | 
			
		||||
                        string color = text.Substring(start + 4, end - (start + 4));
 | 
			
		||||
                        string extraTags = string.Empty;
 | 
			
		||||
                        CheckAndAddSubTags(ref color, ref extraTags, out bool italic);
 | 
			
		||||
 | 
			
		||||
                        color = color.Replace("&", string.Empty, StringComparison.Ordinal).TrimStart('H');
 | 
			
		||||
                        color = color.PadLeft(6, '0');
 | 
			
		||||
 | 
			
		||||
                        // switch to rrggbb from bbggrr
 | 
			
		||||
                        color = "#" + color.Remove(color.Length - 6) + color.Substring(color.Length - 2, 2) + color.Substring(color.Length - 4, 2) + color.Substring(color.Length - 6, 2);
 | 
			
		||||
                        color = color.ToLowerInvariant();
 | 
			
		||||
 | 
			
		||||
                        text = text.Remove(start, end - start + 1);
 | 
			
		||||
                        if (italic)
 | 
			
		||||
                        {
 | 
			
		||||
                            text = text.Insert(start, "<font color=\"" + color + "\"" + extraTags + "><i>");
 | 
			
		||||
                        }
 | 
			
		||||
                        else
 | 
			
		||||
                        {
 | 
			
		||||
                            text = text.Insert(start, "<font color=\"" + color + "\"" + extraTags + ">");
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        int indexOfEndTag = text.IndexOf("{\\c}", start, StringComparison.Ordinal);
 | 
			
		||||
                        if (indexOfEndTag > 0)
 | 
			
		||||
                        {
 | 
			
		||||
                            text = text.Remove(indexOfEndTag, "{\\c}".Length).Insert(indexOfEndTag, "</font>");
 | 
			
		||||
                        }
 | 
			
		||||
                        else
 | 
			
		||||
                        {
 | 
			
		||||
                            text += "</font>";
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (text.Contains(@"{\1c", StringComparison.Ordinal)) // "1" specifices primary color
 | 
			
		||||
                {
 | 
			
		||||
                    int start = text.IndexOf(@"{\1c", StringComparison.Ordinal);
 | 
			
		||||
                    int end = text.IndexOf('}', start);
 | 
			
		||||
                    if (end > 0 && !text.Substring(start).StartsWith("{\\1c}", StringComparison.Ordinal))
 | 
			
		||||
                    {
 | 
			
		||||
                        string color = text.Substring(start + 5, end - (start + 5));
 | 
			
		||||
                        string extraTags = string.Empty;
 | 
			
		||||
                        CheckAndAddSubTags(ref color, ref extraTags, out bool italic);
 | 
			
		||||
 | 
			
		||||
                        color = color.Replace("&", string.Empty, StringComparison.Ordinal).TrimStart('H');
 | 
			
		||||
                        color = color.PadLeft(6, '0');
 | 
			
		||||
 | 
			
		||||
                        // switch to rrggbb from bbggrr
 | 
			
		||||
                        color = "#" + color.Remove(color.Length - 6) + color.Substring(color.Length - 2, 2) + color.Substring(color.Length - 4, 2) + color.Substring(color.Length - 6, 2);
 | 
			
		||||
                        color = color.ToLowerInvariant();
 | 
			
		||||
 | 
			
		||||
                        text = text.Remove(start, end - start + 1);
 | 
			
		||||
                        if (italic)
 | 
			
		||||
                        {
 | 
			
		||||
                            text = text.Insert(start, "<font color=\"" + color + "\"" + extraTags + "><i>");
 | 
			
		||||
                        }
 | 
			
		||||
                        else
 | 
			
		||||
                        {
 | 
			
		||||
                            text = text.Insert(start, "<font color=\"" + color + "\"" + extraTags + ">");
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        int indexOfEndTag = text.IndexOf("{\\1c}", start, StringComparison.Ordinal);
 | 
			
		||||
                        if (indexOfEndTag > 0)
 | 
			
		||||
                        {
 | 
			
		||||
                            text = text.Remove(indexOfEndTag, "{\\1c}".Length).Insert(indexOfEndTag, "</font>");
 | 
			
		||||
                        }
 | 
			
		||||
                        else
 | 
			
		||||
                        {
 | 
			
		||||
                            text += "</font>";
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            text = text.Replace(@"{\i1}", "<i>", StringComparison.Ordinal);
 | 
			
		||||
            text = text.Replace(@"{\i0}", "</i>", StringComparison.Ordinal);
 | 
			
		||||
            text = text.Replace(@"{\i}", "</i>", StringComparison.Ordinal);
 | 
			
		||||
            if (CountTagInText(text, "<i>") > CountTagInText(text, "</i>"))
 | 
			
		||||
            {
 | 
			
		||||
                text += "</i>";
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            text = text.Replace(@"{\u1}", "<u>", StringComparison.Ordinal);
 | 
			
		||||
            text = text.Replace(@"{\u0}", "</u>", StringComparison.Ordinal);
 | 
			
		||||
            text = text.Replace(@"{\u}", "</u>", StringComparison.Ordinal);
 | 
			
		||||
            if (CountTagInText(text, "<u>") > CountTagInText(text, "</u>"))
 | 
			
		||||
            {
 | 
			
		||||
                text += "</u>";
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            text = text.Replace(@"{\b1}", "<b>", StringComparison.Ordinal);
 | 
			
		||||
            text = text.Replace(@"{\b0}", "</b>", StringComparison.Ordinal);
 | 
			
		||||
            text = text.Replace(@"{\b}", "</b>", StringComparison.Ordinal);
 | 
			
		||||
            if (CountTagInText(text, "<b>") > CountTagInText(text, "</b>"))
 | 
			
		||||
            {
 | 
			
		||||
                text += "</b>";
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return text;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private static bool IsInteger(string s)
 | 
			
		||||
            => int.TryParse(s, out _);
 | 
			
		||||
 | 
			
		||||
        private static int CountTagInText(string text, string tag)
 | 
			
		||||
        {
 | 
			
		||||
            int count = 0;
 | 
			
		||||
            int index = text.IndexOf(tag, StringComparison.Ordinal);
 | 
			
		||||
            while (index >= 0)
 | 
			
		||||
            {
 | 
			
		||||
                count++;
 | 
			
		||||
                if (index == text.Length)
 | 
			
		||||
                {
 | 
			
		||||
                    return count;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                index = text.IndexOf(tag, index + 1, StringComparison.Ordinal);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return count;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private static void CheckAndAddSubTags(ref string tagName, ref string extraTags, out bool italic)
 | 
			
		||||
        {
 | 
			
		||||
            italic = false;
 | 
			
		||||
            int indexOfSPlit = tagName.IndexOf('\\', StringComparison.Ordinal);
 | 
			
		||||
            if (indexOfSPlit > 0)
 | 
			
		||||
            {
 | 
			
		||||
                string rest = tagName.Substring(indexOfSPlit).TrimStart('\\');
 | 
			
		||||
                tagName = tagName.Remove(indexOfSPlit);
 | 
			
		||||
 | 
			
		||||
                for (int i = 0; i < 10; i++)
 | 
			
		||||
                {
 | 
			
		||||
                    if (rest.StartsWith("fs", StringComparison.Ordinal) && rest.Length > 2)
 | 
			
		||||
                    {
 | 
			
		||||
                        indexOfSPlit = rest.IndexOf('\\', StringComparison.Ordinal);
 | 
			
		||||
                        string fontSize = rest;
 | 
			
		||||
                        if (indexOfSPlit > 0)
 | 
			
		||||
                        {
 | 
			
		||||
                            fontSize = rest.Substring(0, indexOfSPlit);
 | 
			
		||||
                            rest = rest.Substring(indexOfSPlit).TrimStart('\\');
 | 
			
		||||
                        }
 | 
			
		||||
                        else
 | 
			
		||||
                        {
 | 
			
		||||
                            rest = string.Empty;
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        extraTags += " size=\"" + fontSize.Substring(2) + "\"";
 | 
			
		||||
                    }
 | 
			
		||||
                    else if (rest.StartsWith("fn", StringComparison.Ordinal) && rest.Length > 2)
 | 
			
		||||
                    {
 | 
			
		||||
                        indexOfSPlit = rest.IndexOf('\\', StringComparison.Ordinal);
 | 
			
		||||
                        string fontName = rest;
 | 
			
		||||
                        if (indexOfSPlit > 0)
 | 
			
		||||
                        {
 | 
			
		||||
                            fontName = rest.Substring(0, indexOfSPlit);
 | 
			
		||||
                            rest = rest.Substring(indexOfSPlit).TrimStart('\\');
 | 
			
		||||
                        }
 | 
			
		||||
                        else
 | 
			
		||||
                        {
 | 
			
		||||
                            rest = string.Empty;
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        extraTags += " face=\"" + fontName.Substring(2) + "\"";
 | 
			
		||||
                    }
 | 
			
		||||
                    else if (rest.StartsWith("c", StringComparison.Ordinal) && rest.Length > 2)
 | 
			
		||||
                    {
 | 
			
		||||
                        indexOfSPlit = rest.IndexOf('\\', StringComparison.Ordinal);
 | 
			
		||||
                        string fontColor = rest;
 | 
			
		||||
                        if (indexOfSPlit > 0)
 | 
			
		||||
                        {
 | 
			
		||||
                            fontColor = rest.Substring(0, indexOfSPlit);
 | 
			
		||||
                            rest = rest.Substring(indexOfSPlit).TrimStart('\\');
 | 
			
		||||
                        }
 | 
			
		||||
                        else
 | 
			
		||||
                        {
 | 
			
		||||
                            rest = string.Empty;
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        string color = fontColor.Substring(2);
 | 
			
		||||
                        color = color.Replace("&", string.Empty, StringComparison.Ordinal).TrimStart('H');
 | 
			
		||||
                        color = color.PadLeft(6, '0');
 | 
			
		||||
                        // switch to rrggbb from bbggrr
 | 
			
		||||
                        color = "#" + color.Remove(color.Length - 6) + color.Substring(color.Length - 2, 2) + color.Substring(color.Length - 4, 2) + color.Substring(color.Length - 6, 2);
 | 
			
		||||
                        color = color.ToLowerInvariant();
 | 
			
		||||
 | 
			
		||||
                        extraTags += " color=\"" + color + "\"";
 | 
			
		||||
                    }
 | 
			
		||||
                    else if (rest.StartsWith("i1", StringComparison.Ordinal) && rest.Length > 1)
 | 
			
		||||
                    {
 | 
			
		||||
                        indexOfSPlit = rest.IndexOf('\\', StringComparison.Ordinal);
 | 
			
		||||
                        italic = true;
 | 
			
		||||
                        if (indexOfSPlit > 0)
 | 
			
		||||
                        {
 | 
			
		||||
                            rest = rest.Substring(indexOfSPlit).TrimStart('\\');
 | 
			
		||||
                        }
 | 
			
		||||
                        else
 | 
			
		||||
                        {
 | 
			
		||||
                            rest = string.Empty;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    else if (rest.Length > 0 && rest.Contains('\\', StringComparison.Ordinal))
 | 
			
		||||
                    {
 | 
			
		||||
                        indexOfSPlit = rest.IndexOf('\\', StringComparison.Ordinal);
 | 
			
		||||
                        rest = rest.Substring(indexOfSPlit).TrimStart('\\');
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										63
									
								
								MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,63 @@
 | 
			
		||||
#nullable enable
 | 
			
		||||
 | 
			
		||||
using System.Globalization;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
using MediaBrowser.Common.Extensions;
 | 
			
		||||
using MediaBrowser.Model.MediaInfo;
 | 
			
		||||
using Microsoft.Extensions.Logging;
 | 
			
		||||
using Nikse.SubtitleEdit.Core;
 | 
			
		||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
 | 
			
		||||
using SubtitleFormat = Nikse.SubtitleEdit.Core.SubtitleFormats.SubtitleFormat;
 | 
			
		||||
 | 
			
		||||
namespace MediaBrowser.MediaEncoding.Subtitles
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// SubStation Alpha subtitle parser.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <typeparam name="T">The <see cref="SubtitleFormat" />.</typeparam>
 | 
			
		||||
    public abstract class SubtitleEditParser<T> : ISubtitleParser
 | 
			
		||||
        where T : SubtitleFormat, new()
 | 
			
		||||
    {
 | 
			
		||||
        private readonly ILogger _logger;
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Initializes a new instance of the <see cref="SubtitleEditParser{T}"/> class.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="logger">The logger.</param>
 | 
			
		||||
        protected SubtitleEditParser(ILogger logger)
 | 
			
		||||
        {
 | 
			
		||||
            _logger = logger;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc />
 | 
			
		||||
        public SubtitleTrackInfo Parse(Stream stream, CancellationToken cancellationToken)
 | 
			
		||||
        {
 | 
			
		||||
            var subtitle = new Subtitle();
 | 
			
		||||
            var subRip = new T();
 | 
			
		||||
            var lines = stream.ReadAllLines().ToList();
 | 
			
		||||
            subRip.LoadSubtitle(subtitle, lines, "untitled");
 | 
			
		||||
            if (subRip.ErrorCount > 0)
 | 
			
		||||
            {
 | 
			
		||||
                _logger.LogError("{ErrorCount} errors encountered while parsing subtitle.");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var trackInfo = new SubtitleTrackInfo();
 | 
			
		||||
            int len = subtitle.Paragraphs.Count;
 | 
			
		||||
            var trackEvents = new SubtitleTrackEvent[len];
 | 
			
		||||
            for (int i = 0; i < len; i++)
 | 
			
		||||
            {
 | 
			
		||||
                var p = subtitle.Paragraphs[i];
 | 
			
		||||
                trackEvents[i] = new SubtitleTrackEvent(p.Number.ToString(CultureInfo.InvariantCulture), p.Text)
 | 
			
		||||
                {
 | 
			
		||||
                    StartPositionTicks = p.StartTime.TimeSpan.Ticks,
 | 
			
		||||
                    EndPositionTicks = p.EndTime.TimeSpan.Ticks
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            trackInfo.TrackEvents = trackEvents;
 | 
			
		||||
            return trackInfo;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -27,7 +27,6 @@ namespace MediaBrowser.MediaEncoding.Subtitles
 | 
			
		||||
{
 | 
			
		||||
    public class SubtitleEncoder : ISubtitleEncoder
 | 
			
		||||
    {
 | 
			
		||||
        private readonly ILibraryManager _libraryManager;
 | 
			
		||||
        private readonly ILogger<SubtitleEncoder> _logger;
 | 
			
		||||
        private readonly IApplicationPaths _appPaths;
 | 
			
		||||
        private readonly IFileSystem _fileSystem;
 | 
			
		||||
@ -42,7 +41,6 @@ namespace MediaBrowser.MediaEncoding.Subtitles
 | 
			
		||||
            new ConcurrentDictionary<string, SemaphoreSlim>();
 | 
			
		||||
 | 
			
		||||
        public SubtitleEncoder(
 | 
			
		||||
            ILibraryManager libraryManager,
 | 
			
		||||
            ILogger<SubtitleEncoder> logger,
 | 
			
		||||
            IApplicationPaths appPaths,
 | 
			
		||||
            IFileSystem fileSystem,
 | 
			
		||||
@ -50,7 +48,6 @@ namespace MediaBrowser.MediaEncoding.Subtitles
 | 
			
		||||
            IHttpClientFactory httpClientFactory,
 | 
			
		||||
            IMediaSourceManager mediaSourceManager)
 | 
			
		||||
        {
 | 
			
		||||
            _libraryManager = libraryManager;
 | 
			
		||||
            _logger = logger;
 | 
			
		||||
            _appPaths = appPaths;
 | 
			
		||||
            _fileSystem = fileSystem;
 | 
			
		||||
@ -279,12 +276,12 @@ namespace MediaBrowser.MediaEncoding.Subtitles
 | 
			
		||||
 | 
			
		||||
            if (string.Equals(format, SubtitleFormat.SSA, StringComparison.OrdinalIgnoreCase))
 | 
			
		||||
            {
 | 
			
		||||
                return new SsaParser();
 | 
			
		||||
                return new SsaParser(_logger);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (string.Equals(format, SubtitleFormat.ASS, StringComparison.OrdinalIgnoreCase))
 | 
			
		||||
            {
 | 
			
		||||
                return new AssParser();
 | 
			
		||||
                return new AssParser(_logger);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (throwIfMissing)
 | 
			
		||||
 | 
			
		||||
@ -7,6 +7,13 @@ namespace MediaBrowser.Model.Channels
 | 
			
		||||
{
 | 
			
		||||
    public class ChannelFeatures
 | 
			
		||||
    {
 | 
			
		||||
        public ChannelFeatures()
 | 
			
		||||
        {
 | 
			
		||||
            MediaTypes = Array.Empty<ChannelMediaType>();
 | 
			
		||||
            ContentTypes = Array.Empty<ChannelMediaContentType>();
 | 
			
		||||
            DefaultSortFields = Array.Empty<ChannelItemSortField>();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets the name.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
@ -38,7 +45,7 @@ namespace MediaBrowser.Model.Channels
 | 
			
		||||
        public ChannelMediaContentType[] ContentTypes { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Represents the maximum number of records the channel allows retrieving at a time.
 | 
			
		||||
        /// Gets or sets the maximum number of records the channel allows retrieving at a time.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public int? MaxPageSize { get; set; }
 | 
			
		||||
 | 
			
		||||
@ -55,7 +62,7 @@ namespace MediaBrowser.Model.Channels
 | 
			
		||||
        public ChannelItemSortField[] DefaultSortFields { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Indicates if a sort ascending/descending toggle is supported or not.
 | 
			
		||||
        /// Gets or sets a value indicating whether a sort ascending/descending toggle is supported.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public bool SupportsSortOrderToggle { get; set; }
 | 
			
		||||
 | 
			
		||||
@ -76,12 +83,5 @@ namespace MediaBrowser.Model.Channels
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <value><c>true</c> if [supports content downloading]; otherwise, <c>false</c>.</value>
 | 
			
		||||
        public bool SupportsContentDownloading { get; set; }
 | 
			
		||||
 | 
			
		||||
        public ChannelFeatures()
 | 
			
		||||
        {
 | 
			
		||||
            MediaTypes = Array.Empty<ChannelMediaType>();
 | 
			
		||||
            ContentTypes = Array.Empty<ChannelMediaContentType>();
 | 
			
		||||
            DefaultSortFields = Array.Empty<ChannelItemSortField>();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -10,7 +10,7 @@ namespace MediaBrowser.Model.Channels
 | 
			
		||||
    public class ChannelQuery
 | 
			
		||||
    {
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Fields to return within the items, in addition to basic information.
 | 
			
		||||
        /// Gets or sets the fields to return within the items, in addition to basic information.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <value>The fields.</value>
 | 
			
		||||
        public ItemFields[] Fields { get; set; }
 | 
			
		||||
@ -28,13 +28,13 @@ namespace MediaBrowser.Model.Channels
 | 
			
		||||
        public Guid UserId { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Skips over a given number of items within the results. Use for paging.
 | 
			
		||||
        /// Gets or sets the start index. Use for paging.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <value>The start index.</value>
 | 
			
		||||
        public int? StartIndex { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// The maximum number of items to return.
 | 
			
		||||
        /// Gets or sets the maximum number of items to return.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <value>The limit.</value>
 | 
			
		||||
        public int? Limit { get; set; }
 | 
			
		||||
 | 
			
		||||
@ -5,6 +5,41 @@ namespace MediaBrowser.Model.Configuration
 | 
			
		||||
{
 | 
			
		||||
    public class EncodingOptions
 | 
			
		||||
    {
 | 
			
		||||
        public EncodingOptions()
 | 
			
		||||
        {
 | 
			
		||||
            EnableFallbackFont = false;
 | 
			
		||||
            DownMixAudioBoost = 2;
 | 
			
		||||
            MaxMuxingQueueSize = 2048;
 | 
			
		||||
            EnableThrottling = false;
 | 
			
		||||
            ThrottleDelaySeconds = 180;
 | 
			
		||||
            EncodingThreadCount = -1;
 | 
			
		||||
            // This is a DRM device that is almost guaranteed to be there on every intel platform,
 | 
			
		||||
            // plus it's the default one in ffmpeg if you don't specify anything
 | 
			
		||||
            VaapiDevice = "/dev/dri/renderD128";
 | 
			
		||||
            // This is the OpenCL device that is used for tonemapping.
 | 
			
		||||
            // The left side of the dot is the platform number, and the right side is the device number on the platform.
 | 
			
		||||
            OpenclDevice = "0.0";
 | 
			
		||||
            EnableTonemapping = false;
 | 
			
		||||
            EnableVppTonemapping = false;
 | 
			
		||||
            TonemappingAlgorithm = "hable";
 | 
			
		||||
            TonemappingRange = "auto";
 | 
			
		||||
            TonemappingDesat = 0;
 | 
			
		||||
            TonemappingThreshold = 0.8;
 | 
			
		||||
            TonemappingPeak = 100;
 | 
			
		||||
            TonemappingParam = 0;
 | 
			
		||||
            H264Crf = 23;
 | 
			
		||||
            H265Crf = 28;
 | 
			
		||||
            DeinterlaceDoubleRate = false;
 | 
			
		||||
            DeinterlaceMethod = "yadif";
 | 
			
		||||
            EnableDecodingColorDepth10Hevc = true;
 | 
			
		||||
            EnableDecodingColorDepth10Vp9 = true;
 | 
			
		||||
            EnableEnhancedNvdecDecoder = true;
 | 
			
		||||
            EnableHardwareEncoding = true;
 | 
			
		||||
            AllowHevcEncoding = true;
 | 
			
		||||
            EnableSubtitleExtraction = true;
 | 
			
		||||
            HardwareDecodingCodecs = new string[] { "h264", "vc1" };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public int EncodingThreadCount { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string TranscodingTempPath { get; set; }
 | 
			
		||||
@ -24,12 +59,12 @@ namespace MediaBrowser.Model.Configuration
 | 
			
		||||
        public string HardwareAccelerationType { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// FFmpeg path as set by the user via the UI.
 | 
			
		||||
        /// Gets or sets the FFmpeg path as set by the user via the UI.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public string EncoderAppPath { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// The current FFmpeg path being used by the system and displayed on the transcode page.
 | 
			
		||||
        /// Gets or sets the current FFmpeg path being used by the system and displayed on the transcode page.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public string EncoderAppPathDisplay { get; set; }
 | 
			
		||||
 | 
			
		||||
@ -76,40 +111,5 @@ namespace MediaBrowser.Model.Configuration
 | 
			
		||||
        public bool EnableSubtitleExtraction { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string[] HardwareDecodingCodecs { get; set; }
 | 
			
		||||
 | 
			
		||||
        public EncodingOptions()
 | 
			
		||||
        {
 | 
			
		||||
            EnableFallbackFont = false;
 | 
			
		||||
            DownMixAudioBoost = 2;
 | 
			
		||||
            MaxMuxingQueueSize = 2048;
 | 
			
		||||
            EnableThrottling = false;
 | 
			
		||||
            ThrottleDelaySeconds = 180;
 | 
			
		||||
            EncodingThreadCount = -1;
 | 
			
		||||
            // This is a DRM device that is almost guaranteed to be there on every intel platform,
 | 
			
		||||
            // plus it's the default one in ffmpeg if you don't specify anything
 | 
			
		||||
            VaapiDevice = "/dev/dri/renderD128";
 | 
			
		||||
            // This is the OpenCL device that is used for tonemapping.
 | 
			
		||||
            // The left side of the dot is the platform number, and the right side is the device number on the platform.
 | 
			
		||||
            OpenclDevice = "0.0";
 | 
			
		||||
            EnableTonemapping = false;
 | 
			
		||||
            EnableVppTonemapping = false;
 | 
			
		||||
            TonemappingAlgorithm = "hable";
 | 
			
		||||
            TonemappingRange = "auto";
 | 
			
		||||
            TonemappingDesat = 0;
 | 
			
		||||
            TonemappingThreshold = 0.8;
 | 
			
		||||
            TonemappingPeak = 100;
 | 
			
		||||
            TonemappingParam = 0;
 | 
			
		||||
            H264Crf = 23;
 | 
			
		||||
            H265Crf = 28;
 | 
			
		||||
            DeinterlaceDoubleRate = false;
 | 
			
		||||
            DeinterlaceMethod = "yadif";
 | 
			
		||||
            EnableDecodingColorDepth10Hevc = true;
 | 
			
		||||
            EnableDecodingColorDepth10Vp9 = true;
 | 
			
		||||
            EnableEnhancedNvdecDecoder = true;
 | 
			
		||||
            EnableHardwareEncoding = true;
 | 
			
		||||
            AllowHevcEncoding = true;
 | 
			
		||||
            EnableSubtitleExtraction = true;
 | 
			
		||||
            HardwareDecodingCodecs = new string[] { "h264", "vc1" };
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -6,6 +6,11 @@ namespace MediaBrowser.Model.Configuration
 | 
			
		||||
{
 | 
			
		||||
    public class ImageOption
 | 
			
		||||
    {
 | 
			
		||||
        public ImageOption()
 | 
			
		||||
        {
 | 
			
		||||
            Limit = 1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets the type.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
@ -23,10 +28,5 @@ namespace MediaBrowser.Model.Configuration
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <value>The minimum width.</value>
 | 
			
		||||
        public int MinWidth { get; set; }
 | 
			
		||||
 | 
			
		||||
        public ImageOption()
 | 
			
		||||
        {
 | 
			
		||||
            Limit = 1;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -2,13 +2,30 @@
 | 
			
		||||
#pragma warning disable CS1591
 | 
			
		||||
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using MediaBrowser.Model.Entities;
 | 
			
		||||
 | 
			
		||||
namespace MediaBrowser.Model.Configuration
 | 
			
		||||
{
 | 
			
		||||
    public class LibraryOptions
 | 
			
		||||
    {
 | 
			
		||||
        public LibraryOptions()
 | 
			
		||||
        {
 | 
			
		||||
            TypeOptions = Array.Empty<TypeOptions>();
 | 
			
		||||
            DisabledSubtitleFetchers = Array.Empty<string>();
 | 
			
		||||
            SubtitleFetcherOrder = Array.Empty<string>();
 | 
			
		||||
            DisabledLocalMetadataReaders = Array.Empty<string>();
 | 
			
		||||
 | 
			
		||||
            SkipSubtitlesIfAudioTrackMatches = true;
 | 
			
		||||
            RequirePerfectSubtitleMatch = true;
 | 
			
		||||
 | 
			
		||||
            EnablePhotos = true;
 | 
			
		||||
            SaveSubtitlesWithMedia = true;
 | 
			
		||||
            EnableRealtimeMonitor = true;
 | 
			
		||||
            PathInfos = Array.Empty<MediaPathInfo>();
 | 
			
		||||
            EnableInternetProviders = true;
 | 
			
		||||
            EnableAutomaticSeriesGrouping = true;
 | 
			
		||||
            SeasonZeroDisplayName = "Specials";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public bool EnablePhotos { get; set; }
 | 
			
		||||
 | 
			
		||||
        public bool EnableRealtimeMonitor { get; set; }
 | 
			
		||||
@ -79,387 +96,5 @@ namespace MediaBrowser.Model.Configuration
 | 
			
		||||
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public LibraryOptions()
 | 
			
		||||
        {
 | 
			
		||||
            TypeOptions = Array.Empty<TypeOptions>();
 | 
			
		||||
            DisabledSubtitleFetchers = Array.Empty<string>();
 | 
			
		||||
            SubtitleFetcherOrder = Array.Empty<string>();
 | 
			
		||||
            DisabledLocalMetadataReaders = Array.Empty<string>();
 | 
			
		||||
 | 
			
		||||
            SkipSubtitlesIfAudioTrackMatches = true;
 | 
			
		||||
            RequirePerfectSubtitleMatch = true;
 | 
			
		||||
 | 
			
		||||
            EnablePhotos = true;
 | 
			
		||||
            SaveSubtitlesWithMedia = true;
 | 
			
		||||
            EnableRealtimeMonitor = true;
 | 
			
		||||
            PathInfos = Array.Empty<MediaPathInfo>();
 | 
			
		||||
            EnableInternetProviders = true;
 | 
			
		||||
            EnableAutomaticSeriesGrouping = true;
 | 
			
		||||
            SeasonZeroDisplayName = "Specials";
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public class MediaPathInfo
 | 
			
		||||
    {
 | 
			
		||||
        public string Path { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string NetworkPath { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public class TypeOptions
 | 
			
		||||
    {
 | 
			
		||||
        public string Type { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string[] MetadataFetchers { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string[] MetadataFetcherOrder { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string[] ImageFetchers { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string[] ImageFetcherOrder { get; set; }
 | 
			
		||||
 | 
			
		||||
        public ImageOption[] ImageOptions { get; set; }
 | 
			
		||||
 | 
			
		||||
        public ImageOption GetImageOptions(ImageType type)
 | 
			
		||||
        {
 | 
			
		||||
            foreach (var i in ImageOptions)
 | 
			
		||||
            {
 | 
			
		||||
                if (i.Type == type)
 | 
			
		||||
                {
 | 
			
		||||
                    return i;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (DefaultImageOptions.TryGetValue(Type, out ImageOption[] options))
 | 
			
		||||
            {
 | 
			
		||||
                foreach (var i in options)
 | 
			
		||||
                {
 | 
			
		||||
                    if (i.Type == type)
 | 
			
		||||
                    {
 | 
			
		||||
                        return i;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return DefaultInstance;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public int GetLimit(ImageType type)
 | 
			
		||||
        {
 | 
			
		||||
            return GetImageOptions(type).Limit;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public int GetMinWidth(ImageType type)
 | 
			
		||||
        {
 | 
			
		||||
            return GetImageOptions(type).MinWidth;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public bool IsEnabled(ImageType type)
 | 
			
		||||
        {
 | 
			
		||||
            return GetLimit(type) > 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public TypeOptions()
 | 
			
		||||
        {
 | 
			
		||||
            MetadataFetchers = Array.Empty<string>();
 | 
			
		||||
            MetadataFetcherOrder = Array.Empty<string>();
 | 
			
		||||
            ImageFetchers = Array.Empty<string>();
 | 
			
		||||
            ImageFetcherOrder = Array.Empty<string>();
 | 
			
		||||
            ImageOptions = Array.Empty<ImageOption>();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static Dictionary<string, ImageOption[]> DefaultImageOptions = new Dictionary<string, ImageOption[]>
 | 
			
		||||
        {
 | 
			
		||||
            {
 | 
			
		||||
                "Movie", new []
 | 
			
		||||
                {
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 1,
 | 
			
		||||
                        MinWidth = 1280,
 | 
			
		||||
                        Type = ImageType.Backdrop
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    // Don't download this by default as it's rarely used.
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 0,
 | 
			
		||||
                        Type = ImageType.Art
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    // Don't download this by default as it's rarely used.
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 0,
 | 
			
		||||
                        Type = ImageType.Disc
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 1,
 | 
			
		||||
                        Type = ImageType.Primary
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 0,
 | 
			
		||||
                        Type = ImageType.Banner
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 1,
 | 
			
		||||
                        Type = ImageType.Thumb
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 1,
 | 
			
		||||
                        Type = ImageType.Logo
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "MusicVideo", new []
 | 
			
		||||
                {
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 1,
 | 
			
		||||
                        MinWidth = 1280,
 | 
			
		||||
                        Type = ImageType.Backdrop
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    // Don't download this by default as it's rarely used.
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 0,
 | 
			
		||||
                        Type = ImageType.Art
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    // Don't download this by default as it's rarely used.
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 0,
 | 
			
		||||
                        Type = ImageType.Disc
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 1,
 | 
			
		||||
                        Type = ImageType.Primary
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 0,
 | 
			
		||||
                        Type = ImageType.Banner
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 1,
 | 
			
		||||
                        Type = ImageType.Thumb
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 1,
 | 
			
		||||
                        Type = ImageType.Logo
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "Series", new []
 | 
			
		||||
                {
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 1,
 | 
			
		||||
                        MinWidth = 1280,
 | 
			
		||||
                        Type = ImageType.Backdrop
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    // Don't download this by default as it's rarely used.
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 0,
 | 
			
		||||
                        Type = ImageType.Art
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 1,
 | 
			
		||||
                        Type = ImageType.Primary
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 1,
 | 
			
		||||
                        Type = ImageType.Banner
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 1,
 | 
			
		||||
                        Type = ImageType.Thumb
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 1,
 | 
			
		||||
                        Type = ImageType.Logo
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "MusicAlbum", new []
 | 
			
		||||
                {
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 0,
 | 
			
		||||
                        MinWidth = 1280,
 | 
			
		||||
                        Type = ImageType.Backdrop
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    // Don't download this by default as it's rarely used.
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 0,
 | 
			
		||||
                        Type = ImageType.Disc
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "MusicArtist", new []
 | 
			
		||||
                {
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 1,
 | 
			
		||||
                        MinWidth = 1280,
 | 
			
		||||
                        Type = ImageType.Backdrop
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    // Don't download this by default
 | 
			
		||||
                    // They do look great, but most artists won't have them, which means a banner view isn't really possible
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 0,
 | 
			
		||||
                        Type = ImageType.Banner
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    // Don't download this by default
 | 
			
		||||
                    // Generally not used
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 0,
 | 
			
		||||
                        Type = ImageType.Art
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 1,
 | 
			
		||||
                        Type = ImageType.Logo
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "BoxSet", new []
 | 
			
		||||
                {
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 1,
 | 
			
		||||
                        MinWidth = 1280,
 | 
			
		||||
                        Type = ImageType.Backdrop
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 1,
 | 
			
		||||
                        Type = ImageType.Primary
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 1,
 | 
			
		||||
                        Type = ImageType.Thumb
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 1,
 | 
			
		||||
                        Type = ImageType.Logo
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    // Don't download this by default as it's rarely used.
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 0,
 | 
			
		||||
                        Type = ImageType.Art
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    // Don't download this by default as it's rarely used.
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 0,
 | 
			
		||||
                        Type = ImageType.Disc
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    // Don't download this by default as it's rarely used.
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 0,
 | 
			
		||||
                        Type = ImageType.Banner
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "Season", new []
 | 
			
		||||
                {
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 0,
 | 
			
		||||
                        MinWidth = 1280,
 | 
			
		||||
                        Type = ImageType.Backdrop
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 1,
 | 
			
		||||
                        Type = ImageType.Primary
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 0,
 | 
			
		||||
                        Type = ImageType.Banner
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 0,
 | 
			
		||||
                        Type = ImageType.Thumb
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "Episode", new []
 | 
			
		||||
                {
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 0,
 | 
			
		||||
                        MinWidth = 1280,
 | 
			
		||||
                        Type = ImageType.Backdrop
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 1,
 | 
			
		||||
                        Type = ImageType.Primary
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        public static ImageOption DefaultInstance = new ImageOption();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										12
									
								
								MediaBrowser.Model/Configuration/MediaPathInfo.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								MediaBrowser.Model/Configuration/MediaPathInfo.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,12 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
#pragma warning disable CS1591
 | 
			
		||||
 | 
			
		||||
namespace MediaBrowser.Model.Configuration
 | 
			
		||||
{
 | 
			
		||||
    public class MediaPathInfo
 | 
			
		||||
    {
 | 
			
		||||
        public string Path { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string NetworkPath { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -4,11 +4,11 @@ namespace MediaBrowser.Model.Configuration
 | 
			
		||||
{
 | 
			
		||||
    public class MetadataConfiguration
 | 
			
		||||
    {
 | 
			
		||||
        public bool UseFileCreationTimeForDateAdded { get; set; }
 | 
			
		||||
 | 
			
		||||
        public MetadataConfiguration()
 | 
			
		||||
        {
 | 
			
		||||
            UseFileCreationTimeForDateAdded = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public bool UseFileCreationTimeForDateAdded { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -10,6 +10,16 @@ namespace MediaBrowser.Model.Configuration
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public class MetadataOptions
 | 
			
		||||
    {
 | 
			
		||||
        public MetadataOptions()
 | 
			
		||||
        {
 | 
			
		||||
            DisabledMetadataSavers = Array.Empty<string>();
 | 
			
		||||
            LocalMetadataReaderOrder = Array.Empty<string>();
 | 
			
		||||
            DisabledMetadataFetchers = Array.Empty<string>();
 | 
			
		||||
            MetadataFetcherOrder = Array.Empty<string>();
 | 
			
		||||
            DisabledImageFetchers = Array.Empty<string>();
 | 
			
		||||
            ImageFetcherOrder = Array.Empty<string>();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public string ItemType { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string[] DisabledMetadataSavers { get; set; }
 | 
			
		||||
@ -23,15 +33,5 @@ namespace MediaBrowser.Model.Configuration
 | 
			
		||||
        public string[] DisabledImageFetchers { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string[] ImageFetcherOrder { get; set; }
 | 
			
		||||
 | 
			
		||||
        public MetadataOptions()
 | 
			
		||||
        {
 | 
			
		||||
            DisabledMetadataSavers = Array.Empty<string>();
 | 
			
		||||
            LocalMetadataReaderOrder = Array.Empty<string>();
 | 
			
		||||
            DisabledMetadataFetchers = Array.Empty<string>();
 | 
			
		||||
            MetadataFetcherOrder = Array.Empty<string>();
 | 
			
		||||
            DisabledImageFetchers = Array.Empty<string>();
 | 
			
		||||
            ImageFetcherOrder = Array.Empty<string>();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -8,6 +8,12 @@ namespace MediaBrowser.Model.Configuration
 | 
			
		||||
{
 | 
			
		||||
    public class MetadataPluginSummary
 | 
			
		||||
    {
 | 
			
		||||
        public MetadataPluginSummary()
 | 
			
		||||
        {
 | 
			
		||||
            SupportedImageTypes = Array.Empty<ImageType>();
 | 
			
		||||
            Plugins = Array.Empty<MetadataPlugin>();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets the type of the item.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
@ -25,11 +31,5 @@ namespace MediaBrowser.Model.Configuration
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <value>The supported image types.</value>
 | 
			
		||||
        public ImageType[] SupportedImageTypes { get; set; }
 | 
			
		||||
 | 
			
		||||
        public MetadataPluginSummary()
 | 
			
		||||
        {
 | 
			
		||||
            SupportedImageTypes = Array.Empty<ImageType>();
 | 
			
		||||
            Plugins = Array.Empty<MetadataPlugin>();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										365
									
								
								MediaBrowser.Model/Configuration/TypeOptions.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										365
									
								
								MediaBrowser.Model/Configuration/TypeOptions.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,365 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
#pragma warning disable CS1591
 | 
			
		||||
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using MediaBrowser.Model.Entities;
 | 
			
		||||
 | 
			
		||||
namespace MediaBrowser.Model.Configuration
 | 
			
		||||
{
 | 
			
		||||
    public class TypeOptions
 | 
			
		||||
    {
 | 
			
		||||
        public static readonly ImageOption DefaultInstance = new ImageOption();
 | 
			
		||||
 | 
			
		||||
        public static readonly Dictionary<string, ImageOption[]> DefaultImageOptions = new Dictionary<string, ImageOption[]>
 | 
			
		||||
        {
 | 
			
		||||
            {
 | 
			
		||||
                "Movie", new[]
 | 
			
		||||
                {
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 1,
 | 
			
		||||
                        MinWidth = 1280,
 | 
			
		||||
                        Type = ImageType.Backdrop
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    // Don't download this by default as it's rarely used.
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 0,
 | 
			
		||||
                        Type = ImageType.Art
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    // Don't download this by default as it's rarely used.
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 0,
 | 
			
		||||
                        Type = ImageType.Disc
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 1,
 | 
			
		||||
                        Type = ImageType.Primary
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 0,
 | 
			
		||||
                        Type = ImageType.Banner
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 1,
 | 
			
		||||
                        Type = ImageType.Thumb
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 1,
 | 
			
		||||
                        Type = ImageType.Logo
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "MusicVideo", new[]
 | 
			
		||||
                {
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 1,
 | 
			
		||||
                        MinWidth = 1280,
 | 
			
		||||
                        Type = ImageType.Backdrop
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    // Don't download this by default as it's rarely used.
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 0,
 | 
			
		||||
                        Type = ImageType.Art
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    // Don't download this by default as it's rarely used.
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 0,
 | 
			
		||||
                        Type = ImageType.Disc
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 1,
 | 
			
		||||
                        Type = ImageType.Primary
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 0,
 | 
			
		||||
                        Type = ImageType.Banner
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 1,
 | 
			
		||||
                        Type = ImageType.Thumb
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 1,
 | 
			
		||||
                        Type = ImageType.Logo
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "Series", new[]
 | 
			
		||||
                {
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 1,
 | 
			
		||||
                        MinWidth = 1280,
 | 
			
		||||
                        Type = ImageType.Backdrop
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    // Don't download this by default as it's rarely used.
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 0,
 | 
			
		||||
                        Type = ImageType.Art
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 1,
 | 
			
		||||
                        Type = ImageType.Primary
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 1,
 | 
			
		||||
                        Type = ImageType.Banner
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 1,
 | 
			
		||||
                        Type = ImageType.Thumb
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 1,
 | 
			
		||||
                        Type = ImageType.Logo
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "MusicAlbum", new[]
 | 
			
		||||
                {
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 0,
 | 
			
		||||
                        MinWidth = 1280,
 | 
			
		||||
                        Type = ImageType.Backdrop
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    // Don't download this by default as it's rarely used.
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 0,
 | 
			
		||||
                        Type = ImageType.Disc
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "MusicArtist", new[]
 | 
			
		||||
                {
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 1,
 | 
			
		||||
                        MinWidth = 1280,
 | 
			
		||||
                        Type = ImageType.Backdrop
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    // Don't download this by default
 | 
			
		||||
                    // They do look great, but most artists won't have them, which means a banner view isn't really possible
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 0,
 | 
			
		||||
                        Type = ImageType.Banner
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    // Don't download this by default
 | 
			
		||||
                    // Generally not used
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 0,
 | 
			
		||||
                        Type = ImageType.Art
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 1,
 | 
			
		||||
                        Type = ImageType.Logo
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "BoxSet", new[]
 | 
			
		||||
                {
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 1,
 | 
			
		||||
                        MinWidth = 1280,
 | 
			
		||||
                        Type = ImageType.Backdrop
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 1,
 | 
			
		||||
                        Type = ImageType.Primary
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 1,
 | 
			
		||||
                        Type = ImageType.Thumb
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 1,
 | 
			
		||||
                        Type = ImageType.Logo
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    // Don't download this by default as it's rarely used.
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 0,
 | 
			
		||||
                        Type = ImageType.Art
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    // Don't download this by default as it's rarely used.
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 0,
 | 
			
		||||
                        Type = ImageType.Disc
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    // Don't download this by default as it's rarely used.
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 0,
 | 
			
		||||
                        Type = ImageType.Banner
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "Season", new[]
 | 
			
		||||
                {
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 0,
 | 
			
		||||
                        MinWidth = 1280,
 | 
			
		||||
                        Type = ImageType.Backdrop
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 1,
 | 
			
		||||
                        Type = ImageType.Primary
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 0,
 | 
			
		||||
                        Type = ImageType.Banner
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 0,
 | 
			
		||||
                        Type = ImageType.Thumb
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "Episode", new[]
 | 
			
		||||
                {
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 0,
 | 
			
		||||
                        MinWidth = 1280,
 | 
			
		||||
                        Type = ImageType.Backdrop
 | 
			
		||||
                    },
 | 
			
		||||
 | 
			
		||||
                    new ImageOption
 | 
			
		||||
                    {
 | 
			
		||||
                        Limit = 1,
 | 
			
		||||
                        Type = ImageType.Primary
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        public TypeOptions()
 | 
			
		||||
        {
 | 
			
		||||
            MetadataFetchers = Array.Empty<string>();
 | 
			
		||||
            MetadataFetcherOrder = Array.Empty<string>();
 | 
			
		||||
            ImageFetchers = Array.Empty<string>();
 | 
			
		||||
            ImageFetcherOrder = Array.Empty<string>();
 | 
			
		||||
            ImageOptions = Array.Empty<ImageOption>();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public string Type { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string[] MetadataFetchers { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string[] MetadataFetcherOrder { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string[] ImageFetchers { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string[] ImageFetcherOrder { get; set; }
 | 
			
		||||
 | 
			
		||||
        public ImageOption[] ImageOptions { get; set; }
 | 
			
		||||
 | 
			
		||||
        public ImageOption GetImageOptions(ImageType type)
 | 
			
		||||
        {
 | 
			
		||||
            foreach (var i in ImageOptions)
 | 
			
		||||
            {
 | 
			
		||||
                if (i.Type == type)
 | 
			
		||||
                {
 | 
			
		||||
                    return i;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (DefaultImageOptions.TryGetValue(Type, out ImageOption[] options))
 | 
			
		||||
            {
 | 
			
		||||
                foreach (var i in options)
 | 
			
		||||
                {
 | 
			
		||||
                    if (i.Type == type)
 | 
			
		||||
                    {
 | 
			
		||||
                        return i;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return DefaultInstance;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public int GetLimit(ImageType type)
 | 
			
		||||
        {
 | 
			
		||||
            return GetImageOptions(type).Limit;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public int GetMinWidth(ImageType type)
 | 
			
		||||
        {
 | 
			
		||||
            return GetImageOptions(type).MinWidth;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public bool IsEnabled(ImageType type)
 | 
			
		||||
        {
 | 
			
		||||
            return GetLimit(type) > 0;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -11,6 +11,24 @@ namespace MediaBrowser.Model.Configuration
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public class UserConfiguration
 | 
			
		||||
    {
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Initializes a new instance of the <see cref="UserConfiguration" /> class.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public UserConfiguration()
 | 
			
		||||
        {
 | 
			
		||||
            EnableNextEpisodeAutoPlay = true;
 | 
			
		||||
            RememberAudioSelections = true;
 | 
			
		||||
            RememberSubtitleSelections = true;
 | 
			
		||||
 | 
			
		||||
            HidePlayedInLatest = true;
 | 
			
		||||
            PlayDefaultAudioTrack = true;
 | 
			
		||||
 | 
			
		||||
            LatestItemsExcludes = Array.Empty<string>();
 | 
			
		||||
            OrderedViews = Array.Empty<string>();
 | 
			
		||||
            MyMediaExcludes = Array.Empty<string>();
 | 
			
		||||
            GroupedFolders = Array.Empty<string>();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets the audio language preference.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
@ -52,23 +70,5 @@ namespace MediaBrowser.Model.Configuration
 | 
			
		||||
        public bool RememberSubtitleSelections { get; set; }
 | 
			
		||||
 | 
			
		||||
        public bool EnableNextEpisodeAutoPlay { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Initializes a new instance of the <see cref="UserConfiguration" /> class.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public UserConfiguration()
 | 
			
		||||
        {
 | 
			
		||||
            EnableNextEpisodeAutoPlay = true;
 | 
			
		||||
            RememberAudioSelections = true;
 | 
			
		||||
            RememberSubtitleSelections = true;
 | 
			
		||||
 | 
			
		||||
            HidePlayedInLatest = true;
 | 
			
		||||
            PlayDefaultAudioTrack = true;
 | 
			
		||||
 | 
			
		||||
            LatestItemsExcludes = Array.Empty<string>();
 | 
			
		||||
            OrderedViews = Array.Empty<string>();
 | 
			
		||||
            MyMediaExcludes = Array.Empty<string>();
 | 
			
		||||
            GroupedFolders = Array.Empty<string>();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -5,6 +5,14 @@ namespace MediaBrowser.Model.Configuration
 | 
			
		||||
{
 | 
			
		||||
    public class XbmcMetadataOptions
 | 
			
		||||
    {
 | 
			
		||||
        public XbmcMetadataOptions()
 | 
			
		||||
        {
 | 
			
		||||
            ReleaseDateFormat = "yyyy-MM-dd";
 | 
			
		||||
 | 
			
		||||
            SaveImagePathsInNfo = true;
 | 
			
		||||
            EnablePathSubstitution = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public string UserId { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string ReleaseDateFormat { get; set; }
 | 
			
		||||
@ -14,13 +22,5 @@ namespace MediaBrowser.Model.Configuration
 | 
			
		||||
        public bool EnablePathSubstitution { get; set; }
 | 
			
		||||
 | 
			
		||||
        public bool EnableExtraThumbsDuplication { get; set; }
 | 
			
		||||
 | 
			
		||||
        public XbmcMetadataOptions()
 | 
			
		||||
        {
 | 
			
		||||
            ReleaseDateFormat = "yyyy-MM-dd";
 | 
			
		||||
 | 
			
		||||
            SaveImagePathsInNfo = true;
 | 
			
		||||
            EnablePathSubstitution = true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -34,20 +34,20 @@ namespace MediaBrowser.Model.Dlna
 | 
			
		||||
        public DeviceProfile Profile { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Optional. Only needed if a specific AudioStreamIndex or SubtitleStreamIndex are requested.
 | 
			
		||||
        /// Gets or sets a media source id. Optional. Only needed if a specific AudioStreamIndex or SubtitleStreamIndex are requested.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public string MediaSourceId { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string DeviceId { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Allows an override of supported number of audio channels
 | 
			
		||||
        /// Example: DeviceProfile supports five channel, but user only has stereo speakers
 | 
			
		||||
        /// Gets or sets an override of supported number of audio channels
 | 
			
		||||
        /// Example: DeviceProfile supports five channel, but user only has stereo speakers.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public int? MaxAudioChannels { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// The application's configured quality setting.
 | 
			
		||||
        /// Gets or sets the application's configured quality setting.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public int? MaxBitrate { get; set; }
 | 
			
		||||
 | 
			
		||||
@ -66,6 +66,7 @@ namespace MediaBrowser.Model.Dlna
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets the maximum bitrate.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="isAudio">Whether or not this is audio.</param>
 | 
			
		||||
        /// <returns>System.Nullable<System.Int32>.</returns>
 | 
			
		||||
        public int? GetMaxBitrate(bool isAudio)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
@ -9,6 +9,12 @@ namespace MediaBrowser.Model.Dlna
 | 
			
		||||
{
 | 
			
		||||
    public class CodecProfile
 | 
			
		||||
    {
 | 
			
		||||
        public CodecProfile()
 | 
			
		||||
        {
 | 
			
		||||
            Conditions = Array.Empty<ProfileCondition>();
 | 
			
		||||
            ApplyConditions = Array.Empty<ProfileCondition>();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [XmlAttribute("type")]
 | 
			
		||||
        public CodecType Type { get; set; }
 | 
			
		||||
 | 
			
		||||
@ -22,12 +28,6 @@ namespace MediaBrowser.Model.Dlna
 | 
			
		||||
        [XmlAttribute("container")]
 | 
			
		||||
        public string Container { get; set; }
 | 
			
		||||
 | 
			
		||||
        public CodecProfile()
 | 
			
		||||
        {
 | 
			
		||||
            Conditions = Array.Empty<ProfileCondition>();
 | 
			
		||||
            ApplyConditions = Array.Empty<ProfileCondition>();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public string[] GetCodecs()
 | 
			
		||||
        {
 | 
			
		||||
            return ContainerProfile.SplitValue(Codec);
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,8 @@
 | 
			
		||||
#pragma warning disable CS1591
 | 
			
		||||
 | 
			
		||||
using System;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Globalization;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using MediaBrowser.Model.MediaInfo;
 | 
			
		||||
 | 
			
		||||
namespace MediaBrowser.Model.Dlna
 | 
			
		||||
 | 
			
		||||
@ -9,6 +9,11 @@ namespace MediaBrowser.Model.Dlna
 | 
			
		||||
{
 | 
			
		||||
    public class ContainerProfile
 | 
			
		||||
    {
 | 
			
		||||
        public ContainerProfile()
 | 
			
		||||
        {
 | 
			
		||||
            Conditions = Array.Empty<ProfileCondition>();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [XmlAttribute("type")]
 | 
			
		||||
        public DlnaProfileType Type { get; set; }
 | 
			
		||||
 | 
			
		||||
@ -17,11 +22,6 @@ namespace MediaBrowser.Model.Dlna
 | 
			
		||||
        [XmlAttribute("container")]
 | 
			
		||||
        public string Container { get; set; }
 | 
			
		||||
 | 
			
		||||
        public ContainerProfile()
 | 
			
		||||
        {
 | 
			
		||||
            Conditions = Array.Empty<ProfileCondition>();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public string[] GetContainers()
 | 
			
		||||
        {
 | 
			
		||||
            return SplitValue(Container);
 | 
			
		||||
 | 
			
		||||
@ -81,13 +81,13 @@ namespace MediaBrowser.Model.Dlna
 | 
			
		||||
                            DlnaFlags.DlnaV15;
 | 
			
		||||
 | 
			
		||||
            // if (isDirectStream)
 | 
			
		||||
            //{
 | 
			
		||||
            // {
 | 
			
		||||
            //     flagValue = flagValue | DlnaFlags.ByteBasedSeek;
 | 
			
		||||
            //}
 | 
			
		||||
            // }
 | 
			
		||||
            //  else if (runtimeTicks.HasValue)
 | 
			
		||||
            //{
 | 
			
		||||
            // {
 | 
			
		||||
            //     flagValue = flagValue | DlnaFlags.TimeBasedSeek;
 | 
			
		||||
            //}
 | 
			
		||||
            // }
 | 
			
		||||
 | 
			
		||||
            string dlnaflags = string.Format(
 | 
			
		||||
                CultureInfo.InvariantCulture,
 | 
			
		||||
@ -150,15 +150,17 @@ namespace MediaBrowser.Model.Dlna
 | 
			
		||||
                            DlnaFlags.DlnaV15;
 | 
			
		||||
 | 
			
		||||
            // if (isDirectStream)
 | 
			
		||||
            //{
 | 
			
		||||
            // {
 | 
			
		||||
            //     flagValue = flagValue | DlnaFlags.ByteBasedSeek;
 | 
			
		||||
            //}
 | 
			
		||||
            // }
 | 
			
		||||
            //  else if (runtimeTicks.HasValue)
 | 
			
		||||
            //{
 | 
			
		||||
            // {
 | 
			
		||||
            //     flagValue = flagValue | DlnaFlags.TimeBasedSeek;
 | 
			
		||||
            //}
 | 
			
		||||
            // }
 | 
			
		||||
 | 
			
		||||
            string dlnaflags = string.Format(CultureInfo.InvariantCulture, ";DLNA.ORG_FLAGS={0}",
 | 
			
		||||
            string dlnaflags = string.Format(
 | 
			
		||||
                CultureInfo.InvariantCulture,
 | 
			
		||||
                ";DLNA.ORG_FLAGS={0}",
 | 
			
		||||
                DlnaMaps.FlagsToString(flagValue));
 | 
			
		||||
 | 
			
		||||
            ResponseProfile mediaProfile = _profile.GetVideoMediaProfile(
 | 
			
		||||
 | 
			
		||||
@ -8,6 +8,7 @@ namespace MediaBrowser.Model.Dlna
 | 
			
		||||
    public interface IDeviceDiscovery
 | 
			
		||||
    {
 | 
			
		||||
        event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceDiscovered;
 | 
			
		||||
 | 
			
		||||
        event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceLeft;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -57,7 +57,6 @@ namespace MediaBrowser.Model.Dlna
 | 
			
		||||
                string.Equals(container, "mpegts", StringComparison.OrdinalIgnoreCase) ||
 | 
			
		||||
                string.Equals(container, "m2ts", StringComparison.OrdinalIgnoreCase))
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
                return ResolveVideoMPEG2TSFormat(videoCodec, audioCodec, width, height, timestampType);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@ -323,7 +322,6 @@ namespace MediaBrowser.Model.Dlna
 | 
			
		||||
            if (string.Equals(videoCodec, "wmv", StringComparison.OrdinalIgnoreCase) &&
 | 
			
		||||
                (string.IsNullOrEmpty(audioCodec) || string.Equals(audioCodec, "wma", StringComparison.OrdinalIgnoreCase) || string.Equals(videoCodec, "wmapro", StringComparison.OrdinalIgnoreCase)))
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
                if (width.HasValue && height.HasValue)
 | 
			
		||||
                {
 | 
			
		||||
                    if ((width.Value <= 720) && (height.Value <= 576))
 | 
			
		||||
@ -479,7 +477,9 @@ namespace MediaBrowser.Model.Dlna
 | 
			
		||||
        {
 | 
			
		||||
            if (string.Equals(container, "jpeg", StringComparison.OrdinalIgnoreCase) ||
 | 
			
		||||
                string.Equals(container, "jpg", StringComparison.OrdinalIgnoreCase))
 | 
			
		||||
            {
 | 
			
		||||
                return ResolveImageJPGFormat(width, height);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (string.Equals(container, "png", StringComparison.OrdinalIgnoreCase))
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
@ -4,14 +4,14 @@ namespace MediaBrowser.Model.Dlna
 | 
			
		||||
{
 | 
			
		||||
    public class ResolutionConfiguration
 | 
			
		||||
    {
 | 
			
		||||
        public int MaxWidth { get; set; }
 | 
			
		||||
 | 
			
		||||
        public int MaxBitrate { get; set; }
 | 
			
		||||
 | 
			
		||||
        public ResolutionConfiguration(int maxWidth, int maxBitrate)
 | 
			
		||||
        {
 | 
			
		||||
            MaxWidth = maxWidth;
 | 
			
		||||
            MaxBitrate = maxBitrate;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public int MaxWidth { get; set; }
 | 
			
		||||
 | 
			
		||||
        public int MaxBitrate { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -8,6 +8,11 @@ namespace MediaBrowser.Model.Dlna
 | 
			
		||||
{
 | 
			
		||||
    public class ResponseProfile
 | 
			
		||||
    {
 | 
			
		||||
        public ResponseProfile()
 | 
			
		||||
        {
 | 
			
		||||
            Conditions = Array.Empty<ProfileCondition>();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [XmlAttribute("container")]
 | 
			
		||||
        public string Container { get; set; }
 | 
			
		||||
 | 
			
		||||
@ -28,11 +33,6 @@ namespace MediaBrowser.Model.Dlna
 | 
			
		||||
 | 
			
		||||
        public ProfileCondition[] Conditions { get; set; }
 | 
			
		||||
 | 
			
		||||
        public ResponseProfile()
 | 
			
		||||
        {
 | 
			
		||||
            Conditions = Array.Empty<ProfileCondition>();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public string[] GetContainers()
 | 
			
		||||
        {
 | 
			
		||||
            return ContainerProfile.SplitValue(Container);
 | 
			
		||||
 | 
			
		||||
@ -7,6 +7,46 @@ namespace MediaBrowser.Model.Dlna
 | 
			
		||||
{
 | 
			
		||||
    public class SearchCriteria
 | 
			
		||||
    {
 | 
			
		||||
        public SearchCriteria(string search)
 | 
			
		||||
        {
 | 
			
		||||
            if (search.Length == 0)
 | 
			
		||||
            {
 | 
			
		||||
                throw new ArgumentException("String can't be empty.", nameof(search));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            SearchType = SearchType.Unknown;
 | 
			
		||||
 | 
			
		||||
            string[] factors = RegexSplit(search, "(and|or)");
 | 
			
		||||
            foreach (string factor in factors)
 | 
			
		||||
            {
 | 
			
		||||
                string[] subFactors = RegexSplit(factor.Trim().Trim('(').Trim(')').Trim(), "\\s", 3);
 | 
			
		||||
 | 
			
		||||
                if (subFactors.Length == 3)
 | 
			
		||||
                {
 | 
			
		||||
                    if (string.Equals("upnp:class", subFactors[0], StringComparison.OrdinalIgnoreCase)
 | 
			
		||||
                        && (string.Equals("=", subFactors[1], StringComparison.Ordinal) || string.Equals("derivedfrom", subFactors[1], StringComparison.OrdinalIgnoreCase)))
 | 
			
		||||
                    {
 | 
			
		||||
                        if (string.Equals("\"object.item.imageItem\"", subFactors[2], StringComparison.Ordinal) || string.Equals("\"object.item.imageItem.photo\"", subFactors[2], StringComparison.OrdinalIgnoreCase))
 | 
			
		||||
                        {
 | 
			
		||||
                            SearchType = SearchType.Image;
 | 
			
		||||
                        }
 | 
			
		||||
                        else if (string.Equals("\"object.item.videoItem\"", subFactors[2], StringComparison.OrdinalIgnoreCase))
 | 
			
		||||
                        {
 | 
			
		||||
                            SearchType = SearchType.Video;
 | 
			
		||||
                        }
 | 
			
		||||
                        else if (string.Equals("\"object.container.playlistContainer\"", subFactors[2], StringComparison.OrdinalIgnoreCase))
 | 
			
		||||
                        {
 | 
			
		||||
                            SearchType = SearchType.Playlist;
 | 
			
		||||
                        }
 | 
			
		||||
                        else if (string.Equals("\"object.container.album.musicAlbum\"", subFactors[2], StringComparison.OrdinalIgnoreCase))
 | 
			
		||||
                        {
 | 
			
		||||
                            SearchType = SearchType.MusicAlbum;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public SearchType SearchType { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
@ -31,45 +71,5 @@ namespace MediaBrowser.Model.Dlna
 | 
			
		||||
        {
 | 
			
		||||
            return Regex.Split(str, term, RegexOptions.IgnoreCase);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public SearchCriteria(string search)
 | 
			
		||||
        {
 | 
			
		||||
            if (search.Length == 0)
 | 
			
		||||
            {
 | 
			
		||||
                throw new ArgumentException("String can't be empty.", nameof(search));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            SearchType = SearchType.Unknown;
 | 
			
		||||
 | 
			
		||||
            string[] factors = RegexSplit(search, "(and|or)");
 | 
			
		||||
            foreach (string factor in factors)
 | 
			
		||||
            {
 | 
			
		||||
                string[] subFactors = RegexSplit(factor.Trim().Trim('(').Trim(')').Trim(), "\\s", 3);
 | 
			
		||||
 | 
			
		||||
                if (subFactors.Length == 3)
 | 
			
		||||
                {
 | 
			
		||||
                    if (string.Equals("upnp:class", subFactors[0], StringComparison.OrdinalIgnoreCase) &&
 | 
			
		||||
                        (string.Equals("=", subFactors[1], StringComparison.Ordinal) || string.Equals("derivedfrom", subFactors[1], StringComparison.OrdinalIgnoreCase)))
 | 
			
		||||
                    {
 | 
			
		||||
                        if (string.Equals("\"object.item.imageItem\"", subFactors[2], StringComparison.Ordinal) || string.Equals("\"object.item.imageItem.photo\"", subFactors[2], StringComparison.OrdinalIgnoreCase))
 | 
			
		||||
                        {
 | 
			
		||||
                            SearchType = SearchType.Image;
 | 
			
		||||
                        }
 | 
			
		||||
                        else if (string.Equals("\"object.item.videoItem\"", subFactors[2], StringComparison.OrdinalIgnoreCase))
 | 
			
		||||
                        {
 | 
			
		||||
                            SearchType = SearchType.Video;
 | 
			
		||||
                        }
 | 
			
		||||
                        else if (string.Equals("\"object.container.playlistContainer\"", subFactors[2], StringComparison.OrdinalIgnoreCase))
 | 
			
		||||
                        {
 | 
			
		||||
                            SearchType = SearchType.Playlist;
 | 
			
		||||
                        }
 | 
			
		||||
                        else if (string.Equals("\"object.container.album.musicAlbum\"", subFactors[2], StringComparison.OrdinalIgnoreCase))
 | 
			
		||||
                        {
 | 
			
		||||
                            SearchType = SearchType.MusicAlbum;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -6,10 +6,10 @@ namespace MediaBrowser.Model.Dlna
 | 
			
		||||
{
 | 
			
		||||
    public class SortCriteria
 | 
			
		||||
    {
 | 
			
		||||
        public SortOrder SortOrder => SortOrder.Ascending;
 | 
			
		||||
 | 
			
		||||
        public SortCriteria(string value)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public SortOrder SortOrder => SortOrder.Ascending;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -227,7 +227,7 @@ namespace MediaBrowser.Model.Dlna
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static string NormalizeMediaSourceFormatIntoSingleContainer(string inputContainer, string _, DeviceProfile profile, DlnaProfileType type)
 | 
			
		||||
        public static string NormalizeMediaSourceFormatIntoSingleContainer(string inputContainer, DeviceProfile profile, DlnaProfileType type)
 | 
			
		||||
        {
 | 
			
		||||
            if (string.IsNullOrEmpty(inputContainer))
 | 
			
		||||
            {
 | 
			
		||||
@ -274,14 +274,14 @@ namespace MediaBrowser.Model.Dlna
 | 
			
		||||
            if (options.ForceDirectPlay)
 | 
			
		||||
            {
 | 
			
		||||
                playlistItem.PlayMethod = PlayMethod.DirectPlay;
 | 
			
		||||
                playlistItem.Container = NormalizeMediaSourceFormatIntoSingleContainer(item.Container, item.Path, options.Profile, DlnaProfileType.Audio);
 | 
			
		||||
                playlistItem.Container = NormalizeMediaSourceFormatIntoSingleContainer(item.Container, options.Profile, DlnaProfileType.Audio);
 | 
			
		||||
                return playlistItem;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (options.ForceDirectStream)
 | 
			
		||||
            {
 | 
			
		||||
                playlistItem.PlayMethod = PlayMethod.DirectStream;
 | 
			
		||||
                playlistItem.Container = NormalizeMediaSourceFormatIntoSingleContainer(item.Container, item.Path, options.Profile, DlnaProfileType.Audio);
 | 
			
		||||
                playlistItem.Container = NormalizeMediaSourceFormatIntoSingleContainer(item.Container, options.Profile, DlnaProfileType.Audio);
 | 
			
		||||
                return playlistItem;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@ -349,7 +349,7 @@ namespace MediaBrowser.Model.Dlna
 | 
			
		||||
                        playlistItem.PlayMethod = PlayMethod.DirectStream;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    playlistItem.Container = NormalizeMediaSourceFormatIntoSingleContainer(item.Container, item.Path, options.Profile, DlnaProfileType.Audio);
 | 
			
		||||
                    playlistItem.Container = NormalizeMediaSourceFormatIntoSingleContainer(item.Container, options.Profile, DlnaProfileType.Audio);
 | 
			
		||||
 | 
			
		||||
                    return playlistItem;
 | 
			
		||||
                }
 | 
			
		||||
@ -698,7 +698,7 @@ namespace MediaBrowser.Model.Dlna
 | 
			
		||||
                if (directPlay != null)
 | 
			
		||||
                {
 | 
			
		||||
                    playlistItem.PlayMethod = directPlay.Value;
 | 
			
		||||
                    playlistItem.Container = NormalizeMediaSourceFormatIntoSingleContainer(item.Container, item.Path, options.Profile, DlnaProfileType.Video);
 | 
			
		||||
                    playlistItem.Container = NormalizeMediaSourceFormatIntoSingleContainer(item.Container, options.Profile, DlnaProfileType.Video);
 | 
			
		||||
 | 
			
		||||
                    if (subtitleStream != null)
 | 
			
		||||
                    {
 | 
			
		||||
@ -1404,7 +1404,9 @@ namespace MediaBrowser.Model.Dlna
 | 
			
		||||
            {
 | 
			
		||||
                _logger.LogInformation(
 | 
			
		||||
                    "Bitrate exceeds {PlayBackMethod} limit: media bitrate: {MediaBitrate}, max bitrate: {MaxBitrate}",
 | 
			
		||||
                    playMethod, itemBitrate, requestedMaxBitrate);
 | 
			
		||||
                    playMethod,
 | 
			
		||||
                    itemBitrate,
 | 
			
		||||
                    requestedMaxBitrate);
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -294,13 +294,13 @@ namespace MediaBrowser.Model.Dto
 | 
			
		||||
        public NameGuidPair[] GenreItems { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// If the item does not have a logo, this will hold the Id of the Parent that has one.
 | 
			
		||||
        /// Gets or sets wether the item has a logo, this will hold the Id of the Parent that has one.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <value>The parent logo item id.</value>
 | 
			
		||||
        public string ParentLogoItemId { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// If the item does not have any backdrops, this will hold the Id of the Parent that has one.
 | 
			
		||||
        /// Gets or sets wether the item has any backdrops, this will hold the Id of the Parent that has one.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <value>The parent backdrop item id.</value>
 | 
			
		||||
        public string ParentBackdropItemId { get; set; }
 | 
			
		||||
@ -318,7 +318,7 @@ namespace MediaBrowser.Model.Dto
 | 
			
		||||
        public int? LocalTrailerCount { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// User data for this item based on the user it's being requested for.
 | 
			
		||||
        /// Gets or sets the user data for this item based on the user it's being requested for.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <value>The user data.</value>
 | 
			
		||||
        public UserItemDataDto UserData { get; set; }
 | 
			
		||||
@ -506,7 +506,7 @@ namespace MediaBrowser.Model.Dto
 | 
			
		||||
        public string ParentLogoImageTag { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// If the item does not have a art, this will hold the Id of the Parent that has one.
 | 
			
		||||
        /// Gets or sets wether the item has fan art, this will hold the Id of the Parent that has one.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <value>The parent art item id.</value>
 | 
			
		||||
        public string ParentArtItemId { get; set; }
 | 
			
		||||
@ -695,7 +695,7 @@ namespace MediaBrowser.Model.Dto
 | 
			
		||||
        public string ChannelPrimaryImageTag { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// The start date of the recording, in UTC.
 | 
			
		||||
        /// Gets or sets the start date of the recording, in UTC.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public DateTime? StartDate { get; set; }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -12,6 +12,18 @@ namespace MediaBrowser.Model.Dto
 | 
			
		||||
{
 | 
			
		||||
    public class MediaSourceInfo
 | 
			
		||||
    {
 | 
			
		||||
        public MediaSourceInfo()
 | 
			
		||||
        {
 | 
			
		||||
            Formats = Array.Empty<string>();
 | 
			
		||||
            MediaStreams = new List<MediaStream>();
 | 
			
		||||
            MediaAttachments = Array.Empty<MediaAttachment>();
 | 
			
		||||
            RequiredHttpHeaders = new Dictionary<string, string>();
 | 
			
		||||
            SupportsTranscoding = true;
 | 
			
		||||
            SupportsDirectStream = true;
 | 
			
		||||
            SupportsDirectPlay = true;
 | 
			
		||||
            SupportsProbing = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public MediaProtocol Protocol { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string Id { get; set; }
 | 
			
		||||
@ -31,6 +43,7 @@ namespace MediaBrowser.Model.Dto
 | 
			
		||||
        public string Name { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets a value indicating whether the media is remote.
 | 
			
		||||
        /// Differentiate internet url vs local network.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public bool IsRemote { get; set; }
 | 
			
		||||
@ -95,16 +108,28 @@ namespace MediaBrowser.Model.Dto
 | 
			
		||||
 | 
			
		||||
        public int? AnalyzeDurationMs { get; set; }
 | 
			
		||||
 | 
			
		||||
        public MediaSourceInfo()
 | 
			
		||||
        [JsonIgnore]
 | 
			
		||||
        public TranscodeReason[] TranscodeReasons { get; set; }
 | 
			
		||||
 | 
			
		||||
        public int? DefaultAudioStreamIndex { get; set; }
 | 
			
		||||
 | 
			
		||||
        public int? DefaultSubtitleStreamIndex { get; set; }
 | 
			
		||||
 | 
			
		||||
        [JsonIgnore]
 | 
			
		||||
        public MediaStream VideoStream
 | 
			
		||||
        {
 | 
			
		||||
            Formats = Array.Empty<string>();
 | 
			
		||||
            MediaStreams = new List<MediaStream>();
 | 
			
		||||
            MediaAttachments = Array.Empty<MediaAttachment>();
 | 
			
		||||
            RequiredHttpHeaders = new Dictionary<string, string>();
 | 
			
		||||
            SupportsTranscoding = true;
 | 
			
		||||
            SupportsDirectStream = true;
 | 
			
		||||
            SupportsDirectPlay = true;
 | 
			
		||||
            SupportsProbing = true;
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                foreach (var i in MediaStreams)
 | 
			
		||||
                {
 | 
			
		||||
                    if (i.Type == MediaStreamType.Video)
 | 
			
		||||
                    {
 | 
			
		||||
                        return i;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return null;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void InferTotalBitrate(bool force = false)
 | 
			
		||||
@ -134,13 +159,6 @@ namespace MediaBrowser.Model.Dto
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [JsonIgnore]
 | 
			
		||||
        public TranscodeReason[] TranscodeReasons { get; set; }
 | 
			
		||||
 | 
			
		||||
        public int? DefaultAudioStreamIndex { get; set; }
 | 
			
		||||
 | 
			
		||||
        public int? DefaultSubtitleStreamIndex { get; set; }
 | 
			
		||||
 | 
			
		||||
        public MediaStream GetDefaultAudioStream(int? defaultIndex)
 | 
			
		||||
        {
 | 
			
		||||
            if (defaultIndex.HasValue)
 | 
			
		||||
@ -175,23 +193,6 @@ namespace MediaBrowser.Model.Dto
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [JsonIgnore]
 | 
			
		||||
        public MediaStream VideoStream
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                foreach (var i in MediaStreams)
 | 
			
		||||
                {
 | 
			
		||||
                    if (i.Type == MediaStreamType.Video)
 | 
			
		||||
                    {
 | 
			
		||||
                        return i;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return null;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public MediaStream GetMediaStream(MediaStreamType type, int index)
 | 
			
		||||
        {
 | 
			
		||||
            foreach (var i in MediaStreams)
 | 
			
		||||
 | 
			
		||||
@ -10,6 +10,15 @@ namespace MediaBrowser.Model.Dto
 | 
			
		||||
{
 | 
			
		||||
    public class MetadataEditorInfo
 | 
			
		||||
    {
 | 
			
		||||
        public MetadataEditorInfo()
 | 
			
		||||
        {
 | 
			
		||||
            ParentalRatingOptions = Array.Empty<ParentalRating>();
 | 
			
		||||
            Countries = Array.Empty<CountryInfo>();
 | 
			
		||||
            Cultures = Array.Empty<CultureDto>();
 | 
			
		||||
            ExternalIdInfos = Array.Empty<ExternalIdInfo>();
 | 
			
		||||
            ContentTypeOptions = Array.Empty<NameValuePair>();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public ParentalRating[] ParentalRatingOptions { get; set; }
 | 
			
		||||
 | 
			
		||||
        public CountryInfo[] Countries { get; set; }
 | 
			
		||||
@ -21,14 +30,5 @@ namespace MediaBrowser.Model.Dto
 | 
			
		||||
        public string ContentType { get; set; }
 | 
			
		||||
 | 
			
		||||
        public NameValuePair[] ContentTypeOptions { get; set; }
 | 
			
		||||
 | 
			
		||||
        public MetadataEditorInfo()
 | 
			
		||||
        {
 | 
			
		||||
            ParentalRatingOptions = Array.Empty<ParentalRating>();
 | 
			
		||||
            Countries = Array.Empty<CountryInfo>();
 | 
			
		||||
            Cultures = Array.Empty<CultureDto>();
 | 
			
		||||
            ExternalIdInfos = Array.Empty<ExternalIdInfo>();
 | 
			
		||||
            ContentTypeOptions = Array.Empty<NameValuePair>();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										14
									
								
								MediaBrowser.Model/Dto/NameGuidPair.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								MediaBrowser.Model/Dto/NameGuidPair.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,14 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
#pragma warning disable CS1591
 | 
			
		||||
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
namespace MediaBrowser.Model.Dto
 | 
			
		||||
{
 | 
			
		||||
    public class NameGuidPair
 | 
			
		||||
    {
 | 
			
		||||
        public string Name { get; set; }
 | 
			
		||||
 | 
			
		||||
        public Guid Id { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -19,11 +19,4 @@ namespace MediaBrowser.Model.Dto
 | 
			
		||||
        /// <value>The identifier.</value>
 | 
			
		||||
        public string Id { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public class NameGuidPair
 | 
			
		||||
    {
 | 
			
		||||
        public string Name { get; set; }
 | 
			
		||||
 | 
			
		||||
        public Guid Id { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -10,6 +10,15 @@ namespace MediaBrowser.Model.Dto
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public class UserDto : IItemDto, IHasServerId
 | 
			
		||||
    {
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Initializes a new instance of the <see cref="UserDto"/> class.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public UserDto()
 | 
			
		||||
        {
 | 
			
		||||
            Configuration = new UserConfiguration();
 | 
			
		||||
            Policy = new UserPolicy();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets the name.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
@ -94,15 +103,6 @@ namespace MediaBrowser.Model.Dto
 | 
			
		||||
        /// <value>The primary image aspect ratio.</value>
 | 
			
		||||
        public double? PrimaryImageAspectRatio { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Initializes a new instance of the <see cref="UserDto"/> class.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public UserDto()
 | 
			
		||||
        {
 | 
			
		||||
            Configuration = new UserConfiguration();
 | 
			
		||||
            Policy = new UserPolicy();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc />
 | 
			
		||||
        public override string ToString()
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
@ -24,36 +24,4 @@ namespace MediaBrowser.Model.Entities
 | 
			
		||||
        public const string Playlists = "playlists";
 | 
			
		||||
        public const string Folders = "folders";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static class SpecialFolder
 | 
			
		||||
    {
 | 
			
		||||
        public const string TvShowSeries = "TvShowSeries";
 | 
			
		||||
        public const string TvGenres = "TvGenres";
 | 
			
		||||
        public const string TvGenre = "TvGenre";
 | 
			
		||||
        public const string TvLatest = "TvLatest";
 | 
			
		||||
        public const string TvNextUp = "TvNextUp";
 | 
			
		||||
        public const string TvResume = "TvResume";
 | 
			
		||||
        public const string TvFavoriteSeries = "TvFavoriteSeries";
 | 
			
		||||
        public const string TvFavoriteEpisodes = "TvFavoriteEpisodes";
 | 
			
		||||
 | 
			
		||||
        public const string MovieLatest = "MovieLatest";
 | 
			
		||||
        public const string MovieResume = "MovieResume";
 | 
			
		||||
        public const string MovieMovies = "MovieMovies";
 | 
			
		||||
        public const string MovieCollections = "MovieCollections";
 | 
			
		||||
        public const string MovieFavorites = "MovieFavorites";
 | 
			
		||||
        public const string MovieGenres = "MovieGenres";
 | 
			
		||||
        public const string MovieGenre = "MovieGenre";
 | 
			
		||||
 | 
			
		||||
        public const string MusicArtists = "MusicArtists";
 | 
			
		||||
        public const string MusicAlbumArtists = "MusicAlbumArtists";
 | 
			
		||||
        public const string MusicAlbums = "MusicAlbums";
 | 
			
		||||
        public const string MusicGenres = "MusicGenres";
 | 
			
		||||
        public const string MusicLatest = "MusicLatest";
 | 
			
		||||
        public const string MusicPlaylists = "MusicPlaylists";
 | 
			
		||||
        public const string MusicSongs = "MusicSongs";
 | 
			
		||||
        public const string MusicFavorites = "MusicFavorites";
 | 
			
		||||
        public const string MusicFavoriteArtists = "MusicFavoriteArtists";
 | 
			
		||||
        public const string MusicFavoriteAlbums = "MusicFavoriteAlbums";
 | 
			
		||||
        public const string MusicFavoriteSongs = "MusicFavoriteSongs";
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -84,7 +84,7 @@ namespace MediaBrowser.Model.Entities
 | 
			
		||||
        public string Title { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets the video range.
 | 
			
		||||
        /// Gets the video range.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <value>The video range.</value>
 | 
			
		||||
        public string VideoRange
 | 
			
		||||
@ -108,11 +108,11 @@ namespace MediaBrowser.Model.Entities
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public string localizedUndefined { get; set; }
 | 
			
		||||
        public string LocalizedUndefined { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string localizedDefault { get; set; }
 | 
			
		||||
        public string LocalizedDefault { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string localizedForced { get; set; }
 | 
			
		||||
        public string LocalizedForced { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string DisplayTitle
 | 
			
		||||
        {
 | 
			
		||||
@ -154,7 +154,7 @@ namespace MediaBrowser.Model.Entities
 | 
			
		||||
 | 
			
		||||
                        if (IsDefault)
 | 
			
		||||
                        {
 | 
			
		||||
                            attributes.Add(string.IsNullOrEmpty(localizedDefault) ? "Default" : localizedDefault);
 | 
			
		||||
                            attributes.Add(string.IsNullOrEmpty(LocalizedDefault) ? "Default" : LocalizedDefault);
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        if (!string.IsNullOrEmpty(Title))
 | 
			
		||||
@ -229,17 +229,17 @@ namespace MediaBrowser.Model.Entities
 | 
			
		||||
                        }
 | 
			
		||||
                        else
 | 
			
		||||
                        {
 | 
			
		||||
                            attributes.Add(string.IsNullOrEmpty(localizedUndefined) ? "Und" : localizedUndefined);
 | 
			
		||||
                            attributes.Add(string.IsNullOrEmpty(LocalizedUndefined) ? "Und" : LocalizedUndefined);
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        if (IsDefault)
 | 
			
		||||
                        {
 | 
			
		||||
                            attributes.Add(string.IsNullOrEmpty(localizedDefault) ? "Default" : localizedDefault);
 | 
			
		||||
                            attributes.Add(string.IsNullOrEmpty(LocalizedDefault) ? "Default" : LocalizedDefault);
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        if (IsForced)
 | 
			
		||||
                        {
 | 
			
		||||
                            attributes.Add(string.IsNullOrEmpty(localizedForced) ? "Forced" : localizedForced);
 | 
			
		||||
                            attributes.Add(string.IsNullOrEmpty(LocalizedForced) ? "Forced" : LocalizedForced);
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        if (!string.IsNullOrEmpty(Title))
 | 
			
		||||
@ -266,67 +266,6 @@ namespace MediaBrowser.Model.Entities
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private string GetResolutionText()
 | 
			
		||||
        {
 | 
			
		||||
            var i = this;
 | 
			
		||||
 | 
			
		||||
            if (i.Width.HasValue && i.Height.HasValue)
 | 
			
		||||
            {
 | 
			
		||||
                var width = i.Width.Value;
 | 
			
		||||
                var height = i.Height.Value;
 | 
			
		||||
 | 
			
		||||
                if (width >= 3800 || height >= 2000)
 | 
			
		||||
                {
 | 
			
		||||
                    return "4K";
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (width >= 2500)
 | 
			
		||||
                {
 | 
			
		||||
                    if (i.IsInterlaced)
 | 
			
		||||
                    {
 | 
			
		||||
                        return "1440i";
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    return "1440p";
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (width >= 1900 || height >= 1000)
 | 
			
		||||
                {
 | 
			
		||||
                    if (i.IsInterlaced)
 | 
			
		||||
                    {
 | 
			
		||||
                        return "1080i";
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    return "1080p";
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (width >= 1260 || height >= 700)
 | 
			
		||||
                {
 | 
			
		||||
                    if (i.IsInterlaced)
 | 
			
		||||
                    {
 | 
			
		||||
                        return "720i";
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    return "720p";
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (width >= 700 || height >= 440)
 | 
			
		||||
                {
 | 
			
		||||
 | 
			
		||||
                    if (i.IsInterlaced)
 | 
			
		||||
                    {
 | 
			
		||||
                        return "480i";
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    return "480p";
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return "SD";
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public string NalLengthSize { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
@ -487,6 +426,96 @@ namespace MediaBrowser.Model.Entities
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets a value indicating whether [supports external stream].
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <value><c>true</c> if [supports external stream]; otherwise, <c>false</c>.</value>
 | 
			
		||||
        public bool SupportsExternalStream { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets the filename.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <value>The filename.</value>
 | 
			
		||||
        public string Path { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets the pixel format.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <value>The pixel format.</value>
 | 
			
		||||
        public string PixelFormat { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets the level.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <value>The level.</value>
 | 
			
		||||
        public double? Level { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets whether this instance is anamorphic.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <value><c>true</c> if this instance is anamorphic; otherwise, <c>false</c>.</value>
 | 
			
		||||
        public bool? IsAnamorphic { get; set; }
 | 
			
		||||
 | 
			
		||||
        private string GetResolutionText()
 | 
			
		||||
        {
 | 
			
		||||
            var i = this;
 | 
			
		||||
 | 
			
		||||
            if (i.Width.HasValue && i.Height.HasValue)
 | 
			
		||||
            {
 | 
			
		||||
                var width = i.Width.Value;
 | 
			
		||||
                var height = i.Height.Value;
 | 
			
		||||
 | 
			
		||||
                if (width >= 3800 || height >= 2000)
 | 
			
		||||
                {
 | 
			
		||||
                    return "4K";
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (width >= 2500)
 | 
			
		||||
                {
 | 
			
		||||
                    if (i.IsInterlaced)
 | 
			
		||||
                    {
 | 
			
		||||
                        return "1440i";
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    return "1440p";
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (width >= 1900 || height >= 1000)
 | 
			
		||||
                {
 | 
			
		||||
                    if (i.IsInterlaced)
 | 
			
		||||
                    {
 | 
			
		||||
                        return "1080i";
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    return "1080p";
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (width >= 1260 || height >= 700)
 | 
			
		||||
                {
 | 
			
		||||
                    if (i.IsInterlaced)
 | 
			
		||||
                    {
 | 
			
		||||
                        return "720i";
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    return "720p";
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (width >= 700 || height >= 440)
 | 
			
		||||
                {
 | 
			
		||||
                    if (i.IsInterlaced)
 | 
			
		||||
                    {
 | 
			
		||||
                        return "480i";
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    return "480p";
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return "SD";
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static bool IsTextFormat(string format)
 | 
			
		||||
        {
 | 
			
		||||
            string codec = format ?? string.Empty;
 | 
			
		||||
@ -533,35 +562,5 @@ namespace MediaBrowser.Model.Entities
 | 
			
		||||
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets a value indicating whether [supports external stream].
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <value><c>true</c> if [supports external stream]; otherwise, <c>false</c>.</value>
 | 
			
		||||
        public bool SupportsExternalStream { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets the filename.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <value>The filename.</value>
 | 
			
		||||
        public string Path { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets the pixel format.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <value>The pixel format.</value>
 | 
			
		||||
        public string PixelFormat { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets the level.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <value>The level.</value>
 | 
			
		||||
        public double? Level { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets a value indicating whether this instance is anamorphic.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <value><c>true</c> if this instance is anamorphic; otherwise, <c>false</c>.</value>
 | 
			
		||||
        public bool? IsAnamorphic { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,40 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
#pragma warning disable CS1591
 | 
			
		||||
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
namespace MediaBrowser.Model.Entities
 | 
			
		||||
{
 | 
			
		||||
    public class PackageReviewInfo
 | 
			
		||||
    {
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets the package id (database key) for this review.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public int id { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets the rating value.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public int rating { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets whether or not this review recommends this item.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public bool recommend { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets a short description of the review.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public string title { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets the full review.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public string review { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets the time of review.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public DateTime timestamp { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Diagnostics.CodeAnalysis;
 | 
			
		||||
 | 
			
		||||
namespace MediaBrowser.Model.Entities
 | 
			
		||||
{
 | 
			
		||||
@ -9,14 +10,50 @@ namespace MediaBrowser.Model.Entities
 | 
			
		||||
    public static class ProviderIdsExtensions
 | 
			
		||||
    {
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Determines whether [has provider identifier] [the specified instance].
 | 
			
		||||
        /// Gets a provider id.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="instance">The instance.</param>
 | 
			
		||||
        /// <param name="name">The name.</param>
 | 
			
		||||
        /// <param name="id">The provider id.</param>
 | 
			
		||||
        /// <returns><c>true</c> if a provider id with the given name was found; otherwise <c>false</c>.</returns>
 | 
			
		||||
        public static bool TryGetProviderId(this IHasProviderIds instance, string name, [MaybeNullWhen(false)] out string id)
 | 
			
		||||
        {
 | 
			
		||||
            if (instance == null)
 | 
			
		||||
            {
 | 
			
		||||
                throw new ArgumentNullException(nameof(instance));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (instance.ProviderIds == null)
 | 
			
		||||
            {
 | 
			
		||||
                id = null;
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return instance.ProviderIds.TryGetValue(name, out id);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets a provider id.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="instance">The instance.</param>
 | 
			
		||||
        /// <param name="provider">The provider.</param>
 | 
			
		||||
        /// <returns><c>true</c> if [has provider identifier] [the specified instance]; otherwise, <c>false</c>.</returns>
 | 
			
		||||
        public static bool HasProviderId(this IHasProviderIds instance, MetadataProvider provider)
 | 
			
		||||
        /// <param name="id">The provider id.</param>
 | 
			
		||||
        /// <returns><c>true</c> if a provider id with the given name was found; otherwise <c>false</c>.</returns>
 | 
			
		||||
        public static bool TryGetProviderId(this IHasProviderIds instance, MetadataProvider provider, [MaybeNullWhen(false)] out string id)
 | 
			
		||||
        {
 | 
			
		||||
            return !string.IsNullOrEmpty(instance.GetProviderId(provider.ToString()));
 | 
			
		||||
            return instance.TryGetProviderId(provider.ToString(), out id);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets a provider id.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="instance">The instance.</param>
 | 
			
		||||
        /// <param name="name">The name.</param>
 | 
			
		||||
        /// <returns>System.String.</returns>
 | 
			
		||||
        public static string? GetProviderId(this IHasProviderIds instance, string name)
 | 
			
		||||
        {
 | 
			
		||||
            instance.TryGetProviderId(name, out string? id);
 | 
			
		||||
            return id;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
@ -30,28 +67,6 @@ namespace MediaBrowser.Model.Entities
 | 
			
		||||
            return instance.GetProviderId(provider.ToString());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets a provider id.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="instance">The instance.</param>
 | 
			
		||||
        /// <param name="name">The name.</param>
 | 
			
		||||
        /// <returns>System.String.</returns>
 | 
			
		||||
        public static string? GetProviderId(this IHasProviderIds instance, string name)
 | 
			
		||||
        {
 | 
			
		||||
            if (instance == null)
 | 
			
		||||
            {
 | 
			
		||||
                throw new ArgumentNullException(nameof(instance));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (instance.ProviderIds == null)
 | 
			
		||||
            {
 | 
			
		||||
                return null;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            instance.ProviderIds.TryGetValue(name, out string? id);
 | 
			
		||||
            return string.IsNullOrEmpty(id) ? null : id;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Sets a provider id.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
@ -68,13 +83,7 @@ namespace MediaBrowser.Model.Entities
 | 
			
		||||
            // If it's null remove the key from the dictionary
 | 
			
		||||
            if (string.IsNullOrEmpty(value))
 | 
			
		||||
            {
 | 
			
		||||
                if (instance.ProviderIds != null)
 | 
			
		||||
                {
 | 
			
		||||
                    if (instance.ProviderIds.ContainsKey(name))
 | 
			
		||||
                    {
 | 
			
		||||
                        instance.ProviderIds.Remove(name);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                instance.ProviderIds?.Remove(name);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										36
									
								
								MediaBrowser.Model/Entities/SpecialFolder.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								MediaBrowser.Model/Entities/SpecialFolder.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,36 @@
 | 
			
		||||
#pragma warning disable CS1591
 | 
			
		||||
 | 
			
		||||
namespace MediaBrowser.Model.Entities
 | 
			
		||||
{
 | 
			
		||||
    public static class SpecialFolder
 | 
			
		||||
    {
 | 
			
		||||
        public const string TvShowSeries = "TvShowSeries";
 | 
			
		||||
        public const string TvGenres = "TvGenres";
 | 
			
		||||
        public const string TvGenre = "TvGenre";
 | 
			
		||||
        public const string TvLatest = "TvLatest";
 | 
			
		||||
        public const string TvNextUp = "TvNextUp";
 | 
			
		||||
        public const string TvResume = "TvResume";
 | 
			
		||||
        public const string TvFavoriteSeries = "TvFavoriteSeries";
 | 
			
		||||
        public const string TvFavoriteEpisodes = "TvFavoriteEpisodes";
 | 
			
		||||
 | 
			
		||||
        public const string MovieLatest = "MovieLatest";
 | 
			
		||||
        public const string MovieResume = "MovieResume";
 | 
			
		||||
        public const string MovieMovies = "MovieMovies";
 | 
			
		||||
        public const string MovieCollections = "MovieCollections";
 | 
			
		||||
        public const string MovieFavorites = "MovieFavorites";
 | 
			
		||||
        public const string MovieGenres = "MovieGenres";
 | 
			
		||||
        public const string MovieGenre = "MovieGenre";
 | 
			
		||||
 | 
			
		||||
        public const string MusicArtists = "MusicArtists";
 | 
			
		||||
        public const string MusicAlbumArtists = "MusicAlbumArtists";
 | 
			
		||||
        public const string MusicAlbums = "MusicAlbums";
 | 
			
		||||
        public const string MusicGenres = "MusicGenres";
 | 
			
		||||
        public const string MusicLatest = "MusicLatest";
 | 
			
		||||
        public const string MusicPlaylists = "MusicPlaylists";
 | 
			
		||||
        public const string MusicSongs = "MusicSongs";
 | 
			
		||||
        public const string MusicFavorites = "MusicFavorites";
 | 
			
		||||
        public const string MusicFavoriteArtists = "MusicFavoriteArtists";
 | 
			
		||||
        public const string MusicFavoriteAlbums = "MusicFavoriteAlbums";
 | 
			
		||||
        public const string MusicFavoriteSongs = "MusicFavoriteSongs";
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -11,6 +11,14 @@ namespace MediaBrowser.Model.Entities
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public class VirtualFolderInfo
 | 
			
		||||
    {
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Initializes a new instance of the <see cref="VirtualFolderInfo"/> class.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public VirtualFolderInfo()
 | 
			
		||||
        {
 | 
			
		||||
            Locations = Array.Empty<string>();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets the name.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
@ -31,14 +39,6 @@ namespace MediaBrowser.Model.Entities
 | 
			
		||||
 | 
			
		||||
        public LibraryOptions LibraryOptions { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Initializes a new instance of the <see cref="VirtualFolderInfo"/> class.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public VirtualFolderInfo()
 | 
			
		||||
        {
 | 
			
		||||
            Locations = Array.Empty<string>();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets the item identifier.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
 | 
			
		||||
@ -10,6 +10,11 @@ namespace MediaBrowser.Model.Globalization
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public class CultureDto
 | 
			
		||||
    {
 | 
			
		||||
        public CultureDto()
 | 
			
		||||
        {
 | 
			
		||||
            ThreeLetterISOLanguageNames = Array.Empty<string>();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets the name.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
@ -29,7 +34,7 @@ namespace MediaBrowser.Model.Globalization
 | 
			
		||||
        public string TwoLetterISOLanguageName { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets the name of the three letter ISO language.
 | 
			
		||||
        /// Gets the name of the three letter ISO language.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <value>The name of the three letter ISO language.</value>
 | 
			
		||||
        public string ThreeLetterISOLanguageName
 | 
			
		||||
@ -47,10 +52,5 @@ namespace MediaBrowser.Model.Globalization
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public string[] ThreeLetterISOLanguageNames { get; set; }
 | 
			
		||||
 | 
			
		||||
        public CultureDto()
 | 
			
		||||
        {
 | 
			
		||||
            ThreeLetterISOLanguageNames = Array.Empty<string>();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -155,13 +155,16 @@ namespace MediaBrowser.Model.IO
 | 
			
		||||
        /// Gets the directories.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="path">The path.</param>
 | 
			
		||||
        /// <param name="recursive">if set to <c>true</c> [recursive].</param>
 | 
			
		||||
        /// <returns>IEnumerable<DirectoryInfo>.</returns>
 | 
			
		||||
        /// <param name="recursive">If set to <c>true</c> also searches in subdirectiories.</param>
 | 
			
		||||
        /// <returns>All found directories.</returns>
 | 
			
		||||
        IEnumerable<FileSystemMetadata> GetDirectories(string path, bool recursive = false);
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets the files.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="path">The path in which to search.</param>
 | 
			
		||||
        /// <param name="recursive">If set to <c>true</c> also searches in subdirectiories.</param>
 | 
			
		||||
        /// <returns>All found files.</returns>
 | 
			
		||||
        IEnumerable<FileSystemMetadata> GetFiles(string path, bool recursive = false);
 | 
			
		||||
 | 
			
		||||
        IEnumerable<FileSystemMetadata> GetFiles(string path, IReadOnlyList<string> extensions, bool enableCaseSensitiveExtensions, bool recursive);
 | 
			
		||||
 | 
			
		||||
@ -9,7 +9,7 @@ namespace MediaBrowser.Model.LiveTv
 | 
			
		||||
    public class BaseTimerInfoDto : IHasServerId
 | 
			
		||||
    {
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Id of the recording.
 | 
			
		||||
        /// Gets or sets the Id of the recording.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public string Id { get; set; }
 | 
			
		||||
 | 
			
		||||
@ -28,7 +28,7 @@ namespace MediaBrowser.Model.LiveTv
 | 
			
		||||
        public string ExternalId { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// ChannelId of the recording.
 | 
			
		||||
        /// Gets or sets the channel id of the recording.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public Guid ChannelId { get; set; }
 | 
			
		||||
 | 
			
		||||
@ -39,7 +39,7 @@ namespace MediaBrowser.Model.LiveTv
 | 
			
		||||
        public string ExternalChannelId { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// ChannelName of the recording.
 | 
			
		||||
        /// Gets or sets the channel name of the recording.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public string ChannelName { get; set; }
 | 
			
		||||
 | 
			
		||||
@ -58,22 +58,22 @@ namespace MediaBrowser.Model.LiveTv
 | 
			
		||||
        public string ExternalProgramId { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Name of the recording.
 | 
			
		||||
        /// Gets or sets the name of the recording.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public string Name { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Description of the recording.
 | 
			
		||||
        /// Gets or sets the description of the recording.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public string Overview { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// The start date of the recording, in UTC.
 | 
			
		||||
        /// Gets or sets the start date of the recording, in UTC.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public DateTime StartDate { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// The end date of the recording, in UTC.
 | 
			
		||||
        /// Gets or sets the end date of the recording, in UTC.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public DateTime EndDate { get; set; }
 | 
			
		||||
 | 
			
		||||
@ -108,7 +108,7 @@ namespace MediaBrowser.Model.LiveTv
 | 
			
		||||
        public bool IsPrePaddingRequired { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// If the item does not have any backdrops, this will hold the Id of the Parent that has one.
 | 
			
		||||
        /// Gets or sets the Id of the Parent that has a backdrop if the item does not have one.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <value>The parent backdrop item id.</value>
 | 
			
		||||
        public string ParentBackdropItemId { get; set; }
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										58
									
								
								MediaBrowser.Model/LiveTv/ListingsProviderInfo.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								MediaBrowser.Model/LiveTv/ListingsProviderInfo.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,58 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
#pragma warning disable CS1591
 | 
			
		||||
 | 
			
		||||
using System;
 | 
			
		||||
using MediaBrowser.Model.Dto;
 | 
			
		||||
 | 
			
		||||
namespace MediaBrowser.Model.LiveTv
 | 
			
		||||
{
 | 
			
		||||
    public class ListingsProviderInfo
 | 
			
		||||
    {
 | 
			
		||||
        public ListingsProviderInfo()
 | 
			
		||||
        {
 | 
			
		||||
            NewsCategories = new[] { "news", "journalism", "documentary", "current affairs" };
 | 
			
		||||
            SportsCategories = new[] { "sports", "basketball", "baseball", "football" };
 | 
			
		||||
            KidsCategories = new[] { "kids", "family", "children", "childrens", "disney" };
 | 
			
		||||
            MovieCategories = new[] { "movie" };
 | 
			
		||||
            EnabledTuners = Array.Empty<string>();
 | 
			
		||||
            EnableAllTuners = true;
 | 
			
		||||
            ChannelMappings = Array.Empty<NameValuePair>();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public string Id { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string Type { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string Username { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string Password { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string ListingsId { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string ZipCode { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string Country { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string Path { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string[] EnabledTuners { get; set; }
 | 
			
		||||
 | 
			
		||||
        public bool EnableAllTuners { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string[] NewsCategories { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string[] SportsCategories { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string[] KidsCategories { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string[] MovieCategories { get; set; }
 | 
			
		||||
 | 
			
		||||
        public NameValuePair[] ChannelMappings { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string MoviePrefix { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string PreferredLanguage { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string UserAgent { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -11,6 +11,12 @@ namespace MediaBrowser.Model.LiveTv
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public class LiveTvChannelQuery
 | 
			
		||||
    {
 | 
			
		||||
        public LiveTvChannelQuery()
 | 
			
		||||
        {
 | 
			
		||||
            EnableUserData = true;
 | 
			
		||||
            SortBy = Array.Empty<string>();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets the type of the channel.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
@ -48,13 +54,13 @@ namespace MediaBrowser.Model.LiveTv
 | 
			
		||||
        public Guid UserId { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Skips over a given number of items within the results. Use for paging.
 | 
			
		||||
        /// Gets or sets the start index. Used for paging.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <value>The start index.</value>
 | 
			
		||||
        public int? StartIndex { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// The maximum number of items to return.
 | 
			
		||||
        /// Gets or sets the maximum number of items to return.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <value>The limit.</value>
 | 
			
		||||
        public int? Limit { get; set; }
 | 
			
		||||
@ -68,15 +74,15 @@ namespace MediaBrowser.Model.LiveTv
 | 
			
		||||
        public bool EnableUserData { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Used to specific whether to return news or not.
 | 
			
		||||
        /// Gets or sets a value whether to return news or not.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <remarks>If set to null, all programs will be returned</remarks>
 | 
			
		||||
        /// <remarks>If set to <c>null</c>, all programs will be returned.</remarks>
 | 
			
		||||
        public bool? IsNews { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Used to specific whether to return movies or not.
 | 
			
		||||
        /// Gets or sets a value whether to return movies or not.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <remarks>If set to null, all programs will be returned</remarks>
 | 
			
		||||
        /// <remarks>If set to <c>null</c>, all programs will be returned.</remarks>
 | 
			
		||||
        public bool? IsMovie { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
@ -96,15 +102,9 @@ namespace MediaBrowser.Model.LiveTv
 | 
			
		||||
        public string[] SortBy { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// The sort order to return results with.
 | 
			
		||||
        /// Gets or sets the sort order to return results with.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <value>The sort order.</value>
 | 
			
		||||
        public SortOrder? SortOrder { get; set; }
 | 
			
		||||
 | 
			
		||||
        public LiveTvChannelQuery()
 | 
			
		||||
        {
 | 
			
		||||
            EnableUserData = true;
 | 
			
		||||
            SortBy = Array.Empty<string>();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -2,12 +2,19 @@
 | 
			
		||||
#pragma warning disable CS1591
 | 
			
		||||
 | 
			
		||||
using System;
 | 
			
		||||
using MediaBrowser.Model.Dto;
 | 
			
		||||
 | 
			
		||||
namespace MediaBrowser.Model.LiveTv
 | 
			
		||||
{
 | 
			
		||||
    public class LiveTvOptions
 | 
			
		||||
    {
 | 
			
		||||
        public LiveTvOptions()
 | 
			
		||||
        {
 | 
			
		||||
            TunerHosts = Array.Empty<TunerHostInfo>();
 | 
			
		||||
            ListingProviders = Array.Empty<ListingsProviderInfo>();
 | 
			
		||||
            MediaLocationsCreated = Array.Empty<string>();
 | 
			
		||||
            RecordingPostProcessorArguments = "\"{path}\"";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public int? GuideDays { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string RecordingPath { get; set; }
 | 
			
		||||
@ -33,93 +40,5 @@ namespace MediaBrowser.Model.LiveTv
 | 
			
		||||
        public string RecordingPostProcessor { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string RecordingPostProcessorArguments { get; set; }
 | 
			
		||||
 | 
			
		||||
        public LiveTvOptions()
 | 
			
		||||
        {
 | 
			
		||||
            TunerHosts = Array.Empty<TunerHostInfo>();
 | 
			
		||||
            ListingProviders = Array.Empty<ListingsProviderInfo>();
 | 
			
		||||
            MediaLocationsCreated = Array.Empty<string>();
 | 
			
		||||
            RecordingPostProcessorArguments = "\"{path}\"";
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public class TunerHostInfo
 | 
			
		||||
    {
 | 
			
		||||
        public string Id { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string Url { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string Type { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string DeviceId { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string FriendlyName { get; set; }
 | 
			
		||||
 | 
			
		||||
        public bool ImportFavoritesOnly { get; set; }
 | 
			
		||||
 | 
			
		||||
        public bool AllowHWTranscoding { get; set; }
 | 
			
		||||
 | 
			
		||||
        public bool EnableStreamLooping { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string Source { get; set; }
 | 
			
		||||
 | 
			
		||||
        public int TunerCount { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string UserAgent { get; set; }
 | 
			
		||||
 | 
			
		||||
        public TunerHostInfo()
 | 
			
		||||
        {
 | 
			
		||||
            AllowHWTranscoding = true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public class ListingsProviderInfo
 | 
			
		||||
    {
 | 
			
		||||
        public string Id { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string Type { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string Username { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string Password { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string ListingsId { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string ZipCode { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string Country { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string Path { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string[] EnabledTuners { get; set; }
 | 
			
		||||
 | 
			
		||||
        public bool EnableAllTuners { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string[] NewsCategories { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string[] SportsCategories { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string[] KidsCategories { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string[] MovieCategories { get; set; }
 | 
			
		||||
 | 
			
		||||
        public NameValuePair[] ChannelMappings { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string MoviePrefix { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string PreferredLanguage { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string UserAgent { get; set; }
 | 
			
		||||
 | 
			
		||||
        public ListingsProviderInfo()
 | 
			
		||||
        {
 | 
			
		||||
            NewsCategories = new[] { "news", "journalism", "documentary", "current affairs" };
 | 
			
		||||
            SportsCategories = new[] { "sports", "basketball", "baseball", "football" };
 | 
			
		||||
            KidsCategories = new[] { "kids", "family", "children", "childrens", "disney" };
 | 
			
		||||
            MovieCategories = new[] { "movie" };
 | 
			
		||||
            EnabledTuners = Array.Empty<string>();
 | 
			
		||||
            EnableAllTuners = true;
 | 
			
		||||
            ChannelMappings = Array.Empty<NameValuePair>();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -10,6 +10,11 @@ namespace MediaBrowser.Model.LiveTv
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public class LiveTvServiceInfo
 | 
			
		||||
    {
 | 
			
		||||
        public LiveTvServiceInfo()
 | 
			
		||||
        {
 | 
			
		||||
            Tuners = Array.Empty<string>();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets the name.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
@ -53,10 +58,5 @@ namespace MediaBrowser.Model.LiveTv
 | 
			
		||||
        public bool IsVisible { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string[] Tuners { get; set; }
 | 
			
		||||
 | 
			
		||||
        public LiveTvServiceInfo()
 | 
			
		||||
        {
 | 
			
		||||
            Tuners = Array.Empty<string>();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -12,6 +12,11 @@ namespace MediaBrowser.Model.LiveTv
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public class RecordingQuery
 | 
			
		||||
    {
 | 
			
		||||
        public RecordingQuery()
 | 
			
		||||
        {
 | 
			
		||||
            EnableTotalRecordCount = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets the channel identifier.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
@ -31,13 +36,13 @@ namespace MediaBrowser.Model.LiveTv
 | 
			
		||||
        public string Id { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Skips over a given number of items within the results. Use for paging.
 | 
			
		||||
        /// Gets or sets the start index. Use for paging.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <value>The start index.</value>
 | 
			
		||||
        public int? StartIndex { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// The maximum number of items to return.
 | 
			
		||||
        /// Gets or sets the maximum number of items to return.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <value>The limit.</value>
 | 
			
		||||
        public int? Limit { get; set; }
 | 
			
		||||
@ -61,7 +66,7 @@ namespace MediaBrowser.Model.LiveTv
 | 
			
		||||
        public string SeriesTimerId { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Fields to return within the items, in addition to basic information.
 | 
			
		||||
        /// Gets or sets the fields to return within the items, in addition to basic information.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <value>The fields.</value>
 | 
			
		||||
        public ItemFields[] Fields { get; set; }
 | 
			
		||||
@ -85,10 +90,5 @@ namespace MediaBrowser.Model.LiveTv
 | 
			
		||||
        public ImageType[] EnableImageTypes { get; set; }
 | 
			
		||||
 | 
			
		||||
        public bool EnableTotalRecordCount { get; set; }
 | 
			
		||||
 | 
			
		||||
        public RecordingQuery()
 | 
			
		||||
        {
 | 
			
		||||
            EnableTotalRecordCount = true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -7,6 +7,14 @@ using MediaBrowser.Model.Entities;
 | 
			
		||||
 | 
			
		||||
namespace MediaBrowser.Model.LiveTv
 | 
			
		||||
{
 | 
			
		||||
    public enum KeepUntil
 | 
			
		||||
    {
 | 
			
		||||
        UntilDeleted,
 | 
			
		||||
        UntilSpaceNeeded,
 | 
			
		||||
        UntilWatched,
 | 
			
		||||
        UntilDate
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Class SeriesTimerInfoDto.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
@ -83,12 +91,4 @@ namespace MediaBrowser.Model.LiveTv
 | 
			
		||||
        /// <value>The parent primary image tag.</value>
 | 
			
		||||
        public string ParentPrimaryImageTag { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public enum KeepUntil
 | 
			
		||||
    {
 | 
			
		||||
        UntilDeleted,
 | 
			
		||||
        UntilSpaceNeeded,
 | 
			
		||||
        UntilWatched,
 | 
			
		||||
        UntilDate
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										38
									
								
								MediaBrowser.Model/LiveTv/TunerHostInfo.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								MediaBrowser.Model/LiveTv/TunerHostInfo.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,38 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
#pragma warning disable CS1591
 | 
			
		||||
 | 
			
		||||
using System;
 | 
			
		||||
using MediaBrowser.Model.Dto;
 | 
			
		||||
 | 
			
		||||
namespace MediaBrowser.Model.LiveTv
 | 
			
		||||
{
 | 
			
		||||
    public class TunerHostInfo
 | 
			
		||||
    {
 | 
			
		||||
        public TunerHostInfo()
 | 
			
		||||
        {
 | 
			
		||||
            AllowHWTranscoding = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public string Id { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string Url { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string Type { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string DeviceId { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string FriendlyName { get; set; }
 | 
			
		||||
 | 
			
		||||
        public bool ImportFavoritesOnly { get; set; }
 | 
			
		||||
 | 
			
		||||
        public bool AllowHWTranscoding { get; set; }
 | 
			
		||||
 | 
			
		||||
        public bool EnableStreamLooping { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string Source { get; set; }
 | 
			
		||||
 | 
			
		||||
        public int TunerCount { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string UserAgent { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -17,7 +17,7 @@
 | 
			
		||||
    <TargetFramework>net5.0</TargetFramework>
 | 
			
		||||
    <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
 | 
			
		||||
    <GenerateDocumentationFile>true</GenerateDocumentationFile>
 | 
			
		||||
    <TreatWarningsAsErrors Condition=" '$(Configuration)' == 'Release' ">true</TreatWarningsAsErrors>
 | 
			
		||||
    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
 | 
			
		||||
    <Nullable>enable</Nullable>
 | 
			
		||||
    <LangVersion>latest</LangVersion>
 | 
			
		||||
    <PublishRepositoryUrl>true</PublishRepositoryUrl>
 | 
			
		||||
@ -44,7 +44,7 @@
 | 
			
		||||
 | 
			
		||||
  <!-- Code Analyzers-->
 | 
			
		||||
  <ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
 | 
			
		||||
    <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8" PrivateAssets="All" />
 | 
			
		||||
    <!-- <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8" PrivateAssets="All" /> -->
 | 
			
		||||
    <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
 | 
			
		||||
    <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" />
 | 
			
		||||
    <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
 | 
			
		||||
 | 
			
		||||
@ -10,6 +10,17 @@ namespace MediaBrowser.Model.MediaInfo
 | 
			
		||||
{
 | 
			
		||||
    public class MediaInfo : MediaSourceInfo, IHasProviderIds
 | 
			
		||||
    {
 | 
			
		||||
        public MediaInfo()
 | 
			
		||||
        {
 | 
			
		||||
            Chapters = Array.Empty<ChapterInfo>();
 | 
			
		||||
            Artists = Array.Empty<string>();
 | 
			
		||||
            AlbumArtists = Array.Empty<string>();
 | 
			
		||||
            Studios = Array.Empty<string>();
 | 
			
		||||
            Genres = Array.Empty<string>();
 | 
			
		||||
            People = Array.Empty<BaseItemPerson>();
 | 
			
		||||
            ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public ChapterInfo[] Chapters { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
@ -69,16 +80,5 @@ namespace MediaBrowser.Model.MediaInfo
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <value>The overview.</value>
 | 
			
		||||
        public string Overview { get; set; }
 | 
			
		||||
 | 
			
		||||
        public MediaInfo()
 | 
			
		||||
        {
 | 
			
		||||
            Chapters = Array.Empty<ChapterInfo>();
 | 
			
		||||
            Artists = Array.Empty<string>();
 | 
			
		||||
            AlbumArtists = Array.Empty<string>();
 | 
			
		||||
            Studios = Array.Empty<string>();
 | 
			
		||||
            Genres = Array.Empty<string>();
 | 
			
		||||
            People = Array.Empty<BaseItemPerson>();
 | 
			
		||||
            ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -8,6 +8,17 @@ namespace MediaBrowser.Model.MediaInfo
 | 
			
		||||
{
 | 
			
		||||
    public class PlaybackInfoRequest
 | 
			
		||||
    {
 | 
			
		||||
        public PlaybackInfoRequest()
 | 
			
		||||
        {
 | 
			
		||||
            EnableDirectPlay = true;
 | 
			
		||||
            EnableDirectStream = true;
 | 
			
		||||
            EnableTranscoding = true;
 | 
			
		||||
            AllowVideoStreamCopy = true;
 | 
			
		||||
            AllowAudioStreamCopy = true;
 | 
			
		||||
            IsPlayback = true;
 | 
			
		||||
            DirectPlayProtocols = new MediaProtocol[] { MediaProtocol.Http };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Guid Id { get; set; }
 | 
			
		||||
 | 
			
		||||
        public Guid UserId { get; set; }
 | 
			
		||||
@ -43,16 +54,5 @@ namespace MediaBrowser.Model.MediaInfo
 | 
			
		||||
        public bool AutoOpenLiveStream { get; set; }
 | 
			
		||||
 | 
			
		||||
        public MediaProtocol[] DirectPlayProtocols { get; set; }
 | 
			
		||||
 | 
			
		||||
        public PlaybackInfoRequest()
 | 
			
		||||
        {
 | 
			
		||||
            EnableDirectPlay = true;
 | 
			
		||||
            EnableDirectStream = true;
 | 
			
		||||
            EnableTranscoding = true;
 | 
			
		||||
            AllowVideoStreamCopy = true;
 | 
			
		||||
            AllowAudioStreamCopy = true;
 | 
			
		||||
            IsPlayback = true;
 | 
			
		||||
            DirectPlayProtocols = new MediaProtocol[] { MediaProtocol.Http };
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -10,6 +10,14 @@ namespace MediaBrowser.Model.MediaInfo
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public class PlaybackInfoResponse
 | 
			
		||||
    {
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Initializes a new instance of the <see cref="PlaybackInfoResponse" /> class.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public PlaybackInfoResponse()
 | 
			
		||||
        {
 | 
			
		||||
            MediaSources = Array.Empty<MediaSourceInfo>();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets the media sources.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
@ -27,13 +35,5 @@ namespace MediaBrowser.Model.MediaInfo
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <value>The error code.</value>
 | 
			
		||||
        public PlaybackErrorCode? ErrorCode { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Initializes a new instance of the <see cref="PlaybackInfoResponse" /> class.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public PlaybackInfoResponse()
 | 
			
		||||
        {
 | 
			
		||||
            MediaSources = Array.Empty<MediaSourceInfo>();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,10 +1,15 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
#pragma warning disable CS1591
 | 
			
		||||
 | 
			
		||||
namespace MediaBrowser.Model.MediaInfo
 | 
			
		||||
{
 | 
			
		||||
    public class SubtitleTrackEvent
 | 
			
		||||
    {
 | 
			
		||||
        public SubtitleTrackEvent(string id, string text)
 | 
			
		||||
        {
 | 
			
		||||
            Id = id;
 | 
			
		||||
            Text = text;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public string Id { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string Text { get; set; }
 | 
			
		||||
 | 
			
		||||
@ -1,3 +1,4 @@
 | 
			
		||||
#nullable enable
 | 
			
		||||
#pragma warning disable CS1591
 | 
			
		||||
 | 
			
		||||
using System;
 | 
			
		||||
@ -7,11 +8,11 @@ namespace MediaBrowser.Model.MediaInfo
 | 
			
		||||
{
 | 
			
		||||
    public class SubtitleTrackInfo
 | 
			
		||||
    {
 | 
			
		||||
        public IReadOnlyList<SubtitleTrackEvent> TrackEvents { get; set; }
 | 
			
		||||
 | 
			
		||||
        public SubtitleTrackInfo()
 | 
			
		||||
        {
 | 
			
		||||
            TrackEvents = Array.Empty<SubtitleTrackEvent>();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public IReadOnlyList<SubtitleTrackEvent> TrackEvents { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -23,6 +23,12 @@ namespace MediaBrowser.Model.Net
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Sends a UDP message to a particular end point (uni or multicast).
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="buffer">An array of type <see cref="byte" /> that contains the data to send.</param>
 | 
			
		||||
        /// <param name="offset">The zero-based position in buffer at which to begin sending data.</param>
 | 
			
		||||
        /// <param name="bytes">The number of bytes to send.</param>
 | 
			
		||||
        /// <param name="endPoint">An <see cref="IPEndPoint" /> that represents the remote device.</param>
 | 
			
		||||
        /// <param name="cancellationToken">The cancellation token to cancel operation.</param>
 | 
			
		||||
        /// <returns>The task object representing the asynchronous operation.</returns>
 | 
			
		||||
        Task SendToAsync(byte[] buffer, int offset, int bytes, IPEndPoint endPoint, CancellationToken cancellationToken);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -14,6 +14,9 @@ namespace MediaBrowser.Model.Net
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Creates a new unicast socket using the specified local port number.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="localIp">The local IP address to bind to.</param>
 | 
			
		||||
        /// <param name="localPort">The local port to bind to.</param>
 | 
			
		||||
        /// <returns>A new unicast socket using the specified local port number.</returns>
 | 
			
		||||
        ISocket CreateSsdpUdpSocket(IPAddress localIp, int localPort);
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
 | 
			
		||||
@ -91,9 +91,9 @@ namespace MediaBrowser.Model.Net
 | 
			
		||||
            { ".webp", "image/webp" },
 | 
			
		||||
 | 
			
		||||
            // Type font
 | 
			
		||||
            { ".ttf" , "font/ttf" },
 | 
			
		||||
            { ".woff" , "font/woff" },
 | 
			
		||||
            { ".woff2" , "font/woff2" },
 | 
			
		||||
            { ".ttf", "font/ttf" },
 | 
			
		||||
            { ".woff", "font/woff" },
 | 
			
		||||
            { ".woff2", "font/woff2" },
 | 
			
		||||
 | 
			
		||||
            // Type text
 | 
			
		||||
            { ".ass", "text/x-ssa" },
 | 
			
		||||
@ -168,14 +168,17 @@ namespace MediaBrowser.Model.Net
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets the type of the MIME.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public static string? GetMimeType(string path, bool enableStreamDefault)
 | 
			
		||||
        /// <param name="filename">The filename to find the MIME type of.</param>
 | 
			
		||||
        /// <param name="enableStreamDefault">Whether of not to return a default value if no fitting MIME type is found.</param>
 | 
			
		||||
        /// <returns>The worrect MIME type for the given filename, or `null` if it wasn't found and <paramref name="enableStreamDefault"/> is false.</returns>
 | 
			
		||||
        public static string? GetMimeType(string filename, bool enableStreamDefault)
 | 
			
		||||
        {
 | 
			
		||||
            if (path.Length == 0)
 | 
			
		||||
            if (filename.Length == 0)
 | 
			
		||||
            {
 | 
			
		||||
                throw new ArgumentException("String can't be empty.", nameof(path));
 | 
			
		||||
                throw new ArgumentException("String can't be empty.", nameof(filename));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var ext = Path.GetExtension(path);
 | 
			
		||||
            var ext = Path.GetExtension(filename);
 | 
			
		||||
 | 
			
		||||
            if (_mimeTypeLookup.TryGetValue(ext, out string? result))
 | 
			
		||||
            {
 | 
			
		||||
@ -210,9 +213,9 @@ namespace MediaBrowser.Model.Net
 | 
			
		||||
            return enableStreamDefault ? "application/octet-stream" : null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static string? ToExtension(string? mimeType)
 | 
			
		||||
        public static string? ToExtension(string mimeType)
 | 
			
		||||
        {
 | 
			
		||||
            if (string.IsNullOrEmpty(mimeType))
 | 
			
		||||
            if (mimeType.Length == 0)
 | 
			
		||||
            {
 | 
			
		||||
                throw new ArgumentException("String can't be empty.", nameof(mimeType));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@ -1,33 +0,0 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
#pragma warning disable CS1591
 | 
			
		||||
 | 
			
		||||
namespace MediaBrowser.Model.Net
 | 
			
		||||
{
 | 
			
		||||
    public class NetworkShare
 | 
			
		||||
    {
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// The name of the computer that this share belongs to.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public string Server { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Share name.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public string Name { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Local path.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public string Path { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Share type.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public NetworkShareType ShareType { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Comment.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public string Remark { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -20,12 +20,12 @@ namespace MediaBrowser.Model.Net
 | 
			
		||||
        public int ReceivedBytes { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// The <see cref="IPEndPoint"/> the data was received from.
 | 
			
		||||
        /// Gets or sets the <see cref="IPEndPoint"/> the data was received from.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public IPEndPoint RemoteEndPoint { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// The local <see cref="IPAddress"/>.
 | 
			
		||||
        /// Gets or sets the local <see cref="IPAddress"/>.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public IPAddress LocalIPAddress { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -9,7 +9,7 @@ namespace MediaBrowser.Model.Net
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Class WebSocketMessage.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <typeparam name="T"></typeparam>
 | 
			
		||||
    /// <typeparam name="T">The type of the data.</typeparam>
 | 
			
		||||
    public class WebSocketMessage<T>
 | 
			
		||||
    {
 | 
			
		||||
        /// <summary>
 | 
			
		||||
 | 
			
		||||
@ -2,18 +2,16 @@
 | 
			
		||||
#pragma warning disable CS1591
 | 
			
		||||
 | 
			
		||||
using System;
 | 
			
		||||
using Jellyfin.Data.Enums;
 | 
			
		||||
using MediaBrowser.Model.Extensions;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using Jellyfin.Data.Entities;
 | 
			
		||||
using Jellyfin.Data.Enums;
 | 
			
		||||
using MediaBrowser.Model.Extensions;
 | 
			
		||||
using MediaBrowser.Model.Users;
 | 
			
		||||
 | 
			
		||||
namespace MediaBrowser.Model.Notifications
 | 
			
		||||
{
 | 
			
		||||
    public class NotificationOptions
 | 
			
		||||
    {
 | 
			
		||||
        public NotificationOption[] Options { get; set; }
 | 
			
		||||
 | 
			
		||||
        public NotificationOptions()
 | 
			
		||||
        {
 | 
			
		||||
            Options = new[]
 | 
			
		||||
@ -71,6 +69,8 @@ namespace MediaBrowser.Model.Notifications
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public NotificationOption[] Options { get; set; }
 | 
			
		||||
 | 
			
		||||
        public NotificationOption GetOptions(string type)
 | 
			
		||||
        {
 | 
			
		||||
            foreach (NotificationOption i in Options)
 | 
			
		||||
@ -104,7 +104,7 @@ namespace MediaBrowser.Model.Notifications
 | 
			
		||||
            NotificationOption opt = GetOptions(type);
 | 
			
		||||
 | 
			
		||||
            return opt != null && opt.Enabled &&
 | 
			
		||||
                   !opt.DisabledMonitorUsers.Contains(userId.ToString(""), StringComparer.OrdinalIgnoreCase);
 | 
			
		||||
                   !opt.DisabledMonitorUsers.Contains(userId.ToString(string.Empty), StringComparer.OrdinalIgnoreCase);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public bool IsEnabledToSendToUser(string type, string userId, User user)
 | 
			
		||||
 | 
			
		||||
@ -7,6 +7,12 @@ namespace MediaBrowser.Model.Notifications
 | 
			
		||||
{
 | 
			
		||||
    public class NotificationRequest
 | 
			
		||||
    {
 | 
			
		||||
        public NotificationRequest()
 | 
			
		||||
        {
 | 
			
		||||
            UserIds = Array.Empty<Guid>();
 | 
			
		||||
            Date = DateTime.UtcNow;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public string Name { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string Description { get; set; }
 | 
			
		||||
@ -20,16 +26,10 @@ namespace MediaBrowser.Model.Notifications
 | 
			
		||||
        public DateTime Date { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// The corresponding type name used in configuration. Not for display.
 | 
			
		||||
        /// Gets or sets the corresponding type name used in configuration. Not for display.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public string NotificationType { get; set; }
 | 
			
		||||
 | 
			
		||||
        public SendToUserType? SendToUserMode { get; set; }
 | 
			
		||||
 | 
			
		||||
        public NotificationRequest()
 | 
			
		||||
        {
 | 
			
		||||
            UserIds = Array.Empty<Guid>();
 | 
			
		||||
            Date = DateTime.UtcNow;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -6,11 +6,11 @@ namespace MediaBrowser.Model.Providers
 | 
			
		||||
    public class ExternalIdInfo
 | 
			
		||||
    {
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Represents the external id information for serialization to the client.
 | 
			
		||||
        /// Initializes a new instance of the <see cref="ExternalIdInfo"/> class.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="name">Name of the external id provider (IE: IMDB, MusicBrainz, etc).</param>
 | 
			
		||||
        /// <param name="key">Key for this id. This key should be unique across all providers.</param>
 | 
			
		||||
        /// <param name="type">Specific media type for this id</param>
 | 
			
		||||
        /// <param name="type">Specific media type for this id.</param>
 | 
			
		||||
        /// <param name="urlFormatString">URL format string.</param>
 | 
			
		||||
        public ExternalIdInfo(string name, string key, ExternalIdMediaType? type, string urlFormatString)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
@ -22,7 +22,7 @@ namespace MediaBrowser.Model.Providers
 | 
			
		||||
        public string Url { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets a url used for previewing a smaller version.
 | 
			
		||||
        /// Gets or sets a url used for previewing a smaller version.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public string ThumbnailUrl { get; set; }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -7,6 +7,14 @@ namespace MediaBrowser.Model.Providers
 | 
			
		||||
{
 | 
			
		||||
    public class SubtitleOptions
 | 
			
		||||
    {
 | 
			
		||||
        public SubtitleOptions()
 | 
			
		||||
        {
 | 
			
		||||
            DownloadLanguages = Array.Empty<string>();
 | 
			
		||||
 | 
			
		||||
            SkipIfAudioTrackMatches = true;
 | 
			
		||||
            RequirePerfectMatch = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public bool SkipIfEmbeddedSubtitlesPresent { get; set; }
 | 
			
		||||
 | 
			
		||||
        public bool SkipIfAudioTrackMatches { get; set; }
 | 
			
		||||
@ -24,13 +32,5 @@ namespace MediaBrowser.Model.Providers
 | 
			
		||||
        public bool IsOpenSubtitleVipAccount { get; set; }
 | 
			
		||||
 | 
			
		||||
        public bool RequirePerfectMatch { get; set; }
 | 
			
		||||
 | 
			
		||||
        public SubtitleOptions()
 | 
			
		||||
        {
 | 
			
		||||
            DownloadLanguages = Array.Empty<string>();
 | 
			
		||||
 | 
			
		||||
            SkipIfAudioTrackMatches = true;
 | 
			
		||||
            RequirePerfectMatch = true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -7,6 +7,11 @@ namespace MediaBrowser.Model.Querying
 | 
			
		||||
{
 | 
			
		||||
    public class EpisodeQuery
 | 
			
		||||
    {
 | 
			
		||||
        public EpisodeQuery()
 | 
			
		||||
        {
 | 
			
		||||
            Fields = Array.Empty<ItemFields>();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets the user identifier.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
@ -66,10 +71,5 @@ namespace MediaBrowser.Model.Querying
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <value>The start item identifier.</value>
 | 
			
		||||
        public string StartItemId { get; set; }
 | 
			
		||||
 | 
			
		||||
        public EpisodeQuery()
 | 
			
		||||
        {
 | 
			
		||||
            Fields = Array.Empty<ItemFields>();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -14,31 +14,32 @@ namespace MediaBrowser.Model.Querying
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// The user to localize search results for.
 | 
			
		||||
        /// Gets or sets the user to localize search results for.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <value>The user id.</value>
 | 
			
		||||
        public Guid UserId { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets the parent id.
 | 
			
		||||
        /// Specify this to localize the search to a specific item or folder. Omit to use the root.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <value>The parent id.</value>
 | 
			
		||||
        public Guid ParentId { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Skips over a given number of items within the results. Use for paging.
 | 
			
		||||
        /// Gets or sets the start index. Used for paging.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <value>The start index.</value>
 | 
			
		||||
        public int? StartIndex { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// The maximum number of items to return.
 | 
			
		||||
        /// Gets or sets the maximum number of items to return.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <value>The limit.</value>
 | 
			
		||||
        public int? Limit { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Fields to return within the items, in addition to basic information.
 | 
			
		||||
        /// Gets or sets the fields to return within the items, in addition to basic information.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <value>The fields.</value>
 | 
			
		||||
        public ItemFields[] Fields { get; set; }
 | 
			
		||||
 | 
			
		||||
@ -7,6 +7,13 @@ namespace MediaBrowser.Model.Querying
 | 
			
		||||
{
 | 
			
		||||
    public class MovieRecommendationQuery
 | 
			
		||||
    {
 | 
			
		||||
        public MovieRecommendationQuery()
 | 
			
		||||
        {
 | 
			
		||||
            ItemLimit = 10;
 | 
			
		||||
            CategoryLimit = 6;
 | 
			
		||||
            Fields = Array.Empty<ItemFields>();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets the user identifier.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
@ -36,12 +43,5 @@ namespace MediaBrowser.Model.Querying
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <value>The fields.</value>
 | 
			
		||||
        public ItemFields[] Fields { get; set; }
 | 
			
		||||
 | 
			
		||||
        public MovieRecommendationQuery()
 | 
			
		||||
        {
 | 
			
		||||
            ItemLimit = 10;
 | 
			
		||||
            CategoryLimit = 6;
 | 
			
		||||
            Fields = Array.Empty<ItemFields>();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user