diff options
Diffstat (limited to 'Emby.Server.Implementations')
61 files changed, 471 insertions, 581 deletions
diff --git a/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs index 2a4a8fb132..26b4649dd8 100644 --- a/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs +++ b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs @@ -210,10 +210,7 @@ namespace Emby.Server.Implementations.AppBase /// <exception cref="ArgumentNullException"><c>newConfiguration</c> is <c>null</c>.</exception> public virtual void ReplaceConfiguration(BaseApplicationConfiguration newConfiguration) { - if (newConfiguration == null) - { - throw new ArgumentNullException(nameof(newConfiguration)); - } + ArgumentNullException.ThrowIfNull(newConfiguration); ValidateCachePath(newConfiguration); @@ -365,11 +362,7 @@ namespace Emby.Server.Implementations.AppBase validatingStore.Validate(currentConfiguration, configuration); } - NamedConfigurationUpdating?.Invoke(this, new ConfigurationUpdateEventArgs - { - Key = key, - NewConfiguration = configuration - }); + NamedConfigurationUpdating?.Invoke(this, new ConfigurationUpdateEventArgs(key, configuration)); _configurations.AddOrUpdate(key, configuration, (_, _) => configuration); @@ -391,11 +384,7 @@ namespace Emby.Server.Implementations.AppBase /// <param name="configuration">The old configuration.</param> protected virtual void OnNamedConfigurationUpdated(string key, object configuration) { - NamedConfigurationUpdated?.Invoke(this, new ConfigurationUpdateEventArgs - { - Key = key, - NewConfiguration = configuration - }); + NamedConfigurationUpdated?.Invoke(this, new ConfigurationUpdateEventArgs(key, configuration)); } /// <inheritdoc /> diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index ed70a42047..909972469e 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -66,6 +66,7 @@ using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; +using MediaBrowser.Controller.Lyrics; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Notifications; @@ -93,6 +94,7 @@ using MediaBrowser.Model.Serialization; using MediaBrowser.Model.System; using MediaBrowser.Model.Tasks; using MediaBrowser.Providers.Chapters; +using MediaBrowser.Providers.Lyric; using MediaBrowser.Providers.Manager; using MediaBrowser.Providers.Plugins.Tmdb; using MediaBrowser.Providers.Subtitles; @@ -595,6 +597,7 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton<IMediaSourceManager, MediaSourceManager>(); serviceCollection.AddSingleton<ISubtitleManager, SubtitleManager>(); + serviceCollection.AddSingleton<ILyricManager, LyricManager>(); serviceCollection.AddSingleton<IProviderManager, ProviderManager>(); @@ -627,8 +630,6 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton<IEncodingManager, MediaEncoder.EncodingManager>(); - serviceCollection.AddScoped<ISessionContext, SessionContext>(); - serviceCollection.AddSingleton<IAuthService, AuthService>(); serviceCollection.AddSingleton<IQuickConnect, QuickConnectManager>(); diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs index 92a85e8626..6837cce5cc 100644 --- a/Emby.Server.Implementations/Channels/ChannelManager.cs +++ b/Emby.Server.Implementations/Channels/ChannelManager.cs @@ -1188,10 +1188,7 @@ namespace Emby.Server.Implementations.Channels internal IChannel GetChannelProvider(Channel channel) { - if (channel == null) - { - throw new ArgumentNullException(nameof(channel)); - } + ArgumentNullException.ThrowIfNull(channel); var result = GetAllChannels() .FirstOrDefault(i => GetInternalChannelId(i.Name).Equals(channel.ChannelId) || string.Equals(i.Name, channel.Name, StringComparison.OrdinalIgnoreCase)); diff --git a/Emby.Server.Implementations/Data/SqliteExtensions.cs b/Emby.Server.Implementations/Data/SqliteExtensions.cs index 381eb92a88..736b8125d7 100644 --- a/Emby.Server.Implementations/Data/SqliteExtensions.cs +++ b/Emby.Server.Implementations/Data/SqliteExtensions.cs @@ -54,10 +54,7 @@ namespace Emby.Server.Implementations.Data public static void RunQueries(this SQLiteDatabaseConnection connection, string[] queries) { - if (queries == null) - { - throw new ArgumentNullException(nameof(queries)); - } + ArgumentNullException.ThrowIfNull(queries); connection.RunInTransaction(conn => { diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 1b176e60d7..7622d2fe68 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -178,7 +178,8 @@ namespace Emby.Server.Implementations.Data "RpuPresentFlag", "ElPresentFlag", "BlPresentFlag", - "DvBlSignalCompatibilityId" + "DvBlSignalCompatibilityId", + "IsHearingImpaired" }; private static readonly string _mediaStreamSaveColumnsInsertQuery = @@ -349,7 +350,8 @@ namespace Emby.Server.Implementations.Data public void Initialize(SqliteUserDataRepository userDataRepo, IUserManager userManager) { const string CreateMediaStreamsTableCommand - = "create table if not exists mediastreams (ItemId GUID, StreamIndex INT, StreamType TEXT, Codec TEXT, Language TEXT, ChannelLayout TEXT, Profile TEXT, AspectRatio TEXT, Path TEXT, IsInterlaced BIT, BitRate INT NULL, Channels INT NULL, SampleRate INT NULL, IsDefault BIT, IsForced BIT, IsExternal BIT, Height INT NULL, Width INT NULL, AverageFrameRate FLOAT NULL, RealFrameRate FLOAT NULL, Level FLOAT NULL, PixelFormat TEXT, BitDepth INT NULL, IsAnamorphic BIT NULL, RefFrames INT NULL, CodecTag TEXT NULL, Comment TEXT NULL, NalLengthSize TEXT NULL, IsAvc BIT NULL, Title TEXT NULL, TimeBase TEXT NULL, CodecTimeBase TEXT NULL, ColorPrimaries TEXT NULL, ColorSpace TEXT NULL, ColorTransfer TEXT NULL, DvVersionMajor INT NULL, DvVersionMinor INT NULL, DvProfile INT NULL, DvLevel INT NULL, RpuPresentFlag INT NULL, ElPresentFlag INT NULL, BlPresentFlag INT NULL, DvBlSignalCompatibilityId INT NULL, PRIMARY KEY (ItemId, StreamIndex))"; + = "create table if not exists mediastreams (ItemId GUID, StreamIndex INT, StreamType TEXT, Codec TEXT, Language TEXT, ChannelLayout TEXT, Profile TEXT, AspectRatio TEXT, Path TEXT, IsInterlaced BIT, BitRate INT NULL, Channels INT NULL, SampleRate INT NULL, IsDefault BIT, IsForced BIT, IsExternal BIT, Height INT NULL, Width INT NULL, AverageFrameRate FLOAT NULL, RealFrameRate FLOAT NULL, Level FLOAT NULL, PixelFormat TEXT, BitDepth INT NULL, IsAnamorphic BIT NULL, RefFrames INT NULL, CodecTag TEXT NULL, Comment TEXT NULL, NalLengthSize TEXT NULL, IsAvc BIT NULL, Title TEXT NULL, TimeBase TEXT NULL, CodecTimeBase TEXT NULL, ColorPrimaries TEXT NULL, ColorSpace TEXT NULL, ColorTransfer TEXT NULL, DvVersionMajor INT NULL, DvVersionMinor INT NULL, DvProfile INT NULL, DvLevel INT NULL, RpuPresentFlag INT NULL, ElPresentFlag INT NULL, BlPresentFlag INT NULL, DvBlSignalCompatibilityId INT NULL, IsHearingImpaired BIT NULL, PRIMARY KEY (ItemId, StreamIndex))"; + const string CreateMediaAttachmentsTableCommand = "create table if not exists mediaattachments (ItemId GUID, AttachmentIndex INT, Codec TEXT, CodecTag TEXT NULL, Comment TEXT NULL, Filename TEXT NULL, MIMEType TEXT NULL, PRIMARY KEY (ItemId, AttachmentIndex))"; @@ -572,6 +574,8 @@ namespace Emby.Server.Implementations.Data AddColumn(db, "MediaStreams", "ElPresentFlag", "INT", existingColumnNames); AddColumn(db, "MediaStreams", "BlPresentFlag", "INT", existingColumnNames); AddColumn(db, "MediaStreams", "DvBlSignalCompatibilityId", "INT", existingColumnNames); + + AddColumn(db, "MediaStreams", "IsHearingImpaired", "BIT", existingColumnNames); }, TransactionMode); @@ -583,10 +587,7 @@ namespace Emby.Server.Implementations.Data public void SaveImages(BaseItem item) { - if (item == null) - { - throw new ArgumentNullException(nameof(item)); - } + ArgumentNullException.ThrowIfNull(item); CheckDisposed(); @@ -617,10 +618,7 @@ namespace Emby.Server.Implementations.Data /// </exception> public void SaveItems(IEnumerable<BaseItem> items, CancellationToken cancellationToken) { - if (items == null) - { - throw new ArgumentNullException(nameof(items)); - } + ArgumentNullException.ThrowIfNull(items); cancellationToken.ThrowIfCancellationRequested(); @@ -2085,10 +2083,7 @@ namespace Emby.Server.Implementations.Data throw new ArgumentNullException(nameof(id)); } - if (chapters == null) - { - throw new ArgumentNullException(nameof(chapters)); - } + ArgumentNullException.ThrowIfNull(chapters); var idBlob = id.ToByteArray(); @@ -2557,10 +2552,7 @@ namespace Emby.Server.Implementations.Data public int GetCount(InternalItemsQuery query) { - if (query == null) - { - throw new ArgumentNullException(nameof(query)); - } + ArgumentNullException.ThrowIfNull(query); CheckDisposed(); @@ -2613,10 +2605,7 @@ namespace Emby.Server.Implementations.Data public List<BaseItem> GetItemList(InternalItemsQuery query) { - if (query == null) - { - throw new ArgumentNullException(nameof(query)); - } + ArgumentNullException.ThrowIfNull(query); CheckDisposed(); @@ -2794,10 +2783,7 @@ namespace Emby.Server.Implementations.Data public QueryResult<BaseItem> GetItems(InternalItemsQuery query) { - if (query == null) - { - throw new ArgumentNullException(nameof(query)); - } + ArgumentNullException.ThrowIfNull(query); CheckDisposed(); @@ -3174,10 +3160,7 @@ namespace Emby.Server.Implementations.Data public List<Guid> GetItemIdsList(InternalItemsQuery query) { - if (query == null) - { - throw new ArgumentNullException(nameof(query)); - } + ArgumentNullException.ThrowIfNull(query); CheckDisposed(); @@ -4837,10 +4820,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type public List<string> GetPeopleNames(InternalPeopleQuery query) { - if (query == null) - { - throw new ArgumentNullException(nameof(query)); - } + ArgumentNullException.ThrowIfNull(query); CheckDisposed(); @@ -4880,10 +4860,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type public List<PersonInfo> GetPeople(InternalPeopleQuery query) { - if (query == null) - { - throw new ArgumentNullException(nameof(query)); - } + ArgumentNullException.ThrowIfNull(query); CheckDisposed(); @@ -4999,10 +4976,7 @@ AND Type = @InternalPersonType)"); throw new ArgumentNullException(nameof(itemId)); } - if (ancestorIds == null) - { - throw new ArgumentNullException(nameof(ancestorIds)); - } + ArgumentNullException.ThrowIfNull(ancestorIds); CheckDisposed(); @@ -5175,10 +5149,7 @@ AND Type = @InternalPersonType)"); private QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetItemValues(InternalItemsQuery query, int[] itemValueTypes, string returnType) { - if (query == null) - { - throw new ArgumentNullException(nameof(query)); - } + ArgumentNullException.ThrowIfNull(query); if (!query.Limit.HasValue) { @@ -5531,10 +5502,7 @@ AND Type = @InternalPersonType)"); throw new ArgumentNullException(nameof(itemId)); } - if (values == null) - { - throw new ArgumentNullException(nameof(values)); - } + ArgumentNullException.ThrowIfNull(values); CheckDisposed(); @@ -5607,10 +5575,7 @@ AND Type = @InternalPersonType)"); throw new ArgumentNullException(nameof(itemId)); } - if (people == null) - { - throw new ArgumentNullException(nameof(people)); - } + ArgumentNullException.ThrowIfNull(people); CheckDisposed(); @@ -5710,10 +5675,7 @@ AND Type = @InternalPersonType)"); { CheckDisposed(); - if (query == null) - { - throw new ArgumentNullException(nameof(query)); - } + ArgumentNullException.ThrowIfNull(query); var cmdText = _mediaStreamSaveColumnsSelectQuery; @@ -5766,10 +5728,7 @@ AND Type = @InternalPersonType)"); throw new ArgumentNullException(nameof(id)); } - if (streams == null) - { - throw new ArgumentNullException(nameof(streams)); - } + ArgumentNullException.ThrowIfNull(streams); cancellationToken.ThrowIfCancellationRequested(); @@ -5881,6 +5840,8 @@ AND Type = @InternalPersonType)"); statement.TryBind("@ElPresentFlag" + index, stream.ElPresentFlag); statement.TryBind("@BlPresentFlag" + index, stream.BlPresentFlag); statement.TryBind("@DvBlSignalCompatibilityId" + index, stream.DvBlSignalCompatibilityId); + + statement.TryBind("@IsHearingImpaired" + index, stream.IsHearingImpaired); } statement.Reset(); @@ -6092,12 +6053,15 @@ AND Type = @InternalPersonType)"); item.DvBlSignalCompatibilityId = dvBlSignalCompatibilityId; } + item.IsHearingImpaired = reader.GetBoolean(43); + if (item.Type == MediaStreamType.Subtitle) { item.LocalizedUndefined = _localization.GetLocalizedString("Undefined"); item.LocalizedDefault = _localization.GetLocalizedString("Default"); item.LocalizedForced = _localization.GetLocalizedString("Forced"); item.LocalizedExternal = _localization.GetLocalizedString("External"); + item.LocalizedHearingImpaired = _localization.GetLocalizedString("HearingImpaired"); } return item; @@ -6107,10 +6071,7 @@ AND Type = @InternalPersonType)"); { CheckDisposed(); - if (query == null) - { - throw new ArgumentNullException(nameof(query)); - } + ArgumentNullException.ThrowIfNull(query); var cmdText = _mediaAttachmentSaveColumnsSelectQuery; @@ -6152,10 +6113,7 @@ AND Type = @InternalPersonType)"); throw new ArgumentException("Guid can't be empty.", nameof(id)); } - if (attachments == null) - { - throw new ArgumentNullException(nameof(attachments)); - } + ArgumentNullException.ThrowIfNull(attachments); cancellationToken.ThrowIfCancellationRequested(); diff --git a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs index ba86dc156e..8d78d644dc 100644 --- a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs @@ -133,10 +133,7 @@ namespace Emby.Server.Implementations.Data /// <inheritdoc /> public void SaveUserData(long userId, string key, UserItemData userData, CancellationToken cancellationToken) { - if (userData == null) - { - throw new ArgumentNullException(nameof(userData)); - } + ArgumentNullException.ThrowIfNull(userData); if (userId <= 0) { @@ -154,10 +151,7 @@ namespace Emby.Server.Implementations.Data /// <inheritdoc /> public void SaveAllUserData(long userId, UserItemData[] userData, CancellationToken cancellationToken) { - if (userData == null) - { - throw new ArgumentNullException(nameof(userData)); - } + ArgumentNullException.ThrowIfNull(userData); if (userId <= 0) { @@ -304,10 +298,7 @@ namespace Emby.Server.Implementations.Data public UserItemData GetUserData(long userId, List<string> keys) { - if (keys == null) - { - throw new ArgumentNullException(nameof(keys)); - } + ArgumentNullException.ThrowIfNull(keys); if (keys.Count == 0) { diff --git a/Emby.Server.Implementations/Data/SynchronouseMode.cs b/Emby.Server.Implementations/Data/SynchronousMode.cs index cde524e2e0..cde524e2e0 100644 --- a/Emby.Server.Implementations/Data/SynchronouseMode.cs +++ b/Emby.Server.Implementations/Data/SynchronousMode.cs diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index 09ba368514..f6d37421a8 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; +using Jellyfin.Api.Helpers; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; using Jellyfin.Extensions; @@ -18,6 +19,7 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; +using MediaBrowser.Controller.Lyrics; using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Playlists; using MediaBrowser.Controller.Providers; @@ -50,6 +52,8 @@ namespace Emby.Server.Implementations.Dto private readonly IMediaSourceManager _mediaSourceManager; private readonly Lazy<ILiveTvManager> _livetvManagerFactory; + private readonly ILyricManager _lyricManager; + public DtoService( ILogger<DtoService> logger, ILibraryManager libraryManager, @@ -59,7 +63,8 @@ namespace Emby.Server.Implementations.Dto IProviderManager providerManager, IApplicationHost appHost, IMediaSourceManager mediaSourceManager, - Lazy<ILiveTvManager> livetvManagerFactory) + Lazy<ILiveTvManager> livetvManagerFactory, + ILyricManager lyricManager) { _logger = logger; _libraryManager = libraryManager; @@ -70,6 +75,7 @@ namespace Emby.Server.Implementations.Dto _appHost = appHost; _mediaSourceManager = mediaSourceManager; _livetvManagerFactory = livetvManagerFactory; + _lyricManager = lyricManager; } private ILiveTvManager LivetvManager => _livetvManagerFactory.Value; @@ -139,6 +145,10 @@ namespace Emby.Server.Implementations.Dto { LivetvManager.AddInfoToProgramDto(new[] { (item, dto) }, options.Fields, user).GetAwaiter().GetResult(); } + else if (item is Audio) + { + dto.HasLyrics = _lyricManager.HasLyricFile(item); + } if (item is IItemByName itemByName && options.ContainsField(ItemFields.ItemCounts)) @@ -182,7 +192,7 @@ namespace Emby.Server.Implementations.Dto if (options.ContainsField(ItemFields.People)) { - AttachPeople(dto, item); + AttachPeople(dto, item, user); } if (options.ContainsField(ItemFields.PrimaryImageAspectRatio)) @@ -503,7 +513,8 @@ namespace Emby.Server.Implementations.Dto /// </summary> /// <param name="dto">The dto.</param> /// <param name="item">The item.</param> - private void AttachPeople(BaseItemDto dto, BaseItem item) + /// <param name="user">The requesting user.</param> + private void AttachPeople(BaseItemDto dto, BaseItem item, User user = null) { // Ordering by person type to ensure actors and artists are at the front. // This is taking advantage of the fact that they both begin with A @@ -560,6 +571,9 @@ namespace Emby.Server.Implementations.Dto return null; } }).Where(i => i != null) + .Where(i => user == null ? + true : + i.IsVisible(user)) .GroupBy(i => i.Name, StringComparer.OrdinalIgnoreCase) .Select(x => x.First()) .ToDictionary(i => i.Name, StringComparer.OrdinalIgnoreCase); diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index b574586d6c..c4fdf08b9a 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -29,7 +29,7 @@ <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="6.0.1" /> <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="6.0.0" /> - <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.7" /> + <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.9" /> <PackageReference Include="Mono.Nat" Version="3.0.3" /> <PackageReference Include="prometheus-net.DotNetRuntime" Version="4.2.4" /> <PackageReference Include="SQLitePCL.pretty.netstandard" Version="3.1.0" /> diff --git a/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs b/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs deleted file mode 100644 index 15ab363fe6..0000000000 --- a/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs +++ /dev/null @@ -1,60 +0,0 @@ -#pragma warning disable CS1591 - -using System; -using System.Threading.Tasks; -using Jellyfin.Data.Entities; -using MediaBrowser.Common.Extensions; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Net; -using MediaBrowser.Controller.Session; -using Microsoft.AspNetCore.Http; - -namespace Emby.Server.Implementations.HttpServer.Security -{ - public class SessionContext : ISessionContext - { - private readonly IUserManager _userManager; - private readonly ISessionManager _sessionManager; - private readonly IAuthorizationContext _authContext; - - public SessionContext(IUserManager userManager, IAuthorizationContext authContext, ISessionManager sessionManager) - { - _userManager = userManager; - _authContext = authContext; - _sessionManager = sessionManager; - } - - public async Task<SessionInfo> GetSession(HttpContext requestContext) - { - var authorization = await _authContext.GetAuthorizationInfo(requestContext).ConfigureAwait(false); - - var user = authorization.User; - return await _sessionManager.LogSessionActivity( - authorization.Client, - authorization.Version, - authorization.DeviceId, - authorization.Device, - requestContext.GetNormalizedRemoteIp().ToString(), - user).ConfigureAwait(false); - } - - public Task<SessionInfo> GetSession(object requestContext) - { - return GetSession((HttpContext)requestContext); - } - - public async Task<User?> GetUser(HttpContext requestContext) - { - var session = await GetSession(requestContext).ConfigureAwait(false); - - return session.UserId.Equals(default) - ? null - : _userManager.GetUserById(session.UserId); - } - - public Task<User?> GetUser(object requestContext) - { - return GetUser(((HttpRequest)requestContext).HttpContext); - } - } -} diff --git a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs index 818ccbb1b8..d095248fab 100644 --- a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs +++ b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs @@ -11,7 +11,6 @@ using Jellyfin.Extensions.Json; using MediaBrowser.Controller.Net; using MediaBrowser.Model.Net; using MediaBrowser.Model.Session; -using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.HttpServer diff --git a/Emby.Server.Implementations/Images/DynamicImageProvider.cs b/Emby.Server.Implementations/Images/DynamicImageProvider.cs index 9f9a4902ab..0faa0f8faf 100644 --- a/Emby.Server.Implementations/Images/DynamicImageProvider.cs +++ b/Emby.Server.Implementations/Images/DynamicImageProvider.cs @@ -93,6 +93,7 @@ namespace Emby.Server.Implementations.Images returnItems.Shuffle(); return returnItems; } + returnItems = items .Where(i => i.HasImage(ImageType.Primary)) .ToList(); diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 2843fb8f83..ee94670eb0 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -46,7 +46,6 @@ using MediaBrowser.Model.Library; using MediaBrowser.Model.Querying; using MediaBrowser.Model.Tasks; using Microsoft.Extensions.Caching.Memory; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Episode = MediaBrowser.Controller.Entities.TV.Episode; using EpisodeInfo = Emby.Naming.TV.EpisodeInfo; @@ -282,10 +281,7 @@ namespace Emby.Server.Implementations.Library public void RegisterItem(BaseItem item) { - if (item == null) - { - throw new ArgumentNullException(nameof(item)); - } + ArgumentNullException.ThrowIfNull(item); if (item is IItemByName) { @@ -312,10 +308,7 @@ namespace Emby.Server.Implementations.Library public void DeleteItem(BaseItem item, DeleteOptions options, bool notifyParentItem) { - if (item == null) - { - throw new ArgumentNullException(nameof(item)); - } + ArgumentNullException.ThrowIfNull(item); var parent = item.GetOwner() ?? item.GetParent(); @@ -324,10 +317,7 @@ namespace Emby.Server.Implementations.Library public void DeleteItem(BaseItem item, DeleteOptions options, BaseItem parent, bool notifyParentItem) { - if (item == null) - { - throw new ArgumentNullException(nameof(item)); - } + ArgumentNullException.ThrowIfNull(item); if (item.SourceType == SourceType.Channel) { @@ -510,10 +500,7 @@ namespace Emby.Server.Implementations.Library throw new ArgumentNullException(nameof(key)); } - if (type == null) - { - throw new ArgumentNullException(nameof(type)); - } + ArgumentNullException.ThrowIfNull(type); string programDataPath = _configurationManager.ApplicationPaths.ProgramDataPath; if (key.StartsWith(programDataPath, StringComparison.Ordinal)) @@ -545,10 +532,7 @@ namespace Emby.Server.Implementations.Library string collectionType = null, LibraryOptions libraryOptions = null) { - if (fileInfo == null) - { - throw new ArgumentNullException(nameof(fileInfo)); - } + ArgumentNullException.ThrowIfNull(fileInfo); var fullPath = fileInfo.FullName; @@ -1855,10 +1839,7 @@ namespace Emby.Server.Implementations.Library /// <inheritdoc /> public async Task UpdateImagesAsync(BaseItem item, bool forceUpdate = false) { - if (item == null) - { - throw new ArgumentNullException(nameof(item)); - } + ArgumentNullException.ThrowIfNull(item); var outdated = forceUpdate ? item.ImageInfos.Where(i => i.Path != null).ToArray() @@ -2297,10 +2278,7 @@ namespace Emby.Server.Implementations.Library string viewType, string sortName) { - if (parent == null) - { - throw new ArgumentNullException(nameof(parent)); - } + ArgumentNullException.ThrowIfNull(parent); var name = parent.Name; var parentId = parent.Id; @@ -2529,7 +2507,7 @@ namespace Emby.Server.Implementations.Library } catch (Exception ex) { - _logger.LogError(ex, "Error reading the episode informations with ffprobe. Episode: {EpisodeInfo}", episodeInfo.Path); + _logger.LogError(ex, "Error reading the episode information with ffprobe. Episode: {EpisodeInfo}", episodeInfo.Path); } var changed = false; @@ -2766,7 +2744,8 @@ namespace Emby.Server.Implementations.Library public List<Person> GetPeopleItems(InternalPeopleQuery query) { - return _itemRepository.GetPeopleNames(query).Select(i => + return _itemRepository.GetPeopleNames(query) + .Select(i => { try { @@ -2777,7 +2756,12 @@ namespace Emby.Server.Implementations.Library _logger.LogError(ex, "Error getting person"); return null; } - }).Where(i => i != null).ToList(); + }) + .Where(i => i != null) + .Where(i => query.User == null ? + true : + i.IsVisible(query.User)) + .ToList(); } public List<string> GetPeopleNames(InternalPeopleQuery query) @@ -2978,10 +2962,7 @@ namespace Emby.Server.Implementations.Library private void AddMediaPathInternal(string virtualFolderName, MediaPathInfo pathInfo, bool saveLibraryOptions) { - if (pathInfo == null) - { - throw new ArgumentNullException(nameof(pathInfo)); - } + ArgumentNullException.ThrowIfNull(pathInfo); var path = pathInfo.Path; @@ -3028,10 +3009,7 @@ namespace Emby.Server.Implementations.Library public void UpdateMediaPath(string virtualFolderName, MediaPathInfo mediaPath) { - if (mediaPath == null) - { - throw new ArgumentNullException(nameof(mediaPath)); - } + ArgumentNullException.ThrowIfNull(mediaPath); var rootFolderPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath; var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName); diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs index c0aef18997..bfccc7db72 100644 --- a/Emby.Server.Implementations/Library/MediaSourceManager.cs +++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs @@ -322,10 +322,7 @@ namespace Emby.Server.Implementations.Library public List<MediaSourceInfo> GetStaticMediaSources(BaseItem item, bool enablePathSubstitution, User user = null) { - if (item == null) - { - throw new ArgumentNullException(nameof(item)); - } + ArgumentNullException.ThrowIfNull(item); var hasMediaSources = (IHasMediaSources)item; diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs index da00b9cfa8..a922e36855 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -18,7 +19,7 @@ using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.Library.Resolvers.Audio { /// <summary> - /// Class MusicAlbumResolver. + /// The music album resolver. /// </summary> public class MusicAlbumResolver : ItemResolver<MusicAlbum> { @@ -82,7 +83,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio /// </summary> /// <param name="path">The path to check.</param> /// <param name="directoryService">The directory service.</param> - /// <returns><c>true</c> if the provided path points to a music album, <c>false</c> otherwise.</returns> + /// <returns><c>true</c> if the provided path points to a music album; otherwise, <c>false</c>.</returns> public bool IsMusicAlbum(string path, IDirectoryService directoryService) { return ContainsMusic(directoryService.GetFileSystemEntries(path), true, directoryService); @@ -95,10 +96,19 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio /// <returns><c>true</c> if [is music album] [the specified args]; otherwise, <c>false</c>.</returns> private bool IsMusicAlbum(ItemResolveArgs args) { - // Args points to an album if parent is an Artist folder or it directly contains music if (args.IsDirectory) { - // if (args.Parent is MusicArtist) return true; // saves us from testing children twice + // If args is a artist subfolder it's not a music album + foreach (var subfolder in _namingOptions.ArtistSubfolders) + { + if (Path.GetDirectoryName(args.Path.AsSpan()).Equals(subfolder, StringComparison.OrdinalIgnoreCase)) + { + _logger.LogDebug("Found release folder: {Path}", args.Path); + return false; + } + } + + // If args contains music it's a music album if (ContainsMusic(args.FileSystemChildren, true, args.DirectoryService)) { return true; @@ -111,22 +121,23 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio /// <summary> /// Determine if the supplied list contains what we should consider music. /// </summary> + /// <returns><c>true</c> if the provided path list contains music; otherwise, <c>false</c>.</returns> private bool ContainsMusic( ICollection<FileSystemMetadata> list, bool allowSubfolders, IDirectoryService directoryService) { - // check for audio files before digging down into directories + // Check for audio files before digging down into directories var foundAudioFile = list.Any(fileSystemInfo => !fileSystemInfo.IsDirectory && AudioFileParser.IsAudioFile(fileSystemInfo.FullName, _namingOptions)); if (foundAudioFile) { - // at least one audio file exists + // At least one audio file exists return true; } if (!allowSubfolders) { - // not music since no audio file exists and we're not looking into subfolders + // Not music since no audio file exists and we're not looking into subfolders return false; } diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs index 210ed0953a..2538c2b5b4 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs @@ -13,7 +13,7 @@ using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.Library.Resolvers.Audio { /// <summary> - /// Class MusicArtistResolver. + /// The music artist resolver. /// </summary> public class MusicArtistResolver : ItemResolver<MusicArtist> { @@ -23,8 +23,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio /// <summary> /// Initializes a new instance of the <see cref="MusicArtistResolver"/> class. /// </summary> - /// <param name="logger">The logger for the created <see cref="MusicAlbumResolver"/> instances.</param> - /// <param name="namingOptions">The naming options.</param> + /// <param name="logger">Instance of the <see cref="MusicAlbumResolver"/> interface.</param> + /// <param name="namingOptions">The <see cref="NamingOptions"/>.</param> public MusicArtistResolver( ILogger<MusicAlbumResolver> logger, NamingOptions namingOptions) @@ -40,10 +40,10 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio public override ResolverPriority Priority => ResolverPriority.Second; /// <summary> - /// Resolves the specified args. + /// Resolves the specified resolver arguments. /// </summary> - /// <param name="args">The args.</param> - /// <returns>MusicArtist.</returns> + /// <param name="args">The resolver arguments.</param> + /// <returns>A <see cref="MusicArtist"/>.</returns> protected override MusicArtist Resolve(ItemResolveArgs args) { if (!args.IsDirectory) @@ -61,7 +61,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio var isMusicMediaFolder = string.Equals(collectionType, CollectionType.Music, StringComparison.OrdinalIgnoreCase); - // If there's a collection type and it's not music, it can't be a series + // If there's a collection type and it's not music, it can't be a music artist if (!isMusicMediaFolder) { return null; @@ -82,14 +82,24 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio var albumResolver = new MusicAlbumResolver(_logger, _namingOptions); - // If we contain an album assume we are an artist folder var directories = args.FileSystemChildren.Where(i => i.IsDirectory); var result = Parallel.ForEach(directories, (fileSystemInfo, state) => { + // If we contain a artist subfolder assume we are an artist folder + foreach (var subfolder in _namingOptions.ArtistSubfolders) + { + if (fileSystemInfo.Name.Equals(subfolder, StringComparison.OrdinalIgnoreCase)) + { + // Stop once we see an artist subfolder + state.Stop(); + } + } + + // If we contain a music album assume we are an artist folder if (albumResolver.IsMusicAlbum(fileSystemInfo.FullName, directoryService)) { - // stop once we see a music album + // Stop once we see a music album state.Stop(); } }); diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs index fe4ccd6acb..b2f388a667 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs @@ -387,7 +387,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies if (!string.IsNullOrEmpty(item.Path)) { - // check for imdb id - we use full media path, as we can assume, that this will match in any use case (wither id in parent dir or in file name) + // check for imdb id - we use full media path, as we can assume, that this will match in any use case (either id in parent dir or in file name) var imdbid = item.Path.AsSpan().GetAttributeValue("imdbid"); if (!string.IsNullOrWhiteSpace(imdbid)) @@ -464,7 +464,9 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies var result = ResolveVideos<T>(parent, fileSystemEntries, SupportsMultiVersion, collectionType, parseName) ?? new MultiItemResolverResult(); - if (result.Items.Count == 1) + var isPhotosCollection = string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase) + || string.Equals(collectionType, CollectionType.Photos, StringComparison.OrdinalIgnoreCase); + if (!isPhotosCollection && result.Items.Count == 1) { var videoPath = result.Items[0].Path; var hasPhotos = photos.Any(i => !PhotoResolver.IsOwnedByResolvedMedia(videoPath, i.Name)); diff --git a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs index bc2915db62..af4abfb805 100644 --- a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs @@ -91,10 +91,7 @@ namespace Emby.Server.Implementations.Library.Resolvers internal static bool IsImageFile(string path, IImageProcessor imageProcessor) { - if (path == null) - { - throw new ArgumentNullException(nameof(path)); - } + ArgumentNullException.ThrowIfNull(path); var filename = Path.GetFileNameWithoutExtension(path); diff --git a/Emby.Server.Implementations/Library/UserDataManager.cs b/Emby.Server.Implementations/Library/UserDataManager.cs index 3810a76c45..aecab7d9c8 100644 --- a/Emby.Server.Implementations/Library/UserDataManager.cs +++ b/Emby.Server.Implementations/Library/UserDataManager.cs @@ -53,15 +53,9 @@ namespace Emby.Server.Implementations.Library public void SaveUserData(User user, BaseItem item, UserItemData userData, UserDataSaveReason reason, CancellationToken cancellationToken) { - if (userData == null) - { - throw new ArgumentNullException(nameof(userData)); - } + ArgumentNullException.ThrowIfNull(userData); - if (item == null) - { - throw new ArgumentNullException(nameof(item)); - } + ArgumentNullException.ThrowIfNull(item); cancellationToken.ThrowIfCancellationRequested(); @@ -194,10 +188,7 @@ namespace Emby.Server.Implementations.Library /// <exception cref="ArgumentNullException"><paramref name="data"/> is <c>null</c>.</exception> private UserItemDataDto GetUserItemDataDto(UserItemData data) { - if (data == null) - { - throw new ArgumentNullException(nameof(data)); - } + ArgumentNullException.ThrowIfNull(data); return new UserItemDataDto { diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 2753cf1778..3db422c2f0 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -995,7 +995,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV } } - throw new Exception("Tuner not found."); + throw new ResourceNotFoundException("Tuner not found."); } public async Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken) @@ -1223,10 +1223,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV private async Task RecordStream(TimerInfo timer, DateTime recordingEndDate, ActiveRecordingInfo activeRecordingInfo) { - if (timer == null) - { - throw new ArgumentNullException(nameof(timer)); - } + ArgumentNullException.ThrowIfNull(timer); LiveTvProgram programInfo = null; @@ -2221,6 +2218,12 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { continue; } + + // Skip ShowId without SubKey from duplicate removal actions - https://github.com/jellyfin/jellyfin/issues/5856 + if (group.Key.EndsWith("0000")) + { + continue; + } HandleDuplicateShowIds(groupTimers); } @@ -2347,10 +2350,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV private IEnumerable<TimerInfo> GetTimersForSeries(SeriesTimerInfo seriesTimer) { - if (seriesTimer == null) - { - throw new ArgumentNullException(nameof(seriesTimer)); - } + ArgumentNullException.ThrowIfNull(seriesTimer); var query = new InternalItemsQuery { diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs index 6e05598410..08534de59d 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs @@ -13,6 +13,7 @@ using System.Threading; using System.Threading.Tasks; using Jellyfin.Extensions; using Jellyfin.Extensions.Json; +using MediaBrowser.Common; using MediaBrowser.Common.Configuration; using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; @@ -297,7 +298,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV else { _taskCompletionSource.TrySetException( - new Exception( + new FfmpegException( string.Format( CultureInfo.InvariantCulture, "Recording for {0} failed. Exit code {1}", diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs index 46979bfc57..58b798ce67 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs @@ -84,10 +84,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV public virtual void Update(T item) { - if (item == null) - { - throw new ArgumentNullException(nameof(item)); - } + ArgumentNullException.ThrowIfNull(item); lock (_fileDataLock) { @@ -107,10 +104,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV public virtual void Add(T item) { - if (item == null) - { - throw new ArgumentNullException(nameof(item)); - } + ArgumentNullException.ThrowIfNull(item); lock (_fileDataLock) { diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs index 6ad9ccdf6e..40dcca94f6 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs @@ -2,8 +2,8 @@ using System; using System.Globalization; -using MediaBrowser.Controller.LiveTv; using System.Text; +using MediaBrowser.Controller.LiveTv; namespace Emby.Server.Implementations.LiveTv.EmbyTV { @@ -56,7 +56,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV } tmpName += " " + info.EpisodeTitle; - // Since the filename will be used with file ext. (.mp4, .ts, etc) + // Since the filename will be used with file ext. (.mp4, .ts, etc) if (Encoding.UTF8.GetByteCount(tmpName) < 250) { name = tmpName; diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs index ffa0d9b6a0..4311db28d2 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs @@ -20,6 +20,7 @@ using Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos; using Jellyfin.Extensions; using Jellyfin.Extensions.Json; using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Authentication; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; @@ -591,13 +592,10 @@ namespace Emby.Server.Implementations.LiveTv.Listings } catch (HttpRequestException ex) { - if (ex.StatusCode.HasValue) + if (ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.BadRequest) { - if ((int)ex.StatusCode.Value == 400) - { - _tokens.Clear(); - _lastErrorResponse = DateTime.UtcNow; - } + _tokens.Clear(); + _lastErrorResponse = DateTime.UtcNow; } throw; @@ -662,7 +660,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings return root.Token; } - throw new Exception("Could not authenticate with Schedules Direct Error: " + root.Message); + throw new AuthenticationException("Could not authenticate with Schedules Direct Error: " + root.Message); } private async Task AddLineupToAccount(ListingsProviderInfo info, CancellationToken cancellationToken) @@ -697,7 +695,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings if (string.IsNullOrEmpty(token)) { - throw new Exception("token required"); + throw new ArgumentException("token required"); } _logger.LogInformation("Headends on account "); @@ -768,14 +766,14 @@ namespace Emby.Server.Implementations.LiveTv.Listings var listingsId = info.ListingsId; if (string.IsNullOrEmpty(listingsId)) { - throw new Exception("ListingsId required"); + throw new ArgumentException("ListingsId required"); } var token = await GetToken(info, cancellationToken).ConfigureAwait(false); if (string.IsNullOrEmpty(token)) { - throw new Exception("token required"); + throw new ArgumentException("token required"); } using var options = new HttpRequestMessage(HttpMethod.Get, ApiUrl + "/lineups/" + listingsId); diff --git a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs index 10f7e5cc86..8ed8e1fc6e 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs @@ -104,13 +104,14 @@ namespace Emby.Server.Implementations.LiveTv.Listings int index = originalUrl.IndexOf('?', StringComparison.CurrentCulture); string ext = Path.GetExtension(index > -1 ? originalUrl.Remove(index) : originalUrl); + await using var fileStream = new FileStream(file, FileMode.CreateNew, FileAccess.Write, FileShare.None, IODefaults.CopyToBufferSize, FileOptions.Asynchronous); + if (ext.Equals(".gz", StringComparison.OrdinalIgnoreCase)) { try { using var reader = new GZipStream(stream, CompressionMode.Decompress); - await using var writer = File.Create(file); - await reader.CopyToAsync(writer, cancellationToken).ConfigureAwait(false); + await reader.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false); } catch (Exception ex) { @@ -119,7 +120,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings } else { - await using var fileStream = new FileStream(file, FileMode.CreateNew, FileAccess.Write, FileShare.None, IODefaults.CopyToBufferSize, FileOptions.Asynchronous); await stream.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false); } diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs index 2a468e14dc..bcb42e1626 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs @@ -196,7 +196,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts IsInfiniteStream = true, IsRemote = isRemote, - IgnoreDts = true, + IgnoreDts = info.IgnoreDts, SupportsDirectPlay = supportsDirectPlay, SupportsDirectStream = supportsDirectStream, diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs index 708ff52d79..a423ec8f48 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs @@ -44,10 +44,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts public async Task<Stream> GetListingsStream(TunerHostInfo info, CancellationToken cancellationToken) { - if (info == null) - { - throw new ArgumentNullException(nameof(info)); - } + ArgumentNullException.ThrowIfNull(info); if (!info.Url.StartsWith("http", StringComparison.OrdinalIgnoreCase)) { @@ -199,7 +196,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts if (string.IsNullOrWhiteSpace(numberString)) { // Using this as a fallback now as this leads to Problems with channels like "5 USA" - // where 5 isn't ment to be the channel number + // where 5 isn't meant 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 diff --git a/Emby.Server.Implementations/Localization/Core/en-US.json b/Emby.Server.Implementations/Localization/Core/en-US.json index d8c33d51bd..15088384cc 100644 --- a/Emby.Server.Implementations/Localization/Core/en-US.json +++ b/Emby.Server.Implementations/Localization/Core/en-US.json @@ -28,6 +28,7 @@ "HeaderLiveTV": "Live TV", "HeaderNextUp": "Next Up", "HeaderRecordingGroups": "Recording Groups", + "HearingImpaired": "Hearing Impaired", "HomeVideos": "Home Videos", "Inherit": "Inherit", "ItemAddedWithName": "{0} was added to the library", diff --git a/Emby.Server.Implementations/Localization/Core/et.json b/Emby.Server.Implementations/Localization/Core/et.json index 8db6a0b388..da44e53d00 100644 --- a/Emby.Server.Implementations/Localization/Core/et.json +++ b/Emby.Server.Implementations/Localization/Core/et.json @@ -119,5 +119,6 @@ "SubtitleDownloadFailureFromForItem": "Subtiitrite allalaadimine {0} > {1} nurjus", "UserPolicyUpdatedWithName": "Kasutaja {0} õigusi värskendati", "UserStoppedPlayingItemWithValues": "{0} lõpetas {1} taasesituse seadmes {2}", - "UserOnlineFromDevice": "{0} on ühendatud seadmest {1}" + "UserOnlineFromDevice": "{0} on ühendatud seadmest {1}", + "External": "Väline" } diff --git a/Emby.Server.Implementations/Localization/Core/hr.json b/Emby.Server.Implementations/Localization/Core/hr.json index d2b5122b26..c63cd2b94f 100644 --- a/Emby.Server.Implementations/Localization/Core/hr.json +++ b/Emby.Server.Implementations/Localization/Core/hr.json @@ -122,5 +122,6 @@ "TaskOptimizeDatabase": "Optimiziraj bazu podataka", "External": "Vanjski", "TaskKeyframeExtractorDescription": "Izvlačenje ključnih okvira iz videozapisa za stvaranje objektivnije HLS liste za reprodukciju. Pokretanje ovog zadatka može potrajati.", - "TaskKeyframeExtractor": "Izvoditelj ključnog okvira" + "TaskKeyframeExtractor": "Izvoditelj ključnog okvira", + "TaskOptimizeDatabaseDescription": "Sažima bazu podataka i uklanja prazan prostor. Pokretanje ovog zadatka, može poboljšati performanse nakon provođenja indeksiranja biblioteke ili provođenja drugih promjena koje utječu na bazu podataka." } diff --git a/Emby.Server.Implementations/Localization/Core/ko.json b/Emby.Server.Implementations/Localization/Core/ko.json index 50d019f906..a4b2e75b30 100644 --- a/Emby.Server.Implementations/Localization/Core/ko.json +++ b/Emby.Server.Implementations/Localization/Core/ko.json @@ -3,14 +3,14 @@ "AppDeviceValues": "앱: {0}, 장치: {1}", "Application": "애플리케이션", "Artists": "아티스트", - "AuthenticationSucceededWithUserName": "{0}이 성공적으로 인증됨", + "AuthenticationSucceededWithUserName": "{0}이(가) 성공적으로 인증됨", "Books": "도서", "CameraImageUploadedFrom": "{0}에서 새로운 카메라 이미지가 업로드됨", "Channels": "채널", "ChapterNameValue": "챕터 {0}", "Collections": "컬렉션", "DeviceOfflineWithName": "{0}의 연결 끊김", - "DeviceOnlineWithName": "{0}이 연결됨", + "DeviceOnlineWithName": "{0}이(가) 연결됨", "FailedLoginAttemptWithUserName": "{0}에서 로그인 실패", "Favorites": "즐겨찾기", "Folders": "폴더", @@ -120,5 +120,8 @@ "Forced": "강제하기", "Default": "기본 설정", "TaskOptimizeDatabaseDescription": "데이터베이스를 압축하고 사용 가능한 공간을 늘립니다. 라이브러리를 검색한 후 이 작업을 실행하거나 데이터베이스 수정같은 비슷한 작업을 수행하면 성능이 향상될 수 있습니다.", - "TaskOptimizeDatabase": "데이터베이스 최적화" + "TaskOptimizeDatabase": "데이터베이스 최적화", + "TaskKeyframeExtractorDescription": "비디오 파일에서 키프레임을 추출하여 더 정확한 HLS 재생 목록을 만듭니다. 이 작업은 오랫동안 진행될 수 있습니다.", + "TaskKeyframeExtractor": "키프레임 추출", + "External": "외부" } diff --git a/Emby.Server.Implementations/Localization/Core/lv.json b/Emby.Server.Implementations/Localization/Core/lv.json index 443a74a10f..e460fd7197 100644 --- a/Emby.Server.Implementations/Localization/Core/lv.json +++ b/Emby.Server.Implementations/Localization/Core/lv.json @@ -84,7 +84,7 @@ "CameraImageUploadedFrom": "Jauns kameras attēls ir ticis augšupielādēts no {0}", "Books": "Grāmatas", "Artists": "Izpildītāji", - "Albums": "Albumi", + "Albums": "Albūmi", "ProviderValue": "Provider: {0}", "HeaderFavoriteSongs": "Dziesmu Favorīti", "HeaderFavoriteShows": "Raidījumu Favorīti", @@ -117,7 +117,8 @@ "TaskCleanActivityLogDescription": "Nodzēš darbību žurnāla ierakstus, kuri ir vecāki par doto vecumu.", "TaskCleanActivityLog": "Notīrīt Darbību Žurnālu", "Undefined": "Nenoteikts", - "Default": "Noklusējums", + "Default": "Noklusējuma", "TaskOptimizeDatabaseDescription": "Saspiež datubāzi un atbrīvo atmiņu. Uzdevum palaišana pēc bibliotēku skenēšanas vai citām, ar datubāzi saistītām, izmaiņām iespējams uzlabos ātrdarbību.", - "TaskOptimizeDatabase": "Optimizēt datubāzi" + "TaskOptimizeDatabase": "Optimizēt datubāzi", + "External": "Ārējais" } diff --git a/Emby.Server.Implementations/Localization/Core/mk.json b/Emby.Server.Implementations/Localization/Core/mk.json index 279734c5ee..cbccad87ff 100644 --- a/Emby.Server.Implementations/Localization/Core/mk.json +++ b/Emby.Server.Implementations/Localization/Core/mk.json @@ -64,9 +64,9 @@ "CameraImageUploadedFrom": "Нова слика од камера беше поставена од {0}", "Books": "Книги", "AuthenticationSucceededWithUserName": "{0} успешно поврзан", - "Artists": "Изведувач", + "Artists": "Изведувачи", "Application": "Апликација", - "AppDeviceValues": "Аплиакција: {0}, Уред: {1}", + "AppDeviceValues": "Апликација: {0}, Уред: {1}", "Albums": "Албуми", "VersionNumber": "Верзија {0}", "ValueSpecialEpisodeName": "Специјално - {0}", @@ -100,5 +100,27 @@ "TasksMaintenanceCategory": "Одржување", "Undefined": "Недефинирано", "Forced": "Принудно", - "Default": "Зададено" + "Default": "Зададено", + "TaskKeyframeExtractorDescription": "Извлекува клучни рамки од видео фајлови за да се направат попрецизни HLS плејлисти. Оваа задача може да работи многу долго време.", + "TaskKeyframeExtractor": "Извлекувач на клучни рамки", + "TaskOptimizeDatabaseDescription": "Компактира датабазата и смалува празното место. Извршувањето на оваа задача по скенирање на библиотеката или правење други промени што прават модификации на датабазата може да подобри перформансите.", + "TaskOptimizeDatabase": "Оптимизирај датабаза", + "TaskDownloadMissingSubtitlesDescription": "Пребарува интернет за преводи што недостиваат според метадата конфигурација.", + "TaskDownloadMissingSubtitles": "Симни преводи што недостигаат", + "TaskRefreshChannelsDescription": "Ажурирај информации за интернет канали.", + "TaskRefreshChannels": "Ажурирај Канали", + "TaskCleanTranscodeDescription": "Избриши транскодирани фајлови постари од еден ден.", + "TaskCleanTranscode": "Исчисти Директориум за Транскодирање", + "TaskUpdatePluginsDescription": "Симни и инсталирај ажурирања за плагини што се конфигурирани за автоматско ажурирање.", + "TaskUpdatePlugins": "Ажурирај Плагини", + "TaskRefreshPeopleDescription": "Ажурирај метадата за акери и директори во вашата медиска библиотека.", + "TaskRefreshPeople": "Ажурирајте ги Луѓето", + "TaskCleanLogsDescription": "Избриши лог фајлови постари од {0} денови.", + "TaskCleanLogs": "Избриши Директориум на Логови", + "TaskRefreshLibraryDescription": "Скенирајте ја вашата медиска библиотека за нови фајлови и ажурирај метадата.", + "TaskRefreshLibrary": "Скенирај Медиумска Библиотека", + "TaskRefreshChapterImagesDescription": "Создава тамбнеил за видеата шти имаат поглавја.", + "TaskCleanActivityLogDescription": "Избришува логови на активности постари од определеното време.", + "TaskCleanActivityLog": "Избриши Лог на Активности", + "External": "Надворешен" } diff --git a/Emby.Server.Implementations/Localization/Core/my.json b/Emby.Server.Implementations/Localization/Core/my.json index 2642373fa9..198f7540c8 100644 --- a/Emby.Server.Implementations/Localization/Core/my.json +++ b/Emby.Server.Implementations/Localization/Core/my.json @@ -6,97 +6,97 @@ "Artists": "အနုပညာရှင်များ", "Albums": "သီချင်းအခွေများ", "TaskOptimizeDatabaseDescription": "ဒေတာဘေ့စ်ကို ကျစ်လစ်စေပြီး နေရာလွတ်များကို ဖြတ်တောက်ပေးသည်။ စာကြည့်တိုက်ကို စကင်န်ဖတ်ပြီးနောက် ဤလုပ်ငန်းကို လုပ်ဆောင်ခြင်း သို့မဟုတ် ဒေတာဘေ့စ်မွမ်းမံမှုများ စွမ်းဆောင်ရည်ကို မြှင့်တင်ပေးနိုင်သည်ဟု ရည်ညွှန်းသော အခြားပြောင်းလဲမှုများကို လုပ်ဆောင်ခြင်း။.", - "TaskOptimizeDatabase": "ဒေတာဘေ့စ်ကို အကောင်းဆုံးဖြစ်အောင်လုပ်ပါ။", + "TaskOptimizeDatabase": "ဒေတာဘေ့စ်ကို အကောင်းဆုံးဖြစ်အောင်လုပ်ပါ", "TaskDownloadMissingSubtitlesDescription": "မက်တာဒေတာ ဖွဲ့စည်းမှုပုံစံအပေါ် အခြေခံ၍ ပျောက်ဆုံးနေသော စာတန်းထိုးများအတွက် အင်တာနက်ကို ရှာဖွေသည်။", - "TaskDownloadMissingSubtitles": "ပျောက်ဆုံးနေသော စာတန်းထိုးများကို ဒေါင်းလုဒ်လုပ်ပါ။", + "TaskDownloadMissingSubtitles": "ပျောက်ဆုံးနေသော စာတန်းထိုးများကို ဒေါင်းလုဒ်လုပ်ပါ", "TaskRefreshChannelsDescription": "အင်တာနက်ချန်နယ်အချက်အလက်ကို ပြန်လည်စတင်သည်။", - "TaskRefreshChannels": "ချန်နယ်များကို ပြန်လည်စတင်ပါ။", + "TaskRefreshChannels": "ချန်နယ်များကို ပြန်လည်စတင်ပါ", "TaskCleanTranscodeDescription": "သက်တမ်း တစ်ရက်ထက်ပိုသော အသွင်ပြောင်းကုဒ်ဖိုင်များကို ဖျက်ပါ။", - "TaskCleanTranscode": "Transcode လမ်းညွှန်ကို သန့်ရှင်းပါ။", + "TaskCleanTranscode": "Transcode လမ်းညွှန်ကို သန့်ရှင်းပါ", "TaskUpdatePluginsDescription": "အလိုအလျောက် အပ်ဒိတ်လုပ်ရန် စီစဉ်ထားသော ပလပ်အင်များအတွက် အပ်ဒိတ်များကို ဒေါင်းလုဒ်လုပ်ပြီး ထည့်သွင်းပါ။", - "TaskUpdatePlugins": "ပလပ်အင်များကို အပ်ဒိတ်လုပ်ပါ။", + "TaskUpdatePlugins": "ပလပ်အင်များကို အပ်ဒိတ်လုပ်ပါ", "TaskRefreshPeopleDescription": "သင့်မီဒီယာစာကြည့်တိုက်ရှိ သရုပ်ဆောင်များနှင့် ဒါရိုက်တာများအတွက် မက်တာဒေတာကို အပ်ဒိတ်လုပ်ပါ။", - "TaskRefreshPeople": "လူများကို ပြန်လည်ဆန်းသစ်ပါ။", + "TaskRefreshPeople": "လူများကို ပြန်လည်ဆန်းသစ်ပါ", "TaskCleanLogsDescription": "{0} ရက်ထက်ပိုသော မှတ်တမ်းဖိုင်များကို ဖျက်သည်။", - "TaskCleanLogs": "မှတ်တမ်းလမ်းညွှန်ကို သန့်ရှင်းပါ။", + "TaskCleanLogs": "မှတ်တမ်းလမ်းညွှန်ကို သန့်ရှင်းပါ", "TaskRefreshLibraryDescription": "သင့်မီဒီယာဒစ်ဂျစ်တိုက်ကို ဖိုင်အသစ်များရှိမရှိ စကင်န်ဖတ်ပြီး ဖိုင်ရဲ့အကြောင်းအရာများ ကို ပြန်ပြုပြင်မွမ်းမံပါ။", - "TaskRefreshLibrary": "မီဒီယာစာကြည့်တိုက်ကို စကင်န်ဖတ်ပါ။", + "TaskRefreshLibrary": "မီဒီယာစာကြည့်တိုက်ကို စကင်န်ဖတ်ပါ", "TaskRefreshChapterImagesDescription": "အခန်းများပါရှိသော ဗီဒီယိုများအတွက် ပုံသေးများကို ဖန်တီးပါ။", - "TaskRefreshChapterImages": "အခန်းတစ်ခုစီ ပုံများကို ထုတ်ယူပါ။", + "TaskRefreshChapterImages": "အခန်းတစ်ခုစီ ပုံများကို ထုတ်ယူပါ", "TaskCleanCacheDescription": "စနစ်မှ မလိုအပ်တော့သော ကက်ရှ်ဖိုင်များကို ဖျက်ပါ။.", - "TaskCleanCache": "Cache Directory ကို ရှင်းပါ။", + "TaskCleanCache": "Cache Directory ကို ရှင်းပါ", "TaskCleanActivityLogDescription": "စီစဉ်သတ်မှတ်ထားသော အသက်ထက် ပိုကြီးသော လုပ်ဆောင်ချက်မှတ်တမ်းများကို ဖျက်ပါ။", - "TaskCleanActivityLog": "လုပ်ဆောင်ချက်မှတ်တမ်းကို ရှင်းလင်းပါ။", + "TaskCleanActivityLog": "လုပ်ဆောင်ချက်မှတ်တမ်းကို ရှင်းလင်းပါ", "TasksChannelsCategory": "အင်တာနက် ချန်နယ်လိုင်းများ", "TasksApplicationCategory": "အပလီကေးရှင်း", "TasksLibraryCategory": "မီဒီယာတိုက်", "TasksMaintenanceCategory": "ပြုပြင် ထိန်းသိမ်းခြင်း", "VersionNumber": "ဗားရှင်း {0}", "ValueSpecialEpisodeName": "အထူး- {0}", - "ValueHasBeenAddedToLibrary": "{0} ကို သင့်မီဒီယာဒစ်ဂျစ်တိုက်သို့ ပေါင်းထည့်လိုက်ပါပြီ။", + "ValueHasBeenAddedToLibrary": "{0} ကို သင့်မီဒီယာဒစ်ဂျစ်တိုက်သို့ ပေါင်းထည့်လိုက်ပါပြီ", "UserStoppedPlayingItemWithValues": "{0} သည် {1} ကို {2} တွင် ဖွင့်ပြီးပါပြီ", "UserStartedPlayingItemWithValues": "{0} သည် {1} ကို {2} တွင် ပြသနေသည်", "UserPolicyUpdatedWithName": "{0} အတွက် အသုံးပြုသူမူဝါဒကို အပ်ဒိတ်လုပ်ပြီးပါပြီ", "UserPasswordChangedWithName": "အသုံးပြုသူ {0} အတွက် စကားဝှက်ကို ပြောင်းထားသည်", "UserOnlineFromDevice": "{0} သည် {1} မှ အွန်လိုင်းဖြစ်သည်", "UserOfflineFromDevice": "{0} သည် {1} မှ ချိတ်ဆက်မှုပြတ်တောက်သွားသည်", - "UserLockedOutWithName": "အသုံးပြုသူ {0} အား လော့ခ်ချထားသည်။", + "UserLockedOutWithName": "အသုံးပြုသူ {0} အား လော့ခ်ချထားသည်", "UserDownloadingItemWithValues": "{0} သည် {1} ကို ဒေါင်းလုဒ်လုပ်နေသည်", - "UserDeletedWithName": "အသုံးပြုသူ {0} ကို ဖျက်လိုက်ပါပြီ။", - "UserCreatedWithName": "အသုံးပြုသူ {0} ကို ဖန်တီးပြီးပါပြီ။", + "UserDeletedWithName": "အသုံးပြုသူ {0} ကို ဖျက်လိုက်ပါပြီ", + "UserCreatedWithName": "အသုံးပြုသူ {0} ကို ဖန်တီးပြီးပါပြီ", "User": "အသုံးပြုသူ", "Undefined": "သတ်မှတ်မထားသော", "TvShows": "တီဗီ ဇာတ်လမ်းတွဲများ", "System": "စနစ်", "Sync": "ထပ်တူကျသည်။", - "SubtitleDownloadFailureFromForItem": "{1} အတွက် {0} မှ စာတန်းထိုးများ ဒေါင်းလုဒ်လုပ်ခြင်း မအောင်မြင်ပါ။", + "SubtitleDownloadFailureFromForItem": "{1} အတွက် {0} မှ စာတန်းထိုးများ ဒေါင်းလုဒ်လုပ်ခြင်း မအောင်မြင်ပါ", "StartupEmbyServerIsLoading": "Jellyfin ဆာဗာကို အသင့်ပြင်နေပါသည်။ ခဏနေ ထပ်စမ်းကြည့်ပါ။", "Songs": "သီချင်းများ", "Shows": "ဇာတ်လမ်းတွဲများ", - "ServerNameNeedsToBeRestarted": "{0} ကို ပြန်လည်စတင်ရန် လိုအပ်သည်။", - "ScheduledTaskStartedWithName": "{0} စတင်ခဲ့သည်။", - "ScheduledTaskFailedWithName": "{0} မအောင်မြင်ပါ။", + "ServerNameNeedsToBeRestarted": "{0} ကို ပြန်လည်စတင်ရန် လိုအပ်သည်", + "ScheduledTaskStartedWithName": "{0} စတင်ခဲ့သည်", + "ScheduledTaskFailedWithName": "{0} မအောင်မြင်ပါ", "ProviderValue": "ဝန်ဆောင်မှုပေးသူ- {0}", - "PluginUpdatedWithName": "ပလပ်ခ်အင် {0} ကို အပ်ဒိတ်လုပ်ထားသည်။", - "PluginUninstalledWithName": "ပလပ်ခ်အင် {0} ကို ဖြုတ်လိုက်ပါပြီ။", - "PluginInstalledWithName": "ပလပ်ခ်အင် {0} ကို ထည့်သွင်းခဲ့သည်။", + "PluginUpdatedWithName": "ပလပ်ခ်အင် {0} ကို အပ်ဒိတ်လုပ်ထားသည်", + "PluginUninstalledWithName": "ပလပ်ခ်အင် {0} ကို ဖြုတ်လိုက်ပါပြီ", + "PluginInstalledWithName": "ပလပ်ခ်အင် {0} ကို ထည့်သွင်းခဲ့သည်", "Plugin": "ပလပ်အင်", "Playlists": "အစီအစဉ်များ", "Photos": "ဓာတ်ပုံများ", - "NotificationOptionVideoPlaybackStopped": "ဗီဒီယိုဖွင့်ခြင်း ရပ်သွားသည်။", - "NotificationOptionVideoPlayback": "ဗီဒီယိုဖွင့်ခြင်း စတင်ပါပြီ။", - "NotificationOptionUserLockedOut": "အသုံးပြုသူ ဝင်ရန် တားမြစ်ခံရသည်။", + "NotificationOptionVideoPlaybackStopped": "ဗီဒီယိုဖွင့်ခြင်း ရပ်သွားသည်", + "NotificationOptionVideoPlayback": "ဗီဒီယိုဖွင့်ခြင်း စတင်ပါပြီ", + "NotificationOptionUserLockedOut": "အသုံးပြုသူ ဝင်ရန် တားမြစ်ခံရသည်", "NotificationOptionTaskFailed": "စီစဉ်ထားသော အလုပ်ပျက်ကွက်", - "NotificationOptionServerRestartRequired": "ဆာဗာ ပြန်လည်စတင်ရန် လိုအပ်သည်။", - "NotificationOptionPluginUpdateInstalled": "ပလပ်အင် အပ်ဒိတ် ထည့်သွင်းပြီးပါပြီ။", - "NotificationOptionPluginUninstalled": "ပလပ်အင်ကို ဖြုတ်လိုက်ပါပြီ။", - "NotificationOptionPluginInstalled": "ပလပ်အင် ထည့်သွင်းထားသည်။", - "NotificationOptionPluginError": "ပလပ်အင် ချို့ယွင်းခြင်း။", - "NotificationOptionNewLibraryContent": "အသစ်များ ထပ်ထည့်ထားပါတယ်။", - "NotificationOptionInstallationFailed": "ထည့်သွင်းမှု မအောင်မြင်ပါ။", - "NotificationOptionCameraImageUploaded": "ကင်မရာမှ ဓာတ်ပုံ အပ်လုဒ် ပြီးပါပြီ။", - "NotificationOptionAudioPlaybackStopped": "အသံဖိုင်ဖွင့်ခြင်း ရပ်သွားသည်။", - "NotificationOptionAudioPlayback": "အသံဖွင့်ခြင်း စတင်ပါပြီ။", - "NotificationOptionApplicationUpdateInstalled": "အပလီကေးရှင်း အပ်ဒိတ်ကို ထည့်သွင်းထားသည်။", - "NotificationOptionApplicationUpdateAvailable": "အပလီကေးရှင်း အပ်ဒိတ် ရနိုင်ပါပြီ။", + "NotificationOptionServerRestartRequired": "ဆာဗာ ပြန်လည်စတင်ရန် လိုအပ်သည်", + "NotificationOptionPluginUpdateInstalled": "ပလပ်အင် အပ်ဒိတ် ထည့်သွင်းပြီးပါပြီ", + "NotificationOptionPluginUninstalled": "ပလပ်အင်ကို ဖြုတ်လိုက်ပါပြီ", + "NotificationOptionPluginInstalled": "ပလပ်အင် ထည့်သွင်းထားသည်", + "NotificationOptionPluginError": "ပလပ်အင် ချို့ယွင်းခြင်း", + "NotificationOptionNewLibraryContent": "အသစ်များ ထပ်ထည့်ထားပါတယ်", + "NotificationOptionInstallationFailed": "ထည့်သွင်းမှု မအောင်မြင်ပါ", + "NotificationOptionCameraImageUploaded": "ကင်မရာမှ ဓာတ်ပုံ အပ်လုဒ် ပြီးပါပြီ", + "NotificationOptionAudioPlaybackStopped": "အသံဖိုင်ဖွင့်ခြင်း ရပ်သွားသည်", + "NotificationOptionAudioPlayback": "အသံဖွင့်ခြင်း စတင်ပါပြီ", + "NotificationOptionApplicationUpdateInstalled": "အပလီကေးရှင်း အပ်ဒိတ်ကို ထည့်သွင်းထားသည်", + "NotificationOptionApplicationUpdateAvailable": "အပလီကေးရှင်း အပ်ဒိတ် ရနိုင်ပါပြီ", "NewVersionIsAvailable": "Jellyfin Server ၏ ဗားရှင်းအသစ်ကို ဒေါင်းလုဒ်လုပ်နိုင်ပါပြီ။", "NameSeasonUnknown": "ဇာတ်လမ်းတွဲ အပိုင်းမသိ", "NameSeasonNumber": "ဇာတ်လမ်းတွဲ အပိုင်း {0}", - "NameInstallFailed": "{0} ထည့်သွင်းမှု မအောင်မြင်ပါ။", + "NameInstallFailed": "{0} ထည့်သွင်းမှု မအောင်မြင်ပါ", "MusicVideos": "ဂီတဗီဒီယိုများ", "Music": "တေးဂီတ", "Movies": "ရုပ်ရှင်များ", "MixedContent": "ရောနှောပါဝင်မှု", - "MessageServerConfigurationUpdated": "ဆာဗာဖွဲ့စည်းပုံကို အပ်ဒိတ်လုပ်ပြီးပါပြီ။", - "MessageNamedServerConfigurationUpdatedWithValue": "ဆာဗာဖွဲ့စည်းပုံကဏ္ဍ {0} ကို အပ်ဒိတ်လုပ်ပြီးပါပြီ။", + "MessageServerConfigurationUpdated": "ဆာဗာဖွဲ့စည်းပုံကို အပ်ဒိတ်လုပ်ပြီးပါပြီ", + "MessageNamedServerConfigurationUpdatedWithValue": "ဆာဗာဖွဲ့စည်းပုံကဏ္ဍ {0} ကို အပ်ဒိတ်လုပ်ပြီးပါပြီ", "MessageApplicationUpdatedTo": "Jellyfin ဆာဗာကို {0} သို့ အပ်ဒိတ်လုပ်ထားသည်", - "MessageApplicationUpdated": "Jellyfin ဆာဗာကို အပ်ဒိတ်လုပ်ပြီးပါပြီ။", + "MessageApplicationUpdated": "Jellyfin ဆာဗာကို အပ်ဒိတ်လုပ်ပြီးပါပြီ", "Latest": "နောက်ဆုံး", "LabelRunningTimeValue": "ကြာချိန် - {0}", "LabelIpAddressValue": "IP လိပ်စာ- {0}", - "ItemRemovedWithName": "{0} ကို ဒစ်ဂျစ်တိုက်မှ ဖယ်ရှားခဲ့သည်။", - "ItemAddedWithName": "{0} ကို စာကြည့်တိုက်သို့ ထည့်ထားသည်။", - "Inherit": "ဆက်ခံ၍ လုပ်ဆောင်သည်။", + "ItemRemovedWithName": "{0} ကို ဒစ်ဂျစ်တိုက်မှ ဖယ်ရှားခဲ့သည်", + "ItemAddedWithName": "{0} ကို စာကြည့်တိုက်သို့ ထည့်ထားသည်", + "Inherit": "ဆက်ခံ၍ လုပ်ဆောင်သည်", "HomeVideos": "ကိုယ်တိုင်ရိုက် ဗီဒီယိုများ", "HeaderRecordingGroups": "အသံဖမ်းအဖွဲ့များ", "HeaderNextUp": "နောက်ထပ်", @@ -106,18 +106,18 @@ "HeaderFavoriteEpisodes": "အကြိုက်ဆုံး ဇာတ်လမ်းအပိုင်းများ", "HeaderFavoriteArtists": "အကြိုက်ဆုံးအနုပညာရှင်များ", "HeaderFavoriteAlbums": "အကြိုက်ဆုံး အယ်လ်ဘမ်များ", - "HeaderContinueWatching": "ဆက်လက်ကြည့်ရှုပါ။", + "HeaderContinueWatching": "ဆက်လက်ကြည့်ရှုပါ", "HeaderAlbumArtists": "အယ်လ်ဘမ်အနုပညာရှင်များ", "Genres": "အမျိုးအစားများ", "Forced": "အတင်းအကြပ်", "Folders": "ဖိုလ်ဒါများ", "Favorites": "အကြိုက်ဆုံးများ", "FailedLoginAttemptWithUserName": "{0} မှ အကောင့်ဝင်ရန် မအောင်မြင်ပါ", - "DeviceOnlineWithName": "{0} ကို ချိတ်ဆက်ထားသည်။", - "DeviceOfflineWithName": "{0} နှင့် အဆက်ပြတ်သွားပါပြီ။", + "DeviceOnlineWithName": "{0} ကို ချိတ်ဆက်ထားသည်", + "DeviceOfflineWithName": "{0} နှင့် အဆက်ပြတ်သွားပါပြီ", "ChapterNameValue": "အခန်း {0}", - "CameraImageUploadedFrom": "ကင်မရာပုံအသစ်ကို {0} မှ ထည့်သွင်းလိုက်သည်။", - "AuthenticationSucceededWithUserName": "{0} စစ်မှန်ကြောင်း အောင်မြင်စွာ အတည်ပြုပြီးပါပြီ။", + "CameraImageUploadedFrom": "ကင်မရာပုံအသစ်ကို {0} မှ ထည့်သွင်းလိုက်သည်", + "AuthenticationSucceededWithUserName": "{0} အောင်မြင်စွာ စစ်မှန်ကြောင်း အတည်ပြုပြီးပါပြီ", "Application": "အပလီကေးရှင်း", "AppDeviceValues": "အက်ပ်- {0}၊ စက်- {1}", "External": "ပြင်ပ" diff --git a/Emby.Server.Implementations/Localization/Core/pt-PT.json b/Emby.Server.Implementations/Localization/Core/pt-PT.json index 44600374b1..7047f1c282 100644 --- a/Emby.Server.Implementations/Localization/Core/pt-PT.json +++ b/Emby.Server.Implementations/Localization/Core/pt-PT.json @@ -8,15 +8,15 @@ "CameraImageUploadedFrom": "Uma nova imagem de câmara foi enviada a partir de {0}", "Channels": "Canais", "ChapterNameValue": "Capítulo {0}", - "Collections": "Coleções", + "Collections": "Colecções", "DeviceOfflineWithName": "{0} desligou-se", "DeviceOnlineWithName": "{0} ligou-se", "FailedLoginAttemptWithUserName": "Tentativa de login falhada a partir de {0}", "Favorites": "Favoritos", "Folders": "Pastas", "Genres": "Géneros", - "HeaderAlbumArtists": "Artistas do Álbum", - "HeaderContinueWatching": "Continuar a Ver", + "HeaderAlbumArtists": "Artistas do álbum", + "HeaderContinueWatching": "Continuar a ver", "HeaderFavoriteAlbums": "Álbuns Favoritos", "HeaderFavoriteArtists": "Artistas Favoritos", "HeaderFavoriteEpisodes": "Episódios Favoritos", diff --git a/Emby.Server.Implementations/Localization/Core/pt.json b/Emby.Server.Implementations/Localization/Core/pt.json index 69bc4c90f7..c2c77ccab1 100644 --- a/Emby.Server.Implementations/Localization/Core/pt.json +++ b/Emby.Server.Implementations/Localization/Core/pt.json @@ -1,5 +1,5 @@ { - "HeaderLiveTV": "TV Ao Vivo", + "HeaderLiveTV": "TV Em Direto", "Collections": "Coleções", "Books": "Livros", "Artists": "Artistas", @@ -10,9 +10,9 @@ "HeaderFavoriteAlbums": "Álbuns Favoritos", "HeaderFavoriteEpisodes": "Episódios Favoritos", "HeaderFavoriteShows": "Séries Favoritas", - "HeaderContinueWatching": "Continuar assistindo", + "HeaderContinueWatching": "Continuar a ver", "HeaderAlbumArtists": "Artistas do Álbum", - "Genres": "Gêneros", + "Genres": "Géneros", "Folders": "Diretórios", "Favorites": "Favoritos", "Channels": "Canais", @@ -74,7 +74,7 @@ "ItemRemovedWithName": "{0} foi removido da biblioteca", "ItemAddedWithName": "{0} foi adicionado à biblioteca", "Inherit": "Herdar", - "HomeVideos": "Vídeos principais", + "HomeVideos": "Vídeos Caseiros", "HeaderRecordingGroups": "Grupos de Gravação", "ValueSpecialEpisodeName": "Episódio Especial - {0}", "Sync": "Sincronização", @@ -83,14 +83,14 @@ "Playlists": "Listas de Reprodução", "Photos": "Fotografias", "Movies": "Filmes", - "FailedLoginAttemptWithUserName": "Tentativa falha de login a partir de {0}", - "DeviceOnlineWithName": "{0} está conectado", - "DeviceOfflineWithName": "{0} desconectou-se", + "FailedLoginAttemptWithUserName": "Tentativa de início de sessão falhada a partir de {0}", + "DeviceOnlineWithName": "{0} está ligado", + "DeviceOfflineWithName": "{0} desligou-se", "ChapterNameValue": "Capítulo {0}", "CameraImageUploadedFrom": "Uma nova imagem da câmara foi enviada a partir de {0}", "AuthenticationSucceededWithUserName": "{0} autenticado com sucesso", - "Application": "Aplicativo", - "AppDeviceValues": "Aplicativo {0}, Dispositivo: {1}", + "Application": "Aplicação", + "AppDeviceValues": "Aplicação: {0}, Dispositivo: {1}", "TaskCleanCache": "Limpar Diretório de Cache", "TasksApplicationCategory": "Aplicativo", "TasksLibraryCategory": "Biblioteca", diff --git a/Emby.Server.Implementations/Localization/Core/sl-SI.json b/Emby.Server.Implementations/Localization/Core/sl-SI.json index 30b24e9f0d..d845accac2 100644 --- a/Emby.Server.Implementations/Localization/Core/sl-SI.json +++ b/Emby.Server.Implementations/Localization/Core/sl-SI.json @@ -90,7 +90,7 @@ "UserStartedPlayingItemWithValues": "{0} predvaja {1} na {2}", "UserStoppedPlayingItemWithValues": "{0} je nehal predvajati {1} na {2}", "ValueHasBeenAddedToLibrary": "{0} je bil dodan vaši knjižnici", - "ValueSpecialEpisodeName": "Bonus - {0}", + "ValueSpecialEpisodeName": "Posebna epizoda - {0}", "VersionNumber": "Različica {0}", "TaskDownloadMissingSubtitles": "Prenesi manjkajoče podnapise", "TaskRefreshChannelsDescription": "Osveži podatke spletnih kanalov.", @@ -122,5 +122,6 @@ "TaskOptimizeDatabaseDescription": "Stisne bazo podatkov in uredi prazen prostor. Zagon tega opravila po iskanju predstavnosti ali drugih spremembah ki vplivajo na bazo podatkov lahko izboljša hitrost delovanja.", "TaskOptimizeDatabase": "Optimiziraj bazo podatkov", "TaskKeyframeExtractor": "Ekstraktor ključnih sličic", - "External": "Zunanje" + "External": "Zunanji", + "TaskKeyframeExtractorDescription": "Iz video datoteke Izvleče ključne sličice, da ustvari bolj natančne sezname predvajanja HLS. Proces lahko traja dolgo časa." } diff --git a/Emby.Server.Implementations/Localization/Core/sr.json b/Emby.Server.Implementations/Localization/Core/sr.json index 781e93926e..1be8867f47 100644 --- a/Emby.Server.Implementations/Localization/Core/sr.json +++ b/Emby.Server.Implementations/Localization/Core/sr.json @@ -86,7 +86,7 @@ "Channels": "Канали", "CameraImageUploadedFrom": "Нова фотографија је учитана са {0}", "Books": "Књиге", - "AuthenticationSucceededWithUserName": "{0} Успешна аутентикација", + "AuthenticationSucceededWithUserName": "{0} Успешна аутентификација", "Artists": "Извођачи", "Application": "Апликација", "AppDeviceValues": "Апликација: {0}, Уређај: {1}", @@ -118,7 +118,7 @@ "Undefined": "Недефинисано", "Forced": "Принудно", "Default": "Подразумевано", - "TaskOptimizeDatabase": "Оптимизуј датабазу", + "TaskOptimizeDatabase": "Оптимизуј банку података", "TaskOptimizeDatabaseDescription": "Сажима базу података и скраћује слободан простор. Покретање овог задатка након скенирања библиотеке или других промена које подразумевају измене базе података које могу побољшати перформансе.", "External": "Спољно", "TaskKeyframeExtractorDescription": "Екстрактује кљулне сличице из видео датотека да би креирао више преицзну HLS плеј-листу. Овај задатак може да потраје дуже време.", diff --git a/Emby.Server.Implementations/Localization/Core/ug.json b/Emby.Server.Implementations/Localization/Core/ug.json index aea93c7fa4..0bcbffb41a 100644 --- a/Emby.Server.Implementations/Localization/Core/ug.json +++ b/Emby.Server.Implementations/Localization/Core/ug.json @@ -3,7 +3,122 @@ "Channels": "قانال", "CameraImageUploadedFrom": "{0} ئورۇندىن يېڭى سۈرەت چىقىرىلدى", "Books": "كىتاب", - "AuthenticationSucceededWithUserName": "تىزىملىتىش مۇۋەپپەقىيەتلىك بول", + "AuthenticationSucceededWithUserName": "{0} تەستىقلاش مۇۋاپىقىيەتلىك بولدى", "Artists": "سەنئەتكار", - "Albums": "پىلاستىنكا" + "Albums": "پىلاستىنكا", + "DeviceOnlineWithName": "{0} ئۇلاندى", + "DeviceOfflineWithName": "{0} ئۈزۈلدى", + "Collections": "توپلام", + "Application": "ئەپ", + "AppDeviceValues": "ئەپ: {0}، ئۈسكۈنە: {1}", + "HeaderLiveTV": "تور تېلېۋىزىيەسى", + "Default": "سۈكۈتتىكى", + "Folders": "ھۆججەت خالتىسى", + "Favorites": "ساقلىغۇچ", + "LabelRunningTimeValue": "ئىجرا بولغان ۋاقتى:{0}", + "HeaderRecordingGroups": "خاتىرلەش گۇرۇپىسى", + "Forced": "ئەڭ", + "TaskKeyframeExtractor": "ھالقىلىق رامكا ئاجراتقۇچ", + "TaskKeyframeExtractorDescription": "سىن ھۆججەتلىرىدىن رامكا ئاجرىتىپ، تېخىمۇ ئېنىق بولغان HLS قويۇلۇش تىزىملىكىنى قۇرۇلىدۇ. بۇ ۋەزىپە ئۇزۇن داۋام قىلىشى مۇمكىن.", + "TaskOptimizeDatabase": "سانداننى ئەلالاشتۇرۇش", + "TaskDownloadMissingSubtitlesDescription": "ئامىللار تەڭشىكىگە ئاساسەن توردىن كەم بولغان فىلىم خېتىنى ئىزدەش.", + "TaskDownloadMissingSubtitles": "كەم بولغان فىلىم خەتلىرىنى چۈشۈرۈش", + "TasksChannelsCategory": "ئىنتېرنېت قاناللىرى", + "TaskRefreshChannelsDescription": "ئىنتېرنېت قانىلى ئۇچۇرىنى يېڭىلاش.", + "TaskRefreshChannels": "قانالنى يېڭىلاش", + "TaskCleanTranscodeDescription": "بىر كۈندىن ئاشقان ئالماشتۇرۇش ھۆججەتلىرىنى ئۆچۈرۈش.", + "TaskCleanTranscode": "ئايلاندۇرۇش ھۆججەت قىسقۇچىنى تازىلاش", + "TaskUpdatePluginsDescription": "ئاپتوماتىك يېڭىلاشقا بېكىتىلگەن قىستۇرمىلارنىڭ يېڭىلانمىسىنى چۈشۈرۈش ۋە قاچىلاش.", + "TaskUpdatePlugins": "قىستۇرمىلارنى يېڭىلاش", + "TaskRefreshPeopleDescription": "مېدىيا ئامبىرىدىكى ئارتىس ۋە رېژىسسورلارنىڭ ئۇچۇرىنى يېڭىلاش.", + "TaskRefreshPeople": "ئابونتلارنى يېڭىلاش", + "TaskCleanLogsDescription": "{0} كۈندىن ئاشقان Log ھۆججىتىنى ئۆچۈرۈش.", + "TaskCleanLogs": "Log ھۆججەت قسقۇچىنى تازىلاش", + "TaskRefreshLibraryDescription": "مېدىيا ئامبىرىغا قوشۇلغان يېڭى ھۆججەتلەرنى ئىزدەش ۋە مېدىيا ئۇچۇرلىرىنى يېڭىلاش.", + "TaskRefreshLibrary": "مېدىيا ئامبىرىنى سىكاننېرلاش", + "TaskRefreshChapterImagesDescription": "ۋېدىئو بۆلەكلىرى ئۈچۈن كىچىك سۈرەت ياساش.", + "TaskRefreshChapterImages": "بۆلەكلەر رەسىملىرىنى چىقىرىۋېلىش", + "TaskCleanCacheDescription": "سىستېما ئىھتىياجى يوق بولغان ۋاقىتلىق ھۆججەتلەرنى ئۆچۈرۈش.", + "TaskCleanCache": "ۋاقىتلىق ھۆججەت قىسقۇچنى تازىلاش", + "TaskCleanActivityLogDescription": "ۋاقىت تەڭشىكىدىن بۇرۇنقى پائالىيەت خاتىرىسى خاتىرىسىنى ئۆچۈرۈش.", + "TaskCleanActivityLog": "پروگرامما خاتىرىسىنى تازىلاش", + "TasksApplicationCategory": "پروگرامما", + "TasksLibraryCategory": "مېدىيا ئامبىرى", + "TasksMaintenanceCategory": "ئاسراش", + "VersionNumber": "نەشرى {0}", + "ValueSpecialEpisodeName": "خاسلىق - {0}", + "ValueHasBeenAddedToLibrary": "{0} مېدىيا ئامبىرىڭىزغا قوشۇلدى", + "UserStoppedPlayingItemWithValues": "{0}،{1} نى {2} دە قويۇنشتىن توختىدى", + "UserStartedPlayingItemWithValues": "{0}،{1} نى {2} دە قويۇۋاتىدۇ", + "UserPolicyUpdatedWithName": "ئابونتلار سىياسىتى {0} غا يېڭىلاندى", + "UserPasswordChangedWithName": "ئابونت{0} ئۈچۈن پارول ئۆزگەرتىلدى", + "UserOfflineFromDevice": "{0} بىلەن {1} نىڭ ئالاقىسى ئۈزۈلدى", + "UserLockedOutWithName": "ئابونت {0} قۇلۇپلاندى", + "UserDownloadingItemWithValues": "{0} چۈشۈرۈۋاتىدۇ {1}", + "UserDeletedWithName": "{0} ئابونت ئۆچۈرۈلدى", + "UserCreatedWithName": "{0} ئابونت يېڭىدىن قوشۇلدى", + "User": "ئابونت", + "Undefined": "بېكىتىلمىگەن", + "TvShows": "تىياتىرلار", + "System": "سىستېما", + "Sync": "ماس قەدەمدەش", + "SubtitleDownloadFailureFromForItem": "{0} دىن {0} نىڭ فىلىم خېتىنى چۈشۈرگىلى بولمىدى", + "StartupEmbyServerIsLoading": "Jellyfin مۇلازىمىتېرى يۈكلىنىۋاتىدۇ. سەل تۇرۇپ قايتا سىناڭ.", + "Songs": "ناخشىلار", + "Shows": "پروگراممىلار", + "ServerNameNeedsToBeRestarted": "{0} قايتا قوزغىتىلىشى كېرەك", + "ScheduledTaskStartedWithName": "{0} باشلاندى", + "ScheduledTaskFailedWithName": "{0} مەغلۇپ بولدى", + "ProviderValue": "تەمىنلىگۈچى: {0}", + "PluginUpdatedWithName": "{0} يېڭىلاندى", + "PluginUninstalledWithName": "{0} ئۆچۈرۈلدى", + "PluginInstalledWithName": "{0} قاچىلاندى", + "Plugin": "قىستۇرما", + "Playlists": "قويۇش تىزىملىكى", + "Photos": "رەسىملەر", + "NotificationOptionVideoPlaybackStopped": "سىن قويۇلۇش توختىدى", + "NotificationOptionVideoPlayback": "سىن قويۇلدى", + "NotificationOptionUserLockedOut": "ئابونت قۇلۇپلاندى", + "NotificationOptionTaskFailed": "بەلگىلەنگەن ۋەزىپە مەغلۇپ بولدى", + "NotificationOptionServerRestartRequired": "مۇلازىمىتېر قايتا قوزغىتىلىشى كېرەك", + "NotificationOptionPluginUpdateInstalled": "قىستۇرما يېڭىلانمىسى قاچىلاندى", + "NotificationOptionPluginInstalled": "قىستۇرما قاچىلاندى", + "NotificationOptionPluginUninstalled": "قىستۇرما ئۆچۈرۈلدى", + "NotificationOptionPluginError": "قىستۇرما خاتالىقى", + "NotificationOptionNewLibraryContent": "يېڭى مەزمۇن قوشۇلدى", + "NotificationOptionInstallationFailed": "قاچىلاش مەغلۇب بولدى", + "NotificationOptionCameraImageUploaded": "كامىكامېرا سۈرىتى يوللاندى", + "NotificationOptionAudioPlayback": "ئاۋاز قويۇش باشلاندى", + "NotificationOptionAudioPlaybackStopped": "ئاۋاز قويۇش توختىدى", + "NotificationOptionApplicationUpdateInstalled": "ئەپ يېڭىلانمىسى ئورنىتىلدى", + "NotificationOptionApplicationUpdateAvailable": "ئەپنىڭ نەشرىنى يېڭىلىغىلى بولۇدۇ", + "NewVersionIsAvailable": "Jellyfin Server نىڭ يېڭى نۇسخىسىنى چۈشۈرگىلى بولىدۇ.", + "NameSeasonUnknown": "نامەمۇم بۆلۈم", + "NameSeasonNumber": "{0}-بۆلۈم", + "NameInstallFailed": "{0} قاچىلاش مەغلۇپ بولدى", + "MusicVideos": "سىنلىق مۇزىكا", + "Music": "مۇزىكا", + "Movies": "فىلىملەر", + "MixedContent": "ئارىلاشما مەزمۇن", + "MessageNamedServerConfigurationUpdatedWithValue": "مۇلازىمىتېر تەڭشىكىنىڭ {0} قىسمى يېڭىلىنىپ بولدى", + "MessageServerConfigurationUpdated": "مۇلازىمىتېر يېڭىلىنىپ بولدى", + "MessageApplicationUpdated": "Jellyfin مۇلازىمىتېرى يېڭىلاندى", + "MessageApplicationUpdatedTo": "Jellyfin مۇلازىمىتېر نەشرى {0} گە يېڭىلاندى", + "Latest": "ئەڭ يېڭى", + "LabelIpAddressValue": "{0}: IP ئادرىسى", + "ItemRemovedWithName": "{0} ئامباردىن چىقىرىلدى", + "ItemAddedWithName": "{0} ئامبارغا قوشۇلدى", + "Inherit": "داۋاملاشتۇرۇش", + "HomeVideos": "ئائىلە سىنلىرى", + "HeaderNextUp": "كېيىنكىسى", + "HeaderFavoriteSongs": "ئەڭ ياقتۇرىدىغان ناخشىلار", + "HeaderFavoriteShows": "ئەڭ ياقتۇرىدىغان پروگراممىلار", + "HeaderFavoriteEpisodes": "ئەڭ ياقتۇرىدىغان تىياتېرلار", + "HeaderFavoriteArtists": "ئەڭ ياقتۇرىدىغان سەنئەتكارلار", + "HeaderFavoriteAlbums": "ياقتۇرىدىغان پىلاستىنكىلار", + "HeaderContinueWatching": "داۋاملىق كۆرۈش", + "HeaderAlbumArtists": "پىلاستىنكا سەنئەتكارلىرى", + "Genres": "ئۇسلۇبلار", + "FailedLoginAttemptWithUserName": "{0} كىرىش ئوڭۇشلۇق بولمىدى", + "External": "سىرتقى" } diff --git a/Emby.Server.Implementations/Localization/Core/zh-HK.json b/Emby.Server.Implementations/Localization/Core/zh-HK.json index ac74da67d1..6c8bf76274 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-HK.json +++ b/Emby.Server.Implementations/Localization/Core/zh-HK.json @@ -120,5 +120,8 @@ "Default": "預設", "TaskOptimizeDatabaseDescription": "壓縮數據庫並截斷可用空間。在掃描媒體庫或執行其他數據庫的修改後運行此任務可能會提高性能。", "TaskOptimizeDatabase": "最佳化數據庫", - "TaskCleanActivityLogDescription": "刪除早於設定時間的日誌記錄。" + "TaskCleanActivityLogDescription": "刪除早於設定時間的日誌記錄。", + "TaskKeyframeExtractorDescription": "提取關鍵格以創建更準確的HLS播放列表。次指示可能用時很長。", + "TaskKeyframeExtractor": "關鍵幀提取器", + "External": "外部" } diff --git a/Emby.Server.Implementations/Localization/Ratings/fi.csv b/Emby.Server.Implementations/Localization/Ratings/fi.csv new file mode 100644 index 0000000000..782785890f --- /dev/null +++ b/Emby.Server.Implementations/Localization/Ratings/fi.csv @@ -0,0 +1,10 @@ +FI-S,1 +FI-T,1 +FI-7,4 +FI-12,5 +FI-16,8 +FI-18,9 +FI-K7,4 +FI-K12,5 +FI-K16,8 +FI-K18,9 diff --git a/Emby.Server.Implementations/Localization/Ratings/no.csv b/Emby.Server.Implementations/Localization/Ratings/no.csv new file mode 100644 index 0000000000..127407be86 --- /dev/null +++ b/Emby.Server.Implementations/Localization/Ratings/no.csv @@ -0,0 +1,6 @@ +NO-A,1 +NO-6,3 +NO-9,4 +NO-12,5 +NO-15,8 +NO-18,9 diff --git a/Emby.Server.Implementations/Localization/Ratings/se.csv b/Emby.Server.Implementations/Localization/Ratings/se.csv new file mode 100644 index 0000000000..1443c07df7 --- /dev/null +++ b/Emby.Server.Implementations/Localization/Ratings/se.csv @@ -0,0 +1,5 @@ +SE-Btl,1 +SE-Barntillåten,1 +SE-7,3 +SE-11,5 +SE-15,8 diff --git a/Emby.Server.Implementations/Net/SocketFactory.cs b/Emby.Server.Implementations/Net/SocketFactory.cs index 21795c8f86..303875df55 100644 --- a/Emby.Server.Implementations/Net/SocketFactory.cs +++ b/Emby.Server.Implementations/Net/SocketFactory.cs @@ -63,10 +63,7 @@ namespace Emby.Server.Implementations.Net /// <inheritdoc /> public ISocket CreateUdpMulticastSocket(IPAddress ipAddress, int multicastTimeToLive, int localPort) { - if (ipAddress == null) - { - throw new ArgumentNullException(nameof(ipAddress)); - } + ArgumentNullException.ThrowIfNull(ipAddress); if (multicastTimeToLive <= 0) { diff --git a/Emby.Server.Implementations/Net/UdpSocket.cs b/Emby.Server.Implementations/Net/UdpSocket.cs index bbbca4fc08..c3994fc040 100644 --- a/Emby.Server.Implementations/Net/UdpSocket.cs +++ b/Emby.Server.Implementations/Net/UdpSocket.cs @@ -35,10 +35,7 @@ namespace Emby.Server.Implementations.Net public UdpSocket(Socket socket, int localPort, IPAddress ip) { - if (socket == null) - { - throw new ArgumentNullException(nameof(socket)); - } + ArgumentNullException.ThrowIfNull(socket); _socket = socket; _localPort = localPort; @@ -51,10 +48,7 @@ namespace Emby.Server.Implementations.Net public UdpSocket(Socket socket, IPEndPoint endPoint) { - if (socket == null) - { - throw new ArgumentNullException(nameof(socket)); - } + ArgumentNullException.ThrowIfNull(socket); _socket = socket; _socket.Connect(endPoint); @@ -96,7 +90,7 @@ namespace Emby.Server.Implementations.Net } else { - tcs.TrySetException(new Exception("SocketError: " + e.SocketError)); + tcs.TrySetException(new SocketException((int)e.SocketError)); } } } @@ -114,7 +108,7 @@ namespace Emby.Server.Implementations.Net } else { - tcs.TrySetException(new Exception("SocketError: " + e.SocketError)); + tcs.TrySetException(new SocketException((int)e.SocketError)); } } } diff --git a/Emby.Server.Implementations/Plugins/PluginManager.cs b/Emby.Server.Implementations/Plugins/PluginManager.cs index 45ef36441d..ec4e0dbeb9 100644 --- a/Emby.Server.Implementations/Plugins/PluginManager.cs +++ b/Emby.Server.Implementations/Plugins/PluginManager.cs @@ -234,10 +234,7 @@ namespace Emby.Server.Implementations.Plugins /// <returns>Outcome of the operation.</returns> public bool RemovePlugin(LocalPlugin plugin) { - if (plugin == null) - { - throw new ArgumentNullException(nameof(plugin)); - } + ArgumentNullException.ThrowIfNull(plugin); if (DeletePlugin(plugin)) { @@ -288,10 +285,7 @@ namespace Emby.Server.Implementations.Plugins /// <param name="plugin">The <see cref="LocalPlugin"/> of the plug to disable.</param> public void EnablePlugin(LocalPlugin plugin) { - if (plugin == null) - { - throw new ArgumentNullException(nameof(plugin)); - } + ArgumentNullException.ThrowIfNull(plugin); if (ChangePluginState(plugin, PluginStatus.Active)) { @@ -306,10 +300,7 @@ namespace Emby.Server.Implementations.Plugins /// <param name="plugin">The <see cref="LocalPlugin"/> of the plug to disable.</param> public void DisablePlugin(LocalPlugin plugin) { - if (plugin == null) - { - throw new ArgumentNullException(nameof(plugin)); - } + ArgumentNullException.ThrowIfNull(plugin); // Update the manifest on disk if (ChangePluginState(plugin, PluginStatus.Disabled)) @@ -326,10 +317,7 @@ namespace Emby.Server.Implementations.Plugins public void FailPlugin(Assembly assembly) { // Only save if disabled. - if (assembly == null) - { - throw new ArgumentNullException(nameof(assembly)); - } + ArgumentNullException.ThrowIfNull(assembly); var plugin = _plugins.FirstOrDefault(p => p.DllFiles.Contains(assembly.Location)); if (plugin == null) diff --git a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs index 2c4d6884d2..b370e06efa 100644 --- a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs +++ b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs @@ -92,25 +92,13 @@ namespace Emby.Server.Implementations.ScheduledTasks /// </exception> public ScheduledTaskWorker(IScheduledTask scheduledTask, IApplicationPaths applicationPaths, ITaskManager taskManager, ILogger logger) { - if (scheduledTask == null) - { - throw new ArgumentNullException(nameof(scheduledTask)); - } + ArgumentNullException.ThrowIfNull(scheduledTask); - if (applicationPaths == null) - { - throw new ArgumentNullException(nameof(applicationPaths)); - } + ArgumentNullException.ThrowIfNull(applicationPaths); - if (taskManager == null) - { - throw new ArgumentNullException(nameof(taskManager)); - } + ArgumentNullException.ThrowIfNull(taskManager); - if (logger == null) - { - throw new ArgumentNullException(nameof(logger)); - } + ArgumentNullException.ThrowIfNull(logger); ScheduledTask = scheduledTask; _applicationPaths = applicationPaths; @@ -249,10 +237,7 @@ namespace Emby.Server.Implementations.ScheduledTasks get => _triggers; set { - if (value == null) - { - throw new ArgumentNullException(nameof(value)); - } + ArgumentNullException.ThrowIfNull(value); // Cleanup current triggers if (_triggers != null) @@ -281,10 +266,7 @@ namespace Emby.Server.Implementations.ScheduledTasks set { - if (value == null) - { - throw new ArgumentNullException(nameof(value)); - } + ArgumentNullException.ThrowIfNull(value); // This null check is not great, but is needed to handle bad user input, or user mucking with the config file incorrectly var triggerList = value.Where(i => i != null).ToArray(); diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index d25376297f..0d1029882b 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -665,10 +665,7 @@ namespace Emby.Server.Implementations.Session { CheckDisposed(); - if (info == null) - { - throw new ArgumentNullException(nameof(info)); - } + ArgumentNullException.ThrowIfNull(info); var session = GetSession(info.SessionId); @@ -762,10 +759,7 @@ namespace Emby.Server.Implementations.Session { CheckDisposed(); - if (info == null) - { - throw new ArgumentNullException(nameof(info)); - } + ArgumentNullException.ThrowIfNull(info); var session = GetSession(info.SessionId); @@ -897,10 +891,7 @@ namespace Emby.Server.Implementations.Session { CheckDisposed(); - if (info == null) - { - throw new ArgumentNullException(nameof(info)); - } + ArgumentNullException.ThrowIfNull(info); if (info.PositionTicks.HasValue && info.PositionTicks.Value < 0) { @@ -1242,7 +1233,7 @@ namespace Emby.Server.Implementations.Session if (item == null) { - _logger.LogError("A non-existant item Id {0} was passed into TranslateItemForPlayback", id); + _logger.LogError("A non-existent item Id {0} was passed into TranslateItemForPlayback", id); return Array.Empty<BaseItem>(); } @@ -1341,15 +1332,9 @@ namespace Emby.Server.Implementations.Session private static void AssertCanControl(SessionInfo session, SessionInfo controllingSession) { - if (session == null) - { - throw new ArgumentNullException(nameof(session)); - } + ArgumentNullException.ThrowIfNull(session); - if (controllingSession == null) - { - throw new ArgumentNullException(nameof(controllingSession)); - } + ArgumentNullException.ThrowIfNull(controllingSession); } /// <summary> @@ -1688,10 +1673,7 @@ namespace Emby.Server.Implementations.Session /// </summary> private BaseItemDto GetItemInfo(BaseItem item, MediaSourceInfo mediaSource) { - if (item == null) - { - throw new ArgumentNullException(nameof(item)); - } + ArgumentNullException.ThrowIfNull(item); var dtoOptions = _itemInfoDtoOptions; @@ -1802,10 +1784,7 @@ namespace Emby.Server.Implementations.Session /// <inheritdoc /> public Task<SessionInfo> GetSessionByAuthenticationToken(Device info, string deviceId, string remoteEndpoint, string appVersion) { - if (info == null) - { - throw new ArgumentNullException(nameof(info)); - } + ArgumentNullException.ThrowIfNull(info); var user = info.UserId.Equals(default) ? null diff --git a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs index a085ee546d..c654828b1e 100644 --- a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs +++ b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs @@ -6,7 +6,7 @@ using System.Linq; using System.Net.WebSockets; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.Extensions; +using Jellyfin.Api.Extensions; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Net; @@ -37,7 +37,7 @@ namespace Emby.Server.Implementations.Session private const float ForceKeepAliveFactor = 0.75f; /// <summary> - /// Lock used for accesing the KeepAlive cancellation token. + /// Lock used for accessing the KeepAlive cancellation token. /// </summary> private readonly object _keepAliveLock = new object(); @@ -54,7 +54,6 @@ namespace Emby.Server.Implementations.Session private readonly ISessionManager _sessionManager; private readonly ILogger<SessionWebSocketListener> _logger; private readonly ILoggerFactory _loggerFactory; - private readonly IAuthorizationContext _authorizationContext; /// <summary> /// The KeepAlive cancellation token. @@ -67,17 +66,14 @@ namespace Emby.Server.Implementations.Session /// <param name="logger">The logger.</param> /// <param name="sessionManager">The session manager.</param> /// <param name="loggerFactory">The logger factory.</param> - /// <param name="authorizationContext">The authorization context.</param> public SessionWebSocketListener( ILogger<SessionWebSocketListener> logger, ISessionManager sessionManager, - ILoggerFactory loggerFactory, - IAuthorizationContext authorizationContext) + ILoggerFactory loggerFactory) { _logger = logger; _sessionManager = sessionManager; _loggerFactory = loggerFactory; - _authorizationContext = authorizationContext; } /// <inheritdoc /> @@ -111,21 +107,18 @@ namespace Emby.Server.Implementations.Session private async Task<SessionInfo> GetSession(HttpContext httpContext, string remoteEndpoint) { - var authorizationInfo = await _authorizationContext.GetAuthorizationInfo(httpContext) - .ConfigureAwait(false); - - if (!authorizationInfo.IsAuthenticated) + if (!httpContext.User.Identity?.IsAuthenticated ?? false) { return null; } - var deviceId = authorizationInfo.DeviceId; + var deviceId = httpContext.User.GetDeviceId(); if (httpContext.Request.Query.TryGetValue("deviceId", out var queryDeviceId)) { deviceId = queryDeviceId; } - return await _sessionManager.GetSessionByAuthenticationToken(authorizationInfo.Token, deviceId, remoteEndpoint) + return await _sessionManager.GetSessionByAuthenticationToken(httpContext.User.GetToken(), deviceId, remoteEndpoint) .ConfigureAwait(false); } diff --git a/Emby.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs b/Emby.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs index db8b689491..2d21bd9c2a 100644 --- a/Emby.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs +++ b/Emby.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs @@ -24,15 +24,9 @@ namespace Emby.Server.Implementations.Sorting /// <returns>System.Int32.</returns> public int Compare(BaseItem? x, BaseItem? y) { - if (x == null) - { - throw new ArgumentNullException(nameof(x)); - } + ArgumentNullException.ThrowIfNull(x); - if (y == null) - { - throw new ArgumentNullException(nameof(y)); - } + ArgumentNullException.ThrowIfNull(y); var episode1 = x as Episode; var episode2 = y as Episode; diff --git a/Emby.Server.Implementations/Sorting/CommunityRatingComparer.cs b/Emby.Server.Implementations/Sorting/CommunityRatingComparer.cs index 5f142fa4bb..5cb11ab465 100644 --- a/Emby.Server.Implementations/Sorting/CommunityRatingComparer.cs +++ b/Emby.Server.Implementations/Sorting/CommunityRatingComparer.cs @@ -23,15 +23,9 @@ namespace Emby.Server.Implementations.Sorting /// <returns>System.Int32.</returns> public int Compare(BaseItem? x, BaseItem? y) { - if (x == null) - { - throw new ArgumentNullException(nameof(x)); - } + ArgumentNullException.ThrowIfNull(x); - if (y == null) - { - throw new ArgumentNullException(nameof(y)); - } + ArgumentNullException.ThrowIfNull(y); return (x.CommunityRating ?? 0).CompareTo(y.CommunityRating ?? 0); } diff --git a/Emby.Server.Implementations/Sorting/DateCreatedComparer.cs b/Emby.Server.Implementations/Sorting/DateCreatedComparer.cs index 8b460166ca..6133aaccc6 100644 --- a/Emby.Server.Implementations/Sorting/DateCreatedComparer.cs +++ b/Emby.Server.Implementations/Sorting/DateCreatedComparer.cs @@ -24,15 +24,9 @@ namespace Emby.Server.Implementations.Sorting /// <returns>System.Int32.</returns> public int Compare(BaseItem? x, BaseItem? y) { - if (x == null) - { - throw new ArgumentNullException(nameof(x)); - } + ArgumentNullException.ThrowIfNull(x); - if (y == null) - { - throw new ArgumentNullException(nameof(y)); - } + ArgumentNullException.ThrowIfNull(y); return DateTime.Compare(x.DateCreated, y.DateCreated); } diff --git a/Emby.Server.Implementations/Sorting/IndexNumberComparer.cs b/Emby.Server.Implementations/Sorting/IndexNumberComparer.cs index c5b00afb15..1bcaccd8a3 100644 --- a/Emby.Server.Implementations/Sorting/IndexNumberComparer.cs +++ b/Emby.Server.Implementations/Sorting/IndexNumberComparer.cs @@ -24,15 +24,9 @@ namespace Emby.Server.Implementations.Sorting /// <returns>System.Int32.</returns> public int Compare(BaseItem? x, BaseItem? y) { - if (x == null) - { - throw new ArgumentNullException(nameof(x)); - } + ArgumentNullException.ThrowIfNull(x); - if (y == null) - { - throw new ArgumentNullException(nameof(y)); - } + ArgumentNullException.ThrowIfNull(y); if (!x.IndexNumber.HasValue && !y.IndexNumber.HasValue) { diff --git a/Emby.Server.Implementations/Sorting/NameComparer.cs b/Emby.Server.Implementations/Sorting/NameComparer.cs index c2875eeb97..93bec4db99 100644 --- a/Emby.Server.Implementations/Sorting/NameComparer.cs +++ b/Emby.Server.Implementations/Sorting/NameComparer.cs @@ -24,15 +24,9 @@ namespace Emby.Server.Implementations.Sorting /// <returns>System.Int32.</returns> public int Compare(BaseItem? x, BaseItem? y) { - if (x == null) - { - throw new ArgumentNullException(nameof(x)); - } + ArgumentNullException.ThrowIfNull(x); - if (y == null) - { - throw new ArgumentNullException(nameof(y)); - } + ArgumentNullException.ThrowIfNull(y); return string.Compare(x.Name, y.Name, StringComparison.OrdinalIgnoreCase); } diff --git a/Emby.Server.Implementations/Sorting/OfficialRatingComparer.cs b/Emby.Server.Implementations/Sorting/OfficialRatingComparer.cs index a81f78ebfc..ce44f99a69 100644 --- a/Emby.Server.Implementations/Sorting/OfficialRatingComparer.cs +++ b/Emby.Server.Implementations/Sorting/OfficialRatingComparer.cs @@ -31,15 +31,9 @@ namespace Emby.Server.Implementations.Sorting /// <returns>System.Int32.</returns> public int Compare(BaseItem? x, BaseItem? y) { - if (x == null) - { - throw new ArgumentNullException(nameof(x)); - } - - if (y == null) - { - throw new ArgumentNullException(nameof(y)); - } + ArgumentNullException.ThrowIfNull(x); + + ArgumentNullException.ThrowIfNull(y); var levelX = string.IsNullOrEmpty(x.OfficialRating) ? 0 : _localization.GetRatingLevel(x.OfficialRating) ?? 0; var levelY = string.IsNullOrEmpty(y.OfficialRating) ? 0 : _localization.GetRatingLevel(y.OfficialRating) ?? 0; diff --git a/Emby.Server.Implementations/Sorting/ParentIndexNumberComparer.cs b/Emby.Server.Implementations/Sorting/ParentIndexNumberComparer.cs index 8c408bb4d2..c54750843e 100644 --- a/Emby.Server.Implementations/Sorting/ParentIndexNumberComparer.cs +++ b/Emby.Server.Implementations/Sorting/ParentIndexNumberComparer.cs @@ -24,15 +24,9 @@ namespace Emby.Server.Implementations.Sorting /// <returns>System.Int32.</returns> public int Compare(BaseItem? x, BaseItem? y) { - if (x == null) - { - throw new ArgumentNullException(nameof(x)); - } + ArgumentNullException.ThrowIfNull(x); - if (y == null) - { - throw new ArgumentNullException(nameof(y)); - } + ArgumentNullException.ThrowIfNull(y); if (!x.ParentIndexNumber.HasValue && !y.ParentIndexNumber.HasValue) { diff --git a/Emby.Server.Implementations/Sorting/RuntimeComparer.cs b/Emby.Server.Implementations/Sorting/RuntimeComparer.cs index e32e5552e4..646bafbb54 100644 --- a/Emby.Server.Implementations/Sorting/RuntimeComparer.cs +++ b/Emby.Server.Implementations/Sorting/RuntimeComparer.cs @@ -26,15 +26,9 @@ namespace Emby.Server.Implementations.Sorting /// <returns>System.Int32.</returns> public int Compare(BaseItem x, BaseItem y) { - if (x == null) - { - throw new ArgumentNullException(nameof(x)); - } + ArgumentNullException.ThrowIfNull(x); - if (y == null) - { - throw new ArgumentNullException(nameof(y)); - } + ArgumentNullException.ThrowIfNull(y); return (x.RunTimeTicks ?? 0).CompareTo(y.RunTimeTicks ?? 0); } diff --git a/Emby.Server.Implementations/Sorting/SortNameComparer.cs b/Emby.Server.Implementations/Sorting/SortNameComparer.cs index 79be9a89a4..628b9b3dda 100644 --- a/Emby.Server.Implementations/Sorting/SortNameComparer.cs +++ b/Emby.Server.Implementations/Sorting/SortNameComparer.cs @@ -26,15 +26,9 @@ namespace Emby.Server.Implementations.Sorting /// <returns>System.Int32.</returns> public int Compare(BaseItem x, BaseItem y) { - if (x == null) - { - throw new ArgumentNullException(nameof(x)); - } + ArgumentNullException.ThrowIfNull(x); - if (y == null) - { - throw new ArgumentNullException(nameof(y)); - } + ArgumentNullException.ThrowIfNull(y); return string.Compare(x.SortName, y.SortName, StringComparison.OrdinalIgnoreCase); } diff --git a/Emby.Server.Implementations/Sorting/StudioComparer.cs b/Emby.Server.Implementations/Sorting/StudioComparer.cs index 4d89cfa8b2..457c062714 100644 --- a/Emby.Server.Implementations/Sorting/StudioComparer.cs +++ b/Emby.Server.Implementations/Sorting/StudioComparer.cs @@ -3,7 +3,6 @@ #pragma warning disable CS1591 using System; -using System.Linq; using Jellyfin.Extensions; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Sorting; @@ -27,15 +26,9 @@ namespace Emby.Server.Implementations.Sorting /// <returns>System.Int32.</returns> public int Compare(BaseItem x, BaseItem y) { - if (x == null) - { - throw new ArgumentNullException(nameof(x)); - } + ArgumentNullException.ThrowIfNull(x); - if (y == null) - { - throw new ArgumentNullException(nameof(y)); - } + ArgumentNullException.ThrowIfNull(y); return AlphanumericComparator.CompareValues(x.Studios.FirstOrDefault(), y.Studios.FirstOrDefault()); } diff --git a/Emby.Server.Implementations/TV/TVSeriesManager.cs b/Emby.Server.Implementations/TV/TVSeriesManager.cs index 727b9d4b5c..6005896ad9 100644 --- a/Emby.Server.Implementations/TV/TVSeriesManager.cs +++ b/Emby.Server.Implementations/TV/TVSeriesManager.cs @@ -43,9 +43,9 @@ namespace Emby.Server.Implementations.TV } string presentationUniqueKey = null; - if (!string.IsNullOrEmpty(query.SeriesId)) + if (query.SeriesId.HasValue && !query.SeriesId.Value.Equals(default)) { - if (_libraryManager.GetItemById(query.SeriesId) is Series series) + if (_libraryManager.GetItemById(query.SeriesId.Value) is Series series) { presentationUniqueKey = GetUniqueSeriesKey(series); } @@ -93,9 +93,9 @@ namespace Emby.Server.Implementations.TV string presentationUniqueKey = null; int? limit = null; - if (!string.IsNullOrEmpty(request.SeriesId)) + if (request.SeriesId.HasValue && !request.SeriesId.Value.Equals(default)) { - if (_libraryManager.GetItemById(request.SeriesId) is Series series) + if (_libraryManager.GetItemById(request.SeriesId.Value) is Series series) { presentationUniqueKey = GetUniqueSeriesKey(series); limit = 1; @@ -135,25 +135,20 @@ namespace Emby.Server.Implementations.TV return GetResult(episodes, request); } - public IEnumerable<Episode> GetNextUpEpisodes(NextUpQuery request, User user, IReadOnlyList<string> seriesKeys, DtoOptions dtoOptions) + private IEnumerable<Episode> GetNextUpEpisodes(NextUpQuery request, User user, IReadOnlyList<string> seriesKeys, DtoOptions dtoOptions) { - // Avoid implicitly captured closure - var currentUser = user; - - var allNextUp = seriesKeys - .Select(i => GetNextUp(i, currentUser, dtoOptions, false)); + var allNextUp = seriesKeys.Select(i => GetNextUp(i, user, dtoOptions, false)); if (request.EnableRewatching) { allNextUp = allNextUp.Concat( - seriesKeys.Select(i => GetNextUp(i, currentUser, dtoOptions, true)) - ) - .OrderByDescending(i => i.Item1); + seriesKeys.Select(i => GetNextUp(i, user, dtoOptions, true))) + .OrderByDescending(i => i.LastWatchedDate); } // If viewing all next up for all series, remove first episodes // But if that returns empty, keep those first episodes (avoid completely empty view) - var alwaysEnableFirstEpisode = !string.IsNullOrEmpty(request.SeriesId); + var alwaysEnableFirstEpisode = request.SeriesId.HasValue && !request.SeriesId.Value.Equals(default); var anyFound = false; return allNextUp @@ -161,23 +156,18 @@ namespace Emby.Server.Implementations.TV { if (request.DisableFirstEpisode) { - return i.Item1 != DateTime.MinValue; + return i.LastWatchedDate != DateTime.MinValue; } - if (alwaysEnableFirstEpisode || (i.Item1 != DateTime.MinValue && i.Item1.Date >= request.NextUpDateCutoff)) + if (alwaysEnableFirstEpisode || (i.LastWatchedDate != DateTime.MinValue && i.LastWatchedDate.Date >= request.NextUpDateCutoff)) { anyFound = true; return true; } - if (!anyFound && i.Item1 == DateTime.MinValue) - { - return true; - } - - return false; + return !anyFound && i.LastWatchedDate == DateTime.MinValue; }) - .Select(i => i.Item2()) + .Select(i => i.GetEpisodeFunction()) .Where(i => i != null); } @@ -195,14 +185,14 @@ namespace Emby.Server.Implementations.TV /// Gets the next up. /// </summary> /// <returns>Task{Episode}.</returns> - private Tuple<DateTime, Func<Episode>> GetNextUp(string seriesKey, User user, DtoOptions dtoOptions, bool rewatching) + private (DateTime LastWatchedDate, Func<Episode> GetEpisodeFunction) GetNextUp(string seriesKey, User user, DtoOptions dtoOptions, bool rewatching) { var lastQuery = new InternalItemsQuery(user) { AncestorWithPresentationUniqueKey = null, SeriesPresentationUniqueKey = seriesKey, IncludeItemTypes = new[] { BaseItemKind.Episode }, - OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Descending) }, + OrderBy = new[] { (ItemSortBy.ParentIndexNumber, SortOrder.Descending), (ItemSortBy.IndexNumber, SortOrder.Descending) }, IsPlayed = true, Limit = 1, ParentIndexNumberNotEquals = 0, @@ -216,24 +206,23 @@ namespace Emby.Server.Implementations.TV if (rewatching) { // find last watched by date played, not by newest episode watched - lastQuery.OrderBy = new[] { (ItemSortBy.DatePlayed, SortOrder.Descending) }; + lastQuery.OrderBy = new[] { (ItemSortBy.DatePlayed, SortOrder.Descending), (ItemSortBy.ParentIndexNumber, SortOrder.Descending), (ItemSortBy.IndexNumber, SortOrder.Descending) }; } var lastWatchedEpisode = _libraryManager.GetItemList(lastQuery).Cast<Episode>().FirstOrDefault(); - Func<Episode> getEpisode = () => + Episode GetEpisode() { var nextQuery = new InternalItemsQuery(user) { AncestorWithPresentationUniqueKey = null, SeriesPresentationUniqueKey = seriesKey, IncludeItemTypes = new[] { BaseItemKind.Episode }, - OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) }, + OrderBy = new[] { (ItemSortBy.ParentIndexNumber, SortOrder.Ascending), (ItemSortBy.IndexNumber, SortOrder.Ascending) }, Limit = 1, IsPlayed = rewatching, IsVirtualItem = false, ParentIndexNumberNotEquals = 0, - MinSortName = lastWatchedEpisode?.SortName, DtoOptions = dtoOptions }; @@ -297,7 +286,7 @@ namespace Emby.Server.Implementations.TV } return nextEpisode; - }; + } if (lastWatchedEpisode != null) { @@ -305,11 +294,11 @@ namespace Emby.Server.Implementations.TV var lastWatchedDate = userData.LastPlayedDate ?? DateTime.MinValue.AddDays(1); - return new Tuple<DateTime, Func<Episode>>(lastWatchedDate, getEpisode); + return (lastWatchedDate, GetEpisode); } // Return the first episode - return new Tuple<DateTime, Func<Episode>>(DateTime.MinValue, getEpisode); + return (DateTime.MinValue, GetEpisode); } private static QueryResult<BaseItem> GetResult(IEnumerable<BaseItem> items, NextUpQuery query) diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs index 40c386e823..550f0e0755 100644 --- a/Emby.Server.Implementations/Updates/InstallationManager.cs +++ b/Emby.Server.Implementations/Updates/InstallationManager.cs @@ -294,10 +294,7 @@ namespace Emby.Server.Implementations.Updates /// <inheritdoc /> public async Task InstallPackage(InstallationInfo package, CancellationToken cancellationToken) { - if (package == null) - { - throw new ArgumentNullException(nameof(package)); - } + ArgumentNullException.ThrowIfNull(package); var innerCancellationTokenSource = new CancellationTokenSource(); |
