2
0
Эх сурвалжийг харах

Fix nullability errors in Jellyfin.Api (part 1)

crobibero 4 жил өмнө
parent
commit
01355e0498
29 өөрчлөгдсөн 155 нэмэгдсэн , 54 устгасан
  1. 11 1
      Jellyfin.Api/Controllers/DynamicHlsController.cs
  2. 5 0
      Jellyfin.Api/Controllers/EnvironmentController.cs
  3. 1 1
      Jellyfin.Api/Controllers/GenresController.cs
  4. 5 0
      Jellyfin.Api/Controllers/HlsSegmentController.cs
  5. 1 1
      Jellyfin.Api/Controllers/ImageByNameController.cs
  6. 2 1
      Jellyfin.Api/Controllers/ImageController.cs
  7. 19 2
      Jellyfin.Api/Controllers/ItemLookupController.cs
  8. 3 3
      Jellyfin.Api/Controllers/LibraryController.cs
  9. 1 1
      Jellyfin.Api/Controllers/LiveTvController.cs
  10. 2 2
      Jellyfin.Api/Controllers/MusicGenresController.cs
  11. 5 0
      Jellyfin.Api/Controllers/PackageController.cs
  12. 23 5
      Jellyfin.Api/Controllers/RemoteImageController.cs
  13. 1 1
      Jellyfin.Api/Controllers/SearchController.cs
  14. 7 1
      Jellyfin.Api/Controllers/VideoHlsController.cs
  15. 7 1
      Jellyfin.Api/Helpers/AudioHelper.cs
  16. 8 3
      Jellyfin.Api/Helpers/DynamicHlsHelper.cs
  17. 2 2
      Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs
  18. 3 3
      Jellyfin.Api/Helpers/HlsCodecStringHelpers.cs
  19. 4 0
      Jellyfin.Api/Helpers/HlsHelpers.cs
  20. 5 0
      Jellyfin.Api/Helpers/ProgressiveFileCopier.cs
  21. 4 0
      Jellyfin.Api/Helpers/StreamingHelpers.cs
  22. 18 8
      Jellyfin.Api/Helpers/TranscodingJobHelper.cs
  23. 2 2
      Jellyfin.Api/Models/PlaybackDtos/TranscodingJobDto.cs
  24. 1 1
      Jellyfin.Api/Models/PlaybackDtos/TranscodingThrottler.cs
  25. 1 1
      Jellyfin.Api/WebSocketListeners/ActivityLogWebSocketListener.cs
  26. 3 3
      Jellyfin.Api/WebSocketListeners/ScheduledTasksWebSocketListener.cs
  27. 7 7
      Jellyfin.Api/WebSocketListeners/SessionInfoWebSocketListener.cs
  28. 1 1
      MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
  29. 3 3
      MediaBrowser.Model/Net/MimeTypes.cs

+ 11 - 1
Jellyfin.Api/Controllers/DynamicHlsController.cs

@@ -1347,7 +1347,13 @@ namespace Jellyfin.Api.Controllers
 
 
             var mapArgs = state.IsOutputVideo ? _encodingHelper.GetMapArgs(state) : string.Empty;
             var mapArgs = state.IsOutputVideo ? _encodingHelper.GetMapArgs(state) : string.Empty;
 
 
-            var outputTsArg = Path.Combine(Path.GetDirectoryName(outputPath), Path.GetFileNameWithoutExtension(outputPath)) + "%d" + GetSegmentFileExtension(state.Request.SegmentContainer);
+            var directory = Path.GetDirectoryName(outputPath);
+            if (directory == null)
+            {
+                throw new NullReferenceException(nameof(directory));
+            }
+
+            var outputTsArg = Path.Combine(directory, Path.GetFileNameWithoutExtension(outputPath)) + "%d" + GetSegmentFileExtension(state.Request.SegmentContainer);
 
 
             var segmentFormat = GetSegmentFileExtension(state.Request.SegmentContainer).TrimStart('.');
             var segmentFormat = GetSegmentFileExtension(state.Request.SegmentContainer).TrimStart('.');
             if (string.Equals(segmentFormat, "ts", StringComparison.OrdinalIgnoreCase))
             if (string.Equals(segmentFormat, "ts", StringComparison.OrdinalIgnoreCase))
