From 515ee90fb96b32d89134852b95ebcd8dbb656b94 Mon Sep 17 00:00:00 2001
From: nicknsy <20588554+nicknsy@users.noreply.github.com>
Date: Wed, 22 Feb 2023 18:17:54 -0800
Subject: Hls playlist
---
Jellyfin.Api/Controllers/DynamicHlsController.cs | 26 ++++++++++++++++++++++--
1 file changed, 24 insertions(+), 2 deletions(-)
(limited to 'Jellyfin.Api/Controllers/DynamicHlsController.cs')
diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs
index 9f2088e36..2dbc343e9 100644
--- a/Jellyfin.Api/Controllers/DynamicHlsController.cs
+++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs
@@ -407,6 +407,7 @@ public class DynamicHlsController : BaseJellyfinApiController
/// Optional. The .
/// Optional. The streaming options.
/// Enable adaptive bitrate streaming.
+ /// Enable trickplay image playlists being added to master playlist.
/// Video stream returned.
/// A containing the playlist file.
[HttpGet("Videos/{itemId}/master.m3u8")]
@@ -464,7 +465,8 @@ public class DynamicHlsController : BaseJellyfinApiController
[FromQuery] int? videoStreamIndex,
[FromQuery] EncodingContext? context,
[FromQuery] Dictionary streamOptions,
- [FromQuery] bool enableAdaptiveBitrateStreaming = true)
+ [FromQuery] bool enableAdaptiveBitrateStreaming = true,
+ [FromQuery] bool enableTrickplay = true)
{
var streamingRequest = new HlsVideoRequestDto
{
@@ -518,7 +520,8 @@ public class DynamicHlsController : BaseJellyfinApiController
VideoStreamIndex = videoStreamIndex,
Context = context ?? EncodingContext.Streaming,
StreamOptions = streamOptions,
- EnableAdaptiveBitrateStreaming = enableAdaptiveBitrateStreaming
+ EnableAdaptiveBitrateStreaming = enableAdaptiveBitrateStreaming,
+ EnableTrickplay = enableTrickplay
};
return await _dynamicHlsHelper.GetMasterHlsPlaylist(TranscodingJobType, streamingRequest, enableAdaptiveBitrateStreaming).ConfigureAwait(false);
@@ -1025,6 +1028,25 @@ public class DynamicHlsController : BaseJellyfinApiController
.ConfigureAwait(false);
}
+ ///
+ /// Gets an image tiles playlist for trickplay.
+ ///
+ /// The item id.
+ /// The width of a single tile.
+ /// The media version id.
+ /// Tiles stream returned.
+ /// A containing the trickplay tiles file.
+ [HttpGet("Videos/{itemId}/tiles.m3u8")]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesPlaylistFile]
+ public ActionResult GetTrickplayTilesHlsPlaylist(
+ [FromRoute, Required] Guid itemId,
+ [FromQuery, Required] int width,
+ [FromQuery, Required] string mediaSourceId)
+ {
+ return _dynamicHlsHelper.GetTilesHlsPlaylist(width, mediaSourceId);
+ }
+
///
/// Gets a video stream using HTTP live streaming.
///
--
cgit v1.2.3
From b18d6bd3562a5e5a69f806b461049fbf9f61b70e Mon Sep 17 00:00:00 2001
From: nicknsy <20588554+nicknsy@users.noreply.github.com>
Date: Wed, 22 Feb 2023 23:13:55 -0800
Subject: Trickplay playlist and image controller
---
Jellyfin.Api/Controllers/DynamicHlsController.cs | 19 ---
Jellyfin.Api/Controllers/TrickplayController.cs | 177 +++++++++++++++++++++++
Jellyfin.Api/Helpers/DynamicHlsHelper.cs | 77 +---------
3 files changed, 178 insertions(+), 95 deletions(-)
create mode 100644 Jellyfin.Api/Controllers/TrickplayController.cs
(limited to 'Jellyfin.Api/Controllers/DynamicHlsController.cs')
diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs
index 2dbc343e9..acd811143 100644
--- a/Jellyfin.Api/Controllers/DynamicHlsController.cs
+++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs
@@ -1028,25 +1028,6 @@ public class DynamicHlsController : BaseJellyfinApiController
.ConfigureAwait(false);
}
- ///
- /// Gets an image tiles playlist for trickplay.
- ///
- /// The item id.
- /// The width of a single tile.
- /// The media version id.
- /// Tiles stream returned.
- /// A containing the trickplay tiles file.
- [HttpGet("Videos/{itemId}/tiles.m3u8")]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesPlaylistFile]
- public ActionResult GetTrickplayTilesHlsPlaylist(
- [FromRoute, Required] Guid itemId,
- [FromQuery, Required] int width,
- [FromQuery, Required] string mediaSourceId)
- {
- return _dynamicHlsHelper.GetTilesHlsPlaylist(width, mediaSourceId);
- }
-
///
/// Gets a video stream using HTTP live streaming.
///
diff --git a/Jellyfin.Api/Controllers/TrickplayController.cs b/Jellyfin.Api/Controllers/TrickplayController.cs
new file mode 100644
index 000000000..389eb43ff
--- /dev/null
+++ b/Jellyfin.Api/Controllers/TrickplayController.cs
@@ -0,0 +1,177 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Net.Mime;
+using System.Text;
+using System.Threading.Tasks;
+using Jellyfin.Api.Attributes;
+using Jellyfin.Api.Extensions;
+using Jellyfin.Api.Helpers;
+using MediaBrowser.Common.Extensions;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Trickplay;
+using MediaBrowser.Model;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Logging;
+
+namespace Jellyfin.Api.Controllers;
+
+///
+/// Trickplay controller.
+///
+[Route("")]
+[Authorize]
+public class TrickplayController : BaseJellyfinApiController
+{
+ private readonly ILogger _logger;
+ private readonly IHttpContextAccessor _httpContextAccessor;
+ private readonly ILibraryManager _libraryManager;
+ private readonly ITrickplayManager _trickplayManager;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Instance of the interface.
+ /// Instance of the interface.
+ /// Instance of .
+ /// Instance of .
+ public TrickplayController(
+ ILogger logger,
+ IHttpContextAccessor httpContextAccessor,
+ ILibraryManager libraryManager,
+ ITrickplayManager trickplayManager)
+ {
+ _logger = logger;
+ _httpContextAccessor = httpContextAccessor;
+ _libraryManager = libraryManager;
+ _trickplayManager = trickplayManager;
+ }
+
+ ///
+ /// Gets an image tiles playlist for trickplay.
+ ///
+ /// The item id.
+ /// The width of a single tile.
+ /// The media version id, if using an alternate version.
+ /// Tiles stream returned.
+ /// A containing the trickplay tiles file.
+ [HttpGet("Videos/{itemId}/Trickplay/{width}/tiles.m3u8")]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesPlaylistFile]
+ public ActionResult GetTrickplayHlsPlaylist(
+ [FromRoute, Required] Guid itemId,
+ [FromRoute, Required] int width,
+ [FromQuery] string? mediaSourceId)
+ {
+ return GetTrickplayPlaylistInternal(width, mediaSourceId ?? itemId.ToString("N"));
+ }
+
+ ///
+ /// Gets a trickplay tile grid image.
+ ///
+ /// The item id.
+ /// The width of a single tile.
+ /// The index of the desired tile grid.
+ /// The media version id, if using an alternate version.
+ /// Tiles image returned.
+ /// Tiles image not found at specified index.
+ /// A containing the trickplay tiles image.
+ [HttpGet("Videos/{itemId}/Trickplay/{width}/{index}.jpg")]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status404NotFound)]
+ [ProducesImageFile]
+ public ActionResult GetTrickplayHlsPlaylist(
+ [FromRoute, Required] Guid itemId,
+ [FromRoute, Required] int width,
+ [FromRoute, Required] int index,
+ [FromQuery] string? mediaSourceId)
+ {
+ var item = _libraryManager.GetItemById(mediaSourceId ?? itemId.ToString("N"));
+ if (item is null)
+ {
+ return NotFound();
+ }
+
+ var path = _trickplayManager.GetTrickplayTilePath(item, width, index);
+ if (System.IO.File.Exists(path))
+ {
+ return PhysicalFile(path, MediaTypeNames.Image.Jpeg);
+ }
+
+ return NotFound();
+ }
+
+ private ActionResult GetTrickplayPlaylistInternal(int width, string mediaSourceId)
+ {
+ if (_httpContextAccessor.HttpContext is null)
+ {
+ throw new ResourceNotFoundException(nameof(_httpContextAccessor.HttpContext));
+ }
+
+ var tilesResolutions = _trickplayManager.GetTilesResolutions(Guid.Parse(mediaSourceId));
+ if (tilesResolutions is not null && tilesResolutions.ContainsKey(width))
+ {
+ var builder = new StringBuilder(128);
+ var tilesInfo = tilesResolutions[width];
+
+ if (tilesInfo.TileCount > 0)
+ {
+ const string urlFormat = "Trickplay/{0}/{1}.jpg?MediaSourceId={2}&api_key={3}";
+ const string decimalFormat = "{0:0.###}";
+
+ var resolution = tilesInfo.Width.ToString(CultureInfo.InvariantCulture) + "x" + tilesInfo.Height.ToString(CultureInfo.InvariantCulture);
+ var layout = tilesInfo.TileWidth.ToString(CultureInfo.InvariantCulture) + "x" + tilesInfo.TileHeight.ToString(CultureInfo.InvariantCulture);
+ var tilesPerGrid = tilesInfo.TileWidth * tilesInfo.TileHeight;
+ var tileDuration = (decimal)tilesInfo.Interval / 1000;
+ var tileGridCount = (int)Math.Ceiling((decimal)tilesInfo.TileCount / tilesPerGrid);
+
+ builder.AppendLine("#EXTM3U");
+ builder.Append("#EXT-X-TARGETDURATION:").AppendLine(tileGridCount.ToString(CultureInfo.InvariantCulture));
+ builder.AppendLine("#EXT-X-VERSION:7");
+ builder.AppendLine("#EXT-X-MEDIA-SEQUENCE:1");
+ builder.AppendLine("#EXT-X-PLAYLIST-TYPE:VOD");
+ builder.AppendLine("#EXT-X-IMAGES-ONLY");
+
+ for (int i = 0; i < tileGridCount; i++)
+ {
+ // All tile grids before the last one must contain full amount of tiles.
+ // The final grid will be 0 < count <= maxTiles
+ if (i == tileGridCount - 1)
+ {
+ tilesPerGrid = tilesInfo.TileCount - (i * tilesPerGrid);
+ }
+
+ var infDuration = tileDuration * tilesPerGrid;
+ var url = string.Format(
+ CultureInfo.InvariantCulture,
+ urlFormat,
+ width.ToString(CultureInfo.InvariantCulture),
+ i.ToString(CultureInfo.InvariantCulture),
+ mediaSourceId,
+ _httpContextAccessor.HttpContext.User.GetToken());
+
+ // EXTINF
+ builder.Append("#EXTINF:").Append(string.Format(CultureInfo.InvariantCulture, decimalFormat, infDuration))
+ .AppendLine(",");
+
+ // EXT-X-TILES
+ builder.Append("#EXT-X-TILES:RESOLUTION=").Append(resolution).Append(",LAYOUT=").Append(layout).Append(",DURATION=")
+ .AppendLine(string.Format(CultureInfo.InvariantCulture, decimalFormat, tileDuration));
+
+ // URL
+ builder.AppendLine(url);
+ }
+
+ builder.AppendLine("#EXT-X-ENDLIST");
+ return new FileContentResult(Encoding.UTF8.GetBytes(builder.ToString()), MimeTypes.GetMimeType("playlist.m3u8"));
+ }
+ }
+
+ return new FileContentResult(Array.Empty(), MimeTypes.GetMimeType("playlist.m3u8"));
+ }
+}
diff --git a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs
index d8a16876d..6508dd8cf 100644
--- a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs
+++ b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs
@@ -118,81 +118,6 @@ public class DynamicHlsHelper
cancellationTokenSource).ConfigureAwait(false);
}
- ///
- /// Get trickplay tiles hls playlist.
- ///
- /// The width of a single tile.
- /// The media version id.
- /// The resulting .
- public ActionResult GetTilesHlsPlaylist(int width, string mediaSourceId)
- {
- if (_httpContextAccessor.HttpContext is null)
- {
- throw new ResourceNotFoundException(nameof(_httpContextAccessor.HttpContext));
- }
-
- var tilesResolutions = _trickplayManager.GetTilesResolutions(Guid.Parse(mediaSourceId));
- if (tilesResolutions is not null && tilesResolutions.ContainsKey(width))
- {
- var builder = new StringBuilder(128);
- var tilesInfo = tilesResolutions[width];
-
- if (tilesInfo.TileCount > 0)
- {
- const string urlFormat = "{0}/Trickplay/{1}/{2}.jpg?&api_key={3}";
- const string decimalFormat = "{0:0.###}";
-
- var resolution = tilesInfo.Width.ToString(CultureInfo.InvariantCulture) + "x" + tilesInfo.Height.ToString(CultureInfo.InvariantCulture);
- var layout = tilesInfo.TileWidth.ToString(CultureInfo.InvariantCulture) + "x" + tilesInfo.TileHeight.ToString(CultureInfo.InvariantCulture);
- var tilesPerGrid = tilesInfo.TileWidth * tilesInfo.TileHeight;
- var tileDuration = (decimal)tilesInfo.Interval / 1000;
- var tileGridCount = (int)Math.Ceiling((decimal)tilesInfo.TileCount / tilesPerGrid);
-
- builder.AppendLine("#EXTM3U");
- builder.Append("#EXT-X-TARGETDURATION:").AppendLine(tileGridCount.ToString(CultureInfo.InvariantCulture));
- builder.AppendLine("#EXT-X-VERSION:7");
- builder.AppendLine("#EXT-X-MEDIA-SEQUENCE:1");
- builder.AppendLine("#EXT-X-PLAYLIST-TYPE:VOD");
- builder.AppendLine("#EXT-X-IMAGES-ONLY");
-
- for (int i = 0; i < tileGridCount; i++)
- {
- // All tile grids before the last one must contain full amount of tiles.
- // The final grid will be 0 < count <= maxTiles
- if (i == tileGridCount - 1)
- {
- tilesPerGrid = tilesInfo.TileCount - (i * tilesPerGrid);
- }
-
- var infDuration = tileDuration * tilesPerGrid;
- var url = string.Format(
- CultureInfo.InvariantCulture,
- urlFormat,
- mediaSourceId,
- width.ToString(CultureInfo.InvariantCulture),
- i.ToString(CultureInfo.InvariantCulture),
- _httpContextAccessor.HttpContext.User.GetToken());
-
- // EXTINF
- builder.Append("#EXTINF:").Append(string.Format(CultureInfo.InvariantCulture, decimalFormat, infDuration))
- .AppendLine(",");
-
- // EXT-X-TILES
- builder.Append("#EXT-X-TILES:RESOLUTION=").Append(resolution).Append(",LAYOUT=").Append(layout).Append(",DURATION=")
- .AppendLine(string.Format(CultureInfo.InvariantCulture, decimalFormat, tileDuration));
-
- // URL
- builder.AppendLine(url);
- }
-
- builder.AppendLine("#EXT-X-ENDLIST");
- return new FileContentResult(Encoding.UTF8.GetBytes(builder.ToString()), MimeTypes.GetMimeType("playlist.m3u8"));
- }
- }
-
- return new FileContentResult(Array.Empty(), MimeTypes.GetMimeType("playlist.m3u8"));
- }
-
private async Task GetMasterPlaylistInternal(
StreamingRequestDto streamingRequest,
bool isHeadRequest,
@@ -633,7 +558,7 @@ public class DynamicHlsHelper
var url = string.Format(
CultureInfo.InvariantCulture,
- "tiles.m3u8?Width={0}&MediaSourceId={1}&api_key={2}",
+ "Trickplay/{0}/tiles.m3u8?MediaSourceId={1}&api_key={2}",
width.ToString(CultureInfo.InvariantCulture),
state.Request.MediaSourceId,
user.GetToken());
--
cgit v1.2.3
From 79cff704ff4e00895a8d2b97ecbbea3e2f5f56fc Mon Sep 17 00:00:00 2001
From: Jan Müller
Date: Tue, 1 Aug 2023 17:28:49 +0200
Subject: Allow flac inside mp4 for all HLS audio streams
The -strict -2 setting was only added if the encoder was set to 'copy'.
If 'flac' is explicitly requested, we also need to set it, so that
ffmpeg doesn't abort the conversion.
Fixes: #10066
---
Jellyfin.Api/Controllers/DynamicHlsController.cs | 26 ++++++++++++------------
1 file changed, 13 insertions(+), 13 deletions(-)
(limited to 'Jellyfin.Api/Controllers/DynamicHlsController.cs')
diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs
index ce684e457..94093f167 100644
--- a/Jellyfin.Api/Controllers/DynamicHlsController.cs
+++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs
@@ -1704,18 +1704,29 @@ public class DynamicHlsController : BaseJellyfinApiController
var audioCodec = _encodingHelper.GetAudioEncoder(state);
+ // dts, flac, opus and truehd are experimental in mp4 muxer
+ var strictArgs = string.Empty;
+ var actualOutputAudioCodec = state.ActualOutputAudioCodec;
+ if (string.Equals(actualOutputAudioCodec, "flac", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(actualOutputAudioCodec, "opus", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(actualOutputAudioCodec, "dts", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(actualOutputAudioCodec, "truehd", StringComparison.OrdinalIgnoreCase))
+ {
+ strictArgs = " -strict -2";
+ }
+
if (!state.IsOutputVideo)
{
if (EncodingHelper.IsCopyCodec(audioCodec))
{
var bitStreamArgs = EncodingHelper.GetAudioBitStreamArguments(state, state.Request.SegmentContainer, state.MediaSource.Container);
- return "-acodec copy -strict -2" + bitStreamArgs;
+ return "-acodec copy" + bitStreamArgs + strictArgs;
}
var audioTranscodeParams = string.Empty;
- audioTranscodeParams += "-acodec " + audioCodec;
+ audioTranscodeParams += "-acodec " + audioCodec + strictArgs;
var audioBitrate = state.OutputAudioBitrate;
var audioChannels = state.OutputAudioChannels;
@@ -1747,17 +1758,6 @@ public class DynamicHlsController : BaseJellyfinApiController
return audioTranscodeParams;
}
- // dts, flac, opus and truehd are experimental in mp4 muxer
- var strictArgs = string.Empty;
- var actualOutputAudioCodec = state.ActualOutputAudioCodec;
- if (string.Equals(actualOutputAudioCodec, "flac", StringComparison.OrdinalIgnoreCase)
- || string.Equals(actualOutputAudioCodec, "opus", StringComparison.OrdinalIgnoreCase)
- || string.Equals(actualOutputAudioCodec, "dts", StringComparison.OrdinalIgnoreCase)
- || string.Equals(actualOutputAudioCodec, "truehd", StringComparison.OrdinalIgnoreCase))
- {
- strictArgs = " -strict -2";
- }
-
if (EncodingHelper.IsCopyCodec(audioCodec))
{
var videoCodec = _encodingHelper.GetVideoEncoder(state, _encodingOptions);
--
cgit v1.2.3
From b176beb88e22a36cc056439ac2a4df4fbe68f2c1 Mon Sep 17 00:00:00 2001
From: Bond_009
Date: Fri, 6 Oct 2023 00:40:09 +0200
Subject: Reduce string allocations
Some simple changes to reduce the number of allocated strings
---
Emby.Dlna/DlnaManager.cs | 2 +-
Emby.Naming/ExternalFiles/ExternalPathParser.cs | 2 +-
Emby.Naming/Video/StubResolver.cs | 7 ++---
Emby.Photos/PhotoProvider.cs | 2 +-
.../IO/ManagedFileSystem.cs | 6 ++--
.../Library/LibraryManager.cs | 4 +--
.../Library/Resolvers/Audio/AudioResolver.cs | 6 ++--
.../Library/Resolvers/BaseVideoResolver.cs | 2 +-
.../Library/Resolvers/Books/BookResolver.cs | 9 +++---
.../MediaEncoder/EncodingManager.cs | 2 +-
.../Playlists/PlaylistManager.cs | 16 ++++------
.../Updates/InstallationManager.cs | 3 +-
Jellyfin.Api/Controllers/DynamicHlsController.cs | 4 +--
Jellyfin.Api/Controllers/HlsSegmentController.cs | 9 +++---
Jellyfin.Api/Helpers/StreamingHelpers.cs | 10 +++---
Jellyfin.Api/Helpers/TranscodingJobHelper.cs | 2 +-
.../MediaEncoding/EncodingHelper.cs | 18 +++++------
.../Attachments/AttachmentExtractor.cs | 36 +++-------------------
MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 6 ++--
src/Jellyfin.Drawing.Skia/SkiaEncoder.cs | 2 +-
src/Jellyfin.Drawing.Skia/StripCollageBuilder.cs | 12 ++++----
21 files changed, 63 insertions(+), 97 deletions(-)
(limited to 'Jellyfin.Api/Controllers/DynamicHlsController.cs')
diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs
index 99b3e6e7e..d67cb67b5 100644
--- a/Emby.Dlna/DlnaManager.cs
+++ b/Emby.Dlna/DlnaManager.cs
@@ -228,7 +228,7 @@ namespace Emby.Dlna
try
{
return _fileSystem.GetFilePaths(path)
- .Where(i => string.Equals(Path.GetExtension(i), ".xml", StringComparison.OrdinalIgnoreCase))
+ .Where(i => Path.GetExtension(i.AsSpan()).Equals(".xml", StringComparison.OrdinalIgnoreCase))
.Select(i => ParseProfileFile(i, type))
.Where(i => i is not null)
.ToList()!; // We just filtered out all the nulls
diff --git a/Emby.Naming/ExternalFiles/ExternalPathParser.cs b/Emby.Naming/ExternalFiles/ExternalPathParser.cs
index 953129671..4080ba10d 100644
--- a/Emby.Naming/ExternalFiles/ExternalPathParser.cs
+++ b/Emby.Naming/ExternalFiles/ExternalPathParser.cs
@@ -43,7 +43,7 @@ namespace Emby.Naming.ExternalFiles
return null;
}
- var extension = Path.GetExtension(path);
+ var extension = Path.GetExtension(path.AsSpan());
if (!(_type == DlnaProfileType.Subtitle && _namingOptions.SubtitleFileExtensions.Contains(extension, StringComparison.OrdinalIgnoreCase))
&& !(_type == DlnaProfileType.Audio && _namingOptions.AudioFileExtensions.Contains(extension, StringComparison.OrdinalIgnoreCase)))
{
diff --git a/Emby.Naming/Video/StubResolver.cs b/Emby.Naming/Video/StubResolver.cs
index f7ba606e3..4b9df19b0 100644
--- a/Emby.Naming/Video/StubResolver.cs
+++ b/Emby.Naming/Video/StubResolver.cs
@@ -26,19 +26,18 @@ namespace Emby.Naming.Video
return false;
}
- var extension = Path.GetExtension(path);
+ var extension = Path.GetExtension(path.AsSpan());
if (!options.StubFileExtensions.Contains(extension, StringComparison.OrdinalIgnoreCase))
{
return false;
}
- path = Path.GetFileNameWithoutExtension(path);
- var token = Path.GetExtension(path).TrimStart('.');
+ var token = Path.GetExtension(Path.GetFileNameWithoutExtension(path.AsSpan())).TrimStart('.');
foreach (var rule in options.StubTypes)
{
- if (string.Equals(rule.Token, token, StringComparison.OrdinalIgnoreCase))
+ if (token.Equals(rule.Token, StringComparison.OrdinalIgnoreCase))
{
stubType = rule.StubType;
return true;
diff --git a/Emby.Photos/PhotoProvider.cs b/Emby.Photos/PhotoProvider.cs
index f54066c57..27329a7f2 100644
--- a/Emby.Photos/PhotoProvider.cs
+++ b/Emby.Photos/PhotoProvider.cs
@@ -61,7 +61,7 @@ namespace Emby.Photos
item.SetImagePath(ImageType.Primary, item.Path);
// Examples: https://github.com/mono/taglib-sharp/blob/a5f6949a53d09ce63ee7495580d6802921a21f14/tests/fixtures/TagLib.Tests.Images/NullOrientationTest.cs
- if (_includeExtensions.Contains(Path.GetExtension(item.Path), StringComparison.OrdinalIgnoreCase))
+ if (_includeExtensions.Contains(Path.GetExtension(item.Path.AsSpan()), StringComparison.OrdinalIgnoreCase))
{
try
{
diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs
index 18b00ce0b..4178936ce 100644
--- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs
+++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs
@@ -103,15 +103,17 @@ namespace Emby.Server.Implementations.IO
return filePath;
}
+ var filePathSpan = filePath.AsSpan();
+
// relative path
if (firstChar == '\\')
{
- filePath = filePath.Substring(1);
+ filePathSpan = filePathSpan.Slice(1);
}
try
{
- return Path.GetFullPath(Path.Combine(folderPath, filePath));
+ return Path.GetFullPath(Path.Join(folderPath, filePathSpan));
}
catch (ArgumentException)
{
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs
index b0a4a4151..845e7e351 100644
--- a/Emby.Server.Implementations/Library/LibraryManager.cs
+++ b/Emby.Server.Implementations/Library/LibraryManager.cs
@@ -1162,7 +1162,7 @@ namespace Emby.Server.Implementations.Library
Name = Path.GetFileName(dir),
Locations = _fileSystem.GetFilePaths(dir, false)
- .Where(i => string.Equals(ShortcutFileExtension, Path.GetExtension(i), StringComparison.OrdinalIgnoreCase))
+ .Where(i => Path.GetExtension(i.AsSpan()).Equals(ShortcutFileExtension, StringComparison.OrdinalIgnoreCase))
.Select(i =>
{
try
@@ -3135,7 +3135,7 @@ namespace Emby.Server.Implementations.Library
}
var shortcut = _fileSystem.GetFilePaths(virtualFolderPath, true)
- .Where(i => string.Equals(ShortcutFileExtension, Path.GetExtension(i), StringComparison.OrdinalIgnoreCase))
+ .Where(i => Path.GetExtension(i.AsSpan()).Equals(ShortcutFileExtension, StringComparison.OrdinalIgnoreCase))
.FirstOrDefault(f => _appHost.ExpandVirtualPath(_fileSystem.ResolveShortcut(f)).Equals(mediaPath, StringComparison.OrdinalIgnoreCase));
if (!string.IsNullOrEmpty(shortcut))
diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
index a74f82475..862f144e6 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
@@ -94,9 +94,9 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
if (AudioFileParser.IsAudioFile(args.Path, _namingOptions))
{
- var extension = Path.GetExtension(args.Path);
+ var extension = Path.GetExtension(args.Path.AsSpan());
- if (string.Equals(extension, ".cue", StringComparison.OrdinalIgnoreCase))
+ if (extension.Equals(".cue", StringComparison.OrdinalIgnoreCase))
{
// if audio file exists of same name, return null
return null;
@@ -128,7 +128,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
if (item is not null)
{
- item.IsShortcut = string.Equals(extension, ".strm", StringComparison.OrdinalIgnoreCase);
+ item.IsShortcut = extension.Equals(".strm", StringComparison.OrdinalIgnoreCase);
item.IsInMixedFolder = true;
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
index 381796d0e..779cfd5be 100644
--- a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
@@ -263,7 +263,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
return false;
}
- return directoryService.GetFilePaths(fullPath).Any(i => string.Equals(Path.GetExtension(i), ".vob", StringComparison.OrdinalIgnoreCase));
+ return directoryService.GetFilePaths(fullPath).Any(i => Path.GetExtension(i.AsSpan()).Equals(".vob", StringComparison.OrdinalIgnoreCase));
}
///
diff --git a/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs
index 042422c6f..73861ff59 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs
@@ -32,9 +32,9 @@ namespace Emby.Server.Implementations.Library.Resolvers.Books
return GetBook(args);
}
- var extension = Path.GetExtension(args.Path);
+ var extension = Path.GetExtension(args.Path.AsSpan());
- if (extension is not null && _validExtensions.Contains(extension, StringComparison.OrdinalIgnoreCase))
+ if (_validExtensions.Contains(extension, StringComparison.OrdinalIgnoreCase))
{
// It's a book
return new Book
@@ -51,12 +51,11 @@ namespace Emby.Server.Implementations.Library.Resolvers.Books
{
var bookFiles = args.FileSystemChildren.Where(f =>
{
- var fileExtension = Path.GetExtension(f.FullName)
- ?? string.Empty;
+ var fileExtension = Path.GetExtension(f.FullName.AsSpan());
return _validExtensions.Contains(
fileExtension,
- StringComparer.OrdinalIgnoreCase);
+ StringComparison.OrdinalIgnoreCase);
}).ToList();
// Don't return a Book if there is more (or less) than one document in the directory
diff --git a/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs b/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs
index 7732e32d0..896f47923 100644
--- a/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs
+++ b/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs
@@ -222,7 +222,7 @@ namespace Emby.Server.Implementations.MediaEncoder
{
var deadImages = images
.Except(chapters.Select(i => i.ImagePath).Where(i => !string.IsNullOrEmpty(i)), StringComparer.OrdinalIgnoreCase)
- .Where(i => BaseItem.SupportedImageExtensions.Contains(Path.GetExtension(i), StringComparison.OrdinalIgnoreCase))
+ .Where(i => BaseItem.SupportedImageExtensions.Contains(Path.GetExtension(i.AsSpan()), StringComparison.OrdinalIgnoreCase))
.ToList();
foreach (var image in deadImages)
diff --git a/Emby.Server.Implementations/Playlists/PlaylistManager.cs b/Emby.Server.Implementations/Playlists/PlaylistManager.cs
index 0cb450cdf..649c49924 100644
--- a/Emby.Server.Implementations/Playlists/PlaylistManager.cs
+++ b/Emby.Server.Implementations/Playlists/PlaylistManager.cs
@@ -327,9 +327,9 @@ namespace Emby.Server.Implementations.Playlists
// this is probably best done as a metadata provider
// saving a file over itself will require some work to prevent this from happening when not needed
var playlistPath = item.Path;
- var extension = Path.GetExtension(playlistPath);
+ var extension = Path.GetExtension(playlistPath.AsSpan());
- if (string.Equals(".wpl", extension, StringComparison.OrdinalIgnoreCase))
+ if (extension.Equals(".wpl", StringComparison.OrdinalIgnoreCase))
{
var playlist = new WplPlaylist();
foreach (var child in item.GetLinkedChildren())
@@ -362,8 +362,7 @@ namespace Emby.Server.Implementations.Playlists
string text = new WplContent().ToText(playlist);
File.WriteAllText(playlistPath, text);
}
-
- if (string.Equals(".zpl", extension, StringComparison.OrdinalIgnoreCase))
+ else if (extension.Equals(".zpl", StringComparison.OrdinalIgnoreCase))
{
var playlist = new ZplPlaylist();
foreach (var child in item.GetLinkedChildren())
@@ -396,8 +395,7 @@ namespace Emby.Server.Implementations.Playlists
string text = new ZplContent().ToText(playlist);
File.WriteAllText(playlistPath, text);
}
-
- if (string.Equals(".m3u", extension, StringComparison.OrdinalIgnoreCase))
+ else if (extension.Equals(".m3u", StringComparison.OrdinalIgnoreCase))
{
var playlist = new M3uPlaylist
{
@@ -428,8 +426,7 @@ namespace Emby.Server.Implementations.Playlists
string text = new M3uContent().ToText(playlist);
File.WriteAllText(playlistPath, text);
}
-
- if (string.Equals(".m3u8", extension, StringComparison.OrdinalIgnoreCase))
+ else if (extension.Equals(".m3u8", StringComparison.OrdinalIgnoreCase))
{
var playlist = new M3uPlaylist();
playlist.IsExtended = true;
@@ -458,8 +455,7 @@ namespace Emby.Server.Implementations.Playlists
string text = new M3uContent().ToText(playlist);
File.WriteAllText(playlistPath, text);
}
-
- if (string.Equals(".pls", extension, StringComparison.OrdinalIgnoreCase))
+ else if (extension.Equals(".pls", StringComparison.OrdinalIgnoreCase))
{
var playlist = new PlsPlaylist();
foreach (var child in item.GetLinkedChildren())
diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs
index 6c198b6f9..77d385ba1 100644
--- a/Emby.Server.Implementations/Updates/InstallationManager.cs
+++ b/Emby.Server.Implementations/Updates/InstallationManager.cs
@@ -504,8 +504,7 @@ namespace Emby.Server.Implementations.Updates
private async Task PerformPackageInstallation(InstallationInfo package, PluginStatus status, CancellationToken cancellationToken)
{
- var extension = Path.GetExtension(package.SourceUrl);
- if (!string.Equals(extension, ".zip", StringComparison.OrdinalIgnoreCase))
+ if (!Path.GetExtension(package.SourceUrl.AsSpan()).Equals(".zip", StringComparison.OrdinalIgnoreCase))
{
_logger.LogError("Only zip packages are supported. {SourceUrl} is not a zip archive.", package.SourceUrl);
return;
diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs
index 065a4ce5c..4026a38f8 100644
--- a/Jellyfin.Api/Controllers/DynamicHlsController.cs
+++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs
@@ -2041,9 +2041,9 @@ public class DynamicHlsController : BaseJellyfinApiController
return null;
}
- var playlistFilename = Path.GetFileNameWithoutExtension(playlist);
+ var playlistFilename = Path.GetFileNameWithoutExtension(playlist.AsSpan());
- var indexString = Path.GetFileNameWithoutExtension(file.Name).Substring(playlistFilename.Length);
+ var indexString = Path.GetFileNameWithoutExtension(file.Name.AsSpan()).Slice(playlistFilename.Length);
return int.Parse(indexString, NumberStyles.Integer, CultureInfo.InvariantCulture);
}
diff --git a/Jellyfin.Api/Controllers/HlsSegmentController.cs b/Jellyfin.Api/Controllers/HlsSegmentController.cs
index d7cec865e..6eedfd8c7 100644
--- a/Jellyfin.Api/Controllers/HlsSegmentController.cs
+++ b/Jellyfin.Api/Controllers/HlsSegmentController.cs
@@ -59,7 +59,7 @@ public class HlsSegmentController : BaseJellyfinApiController
public ActionResult GetHlsAudioSegmentLegacy([FromRoute, Required] string itemId, [FromRoute, Required] string segmentId)
{
// TODO: Deprecate with new iOS app
- var file = segmentId + Path.GetExtension(Request.Path);
+ var file = string.Concat(segmentId, Path.GetExtension(Request.Path.Value.AsSpan()));
var transcodePath = _serverConfigurationManager.GetTranscodePath();
file = Path.GetFullPath(Path.Combine(transcodePath, file));
var fileDir = Path.GetDirectoryName(file);
@@ -85,11 +85,12 @@ public class HlsSegmentController : BaseJellyfinApiController
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "itemId", Justification = "Required for ServiceStack")]
public ActionResult GetHlsPlaylistLegacy([FromRoute, Required] string itemId, [FromRoute, Required] string playlistId)
{
- var file = playlistId + Path.GetExtension(Request.Path);
+ var file = string.Concat(playlistId, Path.GetExtension(Request.Path.Value.AsSpan()));
var transcodePath = _serverConfigurationManager.GetTranscodePath();
file = Path.GetFullPath(Path.Combine(transcodePath, file));
var fileDir = Path.GetDirectoryName(file);
- if (string.IsNullOrEmpty(fileDir) || !fileDir.StartsWith(transcodePath, StringComparison.InvariantCulture) || Path.GetExtension(file) != ".m3u8")
+ if (string.IsNullOrEmpty(fileDir) || !fileDir.StartsWith(transcodePath, StringComparison.InvariantCulture)
+ || Path.GetExtension(file.AsSpan()).Equals(".m3u8", StringComparison.OrdinalIgnoreCase))
{
return BadRequest("Invalid segment.");
}
@@ -138,7 +139,7 @@ public class HlsSegmentController : BaseJellyfinApiController
[FromRoute, Required] string segmentId,
[FromRoute, Required] string segmentContainer)
{
- var file = segmentId + Path.GetExtension(Request.Path);
+ var file = string.Concat(segmentId, Path.GetExtension(Request.Path.Value.AsSpan()));
var transcodeFolderPath = _serverConfigurationManager.GetTranscodePath();
file = Path.GetFullPath(Path.Combine(transcodeFolderPath, file));
diff --git a/Jellyfin.Api/Helpers/StreamingHelpers.cs b/Jellyfin.Api/Helpers/StreamingHelpers.cs
index 782cd6568..d15fe4fab 100644
--- a/Jellyfin.Api/Helpers/StreamingHelpers.cs
+++ b/Jellyfin.Api/Helpers/StreamingHelpers.cs
@@ -243,7 +243,7 @@ public static class StreamingHelpers
? GetOutputFileExtension(state, mediaSource)
: ("." + state.OutputContainer);
- state.OutputFilePath = GetOutputFilePath(state, ext!, serverConfigurationManager, streamingRequest.DeviceId, streamingRequest.PlaySessionId);
+ state.OutputFilePath = GetOutputFilePath(state, ext, serverConfigurationManager, streamingRequest.DeviceId, streamingRequest.PlaySessionId);
return state;
}
@@ -418,11 +418,11 @@ public static class StreamingHelpers
/// System.String.
private static string? GetOutputFileExtension(StreamState state, MediaSourceInfo? mediaSource)
{
- var ext = Path.GetExtension(state.RequestedUrl);
+ var ext = Path.GetExtension(state.RequestedUrl.AsSpan());
- if (!string.IsNullOrEmpty(ext))
+ if (ext.IsEmpty)
{
- return ext;
+ return null;
}
// Try to infer based on the desired video codec
@@ -504,7 +504,7 @@ public static class StreamingHelpers
/// The device id.
/// The play session id.
/// The complete file path, including the folder, for the transcoding file.
- private static string GetOutputFilePath(StreamState state, string outputFileExtension, IServerConfigurationManager serverConfigurationManager, string? deviceId, string? playSessionId)
+ private static string GetOutputFilePath(StreamState state, string? outputFileExtension, IServerConfigurationManager serverConfigurationManager, string? deviceId, string? playSessionId)
{
var data = $"{state.MediaPath}-{state.UserAgent}-{deviceId!}-{playSessionId!}";
diff --git a/Jellyfin.Api/Helpers/TranscodingJobHelper.cs b/Jellyfin.Api/Helpers/TranscodingJobHelper.cs
index 73ebb396d..c16a586d6 100644
--- a/Jellyfin.Api/Helpers/TranscodingJobHelper.cs
+++ b/Jellyfin.Api/Helpers/TranscodingJobHelper.cs
@@ -538,7 +538,7 @@ public class TranscodingJobHelper : IDisposable
await _attachmentExtractor.ExtractAllAttachments(state.MediaPath, state.MediaSource, attachmentPath, cancellationTokenSource.Token).ConfigureAwait(false);
}
- if (state.SubtitleStream.IsExternal && string.Equals(Path.GetExtension(state.SubtitleStream.Path), ".mks", StringComparison.OrdinalIgnoreCase))
+ if (state.SubtitleStream.IsExternal && Path.GetExtension(state.SubtitleStream.Path.AsSpan()).Equals(".mks", StringComparison.OrdinalIgnoreCase))
{
string subtitlePath = state.SubtitleStream.Path;
string subtitlePathArgument = string.Format(CultureInfo.InvariantCulture, "file:\"{0}\"", subtitlePath.Replace("\"", "\\\"", StringComparison.Ordinal));
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index c311d3b8a..3be7a4252 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -548,25 +548,25 @@ namespace MediaBrowser.Controller.MediaEncoding
/// System.Nullable{VideoCodecs}.
public string InferVideoCodec(string url)
{
- var ext = Path.GetExtension(url);
+ var ext = Path.GetExtension(url.AsSpan());
- if (string.Equals(ext, ".asf", StringComparison.OrdinalIgnoreCase))
+ if (ext.Equals(".asf", StringComparison.OrdinalIgnoreCase))
{
return "wmv";
}
- if (string.Equals(ext, ".webm", StringComparison.OrdinalIgnoreCase))
+ if (ext.Equals(".webm", StringComparison.OrdinalIgnoreCase))
{
// TODO: this may not always mean VP8, as the codec ages
return "vp8";
}
- if (string.Equals(ext, ".ogg", StringComparison.OrdinalIgnoreCase) || string.Equals(ext, ".ogv", StringComparison.OrdinalIgnoreCase))
+ if (ext.Equals(".ogg", StringComparison.OrdinalIgnoreCase) || ext.Equals(".ogv", StringComparison.OrdinalIgnoreCase))
{
return "theora";
}
- if (string.Equals(ext, ".m3u8", StringComparison.OrdinalIgnoreCase) || string.Equals(ext, ".ts", StringComparison.OrdinalIgnoreCase))
+ if (ext.Equals(".m3u8", StringComparison.OrdinalIgnoreCase) || ext.Equals(".ts", StringComparison.OrdinalIgnoreCase))
{
return "h264";
}
@@ -1080,10 +1080,10 @@ namespace MediaBrowser.Controller.MediaEncoding
&& state.SubtitleStream.IsExternal)
{
var subtitlePath = state.SubtitleStream.Path;
- var subtitleExtension = Path.GetExtension(subtitlePath);
+ var subtitleExtension = Path.GetExtension(subtitlePath.AsSpan());
- if (string.Equals(subtitleExtension, ".sub", StringComparison.OrdinalIgnoreCase)
- || string.Equals(subtitleExtension, ".sup", StringComparison.OrdinalIgnoreCase))
+ if (subtitleExtension.Equals(".sub", StringComparison.OrdinalIgnoreCase)
+ || subtitleExtension.Equals(".sup", StringComparison.OrdinalIgnoreCase))
{
var idxFile = Path.ChangeExtension(subtitlePath, ".idx");
if (File.Exists(idxFile))
@@ -6033,7 +6033,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var format = string.Empty;
var keyFrame = string.Empty;
- if (string.Equals(Path.GetExtension(outputPath), ".mp4", StringComparison.OrdinalIgnoreCase)
+ if (Path.GetExtension(outputPath.AsSpan()).Equals(".mp4", StringComparison.OrdinalIgnoreCase)
&& state.BaseRequest.Context == EncodingContext.Streaming)
{
// Comparison: https://github.com/jansmolders86/mediacenterjs/blob/master/lib/transcoding/desktop.js
diff --git a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs
index 989e386a5..0ec0c84d4 100644
--- a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs
+++ b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs
@@ -1,4 +1,3 @@
-#nullable disable
#pragma warning disable CS1591
using System;
@@ -23,7 +22,7 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.MediaEncoding.Attachments
{
- public class AttachmentExtractor : IAttachmentExtractor, IDisposable
+ public sealed class AttachmentExtractor : IAttachmentExtractor
{
private readonly ILogger _logger;
private readonly IApplicationPaths _appPaths;
@@ -34,8 +33,6 @@ namespace MediaBrowser.MediaEncoding.Attachments
private readonly ConcurrentDictionary _semaphoreLocks =
new ConcurrentDictionary();
- private bool _disposed = false;
-
public AttachmentExtractor(
ILogger logger,
IApplicationPaths appPaths,
@@ -296,7 +293,7 @@ namespace MediaBrowser.MediaEncoding.Attachments
ArgumentException.ThrowIfNullOrEmpty(outputPath);
- Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
+ Directory.CreateDirectory(Path.GetDirectoryName(outputPath) ?? throw new ArgumentException("Path can't be a root directory.", nameof(outputPath)));
var processArgs = string.Format(
CultureInfo.InvariantCulture,
@@ -391,33 +388,8 @@ namespace MediaBrowser.MediaEncoding.Attachments
filename = (mediaPath + attachmentStreamIndex.ToString(CultureInfo.InvariantCulture)).GetMD5().ToString("D", CultureInfo.InvariantCulture);
}
- var prefix = filename.Substring(0, 1);
- return Path.Combine(_appPaths.DataPath, "attachments", prefix, filename);
- }
-
- ///
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- ///
- /// Releases unmanaged and - optionally - managed resources.
- ///
- /// true to release both managed and unmanaged resources; false to release only unmanaged resources.
- protected virtual void Dispose(bool disposing)
- {
- if (_disposed)
- {
- return;
- }
-
- if (disposing)
- {
- }
-
- _disposed = true;
+ var prefix = filename.AsSpan(0, 1);
+ return Path.Join(_appPaths.DataPath, "attachments", prefix, filename);
}
}
}
diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
index 346e97ae1..59c350561 100644
--- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
@@ -316,10 +316,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
{
var files = _fileSystem.GetFilePaths(path, recursive);
- var excludeExtensions = new[] { ".c" };
-
- return files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), filename, StringComparison.OrdinalIgnoreCase)
- && !excludeExtensions.Contains(Path.GetExtension(i) ?? string.Empty));
+ return files.FirstOrDefault(i => Path.GetFileNameWithoutExtension(i.AsSpan()).Equals(filename, StringComparison.OrdinalIgnoreCase)
+ && !Path.GetExtension(i.AsSpan()).Equals(".c", StringComparison.OrdinalIgnoreCase));
}
catch (Exception)
{
diff --git a/src/Jellyfin.Drawing.Skia/SkiaEncoder.cs b/src/Jellyfin.Drawing.Skia/SkiaEncoder.cs
index 126c0503e..416f60217 100644
--- a/src/Jellyfin.Drawing.Skia/SkiaEncoder.cs
+++ b/src/Jellyfin.Drawing.Skia/SkiaEncoder.cs
@@ -188,7 +188,7 @@ public class SkiaEncoder : IImageEncoder
return path;
}
- var tempPath = Path.Combine(_appPaths.TempDirectory, Guid.NewGuid() + Path.GetExtension(path));
+ var tempPath = Path.Combine(_appPaths.TempDirectory, string.Concat(Guid.NewGuid().ToString(), Path.GetExtension(path.AsSpan())));
var directory = Path.GetDirectoryName(tempPath) ?? throw new ResourceNotFoundException($"Provided path ({tempPath}) is not valid.");
Directory.CreateDirectory(directory);
File.Copy(path, tempPath, true);
diff --git a/src/Jellyfin.Drawing.Skia/StripCollageBuilder.cs b/src/Jellyfin.Drawing.Skia/StripCollageBuilder.cs
index 6dff7aa9b..4aff26c16 100644
--- a/src/Jellyfin.Drawing.Skia/StripCollageBuilder.cs
+++ b/src/Jellyfin.Drawing.Skia/StripCollageBuilder.cs
@@ -38,25 +38,25 @@ public partial class StripCollageBuilder
{
ArgumentNullException.ThrowIfNull(outputPath);
- var ext = Path.GetExtension(outputPath);
+ var ext = Path.GetExtension(outputPath.AsSpan());
- if (string.Equals(ext, ".jpg", StringComparison.OrdinalIgnoreCase)
- || string.Equals(ext, ".jpeg", StringComparison.OrdinalIgnoreCase))
+ if (ext.Equals(".jpg", StringComparison.OrdinalIgnoreCase)
+ || ext.Equals(".jpeg", StringComparison.OrdinalIgnoreCase))
{
return SKEncodedImageFormat.Jpeg;
}
- if (string.Equals(ext, ".webp", StringComparison.OrdinalIgnoreCase))
+ if (ext.Equals(".webp", StringComparison.OrdinalIgnoreCase))
{
return SKEncodedImageFormat.Webp;
}
- if (string.Equals(ext, ".gif", StringComparison.OrdinalIgnoreCase))
+ if (ext.Equals(".gif", StringComparison.OrdinalIgnoreCase))
{
return SKEncodedImageFormat.Gif;
}
- if (string.Equals(ext, ".bmp", StringComparison.OrdinalIgnoreCase))
+ if (ext.Equals(".bmp", StringComparison.OrdinalIgnoreCase))
{
return SKEncodedImageFormat.Bmp;
}
--
cgit v1.2.3
From aa073748c00fe2b0508fde88d900143acec09e36 Mon Sep 17 00:00:00 2001
From: Nyanmisaka
Date: Mon, 9 Oct 2023 23:12:41 +0800
Subject: Drop experimental status of flac-in-MP4 for FFmpeg 6+
Signed-off-by: nyanmisaka
---
Jellyfin.Api/Controllers/DynamicHlsController.cs | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
(limited to 'Jellyfin.Api/Controllers/DynamicHlsController.cs')
diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs
index 760fefbf5..44afdb6b1 100644
--- a/Jellyfin.Api/Controllers/DynamicHlsController.cs
+++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs
@@ -45,6 +45,8 @@ public class DynamicHlsController : BaseJellyfinApiController
private const string DefaultEventEncoderPreset = "superfast";
private const TranscodingJobType TranscodingJobType = MediaBrowser.Controller.MediaEncoding.TranscodingJobType.Hls;
+ private readonly Version _minFFmpegFlacInMp4 = new Version(6, 0);
+
private readonly ILibraryManager _libraryManager;
private readonly IUserManager _userManager;
private readonly IDlnaManager _dlnaManager;
@@ -1705,13 +1707,14 @@ public class DynamicHlsController : BaseJellyfinApiController
var audioCodec = _encodingHelper.GetAudioEncoder(state);
var bitStreamArgs = EncodingHelper.GetAudioBitStreamArguments(state, state.Request.SegmentContainer, state.MediaSource.Container);
- // dts, flac, opus and truehd are experimental in mp4 muxer
+ // opus, dts, truehd and flac (in FFmpeg 5 and older) are experimental in mp4 muxer
var strictArgs = string.Empty;
var actualOutputAudioCodec = state.ActualOutputAudioCodec;
- if (string.Equals(actualOutputAudioCodec, "flac", StringComparison.OrdinalIgnoreCase)
- || string.Equals(actualOutputAudioCodec, "opus", StringComparison.OrdinalIgnoreCase)
+ if (string.Equals(actualOutputAudioCodec, "opus", StringComparison.OrdinalIgnoreCase)
|| string.Equals(actualOutputAudioCodec, "dts", StringComparison.OrdinalIgnoreCase)
- || string.Equals(actualOutputAudioCodec, "truehd", StringComparison.OrdinalIgnoreCase))
+ || string.Equals(actualOutputAudioCodec, "truehd", StringComparison.OrdinalIgnoreCase)
+ || (string.Equals(actualOutputAudioCodec, "flac", StringComparison.OrdinalIgnoreCase)
+ && _mediaEncoder.EncoderVersion < _minFFmpegFlacInMp4))
{
strictArgs = " -strict -2";
}
--
cgit v1.2.3
From 74f61fbd79ef2e2ad4a986f5f886581b2827de07 Mon Sep 17 00:00:00 2001
From: lonebyte <61915324+lonebyte@users.noreply.github.com>
Date: Tue, 10 Oct 2023 22:48:52 +0200
Subject: Fix HLS playback of m4a files with mjpeg stream (#10069)
---
Jellyfin.Api/Controllers/DynamicHlsController.cs | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
(limited to 'Jellyfin.Api/Controllers/DynamicHlsController.cs')
diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs
index 7bf366e5d..42c94c29d 100644
--- a/Jellyfin.Api/Controllers/DynamicHlsController.cs
+++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs
@@ -1721,14 +1721,17 @@ public class DynamicHlsController : BaseJellyfinApiController
if (!state.IsOutputVideo)
{
+ var audioTranscodeParams = string.Empty;
+
+ // -vn to drop any video streams
+ audioTranscodeParams += "-vn";
+
if (EncodingHelper.IsCopyCodec(audioCodec))
{
- return "-acodec copy" + bitStreamArgs + strictArgs;
+ return audioTranscodeParams + " -acodec copy" + bitStreamArgs + strictArgs;
}
- var audioTranscodeParams = string.Empty;
-
- audioTranscodeParams += "-acodec " + audioCodec + bitStreamArgs + strictArgs;
+ audioTranscodeParams += " -acodec " + audioCodec + bitStreamArgs + strictArgs;
var audioBitrate = state.OutputAudioBitrate;
var audioChannels = state.OutputAudioChannels;
@@ -1756,7 +1759,6 @@ public class DynamicHlsController : BaseJellyfinApiController
audioTranscodeParams += " -ar " + state.OutputAudioSampleRate.Value.ToString(CultureInfo.InvariantCulture);
}
- audioTranscodeParams += " -vn";
return audioTranscodeParams;
}
--
cgit v1.2.3