aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Emby.Dlna/Didl/DidlBuilder.cs3
-rw-r--r--Emby.Dlna/DlnaManager.cs6
-rw-r--r--Emby.Drawing/ImageProcessor.cs14
-rw-r--r--Emby.Naming/TV/SeasonPathParser.cs2
-rw-r--r--Emby.Server.Implementations/Data/SqliteItemRepository.cs45
-rw-r--r--Emby.Server.Implementations/HttpServer/FileWriter.cs2
-rw-r--r--Emby.Server.Implementations/HttpServer/HttpListenerHost.cs4
-rw-r--r--Emby.Server.Implementations/HttpServer/Security/AuthService.cs4
-rw-r--r--Emby.Server.Implementations/IO/FileRefresher.cs3
-rw-r--r--Emby.Server.Implementations/Images/ArtistImageProvider.cs10
-rw-r--r--Emby.Server.Implementations/Library/IgnorePatterns.cs23
-rw-r--r--Emby.Server.Implementations/Library/LibraryManager.cs2
-rw-r--r--Emby.Server.Implementations/Library/MediaSourceManager.cs35
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs5
-rw-r--r--Emby.Server.Implementations/Library/UserDataManager.cs4
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs4
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs9
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs11
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs25
-rw-r--r--Emby.Server.Implementations/Localization/Core/zh-TW.json2
-rw-r--r--Emby.Server.Implementations/Localization/LocalizationManager.cs8
-rw-r--r--Emby.Server.Implementations/Net/UdpSocket.cs43
-rw-r--r--Emby.Server.Implementations/Networking/NetworkManager.cs2
-rw-r--r--Emby.Server.Implementations/Playlists/PlaylistManager.cs64
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/TaskManager.cs7
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs8
-rw-r--r--Emby.Server.Implementations/Services/ServiceController.cs18
-rw-r--r--Emby.Server.Implementations/Services/ServiceExec.cs9
-rw-r--r--Emby.Server.Implementations/Services/ServiceHandler.cs26
-rw-r--r--Emby.Server.Implementations/Services/ServicePath.cs2
-rw-r--r--Emby.Server.Implementations/Session/SessionManager.cs4
-rw-r--r--Emby.Server.Implementations/Session/SessionWebSocketListener.cs30
-rw-r--r--Emby.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs4
-rw-r--r--Emby.Server.Implementations/SyncPlay/SyncPlayController.cs60
-rw-r--r--Jellyfin.Drawing.Skia/PercentPlayedDrawer.cs22
-rw-r--r--Jellyfin.Drawing.Skia/PlayedIndicatorDrawer.cs34
-rw-r--r--Jellyfin.Drawing.Skia/SkiaCodecException.cs2
-rw-r--r--Jellyfin.Drawing.Skia/SkiaEncoder.cs472
-rw-r--r--Jellyfin.Drawing.Skia/SkiaException.cs2
-rw-r--r--Jellyfin.Drawing.Skia/StripCollageBuilder.cs120
-rw-r--r--Jellyfin.Drawing.Skia/UnplayedCountIndicator.cs52
-rw-r--r--Jellyfin.Server.Implementations/Users/UserManager.cs4
-rw-r--r--MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs2
-rw-r--r--MediaBrowser.Controller/Entities/BaseItem.cs16
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs34
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/AssParser.cs4
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/ParserValues.cs5
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs11
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/SrtWriter.cs4
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs36
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs12
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/TtmlWriter.cs9
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/VttWriter.cs2
-rw-r--r--MediaBrowser.Providers/Plugins/AudioDb/Plugin.cs3
-rw-r--r--MediaBrowser.Providers/Plugins/MusicBrainz/Plugin.cs3
-rw-r--r--MediaBrowser.Providers/Plugins/Omdb/Plugin.cs3
-rw-r--r--MediaBrowser.Providers/Plugins/TheTvdb/Plugin.cs3
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Library/IgnorePatternsTests.cs14
58 files changed, 616 insertions, 751 deletions
diff --git a/Emby.Dlna/Didl/DidlBuilder.cs b/Emby.Dlna/Didl/DidlBuilder.cs
index 66baa9512..70e358019 100644
--- a/Emby.Dlna/Didl/DidlBuilder.cs
+++ b/Emby.Dlna/Didl/DidlBuilder.cs
@@ -364,7 +364,8 @@ namespace Emby.Dlna.Didl
writer.WriteAttributeString("bitrate", totalBitrate.Value.ToString(_usCulture));
}
- var mediaProfile = _profile.GetVideoMediaProfile(streamInfo.Container,
+ var mediaProfile = _profile.GetVideoMediaProfile(
+ streamInfo.Container,
streamInfo.TargetAudioCodec.FirstOrDefault(),
streamInfo.TargetVideoCodec.FirstOrDefault(),
streamInfo.TargetAudioBitrate,
diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs
index 5911a73ef..1e20ff92b 100644
--- a/Emby.Dlna/DlnaManager.cs
+++ b/Emby.Dlna/DlnaManager.cs
@@ -387,7 +387,7 @@ namespace Emby.Dlna
foreach (var name in _assembly.GetManifestResourceNames())
{
- if (!name.StartsWith(namespaceName))
+ if (!name.StartsWith(namespaceName, StringComparison.Ordinal))
{
continue;
}
@@ -406,7 +406,7 @@ namespace Emby.Dlna
using (var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read))
{
- await stream.CopyToAsync(fileStream);
+ await stream.CopyToAsync(fileStream).ConfigureAwait(false);
}
}
}
@@ -509,7 +509,7 @@ namespace Emby.Dlna
return _jsonSerializer.DeserializeFromString<DeviceProfile>(json);
}
- class InternalProfileInfo
+ private class InternalProfileInfo
{
internal DeviceProfileInfo Info { get; set; }
diff --git a/Emby.Drawing/ImageProcessor.cs b/Emby.Drawing/ImageProcessor.cs
index 8696cb280..f585b90ca 100644
--- a/Emby.Drawing/ImageProcessor.cs
+++ b/Emby.Drawing/ImageProcessor.cs
@@ -448,21 +448,21 @@ namespace Emby.Drawing
/// or
/// filename.
/// </exception>
- public string GetCachePath(string path, string filename)
+ public string GetCachePath(ReadOnlySpan<char> path, ReadOnlySpan<char> filename)
{
- if (string.IsNullOrEmpty(path))
+ if (path.IsEmpty)
{
- throw new ArgumentNullException(nameof(path));
+ throw new ArgumentException("Path can't be empty.", nameof(path));
}
- if (string.IsNullOrEmpty(filename))
+ if (path.IsEmpty)
{
- throw new ArgumentNullException(nameof(filename));
+ throw new ArgumentException("Filename can't be empty.", nameof(filename));
}
- var prefix = filename.Substring(0, 1);
+ var prefix = filename.Slice(0, 1);
- return Path.Combine(path, prefix, filename);
+ return Path.Join(path, prefix, filename);
}
/// <inheritdoc />
diff --git a/Emby.Naming/TV/SeasonPathParser.cs b/Emby.Naming/TV/SeasonPathParser.cs
index 2fa6b4353..d2e324dda 100644
--- a/Emby.Naming/TV/SeasonPathParser.cs
+++ b/Emby.Naming/TV/SeasonPathParser.cs
@@ -77,7 +77,7 @@ namespace Emby.Naming.TV
if (filename.StartsWith("s", StringComparison.OrdinalIgnoreCase))
{
- var testFilename = filename.Substring(1);
+ var testFilename = filename.AsSpan().Slice(1);
if (int.TryParse(testFilename, NumberStyles.Integer, CultureInfo.InvariantCulture, out var val))
{
diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
index 9f5566424..3ae167890 100644
--- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
@@ -2776,82 +2776,82 @@ namespace Emby.Server.Implementations.Data
private string FixUnicodeChars(string buffer)
{
- if (buffer.IndexOf('\u2013') > -1)
+ if (buffer.IndexOf('\u2013', StringComparison.Ordinal) > -1)
{
buffer = buffer.Replace('\u2013', '-'); // en dash
}
- if (buffer.IndexOf('\u2014') > -1)
+ if (buffer.IndexOf('\u2014', StringComparison.Ordinal) > -1)
{
buffer = buffer.Replace('\u2014', '-'); // em dash
}
- if (buffer.IndexOf('\u2015') > -1)
+ if (buffer.IndexOf('\u2015', StringComparison.Ordinal) > -1)
{
buffer = buffer.Replace('\u2015', '-'); // horizontal bar
}
- if (buffer.IndexOf('\u2017') > -1)
+ if (buffer.IndexOf('\u2017', StringComparison.Ordinal) > -1)
{
buffer = buffer.Replace('\u2017', '_'); // double low line
}
- if (buffer.IndexOf('\u2018') > -1)
+ if (buffer.IndexOf('\u2018', StringComparison.Ordinal) > -1)
{
buffer = buffer.Replace('\u2018', '\''); // left single quotation mark
}
- if (buffer.IndexOf('\u2019') > -1)
+ if (buffer.IndexOf('\u2019', StringComparison.Ordinal) > -1)
{
buffer = buffer.Replace('\u2019', '\''); // right single quotation mark
}
- if (buffer.IndexOf('\u201a') > -1)
+ if (buffer.IndexOf('\u201a', StringComparison.Ordinal) > -1)
{
buffer = buffer.Replace('\u201a', ','); // single low-9 quotation mark
}
- if (buffer.IndexOf('\u201b') > -1)
+ if (buffer.IndexOf('\u201b', StringComparison.Ordinal) > -1)
{
buffer = buffer.Replace('\u201b', '\''); // single high-reversed-9 quotation mark
}
- if (buffer.IndexOf('\u201c') > -1)
+ if (buffer.IndexOf('\u201c', StringComparison.Ordinal) > -1)
{
buffer = buffer.Replace('\u201c', '\"'); // left double quotation mark
}
- if (buffer.IndexOf('\u201d') > -1)
+ if (buffer.IndexOf('\u201d', StringComparison.Ordinal) > -1)
{
buffer = buffer.Replace('\u201d', '\"'); // right double quotation mark
}
- if (buffer.IndexOf('\u201e') > -1)
+ if (buffer.IndexOf('\u201e', StringComparison.Ordinal) > -1)
{
buffer = buffer.Replace('\u201e', '\"'); // double low-9 quotation mark
}
- if (buffer.IndexOf('\u2026') > -1)
+ if (buffer.IndexOf('\u2026', StringComparison.Ordinal) > -1)
{
- buffer = buffer.Replace("\u2026", "..."); // horizontal ellipsis
+ buffer = buffer.Replace("\u2026", "...", StringComparison.Ordinal); // horizontal ellipsis
}
- if (buffer.IndexOf('\u2032') > -1)
+ if (buffer.IndexOf('\u2032', StringComparison.Ordinal) > -1)
{
buffer = buffer.Replace('\u2032', '\''); // prime
}
- if (buffer.IndexOf('\u2033') > -1)
+ if (buffer.IndexOf('\u2033', StringComparison.Ordinal) > -1)
{
buffer = buffer.Replace('\u2033', '\"'); // double prime
}
- if (buffer.IndexOf('\u0060') > -1)
+ if (buffer.IndexOf('\u0060', StringComparison.Ordinal) > -1)
{
buffer = buffer.Replace('\u0060', '\''); // grave accent
}
- if (buffer.IndexOf('\u00B4') > -1)
+ if (buffer.IndexOf('\u00B4', StringComparison.Ordinal) > -1)
{
buffer = buffer.Replace('\u00B4', '\''); // acute accent
}
@@ -3000,7 +3000,6 @@ namespace Emby.Server.Implementations.Data
{
connection.RunInTransaction(db =>
{
-
var statements = PrepareAll(db, statementTexts).ToList();
if (!isReturningZeroItems)
@@ -4670,8 +4669,12 @@ namespace Emby.Server.Implementations.Data
if (query.BlockUnratedItems.Length > 1)
{
- var inClause = string.Join(",", query.BlockUnratedItems.Select(i => "'" + i.ToString() + "'"));
- whereClauses.Add(string.Format("(InheritedParentalRatingValue > 0 or UnratedType not in ({0}))", inClause));
+ var inClause = string.Join(',', query.BlockUnratedItems.Select(i => "'" + i.ToString() + "'"));
+ whereClauses.Add(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ "(InheritedParentalRatingValue > 0 or UnratedType not in ({0}))",
+ inClause));
}
if (query.ExcludeInheritedTags.Length > 0)
@@ -4680,7 +4683,7 @@ namespace Emby.Server.Implementations.Data
if (statement == null)
{
int index = 0;
- string excludedTags = string.Join(",", query.ExcludeInheritedTags.Select(t => paramName + index++));
+ string excludedTags = string.Join(',', query.ExcludeInheritedTags.Select(t => paramName + index++));
whereClauses.Add("((select CleanValue from itemvalues where ItemId=Guid and Type=6 and cleanvalue in (" + excludedTags + ")) is null)");
}
else
diff --git a/Emby.Server.Implementations/HttpServer/FileWriter.cs b/Emby.Server.Implementations/HttpServer/FileWriter.cs
index 590eee1b4..6fce8de44 100644
--- a/Emby.Server.Implementations/HttpServer/FileWriter.cs
+++ b/Emby.Server.Implementations/HttpServer/FileWriter.cs
@@ -29,7 +29,6 @@ namespace Emby.Server.Implementations.HttpServer
private readonly IStreamHelper _streamHelper;
private readonly ILogger _logger;
- private readonly IFileSystem _fileSystem;
/// <summary>
/// The _options.
@@ -49,7 +48,6 @@ namespace Emby.Server.Implementations.HttpServer
}
_streamHelper = streamHelper;
- _fileSystem = fileSystem;
Path = path;
_logger = logger;
diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
index c3428ee62..0d4a789b5 100644
--- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
+++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
@@ -449,7 +449,7 @@ namespace Emby.Server.Implementations.HttpServer
if (string.Equals(httpReq.Verb, "OPTIONS", StringComparison.OrdinalIgnoreCase))
{
httpRes.StatusCode = 200;
- foreach(var (key, value) in GetDefaultCorsHeaders(httpReq))
+ foreach (var (key, value) in GetDefaultCorsHeaders(httpReq))
{
httpRes.Headers.Add(key, value);
}
@@ -486,7 +486,7 @@ namespace Emby.Server.Implementations.HttpServer
var handler = GetServiceHandler(httpReq);
if (handler != null)
{
- await handler.ProcessRequestAsync(this, httpReq, httpRes, _logger, cancellationToken).ConfigureAwait(false);
+ await handler.ProcessRequestAsync(this, httpReq, httpRes, cancellationToken).ConfigureAwait(false);
}
else
{
diff --git a/Emby.Server.Implementations/HttpServer/Security/AuthService.cs b/Emby.Server.Implementations/HttpServer/Security/AuthService.cs
index 318bc6a24..c9f802a51 100644
--- a/Emby.Server.Implementations/HttpServer/Security/AuthService.cs
+++ b/Emby.Server.Implementations/HttpServer/Security/AuthService.cs
@@ -13,26 +13,22 @@ using MediaBrowser.Controller.Security;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Services;
using Microsoft.AspNetCore.Http;
-using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.HttpServer.Security
{
public class AuthService : IAuthService
{
- private readonly ILogger<AuthService> _logger;
private readonly IAuthorizationContext _authorizationContext;
private readonly ISessionManager _sessionManager;
private readonly IServerConfigurationManager _config;
private readonly INetworkManager _networkManager;
public AuthService(
- ILogger<AuthService> logger,
IAuthorizationContext authorizationContext,
IServerConfigurationManager config,
ISessionManager sessionManager,
INetworkManager networkManager)
{
- _logger = logger;
_authorizationContext = authorizationContext;
_config = config;
_sessionManager = sessionManager;
diff --git a/Emby.Server.Implementations/IO/FileRefresher.cs b/Emby.Server.Implementations/IO/FileRefresher.cs
index ef93779aa..fe74f1de7 100644
--- a/Emby.Server.Implementations/IO/FileRefresher.cs
+++ b/Emby.Server.Implementations/IO/FileRefresher.cs
@@ -21,6 +21,7 @@ namespace Emby.Server.Implementations.IO
private readonly List<string> _affectedPaths = new List<string>();
private readonly object _timerLock = new object();
private Timer _timer;
+ private bool _disposed;
public FileRefresher(string path, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, ILogger logger)
{
@@ -213,11 +214,11 @@ namespace Emby.Server.Implementations.IO
}
}
- private bool _disposed;
public void Dispose()
{
_disposed = true;
DisposeTimer();
+ GC.SuppressFinalize(this);
}
}
}
diff --git a/Emby.Server.Implementations/Images/ArtistImageProvider.cs b/Emby.Server.Implementations/Images/ArtistImageProvider.cs
index 52896720e..bf57382ed 100644
--- a/Emby.Server.Implementations/Images/ArtistImageProvider.cs
+++ b/Emby.Server.Implementations/Images/ArtistImageProvider.cs
@@ -11,7 +11,6 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Playlists;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
@@ -25,14 +24,9 @@ namespace Emby.Server.Implementations.Images
/// </summary>
public class ArtistImageProvider : BaseDynamicImageProvider<MusicArtist>
{
- /// <summary>
- /// The library manager.
- /// </summary>
- private readonly ILibraryManager _libraryManager;
-
- public ArtistImageProvider(IFileSystem fileSystem, IProviderManager providerManager, IApplicationPaths applicationPaths, IImageProcessor imageProcessor, ILibraryManager libraryManager) : base(fileSystem, providerManager, applicationPaths, imageProcessor)
+ public ArtistImageProvider(IFileSystem fileSystem, IProviderManager providerManager, IApplicationPaths applicationPaths, IImageProcessor imageProcessor)
+ : base(fileSystem, providerManager, applicationPaths, imageProcessor)
{
- _libraryManager = libraryManager;
}
/// <summary>
diff --git a/Emby.Server.Implementations/Library/IgnorePatterns.cs b/Emby.Server.Implementations/Library/IgnorePatterns.cs
index 6e6ef1359..e30a67593 100644
--- a/Emby.Server.Implementations/Library/IgnorePatterns.cs
+++ b/Emby.Server.Implementations/Library/IgnorePatterns.cs
@@ -18,7 +18,21 @@ namespace Emby.Server.Implementations.Library
{
"**/small.jpg",
"**/albumart.jpg",
- "**/*sample*",
+
+ // We have neither non-greedy matching or character group repetitions, working around that here.
+ // https://github.com/dazinator/DotNet.Glob#patterns
+ // .*/sample\..{1,5}
+ "**/sample.?",
+ "**/sample.??",
+ "**/sample.???", // Matches sample.mkv
+ "**/sample.????", // Matches sample.webm
+ "**/sample.?????",
+ "**/*.sample.?",
+ "**/*.sample.??",
+ "**/*.sample.???",
+ "**/*.sample.????",
+ "**/*.sample.?????",
+ "**/sample/*",
// Directories
"**/metadata/**",
@@ -64,10 +78,13 @@ namespace Emby.Server.Implementations.Library
"**/.grab/**",
"**/.grab",
- // Unix hidden files and directories
- "**/.*/**",
+ // Unix hidden files
"**/.*",
+ // Mac - if you ever remove the above.
+ // "**/._*",
+ // "**/.DS_Store",
+
// thumbs.db
"**/thumbs.db",
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs
index 5957cd8da..9165ede35 100644
--- a/Emby.Server.Implementations/Library/LibraryManager.cs
+++ b/Emby.Server.Implementations/Library/LibraryManager.cs
@@ -517,7 +517,7 @@ namespace Emby.Server.Implementations.Library
{
// Try to normalize paths located underneath program-data in an attempt to make them more portable
key = key.Substring(_configurationManager.ApplicationPaths.ProgramDataPath.Length)
- .TrimStart(new[] { '/', '\\' })
+ .TrimStart('/', '\\')
.Replace('/', '\\');
}
diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs
index 4e1316abf..67cf8bf5b 100644
--- a/Emby.Server.Implementations/Library/MediaSourceManager.cs
+++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs
@@ -46,8 +46,6 @@ namespace Emby.Server.Implementations.Library
private readonly Dictionary<string, ILiveStream> _openStreams = new Dictionary<string, ILiveStream>(StringComparer.OrdinalIgnoreCase);
private readonly SemaphoreSlim _liveStreamSemaphore = new SemaphoreSlim(1, 1);
- private readonly object _disposeLock = new object();
-
private IMediaSourceProvider[] _providers;
public MediaSourceManager(
@@ -623,12 +621,14 @@ namespace Emby.Server.Implementations.Library
if (liveStreamInfo is IDirectStreamProvider)
{
- var info = await _mediaEncoder.GetMediaInfo(new MediaInfoRequest
- {
- MediaSource = mediaSource,
- ExtractChapters = false,
- MediaType = DlnaProfileType.Video
- }, cancellationToken).ConfigureAwait(false);
+ var info = await _mediaEncoder.GetMediaInfo(
+ new MediaInfoRequest
+ {
+ MediaSource = mediaSource,
+ ExtractChapters = false,
+ MediaType = DlnaProfileType.Video
+ },
+ cancellationToken).ConfigureAwait(false);
mediaSource.MediaStreams = info.MediaStreams;
mediaSource.Container = info.Container;
@@ -859,21 +859,21 @@ namespace Emby.Server.Implementations.Library
}
}
- private Tuple<IMediaSourceProvider, string> GetProvider(string key)
+ private (IMediaSourceProvider, string) GetProvider(string key)
{
if (string.IsNullOrEmpty(key))
{
- throw new ArgumentException("key");
+ throw new ArgumentException("Key can't be empty.", nameof(key));
}
var keys = key.Split(new[] { LiveStreamIdDelimeter }, 2);
var provider = _providers.FirstOrDefault(i => string.Equals(i.GetType().FullName.GetMD5().ToString("N", CultureInfo.InvariantCulture), keys[0], StringComparison.OrdinalIgnoreCase));
- var splitIndex = key.IndexOf(LiveStreamIdDelimeter);
+ var splitIndex = key.IndexOf(LiveStreamIdDelimeter, StringComparison.Ordinal);
var keyId = key.Substring(splitIndex + 1);
- return new Tuple<IMediaSourceProvider, string>(provider, keyId);
+ return (provider, keyId);
}
/// <summary>
@@ -893,15 +893,12 @@ namespace Emby.Server.Implementations.Library
{
if (dispose)
{
- lock (_disposeLock)
+ foreach (var key in _openStreams.Keys.ToList())
{
- foreach (var key in _openStreams.Keys.ToList())
- {
- var task = CloseLiveStream(key);
-
- Task.WaitAll(task);
- }
+ CloseLiveStream(key).GetAwaiter().GetResult();
}
+
+ _liveStreamSemaphore.Dispose();
}
}
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
index c8e41001a..3332e1806 100644
--- a/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
@@ -1,6 +1,5 @@
using System.Globalization;
using Emby.Naming.TV;
-using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Globalization;
@@ -13,7 +12,6 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
/// </summary>
public class SeasonResolver : FolderResolver<Season>
{
- private readonly IServerConfigurationManager _config;
private readonly ILibraryManager _libraryManager;
private readonly ILocalizationManager _localization;
private readonly ILogger<SeasonResolver> _logger;
@@ -21,17 +19,14 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
/// <summary>
/// Initializes a new instance of the <see cref="SeasonResolver"/> class.
/// </summary>
- /// <param name="config">The config.</param>
/// <param name="libraryManager">The library manager.</param>
/// <param name="localization">The localization.</param>
/// <param name="logger">The logger.</param>
public SeasonResolver(
- IServerConfigurationManager config,
ILibraryManager libraryManager,
ILocalizationManager localization,
ILogger<SeasonResolver> logger)
{
- _config = config;
_libraryManager = libraryManager;
_localization = localization;
_logger = logger;
diff --git a/Emby.Server.Implementations/Library/UserDataManager.cs b/Emby.Server.Implementations/Library/UserDataManager.cs
index 175b3af57..f9e5e6bbc 100644
--- a/Emby.Server.Implementations/Library/UserDataManager.cs
+++ b/Emby.Server.Implementations/Library/UserDataManager.cs
@@ -13,7 +13,6 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
-using Microsoft.Extensions.Logging;
using Book = MediaBrowser.Controller.Entities.Book;
namespace Emby.Server.Implementations.Library
@@ -28,18 +27,15 @@ namespace Emby.Server.Implementations.Library
private readonly ConcurrentDictionary<string, UserItemData> _userData =
new ConcurrentDictionary<string, UserItemData>(StringComparer.OrdinalIgnoreCase);
- private readonly ILogger<UserDataManager> _logger;
private readonly IServerConfigurationManager _config;
private readonly IUserManager _userManager;
private readonly IUserDataRepository _repository;
public UserDataManager(
- ILogger<UserDataManager> logger,
IServerConfigurationManager config,
IUserManager userManager,
IUserDataRepository repository)
{
- _logger = logger;
_config = config;
_userManager = userManager;
_repository = repository;
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
index 3709f8fe4..77a7069eb 100644
--- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
@@ -461,7 +461,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
private async Task<List<ScheduleDirect.ShowImages>> GetImageForPrograms(
ListingsProviderInfo info,
List<string> programIds,
- CancellationToken cancellationToken)
+ CancellationToken cancellationToken)
{
if (programIds.Count == 0)
{
@@ -474,7 +474,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
{
var imageId = i.Substring(0, 10);
- if (!imageIdString.Contains(imageId))
+ if (!imageIdString.Contains(imageId, StringComparison.Ordinal))
{
imageIdString += "\"" + imageId + "\",";
}
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
index 2e2488e6e..00420bd2a 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
@@ -195,7 +195,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
while (!sr.EndOfStream)
{
string line = StripXML(sr.ReadLine());
- if (line.Contains("Channel"))
+ if (line.Contains("Channel", StringComparison.Ordinal))
{
LiveTvTunerStatus status;
var index = line.IndexOf("Channel", StringComparison.OrdinalIgnoreCase);
@@ -226,6 +226,11 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
private static string StripXML(string source)
{
+ if (string.IsNullOrEmpty(source))
+ {
+ return string.Empty;
+ }
+
char[] buffer = new char[source.Length];
int bufferIndex = 0;
bool inside = false;
@@ -270,7 +275,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
for (int i = 0; i < model.TunerCount; ++i)
{
- var name = string.Format("Tuner {0}", i + 1);
+ var name = string.Format(CultureInfo.InvariantCulture, "Tuner {0}", i + 1);
var currentChannel = "none"; // @todo Get current channel and map back to Station Id
var isAvailable = await manager.CheckTunerAvailability(ipInfo, i, cancellationToken).ConfigureAwait(false);
var status = isAvailable ? LiveTvTunerStatus.Available : LiveTvTunerStatus.LiveTv;
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
index 57c5b7500..d4a88e299 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
@@ -77,7 +77,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
}
}
- public class HdHomerunManager : IDisposable
+ public sealed class HdHomerunManager : IDisposable
{
public const int HdHomeRunPort = 65001;
@@ -105,6 +105,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
StopStreaming(socket).GetAwaiter().GetResult();
}
}
+
+ GC.SuppressFinalize(this);
}
public async Task<bool> CheckTunerAvailability(IPAddress remoteIp, int tuner, CancellationToken cancellationToken)
@@ -162,7 +164,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
}
_activeTuner = i;
- var lockKeyString = string.Format("{0:d}", lockKeyValue);
+ var lockKeyString = string.Format(CultureInfo.InvariantCulture, "{0:d}", lockKeyValue);
var lockkeyMsg = CreateSetMessage(i, "lockkey", lockKeyString, null);
await stream.WriteAsync(lockkeyMsg, 0, lockkeyMsg.Length, cancellationToken).ConfigureAwait(false);
int receivedBytes = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false);
@@ -173,8 +175,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
continue;
}
- var commandList = commands.GetCommands();
- foreach (var command in commandList)
+ foreach (var command in commands.GetCommands())
{
var channelMsg = CreateSetMessage(i, command.Item1, command.Item2, lockKeyValue);
await stream.WriteAsync(channelMsg, 0, channelMsg.Length, cancellationToken).ConfigureAwait(false);
@@ -188,7 +189,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
}
}
- var targetValue = string.Format("rtp://{0}:{1}", localIp, localPort);
+ var targetValue = string.Format(CultureInfo.InvariantCulture, "rtp://{0}:{1}", localIp, localPort);
var targetMsg = CreateSetMessage(i, "target", targetValue, lockKeyValue);
await stream.WriteAsync(targetMsg, 0, targetMsg.Length, cancellationToken).ConfigureAwait(false);
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
index c798c0a85..875977219 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs
@@ -158,15 +158,14 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
private string GetChannelNumber(string extInf, Dictionary<string, string> attributes, string mediaUrl)
{
var nameParts = extInf.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
- var nameInExtInf = nameParts.Length > 1 ? nameParts[nameParts.Length - 1].Trim() : null;
+ var nameInExtInf = nameParts.Length > 1 ? nameParts[^1].AsSpan().Trim() : ReadOnlySpan<char>.Empty;
string numberString = null;
string attributeValue;
- double doubleValue;
if (attributes.TryGetValue("tvg-chno", out attributeValue))
{
- if (double.TryParse(attributeValue, NumberStyles.Any, CultureInfo.InvariantCulture, out doubleValue))
+ if (double.TryParse(attributeValue, NumberStyles.Any, CultureInfo.InvariantCulture, out _))
{
numberString = attributeValue;
}
@@ -176,36 +175,36 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
{
if (attributes.TryGetValue("tvg-id", out attributeValue))
{
- if (double.TryParse(attributeValue, NumberStyles.Any, CultureInfo.InvariantCulture, out doubleValue))
+ if (double.TryParse(attributeValue, NumberStyles.Any, CultureInfo.InvariantCulture, out _))
{
numberString = attributeValue;
}
else if (attributes.TryGetValue("channel-id", out attributeValue))
{
- if (double.TryParse(attributeValue, NumberStyles.Any, CultureInfo.InvariantCulture, out doubleValue))
+ if (double.TryParse(attributeValue, NumberStyles.Any, CultureInfo.InvariantCulture, out _))
{
numberString = attributeValue;
}
}
}
- if (String.IsNullOrWhiteSpace(numberString))
+ if (string.IsNullOrWhiteSpace(numberString))
{
// Using this as a fallback now as this leads to Problems with channels like "5 USA"
// where 5 isnt ment to be the channel number
// Check for channel number with the format from SatIp
// #EXTINF:0,84. VOX Schweiz
// #EXTINF:0,84.0 - VOX Schweiz
- if (!string.IsNullOrWhiteSpace(nameInExtInf))
+ if (!nameInExtInf.IsEmpty && !nameInExtInf.IsWhiteSpace())
{
var numberIndex = nameInExtInf.IndexOf(' ');
if (numberIndex > 0)
{
- var numberPart = nameInExtInf.Substring(0, numberIndex).Trim(new[] { ' ', '.' });
+ var numberPart = nameInExtInf.Slice(0, numberIndex).Trim(new[] { ' ', '.' });
- if (double.TryParse(numberPart, NumberStyles.Any, CultureInfo.InvariantCulture, out var number))
+ if (double.TryParse(numberPart, NumberStyles.Any, CultureInfo.InvariantCulture, out _))
{
- numberString = numberPart;
+ numberString = numberPart.ToString();
}
}
}
@@ -231,7 +230,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
{
try
{
- numberString = Path.GetFileNameWithoutExtension(mediaUrl.Split('/').Last());
+ numberString = Path.GetFileNameWithoutExtension(mediaUrl.Split('/')[^1]);
if (!IsValidChannelNumber(numberString))
{
@@ -258,7 +257,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
return false;
}
- if (!double.TryParse(numberString, NumberStyles.Any, CultureInfo.InvariantCulture, out var value))
+ if (!double.TryParse(numberString, NumberStyles.Any, CultureInfo.InvariantCulture, out _))
{
return false;
}
@@ -281,7 +280,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
{
var numberPart = nameInExtInf.Substring(0, numberIndex).Trim(new[] { ' ', '.' });
- if (double.TryParse(numberPart, NumberStyles.Any, CultureInfo.InvariantCulture, out var number))
+ if (double.TryParse(numberPart, NumberStyles.Any, CultureInfo.InvariantCulture, out _))
{
// channel.Number = number.ToString();
nameInExtInf = nameInExtInf.Substring(numberIndex + 1).Trim(new[] { ' ', '-' });
diff --git a/Emby.Server.Implementations/Localization/Core/zh-TW.json b/Emby.Server.Implementations/Localization/Core/zh-TW.json
index a22f66df9..a21cdad95 100644
--- a/Emby.Server.Implementations/Localization/Core/zh-TW.json
+++ b/Emby.Server.Implementations/Localization/Core/zh-TW.json
@@ -92,7 +92,7 @@
"HeaderRecordingGroups": "錄製組",
"Inherit": "繼承",
"SubtitleDownloadFailureFromForItem": "無法為 {1} 從 {0} 下載字幕",
- "TaskDownloadMissingSubtitlesDescription": "在網路上透過描述資料搜尋遺失的字幕。",
+ "TaskDownloadMissingSubtitlesDescription": "在網路上透過中繼資料搜尋遺失的字幕。",
"TaskDownloadMissingSubtitles": "下載遺失的字幕",
"TaskRefreshChannels": "重新整理頻道",
"TaskUpdatePlugins": "更新插件",
diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs
index 62a23118f..90e2766b8 100644
--- a/Emby.Server.Implementations/Localization/LocalizationManager.cs
+++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs
@@ -247,7 +247,7 @@ namespace Emby.Server.Implementations.Localization
}
// Try splitting by : to handle "Germany: FSK 18"
- var index = rating.IndexOf(':');
+ var index = rating.IndexOf(':', StringComparison.Ordinal);
if (index != -1)
{
rating = rating.Substring(index).TrimStart(':').Trim();
@@ -312,12 +312,12 @@ namespace Emby.Server.Implementations.Localization
throw new ArgumentNullException(nameof(culture));
}
- const string prefix = "Core";
- var key = prefix + culture;
+ const string Prefix = "Core";
+ var key = Prefix + culture;
return _dictionaries.GetOrAdd(
key,
- f => GetDictionary(prefix, culture, DefaultCulture + ".json").GetAwaiter().GetResult());
+ f => GetDictionary(Prefix, culture, DefaultCulture + ".json").GetAwaiter().GetResult());
}
private async Task<Dictionary<string, string>> GetDictionary(string prefix, string culture, string baseFilename)
diff --git a/Emby.Server.Implementations/Net/UdpSocket.cs b/Emby.Server.Implementations/Net/UdpSocket.cs
index b51c03446..4e25768cf 100644
--- a/Emby.Server.Implementations/Net/UdpSocket.cs
+++ b/Emby.Server.Implementations/Net/UdpSocket.cs
@@ -15,13 +15,11 @@ namespace Emby.Server.Implementations.Net
public sealed class UdpSocket : ISocket, IDisposable
{
private Socket _socket;
- private int _localPort;
+ private readonly int _localPort;
private bool _disposed = false;
public Socket Socket => _socket;
- public IPAddress LocalIPAddress { get; }
-
private readonly SocketAsyncEventArgs _receiveSocketAsyncEventArgs = new SocketAsyncEventArgs()
{
SocketFlags = SocketFlags.None
@@ -51,18 +49,33 @@ namespace Emby.Server.Implementations.Net
InitReceiveSocketAsyncEventArgs();
}
+ public UdpSocket(Socket socket, IPEndPoint endPoint)
+ {
+ if (socket == null)
+ {
+ throw new ArgumentNullException(nameof(socket));
+ }
+
+ _socket = socket;
+ _socket.Connect(endPoint);
+
+ InitReceiveSocketAsyncEventArgs();
+ }
+
+ public IPAddress LocalIPAddress { get; }
+
private void InitReceiveSocketAsyncEventArgs()
{
var receiveBuffer = new byte[8192];
_receiveSocketAsyncEventArgs.SetBuffer(receiveBuffer, 0, receiveBuffer.Length);
- _receiveSocketAsyncEventArgs.Completed += _receiveSocketAsyncEventArgs_Completed;
+ _receiveSocketAsyncEventArgs.Completed += OnReceiveSocketAsyncEventArgsCompleted;
var sendBuffer = new byte[8192];
_sendSocketAsyncEventArgs.SetBuffer(sendBuffer, 0, sendBuffer.Length);
- _sendSocketAsyncEventArgs.Completed += _sendSocketAsyncEventArgs_Completed;
+ _sendSocketAsyncEventArgs.Completed += OnSendSocketAsyncEventArgsCompleted;
}
- private void _receiveSocketAsyncEventArgs_Completed(object sender, SocketAsyncEventArgs e)
+ private void OnReceiveSocketAsyncEventArgsCompleted(object sender, SocketAsyncEventArgs e)
{
var tcs = _currentReceiveTaskCompletionSource;
if (tcs != null)
@@ -86,7 +99,7 @@ namespace Emby.Server.Implementations.Net
}
}
- private void _sendSocketAsyncEventArgs_Completed(object sender, SocketAsyncEventArgs e)
+ private void OnSendSocketAsyncEventArgsCompleted(object sender, SocketAsyncEventArgs e)
{
var tcs = _currentSendTaskCompletionSource;
if (tcs != null)
@@ -104,19 +117,6 @@ namespace Emby.Server.Implementations.Net
}
}
- public UdpSocket(Socket socket, IPEndPoint endPoint)
- {
- if (socket == null)
- {
- throw new ArgumentNullException(nameof(socket));
- }
-
- _socket = socket;
- _socket.Connect(endPoint);
-
- InitReceiveSocketAsyncEventArgs();
- }
-
public IAsyncResult BeginReceive(byte[] buffer, int offset, int count, AsyncCallback callback)
{
ThrowIfDisposed();
@@ -247,6 +247,7 @@ namespace Emby.Server.Implementations.Net
}
}
+ /// <inheritdoc />
public void Dispose()
{
if (_disposed)
@@ -255,6 +256,8 @@ namespace Emby.Server.Implementations.Net
}
_socket?.Dispose();
+ _receiveSocketAsyncEventArgs.Dispose();
+ _sendSocketAsyncEventArgs.Dispose();
_currentReceiveTaskCompletionSource?.TrySetCanceled();
_currentSendTaskCompletionSource?.TrySetCanceled();
diff --git a/Emby.Server.Implementations/Networking/NetworkManager.cs b/Emby.Server.Implementations/Networking/NetworkManager.cs
index 50cb44b28..ff95302ee 100644
--- a/Emby.Server.Implementations/Networking/NetworkManager.cs
+++ b/Emby.Server.Implementations/Networking/NetworkManager.cs
@@ -390,7 +390,7 @@ namespace Emby.Server.Implementations.Networking
var host = uri.DnsSafeHost;
_logger.LogDebug("Resolving host {0}", host);
- address = GetIpAddresses(host).Result.FirstOrDefault();
+ address = GetIpAddresses(host).GetAwaiter().GetResult().FirstOrDefault();
if (address != null)
{
diff --git a/Emby.Server.Implementations/Playlists/PlaylistManager.cs b/Emby.Server.Implementations/Playlists/PlaylistManager.cs
index 5dd1af4b8..38ceadedb 100644
--- a/Emby.Server.Implementations/Playlists/PlaylistManager.cs
+++ b/Emby.Server.Implementations/Playlists/PlaylistManager.cs
@@ -349,16 +349,14 @@ namespace Emby.Server.Implementations.Playlists
AlbumTitle = child.Album
};
- var hasAlbumArtist = child as IHasAlbumArtist;
- if (hasAlbumArtist != null)
+ if (child is IHasAlbumArtist hasAlbumArtist)
{
- entry.AlbumArtist = hasAlbumArtist.AlbumArtists.FirstOrDefault();
+ entry.AlbumArtist = hasAlbumArtist.AlbumArtists.Count > 0 ? hasAlbumArtist.AlbumArtists[0] : null;
}
- var hasArtist = child as IHasArtist;
- if (hasArtist != null)
+ if (child is IHasArtist hasArtist)
{
- entry.TrackArtist = hasArtist.Artists.FirstOrDefault();
+ entry.TrackArtist = hasArtist.Artists.Count > 0 ? hasArtist.Artists[0] : null;
}
if (child.RunTimeTicks.HasValue)
@@ -385,16 +383,14 @@ namespace Emby.Server.Implementations.Playlists
AlbumTitle = child.Album
};
- var hasAlbumArtist = child as IHasAlbumArtist;
- if (hasAlbumArtist != null)
+ if (child is IHasAlbumArtist hasAlbumArtist)
{
- entry.AlbumArtist = hasAlbumArtist.AlbumArtists.FirstOrDefault();
+ entry.AlbumArtist = hasAlbumArtist.AlbumArtists.Count > 0 ? hasAlbumArtist.AlbumArtists[0] : null;
}
- var hasArtist = child as IHasArtist;
- if (hasArtist != null)
+ if (child is IHasArtist hasArtist)
{
- entry.TrackArtist = hasArtist.Artists.FirstOrDefault();
+ entry.TrackArtist = hasArtist.Artists.Count > 0 ? hasArtist.Artists[0] : null;
}
if (child.RunTimeTicks.HasValue)
@@ -411,8 +407,10 @@ namespace Emby.Server.Implementations.Playlists
if (string.Equals(".m3u", extension, StringComparison.OrdinalIgnoreCase))
{
- var playlist = new M3uPlaylist();
- playlist.IsExtended = true;
+ var playlist = new M3uPlaylist
+ {
+ IsExtended = true
+ };
foreach (var child in item.GetLinkedChildren())
{
var entry = new M3uPlaylistEntry()
@@ -422,10 +420,9 @@ namespace Emby.Server.Implementations.Playlists
Album = child.Album
};
- var hasAlbumArtist = child as IHasAlbumArtist;
- if (hasAlbumArtist != null)
+ if (child is IHasAlbumArtist hasAlbumArtist)
{
- entry.AlbumArtist = hasAlbumArtist.AlbumArtists.FirstOrDefault();
+ entry.AlbumArtist = hasAlbumArtist.AlbumArtists.Count > 0 ? hasAlbumArtist.AlbumArtists[0] : null;
}
if (child.RunTimeTicks.HasValue)
@@ -453,10 +450,9 @@ namespace Emby.Server.Implementations.Playlists
Album = child.Album
};
- var hasAlbumArtist = child as IHasAlbumArtist;
- if (hasAlbumArtist != null)
+ if (child is IHasAlbumArtist hasAlbumArtist)
{
- entry.AlbumArtist = hasAlbumArtist.AlbumArtists.FirstOrDefault();
+ entry.AlbumArtist = hasAlbumArtist.AlbumArtists.Count > 0 ? hasAlbumArtist.AlbumArtists[0] : null;
}
if (child.RunTimeTicks.HasValue)
@@ -514,7 +510,7 @@ namespace Emby.Server.Implementations.Playlists
if (!folderPath.EndsWith(Path.DirectorySeparatorChar))
{
- folderPath = folderPath + Path.DirectorySeparatorChar;
+ folderPath += Path.DirectorySeparatorChar;
}
var folderUri = new Uri(folderPath);
@@ -537,32 +533,12 @@ namespace Emby.Server.Implementations.Playlists
return relativePath;
}
- private static string UnEscape(string content)
- {
- if (content == null)
- {
- return content;
- }
-
- return content.Replace("&amp;", "&").Replace("&apos;", "'").Replace("&quot;", "\"").Replace("&gt;", ">").Replace("&lt;", "<");
- }
-
- private static string Escape(string content)
- {
- if (content == null)
- {
- return null;
- }
-
- return content.Replace("&", "&amp;").Replace("'", "&apos;").Replace("\"", "&quot;").Replace(">", "&gt;").Replace("<", "&lt;");
- }
-
public Folder GetPlaylistsFolder(Guid userId)
{
- var typeName = "PlaylistsFolder";
+ const string TypeName = "PlaylistsFolder";
- return _libraryManager.RootFolder.Children.OfType<Folder>().FirstOrDefault(i => string.Equals(i.GetType().Name, typeName, StringComparison.Ordinal)) ??
- _libraryManager.GetUserRootFolder().Children.OfType<Folder>().FirstOrDefault(i => string.Equals(i.GetType().Name, typeName, StringComparison.Ordinal));
+ return _libraryManager.RootFolder.Children.OfType<Folder>().FirstOrDefault(i => string.Equals(i.GetType().Name, TypeName, StringComparison.Ordinal)) ??
+ _libraryManager.GetUserRootFolder().Children.OfType<Folder>().FirstOrDefault(i => string.Equals(i.GetType().Name, TypeName, StringComparison.Ordinal));
}
}
}
diff --git a/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs b/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs
index 3fe15ec68..81096026b 100644
--- a/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs
@@ -7,7 +7,6 @@ using System.Linq;
using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.Events;
-using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Tasks;
using Microsoft.Extensions.Logging;
@@ -37,7 +36,6 @@ namespace Emby.Server.Implementations.ScheduledTasks
private readonly IJsonSerializer _jsonSerializer;
private readonly IApplicationPaths _applicationPaths;
private readonly ILogger<TaskManager> _logger;
- private readonly IFileSystem _fileSystem;
/// <summary>
/// Initializes a new instance of the <see cref="TaskManager" /> class.
@@ -45,17 +43,14 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// <param name="applicationPaths">The application paths.</param>
/// <param name="jsonSerializer">The json serializer.</param>
/// <param name="logger">The logger.</param>
- /// <param name="fileSystem">The filesystem manager.</param>
public TaskManager(
IApplicationPaths applicationPaths,
IJsonSerializer jsonSerializer,
- ILogger<TaskManager> logger,
- IFileSystem fileSystem)
+ ILogger<TaskManager> logger)
{
_applicationPaths = applicationPaths;
_jsonSerializer = jsonSerializer;
_logger = logger;
- _fileSystem = fileSystem;
ScheduledTasks = Array.Empty<IScheduledTaskWorker>();
}
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs
index 3854be703..8439f8a99 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs
@@ -14,7 +14,6 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Tasks;
-using Microsoft.Extensions.Logging;
using MediaBrowser.Model.Globalization;
namespace Emby.Server.Implementations.ScheduledTasks
@@ -25,11 +24,6 @@ namespace Emby.Server.Implementations.ScheduledTasks
public class ChapterImagesTask : IScheduledTask
{
/// <summary>
- /// The _logger.
- /// </summary>
- private readonly ILogger<ChapterImagesTask> _logger;
-
- /// <summary>
/// The _library manager.
/// </summary>
private readonly ILibraryManager _libraryManager;
@@ -46,7 +40,6 @@ namespace Emby.Server.Implementations.ScheduledTasks
/// Initializes a new instance of the <see cref="ChapterImagesTask" /> class.
/// </summary>
public ChapterImagesTask(
- ILoggerFactory loggerFactory,
ILibraryManager libraryManager,
IItemRepository itemRepo,
IApplicationPaths appPaths,
@@ -54,7 +47,6 @@ namespace Emby.Server.Implementations.ScheduledTasks
IFileSystem fileSystem,
ILocalizationManager localization)
{
- _logger = loggerFactory.CreateLogger<ChapterImagesTask>();
_libraryManager = libraryManager;
_itemRepo = itemRepo;
_appPaths = appPaths;
diff --git a/Emby.Server.Implementations/Services/ServiceController.cs b/Emby.Server.Implementations/Services/ServiceController.cs
index d884d4f37..47e7261e8 100644
--- a/Emby.Server.Implementations/Services/ServiceController.cs
+++ b/Emby.Server.Implementations/Services/ServiceController.cs
@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
+using System.Globalization;
using System.Threading.Tasks;
using Emby.Server.Implementations.HttpServer;
using MediaBrowser.Model.Services;
@@ -91,12 +92,22 @@ namespace Emby.Server.Implementations.Services
{
if (restPath.Path[0] != '/')
{
- throw new ArgumentException(string.Format("Route '{0}' on '{1}' must start with a '/'", restPath.Path, restPath.RequestType.GetMethodName()));
+ throw new ArgumentException(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ "Route '{0}' on '{1}' must start with a '/'",
+ restPath.Path,
+ restPath.RequestType.GetMethodName()));
}
if (restPath.Path.IndexOfAny(InvalidRouteChars) != -1)
{
- throw new ArgumentException(string.Format("Route '{0}' on '{1}' contains invalid chars. ", restPath.Path, restPath.RequestType.GetMethodName()));
+ throw new ArgumentException(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ "Route '{0}' on '{1}' contains invalid chars. ",
+ restPath.Path,
+ restPath.RequestType.GetMethodName()));
}
if (RestPathMap.TryGetValue(restPath.FirstMatchHashKey, out List<RestPath> pathsAtFirstMatch))
@@ -179,8 +190,7 @@ namespace Emby.Server.Implementations.Services
var service = httpHost.CreateInstance(serviceType);
- var serviceRequiresContext = service as IRequiresRequest;
- if (serviceRequiresContext != null)
+ if (service is IRequiresRequest serviceRequiresContext)
{
serviceRequiresContext.Request = req;
}
diff --git a/Emby.Server.Implementations/Services/ServiceExec.cs b/Emby.Server.Implementations/Services/ServiceExec.cs
index cbc4b754d..7b970627e 100644
--- a/Emby.Server.Implementations/Services/ServiceExec.cs
+++ b/Emby.Server.Implementations/Services/ServiceExec.cs
@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
+using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
@@ -105,7 +106,13 @@ namespace Emby.Server.Implementations.Services
}
var expectedMethodName = actionName.Substring(0, 1) + actionName.Substring(1).ToLowerInvariant();
- throw new NotImplementedException(string.Format("Could not find method named {1}({0}) or Any({0}) on Service {2}", requestDto.GetType().GetMethodName(), expectedMethodName, serviceType.GetMethodName()));
+ throw new NotImplementedException(
+ string.Format(
+ CultureInfo.InvariantCulture,
+ "Could not find method named {1}({0}) or Any({0}) on Service {2}",
+ requestDto.GetType().GetMethodName(),
+ expectedMethodName,
+ serviceType.GetMethodName()));
}
private static async Task<object> GetTaskResult(Task task)
diff --git a/Emby.Server.Implementations/Services/ServiceHandler.cs b/Emby.Server.Implementations/Services/ServiceHandler.cs
index 3d4e1ca77..b4166f771 100644
--- a/Emby.Server.Implementations/Services/ServiceHandler.cs
+++ b/Emby.Server.Implementations/Services/ServiceHandler.cs
@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
+using System.Net.Mime;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
@@ -45,7 +46,7 @@ namespace Emby.Server.Implementations.Services
var pos = pathInfo.LastIndexOf('.');
if (pos != -1)
{
- var format = pathInfo.Substring(pos + 1);
+ var format = pathInfo.AsSpan().Slice(pos + 1);
contentType = GetFormatContentType(format);
if (contentType != null)
{
@@ -56,18 +57,21 @@ namespace Emby.Server.Implementations.Services
return pathInfo;
}
- private static string GetFormatContentType(string format)
+ private static string GetFormatContentType(ReadOnlySpan<char> format)
{
- // built-in formats
- switch (format)
+ if (format.Equals("json", StringComparison.Ordinal))
{
- case "json": return "application/json";
- case "xml": return "application/xml";
- default: return null;
+ return MediaTypeNames.Application.Json;
}
+ else if (format.Equals("xml", StringComparison.Ordinal))
+ {
+ return MediaTypeNames.Application.Xml;
+ }
+
+ return null;
}
- public async Task ProcessRequestAsync(HttpListenerHost httpHost, IRequest httpReq, HttpResponse httpRes, ILogger logger, CancellationToken cancellationToken)
+ public async Task ProcessRequestAsync(HttpListenerHost httpHost, IRequest httpReq, HttpResponse httpRes, CancellationToken cancellationToken)
{
httpReq.Items["__route"] = _restPath;
@@ -76,10 +80,10 @@ namespace Emby.Server.Implementations.Services
httpReq.ResponseContentType = _responseContentType;
}
- var request = await CreateRequest(httpHost, httpReq, _restPath, logger).ConfigureAwait(false);
+ var request = await CreateRequest(httpHost, httpReq, _restPath).ConfigureAwait(false);
httpHost.ApplyRequestFilters(httpReq, httpRes, request);
-
+
httpRes.HttpContext.SetServiceStackRequest(httpReq);
var response = await httpHost.ServiceController.Execute(httpHost, request, httpReq).ConfigureAwait(false);
@@ -92,7 +96,7 @@ namespace Emby.Server.Implementations.Services
await ResponseHelper.WriteToResponse(httpRes, httpReq, response, cancellationToken).ConfigureAwait(false);
}
- public static async Task<object> CreateRequest(HttpListenerHost host, IRequest httpReq, RestPath restPath, ILogger logger)
+ public static async Task<object> CreateRequest(HttpListenerHost host, IRequest httpReq, RestPath restPath)
{
var requestType = restPath.RequestType;
diff --git a/Emby.Server.Implementations/Services/ServicePath.cs b/Emby.Server.Implementations/Services/ServicePath.cs
index bdda7c089..442b2ab1c 100644
--- a/Emby.Server.Implementations/Services/ServicePath.cs
+++ b/Emby.Server.Implementations/Services/ServicePath.cs
@@ -156,7 +156,7 @@ namespace Emby.Server.Implementations.Services
{
var component = components[i];
- if (component.StartsWith(VariablePrefix))
+ if (component.StartsWith(VariablePrefix, StringComparison.Ordinal))
{
var variableName = component.Substring(1, component.Length - 2);
if (variableName[variableName.Length - 1] == WildCardChar)
diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs
index ca9f95c70..862a7296c 100644
--- a/Emby.Server.Implementations/Session/SessionManager.cs
+++ b/Emby.Server.Implementations/Session/SessionManager.cs
@@ -848,8 +848,8 @@ namespace Emby.Server.Implementations.Session
/// </summary>
/// <param name="info">The info.</param>
/// <returns>Task.</returns>
- /// <exception cref="ArgumentNullException">info</exception>
- /// <exception cref="ArgumentOutOfRangeException">positionTicks</exception>
+ /// <exception cref="ArgumentNullException"><c>info</c> is <c>null</c>.</exception>
+ /// <exception cref="ArgumentOutOfRangeException"><c>info.PositionTicks</c> is <c>null</c> or negative.</exception>
public async Task OnPlaybackStopped(PlaybackStopInfo info)
{
CheckDisposed();
diff --git a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs
index b9db6ecd0..8bebd37dc 100644
--- a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs
+++ b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs
@@ -93,7 +93,7 @@ namespace Emby.Server.Implementations.Session
if (session != null)
{
EnsureController(session, e.Argument);
- await KeepAliveWebSocket(e.Argument);
+ await KeepAliveWebSocket(e.Argument).ConfigureAwait(false);
}
else
{
@@ -177,7 +177,7 @@ namespace Emby.Server.Implementations.Session
// Notify WebSocket about timeout
try
{
- await SendForceKeepAlive(webSocket);
+ await SendForceKeepAlive(webSocket).ConfigureAwait(false);
}
catch (WebSocketException exception)
{
@@ -233,6 +233,7 @@ namespace Emby.Server.Implementations.Session
if (_keepAliveCancellationToken != null)
{
_keepAliveCancellationToken.Cancel();
+ _keepAliveCancellationToken.Dispose();
_keepAliveCancellationToken = null;
}
}
@@ -268,7 +269,7 @@ namespace Emby.Server.Implementations.Session
lost = _webSockets.Where(i => (DateTime.UtcNow - i.LastKeepAliveDate).TotalSeconds >= WebSocketLostTimeout).ToList();
}
- if (inactive.Any())
+ if (inactive.Count > 0)
{
_logger.LogInformation("Sending ForceKeepAlive message to {0} inactive WebSockets.", inactive.Count);
}
@@ -277,7 +278,7 @@ namespace Emby.Server.Implementations.Session
{
try
{
- await SendForceKeepAlive(webSocket);
+ await SendForceKeepAlive(webSocket).ConfigureAwait(false);
}
catch (WebSocketException exception)
{
@@ -288,7 +289,7 @@ namespace Emby.Server.Implementations.Session
lock (_webSocketsLock)
{
- if (lost.Any())
+ if (lost.Count > 0)
{
_logger.LogInformation("Lost {0} WebSockets.", lost.Count);
foreach (var webSocket in lost)
@@ -298,7 +299,7 @@ namespace Emby.Server.Implementations.Session
}
}
- if (!_webSockets.Any())
+ if (_webSockets.Count == 0)
{
StopKeepAlive();
}
@@ -312,11 +313,13 @@ namespace Emby.Server.Implementations.Session
/// <returns>Task.</returns>
private Task SendForceKeepAlive(IWebSocketConnection webSocket)
{
- return webSocket.SendAsync(new WebSocketMessage<int>
- {
- MessageType = "ForceKeepAlive",
- Data = WebSocketLostTimeout
- }, CancellationToken.None);
+ return webSocket.SendAsync(
+ new WebSocketMessage<int>
+ {
+ MessageType = "ForceKeepAlive",
+ Data = WebSocketLostTimeout
+ },
+ CancellationToken.None);
}
/// <summary>
@@ -330,12 +333,11 @@ namespace Emby.Server.Implementations.Session
{
while (!cancellationToken.IsCancellationRequested)
{
- await callback();
- Task task = Task.Delay(interval, cancellationToken);
+ await callback().ConfigureAwait(false);
try
{
- await task;
+ await Task.Delay(interval, cancellationToken).ConfigureAwait(false);
}
catch (TaskCanceledException)
{
diff --git a/Emby.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs b/Emby.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs
index 2b7d818be..1f68a9c81 100644
--- a/Emby.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs
+++ b/Emby.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs
@@ -154,8 +154,8 @@ namespace Emby.Server.Implementations.Sorting
private static int CompareEpisodes(Episode x, Episode y)
{
- var xValue = (x.ParentIndexNumber ?? -1) * 1000 + (x.IndexNumber ?? -1);
- var yValue = (y.ParentIndexNumber ?? -1) * 1000 + (y.IndexNumber ?? -1);
+ var xValue = ((x.ParentIndexNumber ?? -1) * 1000) + (x.IndexNumber ?? -1);
+ var yValue = ((y.ParentIndexNumber ?? -1) * 1000) + (y.IndexNumber ?? -1);
return xValue.CompareTo(yValue);
}
diff --git a/Emby.Server.Implementations/SyncPlay/SyncPlayController.cs b/Emby.Server.Implementations/SyncPlay/SyncPlayController.cs
index 39d17833f..80b977731 100644
--- a/Emby.Server.Implementations/SyncPlay/SyncPlayController.cs
+++ b/Emby.Server.Implementations/SyncPlay/SyncPlayController.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -27,14 +28,17 @@ namespace Emby.Server.Implementations.SyncPlay
/// All sessions will receive the message.
/// </summary>
AllGroup = 0,
+
/// <summary>
/// Only the specified session will receive the message.
/// </summary>
CurrentSession = 1,
+
/// <summary>
/// All sessions, except the current one, will receive the message.
/// </summary>
AllExceptCurrentSession = 2,
+
/// <summary>
/// Only sessions that are not buffering will receive the message.
/// </summary>
@@ -56,15 +60,6 @@ namespace Emby.Server.Implementations.SyncPlay
/// </summary>
private readonly GroupInfo _group = new GroupInfo();
- /// <inheritdoc />
- public Guid GetGroupId() => _group.GroupId;
-
- /// <inheritdoc />
- public Guid GetPlayingItemId() => _group.PlayingItem.Id;
-
- /// <inheritdoc />
- public bool IsGroupEmpty() => _group.IsEmpty();
-
/// <summary>
/// Initializes a new instance of the <see cref="SyncPlayController" /> class.
/// </summary>
@@ -78,6 +73,15 @@ namespace Emby.Server.Implementations.SyncPlay
_syncPlayManager = syncPlayManager;
}
+ /// <inheritdoc />
+ public Guid GetGroupId() => _group.GroupId;
+
+ /// <inheritdoc />
+ public Guid GetPlayingItemId() => _group.PlayingItem.Id;
+
+ /// <inheritdoc />
+ public bool IsGroupEmpty() => _group.IsEmpty();
+
/// <summary>
/// Converts DateTime to UTC string.
/// </summary>
@@ -85,7 +89,7 @@ namespace Emby.Server.Implementations.SyncPlay
/// <value>The UTC string.</value>
private string DateToUTCString(DateTime date)
{
- return date.ToUniversalTime().ToString("o");
+ return date.ToUniversalTime().ToString("o", CultureInfo.InvariantCulture);
}
/// <summary>
@@ -94,23 +98,23 @@ namespace Emby.Server.Implementations.SyncPlay
/// <param name="from">The current session.</param>
/// <param name="type">The filtering type.</param>
/// <value>The array of sessions matching the filter.</value>
- private SessionInfo[] FilterSessions(SessionInfo from, BroadcastType type)
+ private IEnumerable<SessionInfo> FilterSessions(SessionInfo from, BroadcastType type)
{
switch (type)
{
case BroadcastType.CurrentSession:
return new SessionInfo[] { from };
case BroadcastType.AllGroup:
- return _group.Participants.Values.Select(
- session => session.Session).ToArray();
+ return _group.Participants.Values
+ .Select(session => session.Session);
case BroadcastType.AllExceptCurrentSession:
- return _group.Participants.Values.Select(
- session => session.Session).Where(
- session => !session.Id.Equals(from.Id)).ToArray();
+ return _group.Participants.Values
+ .Select(session => session.Session)
+ .Where(session => !session.Id.Equals(from.Id, StringComparison.Ordinal));
case BroadcastType.AllReady:
- return _group.Participants.Values.Where(
- session => !session.IsBuffering).Select(
- session => session.Session).ToArray();
+ return _group.Participants.Values
+ .Where(session => !session.IsBuffering)
+ .Select(session => session.Session);
default:
return Array.Empty<SessionInfo>();
}
@@ -128,10 +132,9 @@ namespace Emby.Server.Implementations.SyncPlay
{
IEnumerable<Task> GetTasks()
{
- SessionInfo[] sessions = FilterSessions(from, type);
- foreach (var session in sessions)
+ foreach (var session in FilterSessions(from, type))
{
- yield return _sessionManager.SendSyncPlayGroupUpdate(session.Id.ToString(), message, cancellationToken);
+ yield return _sessionManager.SendSyncPlayGroupUpdate(session.Id, message, cancellationToken);
}
}
@@ -150,10 +153,9 @@ namespace Emby.Server.Implementations.SyncPlay
{
IEnumerable<Task> GetTasks()
{
- SessionInfo[] sessions = FilterSessions(from, type);
- foreach (var session in sessions)
+ foreach (var session in FilterSessions(from, type))
{
- yield return _sessionManager.SendSyncPlayCommand(session.Id.ToString(), message, cancellationToken);
+ yield return _sessionManager.SendSyncPlayCommand(session.Id, message, cancellationToken);
}
}
@@ -236,9 +238,11 @@ namespace Emby.Server.Implementations.SyncPlay
}
else
{
- var playRequest = new PlayRequest();
- playRequest.ItemIds = new Guid[] { _group.PlayingItem.Id };
- playRequest.StartPositionTicks = _group.PositionTicks;
+ var playRequest = new PlayRequest
+ {
+ ItemIds = new Guid[] { _group.PlayingItem.Id },
+ StartPositionTicks = _group.PositionTicks
+ };
var update = NewSyncPlayGroupUpdate(GroupUpdateType.PrepareSession, playRequest);
SendGroupUpdate(session, BroadcastType.CurrentSession, update, cancellationToken);
}
diff --git a/Jellyfin.Drawing.Skia/PercentPlayedDrawer.cs b/Jellyfin.Drawing.Skia/PercentPlayedDrawer.cs
index f2df066ec..6136a2ff9 100644
--- a/Jellyfin.Drawing.Skia/PercentPlayedDrawer.cs
+++ b/Jellyfin.Drawing.Skia/PercentPlayedDrawer.cs
@@ -19,22 +19,18 @@ namespace Jellyfin.Drawing.Skia
/// <param name="percent">The percentage played to display with the indicator.</param>
public static void Process(SKCanvas canvas, ImageDimensions imageSize, double percent)
{
- using (var paint = new SKPaint())
- {
- var endX = imageSize.Width - 1;
- var endY = imageSize.Height - 1;
+ using var paint = new SKPaint();
+ var endX = imageSize.Width - 1;
+ var endY = imageSize.Height - 1;
- paint.Color = SKColor.Parse("#99000000");
- paint.Style = SKPaintStyle.Fill;
- canvas.DrawRect(SKRect.Create(0, (float)endY - IndicatorHeight, (float)endX, (float)endY), paint);
+ paint.Color = SKColor.Parse("#99000000");
+ paint.Style = SKPaintStyle.Fill;
+ canvas.DrawRect(SKRect.Create(0, (float)endY - IndicatorHeight, endX, endY), paint);
- double foregroundWidth = endX;
- foregroundWidth *= percent;
- foregroundWidth /= 100;
+ double foregroundWidth = (endX * percent) / 100;
- paint.Color = SKColor.Parse("#FF00A4DC");
- canvas.DrawRect(SKRect.Create(0, (float)endY - IndicatorHeight, Convert.ToInt32(foregroundWidth), (float)endY), paint);
- }
+ paint.Color = SKColor.Parse("#FF00A4DC");
+ canvas.DrawRect(SKRect.Create(0, (float)endY - IndicatorHeight, Convert.ToInt32(foregroundWidth), endY), paint);
}
}
}
diff --git a/Jellyfin.Drawing.Skia/PlayedIndicatorDrawer.cs b/Jellyfin.Drawing.Skia/PlayedIndicatorDrawer.cs
index 7eed5f4f7..db4f78398 100644
--- a/Jellyfin.Drawing.Skia/PlayedIndicatorDrawer.cs
+++ b/Jellyfin.Drawing.Skia/PlayedIndicatorDrawer.cs
@@ -22,31 +22,27 @@ namespace Jellyfin.Drawing.Skia
{
var x = imageSize.Width - OffsetFromTopRightCorner;
- using (var paint = new SKPaint())
+ using var paint = new SKPaint
{
- paint.Color = SKColor.Parse("#CC00A4DC");
- paint.Style = SKPaintStyle.Fill;
- canvas.DrawCircle(x, OffsetFromTopRightCorner, 20, paint);
- }
+ Color = SKColor.Parse("#CC00A4DC"),
+ Style = SKPaintStyle.Fill
+ };
- using (var paint = new SKPaint())
- {
- paint.Color = new SKColor(255, 255, 255, 255);
- paint.Style = SKPaintStyle.Fill;
+ canvas.DrawCircle(x, OffsetFromTopRightCorner, 20, paint);
- paint.TextSize = 30;
- paint.IsAntialias = true;
+ paint.Color = new SKColor(255, 255, 255, 255);
+ paint.TextSize = 30;
+ paint.IsAntialias = true;
- // or:
- // var emojiChar = 0x1F680;
- const string Text = "✔️";
- var emojiChar = StringUtilities.GetUnicodeCharacterCode(Text, SKTextEncoding.Utf32);
+ // or:
+ // var emojiChar = 0x1F680;
+ const string Text = "✔️";
+ var emojiChar = StringUtilities.GetUnicodeCharacterCode(Text, SKTextEncoding.Utf32);
- // ask the font manager for a font with that character
- paint.Typeface = SKFontManager.Default.MatchCharacter(emojiChar);
+ // ask the font manager for a font with that character
+ paint.Typeface = SKFontManager.Default.MatchCharacter(emojiChar);
- canvas.DrawText(Text, (float)x - 20, OffsetFromTopRightCorner + 12, paint);
- }
+ canvas.DrawText(Text, (float)x - 20, OffsetFromTopRightCorner + 12, paint);
}
}
}
diff --git a/Jellyfin.Drawing.Skia/SkiaCodecException.cs b/Jellyfin.Drawing.Skia/SkiaCodecException.cs
index 1d2db5515..9a50a4d62 100644
--- a/Jellyfin.Drawing.Skia/SkiaCodecException.cs
+++ b/Jellyfin.Drawing.Skia/SkiaCodecException.cs
@@ -12,7 +12,7 @@ namespace Jellyfin.Drawing.Skia
/// Initializes a new instance of the <see cref="SkiaCodecException" /> class.
/// </summary>
/// <param name="result">The non-successful codec result returned by Skia.</param>
- public SkiaCodecException(SKCodecResult result) : base()
+ public SkiaCodecException(SKCodecResult result)
{
CodecResult = result;
}
diff --git a/Jellyfin.Drawing.Skia/SkiaEncoder.cs b/Jellyfin.Drawing.Skia/SkiaEncoder.cs
index ba9a5809f..8f45ba674 100644
--- a/Jellyfin.Drawing.Skia/SkiaEncoder.cs
+++ b/Jellyfin.Drawing.Skia/SkiaEncoder.cs
@@ -29,9 +29,7 @@ namespace Jellyfin.Drawing.Skia
/// </summary>
/// <param name="logger">The application logger.</param>
/// <param name="appPaths">The application paths.</param>
- public SkiaEncoder(
- ILogger<SkiaEncoder> logger,
- IApplicationPaths appPaths)
+ public SkiaEncoder(ILogger<SkiaEncoder> logger, IApplicationPaths appPaths)
{
_logger = logger;
_appPaths = appPaths;
@@ -102,19 +100,14 @@ namespace Jellyfin.Drawing.Skia
/// <returns>The converted format.</returns>
public static SKEncodedImageFormat GetImageFormat(ImageFormat selectedFormat)
{
- switch (selectedFormat)
- {
- case ImageFormat.Bmp:
- return SKEncodedImageFormat.Bmp;
- case ImageFormat.Jpg:
- return SKEncodedImageFormat.Jpeg;
- case ImageFormat.Gif:
- return SKEncodedImageFormat.Gif;
- case ImageFormat.Webp:
- return SKEncodedImageFormat.Webp;
- default:
- return SKEncodedImageFormat.Png;
- }
+ return selectedFormat switch
+ {
+ ImageFormat.Bmp => SKEncodedImageFormat.Bmp,
+ ImageFormat.Jpg => SKEncodedImageFormat.Jpeg,
+ ImageFormat.Gif => SKEncodedImageFormat.Gif,
+ ImageFormat.Webp => SKEncodedImageFormat.Webp,
+ _ => SKEncodedImageFormat.Png
+ };
}
private static bool IsTransparentRow(SKBitmap bmp, int row)
@@ -146,63 +139,34 @@ namespace Jellyfin.Drawing.Skia
private SKBitmap CropWhiteSpace(SKBitmap bitmap)
{
var topmost = 0;
- for (int row = 0; row < bitmap.Height; ++row)
+ while (topmost < bitmap.Height && IsTransparentRow(bitmap, topmost))
{
- if (IsTransparentRow(bitmap, row))
- {
- topmost = row + 1;
- }
- else
- {
- break;
- }
+ topmost++;
}
int bottommost = bitmap.Height;
- for (int row = bitmap.Height - 1; row >= 0; --row)
+ while (bottommost >= 0 && IsTransparentRow(bitmap, bottommost - 1))
{
- if (IsTransparentRow(bitmap, row))
- {
- bottommost = row;
- }
- else
- {
- break;
- }
+ bottommost--;
}
- int leftmost = 0, rightmost = bitmap.Width;
- for (int col = 0; col < bitmap.Width; ++col)
+ var leftmost = 0;
+ while (leftmost < bitmap.Width && IsTransparentColumn(bitmap, leftmost))
{
- if (IsTransparentColumn(bitmap, col))
- {
- leftmost = col + 1;
- }
- else
- {
- break;
- }
+ leftmost++;
}
- for (int col = bitmap.Width - 1; col >= 0; --col)
+ var rightmost = bitmap.Width;
+ while (rightmost >= 0 && IsTransparentColumn(bitmap, rightmost - 1))
{
- if (IsTransparentColumn(bitmap, col))
- {
- rightmost = col;
- }
- else
- {
- break;
- }
+ rightmost--;
}
var newRect = SKRectI.Create(leftmost, topmost, rightmost - leftmost, bottommost - topmost);
- using (var image = SKImage.FromBitmap(bitmap))
- using (var subset = image.Subset(newRect))
- {
- return SKBitmap.FromImage(subset);
- }
+ using var image = SKImage.FromBitmap(bitmap);
+ using var subset = image.Subset(newRect);
+ return SKBitmap.FromImage(subset);
}
/// <inheritdoc />
@@ -216,14 +180,12 @@ namespace Jellyfin.Drawing.Skia
throw new FileNotFoundException("File not found", path);
}
- using (var codec = SKCodec.Create(path, out SKCodecResult result))
- {
- EnsureSuccess(result);
+ using var codec = SKCodec.Create(path, out SKCodecResult result);
+ EnsureSuccess(result);
- var info = codec.Info;
+ var info = codec.Info;
- return new ImageDimensions(info.Width, info.Height);
- }
+ return new ImageDimensions(info.Width, info.Height);
}
/// <inheritdoc />
@@ -253,12 +215,7 @@ namespace Jellyfin.Drawing.Skia
}
}
- if (HasDiacritics(path))
- {
- return true;
- }
-
- return false;
+ return HasDiacritics(path);
}
private string NormalizePath(string path)
@@ -283,25 +240,17 @@ namespace Jellyfin.Drawing.Skia
return SKEncodedOrigin.TopLeft;
}
- switch (orientation.Value)
- {
- case ImageOrientation.TopRight:
- return SKEncodedOrigin.TopRight;
- case ImageOrientation.RightTop:
- return SKEncodedOrigin.RightTop;
- case ImageOrientation.RightBottom:
- return SKEncodedOrigin.RightBottom;
- case ImageOrientation.LeftTop:
- return SKEncodedOrigin.LeftTop;
- case ImageOrientation.LeftBottom:
- return SKEncodedOrigin.LeftBottom;
- case ImageOrientation.BottomRight:
- return SKEncodedOrigin.BottomRight;
- case ImageOrientation.BottomLeft:
- return SKEncodedOrigin.BottomLeft;
- default:
- return SKEncodedOrigin.TopLeft;
- }
+ return orientation.Value switch
+ {
+ ImageOrientation.TopRight => SKEncodedOrigin.TopRight,
+ ImageOrientation.RightTop => SKEncodedOrigin.RightTop,
+ ImageOrientation.RightBottom => SKEncodedOrigin.RightBottom,
+ ImageOrientation.LeftTop => SKEncodedOrigin.LeftTop,
+ ImageOrientation.LeftBottom => SKEncodedOrigin.LeftBottom,
+ ImageOrientation.BottomRight => SKEncodedOrigin.BottomRight,
+ ImageOrientation.BottomLeft => SKEncodedOrigin.BottomLeft,
+ _ => SKEncodedOrigin.TopLeft
+ };
}
/// <summary>
@@ -323,24 +272,22 @@ namespace Jellyfin.Drawing.Skia
if (requiresTransparencyHack || forceCleanBitmap)
{
- using (var codec = SKCodec.Create(NormalizePath(path)))
+ using var codec = SKCodec.Create(NormalizePath(path));
+ if (codec == null)
{
- if (codec == null)
- {
- origin = GetSKEncodedOrigin(orientation);
- return null;
- }
+ origin = GetSKEncodedOrigin(orientation);
+ return null;
+ }
- // create the bitmap
- var bitmap = new SKBitmap(codec.Info.Width, codec.Info.Height, !requiresTransparencyHack);
+ // create the bitmap
+ var bitmap = new SKBitmap(codec.Info.Width, codec.Info.Height, !requiresTransparencyHack);
- // decode
- _ = codec.GetPixels(bitmap.Info, bitmap.GetPixels());
+ // decode
+ _ = codec.GetPixels(bitmap.Info, bitmap.GetPixels());
- origin = codec.EncodedOrigin;
+ origin = codec.EncodedOrigin;
- return bitmap;
- }
+ return bitmap;
}
var resultBitmap = SKBitmap.Decode(NormalizePath(path));
@@ -367,15 +314,8 @@ namespace Jellyfin.Drawing.Skia
{
if (cropWhitespace)
{
- using (var bitmap = Decode(path, forceAnalyzeBitmap, orientation, out origin))
- {
- if (bitmap == null)
- {
- return null;
- }
-
- return CropWhiteSpace(bitmap);
- }
+ using var bitmap = Decode(path, forceAnalyzeBitmap, orientation, out origin);
+ return bitmap == null ? null : CropWhiteSpace(bitmap);
}
return Decode(path, forceAnalyzeBitmap, orientation, out origin);
@@ -403,133 +343,55 @@ namespace Jellyfin.Drawing.Skia
private SKBitmap OrientImage(SKBitmap bitmap, SKEncodedOrigin origin)
{
+ if (origin == SKEncodedOrigin.Default)
+ {
+ return bitmap;
+ }
+
+ var needsFlip = origin == SKEncodedOrigin.LeftBottom
+ || origin == SKEncodedOrigin.LeftTop
+ || origin == SKEncodedOrigin.RightBottom
+ || origin == SKEncodedOrigin.RightTop;
+ var rotated = needsFlip
+ ? new SKBitmap(bitmap.Height, bitmap.Width)
+ : new SKBitmap(bitmap.Width, bitmap.Height);
+ using var surface = new SKCanvas(rotated);
+ var midX = (float)rotated.Width / 2;
+ var midY = (float)rotated.Height / 2;
+
switch (origin)
{
case SKEncodedOrigin.TopRight:
- {
- var rotated = new SKBitmap(bitmap.Width, bitmap.Height);
- using (var surface = new SKCanvas(rotated))
- {
- surface.Translate(rotated.Width, 0);
- surface.Scale(-1, 1);
- surface.DrawBitmap(bitmap, 0, 0);
- }
-
- return rotated;
- }
-
+ surface.Scale(-1, 1, midX, midY);
+ break;
case SKEncodedOrigin.BottomRight:
- {
- var rotated = new SKBitmap(bitmap.Width, bitmap.Height);
- using (var surface = new SKCanvas(rotated))
- {
- float px = (float)bitmap.Width / 2;
- float py = (float)bitmap.Height / 2;
-
- surface.RotateDegrees(180, px, py);
- surface.DrawBitmap(bitmap, 0, 0);
- }
-
- return rotated;
- }
-
+ surface.RotateDegrees(180, midX, midY);
+ break;
case SKEncodedOrigin.BottomLeft:
- {
- var rotated = new SKBitmap(bitmap.Width, bitmap.Height);
- using (var surface = new SKCanvas(rotated))
- {
- float px = (float)bitmap.Width / 2;
-
- float py = (float)bitmap.Height / 2;
-
- surface.Translate(rotated.Width, 0);
- surface.Scale(-1, 1);
-
- surface.RotateDegrees(180, px, py);
- surface.DrawBitmap(bitmap, 0, 0);
- }
-
- return rotated;
- }
-
+ surface.Scale(1, -1, midX, midY);
+ break;
case SKEncodedOrigin.LeftTop:
- {
- // TODO: Remove dual canvases, had trouble with flipping
- using (var rotated = new SKBitmap(bitmap.Height, bitmap.Width))
- {
- using (var surface = new SKCanvas(rotated))
- {
- surface.Translate(rotated.Width, 0);
-
- surface.RotateDegrees(90);
-
- surface.DrawBitmap(bitmap, 0, 0);
- }
-
- var flippedBitmap = new SKBitmap(rotated.Width, rotated.Height);
- using (var flippedCanvas = new SKCanvas(flippedBitmap))
- {
- flippedCanvas.Translate(flippedBitmap.Width, 0);
- flippedCanvas.Scale(-1, 1);
- flippedCanvas.DrawBitmap(rotated, 0, 0);
- }
-
- return flippedBitmap;
- }
- }
-
+ surface.Translate(0, -rotated.Height);
+ surface.Scale(1, -1, midX, midY);
+ surface.RotateDegrees(-90);
+ break;
case SKEncodedOrigin.RightTop:
- {
- var rotated = new SKBitmap(bitmap.Height, bitmap.Width);
- using (var surface = new SKCanvas(rotated))
- {
- surface.Translate(rotated.Width, 0);
- surface.RotateDegrees(90);
- surface.DrawBitmap(bitmap, 0, 0);
- }
-
- return rotated;
- }
-
+ surface.Translate(rotated.Width, 0);
+ surface.RotateDegrees(90);
+ break;
case SKEncodedOrigin.RightBottom:
- {
- // TODO: Remove dual canvases, had trouble with flipping
- using (var rotated = new SKBitmap(bitmap.Height, bitmap.Width))
- {
- using (var surface = new SKCanvas(rotated))
- {
- surface.Translate(0, rotated.Height);
- surface.RotateDegrees(270);
- surface.DrawBitmap(bitmap, 0, 0);
- }
-
- var flippedBitmap = new SKBitmap(rotated.Width, rotated.Height);
- using (var flippedCanvas = new SKCanvas(flippedBitmap))
- {
- flippedCanvas.Translate(flippedBitmap.Width, 0);
- flippedCanvas.Scale(-1, 1);
- flippedCanvas.DrawBitmap(rotated, 0, 0);
- }
-
- return flippedBitmap;
- }
- }
-
+ surface.Translate(rotated.Width, 0);
+ surface.Scale(1, -1, midX, midY);
+ surface.RotateDegrees(90);
+ break;
case SKEncodedOrigin.LeftBottom:
- {
- var rotated = new SKBitmap(bitmap.Height, bitmap.Width);
- using (var surface = new SKCanvas(rotated))
- {
- surface.Translate(0, rotated.Height);
- surface.RotateDegrees(270);
- surface.DrawBitmap(bitmap, 0, 0);
- }
-
- return rotated;
- }
-
- default: return bitmap;
+ surface.Translate(0, rotated.Height);
+ surface.RotateDegrees(-90);
+ break;
}
+
+ surface.DrawBitmap(bitmap, 0, 0);
+ return rotated;
}
/// <inheritdoc/>
@@ -552,97 +414,87 @@ namespace Jellyfin.Drawing.Skia
var blur = options.Blur ?? 0;
var hasIndicator = options.AddPlayedIndicator || options.UnplayedCount.HasValue || !options.PercentPlayed.Equals(0);
- using (var bitmap = GetBitmap(inputPath, options.CropWhiteSpace, autoOrient, orientation))
+ using var bitmap = GetBitmap(inputPath, options.CropWhiteSpace, autoOrient, orientation);
+ if (bitmap == null)
{
- if (bitmap == null)
- {
- throw new InvalidDataException($"Skia unable to read image {inputPath}");
- }
+ throw new InvalidDataException($"Skia unable to read image {inputPath}");
+ }
- var originalImageSize = new ImageDimensions(bitmap.Width, bitmap.Height);
+ var originalImageSize = new ImageDimensions(bitmap.Width, bitmap.Height);
- if (!options.CropWhiteSpace
- && options.HasDefaultOptions(inputPath, originalImageSize)
- && !autoOrient)
- {
- // Just spit out the original file if all the options are default
- return inputPath;
- }
+ if (!options.CropWhiteSpace
+ && options.HasDefaultOptions(inputPath, originalImageSize)
+ && !autoOrient)
+ {
+ // Just spit out the original file if all the options are default
+ return inputPath;
+ }
- var newImageSize = ImageHelper.GetNewImageSize(options, originalImageSize);
+ var newImageSize = ImageHelper.GetNewImageSize(options, originalImageSize);
- var width = newImageSize.Width;
- var height = newImageSize.Height;
+ var width = newImageSize.Width;
+ var height = newImageSize.Height;
+
+ using var resizedBitmap = new SKBitmap(width, height, bitmap.ColorType, bitmap.AlphaType);
+ // scale image
+ bitmap.ScalePixels(resizedBitmap, SKFilterQuality.High);
+
+ // If all we're doing is resizing then we can stop now
+ if (!hasBackgroundColor && !hasForegroundColor && blur == 0 && !hasIndicator)
+ {
+ Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
+ using var outputStream = new SKFileWStream(outputPath);
+ using var pixmap = new SKPixmap(new SKImageInfo(width, height), resizedBitmap.GetPixels());
+ pixmap.Encode(outputStream, skiaOutputFormat, quality);
+ return outputPath;
+ }
- using (var resizedBitmap = new SKBitmap(width, height, bitmap.ColorType, bitmap.AlphaType))
+ // create bitmap to use for canvas drawing used to draw into bitmap
+ using var saveBitmap = new SKBitmap(width, height);
+ using var canvas = new SKCanvas(saveBitmap);
+ // set background color if present
+ if (hasBackgroundColor)
+ {
+ canvas.Clear(SKColor.Parse(options.BackgroundColor));
+ }
+
+ // Add blur if option is present
+ if (blur > 0)
+ {
+ // create image from resized bitmap to apply blur
+ using var paint = new SKPaint();
+ using var filter = SKImageFilter.CreateBlur(blur, blur);
+ paint.ImageFilter = filter;
+ canvas.DrawBitmap(resizedBitmap, SKRect.Create(width, height), paint);
+ }
+ else
+ {
+ // draw resized bitmap onto canvas
+ canvas.DrawBitmap(resizedBitmap, SKRect.Create(width, height));
+ }
+
+ // If foreground layer present then draw
+ if (hasForegroundColor)
+ {
+ if (!double.TryParse(options.ForegroundLayer, out double opacity))
{
- // scale image
- bitmap.ScalePixels(resizedBitmap, SKFilterQuality.High);
+ opacity = .4;
+ }
- // If all we're doing is resizing then we can stop now
- if (!hasBackgroundColor && !hasForegroundColor && blur == 0 && !hasIndicator)
- {
- Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
- using (var outputStream = new SKFileWStream(outputPath))
- using (var pixmap = new SKPixmap(new SKImageInfo(width, height), resizedBitmap.GetPixels()))
- {
- pixmap.Encode(outputStream, skiaOutputFormat, quality);
- return outputPath;
- }
- }
+ canvas.DrawColor(new SKColor(0, 0, 0, (byte)((1 - opacity) * 0xFF)), SKBlendMode.SrcOver);
+ }
- // create bitmap to use for canvas drawing used to draw into bitmap
- using (var saveBitmap = new SKBitmap(width, height)) // , bitmap.ColorType, bitmap.AlphaType))
- using (var canvas = new SKCanvas(saveBitmap))
- {
- // set background color if present
- if (hasBackgroundColor)
- {
- canvas.Clear(SKColor.Parse(options.BackgroundColor));
- }
-
- // Add blur if option is present
- if (blur > 0)
- {
- // create image from resized bitmap to apply blur
- using (var paint = new SKPaint())
- using (var filter = SKImageFilter.CreateBlur(blur, blur))
- {
- paint.ImageFilter = filter;
- canvas.DrawBitmap(resizedBitmap, SKRect.Create(width, height), paint);
- }
- }
- else
- {
- // draw resized bitmap onto canvas
- canvas.DrawBitmap(resizedBitmap, SKRect.Create(width, height));
- }
-
- // If foreground layer present then draw
- if (hasForegroundColor)
- {
- if (!double.TryParse(options.ForegroundLayer, out double opacity))
- {
- opacity = .4;
- }
-
- canvas.DrawColor(new SKColor(0, 0, 0, (byte)((1 - opacity) * 0xFF)), SKBlendMode.SrcOver);
- }
-
- if (hasIndicator)
- {
- DrawIndicator(canvas, width, height, options);
- }
-
- Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
- using (var outputStream = new SKFileWStream(outputPath))
- {
- using (var pixmap = new SKPixmap(new SKImageInfo(width, height), saveBitmap.GetPixels()))
- {
- pixmap.Encode(outputStream, skiaOutputFormat, quality);
- }
- }
- }
+ if (hasIndicator)
+ {
+ DrawIndicator(canvas, width, height, options);
+ }
+
+ Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
+ using (var outputStream = new SKFileWStream(outputPath))
+ {
+ using (var pixmap = new SKPixmap(new SKImageInfo(width, height), saveBitmap.GetPixels()))
+ {
+ pixmap.Encode(outputStream, skiaOutputFormat, quality);
}
}
diff --git a/Jellyfin.Drawing.Skia/SkiaException.cs b/Jellyfin.Drawing.Skia/SkiaException.cs
index 968d3a244..5b272eac5 100644
--- a/Jellyfin.Drawing.Skia/SkiaException.cs
+++ b/Jellyfin.Drawing.Skia/SkiaException.cs
@@ -10,7 +10,7 @@ namespace Jellyfin.Drawing.Skia
/// <summary>
/// Initializes a new instance of the <see cref="SkiaException"/> class.
/// </summary>
- public SkiaException() : base()
+ public SkiaException()
{
}
diff --git a/Jellyfin.Drawing.Skia/StripCollageBuilder.cs b/Jellyfin.Drawing.Skia/StripCollageBuilder.cs
index 61bef90ec..e0ee4a342 100644
--- a/Jellyfin.Drawing.Skia/StripCollageBuilder.cs
+++ b/Jellyfin.Drawing.Skia/StripCollageBuilder.cs
@@ -69,12 +69,10 @@ namespace Jellyfin.Drawing.Skia
/// <param name="height">The desired height of the collage.</param>
public void BuildSquareCollage(string[] paths, string outputPath, int width, int height)
{
- using (var bitmap = BuildSquareCollageBitmap(paths, width, height))
- using (var outputStream = new SKFileWStream(outputPath))
- using (var pixmap = new SKPixmap(new SKImageInfo(width, height), bitmap.GetPixels()))
- {
- pixmap.Encode(outputStream, GetEncodedFormat(outputPath), 90);
- }
+ using var bitmap = BuildSquareCollageBitmap(paths, width, height);
+ using var outputStream = new SKFileWStream(outputPath);
+ using var pixmap = new SKPixmap(new SKImageInfo(width, height), bitmap.GetPixels());
+ pixmap.Encode(outputStream, GetEncodedFormat(outputPath), 90);
}
/// <summary>
@@ -86,56 +84,46 @@ namespace Jellyfin.Drawing.Skia
/// <param name="height">The desired height of the collage.</param>
public void BuildThumbCollage(string[] paths, string outputPath, int width, int height)
{
- using (var bitmap = BuildThumbCollageBitmap(paths, width, height))
- using (var outputStream = new SKFileWStream(outputPath))
- using (var pixmap = new SKPixmap(new SKImageInfo(width, height), bitmap.GetPixels()))
- {
- pixmap.Encode(outputStream, GetEncodedFormat(outputPath), 90);
- }
+ using var bitmap = BuildThumbCollageBitmap(paths, width, height);
+ using var outputStream = new SKFileWStream(outputPath);
+ using var pixmap = new SKPixmap(new SKImageInfo(width, height), bitmap.GetPixels());
+ pixmap.Encode(outputStream, GetEncodedFormat(outputPath), 90);
}
private SKBitmap BuildThumbCollageBitmap(string[] paths, int width, int height)
{
var bitmap = new SKBitmap(width, height);
- using (var canvas = new SKCanvas(bitmap))
- {
- canvas.Clear(SKColors.Black);
+ using var canvas = new SKCanvas(bitmap);
+ canvas.Clear(SKColors.Black);
- // number of images used in the thumbnail
- var iCount = 3;
+ // number of images used in the thumbnail
+ var iCount = 3;
- // determine sizes for each image that will composited into the final image
- var iSlice = Convert.ToInt32(width / iCount);
- int iHeight = Convert.ToInt32(height * 1.00);
- int imageIndex = 0;
- for (int i = 0; i < iCount; i++)
+ // determine sizes for each image that will composited into the final image
+ var iSlice = Convert.ToInt32(width / iCount);
+ int iHeight = Convert.ToInt32(height * 1.00);
+ int imageIndex = 0;
+ for (int i = 0; i < iCount; i++)
+ {
+ using var currentBitmap = GetNextValidImage(paths, imageIndex, out int newIndex);
+ imageIndex = newIndex;
+ if (currentBitmap == null)
{
- using (var currentBitmap = GetNextValidImage(paths, imageIndex, out int newIndex))
- {
- imageIndex = newIndex;
- if (currentBitmap == null)
- {
- continue;
- }
-
- // resize to the same aspect as the original
- int iWidth = Math.Abs(iHeight * currentBitmap.Width / currentBitmap.Height);
- using (var resizeBitmap = new SKBitmap(iWidth, iHeight, currentBitmap.ColorType, currentBitmap.AlphaType))
- {
- currentBitmap.ScalePixels(resizeBitmap, SKFilterQuality.High);
-
- // crop image
- int ix = Math.Abs((iWidth - iSlice) / 2);
- using (var image = SKImage.FromBitmap(resizeBitmap))
- using (var subset = image.Subset(SKRectI.Create(ix, 0, iSlice, iHeight)))
- {
- // draw image onto canvas
- canvas.DrawImage(subset ?? image, iSlice * i, 0);
- }
- }
- }
+ continue;
}
+
+ // resize to the same aspect as the original
+ int iWidth = Math.Abs(iHeight * currentBitmap.Width / currentBitmap.Height);
+ using var resizeBitmap = new SKBitmap(iWidth, iHeight, currentBitmap.ColorType, currentBitmap.AlphaType);
+ currentBitmap.ScalePixels(resizeBitmap, SKFilterQuality.High);
+
+ // crop image
+ int ix = Math.Abs((iWidth - iSlice) / 2);
+ using var image = SKImage.FromBitmap(resizeBitmap);
+ using var subset = image.Subset(SKRectI.Create(ix, 0, iSlice, iHeight));
+ // draw image onto canvas
+ canvas.DrawImage(subset ?? image, iSlice * i, 0);
}
return bitmap;
@@ -176,33 +164,27 @@ namespace Jellyfin.Drawing.Skia
var cellWidth = width / 2;
var cellHeight = height / 2;
- using (var canvas = new SKCanvas(bitmap))
+ using var canvas = new SKCanvas(bitmap);
+ for (var x = 0; x < 2; x++)
{
- for (var x = 0; x < 2; x++)
+ for (var y = 0; y < 2; y++)
{
- for (var y = 0; y < 2; y++)
+ using var currentBitmap = GetNextValidImage(paths, imageIndex, out int newIndex);
+ imageIndex = newIndex;
+
+ if (currentBitmap == null)
{
- using (var currentBitmap = GetNextValidImage(paths, imageIndex, out int newIndex))
- {
- imageIndex = newIndex;
-
- if (currentBitmap == null)
- {
- continue;
- }
-
- using (var resizedBitmap = new SKBitmap(cellWidth, cellHeight, currentBitmap.ColorType, currentBitmap.AlphaType))
- {
- // scale image
- currentBitmap.ScalePixels(resizedBitmap, SKFilterQuality.High);
-
- // draw this image into the strip at the next position
- var xPos = x * cellWidth;
- var yPos = y * cellHeight;
- canvas.DrawBitmap(resizedBitmap, xPos, yPos);
- }
- }
+ continue;
}
+
+ using var resizedBitmap = new SKBitmap(cellWidth, cellHeight, currentBitmap.ColorType, currentBitmap.AlphaType);
+ // scale image
+ currentBitmap.ScalePixels(resizedBitmap, SKFilterQuality.High);
+
+ // draw this image into the strip at the next position
+ var xPos = x * cellWidth;
+ var yPos = y * cellHeight;
+ canvas.DrawBitmap(resizedBitmap, xPos, yPos);
}
}
diff --git a/Jellyfin.Drawing.Skia/UnplayedCountIndicator.cs b/Jellyfin.Drawing.Skia/UnplayedCountIndicator.cs
index cf3dbde2c..58f887c96 100644
--- a/Jellyfin.Drawing.Skia/UnplayedCountIndicator.cs
+++ b/Jellyfin.Drawing.Skia/UnplayedCountIndicator.cs
@@ -28,41 +28,37 @@ namespace Jellyfin.Drawing.Skia
var x = imageSize.Width - OffsetFromTopRightCorner;
var text = count.ToString(CultureInfo.InvariantCulture);
- using (var paint = new SKPaint())
+ using var paint = new SKPaint
{
- paint.Color = SKColor.Parse("#CC00A4DC");
- paint.Style = SKPaintStyle.Fill;
- canvas.DrawCircle(x, OffsetFromTopRightCorner, 20, paint);
- }
-
- using (var paint = new SKPaint())
- {
- paint.Color = new SKColor(255, 255, 255, 255);
- paint.Style = SKPaintStyle.Fill;
+ Color = SKColor.Parse("#CC00A4DC"),
+ Style = SKPaintStyle.Fill
+ };
- paint.TextSize = 24;
- paint.IsAntialias = true;
+ canvas.DrawCircle(x, OffsetFromTopRightCorner, 20, paint);
- var y = OffsetFromTopRightCorner + 9;
+ paint.Color = new SKColor(255, 255, 255, 255);
+ paint.TextSize = 24;
+ paint.IsAntialias = true;
- if (text.Length == 1)
- {
- x -= 7;
- }
+ var y = OffsetFromTopRightCorner + 9;
- if (text.Length == 2)
- {
- x -= 13;
- }
- else if (text.Length >= 3)
- {
- x -= 15;
- y -= 2;
- paint.TextSize = 18;
- }
+ if (text.Length == 1)
+ {
+ x -= 7;
+ }
- canvas.DrawText(text, x, y, paint);
+ if (text.Length == 2)
+ {
+ x -= 13;
+ }
+ else if (text.Length >= 3)
+ {
+ x -= 15;
+ y -= 2;
+ paint.TextSize = 18;
}
+
+ canvas.DrawText(text, x, y, paint);
}
}
}
diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs
index e49a99fe5..eaa6a0a81 100644
--- a/Jellyfin.Server.Implementations/Users/UserManager.cs
+++ b/Jellyfin.Server.Implementations/Users/UserManager.cs
@@ -766,8 +766,8 @@ namespace Jellyfin.Server.Implementations.Users
{
// This is some regex that matches only on unicode "word" characters, as well as -, _ and @
// In theory this will cut out most if not all 'control' characters which should help minimize any weirdness
- // Usernames can contain letters (a-z + whatever else unicode is cool with), numbers (0-9), at-signs (@), dashes (-), underscores (_), apostrophes ('), and periods (.)
- return Regex.IsMatch(name, @"^[\w\-'._@]*$");
+ // Usernames can contain letters (a-z + whatever else unicode is cool with), numbers (0-9), at-signs (@), dashes (-), underscores (_), apostrophes ('), periods (.) and spaces ( )
+ return Regex.IsMatch(name, @"^[\w\ \-'._@]*$");
}
private IAuthenticationProvider GetAuthenticationProvider(User user)
diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
index fe5f980b1..6ff96ac56 100644
--- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
@@ -369,7 +369,7 @@ namespace MediaBrowser.Api.Playback.Hls
var playlistFilename = Path.GetFileNameWithoutExtension(playlist);
- 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/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs
index 90f62e153..f34309c40 100644
--- a/MediaBrowser.Controller/Entities/BaseItem.cs
+++ b/MediaBrowser.Controller/Entities/BaseItem.cs
@@ -675,11 +675,11 @@ namespace MediaBrowser.Controller.Entities
return System.IO.Path.Combine(basePath, "channels", ChannelId.ToString("N", CultureInfo.InvariantCulture), Id.ToString("N", CultureInfo.InvariantCulture));
}
- var idString = Id.ToString("N", CultureInfo.InvariantCulture);
+ ReadOnlySpan<char> idString = Id.ToString("N", CultureInfo.InvariantCulture);
basePath = System.IO.Path.Combine(basePath, "library");
- return System.IO.Path.Combine(basePath, idString.Substring(0, 2), idString);
+ return System.IO.Path.Join(basePath, idString.Slice(0, 2), idString);
}
/// <summary>
@@ -702,26 +702,27 @@ namespace MediaBrowser.Controller.Entities
foreach (var removeChar in ConfigurationManager.Configuration.SortRemoveCharacters)
{
- sortable = sortable.Replace(removeChar, string.Empty);
+ sortable = sortable.Replace(removeChar, string.Empty, StringComparison.Ordinal);
}
foreach (var replaceChar in ConfigurationManager.Configuration.SortReplaceCharacters)
{
- sortable = sortable.Replace(replaceChar, " ");
+ sortable = sortable.Replace(replaceChar, " ", StringComparison.Ordinal);
}
foreach (var search in ConfigurationManager.Configuration.SortRemoveWords)
{
// Remove from beginning if a space follows
- if (sortable.StartsWith(search + " "))
+ if (sortable.StartsWith(search + " ", StringComparison.Ordinal))
{
sortable = sortable.Remove(0, search.Length + 1);
}
+
// Remove from middle if surrounded by spaces
- sortable = sortable.Replace(" " + search + " ", " ");
+ sortable = sortable.Replace(" " + search + " ", " ", StringComparison.Ordinal);
// Remove from end if followed by a space
- if (sortable.EndsWith(" " + search))
+ if (sortable.EndsWith(" " + search, StringComparison.Ordinal))
{
sortable = sortable.Remove(sortable.Length - (search.Length + 1));
}
@@ -751,6 +752,7 @@ namespace MediaBrowser.Controller.Entities
builder.Append(chunkBuilder);
}
+
// logger.LogDebug("ModifySortChunks Start: {0} End: {1}", name, builder.ToString());
return builder.ToString().RemoveDiacritics();
}
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index 0242338b7..913e171f1 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -456,6 +456,7 @@ namespace MediaBrowser.Controller.MediaEncoding
var isQsvEncoder = outputVideoCodec.IndexOf("qsv", StringComparison.OrdinalIgnoreCase) != -1;
var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
var isLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
+ var isMacOS = RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
if (!IsCopyCodec(outputVideoCodec))
{
@@ -511,6 +512,12 @@ namespace MediaBrowser.Controller.MediaEncoding
}
}
}
+
+ if (state.IsVideoRequest
+ && string.Equals(encodingOptions.HardwareAccelerationType, "videotoolbox", StringComparison.OrdinalIgnoreCase))
+ {
+ arg.Append("-hwaccel videotoolbox ");
+ }
}
arg.Append("-i ")
@@ -1588,7 +1595,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
outputVideoCodec ??= string.Empty;
- var outputSizeParam = string.Empty;
+ var outputSizeParam = ReadOnlySpan<char>.Empty;
var request = state.BaseRequest;
// Add resolution params, if specified
@@ -1602,42 +1609,42 @@ namespace MediaBrowser.Controller.MediaEncoding
var index = outputSizeParam.IndexOf("hwdownload", StringComparison.OrdinalIgnoreCase);
if (index != -1)
{
- outputSizeParam = outputSizeParam.Substring(index);
+ outputSizeParam = outputSizeParam.Slice(index);
}
else
{
index = outputSizeParam.IndexOf("hwupload=extra_hw_frames", StringComparison.OrdinalIgnoreCase);
if (index != -1)
{
- outputSizeParam = outputSizeParam.Substring(index);
+ outputSizeParam = outputSizeParam.Slice(index);
}
else
{
index = outputSizeParam.IndexOf("format", StringComparison.OrdinalIgnoreCase);
if (index != -1)
{
- outputSizeParam = outputSizeParam.Substring(index);
+ outputSizeParam = outputSizeParam.Slice(index);
}
else
{
index = outputSizeParam.IndexOf("yadif", StringComparison.OrdinalIgnoreCase);
if (index != -1)
{
- outputSizeParam = outputSizeParam.Substring(index);
+ outputSizeParam = outputSizeParam.Slice(index);
}
else
{
index = outputSizeParam.IndexOf("scale", StringComparison.OrdinalIgnoreCase);
if (index != -1)
{
- outputSizeParam = outputSizeParam.Substring(index);
+ outputSizeParam = outputSizeParam.Slice(index);
}
else
{
index = outputSizeParam.IndexOf("vpp", StringComparison.OrdinalIgnoreCase);
if (index != -1)
{
- outputSizeParam = outputSizeParam.Substring(index);
+ outputSizeParam = outputSizeParam.Slice(index);
}
}
}
@@ -1685,9 +1692,9 @@ namespace MediaBrowser.Controller.MediaEncoding
// Setup default filtergraph utilizing FFMpeg overlay() and FFMpeg scale() (see the return of this function for index reference)
// Always put the scaler before the overlay for better performance
- var retStr = !string.IsNullOrEmpty(outputSizeParam) ?
- " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay\"" :
- " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay\"";
+ var retStr = !outputSizeParam.IsEmpty
+ ? " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay\""
+ : " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay\"";
// When the input may or may not be hardware VAAPI decodable
if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
@@ -1721,7 +1728,7 @@ namespace MediaBrowser.Controller.MediaEncoding
*/
if (isLinux)
{
- retStr = !string.IsNullOrEmpty(outputSizeParam) ?
+ retStr = !outputSizeParam.IsEmpty ?
" -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay_qsv\"" :
" -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay_qsv\"";
}
@@ -1733,7 +1740,7 @@ namespace MediaBrowser.Controller.MediaEncoding
mapPrefix,
subtitleStreamIndex,
state.VideoStream.Index,
- outputSizeParam,
+ outputSizeParam.ToString(),
videoSizeParam);
}
@@ -2120,7 +2127,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|| state.DeInterlace("h265", true)
|| state.DeInterlace("hevc", true))
{
- var deintParam = string.Empty;
+ string deintParam;
var inputFramerate = videoStream?.RealFrameRate;
// If it is already 60fps then it will create an output framerate that is much too high for roku and others to handle
@@ -2183,7 +2190,6 @@ namespace MediaBrowser.Controller.MediaEncoding
return output;
}
-
/// <summary>
/// Gets the number of threads.
/// </summary>
diff --git a/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs b/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs
index 0e2d70017..43a45291c 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs
@@ -35,7 +35,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
continue;
}
- if (line.StartsWith("["))
+ if (line[0] == '[')
{
break;
}
@@ -62,7 +62,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
return trackInfo;
}
- long GetTicks(string time)
+ private long GetTicks(ReadOnlySpan<char> time)
{
return TimeSpan.TryParseExact(time, @"h\:mm\:ss\.ff", _usCulture, out var span)
? span.Ticks : 0;
diff --git a/MediaBrowser.MediaEncoding/Subtitles/ParserValues.cs b/MediaBrowser.MediaEncoding/Subtitles/ParserValues.cs
index bf8808eb8..dca5c1e8a 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/ParserValues.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/ParserValues.cs
@@ -1,6 +1,9 @@
+#nullable enable
+#pragma warning disable CS1591
+
namespace MediaBrowser.MediaEncoding.Subtitles
{
- public class ParserValues
+ public static class ParserValues
{
public const string NewLine = "\r\n";
}
diff --git a/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs
index 728efa788..cc35efb3f 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -20,6 +22,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
_logger = logger;
}
+ /// <inheritdoc />
public SubtitleTrackInfo Parse(Stream stream, CancellationToken cancellationToken)
{
var trackInfo = new SubtitleTrackInfo();
@@ -55,11 +58,11 @@ namespace MediaBrowser.MediaEncoding.Subtitles
}
subEvent.StartPositionTicks = GetTicks(time[0]);
- var endTime = time[1];
- var idx = endTime.IndexOf(" ", StringComparison.Ordinal);
+ var endTime = time[1].AsSpan();
+ var idx = endTime.IndexOf(' ');
if (idx > 0)
{
- endTime = endTime.Substring(0, idx);
+ endTime = endTime.Slice(0, idx);
}
subEvent.EndPositionTicks = GetTicks(endTime);
@@ -88,7 +91,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
return trackInfo;
}
- long GetTicks(string time)
+ private long GetTicks(ReadOnlySpan<char> time)
{
return TimeSpan.TryParseExact(time, @"hh\:mm\:ss\.fff", _usCulture, out var span)
? span.Ticks
diff --git a/MediaBrowser.MediaEncoding/Subtitles/SrtWriter.cs b/MediaBrowser.MediaEncoding/Subtitles/SrtWriter.cs
index 45b317b2e..143c010b7 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/SrtWriter.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/SrtWriter.cs
@@ -8,8 +8,12 @@ using MediaBrowser.Model.MediaInfo;
namespace MediaBrowser.MediaEncoding.Subtitles
{
+ /// <summary>
+ /// SRT subtitle writer.
+ /// </summary>
public class SrtWriter : ISubtitleWriter
{
+ /// <inheritdoc />
public void Write(SubtitleTrackInfo info, Stream stream, CancellationToken cancellationToken)
{
using (var writer = new StreamWriter(stream, Encoding.UTF8, 1024, true))
diff --git a/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs
index 77033c6b4..bd330f568 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs
@@ -12,6 +12,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
/// </summary>
public class SsaParser : ISubtitleParser
{
+ /// <inheritdoc />
public SubtitleTrackInfo Parse(Stream stream, CancellationToken cancellationToken)
{
var trackInfo = new SubtitleTrackInfo();
@@ -45,7 +46,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
header.AppendLine(line);
}
- if (line.Trim().ToLowerInvariant() == "[events]")
+ if (string.Equals(line.Trim(), "[events]", StringComparison.OrdinalIgnoreCase))
{
eventsStarted = true;
}
@@ -63,27 +64,27 @@ namespace MediaBrowser.MediaEncoding.Subtitles
format = line.ToLowerInvariant().Substring(8).Split(',');
for (int i = 0; i < format.Length; i++)
{
- if (format[i].Trim().ToLowerInvariant() == "layer")
+ if (string.Equals(format[i].Trim(), "layer", StringComparison.OrdinalIgnoreCase))
{
indexLayer = i;
}
- else if (format[i].Trim().ToLowerInvariant() == "start")
+ else if (string.Equals(format[i].Trim(), "start", StringComparison.OrdinalIgnoreCase))
{
indexStart = i;
}
- else if (format[i].Trim().ToLowerInvariant() == "end")
+ else if (string.Equals(format[i].Trim(), "end", StringComparison.OrdinalIgnoreCase))
{
indexEnd = i;
}
- else if (format[i].Trim().ToLowerInvariant() == "text")
+ else if (string.Equals(format[i].Trim(), "text", StringComparison.OrdinalIgnoreCase))
{
indexText = i;
}
- else if (format[i].Trim().ToLowerInvariant() == "effect")
+ else if (string.Equals(format[i].Trim(), "effect", StringComparison.OrdinalIgnoreCase))
{
indexEffect = i;
}
- else if (format[i].Trim().ToLowerInvariant() == "style")
+ else if (string.Equals(format[i].Trim(), "style", StringComparison.OrdinalIgnoreCase))
{
indexStyle = i;
}
@@ -184,12 +185,10 @@ namespace MediaBrowser.MediaEncoding.Subtitles
int.Parse(timeCode[3]) * 10).Ticks;
}
- public static string GetFormattedText(string text)
+ private static string GetFormattedText(string text)
{
text = text.Replace("\\n", ParserValues.NewLine, StringComparison.OrdinalIgnoreCase);
- bool italic = false;
-
for (int i = 0; i < 10; i++) // just look ten times...
{
if (text.Contains(@"{\fn"))
@@ -200,7 +199,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
string fontName = text.Substring(start + 4, end - (start + 4));
string extraTags = string.Empty;
- CheckAndAddSubTags(ref fontName, ref extraTags, out italic);
+ CheckAndAddSubTags(ref fontName, ref extraTags, out bool italic);
text = text.Remove(start, end - start + 1);
if (italic)
{
@@ -231,7 +230,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
string fontSize = text.Substring(start + 4, end - (start + 4));
string extraTags = string.Empty;
- CheckAndAddSubTags(ref fontSize, ref extraTags, out italic);
+ CheckAndAddSubTags(ref fontSize, ref extraTags, out bool italic);
if (IsInteger(fontSize))
{
text = text.Remove(start, end - start + 1);
@@ -265,7 +264,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
string color = text.Substring(start + 4, end - (start + 4));
string extraTags = string.Empty;
- CheckAndAddSubTags(ref color, ref extraTags, out italic);
+ CheckAndAddSubTags(ref color, ref extraTags, out bool italic);
color = color.Replace("&", string.Empty).TrimStart('H');
color = color.PadLeft(6, '0');
@@ -303,7 +302,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
string color = text.Substring(start + 5, end - (start + 5));
string extraTags = string.Empty;
- CheckAndAddSubTags(ref color, ref extraTags, out italic);
+ CheckAndAddSubTags(ref color, ref extraTags, out bool italic);
color = color.Replace("&", string.Empty).TrimStart('H');
color = color.PadLeft(6, '0');
@@ -354,14 +353,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
}
private static bool IsInteger(string s)
- {
- if (int.TryParse(s, out var i))
- {
- return true;
- }
-
- return false;
- }
+ => int.TryParse(s, out _);
private static int CountTagInText(string text, string tag)
{
diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
index ab66e6ac3..f850f5c2e 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
@@ -1,3 +1,5 @@
+#pragma warning disable CS1591
+
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
@@ -172,7 +174,13 @@ namespace MediaBrowser.MediaEncoding.Subtitles
inputFiles = new[] { mediaSource.Path };
}
- var fileInfo = await GetReadableFile(mediaSource.Path, inputFiles, _mediaSourceManager.GetPathProtocol(subtitleStream.Path), subtitleStream, cancellationToken).ConfigureAwait(false);
+ var protocol = mediaSource.Protocol;
+ if (subtitleStream.IsExternal)
+ {
+ protocol = _mediaSourceManager.GetPathProtocol(subtitleStream.Path);
+ }
+
+ var fileInfo = await GetReadableFile(mediaSource.Path, inputFiles, protocol, subtitleStream, cancellationToken).ConfigureAwait(false);
var stream = await GetSubtitleStream(fileInfo.Path, fileInfo.Protocol, fileInfo.IsExternal, cancellationToken).ConfigureAwait(false);
@@ -365,7 +373,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
/// <returns>System.Object.</returns>
private SemaphoreSlim GetLock(string filename)
{
- return _semaphoreLocks.GetOrAdd(filename, key => new SemaphoreSlim(1, 1));
+ return _semaphoreLocks.GetOrAdd(filename, _ => new SemaphoreSlim(1, 1));
}
/// <summary>
diff --git a/MediaBrowser.MediaEncoding/Subtitles/TtmlWriter.cs b/MediaBrowser.MediaEncoding/Subtitles/TtmlWriter.cs
index 7d3e18578..e5c785bc5 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/TtmlWriter.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/TtmlWriter.cs
@@ -6,8 +6,12 @@ using MediaBrowser.Model.MediaInfo;
namespace MediaBrowser.MediaEncoding.Subtitles
{
+ /// <summary>
+ /// TTML subtitle writer.
+ /// </summary>
public class TtmlWriter : ISubtitleWriter
{
+ /// <inheritdoc />
public void Write(SubtitleTrackInfo info, Stream stream, CancellationToken cancellationToken)
{
// Example: https://github.com/zmalltalker/ttml2vtt/blob/master/data/sample.xml
@@ -36,9 +40,10 @@ namespace MediaBrowser.MediaEncoding.Subtitles
text = Regex.Replace(text, @"\\n", "<br/>", RegexOptions.IgnoreCase);
- writer.WriteLine("<p begin=\"{0}\" dur=\"{1}\">{2}</p>",
+ writer.WriteLine(
+ "<p begin=\"{0}\" dur=\"{1}\">{2}</p>",
trackEvent.StartPositionTicks,
- (trackEvent.EndPositionTicks - trackEvent.StartPositionTicks),
+ trackEvent.EndPositionTicks - trackEvent.StartPositionTicks,
text);
}
diff --git a/MediaBrowser.MediaEncoding/Subtitles/VttWriter.cs b/MediaBrowser.MediaEncoding/Subtitles/VttWriter.cs
index de35acbba..ad32cb794 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/VttWriter.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/VttWriter.cs
@@ -47,7 +47,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
text = Regex.Replace(text, @"\\n", " ", RegexOptions.IgnoreCase);
writer.WriteLine(text);
- writer.WriteLine(string.Empty);
+ writer.WriteLine();
}
}
}
diff --git a/MediaBrowser.Providers/Plugins/AudioDb/Plugin.cs b/MediaBrowser.Providers/Plugins/AudioDb/Plugin.cs
index cb7c0362e..54054d015 100644
--- a/MediaBrowser.Providers/Plugins/AudioDb/Plugin.cs
+++ b/MediaBrowser.Providers/Plugins/AudioDb/Plugin.cs
@@ -19,6 +19,9 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
public override string Description => "Get artist and album metadata or images from AudioDB.";
+ // TODO remove when plugin removed from server.
+ public override string ConfigurationFileName => "Jellyfin.Plugin.AudioDb.xml";
+
public Plugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer)
: base(applicationPaths, xmlSerializer)
{
diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/Plugin.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/Plugin.cs
index a7e6267da..90266e440 100644
--- a/MediaBrowser.Providers/Plugins/MusicBrainz/Plugin.cs
+++ b/MediaBrowser.Providers/Plugins/MusicBrainz/Plugin.cs
@@ -23,6 +23,9 @@ namespace MediaBrowser.Providers.Plugins.MusicBrainz
public const long DefaultRateLimit = 2000u;
+ // TODO remove when plugin removed from server.
+ public override string ConfigurationFileName => "Jellyfin.Plugin.MusicBrainz.xml";
+
public Plugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer)
: base(applicationPaths, xmlSerializer)
{
diff --git a/MediaBrowser.Providers/Plugins/Omdb/Plugin.cs b/MediaBrowser.Providers/Plugins/Omdb/Plugin.cs
index 4cf5f4ce6..41ca56164 100644
--- a/MediaBrowser.Providers/Plugins/Omdb/Plugin.cs
+++ b/MediaBrowser.Providers/Plugins/Omdb/Plugin.cs
@@ -19,6 +19,9 @@ namespace MediaBrowser.Providers.Plugins.Omdb
public override string Description => "Get metadata for movies and other video content from OMDb.";
+ // TODO remove when plugin removed from server.
+ public override string ConfigurationFileName => "Jellyfin.Plugin.Omdb.xml";
+
public Plugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer)
: base(applicationPaths, xmlSerializer)
{
diff --git a/MediaBrowser.Providers/Plugins/TheTvdb/Plugin.cs b/MediaBrowser.Providers/Plugins/TheTvdb/Plugin.cs
index aa5f819f0..e7079ed3c 100644
--- a/MediaBrowser.Providers/Plugins/TheTvdb/Plugin.cs
+++ b/MediaBrowser.Providers/Plugins/TheTvdb/Plugin.cs
@@ -17,6 +17,9 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
public override string Description => "Get metadata for movies and other video content from TheTVDB.";
+ // TODO remove when plugin removed from server.
+ public override string ConfigurationFileName => "Jellyfin.Plugin.TheTvdb.xml";
+
public Plugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer)
: base(applicationPaths, xmlSerializer)
{
diff --git a/tests/Jellyfin.Server.Implementations.Tests/Library/IgnorePatternsTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Library/IgnorePatternsTests.cs
index c145ddc9d..b4e6db8f3 100644
--- a/tests/Jellyfin.Server.Implementations.Tests/Library/IgnorePatternsTests.cs
+++ b/tests/Jellyfin.Server.Implementations.Tests/Library/IgnorePatternsTests.cs
@@ -9,17 +9,29 @@ namespace Jellyfin.Server.Implementations.Tests.Library
[InlineData("/media/small.jpg", true)]
[InlineData("/media/albumart.jpg", true)]
[InlineData("/media/movie.sample.mp4", true)]
+ [InlineData("/media/movie/sample.mp4", true)]
+ [InlineData("/media/movie/sample/movie.mp4", true)]
+ [InlineData("/foo/sample/bar/baz.mkv", false)]
+ [InlineData("/media/movies/the sample/the sample.mkv", false)]
+ [InlineData("/media/movies/sampler.mkv", false)]
[InlineData("/media/movies/#Recycle/test.txt", true)]
[InlineData("/media/movies/#recycle/", true)]
[InlineData("/media/movies/#recycle", true)]
[InlineData("thumbs.db", true)]
[InlineData(@"C:\media\movies\movie.avi", false)]
- [InlineData("/media/.hiddendir/file.mp4", true)]
+ [InlineData("/media/.hiddendir/file.mp4", false)]
[InlineData("/media/dir/.hiddenfile.mp4", true)]
+ [InlineData("/media/dir/._macjunk.mp4", true)]
[InlineData("/volume1/video/Series/@eaDir", true)]
[InlineData("/volume1/video/Series/@eaDir/file.txt", true)]
[InlineData("/directory/@Recycle", true)]
[InlineData("/directory/@Recycle/file.mp3", true)]
+ [InlineData("/media/movies/.@__thumb", true)]
+ [InlineData("/media/movies/.@__thumb/foo-bar-thumbnail.png", true)]
+ [InlineData("/media/music/Foo B.A.R./epic.flac", false)]
+ [InlineData("/media/music/Foo B.A.R", false)]
+ // This test is pending an upstream fix: https://github.com/dazinator/DotNet.Glob/issues/78
+ // [InlineData("/media/music/Foo B.A.R.", false)]
public void PathIgnored(string path, bool expected)
{
Assert.Equal(expected, IgnorePatterns.ShouldIgnore(path));