@@ -1566,6 +1572,10 @@ namespace Jellyfin.Api.Controllers
         private string GetSegmentPath(StreamState state, string playlist, int index)
         private string GetSegmentPath(StreamState state, string playlist, int index)
         {
         {
             var folder = Path.GetDirectoryName(playlist);
             var folder = Path.GetDirectoryName(playlist);
+            if (folder == null)
+            {
+                throw new NullReferenceException(nameof(folder));
+            }
 
 
             var filename = Path.GetFileNameWithoutExtension(playlist);
             var filename = Path.GetFileNameWithoutExtension(playlist);
 
 

+ 5 - 0
Jellyfin.Api/Controllers/EnvironmentController.cs

@@ -103,6 +103,11 @@ namespace Jellyfin.Api.Controllers
 
 
                 if (validatePathDto.ValidateWritable)
                 if (validatePathDto.ValidateWritable)
                 {
                 {
+                    if (validatePathDto.Path == null)
+                    {
+                        throw new NullReferenceException(nameof(validatePathDto.Path));
+                    }
+
                     var file = Path.Combine(validatePathDto.Path, Guid.NewGuid().ToString());
                     var file = Path.Combine(validatePathDto.Path, Guid.NewGuid().ToString());
                     try
                     try
                     {
                     {

+ 1 - 1
Jellyfin.Api/Controllers/GenresController.cs

@@ -177,7 +177,7 @@ namespace Jellyfin.Api.Controllers
             return _dtoService.GetBaseItemDto(item, dtoOptions);
             return _dtoService.GetBaseItemDto(item, dtoOptions);
         }
         }
 
 
-        private T GetItemFromSlugName<T>(ILibraryManager libraryManager, string name, DtoOptions dtoOptions)
+        private T? GetItemFromSlugName<T>(ILibraryManager libraryManager, string name, DtoOptions dtoOptions)
             where T : BaseItem, new()
             where T : BaseItem, new()
         {
         {
             var result = libraryManager.GetItemList(new InternalItemsQuery
             var result = libraryManager.GetItemList(new InternalItemsQuery

+ 5 - 0
Jellyfin.Api/Controllers/HlsSegmentController.cs

@@ -136,6 +136,11 @@ namespace Jellyfin.Api.Controllers
                     string.Equals(Path.GetExtension(i), ".m3u8", StringComparison.OrdinalIgnoreCase)
                     string.Equals(Path.GetExtension(i), ".m3u8", StringComparison.OrdinalIgnoreCase)
                     && i.IndexOf(normalizedPlaylistId, StringComparison.OrdinalIgnoreCase) != -1);
                     && i.IndexOf(normalizedPlaylistId, StringComparison.OrdinalIgnoreCase) != -1);
 
 
+            if (playlistPath == null)
+            {
+                throw new NullReferenceException(nameof(playlistPath));
+            }
+
             return GetFileResult(file, playlistPath);
             return GetFileResult(file, playlistPath);
         }
         }
 
 

+ 1 - 1
Jellyfin.Api/Controllers/ImageByNameController.cs

@@ -161,7 +161,7 @@ namespace Jellyfin.Api.Controllers
         /// <param name="theme">Theme to search.</param>
         /// <param name="theme">Theme to search.</param>
         /// <param name="name">File name to search for.</param>
         /// <param name="name">File name to search for.</param>
         /// <returns>A <see cref="FileStreamResult"/> containing the image contents on success, or a <see cref="NotFoundResult"/> if the image could not be found.</returns>
         /// <returns>A <see cref="FileStreamResult"/> containing the image contents on success, or a <see cref="NotFoundResult"/> if the image could not be found.</returns>
-        private ActionResult GetImageFile(string basePath, string? theme, string? name)
+        private ActionResult GetImageFile(string basePath, string theme, string? name)
         {
         {
             var themeFolder = Path.Combine(basePath, theme);
             var themeFolder = Path.Combine(basePath, theme);
             if (Directory.Exists(themeFolder))
             if (Directory.Exists(themeFolder))

+ 2 - 1
Jellyfin.Api/Controllers/ImageController.cs

@@ -5,6 +5,7 @@ using System.Diagnostics.CodeAnalysis;
 using System.Globalization;
 using System.Globalization;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
+using System.Net.Mime;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using Jellyfin.Api.Attributes;
 using Jellyfin.Api.Attributes;
@@ -1268,7 +1269,7 @@ namespace Jellyfin.Api.Controllers
                 Response.Headers.Add(key, value);
                 Response.Headers.Add(key, value);
             }
             }
 
 
-            Response.ContentType = imageContentType;
+            Response.ContentType = imageContentType ?? MediaTypeNames.Text.Plain;
             Response.Headers.Add(HeaderNames.Age, Convert.ToInt64((DateTime.UtcNow - dateImageModified).TotalSeconds).ToString(CultureInfo.InvariantCulture));
             Response.Headers.Add(HeaderNames.Age, Convert.ToInt64((DateTime.UtcNow - dateImageModified).TotalSeconds).ToString(CultureInfo.InvariantCulture));
             Response.Headers.Add(HeaderNames.Vary, HeaderNames.Accept);
             Response.Headers.Add(HeaderNames.Vary, HeaderNames.Accept);
 
 

+ 19 - 2
Jellyfin.Api/Controllers/ItemLookupController.cs

@@ -334,10 +334,21 @@ namespace Jellyfin.Api.Controllers
         private async Task DownloadImage(string providerName, string url, Guid urlHash, string pointerCachePath)
         private async Task DownloadImage(string providerName, string url, Guid urlHash, string pointerCachePath)
         {
         {
             using var result = await _providerManager.GetSearchImage(providerName, url, CancellationToken.None).ConfigureAwait(false);
             using var result = await _providerManager.GetSearchImage(providerName, url, CancellationToken.None).ConfigureAwait(false);
+            if (result.Content.Headers.ContentType?.MediaType == null)
+            {
+                throw new NullReferenceException(nameof(result.Content.Headers.ContentType));
+            }
+
             var ext = result.Content.Headers.ContentType.MediaType.Split('/')[^1];
             var ext = result.Content.Headers.ContentType.MediaType.Split('/')[^1];
             var fullCachePath = GetFullCachePath(urlHash + "." + ext);
             var fullCachePath = GetFullCachePath(urlHash + "." + ext);
 
 
-            Directory.CreateDirectory(Path.GetDirectoryName(fullCachePath));
+            var directory = Path.GetDirectoryName(fullCachePath);
+            if (directory == null)
+            {
+                throw new NullReferenceException(nameof(directory));
+            }
+
+            Directory.CreateDirectory(directory);
             using (var stream = result.Content)
             using (var stream = result.Content)
             {
             {
                 await using var fileStream = new FileStream(
                 await using var fileStream = new FileStream(
@@ -351,7 +362,13 @@ namespace Jellyfin.Api.Controllers
                 await stream.CopyToAsync(fileStream).ConfigureAwait(false);
                 await stream.CopyToAsync(fileStream).ConfigureAwait(false);
             }
             }
 
 
-            Directory.CreateDirectory(Path.GetDirectoryName(pointerCachePath));
+            var pointerCacheDirectory = Path.GetDirectoryName(pointerCachePath);
+            if (pointerCacheDirectory == null)
+            {
+                throw new NullReferenceException(nameof(pointerCacheDirectory));
+            }
+
+            Directory.CreateDirectory(pointerCacheDirectory);
             await System.IO.File.WriteAllTextAsync(pointerCachePath, fullCachePath).ConfigureAwait(false);
             await System.IO.File.WriteAllTextAsync(pointerCachePath, fullCachePath).ConfigureAwait(false);
         }
         }
 
 

+ 3 - 3
Jellyfin.Api/Controllers/LibraryController.cs

@@ -455,7 +455,7 @@ namespace Jellyfin.Api.Controllers
                 : null;
                 : null;
 
 
             var dtoOptions = new DtoOptions().AddClientFields(Request);
             var dtoOptions = new DtoOptions().AddClientFields(Request);
-            BaseItem parent = item.GetParent();
+            BaseItem? parent = item.GetParent();
 
 
             while (parent != null)
             while (parent != null)
             {
             {
@@ -466,7 +466,7 @@ namespace Jellyfin.Api.Controllers
 
 
                 baseItemDtos.Add(_dtoService.GetBaseItemDto(parent, dtoOptions, user));
                 baseItemDtos.Add(_dtoService.GetBaseItemDto(parent, dtoOptions, user));
 
 
-                parent = parent.GetParent();
+                parent = parent?.GetParent();
             }
             }
 
 
             return baseItemDtos;
             return baseItemDtos;
@@ -854,7 +854,7 @@ namespace Jellyfin.Api.Controllers
             return _libraryManager.GetItemsResult(query).TotalRecordCount;
             return _libraryManager.GetItemsResult(query).TotalRecordCount;
         }
         }
 
 
-        private BaseItem TranslateParentItem(BaseItem item, User user)
+        private BaseItem? TranslateParentItem(BaseItem item, User user)
         {
         {
             return item.GetParent() is AggregateFolder
             return item.GetParent() is AggregateFolder
                 ? _libraryManager.GetUserRootFolder().GetChildren(user, true)
                 ? _libraryManager.GetUserRootFolder().GetChildren(user, true)

+ 1 - 1
Jellyfin.Api/Controllers/LiveTvController.cs

@@ -1078,7 +1078,7 @@ namespace Jellyfin.Api.Controllers
             var client = _httpClientFactory.CreateClient(NamedClient.Default);
             var client = _httpClientFactory.CreateClient(NamedClient.Default);
             // https://json.schedulesdirect.org/20141201/available/countries
             // https://json.schedulesdirect.org/20141201/available/countries
             // Can't dispose the response as it's required up the call chain.
             // Can't dispose the response as it's required up the call chain.
-            var response = await client.GetAsync("https://json.schedulesdirect.org/20141201/available/countries")
+            var response = await client.GetAsync(new Uri("https://json.schedulesdirect.org/20141201/available/countries"))
                 .ConfigureAwait(false);
                 .ConfigureAwait(false);
 
 
             return File(await response.Content.ReadAsStreamAsync().ConfigureAwait(false), MediaTypeNames.Application.Json);
             return File(await response.Content.ReadAsStreamAsync().ConfigureAwait(false), MediaTypeNames.Application.Json);

+ 2 - 2
Jellyfin.Api/Controllers/MusicGenresController.cs

@@ -140,7 +140,7 @@ namespace Jellyfin.Api.Controllers
         {
         {
             var dtoOptions = new DtoOptions().AddClientFields(Request);
             var dtoOptions = new DtoOptions().AddClientFields(Request);
 
 
-            MusicGenre item;
+            MusicGenre? item;
 
 
             if (genreName.IndexOf(BaseItem.SlugChar, StringComparison.OrdinalIgnoreCase) != -1)
             if (genreName.IndexOf(BaseItem.SlugChar, StringComparison.OrdinalIgnoreCase) != -1)
             {
             {
@@ -161,7 +161,7 @@ namespace Jellyfin.Api.Controllers
             return _dtoService.GetBaseItemDto(item, dtoOptions);
             return _dtoService.GetBaseItemDto(item, dtoOptions);
         }
         }
 
 
-        private T GetItemFromSlugName<T>(ILibraryManager libraryManager, string name, DtoOptions dtoOptions)
+        private T? GetItemFromSlugName<T>(ILibraryManager libraryManager, string name, DtoOptions dtoOptions)
             where T : BaseItem, new()
             where T : BaseItem, new()
         {
         {
             var result = libraryManager.GetItemList(new InternalItemsQuery
             var result = libraryManager.GetItemList(new InternalItemsQuery

+ 5 - 0
Jellyfin.Api/Controllers/PackageController.cs

@@ -54,6 +54,11 @@ namespace Jellyfin.Api.Controllers
                     string.IsNullOrEmpty(assemblyGuid) ? default : Guid.Parse(assemblyGuid))
                     string.IsNullOrEmpty(assemblyGuid) ? default : Guid.Parse(assemblyGuid))
                 .FirstOrDefault();
                 .FirstOrDefault();
 
 
+            if (result == null)
+            {
+                return NotFound();
+            }
+
             return result;
             return result;
         }
         }
 
 

+ 23 - 5
Jellyfin.Api/Controllers/RemoteImageController.cs

@@ -157,9 +157,9 @@ namespace Jellyfin.Api.Controllers
         [ProducesResponseType(StatusCodes.Status200OK)]
         [ProducesResponseType(StatusCodes.Status200OK)]
         [ProducesResponseType(StatusCodes.Status404NotFound)]
         [ProducesResponseType(StatusCodes.Status404NotFound)]
         [ProducesImageFile]
         [ProducesImageFile]
-        public async Task<ActionResult> GetRemoteImage([FromQuery, Required] string imageUrl)
+        public async Task<ActionResult> GetRemoteImage([FromQuery, Required] Uri imageUrl)
         {
         {
-            var urlHash = imageUrl.GetMD5();
+            var urlHash = imageUrl.ToString().GetMD5();
             var pointerCachePath = GetFullCachePath(urlHash.ToString());
             var pointerCachePath = GetFullCachePath(urlHash.ToString());
 
 
             string? contentPath = null;
             string? contentPath = null;
@@ -245,17 +245,35 @@ namespace Jellyfin.Api.Controllers
         /// <param name="urlHash">The URL hash.</param>
         /// <param name="urlHash">The URL hash.</param>
         /// <param name="pointerCachePath">The pointer cache path.</param>
         /// <param name="pointerCachePath">The pointer cache path.</param>
         /// <returns>Task.</returns>
         /// <returns>Task.</returns>
-        private async Task DownloadImage(string url, Guid urlHash, string pointerCachePath)
+        private async Task DownloadImage(Uri url, Guid urlHash, string pointerCachePath)
         {
         {
             var httpClient = _httpClientFactory.CreateClient(NamedClient.Default);
             var httpClient = _httpClientFactory.CreateClient(NamedClient.Default);
             using var response = await httpClient.GetAsync(url).ConfigureAwait(false);
             using var response = await httpClient.GetAsync(url).ConfigureAwait(false);
+            if (response.Content.Headers.ContentType?.MediaType == null)
+            {
+                throw new NullReferenceException(nameof(response.Content.Headers.ContentType));
+            }
+
             var ext = response.Content.Headers.ContentType.MediaType.Split('/').Last();
             var ext = response.Content.Headers.ContentType.MediaType.Split('/').Last();
             var fullCachePath = GetFullCachePath(urlHash + "." + ext);
             var fullCachePath = GetFullCachePath(urlHash + "." + ext);
 
 
-            Directory.CreateDirectory(Path.GetDirectoryName(fullCachePath));
+            var fullCacheDirectory = Path.GetDirectoryName(fullCachePath);
+            if (fullCacheDirectory == null)
+            {
+                throw new NullReferenceException(nameof(fullCacheDirectory));
+            }
+
+            Directory.CreateDirectory(fullCacheDirectory);
             await using var fileStream = new FileStream(fullCachePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true);
             await using var fileStream = new FileStream(fullCachePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true);
             await response.Content.CopyToAsync(fileStream).ConfigureAwait(false);
             await response.Content.CopyToAsync(fileStream).ConfigureAwait(false);
-            Directory.CreateDirectory(Path.GetDirectoryName(pointerCachePath));
+
+            var pointerCacheDirectory = Path.GetDirectoryName(pointerCachePath);
+            if (pointerCacheDirectory == null)
+            {
+                throw new NullReferenceException(nameof(pointerCacheDirectory));
+            }
+
+            Directory.CreateDirectory(pointerCacheDirectory);
             await System.IO.File.WriteAllTextAsync(pointerCachePath, fullCachePath, CancellationToken.None)
             await System.IO.File.WriteAllTextAsync(pointerCachePath, fullCachePath, CancellationToken.None)
                 .ConfigureAwait(false);
                 .ConfigureAwait(false);
         }
         }

+ 1 - 1
Jellyfin.Api/Controllers/SearchController.cs

@@ -260,7 +260,7 @@ namespace Jellyfin.Api.Controllers
             }
             }
         }
         }
 
 
-        private T GetParentWithImage<T>(BaseItem item, ImageType type)
+        private T? GetParentWithImage<T>(BaseItem item, ImageType type)
             where T : BaseItem
             where T : BaseItem
         {
         {
             return item.GetParents().OfType<T>().FirstOrDefault(i => i.HasImage(type));
             return item.GetParents().OfType<T>().FirstOrDefault(i => i.HasImage(type));

+ 7 - 1
Jellyfin.Api/Controllers/VideoHlsController.cs

@@ -361,7 +361,13 @@ namespace Jellyfin.Api.Controllers
             var threads = _encodingHelper.GetNumberOfThreads(state, _encodingOptions, videoCodec);
             var threads = _encodingHelper.GetNumberOfThreads(state, _encodingOptions, videoCodec);
             var inputModifier = _encodingHelper.GetInputModifier(state, _encodingOptions);
             var inputModifier = _encodingHelper.GetInputModifier(state, _encodingOptions);
             var format = !string.IsNullOrWhiteSpace(state.Request.SegmentContainer) ? "." + state.Request.SegmentContainer : ".ts";
             var format = !string.IsNullOrWhiteSpace(state.Request.SegmentContainer) ? "." + state.Request.SegmentContainer : ".ts";
-            var outputTsArg = Path.Combine(Path.GetDirectoryName(outputPath), Path.GetFileNameWithoutExtension(outputPath)) + "%d" + format;
+            var directory = Path.GetDirectoryName(outputPath);
+            if (directory == null)
+            {
+                throw new NullReferenceException(nameof(directory));
+            }
+
+            var outputTsArg = Path.Combine(directory, Path.GetFileNameWithoutExtension(outputPath)) + "%d" + format;
 
 
             var segmentFormat = format.TrimStart('.');
             var segmentFormat = format.TrimStart('.');
             if (string.Equals(segmentFormat, "ts", StringComparison.OrdinalIgnoreCase))
             if (string.Equals(segmentFormat, "ts", StringComparison.OrdinalIgnoreCase))

+ 7 - 1
Jellyfin.Api/Helpers/AudioHelper.cs

@@ -1,4 +1,5 @@
-using System.Net.Http;
+using System;
+using System.Net.Http;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using Jellyfin.Api.Models.StreamingDtos;
 using Jellyfin.Api.Models.StreamingDtos;
@@ -98,6 +99,11 @@ namespace Jellyfin.Api.Helpers
             TranscodingJobType transcodingJobType,
             TranscodingJobType transcodingJobType,
             StreamingRequestDto streamingRequest)
             StreamingRequestDto streamingRequest)
         {
         {
+            if (_httpContextAccessor.HttpContext == null)
+            {
+                throw new NullReferenceException(nameof(_httpContextAccessor.HttpContext));
+            }
+
             bool isHeadRequest = _httpContextAccessor.HttpContext.Request.Method == System.Net.WebRequestMethods.Http.Head;
             bool isHeadRequest = _httpContextAccessor.HttpContext.Request.Method == System.Net.WebRequestMethods.Http.Head;
             var cancellationTokenSource = new CancellationTokenSource();
             var cancellationTokenSource = new CancellationTokenSource();
 
 

+ 8 - 3
Jellyfin.Api/Helpers/DynamicHlsHelper.cs

@@ -113,7 +113,7 @@ namespace Jellyfin.Api.Helpers
             StreamingRequestDto streamingRequest,
             StreamingRequestDto streamingRequest,
             bool enableAdaptiveBitrateStreaming)
             bool enableAdaptiveBitrateStreaming)
         {
         {
-            var isHeadRequest = _httpContextAccessor.HttpContext.Request.Method == WebRequestMethods.Http.Head;
+            var isHeadRequest = _httpContextAccessor.HttpContext?.Request.Method == WebRequestMethods.Http.Head;
             var cancellationTokenSource = new CancellationTokenSource();
             var cancellationTokenSource = new CancellationTokenSource();
             return await GetMasterPlaylistInternal(
             return await GetMasterPlaylistInternal(
                 streamingRequest,
                 streamingRequest,
@@ -130,6 +130,11 @@ namespace Jellyfin.Api.Helpers
             TranscodingJobType transcodingJobType,
             TranscodingJobType transcodingJobType,
             CancellationTokenSource cancellationTokenSource)
             CancellationTokenSource cancellationTokenSource)
         {
         {
+            if (_httpContextAccessor.HttpContext == null)
+            {
+                throw new NullReferenceException(nameof(_httpContextAccessor.HttpContext));
+            }
+
             using var state = await StreamingHelpers.GetStreamingState(
             using var state = await StreamingHelpers.GetStreamingState(
                     streamingRequest,
                     streamingRequest,
                     _httpContextAccessor.HttpContext.Request,
                     _httpContextAccessor.HttpContext.Request,
@@ -487,14 +492,14 @@ namespace Jellyfin.Api.Helpers
 
 
             if (string.Equals(codec, "h264", StringComparison.OrdinalIgnoreCase))
             if (string.Equals(codec, "h264", StringComparison.OrdinalIgnoreCase))
             {
             {
-                string profile = state.GetRequestedProfiles("h264").FirstOrDefault();
+                string? profile = state.GetRequestedProfiles("h264").FirstOrDefault();
                 return HlsCodecStringHelpers.GetH264String(profile, level);
                 return HlsCodecStringHelpers.GetH264String(profile, level);
             }
             }
 
 
             if (string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase)
             if (string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase)
                 || string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase))
                 || string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase))
             {
             {
-                string profile = state.GetRequestedProfiles("h265").FirstOrDefault();
+                string? profile = state.GetRequestedProfiles("h265").FirstOrDefault();
 
 
                 return HlsCodecStringHelpers.GetH265String(profile, level);
                 return HlsCodecStringHelpers.GetH265String(profile, level);
             }
             }

+ 2 - 2
Jellyfin.Api/Helpers/FileStreamResponseHelpers.cs

@@ -37,8 +37,8 @@ namespace Jellyfin.Api.Helpers
             }
             }
 
 
             // Can't dispose the response as it's required up the call chain.
             // Can't dispose the response as it's required up the call chain.
-            var response = await httpClient.GetAsync(state.MediaPath).ConfigureAwait(false);
-            var contentType = response.Content.Headers.ContentType.ToString();
+            var response = await httpClient.GetAsync(new Uri(state.MediaPath)).ConfigureAwait(false);
+            var contentType = response.Content.Headers.ContentType?.ToString();
 
 
             httpContext.Response.Headers[HeaderNames.AcceptRanges] = "none";
             httpContext.Response.Headers[HeaderNames.AcceptRanges] = "none";
 
 

+ 3 - 3
Jellyfin.Api/Helpers/HlsCodecStringHelpers.cs

@@ -23,7 +23,7 @@ namespace Jellyfin.Api.Helpers
         /// </summary>
         /// </summary>
         /// <param name="profile">AAC profile.</param>
         /// <param name="profile">AAC profile.</param>
         /// <returns>AAC codec string.</returns>
         /// <returns>AAC codec string.</returns>
-        public static string GetAACString(string profile)
+        public static string GetAACString(string? profile)
         {
         {
             StringBuilder result = new StringBuilder("mp4a", 9);
             StringBuilder result = new StringBuilder("mp4a", 9);
 
 
@@ -46,7 +46,7 @@ namespace Jellyfin.Api.Helpers
         /// <param name="profile">H.264 profile.</param>
         /// <param name="profile">H.264 profile.</param>
         /// <param name="level">H.264 level.</param>
         /// <param name="level">H.264 level.</param>
         /// <returns>H.264 string.</returns>
         /// <returns>H.264 string.</returns>
-        public static string GetH264String(string profile, int level)
+        public static string GetH264String(string? profile, int level)
         {
         {
             StringBuilder result = new StringBuilder("avc1", 11);
             StringBuilder result = new StringBuilder("avc1", 11);
 
 
@@ -80,7 +80,7 @@ namespace Jellyfin.Api.Helpers
         /// <param name="profile">H.265 profile.</param>
         /// <param name="profile">H.265 profile.</param>
         /// <param name="level">H.265 level.</param>
         /// <param name="level">H.265 level.</param>
         /// <returns>H.265 string.</returns>
         /// <returns>H.265 string.</returns>
-        public static string GetH265String(string profile, int level)
+        public static string GetH265String(string? profile, int level)
         {
         {
             // The h265 syntax is a bit of a mystery at the time this comment was written.
             // The h265 syntax is a bit of a mystery at the time this comment was written.
             // This is what I've found through various sources:
             // This is what I've found through various sources:

+ 4 - 0
Jellyfin.Api/Helpers/HlsHelpers.cs

@@ -45,6 +45,10 @@ namespace Jellyfin.Api.Helpers
                         while (!reader.EndOfStream)
                         while (!reader.EndOfStream)
                         {
                         {
                             var line = await reader.ReadLineAsync().ConfigureAwait(false);
                             var line = await reader.ReadLineAsync().ConfigureAwait(false);
+                            if (line == null)
+                            {
+                                throw new NullReferenceException(nameof(line));
+                            }
 
 
                             if (line.IndexOf("#EXTINF:", StringComparison.OrdinalIgnoreCase) != -1)
                             if (line.IndexOf("#EXTINF:", StringComparison.OrdinalIgnoreCase) != -1)
                             {
                             {

+ 5 - 0
Jellyfin.Api/Helpers/ProgressiveFileCopier.cs

@@ -90,6 +90,11 @@ namespace Jellyfin.Api.Helpers
                     allowAsyncFileRead = true;
                     allowAsyncFileRead = true;
                 }
                 }
 
 
+                if (_path == null)
+                {
+                    throw new NullReferenceException(nameof(_path));
+                }
+
                 await using var inputStream = new FileStream(_path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, IODefaults.FileStreamBufferSize, fileOptions);
                 await using var inputStream = new FileStream(_path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, IODefaults.FileStreamBufferSize, fileOptions);
 
 
                 var eofCount = 0;
                 var eofCount = 0;

+ 4 - 0
Jellyfin.Api/Helpers/StreamingHelpers.cs

@@ -83,6 +83,10 @@ namespace Jellyfin.Api.Helpers
             }
             }
 
 
             streamingRequest.StreamOptions = ParseStreamOptions(httpRequest.Query);
             streamingRequest.StreamOptions = ParseStreamOptions(httpRequest.Query);
+            if (httpRequest.Path.Value == null)
+            {
+                throw new NullReferenceException(nameof(httpRequest.Path));
+            }
 
 
             var url = httpRequest.Path.Value.Split('.').Last();
             var url = httpRequest.Path.Value.Split('.').Last();
 
 

+ 18 - 8
Jellyfin.Api/Helpers/TranscodingJobHelper.cs

@@ -102,7 +102,7 @@ namespace Jellyfin.Api.Helpers
         /// </summary>
         /// </summary>
         /// <param name="playSessionId">Playback session id.</param>
         /// <param name="playSessionId">Playback session id.</param>
         /// <returns>The transcoding job.</returns>
         /// <returns>The transcoding job.</returns>
-        public TranscodingJobDto GetTranscodingJob(string playSessionId)
+        public TranscodingJobDto? GetTranscodingJob(string playSessionId)
         {
         {
             lock (_activeTranscodingJobs)
             lock (_activeTranscodingJobs)
             {
             {
@@ -116,7 +116,7 @@ namespace Jellyfin.Api.Helpers
         /// <param name="path">Path to the transcoding file.</param>
         /// <param name="path">Path to the transcoding file.</param>
         /// <param name="type">The <see cref="TranscodingJobType"/>.</param>
         /// <param name="type">The <see cref="TranscodingJobType"/>.</param>
         /// <returns>The transcoding job.</returns>
         /// <returns>The transcoding job.</returns>
-        public TranscodingJobDto GetTranscodingJob(string path, TranscodingJobType type)
+        public TranscodingJobDto? GetTranscodingJob(string path, TranscodingJobType type)
         {
         {
             lock (_activeTranscodingJobs)
             lock (_activeTranscodingJobs)
             {
             {
@@ -193,9 +193,13 @@ namespace Jellyfin.Api.Helpers
         /// Called when [transcode kill timer stopped].
         /// Called when [transcode kill timer stopped].
         /// </summary>
         /// </summary>
         /// <param name="state">The state.</param>
         /// <param name="state">The state.</param>
-        private async void OnTranscodeKillTimerStopped(object state)
+        private async void OnTranscodeKillTimerStopped(object? state)
         {
         {
-            var job = (TranscodingJobDto)state;
+            var job = (TranscodingJobDto?)state;
+            if (job == null)
+            {
+                throw new NullReferenceException(nameof(job));
+            }
 
 
             if (!job.HasExited && job.Type != TranscodingJobType.Progressive)
             if (!job.HasExited && job.Type != TranscodingJobType.Progressive)
             {
             {
@@ -489,7 +493,13 @@ namespace Jellyfin.Api.Helpers
             CancellationTokenSource cancellationTokenSource,
             CancellationTokenSource cancellationTokenSource,
             string? workingDirectory = null)
             string? workingDirectory = null)
         {
         {
-            Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
+            var directory = Path.GetDirectoryName(outputPath);
+            if (directory == null)
+            {
+                throw new NullReferenceException(nameof(directory));
+            }
+
+            Directory.CreateDirectory(directory);
 
 
             await AcquireResources(state, cancellationTokenSource).ConfigureAwait(false);
             await AcquireResources(state, cancellationTokenSource).ConfigureAwait(false);
 
 
@@ -523,7 +533,7 @@ namespace Jellyfin.Api.Helpers
                     RedirectStandardInput = true,
                     RedirectStandardInput = true,
                     FileName = _mediaEncoder.EncoderPath,
                     FileName = _mediaEncoder.EncoderPath,
                     Arguments = commandLineArguments,
                     Arguments = commandLineArguments,
-                    WorkingDirectory = string.IsNullOrWhiteSpace(workingDirectory) ? null : workingDirectory,
+                    WorkingDirectory = string.IsNullOrWhiteSpace(workingDirectory) ? string.Empty : workingDirectory,
                     ErrorDialog = false
                     ErrorDialog = false
                 },
                 },
                 EnableRaisingEvents = true
                 EnableRaisingEvents = true
@@ -827,7 +837,7 @@ namespace Jellyfin.Api.Helpers
         {
         {
             lock (_transcodingLocks)
             lock (_transcodingLocks)
             {
             {
-                if (!_transcodingLocks.TryGetValue(outputPath, out SemaphoreSlim result))
+                if (!_transcodingLocks.TryGetValue(outputPath, out SemaphoreSlim? result))
                 {
                 {
                     result = new SemaphoreSlim(1, 1);
                     result = new SemaphoreSlim(1, 1);
                     _transcodingLocks[outputPath] = result;
                     _transcodingLocks[outputPath] = result;
@@ -837,7 +847,7 @@ namespace Jellyfin.Api.Helpers
             }
             }
         }
         }
 
 
-        private void OnPlaybackProgress(object sender, PlaybackProgressEventArgs e)
+        private void OnPlaybackProgress(object? sender, PlaybackProgressEventArgs e)
         {
         {
             if (!string.IsNullOrWhiteSpace(e.PlaySessionId))
             if (!string.IsNullOrWhiteSpace(e.PlaySessionId))
             {
             {

+ 2 - 2
Jellyfin.Api/Models/PlaybackDtos/TranscodingJobDto.cs

@@ -196,7 +196,7 @@ namespace Jellyfin.Api.Models.PlaybackDtos
         /// Start kill timer.
         /// Start kill timer.
         /// </summary>
         /// </summary>
         /// <param name="callback">Callback action.</param>
         /// <param name="callback">Callback action.</param>
-        public void StartKillTimer(Action<object> callback)
+        public void StartKillTimer(Action<object?> callback)
         {
         {
             StartKillTimer(callback, PingTimeout);
             StartKillTimer(callback, PingTimeout);
         }
         }
@@ -206,7 +206,7 @@ namespace Jellyfin.Api.Models.PlaybackDtos
         /// </summary>
         /// </summary>
         /// <param name="callback">Callback action.</param>
         /// <param name="callback">Callback action.</param>
         /// <param name="intervalMs">Callback interval.</param>
         /// <param name="intervalMs">Callback interval.</param>
-        public void StartKillTimer(Action<object> callback, int intervalMs)
+        public void StartKillTimer(Action<object?> callback, int intervalMs)
         {
         {
             if (HasExited)
             if (HasExited)
             {
             {

+ 1 - 1
Jellyfin.Api/Models/PlaybackDtos/TranscodingThrottler.cs

@@ -101,7 +101,7 @@ namespace Jellyfin.Api.Models.PlaybackDtos
             return _config.GetConfiguration<EncodingOptions>("encoding");
             return _config.GetConfiguration<EncodingOptions>("encoding");
         }
         }
 
 
-        private async void TimerCallback(object state)
+        private async void TimerCallback(object? state)
         {
         {
             if (_job.HasExited)
             if (_job.HasExited)
             {
             {

+ 1 - 1
Jellyfin.Api/WebSocketListeners/ActivityLogWebSocketListener.cs

@@ -56,7 +56,7 @@ namespace Jellyfin.Api.WebSocketListeners
             base.Dispose(dispose);
             base.Dispose(dispose);
         }
         }
 
 
-        private void OnEntryCreated(object sender, GenericEventArgs<ActivityLogEntry> e)
+        private void OnEntryCreated(object? sender, GenericEventArgs<ActivityLogEntry> e)
         {
         {
             SendData(true);
             SendData(true);
         }
         }

+ 3 - 3
Jellyfin.Api/WebSocketListeners/ScheduledTasksWebSocketListener.cs

@@ -64,19 +64,19 @@ namespace Jellyfin.Api.WebSocketListeners
             base.Dispose(dispose);
             base.Dispose(dispose);
         }
         }
 
 
-        private void OnTaskCompleted(object sender, TaskCompletionEventArgs e)
+        private void OnTaskCompleted(object? sender, TaskCompletionEventArgs e)
         {
         {
             SendData(true);
             SendData(true);
             e.Task.TaskProgress -= OnTaskProgress;
             e.Task.TaskProgress -= OnTaskProgress;
         }
         }
 
 
-        private void OnTaskExecuting(object sender, GenericEventArgs<IScheduledTaskWorker> e)
+        private void OnTaskExecuting(object? sender, GenericEventArgs<IScheduledTaskWorker> e)
         {
         {
             SendData(true);
             SendData(true);
             e.Argument.TaskProgress += OnTaskProgress;
             e.Argument.TaskProgress += OnTaskProgress;
         }
         }
 
 
-        private void OnTaskProgress(object sender, GenericEventArgs<double> e)
+        private void OnTaskProgress(object? sender, GenericEventArgs<double> e)
         {
         {
             SendData(false);
             SendData(false);
         }
         }

+ 7 - 7
Jellyfin.Api/WebSocketListeners/SessionInfoWebSocketListener.cs

@@ -66,37 +66,37 @@ namespace Jellyfin.Api.WebSocketListeners
             base.Dispose(dispose);
             base.Dispose(dispose);
         }
         }
 
 
-        private async void OnSessionManagerSessionActivity(object sender, SessionEventArgs e)
+        private async void OnSessionManagerSessionActivity(object? sender, SessionEventArgs e)
         {
         {
             await SendData(false).ConfigureAwait(false);
             await SendData(false).ConfigureAwait(false);
         }
         }
 
 
-        private async void OnSessionManagerCapabilitiesChanged(object sender, SessionEventArgs e)
+        private async void OnSessionManagerCapabilitiesChanged(object? sender, SessionEventArgs e)
         {
         {
             await SendData(true).ConfigureAwait(false);
             await SendData(true).ConfigureAwait(false);
         }
         }
 
 
-        private async void OnSessionManagerPlaybackProgress(object sender, PlaybackProgressEventArgs e)
+        private async void OnSessionManagerPlaybackProgress(object? sender, PlaybackProgressEventArgs e)
         {
         {
             await SendData(!e.IsAutomated).ConfigureAwait(false);
             await SendData(!e.IsAutomated).ConfigureAwait(false);
         }
         }
 
 
-        private async void OnSessionManagerPlaybackStopped(object sender, PlaybackStopEventArgs e)
+        private async void OnSessionManagerPlaybackStopped(object? sender, PlaybackStopEventArgs e)
         {
         {
             await SendData(true).ConfigureAwait(false);
             await SendData(true).ConfigureAwait(false);
         }
         }
 
 
-        private async void OnSessionManagerPlaybackStart(object sender, PlaybackProgressEventArgs e)
+        private async void OnSessionManagerPlaybackStart(object? sender, PlaybackProgressEventArgs e)
         {
         {
             await SendData(true).ConfigureAwait(false);
             await SendData(true).ConfigureAwait(false);
         }
         }
 
 
-        private async void OnSessionManagerSessionEnded(object sender, SessionEventArgs e)
+        private async void OnSessionManagerSessionEnded(object? sender, SessionEventArgs e)
         {
         {
             await SendData(true).ConfigureAwait(false);
             await SendData(true).ConfigureAwait(false);
         }
         }
 
 
-        private async void OnSessionManagerSessionStarted(object sender, SessionEventArgs e)
+        private async void OnSessionManagerSessionStarted(object? sender, SessionEventArgs e)
         {
         {
             await SendData(true).ConfigureAwait(false);
             await SendData(true).ConfigureAwait(false);
         }
         }

+ 1 - 1
MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs

@@ -758,7 +758,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
                 case MediaProtocol.Http:
                 case MediaProtocol.Http:
                 {
                 {
                     using var response = await _httpClientFactory.CreateClient(NamedClient.Default)
                     using var response = await _httpClientFactory.CreateClient(NamedClient.Default)
-                        .GetAsync(path, cancellationToken)
+                        .GetAsync(new Uri(path), cancellationToken)
                         .ConfigureAwait(false);
                         .ConfigureAwait(false);
                     return await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
                     return await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
                 }
                 }

+ 3 - 3
MediaBrowser.Model/Net/MimeTypes.cs

@@ -210,9 +210,9 @@ namespace MediaBrowser.Model.Net
             return enableStreamDefault ? "application/octet-stream" : null;
             return enableStreamDefault ? "application/octet-stream" : null;
         }
         }
 
 
-        public static string? ToExtension(string mimeType)
+        public static string? ToExtension(string? mimeType)
         {
         {
-            if (mimeType.Length == 0)
+            if (string.IsNullOrEmpty(mimeType))
             {
             {
                 throw new ArgumentException("String can't be empty.", nameof(mimeType));
                 throw new ArgumentException("String can't be empty.", nameof(mimeType));
             }
             }
@@ -220,7 +220,7 @@ namespace MediaBrowser.Model.Net
             // handle text/html; charset=UTF-8
             // handle text/html; charset=UTF-8
             mimeType = mimeType.Split(';')[0];
             mimeType = mimeType.Split(';')[0];
 
 
-            if (_extensionLookup.TryGetValue(mimeType, out string result))
+            if (_extensionLookup.TryGetValue(mimeType, out string? result))
             {
             {
                 return result;
                 return result;
             }
             }