mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-04-01 07:51:54 +01:00
Merge branch 'jellyfin:master' into gzip
This commit is contained in:
@@ -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 />
|
||||
|
||||
@@ -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>();
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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 =>
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -93,6 +93,7 @@ namespace Emby.Server.Implementations.Images
|
||||
returnItems.Shuffle();
|
||||
return returnItems;
|
||||
}
|
||||
|
||||
returnItems = items
|
||||
.Where(i => i.HasImage(ImageType.Primary))
|
||||
.ToList();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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}",
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -196,7 +196,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
||||
IsInfiniteStream = true,
|
||||
IsRemote = isRemote,
|
||||
|
||||
IgnoreDts = true,
|
||||
IgnoreDts = info.IgnoreDts,
|
||||
SupportsDirectPlay = supportsDirectPlay,
|
||||
SupportsDirectStream = supportsDirectStream,
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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."
|
||||
}
|
||||
|
||||
@@ -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": "외부"
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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": "Надворешен"
|
||||
}
|
||||
|
||||
@@ -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": "ပြင်ပ"
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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."
|
||||
}
|
||||
|
||||
@@ -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 плеј-листу. Овај задатак може да потраје дуже време.",
|
||||
|
||||
@@ -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": "سىرتقى"
|
||||
}
|
||||
|
||||
@@ -120,5 +120,8 @@
|
||||
"Default": "預設",
|
||||
"TaskOptimizeDatabaseDescription": "壓縮數據庫並截斷可用空間。在掃描媒體庫或執行其他數據庫的修改後運行此任務可能會提高性能。",
|
||||
"TaskOptimizeDatabase": "最佳化數據庫",
|
||||
"TaskCleanActivityLogDescription": "刪除早於設定時間的日誌記錄。"
|
||||
"TaskCleanActivityLogDescription": "刪除早於設定時間的日誌記錄。",
|
||||
"TaskKeyframeExtractorDescription": "提取關鍵格以創建更準確的HLS播放列表。次指示可能用時很長。",
|
||||
"TaskKeyframeExtractor": "關鍵幀提取器",
|
||||
"External": "外部"
|
||||
}
|
||||
|
||||
10
Emby.Server.Implementations/Localization/Ratings/fi.csv
Normal file
10
Emby.Server.Implementations/Localization/Ratings/fi.csv
Normal file
@@ -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
|
||||
|
6
Emby.Server.Implementations/Localization/Ratings/no.csv
Normal file
6
Emby.Server.Implementations/Localization/Ratings/no.csv
Normal file
@@ -0,0 +1,6 @@
|
||||
NO-A,1
|
||||
NO-6,3
|
||||
NO-9,4
|
||||
NO-12,5
|
||||
NO-15,8
|
||||
NO-18,9
|
||||
|
5
Emby.Server.Implementations/Localization/Ratings/se.csv
Normal file
5
Emby.Server.Implementations/Localization/Ratings/se.csv
Normal file
@@ -0,0 +1,5 @@
|
||||
SE-Btl,1
|
||||
SE-Barntillåten,1
|
||||
SE-7,3
|
||||
SE-11,5
|
||||
SE-15,8
|
||||
|
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
ArgumentNullException.ThrowIfNull(x);
|
||||
|
||||
if (y == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(y));
|
||||
}
|
||||
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;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user