aboutsummaryrefslogtreecommitdiff
path: root/Emby.Server.Implementations
diff options
context:
space:
mode:
Diffstat (limited to 'Emby.Server.Implementations')
-rw-r--r--Emby.Server.Implementations/ApplicationHost.cs15
-rw-r--r--Emby.Server.Implementations/ConfigurationOptions.cs2
-rw-r--r--Emby.Server.Implementations/Data/BaseSqliteRepository.cs13
-rw-r--r--Emby.Server.Implementations/Data/SqliteItemRepository.cs80
-rw-r--r--Emby.Server.Implementations/HttpServer/Security/SessionContext.cs2
-rw-r--r--Emby.Server.Implementations/Library/LibraryManager.cs33
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs8
-rw-r--r--Emby.Server.Implementations/Localization/Core/id.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/sq.json117
-rw-r--r--Emby.Server.Implementations/Localization/Core/vi.json117
-rw-r--r--Emby.Server.Implementations/Localization/Core/zh-TW.json52
-rw-r--r--Emby.Server.Implementations/Security/AuthenticationRepository.cs5
-rw-r--r--Emby.Server.Implementations/Udp/UdpServer.cs6
-rw-r--r--Emby.Server.Implementations/Updates/InstallationManager.cs13
14 files changed, 361 insertions, 104 deletions
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index 83617111b1..276d0fe30a 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -241,8 +241,14 @@ namespace Emby.Server.Implementations
public IServerConfigurationManager ServerConfigurationManager => (IServerConfigurationManager)ConfigurationManager;
/// <summary>
- /// Initializes a new instance of the <see cref="ApplicationHost" /> class.
+ /// Initializes a new instance of the <see cref="ApplicationHost"/> class.
/// </summary>
+ /// <param name="applicationPaths">Instance of the <see cref="IServerApplicationPaths"/> interface.</param>
+ /// <param name="loggerFactory">Instance of the <see cref="ILoggerFactory"/> interface.</param>
+ /// <param name="options">Instance of the <see cref="IStartupOptions"/> interface.</param>
+ /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
+ /// <param name="networkManager">Instance of the <see cref="INetworkManager"/> interface.</param>
+ /// <param name="serviceCollection">Instance of the <see cref="IServiceCollection"/> interface.</param>
public ApplicationHost(
IServerApplicationPaths applicationPaths,
ILoggerFactory loggerFactory,
@@ -1244,7 +1250,8 @@ namespace Emby.Server.Implementations
Id = SystemId,
OperatingSystem = OperatingSystem.Id.ToString(),
ServerName = FriendlyName,
- LocalAddress = localAddress
+ LocalAddress = localAddress,
+ StartupWizardCompleted = ConfigurationManager.CommonConfiguration.IsStartupWizardCompleted
};
}
@@ -1538,10 +1545,6 @@ namespace Emby.Server.Implementations
}
}
- public virtual void EnableLoopback(string appName)
- {
- }
-
private bool _disposed = false;
/// <summary>
diff --git a/Emby.Server.Implementations/ConfigurationOptions.cs b/Emby.Server.Implementations/ConfigurationOptions.cs
index fde6fa1153..cd9dbb1bda 100644
--- a/Emby.Server.Implementations/ConfigurationOptions.cs
+++ b/Emby.Server.Implementations/ConfigurationOptions.cs
@@ -18,7 +18,7 @@ namespace Emby.Server.Implementations
{ DefaultRedirectKey, "web/index.html" },
{ FfmpegProbeSizeKey, "1G" },
{ FfmpegAnalyzeDurationKey, "200M" },
- { PlaylistsAllowDuplicatesKey, bool.TrueString },
+ { PlaylistsAllowDuplicatesKey, bool.FalseString },
{ BindToUnixSocketKey, bool.FalseString }
};
}
diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
index 8a3716380b..0fb050a7a5 100644
--- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
+++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
@@ -143,8 +143,17 @@ namespace Emby.Server.Implementations.Data
public IStatement PrepareStatement(IDatabaseConnection connection, string sql)
=> connection.PrepareStatement(sql);
- public IEnumerable<IStatement> PrepareAll(IDatabaseConnection connection, IEnumerable<string> sql)
- => sql.Select(connection.PrepareStatement);
+ public IStatement[] PrepareAll(IDatabaseConnection connection, IReadOnlyList<string> sql)
+ {
+ int len = sql.Count;
+ IStatement[] statements = new IStatement[len];
+ for (int i = 0; i < len; i++)
+ {
+ statements[i] = connection.PrepareStatement(sql[i]);
+ }
+
+ return statements;
+ }
protected bool TableExists(ManagedConnection connection, string name)
{
diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
index 5bf740cfcc..ab60cee618 100644
--- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
@@ -138,7 +138,6 @@ namespace Emby.Server.Implementations.Data
"pragma shrink_memory"
};
-
string[] postQueries =
{
// obsolete
@@ -560,7 +559,7 @@ namespace Emby.Server.Implementations.Data
{
SaveItemCommandText,
"delete from AncestorIds where ItemId=@ItemId"
- }).ToList();
+ });
using (var saveItemStatement = statements[0])
using (var deleteAncestorsStatement = statements[1])
@@ -2925,7 +2924,7 @@ namespace Emby.Server.Implementations.Data
{
connection.RunInTransaction(db =>
{
- var statements = PrepareAll(db, statementTexts).ToList();
+ var statements = PrepareAll(db, statementTexts);
if (!isReturningZeroItems)
{
@@ -2963,7 +2962,7 @@ namespace Emby.Server.Implementations.Data
if (query.EnableTotalRecordCount)
{
- using (var statement = statements[statements.Count - 1])
+ using (var statement = statements[statements.Length - 1])
{
if (EnableJoinUserData(query))
{
@@ -3329,7 +3328,7 @@ namespace Emby.Server.Implementations.Data
{
connection.RunInTransaction(db =>
{
- var statements = PrepareAll(db, statementTexts).ToList();
+ var statements = PrepareAll(db, statementTexts);
if (!isReturningZeroItems)
{
@@ -3355,7 +3354,7 @@ namespace Emby.Server.Implementations.Data
if (query.EnableTotalRecordCount)
{
- using (var statement = statements[statements.Count - 1])
+ using (var statement = statements[statements.Length - 1])
{
if (EnableJoinUserData(query))
{
@@ -3718,26 +3717,31 @@ namespace Emby.Server.Implementations.Data
statement?.TryBind("@MaxPremiereDate", query.MaxPremiereDate.Value);
}
+ StringBuilder clauseBuilder = new StringBuilder();
+ const string Or = " OR ";
+
var trailerTypes = query.TrailerTypes;
int trailerTypesLen = trailerTypes.Length;
if (trailerTypesLen > 0)
{
- const string Or = " OR ";
- StringBuilder clause = new StringBuilder("(", trailerTypesLen * 32);
+ clauseBuilder.Append('(');
+
for (int i = 0; i < trailerTypesLen; i++)
{
var paramName = "@TrailerTypes" + i;
- clause.Append("TrailerTypes like ")
+ clauseBuilder.Append("TrailerTypes like ")
.Append(paramName)
.Append(Or);
statement?.TryBind(paramName, "%" + trailerTypes[i] + "%");
}
// Remove last " OR "
- clause.Length -= Or.Length;
- clause.Append(')');
+ clauseBuilder.Length -= Or.Length;
+ clauseBuilder.Append(')');
- whereClauses.Add(clause.ToString());
+ whereClauses.Add(clauseBuilder.ToString());
+
+ clauseBuilder.Length = 0;
}
if (query.IsAiring.HasValue)
@@ -3757,23 +3761,35 @@ namespace Emby.Server.Implementations.Data
}
}
- if (query.PersonIds.Length > 0)
+ int personIdsLen = query.PersonIds.Length;
+ if (personIdsLen > 0)
{
// TODO: Should this query with CleanName ?
- var clauses = new List<string>();
- var index = 0;
- foreach (var personId in query.PersonIds)
+ clauseBuilder.Append('(');
+
+ Span<byte> idBytes = stackalloc byte[16];
+ for (int i = 0; i < personIdsLen; i++)
{
- var paramName = "@PersonId" + index;
+ string paramName = "@PersonId" + i;
+ clauseBuilder.Append("(guid in (select itemid from People where Name = (select Name from TypedBaseItems where guid=")
+ .Append(paramName)
+ .Append("))) OR ");
- clauses.Add("(guid in (select itemid from People where Name = (select Name from TypedBaseItems where guid=" + paramName + ")))");
- statement?.TryBind(paramName, personId.ToByteArray());
- index++;
+ if (statement != null)
+ {
+ query.PersonIds[i].TryWriteBytes(idBytes);
+ statement.TryBind(paramName, idBytes);
+ }
}
- var clause = "(" + string.Join(" OR ", clauses) + ")";
- whereClauses.Add(clause);
+ // Remove last " OR "
+ clauseBuilder.Length -= Or.Length;
+ clauseBuilder.Append(')');
+
+ whereClauses.Add(clauseBuilder.ToString());
+
+ clauseBuilder.Length = 0;
}
if (!string.IsNullOrWhiteSpace(query.Person))
@@ -5149,7 +5165,8 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
CheckDisposed();
- var itemIdBlob = itemId.ToByteArray();
+ Span<byte> itemIdBlob = stackalloc byte[16];
+ itemId.TryWriteBytes(itemIdBlob);
// First delete
deleteAncestorsStatement.Reset();
@@ -5165,17 +5182,15 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
for (var i = 0; i < ancestorIds.Count; i++)
{
- if (i > 0)
- {
- insertText.Append(',');
- }
-
insertText.AppendFormat(
CultureInfo.InvariantCulture,
- "(@ItemId, @AncestorId{0}, @AncestorIdText{0})",
+ "(@ItemId, @AncestorId{0}, @AncestorIdText{0}),",
i.ToString(CultureInfo.InvariantCulture));
}
+ // Remove last ,
+ insertText.Length--;
+
using (var statement = PrepareStatement(db, insertText.ToString()))
{
statement.TryBind("@ItemId", itemIdBlob);
@@ -5185,8 +5200,9 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
var index = i.ToString(CultureInfo.InvariantCulture);
var ancestorId = ancestorIds[i];
+ ancestorId.TryWriteBytes(itemIdBlob);
- statement.TryBind("@AncestorId" + index, ancestorId.ToByteArray());
+ statement.TryBind("@AncestorId" + index, itemIdBlob);
statement.TryBind("@AncestorIdText" + index, ancestorId.ToString("N", CultureInfo.InvariantCulture));
}
@@ -5466,7 +5482,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
connection.RunInTransaction(
db =>
{
- var statements = PrepareAll(db, statementTexts).ToList();
+ var statements = PrepareAll(db, statementTexts);
if (!isReturningZeroItems)
{
@@ -5517,7 +5533,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
+ GetJoinUserDataText(query)
+ whereText;
- using (var statement = statements[statements.Count - 1])
+ using (var statement = statements[statements.Length - 1])
{
statement.TryBind("@SelectType", returnType);
if (EnableJoinUserData(query))
diff --git a/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs b/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs
index 8777c59b7b..86914dea20 100644
--- a/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs
+++ b/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs
@@ -28,7 +28,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
var authorization = _authContext.GetAuthorizationInfo(requestContext);
var user = authorization.User;
- return _sessionManager.LogSessionActivity(authorization.Client, authorization.Version, authorization.DeviceId, authorization.Device, requestContext.Request.RemoteIp(), user);
+ return _sessionManager.LogSessionActivity(authorization.Client, authorization.Version, authorization.DeviceId, authorization.Device, requestContext.GetNormalizedRemoteIp(), user);
}
public SessionInfo GetSession(object requestContext)
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs
index 375f09f5bb..00282b71a5 100644
--- a/Emby.Server.Implementations/Library/LibraryManager.cs
+++ b/Emby.Server.Implementations/Library/LibraryManager.cs
@@ -513,10 +513,11 @@ namespace Emby.Server.Implementations.Library
throw new ArgumentNullException(nameof(type));
}
- if (key.StartsWith(_configurationManager.ApplicationPaths.ProgramDataPath, StringComparison.Ordinal))
+ string programDataPath = _configurationManager.ApplicationPaths.ProgramDataPath;
+ if (key.StartsWith(programDataPath, StringComparison.Ordinal))
{
// Try to normalize paths located underneath program-data in an attempt to make them more portable
- key = key.Substring(_configurationManager.ApplicationPaths.ProgramDataPath.Length)
+ key = key.Substring(programDataPath.Length)
.TrimStart('/', '\\')
.Replace('/', '\\');
}
@@ -871,17 +872,17 @@ namespace Emby.Server.Implementations.Library
public Guid GetStudioId(string name)
{
- return GetItemByNameId<Studio>(Studio.GetPath, name);
+ return GetItemByNameId<Studio>(Studio.GetPath(name));
}
public Guid GetGenreId(string name)
{
- return GetItemByNameId<Genre>(Genre.GetPath, name);
+ return GetItemByNameId<Genre>(Genre.GetPath(name));
}
public Guid GetMusicGenreId(string name)
{
- return GetItemByNameId<MusicGenre>(MusicGenre.GetPath, name);
+ return GetItemByNameId<MusicGenre>(MusicGenre.GetPath(name));
}
/// <summary>
@@ -943,7 +944,7 @@ namespace Emby.Server.Implementations.Library
{
var existing = GetItemList(new InternalItemsQuery
{
- IncludeItemTypes = new[] { typeof(T).Name },
+ IncludeItemTypes = new[] { nameof(MusicArtist) },
Name = name,
DtoOptions = options
}).Cast<MusicArtist>()
@@ -957,13 +958,11 @@ namespace Emby.Server.Implementations.Library
}
}
- var id = GetItemByNameId<T>(getPathFn, name);
-
+ var path = getPathFn(name);
+ var id = GetItemByNameId<T>(path);
var item = GetItemById(id) as T;
-
if (item == null)
{
- var path = getPathFn(name);
item = new T
{
Name = name,
@@ -979,10 +978,9 @@ namespace Emby.Server.Implementations.Library
return item;
}
- private Guid GetItemByNameId<T>(Func<string, string> getPathFn, string name)
+ private Guid GetItemByNameId<T>(string path)
where T : BaseItem, new()
{
- var path = getPathFn(name);
var forceCaseInsensitiveId = _configurationManager.Configuration.EnableNormalizedItemByNameIds;
return GetNewItemIdInternal(path, typeof(T), forceCaseInsensitiveId);
}
@@ -1805,21 +1803,18 @@ namespace Emby.Server.Implementations.Library
/// <param name="items">The items.</param>
/// <param name="parent">The parent item.</param>
/// <param name="cancellationToken">The cancellation token.</param>
- public void CreateItems(IEnumerable<BaseItem> items, BaseItem parent, CancellationToken cancellationToken)
+ public void CreateItems(IReadOnlyList<BaseItem> items, BaseItem parent, CancellationToken cancellationToken)
{
- // Don't iterate multiple times
- var itemsList = items.ToList();
-
- _itemRepository.SaveItems(itemsList, cancellationToken);
+ _itemRepository.SaveItems(items, cancellationToken);
- foreach (var item in itemsList)
+ foreach (var item in items)
{
RegisterItem(item);
}
if (ItemAdded != null)
{
- foreach (var item in itemsList)
+ foreach (var item in items)
{
// With the live tv guide this just creates too much noise
if (item.SourceType != SourceType.Library)
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
index f9ae55af85..655ff58537 100644
--- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
@@ -468,13 +468,15 @@ namespace Emby.Server.Implementations.LiveTv.Listings
imageIdString = imageIdString.TrimEnd(',') + "]";
- using var message = new HttpRequestMessage(HttpMethod.Post, ApiUrl + "/metadata/programs");
- message.Content = new StringContent(imageIdString, Encoding.UTF8, MediaTypeNames.Application.Json);
+ using var message = new HttpRequestMessage(HttpMethod.Post, ApiUrl + "/metadata/programs")
+ {
+ Content = new StringContent(imageIdString, Encoding.UTF8, MediaTypeNames.Application.Json)
+ };
try
{
using var innerResponse2 = await Send(message, true, info, cancellationToken).ConfigureAwait(false);
- await using var response = await innerResponse2.Content.ReadAsStreamAsync();
+ await using var response = await innerResponse2.Content.ReadAsStreamAsync().ConfigureAwait(false);
return await _jsonSerializer.DeserializeFromStreamAsync<List<ScheduleDirect.ShowImages>>(
response).ConfigureAwait(false);
}
diff --git a/Emby.Server.Implementations/Localization/Core/id.json b/Emby.Server.Implementations/Localization/Core/id.json
index b0dfc312e1..585fc6f027 100644
--- a/Emby.Server.Implementations/Localization/Core/id.json
+++ b/Emby.Server.Implementations/Localization/Core/id.json
@@ -19,7 +19,7 @@
"HeaderFavoriteEpisodes": "Episode Favorit",
"HeaderFavoriteArtists": "Artis Favorit",
"HeaderFavoriteAlbums": "Album Favorit",
- "HeaderContinueWatching": "Lanjutkan Menonton",
+ "HeaderContinueWatching": "Lanjut Menonton",
"HeaderCameraUploads": "Unggahan Kamera",
"HeaderAlbumArtists": "Album Artis",
"Genres": "Aliran",
diff --git a/Emby.Server.Implementations/Localization/Core/sq.json b/Emby.Server.Implementations/Localization/Core/sq.json
new file mode 100644
index 0000000000..347ba5f970
--- /dev/null
+++ b/Emby.Server.Implementations/Localization/Core/sq.json
@@ -0,0 +1,117 @@
+{
+ "MessageApplicationUpdatedTo": "Serveri Jellyfin u përditesua në versionin {0}",
+ "Inherit": "Trashgimi",
+ "TaskDownloadMissingSubtitlesDescription": "Kërkon në internet për titra që mungojnë bazuar tek konfigurimi i metadata-ve.",
+ "TaskDownloadMissingSubtitles": "Shkarko titra që mungojnë",
+ "TaskRefreshChannelsDescription": "Rifreskon informacionin e kanaleve të internetit.",
+ "TaskRefreshChannels": "Rifresko Kanalet",
+ "TaskCleanTranscodeDescription": "Fshin skedarët e transkodimit që janë më të vjetër se një ditë.",
+ "TaskCleanTranscode": "Fshi dosjen e transkodimit",
+ "TaskUpdatePluginsDescription": "Shkarkon dhe instalon përditësimi për plugin që janë konfiguruar të përditësohen automatikisht.",
+ "TaskUpdatePlugins": "Përditëso Plugin",
+ "TaskRefreshPeopleDescription": "Përditëson metadata të aktorëve dhe regjizorëve në librarinë tuaj.",
+ "TaskRefreshPeople": "Rifresko aktorët",
+ "TaskCleanLogsDescription": "Fshin skëdarët log që janë më të vjetër se {0} ditë.",
+ "TaskCleanLogs": "Fshi dosjen Log",
+ "TaskRefreshLibraryDescription": "Skanon librarinë media për skedarë të rinj dhe rifreskon metadata.",
+ "TaskRefreshLibrary": "Skano librarinë media",
+ "TaskRefreshChapterImagesDescription": "Krijon imazh për videot që kanë kapituj.",
+ "TaskRefreshChapterImages": "Ekstrakto Imazhet e Kapitullit",
+ "TaskCleanCacheDescription": "Fshi skedarët e cache-s që nuk i duhen më sistemit.",
+ "TaskCleanCache": "Pastro memorjen cache",
+ "TasksChannelsCategory": "Kanalet nga interneti",
+ "TasksApplicationCategory": "Aplikacioni",
+ "TasksLibraryCategory": "Libraria",
+ "TasksMaintenanceCategory": "Mirëmbajtje",
+ "VersionNumber": "Versioni {0}",
+ "ValueSpecialEpisodeName": "Speciale - {0}",
+ "ValueHasBeenAddedToLibrary": "{0} u shtua tek libraria juaj",
+ "UserStoppedPlayingItemWithValues": "{0} mbaroi së shikuari {1} tek {2}",
+ "UserStartedPlayingItemWithValues": "{0} po shikon {1} tek {2}",
+ "UserPolicyUpdatedWithName": "Politika e përdoruesit u përditësua për {0}",
+ "UserPasswordChangedWithName": "Fjalëkalimi u ndryshua për përdoruesin {0}",
+ "UserOnlineFromDevice": "{0} është në linjë nga {1}",
+ "UserOfflineFromDevice": "{0} u shkëput nga {1}",
+ "UserLockedOutWithName": "Përdoruesi {0} u përjashtua",
+ "UserDownloadingItemWithValues": "{0} po shkarkon {1}",
+ "UserDeletedWithName": "Përdoruesi {0} u fshi",
+ "UserCreatedWithName": "Përdoruesi {0} u krijua",
+ "User": "Përdoruesi",
+ "TvShows": "Seriale TV",
+ "System": "Sistemi",
+ "Sync": "Sinkronizo",
+ "SubtitleDownloadFailureFromForItem": "Titrat deshtuan të shkarkohen nga {0} për {1}",
+ "StartupEmbyServerIsLoading": "Serveri Jellyfin po ngarkohet. Ju lutemi provoni përseri pas pak.",
+ "Songs": "Këngë",
+ "Shows": "Seriale",
+ "ServerNameNeedsToBeRestarted": "{0} duhet të ristartoj",
+ "ScheduledTaskStartedWithName": "{0} filloi",
+ "ScheduledTaskFailedWithName": "{0} dështoi",
+ "ProviderValue": "Ofruesi: {0}",
+ "PluginUpdatedWithName": "{0} u përditësua",
+ "PluginUninstalledWithName": "{0} u çinstalua",
+ "PluginInstalledWithName": "{0} u instalua",
+ "Plugin": "Plugin",
+ "Playlists": "Listat për luajtje",
+ "Photos": "Fotografitë",
+ "NotificationOptionVideoPlaybackStopped": "Luajtja e videos ndaloi",
+ "NotificationOptionVideoPlayback": "Luajtja e videos filloi",
+ "NotificationOptionUserLockedOut": "Përdoruesi u përjashtua",
+ "NotificationOptionTaskFailed": "Ushtrimi i planifikuar dështoi",
+ "NotificationOptionServerRestartRequired": "Kërkohet ristartim i serverit",
+ "NotificationOptionPluginUpdateInstalled": "Përditësimi i plugin u instalua",
+ "NotificationOptionPluginUninstalled": "Plugin u çinstalua",
+ "NotificationOptionPluginInstalled": "Plugin u instalua",
+ "NotificationOptionPluginError": "Plugin dështoi",
+ "NotificationOptionNewLibraryContent": "Një përmbajtje e re u shtua",
+ "NotificationOptionInstallationFailed": "Instalimi dështoi",
+ "NotificationOptionCameraImageUploaded": "Fotoja nga kamera u ngarkua",
+ "NotificationOptionAudioPlaybackStopped": "Luajtja e audios ndaloi",
+ "NotificationOptionAudioPlayback": "Luajtja e audios filloi",
+ "NotificationOptionApplicationUpdateInstalled": "Përditësimi i aplikacionit u instalua",
+ "NotificationOptionApplicationUpdateAvailable": "Një perditësim i aplikacionit është gati",
+ "NewVersionIsAvailable": "Një version i ri i Jellyfin është gati për tu shkarkuar.",
+ "NameSeasonUnknown": "Sezon i panjohur",
+ "NameSeasonNumber": "Sezoni {0}",
+ "NameInstallFailed": "Instalimi i {0} dështoi",
+ "MusicVideos": "Video muzikore",
+ "Music": "Muzikë",
+ "Movies": "Filma",
+ "MixedContent": "Përmbajtje e përzier",
+ "MessageServerConfigurationUpdated": "Konfigurimet e serverit u përditësuan",
+ "MessageNamedServerConfigurationUpdatedWithValue": "Seksioni i konfigurimit të serverit {0} u përditësua",
+ "MessageApplicationUpdated": "Serveri Jellyfin u përditësua",
+ "Latest": "Të fundit",
+ "LabelRunningTimeValue": "Kohëzgjatja: {0}",
+ "LabelIpAddressValue": "Adresa IP: {0}",
+ "ItemRemovedWithName": "{0} u fshi nga libraria",
+ "ItemAddedWithName": "{0} u shtua tek libraria",
+ "HomeVideos": "Video personale",
+ "HeaderRecordingGroups": "Grupet e regjistrimit",
+ "HeaderNextUp": "Në vazhdim",
+ "HeaderLiveTV": "TV Live",
+ "HeaderFavoriteSongs": "Kënget e preferuara",
+ "HeaderFavoriteShows": "Serialet e preferuar",
+ "HeaderFavoriteEpisodes": "Episodet e preferuar",
+ "HeaderFavoriteArtists": "Artistët e preferuar",
+ "HeaderFavoriteAlbums": "Albumet e preferuar",
+ "HeaderContinueWatching": "Vazhdo të shikosh",
+ "HeaderCameraUploads": "Ngarkimet nga Kamera",
+ "HeaderAlbumArtists": "Artistët e albumeve",
+ "Genres": "Zhanre",
+ "Folders": "Dosje",
+ "Favorites": "Të preferuara",
+ "FailedLoginAttemptWithUserName": "Përpjekja për hyrje dështoi nga {0}",
+ "DeviceOnlineWithName": "{0} u lidh",
+ "DeviceOfflineWithName": "{0} u shkëput",
+ "Collections": "Koleksione",
+ "ChapterNameValue": "Kapituj",
+ "Channels": "Kanale",
+ "CameraImageUploadedFrom": "Një foto e re nga kamera u ngarkua nga {0}",
+ "Books": "Libra",
+ "AuthenticationSucceededWithUserName": "{0} u identifikua me sukses",
+ "Artists": "Artistë",
+ "Application": "Aplikacioni",
+ "AppDeviceValues": "Aplikacioni: {0}, Pajisja: {1}",
+ "Albums": "Albumet"
+}
diff --git a/Emby.Server.Implementations/Localization/Core/vi.json b/Emby.Server.Implementations/Localization/Core/vi.json
new file mode 100644
index 0000000000..bf4cfe75ca
--- /dev/null
+++ b/Emby.Server.Implementations/Localization/Core/vi.json
@@ -0,0 +1,117 @@
+{
+ "Collections": "Bộ Sưu Tập",
+ "Favorites": "Sở Thích",
+ "Folders": "Thư Mục",
+ "Genres": "Thể Loại",
+ "HeaderAlbumArtists": "Bộ Sưu Tập Nghệ sĩ",
+ "HeaderContinueWatching": "Tiếp Tục Xem Tiếp",
+ "HeaderLiveTV": "TV Trực Tiếp",
+ "Movies": "Phim",
+ "Photos": "Ảnh",
+ "Playlists": "Danh Sách Chơi",
+ "Shows": "Các Chương Trình",
+ "Songs": "Các Bài Hát",
+ "Sync": "Đồng Bộ",
+ "ValueSpecialEpisodeName": "Đặc Biệt - {0}",
+ "Albums": "Bộ Sưu Tập",
+ "Artists": "Các Nghệ Sĩ",
+ "TaskDownloadMissingSubtitlesDescription": "Tìm kiếm phụ đề bị thiếu trên Internet dựa trên cấu hình thông tin chi tiết.",
+ "TaskDownloadMissingSubtitles": "Tải xuống phụ đề bị thiếu",
+ "TaskRefreshChannelsDescription": "Làm mới thông tin kênh internet.",
+ "TaskRefreshChannels": "Làm Mới Kênh",
+ "TaskCleanTranscodeDescription": "Xóa các tệp chuyển mã cũ hơn một ngày.",
+ "TaskCleanTranscode": "Làm Sạch Thư Mục Chuyển Mã",
+ "TaskUpdatePluginsDescription": "Tải xuống và cài đặt các bản cập nhật cho các plugin được định cấu hình để cập nhật tự động.",
+ "TaskUpdatePlugins": "Cập Nhật Plugins",
+ "TaskRefreshPeopleDescription": "Cập nhật thông tin chi tiết cho diễn viên và đạo diễn trong thư viện phương tiện của bạn.",
+ "TaskRefreshPeople": "Làm mới Người dùng",
+ "TaskCleanLogsDescription": "Xóa tập tin nhật ký cũ hơn {0} ngày.",
+ "TaskCleanLogs": "Làm sạch nhật ký",
+ "TaskRefreshLibraryDescription": "Quét thư viện phương tiện của bạn để tìm các tệp mới và làm mới thông tin chi tiết.",
+ "TaskRefreshLibrary": "Quét Thư viện Phương tiện",
+ "TaskRefreshChapterImagesDescription": "Tạo hình thu nhỏ cho các video có chương.",
+ "TaskRefreshChapterImages": "Trích xuất hình ảnh chương",
+ "TaskCleanCacheDescription": "Xóa các tệp cache không còn cần thiết của hệ thống.",
+ "TaskCleanCache": "Làm Sạch Thư Mục Cache",
+ "TasksChannelsCategory": "Kênh Internet",
+ "TasksApplicationCategory": "Ứng Dụng",
+ "TasksLibraryCategory": "Thư Viện",
+ "TasksMaintenanceCategory": "Bảo Trì",
+ "VersionNumber": "Phiên Bản {0}",
+ "ValueHasBeenAddedToLibrary": "{0} đã được thêm vào thư viện của bạn",
+ "UserStoppedPlayingItemWithValues": "{0} đã phát xong {1} trên {2}",
+ "UserStartedPlayingItemWithValues": "{0} đang phát {1} trên {2}",
+ "UserPolicyUpdatedWithName": "Chính sách người dùng đã được cập nhật cho {0}",
+ "UserPasswordChangedWithName": "Mật khẩu đã được thay đổi cho người dùng {0}",
+ "UserOnlineFromDevice": "{0} trực tuyến từ {1}",
+ "UserOfflineFromDevice": "{0} đã ngắt kết nối từ {1}",
+ "UserLockedOutWithName": "User {0} đã bị khóa",
+ "UserDownloadingItemWithValues": "{0} đang tải xuống {1}",
+ "UserDeletedWithName": "Người Dùng {0} đã được xóa",
+ "UserCreatedWithName": "Người Dùng {0} đã được tạo",
+ "User": "Người Dùng",
+ "TvShows": "Chương Trình TV",
+ "System": "Hệ Thống",
+ "SubtitleDownloadFailureFromForItem": "Không thể tải xuống phụ đề từ {0} cho {1}",
+ "StartupEmbyServerIsLoading": "Jellyfin Server đang tải. Vui lòng thử lại trong thời gian ngắn.",
+ "ServerNameNeedsToBeRestarted": "{0} cần được khởi động lại",
+ "ScheduledTaskStartedWithName": "{0} đã bắt đầu",
+ "ScheduledTaskFailedWithName": "{0} đã thất bại",
+ "ProviderValue": "Provider: {0}",
+ "PluginUpdatedWithName": "{0} đã cập nhật",
+ "PluginUninstalledWithName": "{0} đã được gỡ bỏ",
+ "PluginInstalledWithName": "{0} đã được cài đặt",
+ "Plugin": "Plugin",
+ "NotificationOptionVideoPlaybackStopped": "Phát lại video đã dừng",
+ "NotificationOptionVideoPlayback": "Đã bắt đầu phát lại video",
+ "NotificationOptionUserLockedOut": "Người dùng bị khóa",
+ "NotificationOptionTaskFailed": "Lỗi tác vụ đã lên lịch",
+ "NotificationOptionServerRestartRequired": "Yêu cầu khởi động lại Server",
+ "NotificationOptionPluginUpdateInstalled": "Cập nhật Plugin đã được cài đặt",
+ "NotificationOptionPluginUninstalled": "Đã gỡ bỏ Plugin",
+ "NotificationOptionPluginInstalled": "Đã cài đặt Plugin",
+ "NotificationOptionPluginError": "Thất bại Plugin",
+ "NotificationOptionNewLibraryContent": "Nội dung mới được thêm vào",
+ "NotificationOptionInstallationFailed": "Cài đặt thất bại",
+ "NotificationOptionCameraImageUploaded": "Đã tải lên hình ảnh máy ảnh",
+ "NotificationOptionAudioPlaybackStopped": "Phát lại âm thanh đã dừng",
+ "NotificationOptionAudioPlayback": "Phát lại âm thanh đã bắt đầu",
+ "NotificationOptionApplicationUpdateInstalled": "Bản cập nhật ứng dụng đã được cài đặt",
+ "NotificationOptionApplicationUpdateAvailable": "Bản cập nhật ứng dụng hiện sẵn có",
+ "NewVersionIsAvailable": "Một phiên bản mới của Jellyfin Server sẵn có để tải.",
+ "NameSeasonUnknown": "Không Rõ Mùa",
+ "NameSeasonNumber": "Mùa {0}",
+ "NameInstallFailed": "{0} cài đặt thất bại",
+ "MusicVideos": "Video Nhạc",
+ "Music": "Nhạc",
+ "MixedContent": "Nội dung hỗn hợp",
+ "MessageServerConfigurationUpdated": "Cấu hình máy chủ đã được cập nhật",
+ "MessageNamedServerConfigurationUpdatedWithValue": "Phần cấu hình máy chủ {0} đã được cập nhật",
+ "MessageApplicationUpdatedTo": "Jellyfin Server đã được cập nhật lên {0}",
+ "MessageApplicationUpdated": "Jellyfin Server đã được cập nhật",
+ "Latest": "Gần Nhất",
+ "LabelRunningTimeValue": "Thời Gian Chạy: {0}",
+ "LabelIpAddressValue": "Địa Chỉ IP: {0}",
+ "ItemRemovedWithName": "{0} đã xóa khỏi thư viện",
+ "ItemAddedWithName": "{0} được thêm vào thư viện",
+ "Inherit": "Thừa hưởng",
+ "HomeVideos": "Video nhà",
+ "HeaderRecordingGroups": "Nhóm Ghi Video",
+ "HeaderNextUp": "Tiếp Theo",
+ "HeaderFavoriteSongs": "Bài Hát Yêu Thích",
+ "HeaderFavoriteShows": "Chương Trình Yêu Thích",
+ "HeaderFavoriteEpisodes": "Tập Phim Yêu Thích",
+ "HeaderFavoriteArtists": "Nghệ Sĩ Yêu Thích",
+ "HeaderFavoriteAlbums": "Album Ưa Thích",
+ "HeaderCameraUploads": "Máy Ảnh Tải Lên",
+ "FailedLoginAttemptWithUserName": "Nỗ lực đăng nhập thất bại từ {0}",
+ "DeviceOnlineWithName": "{0} đã kết nối",
+ "DeviceOfflineWithName": "{0} đã ngắt kết nối",
+ "ChapterNameValue": "Chương {0}",
+ "Channels": "Các Kênh",
+ "CameraImageUploadedFrom": "Một hình ảnh máy ảnh mới đã được tải lên từ {0}",
+ "Books": "Các Quyển Sách",
+ "AuthenticationSucceededWithUserName": "{0} xác thực thành công",
+ "Application": "Ứng Dụng",
+ "AppDeviceValues": "Ứng Dụng: {0}, Thiết Bị: {1}"
+}
diff --git a/Emby.Server.Implementations/Localization/Core/zh-TW.json b/Emby.Server.Implementations/Localization/Core/zh-TW.json
index a21cdad953..01108fe84d 100644
--- a/Emby.Server.Implementations/Localization/Core/zh-TW.json
+++ b/Emby.Server.Implementations/Localization/Core/zh-TW.json
@@ -1,6 +1,6 @@
{
"Albums": "專輯",
- "AppDeviceValues": "軟體: {0}, 裝置: {1}",
+ "AppDeviceValues": "軟體:{0},裝置:{1}",
"Application": "應用程式",
"Artists": "演出者",
"AuthenticationSucceededWithUserName": "{0} 成功授權",
@@ -11,7 +11,7 @@
"Collections": "合輯",
"DeviceOfflineWithName": "{0} 已經斷線",
"DeviceOnlineWithName": "{0} 已經連線",
- "FailedLoginAttemptWithUserName": "來自 {0} 的失敗登入嘗試",
+ "FailedLoginAttemptWithUserName": "來自使用者 {0} 的失敗登入",
"Favorites": "我的最愛",
"Folders": "資料夾",
"Genres": "風格",
@@ -28,8 +28,8 @@
"HomeVideos": "自製影片",
"ItemAddedWithName": "{0} 已新增至媒體庫",
"ItemRemovedWithName": "{0} 已從媒體庫移除",
- "LabelIpAddressValue": "IP 位置: {0}",
- "LabelRunningTimeValue": "運行時間: {0}",
+ "LabelIpAddressValue": "IP 位址:{0}",
+ "LabelRunningTimeValue": "運行時間:{0}",
"Latest": "最新",
"MessageApplicationUpdated": "Jellyfin Server 已經更新",
"MessageApplicationUpdatedTo": "Jellyfin Server 已經更新至 {0}",
@@ -42,18 +42,18 @@
"NameInstallFailed": "{0} 安裝失敗",
"NameSeasonNumber": "第 {0} 季",
"NameSeasonUnknown": "未知季數",
- "NewVersionIsAvailable": "新版本的Jellyfin Server 軟體已經推出可供下載。",
+ "NewVersionIsAvailable": "新版本的 Jellyfin Server 軟體已經可供下載。",
"NotificationOptionApplicationUpdateAvailable": "有可用的應用程式更新",
- "NotificationOptionApplicationUpdateInstalled": "應用程式已更新",
+ "NotificationOptionApplicationUpdateInstalled": "軟體更新已安裝",
"NotificationOptionAudioPlayback": "音樂開始播放",
"NotificationOptionAudioPlaybackStopped": "音樂停止播放",
"NotificationOptionCameraImageUploaded": "相機相片已上傳",
"NotificationOptionInstallationFailed": "安裝失敗",
"NotificationOptionNewLibraryContent": "已新增新內容",
- "NotificationOptionPluginError": "插件安裝錯誤",
- "NotificationOptionPluginInstalled": "插件已安裝",
- "NotificationOptionPluginUninstalled": "插件已移除",
- "NotificationOptionPluginUpdateInstalled": "插件已更新",
+ "NotificationOptionPluginError": "外掛安裝失敗",
+ "NotificationOptionPluginInstalled": "外掛已安裝",
+ "NotificationOptionPluginUninstalled": "外掛已移除",
+ "NotificationOptionPluginUpdateInstalled": "外掛已更新",
"NotificationOptionServerRestartRequired": "伺服器需要重新啟動",
"NotificationOptionTaskFailed": "排程任務失敗",
"NotificationOptionUserLockedOut": "使用者已鎖定",
@@ -61,14 +61,14 @@
"NotificationOptionVideoPlaybackStopped": "影片停止播放",
"Photos": "相片",
"Playlists": "播放清單",
- "Plugin": "插件",
+ "Plugin": "外掛",
"PluginInstalledWithName": "{0} 已安裝",
"PluginUninstalledWithName": "{0} 已移除",
"PluginUpdatedWithName": "{0} 已更新",
"ProviderValue": "提供商: {0}",
- "ScheduledTaskFailedWithName": "{0} 已失敗",
- "ScheduledTaskStartedWithName": "{0} 已開始",
- "ServerNameNeedsToBeRestarted": "{0} 需要重新啟動",
+ "ScheduledTaskFailedWithName": "排程任務 {0} 已失敗",
+ "ScheduledTaskStartedWithName": "排程任務 {0} 已開始",
+ "ServerNameNeedsToBeRestarted": "伺服器 {0} 需要重新啟動",
"Shows": "節目",
"Songs": "歌曲",
"StartupEmbyServerIsLoading": "Jellyfin Server正在啟動,請稍後再試一次。",
@@ -78,10 +78,10 @@
"User": "使用者",
"UserCreatedWithName": "使用者 {0} 已建立",
"UserDeletedWithName": "使用者 {0} 已移除",
- "UserDownloadingItemWithValues": "{0} 正在下載 {1}",
+ "UserDownloadingItemWithValues": "使用者 {0} 正在下載 {1}",
"UserLockedOutWithName": "使用者 {0} 已鎖定",
- "UserOfflineFromDevice": "{0} 已從 {1} 斷線",
- "UserOnlineFromDevice": "{0} 已連線,來自 {1}",
+ "UserOfflineFromDevice": "使用者 {0} 已從 {1} 斷線",
+ "UserOnlineFromDevice": "使用者 {0} 已從 {1} 連線",
"UserPasswordChangedWithName": "使用者 {0} 的密碼已變更",
"UserPolicyUpdatedWithName": "使用者條約已更新為 {0}",
"UserStartedPlayingItemWithValues": "{0}正在使用 {2} 播放 {1}",
@@ -95,23 +95,23 @@
"TaskDownloadMissingSubtitlesDescription": "在網路上透過中繼資料搜尋遺失的字幕。",
"TaskDownloadMissingSubtitles": "下載遺失的字幕",
"TaskRefreshChannels": "重新整理頻道",
- "TaskUpdatePlugins": "更新插件",
+ "TaskUpdatePlugins": "更新外掛",
"TaskRefreshPeople": "重新整理人員",
- "TaskCleanLogsDescription": "刪除超過{0}天的紀錄檔案。",
+ "TaskCleanLogsDescription": "刪除超過 {0} 天的舊紀錄檔。",
"TaskCleanLogs": "清空紀錄資料夾",
- "TaskRefreshLibraryDescription": "掃描媒體庫內新的檔案並重新整理描述資料。",
- "TaskRefreshLibrary": "掃描媒體庫",
+ "TaskRefreshLibraryDescription": "重新掃描媒體庫的新檔案並更新描述資料。",
+ "TaskRefreshLibrary": "重新掃描媒體庫",
"TaskRefreshChapterImages": "擷取章節圖片",
- "TaskCleanCacheDescription": "刪除系統長時間不需要的快取。",
+ "TaskCleanCacheDescription": "刪除系統已不需要的快取。",
"TaskCleanCache": "清除快取資料夾",
"TasksLibraryCategory": "媒體庫",
- "TaskRefreshChannelsDescription": "重新整理網絡頻道資料。",
+ "TaskRefreshChannelsDescription": "重新整理網路頻道資料。",
"TaskCleanTranscodeDescription": "刪除超過一天的轉碼檔案。",
"TaskCleanTranscode": "清除轉碼資料夾",
- "TaskUpdatePluginsDescription": "下載並安裝配置為自動更新的插件的更新。",
+ "TaskUpdatePluginsDescription": "為設置自動更新的外掛下載並安裝更新。",
"TaskRefreshPeopleDescription": "更新媒體庫中演員和導演的中繼資料。",
- "TaskRefreshChapterImagesDescription": "為有章節的視頻創建縮圖。",
- "TasksChannelsCategory": "網絡頻道",
+ "TaskRefreshChapterImagesDescription": "為有章節的影片建立縮圖。",
+ "TasksChannelsCategory": "網路頻道",
"TasksApplicationCategory": "應用程式",
"TasksMaintenanceCategory": "維修"
}
diff --git a/Emby.Server.Implementations/Security/AuthenticationRepository.cs b/Emby.Server.Implementations/Security/AuthenticationRepository.cs
index 4dfadc7032..29393ae07d 100644
--- a/Emby.Server.Implementations/Security/AuthenticationRepository.cs
+++ b/Emby.Server.Implementations/Security/AuthenticationRepository.cs
@@ -257,8 +257,7 @@ namespace Emby.Server.Implementations.Security
connection.RunInTransaction(
db =>
{
- var statements = PrepareAll(db, statementTexts)
- .ToList();
+ var statements = PrepareAll(db, statementTexts);
using (var statement = statements[0])
{
@@ -282,7 +281,7 @@ namespace Emby.Server.Implementations.Security
ReadTransactionMode);
}
- result.Items = list.ToArray();
+ result.Items = list;
return result;
}
diff --git a/Emby.Server.Implementations/Udp/UdpServer.cs b/Emby.Server.Implementations/Udp/UdpServer.cs
index 73fcbcec3a..b7a59cee2d 100644
--- a/Emby.Server.Implementations/Udp/UdpServer.cs
+++ b/Emby.Server.Implementations/Udp/UdpServer.cs
@@ -68,12 +68,6 @@ namespace Emby.Server.Implementations.Udp
{
_logger.LogError(ex, "Error sending response message");
}
-
- var parts = messageText.Split('|');
- if (parts.Length > 1)
- {
- _appHost.EnableLoopback(parts[1]);
- }
}
else
{
diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs
index ba0fdfc340..8e24bf55ce 100644
--- a/Emby.Server.Implementations/Updates/InstallationManager.cs
+++ b/Emby.Server.Implementations/Updates/InstallationManager.cs
@@ -185,7 +185,8 @@ namespace Emby.Server.Implementations.Updates
IEnumerable<PackageInfo> availablePackages,
string name = null,
Guid guid = default,
- Version minVersion = null)
+ Version minVersion = null,
+ Version specificVersion = null)
{
var package = FilterPackages(availablePackages, name, guid).FirstOrDefault();
@@ -199,7 +200,11 @@ namespace Emby.Server.Implementations.Updates
var availableVersions = package.versions
.Where(x => Version.Parse(x.targetAbi) <= appVer);
- if (minVersion != null)
+ if (specificVersion != null)
+ {
+ availableVersions = availableVersions.Where(x => new Version(x.version) == specificVersion);
+ }
+ else if (minVersion != null)
{
availableVersions = availableVersions.Where(x => new Version(x.version) >= minVersion);
}
@@ -229,8 +234,8 @@ namespace Emby.Server.Implementations.Updates
{
foreach (var plugin in _applicationHost.Plugins)
{
- var compatibleversions = GetCompatibleVersions(pluginCatalog, plugin.Name, plugin.Id, plugin.Version);
- var version = compatibleversions.FirstOrDefault(y => y.Version > plugin.Version);
+ var compatibleVersions = GetCompatibleVersions(pluginCatalog, plugin.Name, plugin.Id, minVersion: plugin.Version);
+ var version = compatibleVersions.FirstOrDefault(y => y.Version > plugin.Version);
if (version != null && CompletedInstallations.All(x => x.Guid != version.Guid))
{
yield return version;