From 5677566a41638f4c62f107f3540363457c099019 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 29 Jul 2023 21:35:38 +0200 Subject: [PATCH] Enable nullable for more files --- Emby.Dlna/Didl/DidlBuilder.cs | 64 +++++----- Emby.Dlna/PlayTo/Device.cs | 116 +++++++----------- Emby.Dlna/PlayTo/PlayToController.cs | 4 +- Emby.Dlna/PlayTo/PlayToManager.cs | 10 +- .../Plugins/PluginManager.cs | 2 +- .../Drawing/ImageProcessorExtensions.cs | 6 +- MediaBrowser.Model/Dlna/DeviceProfile.cs | 8 +- MediaBrowser.Model/Dlna/StreamBuilder.cs | 31 ++--- MediaBrowser.Model/Dlna/StreamInfo.cs | 90 ++++++++------ .../Dlna/StreamBuilderTests.cs | 20 +-- 10 files changed, 161 insertions(+), 190 deletions(-) diff --git a/Emby.Dlna/Didl/DidlBuilder.cs b/Emby.Dlna/Didl/DidlBuilder.cs index f668dc829a..5ed982876d 100644 --- a/Emby.Dlna/Didl/DidlBuilder.cs +++ b/Emby.Dlna/Didl/DidlBuilder.cs @@ -1,5 +1,3 @@ -#nullable disable - #pragma warning disable CS1591 using System; @@ -45,8 +43,8 @@ namespace Emby.Dlna.Didl private readonly DeviceProfile _profile; private readonly IImageProcessor _imageProcessor; private readonly string _serverAddress; - private readonly string _accessToken; - private readonly User _user; + private readonly string? _accessToken; + private readonly User? _user; private readonly IUserDataManager _userDataManager; private readonly ILocalizationManager _localization; private readonly IMediaSourceManager _mediaSourceManager; @@ -56,10 +54,10 @@ namespace Emby.Dlna.Didl public DidlBuilder( DeviceProfile profile, - User user, + User? user, IImageProcessor imageProcessor, string serverAddress, - string accessToken, + string? accessToken, IUserDataManager userDataManager, ILocalizationManager localization, IMediaSourceManager mediaSourceManager, @@ -85,7 +83,7 @@ namespace Emby.Dlna.Didl return url + "&dlnaheaders=true"; } - public string GetItemDidl(BaseItem item, User user, BaseItem context, string deviceId, Filter filter, StreamInfo streamInfo) + public string GetItemDidl(BaseItem item, User? user, BaseItem? context, string deviceId, Filter filter, StreamInfo streamInfo) { var settings = new XmlWriterSettings { @@ -140,12 +138,12 @@ namespace Emby.Dlna.Didl public void WriteItemElement( XmlWriter writer, BaseItem item, - User user, - BaseItem context, + User? user, + BaseItem? context, StubType? contextStubType, string deviceId, Filter filter, - StreamInfo streamInfo = null) + StreamInfo? streamInfo = null) { var clientId = GetClientId(item, null); @@ -190,7 +188,7 @@ namespace Emby.Dlna.Didl writer.WriteFullEndElement(); } - private void AddVideoResource(XmlWriter writer, BaseItem video, string deviceId, Filter filter, StreamInfo streamInfo = null) + private void AddVideoResource(XmlWriter writer, BaseItem video, string deviceId, Filter filter, StreamInfo? streamInfo = null) { if (streamInfo is null) { @@ -203,7 +201,7 @@ namespace Emby.Dlna.Didl Profile = _profile, DeviceId = deviceId, MaxBitrate = _profile.MaxStreamingBitrate - }); + }) ?? throw new InvalidOperationException("No optimal video stream found"); } var targetWidth = streamInfo.TargetWidth; @@ -315,7 +313,7 @@ namespace Emby.Dlna.Didl var mediaSource = streamInfo.MediaSource; - if (mediaSource.RunTimeTicks.HasValue) + if (mediaSource?.RunTimeTicks.HasValue == true) { writer.WriteAttributeString("duration", TimeSpan.FromTicks(mediaSource.RunTimeTicks.Value).ToString("c", CultureInfo.InvariantCulture)); } @@ -410,7 +408,7 @@ namespace Emby.Dlna.Didl writer.WriteFullEndElement(); } - private string GetDisplayName(BaseItem item, StubType? itemStubType, BaseItem context) + private string GetDisplayName(BaseItem item, StubType? itemStubType, BaseItem? context) { if (itemStubType.HasValue) { @@ -452,7 +450,7 @@ namespace Emby.Dlna.Didl /// The episode. /// Current context. /// Formatted name of the episode. - private string GetEpisodeDisplayName(Episode episode, BaseItem context) + private string GetEpisodeDisplayName(Episode episode, BaseItem? context) { string[] components; @@ -530,7 +528,7 @@ namespace Emby.Dlna.Didl private bool NotNullOrWhiteSpace(string s) => !string.IsNullOrWhiteSpace(s); - private void AddAudioResource(XmlWriter writer, BaseItem audio, string deviceId, Filter filter, StreamInfo streamInfo = null) + private void AddAudioResource(XmlWriter writer, BaseItem audio, string deviceId, Filter filter, StreamInfo? streamInfo = null) { writer.WriteStartElement(string.Empty, "res", NsDidl); @@ -544,14 +542,14 @@ namespace Emby.Dlna.Didl MediaSources = sources.ToArray(), Profile = _profile, DeviceId = deviceId - }); + }) ?? throw new InvalidOperationException("No optimal audio stream found"); } var url = NormalizeDlnaMediaUrl(streamInfo.ToUrl(_serverAddress, _accessToken)); var mediaSource = streamInfo.MediaSource; - if (mediaSource.RunTimeTicks.HasValue) + if (mediaSource?.RunTimeTicks is not null) { writer.WriteAttributeString("duration", TimeSpan.FromTicks(mediaSource.RunTimeTicks.Value).ToString("c", CultureInfo.InvariantCulture)); } @@ -634,7 +632,7 @@ namespace Emby.Dlna.Didl // Samsung sometimes uses 1 as root || string.Equals(id, "1", StringComparison.OrdinalIgnoreCase); - public void WriteFolderElement(XmlWriter writer, BaseItem folder, StubType? stubType, BaseItem context, int childCount, Filter filter, string requestedId = null) + public void WriteFolderElement(XmlWriter writer, BaseItem folder, StubType? stubType, BaseItem context, int childCount, Filter filter, string? requestedId = null) { writer.WriteStartElement(string.Empty, "container", NsDidl); @@ -678,14 +676,14 @@ namespace Emby.Dlna.Didl writer.WriteFullEndElement(); } - private void AddSamsungBookmarkInfo(BaseItem item, User user, XmlWriter writer, StreamInfo streamInfo) + private void AddSamsungBookmarkInfo(BaseItem item, User? user, XmlWriter writer, StreamInfo? streamInfo) { if (!item.SupportsPositionTicksResume || item is Folder) { return; } - XmlAttribute secAttribute = null; + XmlAttribute? secAttribute = null; foreach (var attribute in _profile.XmlRootAttributes) { if (string.Equals(attribute.Name, "xmlns:sec", StringComparison.OrdinalIgnoreCase)) @@ -695,8 +693,8 @@ namespace Emby.Dlna.Didl } } - // Not a samsung device - if (secAttribute is null) + // Not a samsung device or no user data + if (secAttribute is null || user is null) { return; } @@ -717,7 +715,7 @@ namespace Emby.Dlna.Didl /// /// Adds fields used by both items and folders. /// - private void AddCommonFields(BaseItem item, StubType? itemStubType, BaseItem context, XmlWriter writer, Filter filter) + private void AddCommonFields(BaseItem item, StubType? itemStubType, BaseItem? context, XmlWriter writer, Filter filter) { // Don't filter on dc:title because not all devices will include it in the filter // MediaMonkey for example won't display content without a title @@ -795,7 +793,7 @@ namespace Emby.Dlna.Didl if (item.IsDisplayedAsFolder || stubType.HasValue) { - string classType = null; + string? classType = null; if (!_profile.RequiresPlainFolders) { @@ -899,7 +897,7 @@ namespace Emby.Dlna.Didl } } - private void AddGeneralProperties(BaseItem item, StubType? itemStubType, BaseItem context, XmlWriter writer, Filter filter) + private void AddGeneralProperties(BaseItem item, StubType? itemStubType, BaseItem? context, XmlWriter writer, Filter filter) { AddCommonFields(item, itemStubType, context, writer, filter); @@ -975,7 +973,7 @@ namespace Emby.Dlna.Didl private void AddCover(BaseItem item, StubType? stubType, XmlWriter writer) { - ImageDownloadInfo imageInfo = GetImageInfo(item); + ImageDownloadInfo? imageInfo = GetImageInfo(item); if (imageInfo is null) { @@ -1073,7 +1071,7 @@ namespace Emby.Dlna.Didl writer.WriteFullEndElement(); } - private ImageDownloadInfo GetImageInfo(BaseItem item) + private ImageDownloadInfo? GetImageInfo(BaseItem item) { if (item.HasImage(ImageType.Primary)) { @@ -1118,7 +1116,7 @@ namespace Emby.Dlna.Didl return null; } - private BaseItem GetFirstParentWithImageBelowUserRoot(BaseItem item) + private BaseItem? GetFirstParentWithImageBelowUserRoot(BaseItem item) { if (item is null) { @@ -1148,7 +1146,7 @@ namespace Emby.Dlna.Didl private ImageDownloadInfo GetImageInfo(BaseItem item, ImageType type) { var imageInfo = item.GetImageInfo(type, 0); - string tag = null; + string? tag = null; try { @@ -1250,7 +1248,7 @@ namespace Emby.Dlna.Didl { internal Guid ItemId { get; set; } - internal string ImageTag { get; set; } + internal string? ImageTag { get; set; } internal ImageType Type { get; set; } @@ -1260,9 +1258,9 @@ namespace Emby.Dlna.Didl internal bool IsDirectStream { get; set; } - internal string Format { get; set; } + internal required string Format { get; set; } - internal ItemImageInfo ItemImageInfo { get; set; } + internal required ItemImageInfo ItemImageInfo { get; set; } } } } diff --git a/Emby.Dlna/PlayTo/Device.cs b/Emby.Dlna/PlayTo/Device.cs index 9c476119df..d21cc69132 100644 --- a/Emby.Dlna/PlayTo/Device.cs +++ b/Emby.Dlna/PlayTo/Device.cs @@ -1,5 +1,3 @@ -#nullable disable - #pragma warning disable CS1591 using System; @@ -25,7 +23,7 @@ namespace Emby.Dlna.PlayTo private readonly ILogger _logger; private readonly object _timerLock = new object(); - private Timer _timer; + private Timer? _timer; private int _muteVol; private int _volume; private DateTime _lastVolumeRefresh; @@ -40,13 +38,13 @@ namespace Emby.Dlna.PlayTo _logger = logger; } - public event EventHandler PlaybackStart; + public event EventHandler? PlaybackStart; - public event EventHandler PlaybackProgress; + public event EventHandler? PlaybackProgress; - public event EventHandler PlaybackStopped; + public event EventHandler? PlaybackStopped; - public event EventHandler MediaChanged; + public event EventHandler? MediaChanged; public DeviceInfo Properties { get; set; } @@ -75,13 +73,13 @@ namespace Emby.Dlna.PlayTo public bool IsStopped => TransportState == TransportState.STOPPED; - public Action OnDeviceUnavailable { get; set; } + public Action? OnDeviceUnavailable { get; set; } - private TransportCommands AvCommands { get; set; } + private TransportCommands? AvCommands { get; set; } - private TransportCommands RendererCommands { get; set; } + private TransportCommands? RendererCommands { get; set; } - public UBaseObject CurrentMediaInfo { get; private set; } + public UBaseObject? CurrentMediaInfo { get; private set; } public void Start() { @@ -131,7 +129,7 @@ namespace Emby.Dlna.PlayTo _volumeRefreshActive = true; var time = immediate ? 100 : 10000; - _timer.Change(time, Timeout.Infinite); + _timer?.Change(time, Timeout.Infinite); } } @@ -149,7 +147,7 @@ namespace Emby.Dlna.PlayTo _volumeRefreshActive = false; - _timer.Change(Timeout.Infinite, Timeout.Infinite); + _timer?.Change(Timeout.Infinite, Timeout.Infinite); } } @@ -199,7 +197,7 @@ namespace Emby.Dlna.PlayTo } } - private DeviceService GetServiceRenderingControl() + private DeviceService? GetServiceRenderingControl() { var services = Properties.Services; @@ -207,7 +205,7 @@ namespace Emby.Dlna.PlayTo services.FirstOrDefault(s => (s.ServiceType ?? string.Empty).StartsWith("urn:schemas-upnp-org:service:RenderingControl", StringComparison.OrdinalIgnoreCase)); } - private DeviceService GetAvTransportService() + private DeviceService? GetAvTransportService() { var services = Properties.Services; @@ -240,7 +238,7 @@ namespace Emby.Dlna.PlayTo Properties.BaseUrl, service, command.Name, - rendererCommands.BuildPost(command, service.ServiceType, value), + rendererCommands!.BuildPost(command, service.ServiceType, value), // null checked above cancellationToken: cancellationToken) .ConfigureAwait(false); @@ -265,12 +263,7 @@ namespace Emby.Dlna.PlayTo return; } - var service = GetServiceRenderingControl(); - - if (service is null) - { - throw new InvalidOperationException("Unable to find service"); - } + var service = GetServiceRenderingControl() ?? throw new InvalidOperationException("Unable to find service"); // Set it early and assume it will succeed // Remote control will perform better @@ -281,7 +274,7 @@ namespace Emby.Dlna.PlayTo Properties.BaseUrl, service, command.Name, - rendererCommands.BuildPost(command, service.ServiceType, value), + rendererCommands!.BuildPost(command, service.ServiceType, value), // null checked above cancellationToken: cancellationToken) .ConfigureAwait(false); } @@ -296,26 +289,20 @@ namespace Emby.Dlna.PlayTo return; } - var service = GetAvTransportService(); - - if (service is null) - { - throw new InvalidOperationException("Unable to find service"); - } - + var service = GetAvTransportService() ?? throw new InvalidOperationException("Unable to find service"); await new DlnaHttpClient(_logger, _httpClientFactory) .SendCommandAsync( Properties.BaseUrl, service, command.Name, - avCommands.BuildPost(command, service.ServiceType, string.Format(CultureInfo.InvariantCulture, "{0:hh}:{0:mm}:{0:ss}", value), "REL_TIME"), + avCommands!.BuildPost(command, service.ServiceType, string.Format(CultureInfo.InvariantCulture, "{0:hh}:{0:mm}:{0:ss}", value), "REL_TIME"), // null checked above cancellationToken: cancellationToken) .ConfigureAwait(false); RestartTimer(true); } - public async Task SetAvTransport(string url, string header, string metaData, CancellationToken cancellationToken) + public async Task SetAvTransport(string url, string? header, string metaData, CancellationToken cancellationToken) { var avCommands = await GetAVProtocolAsync(cancellationToken).ConfigureAwait(false); @@ -335,14 +322,8 @@ namespace Emby.Dlna.PlayTo { "CurrentURIMetaData", CreateDidlMeta(metaData) } }; - var service = GetAvTransportService(); - - if (service is null) - { - throw new InvalidOperationException("Unable to find service"); - } - - var post = avCommands.BuildPost(command, service.ServiceType, url, dictionary); + var service = GetAvTransportService() ?? throw new InvalidOperationException("Unable to find service"); + var post = avCommands!.BuildPost(command, service.ServiceType, url, dictionary); // null checked above await new DlnaHttpClient(_logger, _httpClientFactory) .SendCommandAsync( Properties.BaseUrl, @@ -372,7 +353,7 @@ namespace Emby.Dlna.PlayTo * SetNextAvTransport is used to specify to the DLNA device what is the next track to play. * Without that information, the next track command on the device does not work. */ - public async Task SetNextAvTransport(string url, string header, string metaData, CancellationToken cancellationToken = default) + public async Task SetNextAvTransport(string url, string? header, string metaData, CancellationToken cancellationToken = default) { var avCommands = await GetAVProtocolAsync(cancellationToken).ConfigureAwait(false); @@ -380,7 +361,7 @@ namespace Emby.Dlna.PlayTo _logger.LogDebug("{PropertyName} - SetNextAvTransport Uri: {Url} DlnaHeaders: {Header}", Properties.Name, url, header); - var command = avCommands.ServiceActions.FirstOrDefault(c => string.Equals(c.Name, "SetNextAVTransportURI", StringComparison.OrdinalIgnoreCase)); + var command = avCommands?.ServiceActions.FirstOrDefault(c => string.Equals(c.Name, "SetNextAVTransportURI", StringComparison.OrdinalIgnoreCase)); if (command is null) { return; @@ -392,14 +373,8 @@ namespace Emby.Dlna.PlayTo { "NextURIMetaData", CreateDidlMeta(metaData) } }; - var service = GetAvTransportService(); - - if (service is null) - { - throw new InvalidOperationException("Unable to find service"); - } - - var post = avCommands.BuildPost(command, service.ServiceType, url, dictionary); + var service = GetAvTransportService() ?? throw new InvalidOperationException("Unable to find service"); + var post = avCommands!.BuildPost(command, service.ServiceType, url, dictionary); // null checked above await new DlnaHttpClient(_logger, _httpClientFactory) .SendCommandAsync(Properties.BaseUrl, service, command.Name, post, header, cancellationToken) .ConfigureAwait(false); @@ -423,12 +398,7 @@ namespace Emby.Dlna.PlayTo return Task.CompletedTask; } - var service = GetAvTransportService(); - if (service is null) - { - throw new InvalidOperationException("Unable to find service"); - } - + var service = GetAvTransportService() ?? throw new InvalidOperationException("Unable to find service"); return new DlnaHttpClient(_logger, _httpClientFactory).SendCommandAsync( Properties.BaseUrl, service, @@ -460,14 +430,13 @@ namespace Emby.Dlna.PlayTo return; } - var service = GetAvTransportService(); - + var service = GetAvTransportService() ?? throw new InvalidOperationException("Unable to find service"); await new DlnaHttpClient(_logger, _httpClientFactory) .SendCommandAsync( Properties.BaseUrl, service, command.Name, - avCommands.BuildPost(command, service.ServiceType, 1), + avCommands!.BuildPost(command, service.ServiceType, 1), // null checked above cancellationToken: cancellationToken) .ConfigureAwait(false); @@ -484,14 +453,13 @@ namespace Emby.Dlna.PlayTo return; } - var service = GetAvTransportService(); - + var service = GetAvTransportService() ?? throw new InvalidOperationException("Unable to find service"); await new DlnaHttpClient(_logger, _httpClientFactory) .SendCommandAsync( Properties.BaseUrl, service, command.Name, - avCommands.BuildPost(command, service.ServiceType, 1), + avCommands!.BuildPost(command, service.ServiceType, 1), // null checked above cancellationToken: cancellationToken) .ConfigureAwait(false); @@ -500,7 +468,7 @@ namespace Emby.Dlna.PlayTo RestartTimer(true); } - private async void TimerCallback(object sender) + private async void TimerCallback(object? sender) { if (_disposed) { @@ -623,7 +591,7 @@ namespace Emby.Dlna.PlayTo Properties.BaseUrl, service, command.Name, - rendererCommands.BuildPost(command, service.ServiceType), + rendererCommands!.BuildPost(command, service.ServiceType), // null checked above cancellationToken: cancellationToken).ConfigureAwait(false); if (result is null || result.Document is null) @@ -673,7 +641,7 @@ namespace Emby.Dlna.PlayTo Properties.BaseUrl, service, command.Name, - rendererCommands.BuildPost(command, service.ServiceType), + rendererCommands!.BuildPost(command, service.ServiceType), // null checked above cancellationToken: cancellationToken).ConfigureAwait(false); if (result is null || result.Document is null) @@ -728,7 +696,7 @@ namespace Emby.Dlna.PlayTo return null; } - private async Task GetMediaInfo(TransportCommands avCommands, CancellationToken cancellationToken) + private async Task GetMediaInfo(TransportCommands avCommands, CancellationToken cancellationToken) { var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetMediaInfo"); if (command is null) @@ -798,7 +766,7 @@ namespace Emby.Dlna.PlayTo return null; } - private async Task<(bool Success, UBaseObject Track)> GetPositionInfo(TransportCommands avCommands, CancellationToken cancellationToken) + private async Task<(bool Success, UBaseObject? Track)> GetPositionInfo(TransportCommands avCommands, CancellationToken cancellationToken) { var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetPositionInfo"); if (command is null) @@ -871,7 +839,7 @@ namespace Emby.Dlna.PlayTo return (true, null); } - XElement uPnpResponse = null; + XElement? uPnpResponse = null; try { @@ -895,7 +863,7 @@ namespace Emby.Dlna.PlayTo return (true, uTrack); } - private XElement ParseResponse(string xml) + private XElement? ParseResponse(string xml) { // Handle different variations sent back by devices. try @@ -929,7 +897,7 @@ namespace Emby.Dlna.PlayTo return null; } - private static UBaseObject CreateUBaseObject(XElement container, string trackUri) + private static UBaseObject CreateUBaseObject(XElement? container, string? trackUri) { ArgumentNullException.ThrowIfNull(container); @@ -972,7 +940,7 @@ namespace Emby.Dlna.PlayTo return new string[4]; } - private async Task GetAVProtocolAsync(CancellationToken cancellationToken) + private async Task GetAVProtocolAsync(CancellationToken cancellationToken) { if (AvCommands is not null) { @@ -1004,7 +972,7 @@ namespace Emby.Dlna.PlayTo return AvCommands; } - private async Task GetRenderingProtocolAsync(CancellationToken cancellationToken) + private async Task GetRenderingProtocolAsync(CancellationToken cancellationToken) { if (RendererCommands is not null) { @@ -1054,7 +1022,7 @@ namespace Emby.Dlna.PlayTo return baseUrl + url; } - public static async Task CreateuPnpDeviceAsync(Uri url, IHttpClientFactory httpClientFactory, ILogger logger, CancellationToken cancellationToken) + public static async Task CreateuPnpDeviceAsync(Uri url, IHttpClientFactory httpClientFactory, ILogger logger, CancellationToken cancellationToken) { var ssdpHttpClient = new DlnaHttpClient(logger, httpClientFactory); @@ -1287,7 +1255,7 @@ namespace Emby.Dlna.PlayTo } _timer = null; - Properties = null; + Properties = null!; _disposed = true; } diff --git a/Emby.Dlna/PlayTo/PlayToController.cs b/Emby.Dlna/PlayTo/PlayToController.cs index 86db363374..b1ad15cdc9 100644 --- a/Emby.Dlna/PlayTo/PlayToController.cs +++ b/Emby.Dlna/PlayTo/PlayToController.cs @@ -42,7 +42,7 @@ namespace Emby.Dlna.PlayTo private readonly IDeviceDiscovery _deviceDiscovery; private readonly string _serverAddress; - private readonly string _accessToken; + private readonly string? _accessToken; private readonly List _playlist = new List(); private Device _device; @@ -59,7 +59,7 @@ namespace Emby.Dlna.PlayTo IUserManager userManager, IImageProcessor imageProcessor, string serverAddress, - string accessToken, + string? accessToken, IDeviceDiscovery deviceDiscovery, IUserDataManager userDataManager, ILocalizationManager localization, diff --git a/Emby.Dlna/PlayTo/PlayToManager.cs b/Emby.Dlna/PlayTo/PlayToManager.cs index 241dff5ae3..ef617422c4 100644 --- a/Emby.Dlna/PlayTo/PlayToManager.cs +++ b/Emby.Dlna/PlayTo/PlayToManager.cs @@ -1,5 +1,3 @@ -#nullable disable - #pragma warning disable CS1591 using System; @@ -67,7 +65,7 @@ namespace Emby.Dlna.PlayTo _deviceDiscovery.DeviceDiscovered += OnDeviceDiscoveryDeviceDiscovered; } - private async void OnDeviceDiscoveryDeviceDiscovered(object sender, GenericEventArgs e) + private async void OnDeviceDiscoveryDeviceDiscovered(object? sender, GenericEventArgs e) { if (_disposed) { @@ -76,12 +74,12 @@ namespace Emby.Dlna.PlayTo var info = e.Argument; - if (!info.Headers.TryGetValue("USN", out string usn)) + if (!info.Headers.TryGetValue("USN", out string? usn)) { usn = string.Empty; } - if (!info.Headers.TryGetValue("NT", out string nt)) + if (!info.Headers.TryGetValue("NT", out string? nt)) { nt = string.Empty; } @@ -161,7 +159,7 @@ namespace Emby.Dlna.PlayTo var uri = info.Location; _logger.LogDebug("Attempting to create PlayToController from location {0}", uri); - if (info.Headers.TryGetValue("USN", out string uuid)) + if (info.Headers.TryGetValue("USN", out string? uuid)) { uuid = GetUuid(uuid); } diff --git a/Emby.Server.Implementations/Plugins/PluginManager.cs b/Emby.Server.Implementations/Plugins/PluginManager.cs index 48584ae0cb..1303012e1a 100644 --- a/Emby.Server.Implementations/Plugins/PluginManager.cs +++ b/Emby.Server.Implementations/Plugins/PluginManager.cs @@ -677,7 +677,7 @@ namespace Emby.Server.Implementations.Plugins } catch (JsonException ex) { - _logger.LogError(ex, "Error deserializing {Json}.", Encoding.UTF8.GetString(data!)); + _logger.LogError(ex, "Error deserializing {Json}.", Encoding.UTF8.GetString(data)); } if (manifest is not null) diff --git a/MediaBrowser.Controller/Drawing/ImageProcessorExtensions.cs b/MediaBrowser.Controller/Drawing/ImageProcessorExtensions.cs index 62b70ce53c..10326363a9 100644 --- a/MediaBrowser.Controller/Drawing/ImageProcessorExtensions.cs +++ b/MediaBrowser.Controller/Drawing/ImageProcessorExtensions.cs @@ -1,5 +1,3 @@ -#nullable disable - #pragma warning disable CS1591 using MediaBrowser.Controller.Entities; @@ -9,12 +7,12 @@ namespace MediaBrowser.Controller.Drawing { public static class ImageProcessorExtensions { - public static string GetImageCacheTag(this IImageProcessor processor, BaseItem item, ImageType imageType) + public static string? GetImageCacheTag(this IImageProcessor processor, BaseItem item, ImageType imageType) { return processor.GetImageCacheTag(item, imageType, 0); } - public static string GetImageCacheTag(this IImageProcessor processor, BaseItem item, ImageType imageType, int imageIndex) + public static string? GetImageCacheTag(this IImageProcessor processor, BaseItem item, ImageType imageType, int imageIndex) { var imageInfo = item.GetImageInfo(imageType, imageIndex); diff --git a/MediaBrowser.Model/Dlna/DeviceProfile.cs b/MediaBrowser.Model/Dlna/DeviceProfile.cs index b7c23669df..07bb002eae 100644 --- a/MediaBrowser.Model/Dlna/DeviceProfile.cs +++ b/MediaBrowser.Model/Dlna/DeviceProfile.cs @@ -314,7 +314,7 @@ namespace MediaBrowser.Model.Dlna /// The audio sample rate. /// The audio bit depth. /// The . - public ResponseProfile? GetAudioMediaProfile(string container, string? audioCodec, int? audioChannels, int? audioBitrate, int? audioSampleRate, int? audioBitDepth) + public ResponseProfile? GetAudioMediaProfile(string? container, string? audioCodec, int? audioChannels, int? audioBitrate, int? audioSampleRate, int? audioBitDepth) { foreach (var i in ResponseProfiles) { @@ -438,14 +438,14 @@ namespace MediaBrowser.Model.Dlna /// True if Avc. /// The . public ResponseProfile? GetVideoMediaProfile( - string container, + string? container, string? audioCodec, string? videoCodec, int? width, int? height, int? bitDepth, int? videoBitrate, - string videoProfile, + string? videoProfile, VideoRangeType videoRangeType, double? videoLevel, float? videoFramerate, @@ -456,7 +456,7 @@ namespace MediaBrowser.Model.Dlna int? refFrames, int? numVideoStreams, int? numAudioStreams, - string videoCodecTag, + string? videoCodecTag, bool? isAvc) { foreach (var i in ResponseProfiles) diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index f6b882c3e6..7aac4fda50 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -179,15 +179,9 @@ namespace MediaBrowser.Model.Dlna { ValidateMediaOptions(options, true); - var mediaSources = new List(); - foreach (var mediaSourceInfo in options.MediaSources) - { - if (string.IsNullOrEmpty(options.MediaSourceId) - || string.Equals(mediaSourceInfo.Id, options.MediaSourceId, StringComparison.OrdinalIgnoreCase)) - { - mediaSources.Add(mediaSourceInfo); - } - } + var mediaSources = string.IsNullOrEmpty(options.MediaSourceId) + ? options.MediaSources + : options.MediaSources.Where(x => string.Equals(x.Id, options.MediaSourceId, StringComparison.OrdinalIgnoreCase)); var streams = new List(); foreach (var mediaSourceInfo in mediaSources) @@ -216,7 +210,7 @@ namespace MediaBrowser.Model.Dlna return streams.OrderBy(i => { // Nothing beats direct playing a file - if (i.PlayMethod == PlayMethod.DirectPlay && i.MediaSource.Protocol == MediaProtocol.File) + if (i.PlayMethod == PlayMethod.DirectPlay && i.MediaSource?.Protocol == MediaProtocol.File) { return 0; } @@ -235,7 +229,7 @@ namespace MediaBrowser.Model.Dlna } }).ThenBy(i => { - switch (i.MediaSource.Protocol) + switch (i.MediaSource?.Protocol) { case MediaProtocol.File: return 0; @@ -246,7 +240,7 @@ namespace MediaBrowser.Model.Dlna { if (maxBitrate > 0) { - if (i.MediaSource.Bitrate.HasValue) + if (i.MediaSource?.Bitrate is not null) { return Math.Abs(i.MediaSource.Bitrate.Value - maxBitrate); } @@ -585,10 +579,10 @@ namespace MediaBrowser.Model.Dlna MediaSource = item, RunTimeTicks = item.RunTimeTicks, Context = options.Context, - DeviceProfile = options.Profile + DeviceProfile = options.Profile, + SubtitleStreamIndex = options.SubtitleStreamIndex ?? GetDefaultSubtitleStreamIndex(item, options.Profile.SubtitleProfiles) }; - playlistItem.SubtitleStreamIndex = options.SubtitleStreamIndex ?? GetDefaultSubtitleStreamIndex(item, options.Profile.SubtitleProfiles); var subtitleStream = playlistItem.SubtitleStreamIndex.HasValue ? item.GetMediaStream(MediaStreamType.Subtitle, playlistItem.SubtitleStreamIndex.Value) : null; var audioStream = item.GetDefaultAudioStream(options.AudioStreamIndex ?? item.DefaultAudioStreamIndex); @@ -659,7 +653,8 @@ namespace MediaBrowser.Model.Dlna if (audioStreamIndex.HasValue) { playlistItem.AudioStreamIndex = audioStreamIndex; - playlistItem.AudioCodecs = new[] { item.GetMediaStream(MediaStreamType.Audio, audioStreamIndex.Value)?.Codec }; + var audioCodec = item.GetMediaStream(MediaStreamType.Audio, audioStreamIndex.Value)?.Codec; + playlistItem.AudioCodecs = audioCodec is null ? Array.Empty() : new[] { audioCodec }; } } else if (directPlay == PlayMethod.DirectStream) @@ -842,7 +837,7 @@ namespace MediaBrowser.Model.Dlna if (videoStream is not null && videoStream.Level != 0) { - playlistItem.SetOption(qualifier, "level", videoStream.Level.ToString()); + playlistItem.SetOption(qualifier, "level", videoStream.Level.ToString() ?? string.Empty); } // Prefer matching audio codecs, could do better here @@ -871,7 +866,7 @@ namespace MediaBrowser.Model.Dlna // Copy matching audio codec options playlistItem.AudioSampleRate = audioStream.SampleRate; - playlistItem.SetOption(qualifier, "audiochannels", audioStream.Channels.ToString()); + playlistItem.SetOption(qualifier, "audiochannels", audioStream.Channels.ToString() ?? string.Empty); if (!string.IsNullOrEmpty(audioStream.Profile)) { @@ -880,7 +875,7 @@ namespace MediaBrowser.Model.Dlna if (audioStream.Level != 0) { - playlistItem.SetOption(audioStream.Codec, "level", audioStream.Level.ToString()); + playlistItem.SetOption(audioStream.Codec, "level", audioStream.Level.ToString() ?? string.Empty); } } diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs index 00543616d1..fc146df306 100644 --- a/MediaBrowser.Model/Dlna/StreamInfo.cs +++ b/MediaBrowser.Model/Dlna/StreamInfo.cs @@ -1,9 +1,9 @@ -#nullable disable #pragma warning disable CS1591 using System; using System.Collections.Generic; using System.Globalization; +using System.Linq; using Jellyfin.Data.Enums; using MediaBrowser.Model.Drawing; using MediaBrowser.Model.Dto; @@ -34,9 +34,9 @@ namespace MediaBrowser.Model.Dlna public DlnaProfileType MediaType { get; set; } - public string Container { get; set; } + public string? Container { get; set; } - public string SubProtocol { get; set; } + public string? SubProtocol { get; set; } public long StartPositionTicks { get; set; } @@ -80,11 +80,11 @@ namespace MediaBrowser.Model.Dlna public float? MaxFramerate { get; set; } - public DeviceProfile DeviceProfile { get; set; } + public required DeviceProfile DeviceProfile { get; set; } - public string DeviceProfileId { get; set; } + public string? DeviceProfileId { get; set; } - public string DeviceId { get; set; } + public string? DeviceId { get; set; } public long? RunTimeTicks { get; set; } @@ -92,21 +92,21 @@ namespace MediaBrowser.Model.Dlna public bool EstimateContentLength { get; set; } - public MediaSourceInfo MediaSource { get; set; } + public MediaSourceInfo? MediaSource { get; set; } public string[] SubtitleCodecs { get; set; } public SubtitleDeliveryMethod SubtitleDeliveryMethod { get; set; } - public string SubtitleFormat { get; set; } + public string? SubtitleFormat { get; set; } - public string PlaySessionId { get; set; } + public string? PlaySessionId { get; set; } public TranscodeReason TranscodeReasons { get; set; } public Dictionary StreamOptions { get; private set; } - public string MediaSourceId => MediaSource?.Id; + public string? MediaSourceId => MediaSource?.Id; public bool IsDirectStream => MediaSource?.VideoType is not (VideoType.Dvd or VideoType.BluRay) && PlayMethod is PlayMethod.DirectStream or PlayMethod.DirectPlay; @@ -114,12 +114,12 @@ namespace MediaBrowser.Model.Dlna /// /// Gets the audio stream that will be used. /// - public MediaStream TargetAudioStream => MediaSource?.GetDefaultAudioStream(AudioStreamIndex); + public MediaStream? TargetAudioStream => MediaSource?.GetDefaultAudioStream(AudioStreamIndex); /// /// Gets the video stream that will be used. /// - public MediaStream TargetVideoStream => MediaSource?.VideoStream; + public MediaStream? TargetVideoStream => MediaSource?.VideoStream; /// /// Gets the audio sample rate that will be in the output stream. @@ -259,7 +259,7 @@ namespace MediaBrowser.Model.Dlna /// /// Gets the audio sample rate that will be in the output stream. /// - public string TargetVideoProfile + public string? TargetVideoProfile { get { @@ -307,7 +307,7 @@ namespace MediaBrowser.Model.Dlna /// Gets the target video codec tag. /// /// The target video codec tag. - public string TargetVideoCodecTag + public string? TargetVideoCodecTag { get { @@ -364,7 +364,7 @@ namespace MediaBrowser.Model.Dlna { var stream = TargetAudioStream; - string inputCodec = stream?.Codec; + string? inputCodec = stream?.Codec; if (IsDirectStream) { @@ -389,7 +389,7 @@ namespace MediaBrowser.Model.Dlna { var stream = TargetVideoStream; - string inputCodec = stream?.Codec; + string? inputCodec = stream?.Codec; if (IsDirectStream) { @@ -417,7 +417,7 @@ namespace MediaBrowser.Model.Dlna { if (IsDirectStream) { - return MediaSource.Size; + return MediaSource?.Size; } if (RunTimeTicks.HasValue) @@ -580,7 +580,7 @@ namespace MediaBrowser.Model.Dlna } } - public void SetOption(string qualifier, string name, string value) + public void SetOption(string? qualifier, string name, string value) { if (string.IsNullOrEmpty(qualifier)) { @@ -597,7 +597,7 @@ namespace MediaBrowser.Model.Dlna StreamOptions[name] = value; } - public string GetOption(string qualifier, string name) + public string? GetOption(string? qualifier, string name) { var value = GetOption(qualifier + "-" + name); @@ -609,7 +609,7 @@ namespace MediaBrowser.Model.Dlna return value; } - public string GetOption(string name) + public string? GetOption(string name) { if (StreamOptions.TryGetValue(name, out var value)) { @@ -619,7 +619,7 @@ namespace MediaBrowser.Model.Dlna return null; } - public string ToUrl(string baseUrl, string accessToken) + public string ToUrl(string baseUrl, string? accessToken) { ArgumentException.ThrowIfNullOrEmpty(baseUrl); @@ -686,7 +686,7 @@ namespace MediaBrowser.Model.Dlna return string.Format(CultureInfo.InvariantCulture, "{0}/videos/{1}/stream{2}?{3}", baseUrl, ItemId, extension, queryString); } - private static IEnumerable BuildParams(StreamInfo item, string accessToken) + private static IEnumerable BuildParams(StreamInfo item, string? accessToken) { var list = new List(); @@ -730,7 +730,7 @@ namespace MediaBrowser.Model.Dlna list.Add(new NameValuePair("PlaySessionId", item.PlaySessionId ?? string.Empty)); list.Add(new NameValuePair("api_key", accessToken ?? string.Empty)); - string liveStreamId = item.MediaSource?.LiveStreamId; + string? liveStreamId = item.MediaSource?.LiveStreamId; list.Add(new NameValuePair("LiveStreamId", liveStreamId ?? string.Empty)); list.Add(new NameValuePair("SubtitleMethod", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod != SubtitleDeliveryMethod.External ? item.SubtitleDeliveryMethod.ToString() : string.Empty)); @@ -772,7 +772,7 @@ namespace MediaBrowser.Model.Dlna list.Add(new NameValuePair("RequireAvc", item.RequireAvc.ToString(CultureInfo.InvariantCulture).ToLowerInvariant())); } - list.Add(new NameValuePair("Tag", item.MediaSource.ETag ?? string.Empty)); + list.Add(new NameValuePair("Tag", item.MediaSource?.ETag ?? string.Empty)); string subtitleCodecs = item.SubtitleCodecs.Length == 0 ? string.Empty : @@ -816,13 +816,18 @@ namespace MediaBrowser.Model.Dlna return list; } - public IEnumerable GetSubtitleProfiles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, string baseUrl, string accessToken) + public IEnumerable GetSubtitleProfiles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, string baseUrl, string? accessToken) { return GetSubtitleProfiles(transcoderSupport, includeSelectedTrackOnly, false, baseUrl, accessToken); } - public IEnumerable GetSubtitleProfiles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, bool enableAllProfiles, string baseUrl, string accessToken) + public IEnumerable GetSubtitleProfiles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, bool enableAllProfiles, string baseUrl, string? accessToken) { + if (MediaSource is null) + { + return Enumerable.Empty(); + } + var list = new List(); // HLS will preserve timestamps so we can just grab the full subtitle stream @@ -856,27 +861,36 @@ namespace MediaBrowser.Model.Dlna return list; } - private void AddSubtitleProfiles(List list, MediaStream stream, ITranscoderSupport transcoderSupport, bool enableAllProfiles, string baseUrl, string accessToken, long startPositionTicks) + private void AddSubtitleProfiles(List list, MediaStream stream, ITranscoderSupport transcoderSupport, bool enableAllProfiles, string baseUrl, string? accessToken, long startPositionTicks) { if (enableAllProfiles) { foreach (var profile in DeviceProfile.SubtitleProfiles) { var info = GetSubtitleStreamInfo(stream, baseUrl, accessToken, startPositionTicks, new[] { profile }, transcoderSupport); - - list.Add(info); + if (info is not null) + { + list.Add(info); + } } } else { var info = GetSubtitleStreamInfo(stream, baseUrl, accessToken, startPositionTicks, DeviceProfile.SubtitleProfiles, transcoderSupport); - - list.Add(info); + if (info is not null) + { + list.Add(info); + } } } - private SubtitleStreamInfo GetSubtitleStreamInfo(MediaStream stream, string baseUrl, string accessToken, long startPositionTicks, SubtitleProfile[] subtitleProfiles, ITranscoderSupport transcoderSupport) + private SubtitleStreamInfo? GetSubtitleStreamInfo(MediaStream stream, string baseUrl, string? accessToken, long startPositionTicks, SubtitleProfile[] subtitleProfiles, ITranscoderSupport transcoderSupport) { + if (MediaSource is null) + { + return null; + } + var subtitleProfile = StreamBuilder.GetSubtitleProfile(MediaSource, stream, subtitleProfiles, PlayMethod, transcoderSupport, Container, SubProtocol); var info = new SubtitleStreamInfo { @@ -920,7 +934,7 @@ namespace MediaBrowser.Model.Dlna return info; } - public int? GetTargetVideoBitDepth(string codec) + public int? GetTargetVideoBitDepth(string? codec) { var value = GetOption(codec, "videobitdepth"); @@ -932,7 +946,7 @@ namespace MediaBrowser.Model.Dlna return null; } - public int? GetTargetAudioBitDepth(string codec) + public int? GetTargetAudioBitDepth(string? codec) { var value = GetOption(codec, "audiobitdepth"); @@ -944,7 +958,7 @@ namespace MediaBrowser.Model.Dlna return null; } - public double? GetTargetVideoLevel(string codec) + public double? GetTargetVideoLevel(string? codec) { var value = GetOption(codec, "level"); @@ -956,7 +970,7 @@ namespace MediaBrowser.Model.Dlna return null; } - public int? GetTargetRefFrames(string codec) + public int? GetTargetRefFrames(string? codec) { var value = GetOption(codec, "maxrefframes"); @@ -968,7 +982,7 @@ namespace MediaBrowser.Model.Dlna return null; } - public int? GetTargetAudioChannels(string codec) + public int? GetTargetAudioChannels(string? codec) { var defaultValue = GlobalMaxAudioChannels ?? TranscodingMaxAudioChannels; @@ -988,7 +1002,7 @@ namespace MediaBrowser.Model.Dlna private int? GetMediaStreamCount(MediaStreamType type, int limit) { - var count = MediaSource.GetStreamCount(type); + var count = MediaSource?.GetStreamCount(type); if (count.HasValue) { diff --git a/tests/Jellyfin.Model.Tests/Dlna/StreamBuilderTests.cs b/tests/Jellyfin.Model.Tests/Dlna/StreamBuilderTests.cs index c30dad6f90..e4a1e1f887 100644 --- a/tests/Jellyfin.Model.Tests/Dlna/StreamBuilderTests.cs +++ b/tests/Jellyfin.Model.Tests/Dlna/StreamBuilderTests.cs @@ -351,11 +351,11 @@ namespace Jellyfin.Model.Tests // Assert.Contains(uri.Extension, containers); // Check expected video codec (1) - Assert.Contains(targetVideoStream.Codec, streamInfo.TargetVideoCodec); + Assert.Contains(targetVideoStream?.Codec, streamInfo.TargetVideoCodec); Assert.Single(streamInfo.TargetVideoCodec); // Check expected audio codecs (1) - Assert.Contains(targetAudioStream.Codec, streamInfo.TargetAudioCodec); + Assert.Contains(targetAudioStream?.Codec, streamInfo.TargetAudioCodec); Assert.Single(streamInfo.TargetAudioCodec); // Assert.Single(val.AudioCodecs); @@ -410,13 +410,13 @@ namespace Jellyfin.Model.Tests else { // Check expected video codec (1) - Assert.Contains(targetVideoStream.Codec, streamInfo.TargetVideoCodec); + Assert.Contains(targetVideoStream?.Codec, streamInfo.TargetVideoCodec); Assert.Single(streamInfo.TargetVideoCodec); if (transcodeMode.Equals("DirectStream", StringComparison.Ordinal)) { // Check expected audio codecs (1) - if (!targetAudioStream.IsExternal) + if (targetAudioStream?.IsExternal == false) { // Check expected audio codecs (1) if (streamInfo.TranscodeReasons.HasFlag(TranscodeReason.ContainerNotSupported)) @@ -432,7 +432,7 @@ namespace Jellyfin.Model.Tests else if (transcodeMode.Equals("Remux", StringComparison.Ordinal)) { // Check expected audio codecs (1) - Assert.Contains(targetAudioStream.Codec, streamInfo.AudioCodecs); + Assert.Contains(targetAudioStream?.Codec, streamInfo.AudioCodecs); Assert.Single(streamInfo.AudioCodecs); } @@ -440,10 +440,10 @@ namespace Jellyfin.Model.Tests var videoStream = targetVideoStream; Assert.False(streamInfo.EstimateContentLength); Assert.Equal(TranscodeSeekInfo.Auto, streamInfo.TranscodeSeekInfo); - Assert.Contains(videoStream.Profile?.ToLowerInvariant() ?? string.Empty, streamInfo.TargetVideoProfile?.Split(",").Select(s => s.ToLowerInvariant()) ?? Array.Empty()); - Assert.Equal(videoStream.Level, streamInfo.TargetVideoLevel); - Assert.Equal(videoStream.BitDepth, streamInfo.TargetVideoBitDepth); - Assert.InRange(streamInfo.VideoBitrate.GetValueOrDefault(), videoStream.BitRate.GetValueOrDefault(), int.MaxValue); + Assert.Contains(videoStream?.Profile?.ToLowerInvariant() ?? string.Empty, streamInfo.TargetVideoProfile?.Split(",").Select(s => s.ToLowerInvariant()) ?? Array.Empty()); + Assert.Equal(videoStream?.Level, streamInfo.TargetVideoLevel); + Assert.Equal(videoStream?.BitDepth, streamInfo.TargetVideoBitDepth); + Assert.InRange(streamInfo.VideoBitrate.GetValueOrDefault(), videoStream?.BitRate.GetValueOrDefault() ?? 0, int.MaxValue); // Audio codec not supported if ((why & TranscodeReason.AudioCodecNotSupported) != 0) @@ -452,7 +452,7 @@ namespace Jellyfin.Model.Tests if (options.AudioStreamIndex >= 0) { // TODO:fixme - if (!targetAudioStream.IsExternal) + if (targetAudioStream?.IsExternal == false) { Assert.DoesNotContain(targetAudioStream.Codec, streamInfo.AudioCodecs); }