Merge pull request #10056 from Bond-009/nullable2

This commit is contained in:
Bond-009 2023-09-12 11:09:20 +02:00 committed by GitHub
commit 3087881d93
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 161 additions and 190 deletions

View File

@ -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
/// <param name="episode">The episode.</param>
/// <param name="context">Current context.</param>
/// <returns>Formatted name of the episode.</returns>
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
/// <summary>
/// Adds fields used by both items and folders.
/// </summary>
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; }
}
}
}

View File

@ -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<PlaybackStartEventArgs> PlaybackStart;
public event EventHandler<PlaybackStartEventArgs>? PlaybackStart;
public event EventHandler<PlaybackProgressEventArgs> PlaybackProgress;
public event EventHandler<PlaybackProgressEventArgs>? PlaybackProgress;
public event EventHandler<PlaybackStoppedEventArgs> PlaybackStopped;
public event EventHandler<PlaybackStoppedEventArgs>? PlaybackStopped;
public event EventHandler<MediaChangedEventArgs> MediaChanged;
public event EventHandler<MediaChangedEventArgs>? 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<UBaseObject> GetMediaInfo(TransportCommands avCommands, CancellationToken cancellationToken)
private async Task<UBaseObject?> 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<TransportCommands> GetAVProtocolAsync(CancellationToken cancellationToken)
private async Task<TransportCommands?> GetAVProtocolAsync(CancellationToken cancellationToken)
{
if (AvCommands is not null)
{
@ -1004,7 +972,7 @@ namespace Emby.Dlna.PlayTo
return AvCommands;
}
private async Task<TransportCommands> GetRenderingProtocolAsync(CancellationToken cancellationToken)
private async Task<TransportCommands?> GetRenderingProtocolAsync(CancellationToken cancellationToken)
{
if (RendererCommands is not null)
{
@ -1054,7 +1022,7 @@ namespace Emby.Dlna.PlayTo
return baseUrl + url;
}
public static async Task<Device> CreateuPnpDeviceAsync(Uri url, IHttpClientFactory httpClientFactory, ILogger logger, CancellationToken cancellationToken)
public static async Task<Device?> 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;
}

View File

@ -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<PlaylistItem> _playlist = new List<PlaylistItem>();
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,

View File

@ -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<UpnpDeviceInfo> e)
private async void OnDeviceDiscoveryDeviceDiscovered(object? sender, GenericEventArgs<UpnpDeviceInfo> 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);
}

View File

@ -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)

View File

@ -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);

View File

@ -314,7 +314,7 @@ namespace MediaBrowser.Model.Dlna
/// <param name="audioSampleRate">The audio sample rate.</param>
/// <param name="audioBitDepth">The audio bit depth.</param>
/// <returns>The <see cref="ResponseProfile"/>.</returns>
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
/// <param name="isAvc">True if Avc.</param>
/// <returns>The <see cref="ResponseProfile"/>.</returns>
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)

View File

@ -179,15 +179,9 @@ namespace MediaBrowser.Model.Dlna
{
ValidateMediaOptions(options, true);
var mediaSources = new List<MediaSourceInfo>();
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<StreamInfo>();
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<string>() : 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);
}
}

View File

@ -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<string, string> 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
/// <summary>
/// Gets the audio stream that will be used.
/// </summary>
public MediaStream TargetAudioStream => MediaSource?.GetDefaultAudioStream(AudioStreamIndex);
public MediaStream? TargetAudioStream => MediaSource?.GetDefaultAudioStream(AudioStreamIndex);
/// <summary>
/// Gets the video stream that will be used.
/// </summary>
public MediaStream TargetVideoStream => MediaSource?.VideoStream;
public MediaStream? TargetVideoStream => MediaSource?.VideoStream;
/// <summary>
/// Gets the audio sample rate that will be in the output stream.
@ -259,7 +259,7 @@ namespace MediaBrowser.Model.Dlna
/// <summary>
/// Gets the audio sample rate that will be in the output stream.
/// </summary>
public string TargetVideoProfile
public string? TargetVideoProfile
{
get
{
@ -307,7 +307,7 @@ namespace MediaBrowser.Model.Dlna
/// Gets the target video codec tag.
/// </summary>
/// <value>The target video codec tag.</value>
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<NameValuePair> BuildParams(StreamInfo item, string accessToken)
private static IEnumerable<NameValuePair> BuildParams(StreamInfo item, string? accessToken)
{
var list = new List<NameValuePair>();
@ -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<SubtitleStreamInfo> GetSubtitleProfiles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, string baseUrl, string accessToken)
public IEnumerable<SubtitleStreamInfo> GetSubtitleProfiles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, string baseUrl, string? accessToken)
{
return GetSubtitleProfiles(transcoderSupport, includeSelectedTrackOnly, false, baseUrl, accessToken);
}
public IEnumerable<SubtitleStreamInfo> GetSubtitleProfiles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, bool enableAllProfiles, string baseUrl, string accessToken)
public IEnumerable<SubtitleStreamInfo> GetSubtitleProfiles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, bool enableAllProfiles, string baseUrl, string? accessToken)
{
if (MediaSource is null)
{
return Enumerable.Empty<SubtitleStreamInfo>();
}
var list = new List<SubtitleStreamInfo>();
// 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<SubtitleStreamInfo> list, MediaStream stream, ITranscoderSupport transcoderSupport, bool enableAllProfiles, string baseUrl, string accessToken, long startPositionTicks)
private void AddSubtitleProfiles(List<SubtitleStreamInfo> 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)
{

View File

@ -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<string>());
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<string>());
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);
}