aboutsummaryrefslogtreecommitdiff
path: root/Emby.Server.Implementations
diff options
context:
space:
mode:
Diffstat (limited to 'Emby.Server.Implementations')
-rw-r--r--Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs21
-rw-r--r--Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs3
-rw-r--r--Emby.Server.Implementations/ApplicationHost.cs47
-rw-r--r--Emby.Server.Implementations/Chapters/ChapterManager.cs21
-rw-r--r--Emby.Server.Implementations/Collections/CollectionManager.cs29
-rw-r--r--Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs36
-rw-r--r--Emby.Server.Implementations/Devices/DeviceId.cs13
-rw-r--r--Emby.Server.Implementations/Dto/DtoService.cs300
-rw-r--r--Emby.Server.Implementations/HttpServer/WebSocketConnection.cs23
-rw-r--r--Emby.Server.Implementations/HttpServer/WebSocketManager.cs9
-rw-r--r--Emby.Server.Implementations/IO/LibraryMonitor.cs9
-rw-r--r--Emby.Server.Implementations/IO/ManagedFileSystem.cs8
-rw-r--r--Emby.Server.Implementations/Images/CollectionFolderImageProvider.cs42
-rw-r--r--Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs273
-rw-r--r--Emby.Server.Implementations/Library/ExternalDataManager.cs40
-rw-r--r--Emby.Server.Implementations/Library/IgnorePatterns.cs14
-rw-r--r--Emby.Server.Implementations/Library/LibraryManager.cs613
-rw-r--r--Emby.Server.Implementations/Library/MediaSourceManager.cs171
-rw-r--r--Emby.Server.Implementations/Library/PathExtensions.cs12
-rw-r--r--Emby.Server.Implementations/Library/PathManager.cs62
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs8
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs25
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs12
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs26
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs34
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs10
-rw-r--r--Emby.Server.Implementations/Library/Search/SearchManager.cs458
-rw-r--r--Emby.Server.Implementations/Library/Search/SqlSearchProvider.cs230
-rw-r--r--Emby.Server.Implementations/Library/SearchEngine.cs200
-rw-r--r--Emby.Server.Implementations/Library/SimilarItems/AudioSimilarItemsProvider.cs55
-rw-r--r--Emby.Server.Implementations/Library/SimilarItems/LiveTvProgramSimilarItemsProvider.cs94
-rw-r--r--Emby.Server.Implementations/Library/SimilarItems/MovieSimilarItemsProvider.cs342
-rw-r--r--Emby.Server.Implementations/Library/SimilarItems/MusicAlbumSimilarItemsProvider.cs55
-rw-r--r--Emby.Server.Implementations/Library/SimilarItems/MusicArtistSimilarItemsProvider.cs55
-rw-r--r--Emby.Server.Implementations/Library/SimilarItems/SeriesSimilarItemsProvider.cs54
-rw-r--r--Emby.Server.Implementations/Library/SimilarItems/SimilarItemsManager.cs638
-rw-r--r--Emby.Server.Implementations/Library/SplashscreenPostScanTask.cs2
-rw-r--r--Emby.Server.Implementations/Library/UserDataManager.cs204
-rw-r--r--Emby.Server.Implementations/Library/UserViewManager.cs52
-rw-r--r--Emby.Server.Implementations/Library/Validators/ArtistsValidator.cs46
-rw-r--r--Emby.Server.Implementations/Library/Validators/CollectionPostScanTask.cs2
-rw-r--r--Emby.Server.Implementations/Library/Validators/GenresValidator.cs40
-rw-r--r--Emby.Server.Implementations/Library/Validators/MusicGenresValidator.cs17
-rw-r--r--Emby.Server.Implementations/Library/Validators/PeopleValidator.cs2
-rw-r--r--Emby.Server.Implementations/Library/Validators/StudiosValidator.cs40
-rw-r--r--Emby.Server.Implementations/Localization/Core/ab.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/af.json35
-rw-r--r--Emby.Server.Implementations/Localization/Core/ar.json189
-rw-r--r--Emby.Server.Implementations/Localization/Core/ar_SA.json (renamed from Emby.Server.Implementations/Localization/Core/enm.json)0
-rw-r--r--Emby.Server.Implementations/Localization/Core/as.json11
-rw-r--r--Emby.Server.Implementations/Localization/Core/az.json19
-rw-r--r--Emby.Server.Implementations/Localization/Core/be.json33
-rw-r--r--Emby.Server.Implementations/Localization/Core/bg-BG.json31
-rw-r--r--Emby.Server.Implementations/Localization/Core/bn.json31
-rw-r--r--Emby.Server.Implementations/Localization/Core/br.json63
-rw-r--r--Emby.Server.Implementations/Localization/Core/bs.json110
-rw-r--r--Emby.Server.Implementations/Localization/Core/ca.json39
-rw-r--r--Emby.Server.Implementations/Localization/Core/chr.json14
-rw-r--r--Emby.Server.Implementations/Localization/Core/cs.json35
-rw-r--r--Emby.Server.Implementations/Localization/Core/cy.json31
-rw-r--r--Emby.Server.Implementations/Localization/Core/da.json47
-rw-r--r--Emby.Server.Implementations/Localization/Core/de.json37
-rw-r--r--Emby.Server.Implementations/Localization/Core/el.json37
-rw-r--r--Emby.Server.Implementations/Localization/Core/en-GB.json35
-rw-r--r--Emby.Server.Implementations/Localization/Core/en-US.json33
-rw-r--r--Emby.Server.Implementations/Localization/Core/eo.json29
-rw-r--r--Emby.Server.Implementations/Localization/Core/es-AR.json35
-rw-r--r--Emby.Server.Implementations/Localization/Core/es-MX.json31
-rw-r--r--Emby.Server.Implementations/Localization/Core/es.json35
-rw-r--r--Emby.Server.Implementations/Localization/Core/es_419.json35
-rw-r--r--Emby.Server.Implementations/Localization/Core/es_DO.json31
-rw-r--r--Emby.Server.Implementations/Localization/Core/et.json39
-rw-r--r--Emby.Server.Implementations/Localization/Core/eu.json35
-rw-r--r--Emby.Server.Implementations/Localization/Core/fa.json31
-rw-r--r--Emby.Server.Implementations/Localization/Core/fi.json39
-rw-r--r--Emby.Server.Implementations/Localization/Core/fil.json29
-rw-r--r--Emby.Server.Implementations/Localization/Core/fo.json10
-rw-r--r--Emby.Server.Implementations/Localization/Core/fr-CA.json35
-rw-r--r--Emby.Server.Implementations/Localization/Core/fr.json35
-rw-r--r--Emby.Server.Implementations/Localization/Core/ga.json35
-rw-r--r--Emby.Server.Implementations/Localization/Core/gl.json34
-rw-r--r--Emby.Server.Implementations/Localization/Core/gsw.json29
-rw-r--r--Emby.Server.Implementations/Localization/Core/he.json35
-rw-r--r--Emby.Server.Implementations/Localization/Core/he_IL.json113
-rw-r--r--Emby.Server.Implementations/Localization/Core/hi.json35
-rw-r--r--Emby.Server.Implementations/Localization/Core/hr.json54
-rw-r--r--Emby.Server.Implementations/Localization/Core/ht.json21
-rw-r--r--Emby.Server.Implementations/Localization/Core/hu.json39
-rw-r--r--Emby.Server.Implementations/Localization/Core/hy.json13
-rw-r--r--Emby.Server.Implementations/Localization/Core/id.json35
-rw-r--r--Emby.Server.Implementations/Localization/Core/is.json36
-rw-r--r--Emby.Server.Implementations/Localization/Core/it.json35
-rw-r--r--Emby.Server.Implementations/Localization/Core/ja.json35
-rw-r--r--Emby.Server.Implementations/Localization/Core/jbo.json5
-rw-r--r--Emby.Server.Implementations/Localization/Core/ka.json109
-rw-r--r--Emby.Server.Implementations/Localization/Core/kab.json6
-rw-r--r--Emby.Server.Implementations/Localization/Core/kk.json29
-rw-r--r--Emby.Server.Implementations/Localization/Core/km.json33
-rw-r--r--Emby.Server.Implementations/Localization/Core/kn.json35
-rw-r--r--Emby.Server.Implementations/Localization/Core/ko.json35
-rw-r--r--Emby.Server.Implementations/Localization/Core/kw.json31
-rw-r--r--Emby.Server.Implementations/Localization/Core/lb.json31
-rw-r--r--Emby.Server.Implementations/Localization/Core/lt-LT.json31
-rw-r--r--Emby.Server.Implementations/Localization/Core/lv.json34
-rw-r--r--Emby.Server.Implementations/Localization/Core/lzh.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/mi.json12
-rw-r--r--Emby.Server.Implementations/Localization/Core/mk.json37
-rw-r--r--Emby.Server.Implementations/Localization/Core/ml.json38
-rw-r--r--Emby.Server.Implementations/Localization/Core/mn.json31
-rw-r--r--Emby.Server.Implementations/Localization/Core/mr.json31
-rw-r--r--Emby.Server.Implementations/Localization/Core/ms.json31
-rw-r--r--Emby.Server.Implementations/Localization/Core/mt.json31
-rw-r--r--Emby.Server.Implementations/Localization/Core/my.json31
-rw-r--r--Emby.Server.Implementations/Localization/Core/nb.json31
-rw-r--r--Emby.Server.Implementations/Localization/Core/ne.json31
-rw-r--r--Emby.Server.Implementations/Localization/Core/nl.json43
-rw-r--r--Emby.Server.Implementations/Localization/Core/nn.json29
-rw-r--r--Emby.Server.Implementations/Localization/Core/oc.json4
-rw-r--r--Emby.Server.Implementations/Localization/Core/or.json3
-rw-r--r--Emby.Server.Implementations/Localization/Core/pa.json31
-rw-r--r--Emby.Server.Implementations/Localization/Core/pl.json35
-rw-r--r--Emby.Server.Implementations/Localization/Core/pr.json29
-rw-r--r--Emby.Server.Implementations/Localization/Core/pt-BR.json35
-rw-r--r--Emby.Server.Implementations/Localization/Core/pt-PT.json35
-rw-r--r--Emby.Server.Implementations/Localization/Core/pt.json35
-rw-r--r--Emby.Server.Implementations/Localization/Core/ro.json35
-rw-r--r--Emby.Server.Implementations/Localization/Core/ru.json35
-rw-r--r--Emby.Server.Implementations/Localization/Core/si.json16
-rw-r--r--Emby.Server.Implementations/Localization/Core/sk.json35
-rw-r--r--Emby.Server.Implementations/Localization/Core/sl-SI.json35
-rw-r--r--Emby.Server.Implementations/Localization/Core/sn.json10
-rw-r--r--Emby.Server.Implementations/Localization/Core/sq.json35
-rw-r--r--Emby.Server.Implementations/Localization/Core/sr.json35
-rw-r--r--Emby.Server.Implementations/Localization/Core/sv.json37
-rw-r--r--Emby.Server.Implementations/Localization/Core/sw.json6
-rw-r--r--Emby.Server.Implementations/Localization/Core/ta.json31
-rw-r--r--Emby.Server.Implementations/Localization/Core/te.json20
-rw-r--r--Emby.Server.Implementations/Localization/Core/th.json31
-rw-r--r--Emby.Server.Implementations/Localization/Core/tr.json35
-rw-r--r--Emby.Server.Implementations/Localization/Core/ug.json29
-rw-r--r--Emby.Server.Implementations/Localization/Core/uk.json43
-rw-r--r--Emby.Server.Implementations/Localization/Core/ur.json6
-rw-r--r--Emby.Server.Implementations/Localization/Core/ur_PK.json29
-rw-r--r--Emby.Server.Implementations/Localization/Core/uz.json33
-rw-r--r--Emby.Server.Implementations/Localization/Core/vi.json35
-rw-r--r--Emby.Server.Implementations/Localization/Core/zh-CN.json35
-rw-r--r--Emby.Server.Implementations/Localization/Core/zh-HK.json183
-rw-r--r--Emby.Server.Implementations/Localization/Core/zh-TW.json31
-rw-r--r--Emby.Server.Implementations/Localization/Core/zu.json15
-rw-r--r--Emby.Server.Implementations/Localization/LocalizationManager.cs366
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/ca.json15
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/gr.json27
-rw-r--r--Emby.Server.Implementations/Localization/Ratings/se.json13
-rw-r--r--Emby.Server.Implementations/Localization/countries.json6
-rw-r--r--Emby.Server.Implementations/Playlists/PlaylistsFolder.cs5
-rw-r--r--Emby.Server.Implementations/Plugins/PluginManager.cs3
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/AudioNormalizationTask.cs19
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs3
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/CleanupCollectionAndPlaylistPathsTask.cs139
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/MediaSegmentExtractionTask.cs3
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/OptimizeDatabaseTask.cs16
-rw-r--r--Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs142
-rw-r--r--Emby.Server.Implementations/Serialization/MyXmlSerializer.cs12
-rw-r--r--Emby.Server.Implementations/Session/SessionManager.cs107
-rw-r--r--Emby.Server.Implementations/Session/SessionWebSocketListener.cs15
-rw-r--r--Emby.Server.Implementations/Sorting/OfficialRatingComparer.cs4
-rw-r--r--Emby.Server.Implementations/SyncPlay/Group.cs3
-rw-r--r--Emby.Server.Implementations/SystemManager.cs10
-rw-r--r--Emby.Server.Implementations/TV/TVSeriesManager.cs276
-rw-r--r--Emby.Server.Implementations/Updates/InstallationManager.cs97
170 files changed, 5445 insertions, 3912 deletions
diff --git a/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs b/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs
index de722332a4..56d977bbcb 100644
--- a/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs
+++ b/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs
@@ -90,6 +90,7 @@ namespace Emby.Server.Implementations.AppBase
CreateAndCheckMarker(ProgramDataPath, "data");
CreateAndCheckMarker(CachePath, "cache");
CreateAndCheckMarker(DataPath, "data");
+ CreateCacheDirTag(CachePath);
}
/// <inheritdoc />
@@ -100,6 +101,26 @@ namespace Emby.Server.Implementations.AppBase
CheckOrCreateMarker(path, $".jellyfin-{markerName}", recursive);
}
+ /// <summary>
+ /// Creates a CACHEDIR.TAG file in the specified directory per the Cache Directory Tagging specification.
+ /// This signals to backup tools (e.g. Restic, Borg) that the directory contains cached data
+ /// and can be excluded from backups.
+ /// </summary>
+ /// <param name="path">The cache directory path.</param>
+ internal static void CreateCacheDirTag(string path)
+ {
+ var tagPath = Path.Combine(path, "CACHEDIR.TAG");
+ if (!File.Exists(tagPath))
+ {
+ File.WriteAllText(
+ tagPath,
+ "Signature: 8a477f597d28d172789f06886806bc55\n"
+ + "# This file is a cache directory tag created by Jellyfin.\n"
+ + "# For information about cache directory tags, see:\n"
+ + "#\thttps://bford.info/cachedir/\n");
+ }
+ }
+
private IEnumerable<string> GetMarkers(string path, bool recursive = false)
{
return Directory.EnumerateFiles(path, ".jellyfin-*", recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly);
diff --git a/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs
index 81ef0e5f9a..aa19948e36 100644
--- a/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs
+++ b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs
@@ -132,7 +132,7 @@ namespace Emby.Server.Implementations.AppBase
}
else
{
- _configurationFactories = [.._configurationFactories, factory];
+ _configurationFactories = [.. _configurationFactories, factory];
}
_configurationStores = _configurationFactories
@@ -228,6 +228,7 @@ namespace Emby.Server.Implementations.AppBase
Logger.LogInformation("Setting cache path: {Path}", cachePath);
((BaseApplicationPaths)CommonApplicationPaths).CachePath = cachePath;
CommonApplicationPaths.CreateAndCheckMarker(((BaseApplicationPaths)CommonApplicationPaths).CachePath, "cache");
+ BaseApplicationPaths.CreateCacheDirTag(cachePath);
}
/// <summary>
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index cbb0f6c565..69e23bcb63 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -14,6 +14,7 @@ using System.Reflection;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using Emby.Naming.Common;
+using Emby.Naming.Video;
using Emby.Photos;
using Emby.Server.Implementations.Chapters;
using Emby.Server.Implementations.Collections;
@@ -25,6 +26,8 @@ using Emby.Server.Implementations.Dto;
using Emby.Server.Implementations.HttpServer.Security;
using Emby.Server.Implementations.IO;
using Emby.Server.Implementations.Library;
+using Emby.Server.Implementations.Library.Search;
+using Emby.Server.Implementations.Library.SimilarItems;
using Emby.Server.Implementations.Localization;
using Emby.Server.Implementations.Playlists;
using Emby.Server.Implementations.Plugins;
@@ -90,9 +93,16 @@ using MediaBrowser.Model.Net;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.System;
using MediaBrowser.Model.Tasks;
+using MediaBrowser.Providers.Books;
+using MediaBrowser.Providers.Books.ComicBookInfo;
+using MediaBrowser.Providers.Books.ComicInfo;
using MediaBrowser.Providers.Lyric;
using MediaBrowser.Providers.Manager;
+using MediaBrowser.Providers.Plugins.ListenBrainz;
+using MediaBrowser.Providers.Plugins.ListenBrainz.Api;
using MediaBrowser.Providers.Plugins.Tmdb;
+using MediaBrowser.Providers.Plugins.Tmdb.Movies;
+using MediaBrowser.Providers.Plugins.Tmdb.TV;
using MediaBrowser.Providers.Subtitles;
using MediaBrowser.XbmcMetadata.Providers;
using Microsoft.AspNetCore.Http;
@@ -166,8 +176,6 @@ namespace Emby.Server.Implementations
ConfigurationManager.Configuration,
ApplicationPaths.PluginsPath,
ApplicationVersion);
-
- _disposableParts.Add(_pluginManager);
}
/// <summary>
@@ -485,6 +493,19 @@ namespace Emby.Server.Implementations
serviceCollection.AddScoped<ISystemManager, SystemManager>();
serviceCollection.AddSingleton<TmdbClientManager>();
+ serviceCollection.AddSingleton<TmdbMovieSimilarProvider>();
+ serviceCollection.AddSingleton<TmdbSeriesSimilarProvider>();
+
+ serviceCollection.AddSingleton<ListenBrainzLabsClient>();
+ serviceCollection.AddSingleton<ListenBrainzSimilarArtistProvider>();
+
+ // register the generic local metadata provider for comic files
+ serviceCollection.AddSingleton<ComicProvider>();
+
+ // register the actual implementations of the local metadata provider for comic files
+ serviceCollection.AddSingleton<IComicProvider, ComicBookInfoProvider>();
+ serviceCollection.AddSingleton<IComicProvider, ExternalComicInfoProvider>();
+ serviceCollection.AddSingleton<IComicProvider, InternalComicInfoProvider>();
serviceCollection.AddSingleton(NetManager);
@@ -507,7 +528,13 @@ namespace Emby.Server.Implementations
serviceCollection.AddSingleton<IUserDataManager, UserDataManager>();
- serviceCollection.AddSingleton<IItemRepository, BaseItemRepository>();
+ serviceCollection.AddSingleton<BaseItemRepository>();
+ serviceCollection.AddSingleton<IItemRepository>(sp => sp.GetRequiredService<BaseItemRepository>());
+ serviceCollection.AddSingleton<IItemQueryHelpers>(sp => sp.GetRequiredService<BaseItemRepository>());
+ serviceCollection.AddSingleton<IItemPersistenceService, ItemPersistenceService>();
+ serviceCollection.AddSingleton<INextUpService, NextUpService>();
+ serviceCollection.AddSingleton<IItemCountService, ItemCountService>();
+ serviceCollection.AddSingleton<ILinkedChildrenService, LinkedChildrenService>();
serviceCollection.AddSingleton<IPeopleRepository, PeopleRepository>();
serviceCollection.AddSingleton<IChapterRepository, ChapterRepository>();
serviceCollection.AddSingleton<IMediaAttachmentRepository, MediaAttachmentRepository>();
@@ -524,14 +551,20 @@ namespace Emby.Server.Implementations
serviceCollection.AddTransient(provider => new Lazy<ILibraryMonitor>(provider.GetRequiredService<ILibraryMonitor>));
serviceCollection.AddTransient(provider => new Lazy<IProviderManager>(provider.GetRequiredService<IProviderManager>));
serviceCollection.AddTransient(provider => new Lazy<IUserViewManager>(provider.GetRequiredService<IUserViewManager>));
+ serviceCollection.AddTransient(provider => new Lazy<IExternalDataManager>(provider.GetRequiredService<IExternalDataManager>));
serviceCollection.AddSingleton<ILibraryManager, LibraryManager>();
serviceCollection.AddSingleton<NamingOptions>();
+ serviceCollection.AddSingleton<VideoListResolver>();
serviceCollection.AddSingleton<IMusicManager, MusicManager>();
serviceCollection.AddSingleton<ILibraryMonitor, LibraryMonitor>();
+ serviceCollection.AddSingleton<DotIgnoreIgnoreRule>();
- serviceCollection.AddSingleton<ISearchEngine, SearchEngine>();
+ serviceCollection.AddSingleton<ISimilarItemsManager, SimilarItemsManager>();
+
+ serviceCollection.AddSingleton<ISearchManager, SearchManager>();
+ serviceCollection.AddSingleton<ISearchProvider, SqlSearchProvider>();
serviceCollection.AddSingleton<IWebSocketManager, WebSocketManager>();
@@ -641,6 +674,7 @@ namespace Emby.Server.Implementations
BaseItem.ConfigurationManager = ConfigurationManager;
BaseItem.FileSystem = Resolve<IFileSystem>();
BaseItem.ItemRepository = Resolve<IItemRepository>();
+ BaseItem.ItemCountService = Resolve<IItemCountService>();
BaseItem.LibraryManager = Resolve<ILibraryManager>();
BaseItem.LocalizationManager = Resolve<ILocalizationManager>();
BaseItem.Logger = Resolve<ILogger<BaseItem>>();
@@ -687,6 +721,9 @@ namespace Emby.Server.Implementations
GetExports<IExternalUrlProvider>());
Resolve<IMediaSourceManager>().AddParts(GetExports<IMediaSourceProvider>());
+
+ Resolve<ISimilarItemsManager>().AddParts(GetExports<ISimilarItemsProvider>());
+ Resolve<ISearchManager>().AddParts(GetExports<ISearchProvider>());
}
/// <summary>
@@ -1006,6 +1043,8 @@ namespace Emby.Server.Implementations
}
_disposableParts.Clear();
+
+ _pluginManager?.Dispose();
}
_disposed = true;
diff --git a/Emby.Server.Implementations/Chapters/ChapterManager.cs b/Emby.Server.Implementations/Chapters/ChapterManager.cs
index d09ed30ae3..69cbe533c6 100644
--- a/Emby.Server.Implementations/Chapters/ChapterManager.cs
+++ b/Emby.Server.Implementations/Chapters/ChapterManager.cs
@@ -7,6 +7,7 @@ using System.Threading.Tasks;
using Jellyfin.Extensions;
using MediaBrowser.Controller.Chapters;
using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding;
@@ -128,7 +129,7 @@ public class ChapterManager : IChapterManager
var averageChapterDuration = GetAverageDurationBetweenChapters(chapters);
var threshold = TimeSpan.FromSeconds(1).Ticks;
- if (averageChapterDuration < threshold)
+ if (chapters.Count >= 2 && averageChapterDuration < threshold)
{
_logger.LogInformation("Skipping chapter image extraction for {Video} as the average chapter duration {AverageDuration} was lower than the minimum threshold {Threshold}", video.Name, averageChapterDuration, threshold);
extractImages = false;
@@ -232,11 +233,21 @@ public class ChapterManager : IChapterManager
}
/// <inheritdoc />
- public void SaveChapters(Video video, IReadOnlyList<ChapterInfo> chapters)
+ public bool Supports(BaseItem item)
+ => item is Video or Audio;
+
+ /// <inheritdoc />
+ public void SaveChapters(BaseItem item, IReadOnlyList<ChapterInfo> chapters)
{
- // Remove any chapters that are outside of the runtime of the video
- var validChapters = chapters.Where(c => c.StartPositionTicks < video.RunTimeTicks).ToList();
- _chapterRepository.SaveChapters(video.Id, validChapters);
+ if (!Supports(item))
+ {
+ _logger.LogWarning("Attempted to save chapters for unsupported item type {Type}: {Name} ({Id})", item.GetType().Name, item.Name, item.Id);
+ return;
+ }
+
+ // Remove any chapters that are outside of the runtime of the item
+ var validChapters = chapters.Where(c => c.StartPositionTicks < item.RunTimeTicks).ToList();
+ _chapterRepository.SaveChapters(item.Id, validChapters);
}
/// <inheritdoc />
diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs
index a320a774c6..295efd456c 100644
--- a/Emby.Server.Implementations/Collections/CollectionManager.cs
+++ b/Emby.Server.Implementations/Collections/CollectionManager.cs
@@ -4,12 +4,15 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Enums;
using Jellyfin.Database.Implementations.Entities;
+using Jellyfin.Extensions;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Collections;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
@@ -29,6 +32,7 @@ namespace Emby.Server.Implementations.Collections
private readonly ILibraryMonitor _iLibraryMonitor;
private readonly ILogger<CollectionManager> _logger;
private readonly IProviderManager _providerManager;
+ private readonly ILinkedChildrenService _linkedChildrenService;
private readonly ILocalizationManager _localizationManager;
private readonly IApplicationPaths _appPaths;
@@ -42,6 +46,7 @@ namespace Emby.Server.Implementations.Collections
/// <param name="iLibraryMonitor">The library monitor.</param>
/// <param name="loggerFactory">The logger factory.</param>
/// <param name="providerManager">The provider manager.</param>
+ /// <param name="linkedChildrenService">The linked children service.</param>
public CollectionManager(
ILibraryManager libraryManager,
IApplicationPaths appPaths,
@@ -49,13 +54,15 @@ namespace Emby.Server.Implementations.Collections
IFileSystem fileSystem,
ILibraryMonitor iLibraryMonitor,
ILoggerFactory loggerFactory,
- IProviderManager providerManager)
+ IProviderManager providerManager,
+ ILinkedChildrenService linkedChildrenService)
{
_libraryManager = libraryManager;
_fileSystem = fileSystem;
_iLibraryMonitor = iLibraryMonitor;
_logger = loggerFactory.CreateLogger<CollectionManager>();
_providerManager = providerManager;
+ _linkedChildrenService = linkedChildrenService;
_localizationManager = localizationManager;
_appPaths = appPaths;
}
@@ -120,6 +127,22 @@ namespace Emby.Server.Implementations.Collections
return EnsureLibraryFolder(GetCollectionsFolderPath(), createIfNeeded);
}
+ /// <inheritdoc />
+ public IEnumerable<BoxSet> GetCollectionsContainingItem(User user, Guid itemId)
+ {
+ ArgumentNullException.ThrowIfNull(user);
+
+ if (itemId.IsEmpty())
+ {
+ return Enumerable.Empty<BoxSet>();
+ }
+
+ return _linkedChildrenService
+ .GetManualLinkedParentIds(itemId, BaseItemKind.BoxSet)
+ .Select(parentId => _libraryManager.GetItemById<BoxSet>(parentId, user))
+ .OfType<BoxSet>();
+ }
+
private IEnumerable<BoxSet> GetCollections(User user)
{
var folder = GetCollectionsFolder(false).GetAwaiter().GetResult();
@@ -272,7 +295,7 @@ namespace Emby.Server.Implementations.Collections
{
var childItem = _libraryManager.GetItemById(guidId);
- var child = collection.LinkedChildren.FirstOrDefault(i => (i.ItemId.HasValue && i.ItemId.Value.Equals(guidId)) || (childItem is not null && string.Equals(childItem.Path, i.Path, StringComparison.OrdinalIgnoreCase)));
+ var child = collection.LinkedChildren.FirstOrDefault(i => i.ItemId.HasValue && i.ItemId.Value.Equals(guidId));
if (child is null)
{
@@ -342,7 +365,7 @@ namespace Emby.Server.Implementations.Collections
// this is kind of a performance hack because only Video has alternate versions that should be in a box set?
if (item is Video video)
{
- foreach (var childId in video.GetLocalAlternateVersionIds())
+ foreach (var childId in _libraryManager.GetLocalAlternateVersionIds(video))
{
if (!results.ContainsKey(childId))
{
diff --git a/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs b/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs
index 676bb7f816..17355960c3 100644
--- a/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs
+++ b/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs
@@ -5,10 +5,12 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Enums;
using Jellyfin.Database.Implementations;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Playlists;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
@@ -35,7 +37,11 @@ public class CleanDatabaseScheduledTask : ILibraryPostScanTask
public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{
- await CleanDeadItems(cancellationToken, progress).ConfigureAwait(false);
+ var deadItemsProgress = new Progress<double>(val => progress.Report(val * 0.8));
+ await CleanDeadItems(cancellationToken, deadItemsProgress).ConfigureAwait(false);
+
+ var playlistProgress = new Progress<double>(val => progress.Report(80 + (val * 0.2)));
+ await CleanOrphanedFilePlaylistsAsync(cancellationToken, playlistProgress).ConfigureAwait(false);
}
private async Task CleanDeadItems(CancellationToken cancellationToken, IProgress<double> progress)
@@ -116,4 +122,32 @@ public class CleanDatabaseScheduledTask : ILibraryPostScanTask
progress.Report(100);
}
+
+ private async Task CleanOrphanedFilePlaylistsAsync(CancellationToken cancellationToken, IProgress<double> progress)
+ {
+ var playlists = _libraryManager.GetItemList(new InternalItemsQuery
+ {
+ IncludeItemTypes = [BaseItemKind.Playlist],
+ Recursive = true
+ }).OfType<Playlist>().ToList();
+
+ var numComplete = 0;
+ var numItems = Math.Max(playlists.Count, 1);
+
+ foreach (var playlist in playlists)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ if (playlist.IsFile && !File.Exists(playlist.Path))
+ {
+ _logger.LogInformation("Removing file-based playlist {Name} because source file {Path} no longer exists", playlist.Name, playlist.Path);
+ _libraryManager.DeleteItem(playlist, new DeleteOptions { DeleteFileLocation = false });
+ }
+
+ numComplete++;
+ progress.Report((double)numComplete / numItems * 100);
+ }
+
+ progress.Report(100);
+ }
}
diff --git a/Emby.Server.Implementations/Devices/DeviceId.cs b/Emby.Server.Implementations/Devices/DeviceId.cs
index 0b3c3bbd4f..4929568935 100644
--- a/Emby.Server.Implementations/Devices/DeviceId.cs
+++ b/Emby.Server.Implementations/Devices/DeviceId.cs
@@ -1,5 +1,3 @@
-#pragma warning disable CS1591
-
using System;
using System.Globalization;
using System.IO;
@@ -10,6 +8,9 @@ using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Devices
{
+ /// <summary>
+ /// Provides the persistent unique identifier of this server installation.
+ /// </summary>
public class DeviceId
{
private readonly IApplicationPaths _appPaths;
@@ -18,12 +19,20 @@ namespace Emby.Server.Implementations.Devices
private string? _id;
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DeviceId"/> class.
+ /// </summary>
+ /// <param name="appPaths">Instance of the <see cref="IApplicationPaths"/> interface.</param>
+ /// <param name="logger">Instance of the <see cref="ILogger{DeviceId}"/> interface.</param>
public DeviceId(IApplicationPaths appPaths, ILogger<DeviceId> logger)
{
_appPaths = appPaths;
_logger = logger;
}
+ /// <summary>
+ /// Gets the device id, loading it from disk or generating and persisting a new one if none exists.
+ /// </summary>
public string Value => _id ??= GetDeviceId();
private string CachePath => Path.Combine(_appPaths.DataPath, "device.txt");
diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs
index b392340f71..8cbf42585d 100644
--- a/Emby.Server.Implementations/Dto/DtoService.cs
+++ b/Emby.Server.Implementations/Dto/DtoService.cs
@@ -71,6 +71,8 @@ namespace Emby.Server.Implementations.Dto
{
BaseItemKind.Person, [
BaseItemKind.Audio,
+ BaseItemKind.AudioBook,
+ BaseItemKind.Book,
BaseItemKind.Episode,
BaseItemKind.Movie,
BaseItemKind.LiveTvProgram,
@@ -153,17 +155,107 @@ namespace Emby.Server.Implementations.Dto
private ILiveTvManager LivetvManager => _livetvManagerFactory.Value;
/// <inheritdoc />
- public IReadOnlyList<BaseItemDto> GetBaseItemDtos(IReadOnlyList<BaseItem> items, DtoOptions options, User? user = null, BaseItem? owner = null)
+ public IReadOnlyList<BaseItemDto> GetBaseItemDtos(
+ IReadOnlyList<BaseItem> items,
+ DtoOptions options,
+ User? user = null,
+ BaseItem? owner = null,
+ bool skipVisibilityCheck = false)
{
- var accessibleItems = user is null ? items : items.Where(x => x.IsVisible(user)).ToList();
+ var accessibleItems = skipVisibilityCheck || user is null ? items : items.Where(x => x.IsVisible(user)).ToList();
var returnItems = new BaseItemDto[accessibleItems.Count];
List<(BaseItem, BaseItemDto)>? programTuples = null;
List<(BaseItemDto, LiveTvChannel)>? channelTuples = null;
+ // Batch-fetch user data for all items
+ Dictionary<Guid, UserItemData>? userDataBatch = null;
+ IReadOnlyDictionary<Guid, VersionResumeData>? resumeDataBatch = null;
+ if (user is not null && options.EnableUserData)
+ {
+ userDataBatch = _userDataRepository.GetUserDataBatch(accessibleItems, user);
+
+ // For items with alternate versions, the most recently played version drives resume.
+ resumeDataBatch = _userDataRepository.GetResumeUserDataBatch(accessibleItems, user);
+ }
+
+ // Pre-compute collection folders once to avoid N+1 queries in CanDelete
+ List<Folder>? allCollectionFolders = null;
+ if (user is not null && options.ContainsField(ItemFields.CanDelete))
+ {
+ allCollectionFolders = _libraryManager.GetUserRootFolder().Children.OfType<Folder>().ToList();
+ }
+
+ // Batch-fetch child counts for all folders to avoid N+1 queries
+ Dictionary<Guid, int>? childCountBatch = null;
+ if (options.ContainsField(ItemFields.ChildCount))
+ {
+ var folderIds = accessibleItems.OfType<Folder>().Select(f => f.Id).ToList();
+ if (folderIds.Count > 0)
+ {
+ childCountBatch = _libraryManager.GetChildCountBatch(folderIds, user?.Id);
+ }
+ }
+
+ // Batch-fetch played/total counts for all folders to avoid N+1 queries
+ Dictionary<Guid, (int Played, int Total)>? playedCountBatch = null;
+ if (user is not null && options.EnableUserData)
+ {
+ var folderIds = accessibleItems.OfType<Folder>()
+ .Where(f => f.SupportsUserDataFromChildren && (f.SupportsPlayedStatus || options.ContainsField(ItemFields.RecursiveItemCount)))
+ .Select(f => f.Id).ToList();
+ if (folderIds.Count > 0)
+ {
+ playedCountBatch = _libraryManager.GetPlayedAndTotalCountBatch(folderIds, user);
+ }
+ }
+
+ // Batch-fetch MusicArtist lookups across all items to avoid N+1 queries.
+ IReadOnlyDictionary<string, MusicArtist[]>? artistsBatch = null;
+ var artistNames = new HashSet<string>(StringComparer.Ordinal);
+ foreach (var item in accessibleItems)
+ {
+ if (item is IHasArtist hasArtist)
+ {
+ foreach (var name in hasArtist.Artists)
+ {
+ if (!string.IsNullOrWhiteSpace(name))
+ {
+ artistNames.Add(name);
+ }
+ }
+ }
+
+ if (item is IHasAlbumArtist hasAlbumArtist)
+ {
+ foreach (var name in hasAlbumArtist.AlbumArtists)
+ {
+ if (!string.IsNullOrWhiteSpace(name))
+ {
+ artistNames.Add(name);
+ }
+ }
+ }
+ }
+
+ if (artistNames.Count > 0)
+ {
+ artistsBatch = _libraryManager.GetArtists(artistNames.ToArray());
+ }
+
for (int index = 0; index < accessibleItems.Count; index++)
{
var item = accessibleItems[index];
- var dto = GetBaseItemDtoInternal(item, options, user, owner);
+ var dto = GetBaseItemDtoInternal(
+ item,
+ options,
+ user,
+ owner,
+ userDataBatch?.GetValueOrDefault(item.Id),
+ allCollectionFolders,
+ childCountBatch,
+ playedCountBatch,
+ artistsBatch,
+ resumeDataBatch?.GetValueOrDefault(item.Id));
if (item is LiveTvChannel tvChannel)
{
@@ -197,7 +289,7 @@ namespace Emby.Server.Implementations.Dto
public BaseItemDto GetBaseItemDto(BaseItem item, DtoOptions options, User? user = null, BaseItem? owner = null)
{
- var dto = GetBaseItemDtoInternal(item, options, user, owner);
+ var dto = GetBaseItemDtoInternal(item, options, user, owner, null);
if (item is LiveTvChannel tvChannel)
{
LivetvManager.AddChannelInfo(new[] { (dto, tvChannel) }, options, user);
@@ -215,7 +307,17 @@ namespace Emby.Server.Implementations.Dto
return dto;
}
- private BaseItemDto GetBaseItemDtoInternal(BaseItem item, DtoOptions options, User? user = null, BaseItem? owner = null)
+ private BaseItemDto GetBaseItemDtoInternal(
+ BaseItem item,
+ DtoOptions options,
+ User? user = null,
+ BaseItem? owner = null,
+ UserItemData? userData = null,
+ List<Folder>? allCollectionFolders = null,
+ Dictionary<Guid, int>? childCountBatch = null,
+ Dictionary<Guid, (int Played, int Total)>? playedCountBatch = null,
+ IReadOnlyDictionary<string, MusicArtist[]>? artistsBatch = null,
+ VersionResumeData? resumeData = null)
{
var dto = new BaseItemDto
{
@@ -252,7 +354,15 @@ namespace Emby.Server.Implementations.Dto
if (user is not null)
{
- AttachUserSpecificInfo(dto, item, user, options);
+ AttachUserSpecificInfo(
+ dto,
+ item,
+ user,
+ options,
+ userData,
+ childCountBatch,
+ playedCountBatch,
+ resumeData);
}
if (item is IHasMediaSources
@@ -268,13 +378,15 @@ namespace Emby.Server.Implementations.Dto
AttachStudios(dto, item);
}
- AttachBasicFields(dto, item, owner, options);
+ AttachBasicFields(dto, item, owner, options, artistsBatch, user);
if (options.ContainsField(ItemFields.CanDelete))
{
dto.CanDelete = user is null
? item.CanDelete()
- : item.CanDelete(user);
+ : allCollectionFolders is not null
+ ? item.CanDelete(user, allCollectionFolders)
+ : item.CanDelete(user);
}
if (options.ContainsField(ItemFields.CanDownload))
@@ -378,37 +490,7 @@ namespace Emby.Server.Implementations.Dto
return;
}
- var query = new InternalItemsQuery(user)
- {
- Recursive = true,
- DtoOptions = new DtoOptions(false) { EnableImages = false },
- IncludeItemTypes = relatedItemKinds
- };
-
- switch (dto.Type)
- {
- case BaseItemKind.Genre:
- case BaseItemKind.MusicGenre:
- query.GenreIds = [dto.Id];
- break;
- case BaseItemKind.MusicArtist:
- query.ArtistIds = [dto.Id];
- break;
- case BaseItemKind.Person:
- query.PersonIds = [dto.Id];
- break;
- case BaseItemKind.Studio:
- query.StudioIds = [dto.Id];
- break;
- case BaseItemKind.Year
- when int.TryParse(dto.Name, NumberStyles.Integer, CultureInfo.InvariantCulture, out var year):
- query.Years = [year];
- break;
- default:
- return;
- }
-
- var counts = _libraryManager.GetItemCounts(query);
+ var counts = _libraryManager.GetItemCountsForNameItem(dto.Type, dto.Id, relatedItemKinds, user);
dto.AlbumCount = counts.AlbumCount;
dto.ArtistCount = counts.ArtistCount;
@@ -458,7 +540,15 @@ namespace Emby.Server.Implementations.Dto
/// <summary>
/// Attaches the user specific info.
/// </summary>
- private void AttachUserSpecificInfo(BaseItemDto dto, BaseItem item, User user, DtoOptions options)
+ private void AttachUserSpecificInfo(
+ BaseItemDto dto,
+ BaseItem item,
+ User user,
+ DtoOptions options,
+ UserItemData? userData = null,
+ Dictionary<Guid, int>? childCountBatch = null,
+ Dictionary<Guid, (int Played, int Total)>? playedCountBatch = null,
+ VersionResumeData? resumeData = null)
{
if (item.IsFolder)
{
@@ -466,7 +556,19 @@ namespace Emby.Server.Implementations.Dto
if (options.EnableUserData)
{
- dto.UserData = _userDataRepository.GetUserDataDto(item, dto, user, options);
+ if (userData is not null)
+ {
+ // Use pre-fetched user data
+ dto.UserData = GetUserItemDataDto(userData, item.Id);
+ (int Played, int Total)? precomputed = playedCountBatch is not null
+ && playedCountBatch.TryGetValue(item.Id, out var counts) ? counts : null;
+ item.FillUserDataDtoValues(dto.UserData, userData, dto, user, options, precomputed);
+ }
+ else
+ {
+ // Fall back to individual fetch
+ dto.UserData = _userDataRepository.GetUserDataDto(item, dto, user, options);
+ }
}
if (!dto.ChildCount.HasValue && item.SourceType == SourceType.Library)
@@ -485,7 +587,7 @@ namespace Emby.Server.Implementations.Dto
if (options.ContainsField(ItemFields.ChildCount))
{
- dto.ChildCount ??= GetChildCount(folder, user);
+ dto.ChildCount ??= GetChildCount(folder, user, childCountBatch);
}
}
@@ -503,7 +605,20 @@ namespace Emby.Server.Implementations.Dto
{
if (options.EnableUserData)
{
- dto.UserData = _userDataRepository.GetUserDataDto(item, user);
+ if (userData is not null)
+ {
+ // Use pre-fetched user data
+ dto.UserData = GetUserItemDataDto(userData, item.Id);
+ item.FillUserDataDtoValues(dto.UserData, userData, dto, user, options);
+
+ // For items with alternate versions, the most recently played version drives resume.
+ resumeData?.ApplyTo(dto.UserData);
+ }
+ else
+ {
+ // Fall back to individual fetch
+ dto.UserData = _userDataRepository.GetUserDataDto(item, user);
+ }
}
}
@@ -513,7 +628,25 @@ namespace Emby.Server.Implementations.Dto
}
}
- private static int GetChildCount(Folder folder, User user)
+ private static UserItemDataDto GetUserItemDataDto(UserItemData data, Guid itemId)
+ {
+ ArgumentNullException.ThrowIfNull(data);
+
+ return new UserItemDataDto
+ {
+ IsFavorite = data.IsFavorite,
+ Likes = data.Likes,
+ PlaybackPositionTicks = data.PlaybackPositionTicks,
+ PlayCount = data.PlayCount,
+ Rating = data.Rating,
+ Played = data.Played,
+ LastPlayedDate = data.LastPlayedDate,
+ ItemId = itemId,
+ Key = data.Key
+ };
+ }
+
+ private static int GetChildCount(Folder folder, User user, Dictionary<Guid, int>? childCountBatch)
{
// Right now this is too slow to calculate for top level folders on a per-user basis
// Just return something so that apps that are expecting a value won't think the folders are empty
@@ -522,6 +655,13 @@ namespace Emby.Server.Implementations.Dto
return Random.Shared.Next(1, 10);
}
+ // Use pre-fetched batch data if available
+ if (childCountBatch is not null && childCountBatch.TryGetValue(folder.Id, out var count))
+ {
+ return count;
+ }
+
+ // Fall back to individual query for special cases (Series, Season, etc.)
return folder.GetChildCount(user);
}
@@ -815,7 +955,9 @@ namespace Emby.Server.Implementations.Dto
/// <param name="item">The item.</param>
/// <param name="owner">The owner.</param>
/// <param name="options">The options.</param>
- private void AttachBasicFields(BaseItemDto dto, BaseItem item, BaseItem? owner, DtoOptions options)
+ /// <param name="artistsBatch">Optional pre-fetched artist lookup shared across a batch of items.</param>
+ /// <param name="user">The user, for per-user values such as the accessible media source count.</param>
+ private void AttachBasicFields(BaseItemDto dto, BaseItem item, BaseItem? owner, DtoOptions options, IReadOnlyDictionary<string, MusicArtist[]>? artistsBatch = null, User? user = null)
{
if (options.ContainsField(ItemFields.DateCreated))
{
@@ -939,6 +1081,8 @@ namespace Emby.Server.Implementations.Dto
dto.OriginalTitle = item.OriginalTitle;
}
+ dto.OriginalLanguage = item.OriginalLanguage;
+
if (options.ContainsField(ItemFields.ParentId))
{
dto.ParentId = item.DisplayParentId;
@@ -1019,6 +1163,15 @@ namespace Emby.Server.Implementations.Dto
{
dto.AlbumId = albumParent.Id;
dto.AlbumPrimaryImageTag = GetTagAndFillBlurhash(dto, albumParent, ImageType.Primary);
+ if (albumParent.LUFS.HasValue)
+ {
+ // -18 LUFS reference, same as ReplayGain 2.0, compatible with ReplayGain 1.0
+ dto.AlbumNormalizationGain = -18f - albumParent.LUFS;
+ }
+ else if (albumParent.NormalizationGain.HasValue)
+ {
+ dto.AlbumNormalizationGain = albumParent.NormalizationGain;
+ }
}
// if (options.ContainsField(ItemFields.MediaSourceCount))
@@ -1051,7 +1204,8 @@ namespace Emby.Server.Implementations.Dto
// Include artists that are not in the database yet, e.g., just added via metadata editor
// var foundArtists = artistItems.Items.Select(i => i.Item1.Name).ToList();
- var artistsLookup = _libraryManager.GetArtists([.. hasArtist.Artists.Where(e => !string.IsNullOrWhiteSpace(e))]);
+ var artistsLookup = artistsBatch
+ ?? _libraryManager.GetArtists([.. hasArtist.Artists.Where(e => !string.IsNullOrWhiteSpace(e))]);
dto.ArtistItems = hasArtist.Artists
.Where(name => !string.IsNullOrWhiteSpace(name))
@@ -1085,7 +1239,8 @@ namespace Emby.Server.Implementations.Dto
// })
// .ToList();
- var albumArtistsLookup = _libraryManager.GetArtists([.. hasAlbumArtist.AlbumArtists.Where(e => !string.IsNullOrWhiteSpace(e))]);
+ var albumArtistsLookup = artistsBatch
+ ?? _libraryManager.GetArtists([.. hasAlbumArtist.AlbumArtists.Where(e => !string.IsNullOrWhiteSpace(e))]);
dto.AlbumArtists = hasAlbumArtist.AlbumArtists
.Where(name => !string.IsNullOrWhiteSpace(name))
@@ -1116,18 +1271,18 @@ namespace Emby.Server.Implementations.Dto
if (options.ContainsField(ItemFields.MediaSourceCount))
{
- var mediaSourceCount = video.MediaSourceCount;
+ // Match the per-user filtering of the media sources: versions the user cannot
+ // access are not selectable, so they must not count towards the badge either.
+ var mediaSourceCount = user is null
+ || (!video.PrimaryVersionId.HasValue && video.LinkedAlternateVersions.Length == 0 && !video.HasLocalAlternateVersions)
+ ? video.MediaSourceCount
+ : video.GetAllVersions().Count(v => v.Id.Equals(video.Id) || v.IsVisibleStandalone(user));
if (mediaSourceCount != 1)
{
dto.MediaSourceCount = mediaSourceCount;
}
}
- if (options.ContainsField(ItemFields.Chapters))
- {
- dto.Chapters = _chapterManager.GetChapters(item.Id).ToList();
- }
-
if (options.ContainsField(ItemFields.Trickplay))
{
var trickplay = _trickplayManager.GetTrickplayManifest(item).GetAwaiter().GetResult();
@@ -1141,6 +1296,11 @@ namespace Emby.Server.Implementations.Dto
dto.ExtraType = video.ExtraType;
}
+ if (options.ContainsField(ItemFields.Chapters))
+ {
+ dto.Chapters = _chapterManager.GetChapters(item.Id).ToList();
+ }
+
if (options.ContainsField(ItemFields.MediaStreams))
{
// Add VideoInfo
@@ -1225,6 +1385,25 @@ namespace Emby.Server.Implementations.Dto
}
}
+ if (options.GetImageLimit(ImageType.Primary) > 0)
+ {
+ var episodeSeason = episode.Season;
+ var seasonPrimaryTag = episodeSeason is not null
+ ? GetTagAndFillBlurhash(dto, episodeSeason, ImageType.Primary)
+ : null;
+
+ if (seasonPrimaryTag is not null)
+ {
+ dto.ParentPrimaryImageItemId = episodeSeason!.Id;
+ dto.ParentPrimaryImageTag = seasonPrimaryTag;
+ }
+ else if (episodeSeries is not null && dto.SeriesPrimaryImageTag is not null)
+ {
+ dto.ParentPrimaryImageItemId = episodeSeries.Id;
+ dto.ParentPrimaryImageTag = dto.SeriesPrimaryImageTag;
+ }
+ }
+
if (options.ContainsField(ItemFields.SeriesStudio))
{
episodeSeries ??= episode.Series;
@@ -1363,6 +1542,21 @@ namespace Emby.Server.Implementations.Dto
private void AddInheritedImages(BaseItemDto dto, BaseItem item, DtoOptions options, BaseItem? owner)
{
+ if (item is UserView { ViewType: CollectionType.playlists } playlistsView
+ && options.GetImageLimit(ImageType.Primary) > 0
+ && !playlistsView.DisplayParentId.IsEmpty())
+ {
+ var displayParent = _libraryManager.GetItemById(playlistsView.DisplayParentId);
+ var displayParentPrimaryImage = displayParent?.GetImageInfo(ImageType.Primary, 0);
+
+ if (displayParentPrimaryImage is not null)
+ {
+ dto.ImageTags?.Remove(ImageType.Primary);
+ dto.ParentPrimaryImageItemId = displayParent!.Id;
+ dto.ParentPrimaryImageTag = GetTagAndFillBlurhash(dto, displayParent, displayParentPrimaryImage);
+ }
+ }
+
if (!item.SupportsInheritedParentImages)
{
return;
diff --git a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs
index 373b0994a6..dc7f972c13 100644
--- a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs
+++ b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs
@@ -1,5 +1,6 @@
using System;
using System.Buffers;
+using System.Globalization;
using System.IO.Pipelines;
using System.Net;
using System.Net.WebSockets;
@@ -69,6 +70,11 @@ namespace Emby.Server.Implementations.HttpServer
/// <inheritdoc />
public IPAddress? RemoteEndPoint { get; }
+ /// <summary>
+ /// Gets or initializes the UI culture captured from the upgrade request.
+ /// </summary>
+ public CultureInfo? RequestUICulture { get; init; }
+
/// <inheritdoc />
public Func<WebSocketMessageInfo, Task>? OnReceive { get; set; }
@@ -82,6 +88,17 @@ namespace Emby.Server.Implementations.HttpServer
public WebSocketState State => _socket.State;
/// <inheritdoc />
+ public void ApplyRequestCulture()
+ {
+ if (RequestUICulture is null)
+ {
+ return;
+ }
+
+ CultureInfo.CurrentUICulture = RequestUICulture;
+ }
+
+ /// <inheritdoc />
public async Task SendAsync(OutboundWebSocketMessage message, CancellationToken cancellationToken)
{
var json = JsonSerializer.SerializeToUtf8Bytes(message, _jsonOptions);
@@ -110,8 +127,12 @@ namespace Emby.Server.Implementations.HttpServer
{
receiveResult = await _socket.ReceiveAsync(memory, cancellationToken).ConfigureAwait(false);
}
- catch (WebSocketException ex)
+ catch (Exception ex) when (ex is WebSocketException or ObjectDisposedException or OperationCanceledException)
{
+ // ObjectDisposedException/OperationCanceledException: the socket was torn
+ // down underneath us (e.g. by the keep-alive watchdog after the connection
+ // was declared lost). Fall through so Closed is still raised and the
+ // session can release this connection.
_logger.LogWarning("WS {IP} error receiving data: {Message}", RemoteEndPoint, ex.Message);
break;
}
diff --git a/Emby.Server.Implementations/HttpServer/WebSocketManager.cs b/Emby.Server.Implementations/HttpServer/WebSocketManager.cs
index cb5b3993b8..072034c4bf 100644
--- a/Emby.Server.Implementations/HttpServer/WebSocketManager.cs
+++ b/Emby.Server.Implementations/HttpServer/WebSocketManager.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
+using System.Globalization;
using System.Linq;
using System.Net.WebSockets;
using System.Threading.Tasks;
@@ -47,14 +48,18 @@ namespace Emby.Server.Implementations.HttpServer
_logger.LogInformation("WS {IP} request", context.Connection.RemoteIpAddress);
WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync().ConfigureAwait(false);
-
var connection = new WebSocketConnection(
_loggerFactory.CreateLogger<WebSocketConnection>(),
webSocket,
authorizationInfo,
context.GetNormalizedRemoteIP())
{
- OnReceive = ProcessWebSocketMessageReceived
+ RequestUICulture = CultureInfo.CurrentUICulture
+ };
+ connection.OnReceive = result =>
+ {
+ connection.ApplyRequestCulture();
+ return ProcessWebSocketMessageReceived(result);
};
await using (connection.ConfigureAwait(false))
{
diff --git a/Emby.Server.Implementations/IO/LibraryMonitor.cs b/Emby.Server.Implementations/IO/LibraryMonitor.cs
index 7cff2a25b6..1bf0f8c76c 100644
--- a/Emby.Server.Implementations/IO/LibraryMonitor.cs
+++ b/Emby.Server.Implementations/IO/LibraryMonitor.cs
@@ -21,6 +21,7 @@ namespace Emby.Server.Implementations.IO
private readonly ILibraryManager _libraryManager;
private readonly IServerConfigurationManager _configurationManager;
private readonly IFileSystem _fileSystem;
+ private readonly DotIgnoreIgnoreRule _dotIgnoreIgnoreRule;
/// <summary>
/// The file system watchers.
@@ -47,19 +48,23 @@ namespace Emby.Server.Implementations.IO
/// <param name="configurationManager">The configuration manager.</param>
/// <param name="fileSystem">The filesystem.</param>
/// <param name="appLifetime">The <see cref="IHostApplicationLifetime"/>.</param>
+ /// <param name="dotIgnoreIgnoreRule">The .ignore rule handler.</param>
public LibraryMonitor(
ILogger<LibraryMonitor> logger,
ILibraryManager libraryManager,
IServerConfigurationManager configurationManager,
IFileSystem fileSystem,
- IHostApplicationLifetime appLifetime)
+ IHostApplicationLifetime appLifetime,
+ DotIgnoreIgnoreRule dotIgnoreIgnoreRule)
{
_libraryManager = libraryManager;
_logger = logger;
_configurationManager = configurationManager;
_fileSystem = fileSystem;
+ _dotIgnoreIgnoreRule = dotIgnoreIgnoreRule;
appLifetime.ApplicationStarted.Register(Start);
+ appLifetime.ApplicationStopping.Register(Stop);
}
/// <inheritdoc />
@@ -353,7 +358,7 @@ namespace Emby.Server.Implementations.IO
}
var fileInfo = _fileSystem.GetFileSystemInfo(path);
- if (DotIgnoreIgnoreRule.IsIgnored(fileInfo, null))
+ if (_dotIgnoreIgnoreRule.ShouldIgnore(fileInfo, null))
{
return;
}
diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs
index 4d68cb4444..ede9b27592 100644
--- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs
+++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs
@@ -586,6 +586,12 @@ namespace Emby.Server.Implementations.IO
/// <inheritdoc />
public virtual IEnumerable<FileSystemMetadata> GetFiles(string path, string searchPattern, IReadOnlyList<string>? extensions, bool enableCaseSensitiveExtensions, bool recursive = false)
{
+ if (!Directory.Exists(path))
+ {
+ _logger.LogWarning("Directory does not exist: {Path}", path);
+ return [];
+ }
+
var enumerationOptions = GetEnumerationOptions(recursive);
// On linux and macOS the search pattern is case-sensitive
@@ -685,7 +691,7 @@ namespace Emby.Server.Implementations.IO
}
catch (Exception ex) when (ex is UnauthorizedAccessException or DirectoryNotFoundException or SecurityException)
{
- _logger.LogError(ex, "Failed to enumerate path {Path}", path);
+ _logger.LogWarning("Failed to enumerate path \"{Path}\": {Message}", path, ex.Message);
return Enumerable.Empty<string>();
}
}
diff --git a/Emby.Server.Implementations/Images/CollectionFolderImageProvider.cs b/Emby.Server.Implementations/Images/CollectionFolderImageProvider.cs
index a25373326f..b701e7eb6d 100644
--- a/Emby.Server.Implementations/Images/CollectionFolderImageProvider.cs
+++ b/Emby.Server.Implementations/Images/CollectionFolderImageProvider.cs
@@ -5,6 +5,7 @@
using System;
using System.Collections.Generic;
using System.IO;
+using Jellyfin.Api.Extensions;
using Jellyfin.Data.Enums;
using Jellyfin.Database.Implementations.Enums;
using MediaBrowser.Common.Configuration;
@@ -14,7 +15,6 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Querying;
namespace Emby.Server.Implementations.Images
{
@@ -28,38 +28,7 @@ namespace Emby.Server.Implementations.Images
{
var view = (CollectionFolder)item;
var viewType = view.CollectionType;
-
- BaseItemKind[] includeItemTypes;
-
- switch (viewType)
- {
- case CollectionType.movies:
- includeItemTypes = new[] { BaseItemKind.Movie };
- break;
- case CollectionType.tvshows:
- includeItemTypes = new[] { BaseItemKind.Series };
- break;
- case CollectionType.music:
- includeItemTypes = new[] { BaseItemKind.MusicAlbum };
- break;
- case CollectionType.musicvideos:
- includeItemTypes = new[] { BaseItemKind.MusicVideo };
- break;
- case CollectionType.books:
- includeItemTypes = new[] { BaseItemKind.Book, BaseItemKind.AudioBook };
- break;
- case CollectionType.boxsets:
- includeItemTypes = new[] { BaseItemKind.BoxSet };
- break;
- case CollectionType.homevideos:
- case CollectionType.photos:
- includeItemTypes = new[] { BaseItemKind.Video, BaseItemKind.Photo };
- break;
- default:
- includeItemTypes = new[] { BaseItemKind.Video, BaseItemKind.Audio, BaseItemKind.Photo, BaseItemKind.Movie, BaseItemKind.Series };
- break;
- }
-
+ var includeItemTypes = DtoExtensions.GetBaseItemKindsForCollectionType(viewType);
var recursive = viewType != CollectionType.playlists;
return view.GetItemList(new InternalItemsQuery
@@ -67,12 +36,9 @@ namespace Emby.Server.Implementations.Images
CollapseBoxSetItems = false,
Recursive = recursive,
DtoOptions = new DtoOptions(false),
- ImageTypes = new[] { ImageType.Primary },
+ ImageTypes = [ImageType.Primary],
Limit = 8,
- OrderBy = new[]
- {
- (ItemSortBy.Random, SortOrder.Ascending)
- },
+ OrderBy = [(ItemSortBy.Random, SortOrder.Ascending)],
IncludeItemTypes = includeItemTypes
});
}
diff --git a/Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs b/Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs
index ef5d24c70f..023c1e8915 100644
--- a/Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs
+++ b/Emby.Server.Implementations/Library/DotIgnoreIgnoreRule.cs
@@ -1,6 +1,8 @@
using System;
+using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
+using BitFaster.Caching.Lru;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Resolvers;
@@ -15,22 +17,36 @@ public class DotIgnoreIgnoreRule : IResolverIgnoreRule
{
private static readonly bool IsWindows = OperatingSystem.IsWindows();
- private static FileInfo? FindIgnoreFile(DirectoryInfo directory)
- {
- for (var current = directory; current is not null; current = current.Parent)
- {
- var ignorePath = Path.Join(current.FullName, ".ignore");
- if (File.Exists(ignorePath))
- {
- return new FileInfo(ignorePath);
- }
- }
+ private readonly FastConcurrentLru<string, IgnoreFileCacheEntry> _directoryCache;
+ private readonly FastConcurrentLru<string, ParsedIgnoreCacheEntry> _rulesCache;
- return null;
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DotIgnoreIgnoreRule"/> class.
+ /// </summary>
+ public DotIgnoreIgnoreRule()
+ {
+ var cacheSize = Math.Max(100, Environment.ProcessorCount * 100);
+ _directoryCache = new FastConcurrentLru<string, IgnoreFileCacheEntry>(
+ Environment.ProcessorCount,
+ cacheSize,
+ StringComparer.Ordinal);
+ _rulesCache = new FastConcurrentLru<string, ParsedIgnoreCacheEntry>(
+ Environment.ProcessorCount,
+ Math.Max(32, cacheSize / 4),
+ StringComparer.Ordinal);
}
/// <inheritdoc />
- public bool ShouldIgnore(FileSystemMetadata fileInfo, BaseItem? parent) => IsIgnored(fileInfo, parent);
+ public bool ShouldIgnore(FileSystemMetadata fileInfo, BaseItem? parent) => IsIgnoredInternal(fileInfo, parent);
+
+ /// <summary>
+ /// Clears the directory lookup cache. The parsed rules cache is not cleared
+ /// as it validates file modification time on each access.
+ /// </summary>
+ public void ClearDirectoryCache()
+ {
+ _directoryCache.Clear();
+ }
/// <summary>
/// Checks whether or not the file is ignored.
@@ -38,40 +54,38 @@ public class DotIgnoreIgnoreRule : IResolverIgnoreRule
/// <param name="fileInfo">The file information.</param>
/// <param name="parent">The parent BaseItem.</param>
/// <returns>True if the file should be ignored.</returns>
- public static bool IsIgnored(FileSystemMetadata fileInfo, BaseItem? parent)
+ public bool IsIgnoredInternal(FileSystemMetadata fileInfo, BaseItem? parent)
{
var searchDirectory = fileInfo.IsDirectory
- ? new DirectoryInfo(fileInfo.FullName)
- : new DirectoryInfo(Path.GetDirectoryName(fileInfo.FullName) ?? string.Empty);
+ ? fileInfo.FullName
+ : Path.GetDirectoryName(fileInfo.FullName);
- if (string.IsNullOrEmpty(searchDirectory.FullName))
+ if (string.IsNullOrEmpty(searchDirectory))
{
return false;
}
- var ignoreFile = FindIgnoreFile(searchDirectory);
+ var ignoreFile = FindIgnoreFileCached(searchDirectory);
if (ignoreFile is null)
{
return false;
}
- // Fast path in case the ignore files isn't a symlink and is empty
- if (ignoreFile.LinkTarget is null && ignoreFile.Length == 0)
+ var parsedEntry = GetParsedRules(ignoreFile);
+ if (parsedEntry is null)
{
- // Ignore directory if we just have the file
- return true;
+ // File was deleted after we cached the path - clear the directory cache entry and return false
+ _directoryCache.TryRemove(searchDirectory, out _);
+ return false;
}
- var content = GetFileContent(ignoreFile);
- return string.IsNullOrWhiteSpace(content)
- || CheckIgnoreRules(fileInfo.FullName, content, fileInfo.IsDirectory);
- }
+ // Empty file means ignore everything
+ if (parsedEntry.IsEmpty)
+ {
+ return true;
+ }
- private static bool CheckIgnoreRules(string path, string ignoreFileContent, bool isDirectory)
- {
- // If file has content, base ignoring off the content .gitignore-style rules
- var rules = ignoreFileContent.Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
- return CheckIgnoreRules(path, rules, isDirectory);
+ return parsedEntry.Rules.IsIgnored(GetPathToCheck(fileInfo.FullName, fileInfo.IsDirectory));
}
/// <summary>
@@ -117,8 +131,8 @@ public class DotIgnoreIgnoreRule : IResolverIgnoreRule
return true;
}
- // Mitigate the problem of the Ignore library not handling Windows paths correctly.
- // See https://github.com/jellyfin/jellyfin/issues/15484
+ // Mitigate the problem of the Ignore library not handling Windows paths correctly.
+ // See https://github.com/jellyfin/jellyfin/issues/15484
var pathToCheck = normalizePath ? path.NormalizePath('/') : path;
// Add trailing slash for directories to match "folder/"
@@ -130,11 +144,196 @@ public class DotIgnoreIgnoreRule : IResolverIgnoreRule
return ignore.IsIgnored(pathToCheck);
}
- private static string GetFileContent(FileInfo ignoreFile)
+ private FileInfo? FindIgnoreFileCached(string directory)
+ {
+ // Check if we have a cached result for this directory
+ if (_directoryCache.TryGet(directory, out var cached))
+ {
+ return cached.IgnoreFileDirectory is null
+ ? null
+ : new FileInfo(Path.Join(cached.IgnoreFileDirectory, ".ignore"));
+ }
+
+ DirectoryInfo startDir;
+ try
+ {
+ startDir = new DirectoryInfo(directory);
+ }
+ catch (ArgumentException)
+ {
+ return null;
+ }
+
+ // Walk up the directory tree to find .ignore file using DirectoryInfo.Parent
+ var checkedDirs = new List<string> { directory };
+
+ for (var current = startDir; current is not null; current = current.Parent)
+ {
+ var currentPath = current.FullName;
+
+ // Check if this intermediate directory is cached
+ if (current != startDir && _directoryCache.TryGet(currentPath, out var parentCached))
+ {
+ // Cache the result for all directories we checked
+ var entry = new IgnoreFileCacheEntry(parentCached.IgnoreFileDirectory);
+ foreach (var dir in checkedDirs)
+ {
+ _directoryCache.AddOrUpdate(dir, entry);
+ }
+
+ return parentCached.IgnoreFileDirectory is null
+ ? null
+ : new FileInfo(Path.Join(parentCached.IgnoreFileDirectory, ".ignore"));
+ }
+
+ var ignoreFile = new FileInfo(Path.Join(currentPath, ".ignore"));
+ if (ignoreFile.Exists)
+ {
+ // Cache for all directories we checked
+ var entry = new IgnoreFileCacheEntry(currentPath);
+ foreach (var dir in checkedDirs)
+ {
+ _directoryCache.AddOrUpdate(dir, entry);
+ }
+
+ return ignoreFile;
+ }
+
+ if (current != startDir)
+ {
+ checkedDirs.Add(currentPath);
+ }
+ }
+
+ // No .ignore file found - cache null result for all directories
+ var nullEntry = new IgnoreFileCacheEntry((string?)null);
+ foreach (var dir in checkedDirs)
+ {
+ _directoryCache.AddOrUpdate(dir, nullEntry);
+ }
+
+ return null;
+ }
+
+ private ParsedIgnoreCacheEntry? GetParsedRules(FileInfo ignoreFile)
+ {
+ if (!ignoreFile.Exists)
+ {
+ _rulesCache.TryRemove(ignoreFile.FullName, out _);
+ return null;
+ }
+
+ var lastModified = ignoreFile.LastWriteTimeUtc;
+ var fileLength = ignoreFile.Length;
+ var key = ignoreFile.FullName;
+
+ // Check cache
+ if (_rulesCache.TryGet(key, out var cached))
+ {
+ if (cached.FileLastModified == lastModified && cached.FileLength == fileLength)
+ {
+ return cached;
+ }
+
+ // Stale - need to reparse
+ _rulesCache.TryRemove(key, out _);
+ }
+
+ // Parse the file
+ var parsedEntry = ParseIgnoreFile(ignoreFile, lastModified, fileLength);
+ _rulesCache.AddOrUpdate(key, parsedEntry);
+ return parsedEntry;
+ }
+
+ private static ParsedIgnoreCacheEntry ParseIgnoreFile(FileInfo ignoreFile, DateTime lastModified, long fileLength)
{
- ignoreFile = FileSystemHelper.ResolveLinkTarget(ignoreFile, returnFinalTarget: true) ?? ignoreFile;
- return ignoreFile.Exists
- ? File.ReadAllText(ignoreFile.FullName)
- : string.Empty;
+ if (ignoreFile.LinkTarget is null && fileLength == 0)
+ {
+ return new ParsedIgnoreCacheEntry
+ {
+ Rules = new Ignore.Ignore(),
+ FileLastModified = lastModified,
+ FileLength = fileLength,
+ IsEmpty = true
+ };
+ }
+
+ // Resolve symlinks
+ var resolvedFile = FileSystemHelper.ResolveLinkTarget(ignoreFile, returnFinalTarget: true) ?? ignoreFile;
+ if (!resolvedFile.Exists)
+ {
+ return new ParsedIgnoreCacheEntry
+ {
+ Rules = new Ignore.Ignore(),
+ FileLastModified = lastModified,
+ FileLength = fileLength,
+ IsEmpty = true
+ };
+ }
+
+ var content = File.ReadAllText(resolvedFile.FullName);
+ if (string.IsNullOrWhiteSpace(content))
+ {
+ return new ParsedIgnoreCacheEntry
+ {
+ Rules = new Ignore.Ignore(),
+ FileLastModified = lastModified,
+ FileLength = fileLength,
+ IsEmpty = true
+ };
+ }
+
+ var rules = content.Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
+ var ignore = new Ignore.Ignore();
+ var validRulesAdded = 0;
+
+ foreach (var rule in rules)
+ {
+ try
+ {
+ ignore.Add(rule);
+ validRulesAdded++;
+ }
+ catch (RegexParseException)
+ {
+ // Ignore invalid patterns
+ }
+ }
+
+ // No valid rules means treat as empty (ignore all)
+ return new ParsedIgnoreCacheEntry
+ {
+ Rules = ignore,
+ FileLastModified = lastModified,
+ FileLength = fileLength,
+ IsEmpty = validRulesAdded == 0
+ };
+ }
+
+ private static string GetPathToCheck(string path, bool isDirectory)
+ {
+ // Normalize Windows paths
+ var pathToCheck = IsWindows ? path.NormalizePath('/') : path;
+
+ // Add trailing slash for directories to match "folder/"
+ if (isDirectory)
+ {
+ pathToCheck = string.Concat(pathToCheck.AsSpan().TrimEnd('/'), "/");
+ }
+
+ return pathToCheck;
+ }
+
+ private readonly record struct IgnoreFileCacheEntry(string? IgnoreFileDirectory);
+
+ private sealed class ParsedIgnoreCacheEntry
+ {
+ public required Ignore.Ignore Rules { get; init; }
+
+ public required DateTime FileLastModified { get; init; }
+
+ public required long FileLength { get; init; }
+
+ public required bool IsEmpty { get; init; }
}
}
diff --git a/Emby.Server.Implementations/Library/ExternalDataManager.cs b/Emby.Server.Implementations/Library/ExternalDataManager.cs
index 4ad0f999bf..2c18e56df7 100644
--- a/Emby.Server.Implementations/Library/ExternalDataManager.cs
+++ b/Emby.Server.Implementations/Library/ExternalDataManager.cs
@@ -1,6 +1,5 @@
using System;
using System.IO;
-using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Chapters;
@@ -52,26 +51,33 @@ public class ExternalDataManager : IExternalDataManager
/// <inheritdoc/>
public async Task DeleteExternalItemDataAsync(BaseItem item, CancellationToken cancellationToken)
{
- var validPaths = _pathManager.GetExtractedDataPaths(item).Where(Directory.Exists).ToList();
- var itemId = item.Id;
- if (validPaths.Count > 0)
- {
- foreach (var path in validPaths)
- {
- try
- {
- Directory.Delete(path, true);
- }
- catch (Exception ex)
- {
- _logger.LogWarning("Unable to prune external item data at {Path}: {Exception}", path, ex);
- }
- }
- }
+ DeleteExternalItemFiles(item);
+ var itemId = item.Id;
await _keyframeManager.DeleteKeyframeDataAsync(itemId, cancellationToken).ConfigureAwait(false);
await _mediaSegmentManager.DeleteSegmentsAsync(itemId, cancellationToken).ConfigureAwait(false);
await _trickplayManager.DeleteTrickplayDataAsync(itemId, cancellationToken).ConfigureAwait(false);
await _chapterManager.DeleteChapterDataAsync(itemId, cancellationToken).ConfigureAwait(false);
}
+
+ /// <inheritdoc/>
+ public void DeleteExternalItemFiles(BaseItem item)
+ {
+ foreach (var path in _pathManager.GetExtractedDataPaths(item))
+ {
+ if (!Directory.Exists(path))
+ {
+ continue;
+ }
+
+ try
+ {
+ Directory.Delete(path, true);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogWarning("Unable to prune external item data at {Path}: {Exception}", path, ex);
+ }
+ }
+ }
}
diff --git a/Emby.Server.Implementations/Library/IgnorePatterns.cs b/Emby.Server.Implementations/Library/IgnorePatterns.cs
index 59ccb9e2c7..197ec42c50 100644
--- a/Emby.Server.Implementations/Library/IgnorePatterns.cs
+++ b/Emby.Server.Implementations/Library/IgnorePatterns.cs
@@ -31,6 +31,20 @@ namespace Emby.Server.Implementations.Library
"**/*.sample.?????",
"**/sample/*",
+ // Avoid adding Hungarian sample files
+ // https://github.com/jellyfin/jellyfin/issues/16237
+ "**/minta.?",
+ "**/minta.??",
+ "**/minta.???", // Matches minta.mkv
+ "**/minta.????", // Matches minta.webm
+ "**/minta.?????",
+ "**/*.minta.?",
+ "**/*.minta.??",
+ "**/*.minta.???",
+ "**/*.minta.????",
+ "**/*.minta.?????",
+ "**/minta/*",
+
// Directories
"**/metadata/**",
"**/metadata",
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs
index f7f5c387e1..3691f4e19d 100644
--- a/Emby.Server.Implementations/Library/LibraryManager.cs
+++ b/Emby.Server.Implementations/Library/LibraryManager.cs
@@ -13,6 +13,7 @@ using System.Threading.Tasks;
using BitFaster.Caching.Lru;
using Emby.Naming.Common;
using Emby.Naming.TV;
+using Emby.Naming.Video;
using Emby.Server.Implementations.Library.Resolvers;
using Emby.Server.Implementations.Library.Validators;
using Emby.Server.Implementations.Playlists;
@@ -30,18 +31,17 @@ using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Controller.MediaSegments;
using MediaBrowser.Controller.Persistence;
+using MediaBrowser.Controller.Playlists;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Controller.Sorting;
-using MediaBrowser.Controller.Trickplay;
using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
@@ -77,12 +77,19 @@ namespace Emby.Server.Implementations.Library
private readonly IMediaEncoder _mediaEncoder;
private readonly IFileSystem _fileSystem;
private readonly IItemRepository _itemRepository;
+ private readonly IItemPersistenceService _persistenceService;
+ private readonly INextUpService _nextUpService;
+ private readonly IItemCountService _countService;
+ private readonly ILinkedChildrenService _linkedChildrenService;
private readonly IImageProcessor _imageProcessor;
private readonly NamingOptions _namingOptions;
private readonly IPeopleRepository _peopleRepository;
private readonly ExtraResolver _extraResolver;
private readonly IPathManager _pathManager;
private readonly FastConcurrentLru<Guid, BaseItem> _cache;
+ private readonly DotIgnoreIgnoreRule _dotIgnoreIgnoreRule;
+ private readonly IMediaStreamRepository _mediaStreamRepository;
+ private readonly Lazy<IExternalDataManager> _externalDataManagerFactory;
/// <summary>
/// The _root folder sync lock.
@@ -115,11 +122,18 @@ namespace Emby.Server.Implementations.Library
/// <param name="userViewManagerFactory">The user view manager.</param>
/// <param name="mediaEncoder">The media encoder.</param>
/// <param name="itemRepository">The item repository.</param>
+ /// <param name="persistenceService">The item persistence service.</param>
+ /// <param name="nextUpService">The next up service.</param>
+ /// <param name="countService">The item count service.</param>
+ /// <param name="linkedChildrenService">The linked children service.</param>
/// <param name="imageProcessor">The image processor.</param>
/// <param name="namingOptions">The naming options.</param>
/// <param name="directoryService">The directory service.</param>
/// <param name="peopleRepository">The people repository.</param>
/// <param name="pathManager">The path manager.</param>
+ /// <param name="dotIgnoreIgnoreRule">The .ignore rule handler.</param>
+ /// <param name="mediaStreamRepository">The media stream repository.</param>
+ /// <param name="externalDataManagerFactory">The external data manager (lazy, to break the DI cycle through ChapterManager).</param>
public LibraryManager(
IServerApplicationHost appHost,
ILoggerFactory loggerFactory,
@@ -133,11 +147,18 @@ namespace Emby.Server.Implementations.Library
Lazy<IUserViewManager> userViewManagerFactory,
IMediaEncoder mediaEncoder,
IItemRepository itemRepository,
+ IItemPersistenceService persistenceService,
+ INextUpService nextUpService,
+ IItemCountService countService,
+ ILinkedChildrenService linkedChildrenService,
IImageProcessor imageProcessor,
NamingOptions namingOptions,
IDirectoryService directoryService,
IPeopleRepository peopleRepository,
- IPathManager pathManager)
+ IPathManager pathManager,
+ DotIgnoreIgnoreRule dotIgnoreIgnoreRule,
+ IMediaStreamRepository mediaStreamRepository,
+ Lazy<IExternalDataManager> externalDataManagerFactory)
{
_appHost = appHost;
_logger = loggerFactory.CreateLogger<LibraryManager>();
@@ -151,6 +172,10 @@ namespace Emby.Server.Implementations.Library
_userViewManagerFactory = userViewManagerFactory;
_mediaEncoder = mediaEncoder;
_itemRepository = itemRepository;
+ _persistenceService = persistenceService;
+ _nextUpService = nextUpService;
+ _countService = countService;
+ _linkedChildrenService = linkedChildrenService;
_imageProcessor = imageProcessor;
_cache = new FastConcurrentLru<Guid, BaseItem>(_configurationManager.Configuration.CacheSize);
@@ -158,10 +183,14 @@ namespace Emby.Server.Implementations.Library
_namingOptions = namingOptions;
_peopleRepository = peopleRepository;
_pathManager = pathManager;
+ _dotIgnoreIgnoreRule = dotIgnoreIgnoreRule;
_extraResolver = new ExtraResolver(loggerFactory.CreateLogger<ExtraResolver>(), namingOptions, directoryService);
_configurationManager.ConfigurationUpdated += ConfigurationUpdated;
+ _mediaStreamRepository = mediaStreamRepository;
+ _externalDataManagerFactory = externalDataManagerFactory;
+
RecordConfigurationValues(_configurationManager.Configuration);
}
@@ -327,9 +356,17 @@ namespace Emby.Server.Implementations.Library
DeleteItem(item, options, parent, notifyParentItem);
}
- public void DeleteItemsUnsafeFast(IEnumerable<BaseItem> items)
+ public void DeleteItemsUnsafeFast(IReadOnlyCollection<BaseItem> items, bool deleteSourceFiles = false)
{
- var pathMaps = items.Select(e => (Item: e, InternalPath: GetInternalMetadataPaths(e), DeletePaths: e.GetDeletePaths())).ToArray();
+ if (items.Count == 0)
+ {
+ return;
+ }
+
+ var pathMaps = items.Select(e =>
+ (Item: e,
+ InternalPath: GetInternalMetadataPaths(e),
+ DeletePaths: deleteSourceFiles ? e.GetDeletePaths() : [])).ToArray();
foreach (var (item, internalPaths, pathsToDelete) in pathMaps)
{
@@ -363,7 +400,13 @@ namespace Emby.Server.Implementations.Library
}
}
- _itemRepository.DeleteItem([.. pathMaps.Select(f => f.Item.Id)]);
+ var externalDataManager = _externalDataManagerFactory.Value;
+ foreach (var (item, _, _) in pathMaps)
+ {
+ externalDataManager.DeleteExternalItemFiles(item);
+ }
+
+ _persistenceService.DeleteItem([.. pathMaps.Select(f => f.Item.Id)]);
}
public void DeleteItem(BaseItem item, DeleteOptions options, BaseItem parent, bool notifyParentItem)
@@ -406,6 +449,99 @@ namespace Emby.Server.Implementations.Library
item.Id);
}
+ // If deleting a primary version video, clear PrimaryVersionId from alternate versions
+ // OwnerId check: items with OwnerId set are alternate versions or extras, not primaries
+ if (item is Video video && !video.PrimaryVersionId.HasValue && video.OwnerId.IsEmpty())
+ {
+ var localAlternateIds = GetLocalAlternateVersionIds(video).ToHashSet();
+ var allAlternateVersions = localAlternateIds
+ .Concat(GetLinkedAlternateVersions(video).Select(v => v.Id))
+ .Distinct()
+ .Select(id => GetItemById(id))
+ .OfType<Video>()
+ .ToList();
+
+ // Partition alternates by whether their files still exist on disk
+ var alternateVersions = new List<Video>();
+ var missingAlternates = new List<Video>();
+ foreach (var alt in allAlternateVersions)
+ {
+ if (!string.IsNullOrEmpty(alt.Path) && !_fileSystem.FileExists(alt.Path))
+ {
+ missingAlternates.Add(alt);
+ }
+ else
+ {
+ alternateVersions.Add(alt);
+ }
+ }
+
+ // Delete alternates whose files no longer exist to avoid ghost items.
+ // Clear PrimaryVersionId first so DeleteItem doesn't try to update the primary being deleted.
+ foreach (var missing in missingAlternates)
+ {
+ _logger.LogInformation(
+ "Deleting missing alternate version {Name} ({Path})",
+ missing.Name ?? "Unknown name",
+ missing.Path ?? string.Empty);
+ missing.SetPrimaryVersionId(null);
+ missing.OwnerId = Guid.Empty;
+ missing.LocalAlternateVersions = [];
+ missing.LinkedAlternateVersions = [];
+ DeleteItem(missing, new DeleteOptions { DeleteFileLocation = false }, false);
+ }
+
+ if (alternateVersions.Count > 0)
+ {
+ _logger.LogInformation(
+ "Clearing PrimaryVersionId from {Count} alternate versions of {Name}",
+ alternateVersions.Count,
+ item.Name ?? "Unknown name");
+
+ // Promote the first alternate version to be the new primary
+ var newPrimary = alternateVersions[0];
+ newPrimary.SetPrimaryVersionId(null);
+ newPrimary.OwnerId = Guid.Empty;
+
+ // Transfer alternate version arrays from old primary to new primary
+ // so UpdateToRepositoryAsync creates correct LinkedChildren entries
+ newPrimary.LocalAlternateVersions = video.LocalAlternateVersions
+ .Where(p => !string.Equals(p, newPrimary.Path, StringComparison.OrdinalIgnoreCase))
+ .ToArray();
+ newPrimary.LinkedAlternateVersions = video.LinkedAlternateVersions
+ .Where(lc => !lc.ItemId.HasValue || !lc.ItemId.Value.Equals(newPrimary.Id))
+ .ToArray();
+
+ newPrimary.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).GetAwaiter().GetResult();
+
+ // Re-route playlist/collection references from deleted primary to new primary
+ RerouteLinkedChildReferencesAsync(video.Id, newPrimary.Id).GetAwaiter().GetResult();
+
+ // Update remaining alternates to point to new primary
+ foreach (var alternate in alternateVersions.Skip(1))
+ {
+ alternate.SetPrimaryVersionId(newPrimary.Id);
+ // Only set OwnerId for local alternates; linked alternates are independent items
+ alternate.OwnerId = localAlternateIds.Contains(alternate.Id) ? newPrimary.Id : Guid.Empty;
+ alternate.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).GetAwaiter().GetResult();
+ }
+ }
+ }
+ else if (item is Video alternateVideo && alternateVideo.PrimaryVersionId.HasValue)
+ {
+ // If deleting an alternate version, re-route references to its primary
+ RerouteLinkedChildReferencesAsync(alternateVideo.Id, alternateVideo.PrimaryVersionId.Value).GetAwaiter().GetResult();
+
+ // Remove deleted alternate from primary's LinkedAlternateVersions
+ if (GetItemById(alternateVideo.PrimaryVersionId.Value) is Video primaryVideo)
+ {
+ primaryVideo.LinkedAlternateVersions = primaryVideo.LinkedAlternateVersions
+ .Where(lc => !lc.ItemId.HasValue || !lc.ItemId.Value.Equals(alternateVideo.Id))
+ .ToArray();
+ primaryVideo.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).GetAwaiter().GetResult();
+ }
+ }
+
var children = item.IsFolder
? ((Folder)item).GetRecursiveChildren(false)
: [];
@@ -450,7 +586,14 @@ namespace Emby.Server.Implementations.Library
item.SetParent(null);
- _itemRepository.DeleteItem([item.Id, .. children.Select(f => f.Id)]);
+ var externalDataManager = _externalDataManagerFactory.Value;
+ externalDataManager.DeleteExternalItemFiles(item);
+ foreach (var child in children)
+ {
+ externalDataManager.DeleteExternalItemFiles(child);
+ }
+
+ _persistenceService.DeleteItem([item.Id, .. children.Select(f => f.Id)]);
_cache.TryRemove(item.Id, out _);
foreach (var child in children)
{
@@ -576,6 +719,9 @@ namespace Emby.Server.Implementations.Library
// Trickplay
list.Add(_pathManager.GetTrickplayDirectory(video));
+ // Chapter Images
+ list.Add(_pathManager.GetChapterImageFolderPath(video));
+
// Subtitles and attachments
foreach (var mediaSource in item.GetMediaSources(false))
{
@@ -657,8 +803,99 @@ namespace Emby.Server.Implementations.Library
return key.GetMD5();
}
- public BaseItem? ResolvePath(FileSystemMetadata fileInfo, Folder? parent = null, IDirectoryService? directoryService = null)
- => ResolvePath(fileInfo, directoryService ?? new DirectoryService(_fileSystem), null, parent);
+ public BaseItem? ResolvePath(
+ FileSystemMetadata fileInfo,
+ Folder? parent = null,
+ IDirectoryService? directoryService = null,
+ CollectionType? collectionType = null)
+ => ResolvePath(fileInfo, directoryService ?? new DirectoryService(_fileSystem), null, parent, collectionType);
+
+ private void SetAdditionalPartsFromStack(Video altVideo, string path)
+ {
+ if (altVideo.AdditionalParts is { Length: > 0 })
+ {
+ return;
+ }
+
+ var directory = Path.GetDirectoryName(path);
+ if (string.IsNullOrEmpty(directory))
+ {
+ return;
+ }
+
+ IEnumerable<FileSystemMetadata> siblings;
+ try
+ {
+ siblings = _fileSystem.GetFiles(directory);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogWarning(ex, "Failed to enumerate siblings to detect stack for {Path}", path);
+ return;
+ }
+
+ var stacks = StackResolver.Resolve(siblings, _namingOptions);
+ foreach (var stack in stacks)
+ {
+ if (stack.Files.Count > 1
+ && string.Equals(stack.Files[0], path, StringComparison.OrdinalIgnoreCase))
+ {
+ altVideo.AdditionalParts = stack.Files.Skip(1).ToArray();
+ return;
+ }
+ }
+ }
+
+ /// <inheritdoc />
+ public Video? ResolveAlternateVersion(string path, Type expectedVideoType, Folder? parent, CollectionType? collectionType)
+ {
+ // Clean up any existing item saved with wrong type (e.g. Video instead of Movie).
+ // This happens when items were previously resolved without proper type context
+ // in mixed-content libraries where collectionType is null.
+ var expectedId = GetNewItemId(path, expectedVideoType);
+ if (expectedVideoType != typeof(Video))
+ {
+ var wrongTypeId = GetNewItemId(path, typeof(Video));
+ if (!wrongTypeId.Equals(expectedId) && GetItemById(wrongTypeId) is Video wrongTypeItem)
+ {
+ _logger.LogInformation(
+ "Removing alternate version with wrong type {WrongType}, expected {ExpectedType}: {Path}",
+ wrongTypeItem.GetType().Name,
+ expectedVideoType.Name,
+ path);
+ DeleteItem(wrongTypeItem, new DeleteOptions { DeleteFileLocation = false });
+ }
+ }
+
+ var resolved = ResolvePath(
+ _fileSystem.GetFileSystemInfo(path),
+ parent,
+ collectionType: collectionType) as Video;
+
+ if (resolved is null)
+ {
+ return null;
+ }
+
+ // Ensure the alternate version has the same concrete type as the primary video.
+ // ResolvePath may return a generic Video for files in mixed-content libraries
+ // where collectionType is null, even though the primary is a Movie/Episode/etc.
+ if (resolved.GetType() != expectedVideoType)
+ {
+ if (Activator.CreateInstance(expectedVideoType) is Video correctVideo)
+ {
+ correctVideo.Path = resolved.Path;
+ correctVideo.Name = resolved.Name;
+ correctVideo.VideoType = resolved.VideoType;
+ correctVideo.ProductionYear = resolved.ProductionYear;
+ correctVideo.ExtraType = resolved.ExtraType;
+ resolved = correctVideo;
+ }
+ }
+
+ resolved.Id = expectedId;
+ return resolved;
+ }
private BaseItem? ResolvePath(
FileSystemMetadata fileInfo,
@@ -1041,7 +1278,7 @@ namespace Emby.Server.Implementations.Library
public IReadOnlyDictionary<string, MusicArtist[]> GetArtists(IReadOnlyList<string> names)
{
- return _itemRepository.FindArtists(names);
+ return _linkedChildrenService.FindArtists(names);
}
public MusicArtist GetArtist(string name, DtoOptions options)
@@ -1131,6 +1368,7 @@ namespace Emby.Server.Implementations.Library
public async Task ValidateMediaLibraryInternal(IProgress<double> progress, CancellationToken cancellationToken)
{
IsScanRunning = true;
+ ClearIgnoreRuleCache();
LibraryMonitor.Stop();
try
@@ -1139,6 +1377,7 @@ namespace Emby.Server.Implementations.Library
}
finally
{
+ ClearIgnoreRuleCache();
LibraryMonitor.Start();
IsScanRunning = false;
}
@@ -1146,6 +1385,7 @@ namespace Emby.Server.Implementations.Library
public async Task ValidateTopLibraryFolders(CancellationToken cancellationToken, bool removeRoot = false)
{
+ ClearIgnoreRuleCache();
RootFolder.Children = null;
await RootFolder.RefreshMetadata(cancellationToken).ConfigureAwait(false);
@@ -1186,8 +1426,16 @@ namespace Emby.Server.Implementations.Library
if (toDelete.Count > 0)
{
- _itemRepository.DeleteItem(toDelete.ToArray());
+ _persistenceService.DeleteItem(toDelete.ToArray());
}
+
+ ClearIgnoreRuleCache();
+ }
+
+ /// <inheritdoc />
+ public void ClearIgnoreRuleCache()
+ {
+ _dotIgnoreIgnoreRule.ClearDirectoryCache();
}
private async Task PerformLibraryValidation(IProgress<double> progress, CancellationToken cancellationToken)
@@ -1262,7 +1510,7 @@ namespace Emby.Server.Implementations.Library
progress.Report(percent * 100);
}
- _itemRepository.UpdateInheritedValues();
+ _persistenceService.UpdateInheritedValues();
progress.Report(100);
}
@@ -1421,14 +1669,7 @@ namespace Emby.Server.Implementations.Library
AddUserToQuery(query, query.User, allowExternalContent);
}
- var itemList = _itemRepository.GetItemList(query);
- var user = query.User;
- if (user is not null)
- {
- return itemList.Where(i => i.IsVisible(user)).ToList();
- }
-
- return itemList;
+ return _itemRepository.GetItemList(query);
}
public IReadOnlyList<BaseItem> GetItemList(InternalItemsQuery query)
@@ -1452,7 +1693,7 @@ namespace Emby.Server.Implementations.Library
AddUserToQuery(query, query.User);
}
- return _itemRepository.GetCount(query);
+ return _countService.GetCount(query);
}
public ItemCounts GetItemCounts(InternalItemsQuery query)
@@ -1471,7 +1712,30 @@ namespace Emby.Server.Implementations.Library
AddUserToQuery(query, query.User);
}
- return _itemRepository.GetItemCounts(query);
+ return _countService.GetItemCounts(query);
+ }
+
+ /// <inheritdoc/>
+ public ItemCounts GetItemCountsForNameItem(BaseItemKind kind, Guid id, BaseItemKind[] relatedItemKinds, User? user)
+ {
+ var query = new InternalItemsQuery(user);
+ if (user is not null)
+ {
+ AddUserToQuery(query, user);
+ }
+
+ return _countService.GetItemCountsForNameItem(kind, id, relatedItemKinds, query);
+ }
+
+ public Dictionary<Guid, int> GetChildCountBatch(IReadOnlyList<Guid> parentIds, Guid? userId)
+ {
+ return _countService.GetChildCountBatch(parentIds, userId);
+ }
+
+ /// <inheritdoc/>
+ public Dictionary<Guid, (int Played, int Total)> GetPlayedAndTotalCountBatch(IReadOnlyList<Guid> folderIds, User user)
+ {
+ return _countService.GetPlayedAndTotalCountBatch(folderIds, user);
}
public IReadOnlyList<BaseItem> GetItemList(InternalItemsQuery query, List<BaseItem> parents)
@@ -1516,7 +1780,17 @@ namespace Emby.Server.Implementations.Library
}
}
- return _itemRepository.GetNextUpSeriesKeys(query, dateCutoff);
+ return _nextUpService.GetNextUpSeriesKeys(query, dateCutoff);
+ }
+
+ /// <inheritdoc />
+ public IReadOnlyDictionary<string, MediaBrowser.Controller.Persistence.NextUpEpisodeBatchResult> GetNextUpEpisodesBatch(
+ InternalItemsQuery query,
+ IReadOnlyList<string> seriesKeys,
+ bool includeSpecials,
+ bool includeWatchedForRewatching)
+ {
+ return _nextUpService.GetNextUpEpisodesBatch(query, seriesKeys, includeSpecials, includeWatchedForRewatching);
}
public QueryResult<BaseItem> QueryItems(InternalItemsQuery query)
@@ -1683,6 +1957,25 @@ namespace Emby.Server.Implementations.Library
query.TopParentIds = [Guid.NewGuid()];
}
}
+ else if (parents.Count == 1 && parents.First() is Folder folder
+ && (folder is Playlist || folder is BoxSet)
+ && folder.LinkedChildren.Length > 0)
+ {
+ // Playlists and BoxSets store their contents in LinkedChildren and never
+ // populate AncestorIds for those items, so a recursive AncestorIds query
+ // would return zero rows. Resolve to the linked child IDs up front and
+ // route through the existing indexed ItemIds filter.
+ query.ItemIds = folder.LinkedChildren
+ .Where(lc => lc.ItemId.HasValue && !lc.ItemId.Value.IsEmpty())
+ .Select(lc => lc.ItemId!.Value)
+ .ToArray();
+
+ // Empty linked-children should still return empty rather than scanning everything.
+ if (query.ItemIds.Length == 0)
+ {
+ query.ItemIds = [Guid.NewGuid()];
+ }
+ }
else
{
// We need to be able to query from any arbitrary ancestor up the tree
@@ -1700,13 +1993,19 @@ namespace Emby.Server.Implementations.Library
private void AddUserToQuery(InternalItemsQuery query, User user, bool allowExternalContent = true)
{
+ if (query.User is null)
+ {
+ query.SetUser(user);
+ }
+
if (query.AncestorIds.Length == 0 &&
query.ParentId.IsEmpty() &&
query.ChannelIds.Count == 0 &&
query.TopParentIds.Length == 0 &&
string.IsNullOrEmpty(query.AncestorWithPresentationUniqueKey) &&
string.IsNullOrEmpty(query.SeriesPresentationUniqueKey) &&
- query.ItemIds.Length == 0)
+ query.ItemIds.Length == 0 &&
+ query.OwnerIds.Length == 0)
{
var userViews = UserViewManager.GetUserViews(new UserViewQuery
{
@@ -1725,6 +2024,15 @@ namespace Emby.Server.Implementations.Library
}
}
+ /// <inheritdoc/>
+ public void ConfigureUserAccess(InternalItemsQuery query, User user)
+ {
+ ArgumentNullException.ThrowIfNull(query);
+ ArgumentNullException.ThrowIfNull(user);
+
+ AddUserToQuery(query, user);
+ }
+
private IEnumerable<Guid> GetTopParentIdsForQuery(BaseItem item, User? user)
{
if (item is UserView view)
@@ -1890,6 +2198,44 @@ namespace Emby.Server.Implementations.Library
}
/// <inheritdoc />
+ public IEnumerable<Guid> GetLocalAlternateVersionIds(Video video)
+ {
+ ArgumentNullException.ThrowIfNull(video);
+
+ var linkedIds = _linkedChildrenService.GetLinkedChildrenIds(video.Id, (int)MediaBrowser.Controller.Entities.LinkedChildType.LocalAlternateVersion);
+ if (linkedIds.Count > 0)
+ {
+ return linkedIds;
+ }
+
+ return [];
+ }
+
+ /// <inheritdoc />
+ public IEnumerable<Video> GetLinkedAlternateVersions(Video video)
+ {
+ ArgumentNullException.ThrowIfNull(video);
+
+ var linkedIds = _linkedChildrenService.GetLinkedChildrenIds(video.Id, (int)MediaBrowser.Controller.Entities.LinkedChildType.LinkedAlternateVersion);
+ if (linkedIds.Count > 0)
+ {
+ return linkedIds
+ .Select(id => GetItemById(id))
+ .Where(i => i is not null)
+ .OfType<Video>()
+ .OrderBy(i => i.SortName);
+ }
+
+ return [];
+ }
+
+ /// <inheritdoc />
+ public void UpsertLinkedChild(Guid parentId, Guid childId, MediaBrowser.Controller.Entities.LinkedChildType childType)
+ {
+ _linkedChildrenService.UpsertLinkedChild(parentId, childId, childType);
+ }
+
+ /// <inheritdoc />
public IEnumerable<BaseItem> Sort(IEnumerable<BaseItem> items, User? user, IEnumerable<ItemSortBy> sortBy, SortOrder sortOrder)
{
IOrderedEnumerable<BaseItem>? orderedItems = null;
@@ -1993,10 +2339,49 @@ namespace Emby.Server.Implementations.Library
/// <inheritdoc />
public void CreateItems(IReadOnlyList<BaseItem> items, BaseItem? parent, CancellationToken cancellationToken)
{
- _itemRepository.SaveItems(items, cancellationToken);
-
+ // Resolve and add any local alternate version items that don't exist yet
+ // This ensures they exist in the database when LinkedChildren are processed
+ var allItems = new List<BaseItem>(items);
+ var parentFolder = parent as Folder;
+ var parentCollectionType = parent is not null ? GetTopFolderContentType(parent) : null;
foreach (var item in items)
{
+ if (item is Video video && video.LocalAlternateVersions.Length > 0)
+ {
+ var videoType = video.GetType();
+ foreach (var path in video.LocalAlternateVersions)
+ {
+ if (string.IsNullOrEmpty(path))
+ {
+ continue;
+ }
+
+ // Use the primary video's type for ID calculation to ensure consistency
+ var altId = GetNewItemId(path, videoType);
+ if (GetItemById(altId) is null && !allItems.Any(i => i.Id.Equals(altId)))
+ {
+ // Alternate version doesn't exist, resolve and create it
+ // ensuring it has the same type as the primary video
+ var altVideo = ResolveAlternateVersion(path, videoType, parentFolder, parentCollectionType);
+ if (altVideo is not null)
+ {
+ altVideo.OwnerId = video.Id;
+ altVideo.SetPrimaryVersionId(video.Id);
+ // ResolveAlternateVersion only sees the alternate's primary file.
+ // If the alternate is itself a stack (e.g. 1080p part1 + part2),
+ // detect its parts from sibling files so its AdditionalParts persist.
+ SetAdditionalPartsFromStack(altVideo, path);
+ allItems.Add(altVideo);
+ }
+ }
+ }
+ }
+ }
+
+ _persistenceService.SaveItems(allItems, cancellationToken);
+
+ foreach (var item in allItems)
+ {
RegisterItem(item);
}
@@ -2065,8 +2450,14 @@ namespace Emby.Server.Implementations.Library
var outdated = forceUpdate
? item.ImageInfos.Where(i => i.Path is not null).ToArray()
: item.ImageInfos.Where(ImageNeedsRefresh).ToArray();
- // Skip image processing if current or live tv source
- if (outdated.Length == 0 || item.SourceType != SourceType.Library)
+
+ var parentItem = item.GetParent();
+ var isLiveTvShow = item.SourceType != SourceType.Library &&
+ parentItem is not null &&
+ parentItem.SourceType != SourceType.Library; // not a channel
+
+ // Skip image processing if current or live tv show
+ if (outdated.Length == 0 || isLiveTvShow)
{
RegisterItem(item);
return;
@@ -2144,7 +2535,7 @@ namespace Emby.Server.Implementations.Library
item.ValidateImages();
- await _itemRepository.SaveImagesAsync(item).ConfigureAwait(false);
+ await _persistenceService.SaveImagesAsync(item).ConfigureAwait(false);
RegisterItem(item);
}
@@ -2161,7 +2552,54 @@ namespace Emby.Server.Implementations.Library
item.DateLastSaved = DateTime.UtcNow;
}
- _itemRepository.SaveItems(items, cancellationToken);
+ // Resolve and add any local alternate version items that don't exist yet
+ // This ensures they exist in the database when LinkedChildren are processed
+ var allItems = new List<BaseItem>(items);
+ var parentFolder = parent as Folder;
+ var parentCollectionType = GetTopFolderContentType(parent);
+ foreach (var item in items)
+ {
+ if (item is Video video && video.LocalAlternateVersions.Length > 0)
+ {
+ var videoType = video.GetType();
+ foreach (var path in video.LocalAlternateVersions)
+ {
+ if (string.IsNullOrEmpty(path))
+ {
+ continue;
+ }
+
+ // Use the primary video's type for ID calculation to ensure consistency
+ var altId = GetNewItemId(path, videoType);
+ if (GetItemById(altId) is null && !allItems.Any(i => i.Id.Equals(altId)))
+ {
+ // Alternate version doesn't exist, resolve and create it
+ // ensuring it has the same type as the primary video
+ var altVideo = ResolveAlternateVersion(path, videoType, parentFolder, parentCollectionType);
+ if (altVideo is not null)
+ {
+ altVideo.OwnerId = video.Id;
+ altVideo.SetPrimaryVersionId(video.Id);
+ // ResolveAlternateVersion only sees the alternate's primary file.
+ // If the alternate is itself a stack (e.g. 1080p part1 + part2),
+ // detect its parts from sibling files so its AdditionalParts persist.
+ SetAdditionalPartsFromStack(altVideo, path);
+ allItems.Add(altVideo);
+ }
+ }
+ }
+ }
+ }
+
+ _persistenceService.SaveItems(allItems, cancellationToken);
+
+ foreach (var item in allItems)
+ {
+ if (!items.Contains(item))
+ {
+ RegisterItem(item);
+ }
+ }
if (parent is Folder folder)
{
@@ -2205,7 +2643,7 @@ namespace Emby.Server.Implementations.Library
/// <inheritdoc />
public async Task ReattachUserDataAsync(BaseItem item, CancellationToken cancellationToken)
{
- await _itemRepository.ReattachUserDataAsync(item, cancellationToken).ConfigureAwait(false);
+ await _persistenceService.ReattachUserDataAsync(item, cancellationToken).ConfigureAwait(false);
}
public async Task RunMetadataSavers(BaseItem item, ItemUpdateType updateReason)
@@ -2289,7 +2727,7 @@ namespace Emby.Server.Implementations.Library
if (item is null)
{
- return new List<Folder>();
+ return [];
}
return GetCollectionFoldersInternal(item, allUserRootChildren);
@@ -2833,8 +3271,9 @@ namespace Emby.Server.Implementations.Library
public IEnumerable<BaseItem> FindExtras(BaseItem owner, IReadOnlyList<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService)
{
// Apply .ignore rules
- var filtered = fileSystemChildren.Where(c => !DotIgnoreIgnoreRule.IsIgnored(c, owner)).ToList();
- var ownerVideoInfo = VideoResolver.Resolve(owner.Path, owner.IsFolder, _namingOptions, libraryRoot: owner.ContainingFolderPath);
+ var filtered = fileSystemChildren.Where(c => !_dotIgnoreIgnoreRule.ShouldIgnore(c, owner)).ToList();
+ var isFolder = owner.IsFolder || (owner is Video video && (video.VideoType == VideoType.BluRay || video.VideoType == VideoType.Dvd));
+ var ownerVideoInfo = VideoResolver.Resolve(owner.Path, isFolder, _namingOptions, libraryRoot: owner.ContainingFolderPath);
if (ownerVideoInfo is null)
{
yield break;
@@ -2896,10 +3335,16 @@ namespace Emby.Server.Implementations.Library
extra.ExtraType = extraType;
}
- extra.ParentId = Guid.Empty;
- extra.OwnerId = owner.Id;
- extra.IsInMixedFolder = isInMixedFolder;
- return extra;
+ // Only return items that are actual extras (have ExtraType set)
+ // Note: OwnerId and ParentId are set by RefreshExtras, not here,
+ // so that RefreshExtras can detect when they need updating and set ForceSave.
+ if (extra.ExtraType is not null)
+ {
+ extra.IsInMixedFolder = isInMixedFolder;
+ return extra;
+ }
+
+ return null;
}
}
@@ -2918,7 +3363,7 @@ namespace Emby.Server.Implementations.Library
public IReadOnlyList<PersonInfo> GetPeople(InternalPeopleQuery query)
{
- return _peopleRepository.GetPeople(query);
+ return _peopleRepository.GetPeople(query).Items;
}
public IReadOnlyList<PersonInfo> GetPeople(BaseItem item)
@@ -2939,24 +3384,33 @@ namespace Emby.Server.Implementations.Library
return [];
}
- public IReadOnlyList<Person> GetPeopleItems(InternalPeopleQuery query)
+ public QueryResult<BaseItem> GetPeopleItems(InternalPeopleQuery query)
{
- return _peopleRepository.GetPeopleNames(query)
- .Select(i =>
- {
- try
- {
- return GetPerson(i);
- }
- catch (Exception ex)
+ var queryResult = _peopleRepository.GetPeople(query);
+ var baseItems = queryResult.Items.Select(i =>
{
- _logger.LogError(ex, "Error getting person");
- return null;
- }
- })
- .Where(i => i is not null)
- .Where(i => query.User is null || i!.IsVisible(query.User))
- .ToList()!; // null values are filtered out
+ try
+ {
+ return GetPerson(i.Name);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "error retrieving BaseItem for person: {0}", i.Name);
+ return null;
+ }
+ })
+ .Where(i => i is not null)
+ .Where(i => query.User is null || i!.IsVisible(query.User))
+ .OfType<BaseItem>()
+ .ToList()
+ .AsReadOnly();
+
+ return new QueryResult<BaseItem>
+ {
+ StartIndex = queryResult.StartIndex,
+ TotalRecordCount = queryResult.TotalRecordCount,
+ Items = baseItems,
+ };
}
public IReadOnlyList<string> GetPeopleNames(InternalPeopleQuery query)
@@ -2964,6 +3418,12 @@ namespace Emby.Server.Implementations.Library
return _peopleRepository.GetPeopleNames(query);
}
+ /// <inheritdoc/>
+ public IReadOnlyDictionary<Guid, IReadOnlyList<string>> GetPeopleNamesByItems(IReadOnlyList<Guid> itemIds, IReadOnlyList<string> personTypes)
+ {
+ return _peopleRepository.GetPeopleNamesByItems(itemIds, personTypes);
+ }
+
public void UpdatePeople(BaseItem item, List<PersonInfo> people)
{
UpdatePeopleAsync(item, people, CancellationToken.None).GetAwaiter().GetResult();
@@ -3385,5 +3845,46 @@ namespace Emby.Server.Implementations.Library
_fileSystem.CreateShortcut(lnk, _appHost.ReverseVirtualPath(path));
RemoveContentTypeOverrides(path);
}
+
+ /// <inheritdoc />
+ public async Task RerouteLinkedChildReferencesAsync(Guid fromChildId, Guid toChildId)
+ {
+ var affectedParentIds = _linkedChildrenService.RerouteLinkedChildren(fromChildId, toChildId);
+
+ // Update in-memory LinkedChildren and re-save metadata (NFO) for affected parents
+ foreach (var parentId in affectedParentIds)
+ {
+ if (GetItemById(parentId) is Folder parent)
+ {
+ foreach (var lc in parent.LinkedChildren)
+ {
+ if (lc.ItemId.HasValue && lc.ItemId.Value.Equals(fromChildId))
+ {
+ lc.ItemId = toChildId;
+ }
+ }
+
+ await RunMetadataSavers(parent, ItemUpdateType.MetadataEdit).ConfigureAwait(false);
+ }
+ }
+ }
+
+ /// <inheritdoc />
+ public QueryFiltersLegacy GetQueryFiltersLegacy(InternalItemsQuery query)
+ {
+ if (query.User is not null)
+ {
+ AddUserToQuery(query, query.User);
+ }
+
+ SetTopParentOrAncestorIds(query);
+ return _itemRepository.GetQueryFiltersLegacy(query);
+ }
+
+ /// <inheritdoc />
+ public IReadOnlyList<string> GetMediaStreamLanguages(MediaStreamType mediaStreamType)
+ {
+ return _mediaStreamRepository.GetMediaStreamLanguages(mediaStreamType);
+ }
}
}
diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs
index c667fb0600..97e00177b6 100644
--- a/Emby.Server.Implementations/Library/MediaSourceManager.cs
+++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs
@@ -23,6 +23,8 @@ using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.MediaEncoding;
@@ -126,6 +128,11 @@ namespace Emby.Server.Implementations.Library
return true;
}
+ if (stream.IsVobSubSubtitleStream)
+ {
+ return true;
+ }
+
return false;
}
@@ -170,6 +177,7 @@ namespace Emby.Server.Implementations.Library
public async Task<IReadOnlyList<MediaSourceInfo>> GetPlaybackMediaSources(BaseItem item, User user, bool allowMediaProbe, bool enablePathSubstitution, CancellationToken cancellationToken)
{
var mediaSources = GetStaticMediaSources(item, enablePathSubstitution, user);
+ ResolveSymlinkPaths(mediaSources, enablePathSubstitution);
// If file is strm or main media stream is missing, force a metadata refresh with remote probing
if (allowMediaProbe && mediaSources[0].Type != MediaSourceType.Placeholder
@@ -186,6 +194,7 @@ namespace Emby.Server.Implementations.Library
cancellationToken).ConfigureAwait(false);
mediaSources = GetStaticMediaSources(item, enablePathSubstitution, user);
+ ResolveSymlinkPaths(mediaSources, enablePathSubstitution);
}
var dynamicMediaSources = await GetDynamicMediaSources(item, cancellationToken).ConfigureAwait(false);
@@ -220,7 +229,11 @@ namespace Emby.Server.Implementations.Library
list.Add(source);
}
- return SortMediaSources(list).ToArray();
+ var preferredId = mediaSources.Count > 0 && Guid.TryParse(mediaSources[0].Id, out var topSourceId)
+ ? topSourceId
+ : item.Id;
+
+ return SortMediaSources(list, preferredId).ToArray();
}
/// <inheritdoc />>
@@ -318,6 +331,28 @@ namespace Emby.Server.Implementations.Library
}
}
+ /// <summary>
+ /// Resolves symlinked file paths on the supplied sources to the real on-disk target.
+ /// Skipped when <paramref name="enablePathSubstitution"/> is set because the path may
+ /// already have been rewritten to a UNC/URL meant for the client to consume directly.
+ /// </summary>
+ private static void ResolveSymlinkPaths(IReadOnlyList<MediaSourceInfo> sources, bool enablePathSubstitution)
+ {
+ if (enablePathSubstitution)
+ {
+ return;
+ }
+
+ foreach (var source in sources)
+ {
+ if (source.Protocol == MediaProtocol.File
+ && FileSystemHelper.ResolveLinkTarget(source.Path, returnFinalTarget: true) is { Exists: true } target)
+ {
+ source.Path = target.FullName;
+ }
+ }
+ }
+
private static void SetKeyProperties(IMediaSourceProvider provider, MediaSourceInfo mediaSource)
{
var prefix = provider.GetType().FullName.GetMD5().ToString("N", CultureInfo.InvariantCulture) + LiveStreamIdDelimiter;
@@ -355,6 +390,12 @@ namespace Emby.Server.Implementations.Library
if (user is not null)
{
+ sources = sources
+ .Where(source => !Guid.TryParse(source.Id, out var sourceId)
+ || sourceId.Equals(item.Id)
+ || _libraryManager.GetItemById<BaseItem>(sourceId, user) is not null)
+ .ToArray();
+
foreach (var source in sources)
{
SetDefaultAudioAndSubtitleStreamIndices(item, source, user);
@@ -369,6 +410,59 @@ namespace Emby.Server.Implementations.Library
source.SupportsDirectStream = user.HasPermission(PermissionKind.EnablePlaybackRemuxing);
}
}
+
+ sources = SetAlternateVersionResumeStates(item, sources, user);
+ }
+
+ return sources;
+ }
+
+ /// <summary>
+ /// When the queried item is a primary, moves the most recently played version to the front so
+ /// that resuming without an explicit source selection plays the version that was last watched.
+ /// A directly queried alternate version keeps its own source first. Per-user playback position
+ /// is not surfaced on the source itself; it is carried by each version's own UserData.
+ /// </summary>
+ /// <param name="item">The queried item.</param>
+ /// <param name="sources">The item's media sources.</param>
+ /// <param name="user">The user.</param>
+ /// <returns>The media sources, reordered when a version drives resume.</returns>
+ private IReadOnlyList<MediaSourceInfo> SetAlternateVersionResumeStates(BaseItem item, IReadOnlyList<MediaSourceInfo> sources, User user)
+ {
+ // For a video, multiple sources means alternate versions.
+ if (item is not Video video || sources.Count < 2)
+ {
+ return sources;
+ }
+
+ var versions = video.GetAllVersions();
+ if (versions.Count < 2)
+ {
+ return sources;
+ }
+
+ var userDataByVersion = _userDataManager.GetUserDataBatch(versions, user);
+ var dataBySourceId = new Dictionary<string, UserItemData>(versions.Count, StringComparer.OrdinalIgnoreCase);
+ foreach (var version in versions)
+ {
+ if (userDataByVersion.TryGetValue(version.Id, out var data))
+ {
+ dataBySourceId[version.Id.ToString("N", CultureInfo.InvariantCulture)] = data;
+ }
+ }
+
+ // Reorder only for a resumable (in-progress) version;
+ // a completed version has no position to resume, so it must not be pulled to the front here.
+ var resumeSource = VersionPlaybackSelector.SelectMostRecentlyPlayed(
+ sources,
+ source => source.Id is not null ? dataBySourceId.GetValueOrDefault(source.Id) : null,
+ data => data.PlaybackPositionTicks > 0);
+
+ if (resumeSource is not null && !video.PrimaryVersionId.HasValue && !ReferenceEquals(sources[0], resumeSource))
+ {
+ var reordered = new List<MediaSourceInfo>(sources.Count) { resumeSource };
+ reordered.AddRange(sources.Where(s => !ReferenceEquals(s, resumeSource)));
+ return reordered;
}
return sources;
@@ -423,7 +517,7 @@ namespace Emby.Server.Implementations.Library
MediaStreamSelector.SetSubtitleStreamScores(source.MediaStreams, preferredSubs, user.SubtitleMode, audioLanguage);
}
- private void SetDefaultAudioStreamIndex(MediaSourceInfo source, UserItemData userData, User user, bool allowRememberingSelection)
+ private void SetDefaultAudioStreamIndex(MediaSourceInfo source, UserItemData userData, User user, bool allowRememberingSelection, string originalLanguage)
{
if (userData is not null && userData.AudioStreamIndex.HasValue && user.RememberAudioSelections && allowRememberingSelection)
{
@@ -437,7 +531,38 @@ namespace Emby.Server.Implementations.Library
}
}
- var preferredAudio = NormalizeLanguage(user.AudioLanguagePreference);
+ if (string.Equals(user.AudioLanguagePreference, "OriginalLanguage", StringComparison.OrdinalIgnoreCase))
+ {
+ if (user.PlayDefaultAudioTrack)
+ {
+ source.DefaultAudioStreamIndex = MediaStreamSelector.GetDefaultAudioStreamIndex(
+ source.MediaStreams,
+ NormalizeLanguage(originalLanguage),
+ user.PlayDefaultAudioTrack);
+ return;
+ }
+
+ var originalIndex = source.MediaStreams.FindIndex(i => i.Type == MediaStreamType.Audio && i.IsOriginal);
+
+ if (!string.IsNullOrWhiteSpace(originalLanguage) && originalIndex != -1)
+ {
+ var mediaLanguageOriginal = source.MediaStreams[originalIndex].Language;
+ if (NormalizeLanguage(mediaLanguageOriginal).Contains(NormalizeLanguage(originalLanguage).FirstOrDefault()))
+ {
+ source.DefaultAudioStreamIndex = originalIndex;
+ return;
+ }
+ }
+ else if (originalIndex != -1)
+ {
+ source.DefaultAudioStreamIndex = originalIndex;
+ return;
+ }
+ }
+
+ var preferredAudio = string.Equals(user.AudioLanguagePreference, "OriginalLanguage", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrWhiteSpace(originalLanguage)
+ ? NormalizeLanguage(originalLanguage)
+ : NormalizeLanguage(user.AudioLanguagePreference);
source.DefaultAudioStreamIndex = MediaStreamSelector.GetDefaultAudioStreamIndex(source.MediaStreams, preferredAudio, user.PlayDefaultAudioTrack);
if (user.PlayDefaultAudioTrack)
@@ -462,7 +587,9 @@ namespace Emby.Server.Implementations.Library
var allowRememberingSelection = item is null || item.EnableRememberingTrackSelections;
- SetDefaultAudioStreamIndex(source, userData, user, allowRememberingSelection);
+ var originalLanguage = item?.GetInheritedOriginalLanguage();
+
+ SetDefaultAudioStreamIndex(source, userData, user, allowRememberingSelection, originalLanguage);
SetDefaultSubtitleStreamIndex(source, userData, user, allowRememberingSelection);
}
else if (mediaType == MediaType.Audio)
@@ -476,24 +603,32 @@ namespace Emby.Server.Implementations.Library
}
}
- private static IEnumerable<MediaSourceInfo> SortMediaSources(IEnumerable<MediaSourceInfo> sources)
+ private static IEnumerable<MediaSourceInfo> SortMediaSources(IEnumerable<MediaSourceInfo> sources, Guid preferredItemId = default)
{
- return sources.OrderBy(i =>
- {
- if (i.VideoType.HasValue && i.VideoType.Value == VideoType.VideoFile)
+ // The source belonging to the queried item sorts first so it stays the default that gets played.
+ var preferredId = preferredItemId.IsEmpty()
+ ? null
+ : preferredItemId.ToString("N", CultureInfo.InvariantCulture);
+
+ return sources
+ .OrderByDescending(i => preferredId is not null && string.Equals(i.Id, preferredId, StringComparison.OrdinalIgnoreCase))
+ .ThenBy(i =>
{
- return 0;
- }
+ if (i.VideoType.HasValue && i.VideoType.Value == VideoType.VideoFile)
+ {
+ return 0;
+ }
- return 1;
- }).ThenBy(i => i.Video3DFormat.HasValue ? 1 : 0)
- .ThenByDescending(i =>
- {
- var stream = i.VideoStream;
+ return 1;
+ })
+ .ThenBy(i => i.Video3DFormat.HasValue ? 1 : 0)
+ .ThenByDescending(i =>
+ {
+ var stream = i.VideoStream;
- return stream?.Width ?? 0;
- })
- .Where(i => i.Type != MediaSourceType.Placeholder);
+ return stream?.Width ?? 0;
+ })
+ .Where(i => i.Type != MediaSourceType.Placeholder);
}
public async Task<Tuple<LiveStreamResponse, IDirectStreamProvider>> OpenLiveStreamInternal(LiveStreamRequest request, CancellationToken cancellationToken)
diff --git a/Emby.Server.Implementations/Library/PathExtensions.cs b/Emby.Server.Implementations/Library/PathExtensions.cs
index fc63251ad0..7591359ea4 100644
--- a/Emby.Server.Implementations/Library/PathExtensions.cs
+++ b/Emby.Server.Implementations/Library/PathExtensions.cs
@@ -45,7 +45,7 @@ namespace Emby.Server.Implementations.Library
'[' => ']',
'(' => ')',
'{' => '}',
- _ => '\0'
+ _ => '\0'
};
if (attributeCloser != '\0' && (str[attributeEnd] == '=' || str[attributeEnd] == '-'))
{
@@ -70,6 +70,16 @@ namespace Emby.Server.Implementations.Library
return match ? imdbId.ToString() : null;
}
+ // Allow tmdb as an alias for tmdbid
+ if (attribute.Equals("tmdbid", StringComparison.OrdinalIgnoreCase))
+ {
+ var tmdbValue = str.GetAttributeValue("tmdb");
+ if (tmdbValue is not null)
+ {
+ return tmdbValue;
+ }
+ }
+
return null;
}
diff --git a/Emby.Server.Implementations/Library/PathManager.cs b/Emby.Server.Implementations/Library/PathManager.cs
index a9b7a1274b..fad948ad97 100644
--- a/Emby.Server.Implementations/Library/PathManager.cs
+++ b/Emby.Server.Implementations/Library/PathManager.cs
@@ -6,6 +6,7 @@ using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.IO;
+using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Library;
@@ -14,18 +15,22 @@ namespace Emby.Server.Implementations.Library;
/// </summary>
public class PathManager : IPathManager
{
+ private readonly ILogger<PathManager> _logger;
private readonly IServerConfigurationManager _config;
private readonly IApplicationPaths _appPaths;
/// <summary>
/// Initializes a new instance of the <see cref="PathManager"/> class.
/// </summary>
+ /// <param name="logger">The logger.</param>
/// <param name="config">The server configuration manager.</param>
/// <param name="appPaths">The application paths.</param>
public PathManager(
+ ILogger<PathManager> logger,
IServerConfigurationManager config,
IApplicationPaths appPaths)
{
+ _logger = logger;
_config = config;
_appPaths = appPaths;
}
@@ -35,31 +40,43 @@ public class PathManager : IPathManager
private string AttachmentCachePath => Path.Combine(_appPaths.DataPath, "attachments");
/// <inheritdoc />
- public string GetAttachmentPath(string mediaSourceId, string fileName)
+ public string? GetAttachmentPath(string mediaSourceId, string fileName)
{
- return Path.Combine(GetAttachmentFolderPath(mediaSourceId), fileName);
+ var folder = GetAttachmentFolderPath(mediaSourceId);
+ return folder is null ? null : Path.Combine(folder, fileName);
}
/// <inheritdoc />
- public string GetAttachmentFolderPath(string mediaSourceId)
+ public string? GetAttachmentFolderPath(string mediaSourceId)
{
- var id = Guid.Parse(mediaSourceId).ToString("D", CultureInfo.InvariantCulture).AsSpan();
+ if (!Guid.TryParse(mediaSourceId, out var parsed))
+ {
+ _logger.LogDebug("MediaSource Id '{MediaSourceId}' is not a GUID; no on-disk attachment folder.", mediaSourceId);
+ return null;
+ }
+ var id = parsed.ToString("D", CultureInfo.InvariantCulture).AsSpan();
return Path.Join(AttachmentCachePath, id[..2], id);
}
/// <inheritdoc />
- public string GetSubtitleFolderPath(string mediaSourceId)
+ public string? GetSubtitleFolderPath(string mediaSourceId)
{
- var id = Guid.Parse(mediaSourceId).ToString("D", CultureInfo.InvariantCulture).AsSpan();
+ if (!Guid.TryParse(mediaSourceId, out var parsed))
+ {
+ _logger.LogDebug("MediaSource Id '{MediaSourceId}' is not a GUID; no on-disk subtitle folder.", mediaSourceId);
+ return null;
+ }
+ var id = parsed.ToString("D", CultureInfo.InvariantCulture).AsSpan();
return Path.Join(SubtitleCachePath, id[..2], id);
}
/// <inheritdoc />
- public string GetSubtitlePath(string mediaSourceId, int streamIndex, string extension)
+ public string? GetSubtitlePath(string mediaSourceId, int streamIndex, string extension)
{
- return Path.Combine(GetSubtitleFolderPath(mediaSourceId), streamIndex.ToString(CultureInfo.InvariantCulture) + extension);
+ var folder = GetSubtitleFolderPath(mediaSourceId);
+ return folder is null ? null : Path.Combine(folder, streamIndex.ToString(CultureInfo.InvariantCulture) + extension);
}
/// <inheritdoc />
@@ -90,12 +107,27 @@ public class PathManager : IPathManager
public IReadOnlyList<string> GetExtractedDataPaths(BaseItem item)
{
var mediaSourceId = item.Id.ToString("N", CultureInfo.InvariantCulture);
- return [
- GetAttachmentFolderPath(mediaSourceId),
- GetSubtitleFolderPath(mediaSourceId),
- GetTrickplayDirectory(item, false),
- GetTrickplayDirectory(item, true),
- GetChapterImageFolderPath(item)
- ];
+ List<string> paths = [];
+ var attachmentFolder = GetAttachmentFolderPath(mediaSourceId);
+ if (attachmentFolder is not null)
+ {
+ paths.Add(attachmentFolder);
+ }
+
+ var subtitleFolder = GetSubtitleFolderPath(mediaSourceId);
+ if (subtitleFolder is not null)
+ {
+ paths.Add(subtitleFolder);
+ }
+
+ paths.Add(GetTrickplayDirectory(item, false));
+ if (!string.IsNullOrEmpty(item.Path))
+ {
+ paths.Add(GetTrickplayDirectory(item, true));
+ }
+
+ paths.Add(GetChapterImageFolderPath(item));
+
+ return paths;
}
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs
index 1e885aad6e..7d51a0daa0 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
#pragma warning disable CS1591
using System;
@@ -18,7 +16,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Books
{
private readonly string[] _validExtensions = { ".azw", ".azw3", ".cb7", ".cbr", ".cbt", ".cbz", ".epub", ".mobi", ".pdf" };
- protected override Book Resolve(ItemResolveArgs args)
+ protected override Book? Resolve(ItemResolveArgs args)
{
var collectionType = args.GetCollectionType();
@@ -47,13 +45,14 @@ namespace Emby.Server.Implementations.Library.Resolvers.Books
Path = args.Path,
Name = result.Name ?? string.Empty,
IndexNumber = result.Index,
+ ParentIndexNumber = result.ParentIndex,
ProductionYear = result.Year,
SeriesName = result.SeriesName ?? Path.GetFileName(Path.GetDirectoryName(args.Path)),
IsInMixedFolder = true,
};
}
- private Book GetBook(ItemResolveArgs args)
+ private Book? GetBook(ItemResolveArgs args)
{
var bookFiles = args.FileSystemChildren.Where(f =>
{
@@ -78,6 +77,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Books
Path = bookFiles[0].FullName,
Name = result.Name ?? string.Empty,
IndexNumber = result.Index,
+ ParentIndexNumber = result.ParentIndex,
ProductionYear = result.Year,
SeriesName = result.SeriesName ?? string.Empty,
};
diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
index 98e8f5350b..68b66ab7f5 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
@@ -28,15 +28,16 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
public partial class MovieResolver : BaseVideoResolver<Video>, IMultiItemResolver
{
private readonly IImageProcessor _imageProcessor;
+ private readonly VideoListResolver _videoListResolver;
- private static readonly CollectionType[] _validCollectionTypes = new[]
- {
+ private static readonly CollectionType[] _validCollectionTypes =
+ [
CollectionType.movies,
CollectionType.homevideos,
CollectionType.musicvideos,
CollectionType.tvshows,
CollectionType.photos
- };
+ ];
/// <summary>
/// Initializes a new instance of the <see cref="MovieResolver"/> class.
@@ -45,10 +46,12 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
/// <param name="logger">The logger.</param>
/// <param name="namingOptions">The naming options.</param>
/// <param name="directoryService">The directory service.</param>
- public MovieResolver(IImageProcessor imageProcessor, ILogger<MovieResolver> logger, NamingOptions namingOptions, IDirectoryService directoryService)
+ /// <param name="videoListResolver">The video list resolver.</param>
+ public MovieResolver(IImageProcessor imageProcessor, ILogger<MovieResolver> logger, NamingOptions namingOptions, IDirectoryService directoryService, VideoListResolver videoListResolver)
: base(logger, namingOptions, directoryService)
{
_imageProcessor = imageProcessor;
+ _videoListResolver = videoListResolver;
}
/// <summary>
@@ -228,7 +231,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
if (collectionType == CollectionType.tvshows)
{
- return ResolveVideos<Episode>(parent, files, false, collectionType, true);
+ return ResolveVideos<Episode>(parent, files, true, collectionType, true);
}
return null;
@@ -274,7 +277,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
.Where(f => f is not null)
.ToList();
- var resolverResult = VideoListResolver.Resolve(videoInfos, NamingOptions, supportMultiEditions, parseName, parent.ContainingFolderPath);
+ var resolverResult = _videoListResolver.Resolve(videoInfos, supportMultiEditions, parseName, parent.ContainingFolderPath, collectionType);
var result = new MultiItemResolverResult
{
@@ -302,7 +305,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
ProductionYear = video.Year,
Name = parseName ? video.Name : firstVideo.Name,
AdditionalParts = additionalParts,
- LocalAlternateVersions = video.AlternateVersions.Select(i => i.Path).ToArray()
+ LocalAlternateVersions = video.AlternateVersions.Select(av => av.Files[0].Path).ToArray()
};
SetVideoType(videoItem, firstVideo);
@@ -331,9 +334,13 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
for (var j = 0; j < current.AlternateVersions.Count; j++)
{
- if (ContainsFile(current.AlternateVersions[j], file))
+ var alternate = current.AlternateVersions[j];
+ for (var k = 0; k < alternate.Files.Count; k++)
{
- return true;
+ if (ContainsFile(alternate.Files[k], file))
+ {
+ return true;
+ }
}
}
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs b/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs
index 14798dda65..74c1f69616 100644
--- a/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs
@@ -1,6 +1,7 @@
#nullable disable
using System;
+using System.Collections.Generic;
using System.IO;
using System.Linq;
using Jellyfin.Data.Enums;
@@ -46,7 +47,16 @@ namespace Emby.Server.Implementations.Library.Resolvers
}
// It's a directory-based playlist if the directory contains a playlist file
- var filePaths = Directory.EnumerateFiles(args.Path, "*", new EnumerationOptions { IgnoreInaccessible = true });
+ IEnumerable<string> filePaths;
+ try
+ {
+ filePaths = Directory.EnumerateFiles(args.Path, "*", new EnumerationOptions { IgnoreInaccessible = true });
+ }
+ catch (IOException)
+ {
+ return null;
+ }
+
if (filePaths.Any(f => f.EndsWith(PlaylistXmlSaver.DefaultPlaylistFilename, StringComparison.OrdinalIgnoreCase)))
{
return new Playlist
diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs
index 5fd23c9f50..85bf20cc2a 100644
--- a/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs
@@ -1,8 +1,10 @@
#nullable disable
using System;
+using System.IO;
using System.Linq;
using Emby.Naming.Common;
+using Emby.Server.Implementations.Library;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
@@ -81,10 +83,34 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
episode.ParentIndexNumber = 1;
}
+ SetProviderIdFromPath(episode, args.Path);
+
return episode;
}
return null;
}
+
+ /// <summary>
+ /// Sets provider ids from the episode file name.
+ /// </summary>
+ /// <param name="item">The episode.</param>
+ /// <param name="path">The episode file path.</param>
+ private static void SetProviderIdFromPath(Episode item, string path)
+ {
+ var justName = Path.GetFileNameWithoutExtension(path.AsSpan());
+
+ var imdbId = justName.GetAttributeValue("imdbid");
+ item.TrySetProviderId(MetadataProvider.Imdb, imdbId);
+
+ var tvdbId = justName.GetAttributeValue("tvdbid");
+ item.TrySetProviderId(MetadataProvider.Tvdb, tvdbId);
+
+ var tvmazeId = justName.GetAttributeValue("tvmazeid");
+ item.TrySetProviderId(MetadataProvider.TvMaze, tvmazeId);
+
+ var tmdbId = justName.GetAttributeValue("tmdbid");
+ item.TrySetProviderId(MetadataProvider.Tmdb, tmdbId);
+ }
}
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
index 6cb63a28a2..6e9a38fd34 100644
--- a/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
@@ -1,10 +1,15 @@
#nullable disable
+using System;
using System.Globalization;
+using System.IO;
+using System.Linq;
using Emby.Naming.Common;
using Emby.Naming.TV;
+using Emby.Server.Implementations.Library;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Globalization;
using Microsoft.Extensions.Logging;
@@ -77,6 +82,14 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
return null;
}
+
+ var hasAnyVideo = Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories)
+ .Any(file => _namingOptions.VideoFileExtensions.Contains(Path.GetExtension(file)));
+
+ if (!hasAnyVideo)
+ {
+ return null;
+ }
}
if (season.IndexNumber.HasValue && string.IsNullOrEmpty(season.Name))
@@ -91,10 +104,31 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
args.LibraryOptions.PreferredMetadataLanguage);
}
+ SetProviderIdFromPath(season, path);
+
return season;
}
return null;
}
+
+ /// <summary>
+ /// Sets provider ids from the season folder name.
+ /// </summary>
+ /// <param name="item">The season.</param>
+ /// <param name="path">The season folder path.</param>
+ private static void SetProviderIdFromPath(Season item, string path)
+ {
+ var justName = Path.GetFileName(path.AsSpan());
+
+ var tvdbId = justName.GetAttributeValue("tvdbid");
+ item.TrySetProviderId(MetadataProvider.Tvdb, tvdbId);
+
+ var tvmazeId = justName.GetAttributeValue("tvmazeid");
+ item.TrySetProviderId(MetadataProvider.TvMaze, tvmazeId);
+
+ var tmdbId = justName.GetAttributeValue("tmdbid");
+ item.TrySetProviderId(MetadataProvider.Tmdb, tmdbId);
+ }
}
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
index c81a0adb89..8d40eab006 100644
--- a/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
@@ -31,7 +31,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
/// </summary>
/// <param name="logger">The logger.</param>
/// <param name="namingOptions">The naming options.</param>
- public SeriesResolver(ILogger<SeriesResolver> logger, NamingOptions namingOptions)
+ public SeriesResolver(ILogger<SeriesResolver> logger, NamingOptions namingOptions)
{
_logger = logger;
_namingOptions = namingOptions;
@@ -57,6 +57,11 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
return null;
}
+ if (args.Parent is not null && args.Parent.IsRoot)
+ {
+ return null;
+ }
+
var seriesInfo = Naming.TV.SeriesResolver.Resolve(_namingOptions, args.Path);
var collectionType = args.GetCollectionType();
@@ -69,7 +74,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
return new Series
{
Path = args.Path,
- Name = seriesInfo.Name
+ Name = seriesInfo.Name,
+ ProductionYear = seriesInfo.Year
};
}
}
diff --git a/Emby.Server.Implementations/Library/Search/SearchManager.cs b/Emby.Server.Implementations/Library/Search/SearchManager.cs
new file mode 100644
index 0000000000..a5be3f07bd
--- /dev/null
+++ b/Emby.Server.Implementations/Library/Search/SearchManager.cs
@@ -0,0 +1,458 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Jellyfin.Data.Enums;
+using Jellyfin.Database.Implementations;
+using Jellyfin.Database.Implementations.Entities;
+using Jellyfin.Extensions;
+using MediaBrowser.Controller.Dto;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Persistence;
+using MediaBrowser.Model.Querying;
+using MediaBrowser.Model.Search;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Logging;
+
+namespace Emby.Server.Implementations.Library.Search;
+
+/// <summary>
+/// Manages search providers and orchestrates search operations.
+/// </summary>
+public class SearchManager : ISearchManager
+{
+ private readonly ILibraryManager _libraryManager;
+ private readonly IUserManager _userManager;
+ private readonly IDbContextFactory<JellyfinDbContext> _dbProvider;
+ private readonly IItemQueryHelpers _queryHelpers;
+ private readonly ILogger<SearchManager> _logger;
+ private IExternalSearchProvider[] _externalProviders = [];
+ private IInternalSearchProvider[] _internalProviders = [];
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SearchManager"/> class.
+ /// </summary>
+ /// <param name="libraryManager">The library manager.</param>
+ /// <param name="userManager">The user manager.</param>
+ /// <param name="dbProvider">The database context factory.</param>
+ /// <param name="queryHelpers">The shared item query helpers.</param>
+ /// <param name="logger">The logger.</param>
+ public SearchManager(
+ ILibraryManager libraryManager,
+ IUserManager userManager,
+ IDbContextFactory<JellyfinDbContext> dbProvider,
+ IItemQueryHelpers queryHelpers,
+ ILogger<SearchManager> logger)
+ {
+ _libraryManager = libraryManager;
+ _userManager = userManager;
+ _dbProvider = dbProvider;
+ _queryHelpers = queryHelpers;
+ _logger = logger;
+ }
+
+ /// <inheritdoc/>
+ public void AddParts(IEnumerable<ISearchProvider> providers)
+ {
+ var allProviders = providers.OrderBy(p => p.Priority).ToArray();
+
+ _externalProviders = allProviders.OfType<IExternalSearchProvider>().ToArray();
+ _internalProviders = allProviders.OfType<IInternalSearchProvider>().ToArray();
+
+ _logger.LogInformation(
+ "Registered {ExternalCount} external search providers: {ExternalProviders}. Fallback providers: {FallbackProviders}",
+ _externalProviders.Length,
+ string.Join(", ", _externalProviders.Select(p => $"{p.Name} (priority {p.Priority})")),
+ string.Join(", ", _internalProviders.Select(p => $"{p.Name} (priority {p.Priority})")));
+ }
+
+ /// <inheritdoc/>
+ public IReadOnlyList<ISearchProvider> GetProviders()
+ {
+ return [.. _externalProviders, .. _internalProviders];
+ }
+
+ /// <inheritdoc/>
+ public async Task<IReadOnlyList<SearchResult>> GetSearchResultsAsync(
+ SearchProviderQuery query,
+ CancellationToken cancellationToken = default)
+ {
+ ArgumentNullException.ThrowIfNull(query);
+ ArgumentException.ThrowIfNullOrWhiteSpace(query.SearchTerm);
+
+ var searchTerm = query.SearchTerm.Trim().RemoveDiacritics();
+
+ var externalTask = CollectFromProvidersAsync(_externalProviders, query, searchTerm, cancellationToken);
+ var internalTask = _internalProviders.Length > 0
+ ? CollectFromProvidersAsync(_internalProviders, query, searchTerm, cancellationToken)
+ : Task.FromResult<IReadOnlyList<SearchResult>>([]);
+
+ await Task.WhenAll(externalTask, internalTask).ConfigureAwait(false);
+
+ var externalResults = await externalTask.ConfigureAwait(false);
+ var fromExternal = externalResults.Count > 0;
+ IReadOnlyList<SearchResult> results;
+ if (fromExternal)
+ {
+ results = externalResults;
+ }
+ else
+ {
+ results = await internalTask.ConfigureAwait(false);
+ if (_internalProviders.Length > 0)
+ {
+ _logger.LogDebug("No results from external providers, using internal provider results");
+ }
+ }
+
+ // Internal providers apply user-access filtering inline in their queries. External
+ // providers don't know about user permissions, so they may return IDs from hidden
+ // libraries or items the user is otherwise blocked from. Run the post-filter only
+ // when results came from externals to close that gap. The Items controller's second
+ // roundtrip via folder.GetItems applies most of these again, but it does not restrict
+ // by TopParentIds when ItemIds is set.
+ if (fromExternal && results.Count > 0 && query.UserId.HasValue && !query.UserId.Value.IsEmpty())
+ {
+ var user = _userManager.GetUserById(query.UserId.Value);
+ if (user is not null)
+ {
+ results = await FilterByUserAccessAsync(results, user, cancellationToken).ConfigureAwait(false);
+ }
+ }
+
+ return results;
+ }
+
+ private async Task<IReadOnlyList<SearchResult>> FilterByUserAccessAsync(
+ IReadOnlyList<SearchResult> candidates,
+ User user,
+ CancellationToken cancellationToken)
+ {
+ // SetUser populates parental rating + blocked/allowed tags. ConfigureUserAccess populates
+ // TopParentIds for the user's accessible libraries — we call it before assigning ItemIds
+ // because LibraryManager.AddUserToQuery skips TopParentIds when ItemIds is non-empty.
+ var accessFilter = new InternalItemsQuery(user);
+ _libraryManager.ConfigureUserAccess(accessFilter, user);
+
+ Guid[] candidateIds = [.. candidates.Select(c => c.ItemId)];
+
+ var dbContext = await _dbProvider.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
+ await using (dbContext.ConfigureAwait(false))
+ {
+ var baseQuery = dbContext.BaseItems
+ .AsNoTracking()
+ .WhereOneOrMany(candidateIds, e => e.Id);
+
+ baseQuery = _queryHelpers.ApplyAccessFiltering(dbContext, baseQuery, accessFilter);
+
+ var allowedCount = await baseQuery.CountAsync(cancellationToken).ConfigureAwait(false);
+ if (allowedCount == candidates.Count)
+ {
+ return candidates;
+ }
+
+ var allowedIds = await baseQuery
+ .Select(e => e.Id)
+ .ToHashSetAsync(cancellationToken)
+ .ConfigureAwait(false);
+
+ var filtered = candidates.Where(c => allowedIds.Contains(c.ItemId)).ToList();
+ if (filtered.Count < candidates.Count)
+ {
+ _logger.LogDebug(
+ "Dropped {Dropped} of {Total} search candidates due to user access filtering",
+ candidates.Count - filtered.Count,
+ candidates.Count);
+ }
+
+ return filtered;
+ }
+ }
+
+ /// <inheritdoc/>
+ public async Task<QueryResult<SearchHintInfo>> GetSearchHintsAsync(SearchQuery query, CancellationToken cancellationToken = default)
+ {
+ ArgumentNullException.ThrowIfNull(query);
+ ArgumentException.ThrowIfNullOrWhiteSpace(query.SearchTerm);
+
+ var providerQuery = BuildProviderQuery(query);
+ var candidates = await GetSearchResultsAsync(providerQuery, cancellationToken).ConfigureAwait(false);
+ if (candidates.Count == 0)
+ {
+ return new QueryResult<SearchHintInfo>();
+ }
+
+ var candidateScores = BuildScoreLookup(candidates);
+ var user = query.UserId.IsEmpty() ? null : _userManager.GetUserById(query.UserId);
+
+ var excludeItemTypes = BuildExcludeItemTypes(query);
+ var includeItemTypes = BuildIncludeItemTypes(query);
+
+ var internalQuery = new InternalItemsQuery(user)
+ {
+ ItemIds = candidateScores.Keys.ToArray(),
+ ExcludeItemTypes = excludeItemTypes.ToArray(),
+ IncludeItemTypes = includeItemTypes.Count > 0 ? includeItemTypes.ToArray() : [],
+ MediaTypes = query.MediaTypes.ToArray(),
+ IncludeItemsByName = !query.ParentId.HasValue,
+ ParentId = query.ParentId ?? Guid.Empty,
+ Recursive = true,
+ IsKids = query.IsKids,
+ IsMovie = query.IsMovie,
+ IsNews = query.IsNews,
+ IsSeries = query.IsSeries,
+ IsSports = query.IsSports,
+ DtoOptions = new DtoOptions
+ {
+ Fields =
+ [
+ ItemFields.AirTime,
+ ItemFields.DateCreated,
+ ItemFields.ChannelInfo,
+ ItemFields.ParentId
+ ]
+ }
+ };
+
+ // MusicArtist items are "ItemsByName" entities - virtual items that aggregate content by artist name
+ // rather than being stored as regular library items. They require special handling:
+ // 1. Convert ParentId to AncestorIds (to filter by library folder)
+ // 2. Set IncludeItemsByName = true (to include these virtual items in results)
+ // 3. Clear IncludeItemTypes (GetAllArtists handles type filtering internally)
+ // 4. Use GetAllArtists() instead of GetItemList() to query the artist index
+ IReadOnlyList<BaseItem> items;
+ if (internalQuery.IncludeItemTypes.Length == 1 && internalQuery.IncludeItemTypes[0] == BaseItemKind.MusicArtist)
+ {
+ if (!internalQuery.ParentId.IsEmpty())
+ {
+ internalQuery.AncestorIds = [internalQuery.ParentId];
+ internalQuery.ParentId = Guid.Empty;
+ }
+
+ internalQuery.IncludeItemsByName = true;
+ internalQuery.IncludeItemTypes = [];
+ items = _libraryManager.GetAllArtists(internalQuery).Items.Select(i => i.Item).ToList();
+ }
+ else
+ {
+ items = _libraryManager.GetItemList(internalQuery);
+ }
+
+ var orderedResults = items
+ .Select(item => new SearchHintInfo { Item = item })
+ .OrderByDescending(hint => candidateScores.GetValueOrDefault(hint.Item.Id, 0f))
+ .ToList();
+
+ var totalCount = orderedResults.Count;
+
+ if (query.StartIndex.HasValue)
+ {
+ orderedResults = orderedResults.Skip(query.StartIndex.Value).ToList();
+ }
+
+ if (query.Limit.HasValue)
+ {
+ orderedResults = orderedResults.Take(query.Limit.Value).ToList();
+ }
+
+ return new QueryResult<SearchHintInfo>(query.StartIndex, totalCount, orderedResults);
+ }
+
+ private async Task<IReadOnlyList<SearchResult>> CollectFromProvidersAsync(
+ IEnumerable<ISearchProvider> providers,
+ SearchProviderQuery providerQuery,
+ string searchTerm,
+ CancellationToken cancellationToken)
+ {
+ var requestedLimit = providerQuery.Limit ?? 100;
+ var applicable = providers.Where(p => p.CanSearch(providerQuery)).ToArray();
+ if (applicable.Length == 0)
+ {
+ return [];
+ }
+
+ var perProvider = await Task.WhenAll(
+ applicable.Select(p => CollectFromProviderAsync(p, providerQuery, searchTerm, requestedLimit, cancellationToken)))
+ .ConfigureAwait(false);
+
+ var bestScores = new Dictionary<Guid, float>();
+ foreach (var providerResults in perProvider)
+ {
+ foreach (var result in providerResults)
+ {
+ UpdateBestScore(bestScores, result);
+ }
+ }
+
+ return bestScores
+ .Select(kvp => new SearchResult(kvp.Key, kvp.Value))
+ .OrderByDescending(r => r.Score)
+ .Take(requestedLimit)
+ .ToList();
+ }
+
+ private async Task<IReadOnlyList<SearchResult>> CollectFromProviderAsync(
+ ISearchProvider provider,
+ SearchProviderQuery providerQuery,
+ string searchTerm,
+ int requestedLimit,
+ CancellationToken cancellationToken)
+ {
+ try
+ {
+ var results = provider is IExternalSearchProvider externalProvider
+ ? await CollectFromExternalProviderAsync(externalProvider, providerQuery, requestedLimit, cancellationToken).ConfigureAwait(false)
+ : await provider.SearchAsync(providerQuery, cancellationToken).ConfigureAwait(false);
+
+ _logger.LogDebug(
+ "Provider {Provider} returned {Count} candidates for search term '{SearchTerm}'",
+ provider.Name,
+ results.Count,
+ searchTerm);
+ return results;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogWarning(ex, "Search provider {Provider} failed for term '{SearchTerm}'", provider.Name, searchTerm);
+ return [];
+ }
+ }
+
+ private static async Task<IReadOnlyList<SearchResult>> CollectFromExternalProviderAsync(
+ IExternalSearchProvider provider,
+ SearchProviderQuery providerQuery,
+ int requestedLimit,
+ CancellationToken cancellationToken)
+ {
+ var results = new List<SearchResult>();
+ await foreach (var result in provider.SearchAsync(providerQuery, cancellationToken).ConfigureAwait(false))
+ {
+ results.Add(result);
+ if (results.Count >= requestedLimit)
+ {
+ break;
+ }
+ }
+
+ return results;
+ }
+
+ private static void UpdateBestScore(Dictionary<Guid, float> bestScores, SearchResult result)
+ {
+ if (!bestScores.TryGetValue(result.ItemId, out var existingScore) || result.Score > existingScore)
+ {
+ bestScores[result.ItemId] = result.Score;
+ }
+ }
+
+ private static Dictionary<Guid, float> BuildScoreLookup(IReadOnlyList<SearchResult> results)
+ {
+ var lookup = new Dictionary<Guid, float>(results.Count);
+ foreach (var result in results)
+ {
+ lookup[result.ItemId] = result.Score;
+ }
+
+ return lookup;
+ }
+
+ private static SearchProviderQuery BuildProviderQuery(SearchQuery query)
+ {
+ var excludeItemTypes = BuildExcludeItemTypes(query);
+ var includeItemTypes = BuildIncludeItemTypes(query);
+
+ // Remove any excluded types from includes
+ if (includeItemTypes.Count > 0 && excludeItemTypes.Count > 0)
+ {
+ includeItemTypes.RemoveAll(excludeItemTypes.Contains);
+ }
+
+ return new SearchProviderQuery
+ {
+ SearchTerm = query.SearchTerm,
+ UserId = query.UserId.IsEmpty() ? null : query.UserId,
+ IncludeItemTypes = includeItemTypes.ToArray(),
+ ExcludeItemTypes = excludeItemTypes.ToArray(),
+ MediaTypes = query.MediaTypes.ToArray(),
+ Limit = query.Limit,
+ ParentId = query.ParentId
+ };
+ }
+
+ private static List<BaseItemKind> BuildExcludeItemTypes(SearchQuery query)
+ {
+ var excludeItemTypes = query.ExcludeItemTypes.ToList();
+
+ excludeItemTypes.Add(BaseItemKind.Year);
+ excludeItemTypes.Add(BaseItemKind.Folder);
+ excludeItemTypes.Add(BaseItemKind.CollectionFolder);
+
+ if (!query.IncludeGenres)
+ {
+ AddIfMissing(excludeItemTypes, BaseItemKind.Genre);
+ AddIfMissing(excludeItemTypes, BaseItemKind.MusicGenre);
+ }
+
+ if (!query.IncludePeople)
+ {
+ AddIfMissing(excludeItemTypes, BaseItemKind.Person);
+ }
+
+ if (!query.IncludeStudios)
+ {
+ AddIfMissing(excludeItemTypes, BaseItemKind.Studio);
+ }
+
+ if (!query.IncludeArtists)
+ {
+ AddIfMissing(excludeItemTypes, BaseItemKind.MusicArtist);
+ }
+
+ return excludeItemTypes;
+ }
+
+ private static List<BaseItemKind> BuildIncludeItemTypes(SearchQuery query)
+ {
+ var includeItemTypes = query.IncludeItemTypes.ToList();
+ if (query.IncludeMedia)
+ {
+ return includeItemTypes;
+ }
+
+ if (query.IncludeGenres && IsEmptyOrContains(includeItemTypes, BaseItemKind.Genre))
+ {
+ AddIfMissing(includeItemTypes, BaseItemKind.Genre);
+ AddIfMissing(includeItemTypes, BaseItemKind.MusicGenre);
+ }
+
+ if (query.IncludePeople && IsEmptyOrContains(includeItemTypes, BaseItemKind.Person))
+ {
+ AddIfMissing(includeItemTypes, BaseItemKind.Person);
+ }
+
+ if (query.IncludeStudios && IsEmptyOrContains(includeItemTypes, BaseItemKind.Studio))
+ {
+ AddIfMissing(includeItemTypes, BaseItemKind.Studio);
+ }
+
+ if (query.IncludeArtists && IsEmptyOrContains(includeItemTypes, BaseItemKind.MusicArtist))
+ {
+ AddIfMissing(includeItemTypes, BaseItemKind.MusicArtist);
+ }
+
+ return includeItemTypes;
+ }
+
+ private static bool IsEmptyOrContains(List<BaseItemKind> list, BaseItemKind value)
+ => list.Count == 0 || list.Contains(value);
+
+ private static void AddIfMissing(List<BaseItemKind> list, BaseItemKind value)
+ {
+ if (!list.Contains(value))
+ {
+ list.Add(value);
+ }
+ }
+}
diff --git a/Emby.Server.Implementations/Library/Search/SqlSearchProvider.cs b/Emby.Server.Implementations/Library/Search/SqlSearchProvider.cs
new file mode 100644
index 0000000000..bc766f1c8c
--- /dev/null
+++ b/Emby.Server.Implementations/Library/Search/SqlSearchProvider.cs
@@ -0,0 +1,230 @@
+#pragma warning disable RS0030 // Do not use banned APIs
+#pragma warning disable CA1862 // Use the 'StringComparison' method overloads to perform case-insensitive string comparisons
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Jellyfin.Data.Enums;
+using Jellyfin.Database.Implementations;
+using Jellyfin.Database.Implementations.Entities;
+using Jellyfin.Extensions;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Persistence;
+using MediaBrowser.Model.Configuration;
+using Microsoft.EntityFrameworkCore;
+
+namespace Emby.Server.Implementations.Library.Search;
+
+/// <summary>
+/// Built-in SQL-based search provider that queries the library database directly.
+/// </summary>
+public class SqlSearchProvider : IInternalSearchProvider
+{
+ private const int DefaultSearchLimit = 100;
+ private const float ExactMatchScore = 100f;
+ private const float PrefixMatchScore = 80f;
+ private const float WordPrefixMatchScore = 75f;
+ private const float ContainsMatchScore = 50f;
+
+ private static readonly Guid _placeholderId = Guid.Parse("00000000-0000-0000-0000-000000000001");
+
+ private readonly IDbContextFactory<JellyfinDbContext> _dbProvider;
+ private readonly IItemTypeLookup _itemTypeLookup;
+ private readonly ILibraryManager _libraryManager;
+ private readonly IUserManager _userManager;
+ private readonly IItemQueryHelpers _queryHelpers;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SqlSearchProvider"/> class.
+ /// </summary>
+ /// <param name="dbProvider">The database context factory.</param>
+ /// <param name="itemTypeLookup">The item type lookup.</param>
+ /// <param name="libraryManager">The library manager.</param>
+ /// <param name="userManager">The user manager.</param>
+ /// <param name="queryHelpers">The shared item query helpers.</param>
+ public SqlSearchProvider(
+ IDbContextFactory<JellyfinDbContext> dbProvider,
+ IItemTypeLookup itemTypeLookup,
+ ILibraryManager libraryManager,
+ IUserManager userManager,
+ IItemQueryHelpers queryHelpers)
+ {
+ _dbProvider = dbProvider;
+ _itemTypeLookup = itemTypeLookup;
+ _libraryManager = libraryManager;
+ _userManager = userManager;
+ _queryHelpers = queryHelpers;
+ }
+
+ /// <inheritdoc/>
+ public string Name => "Database";
+
+ /// <inheritdoc/>
+ public MetadataPluginType Type => MetadataPluginType.SearchProvider;
+
+ /// <inheritdoc/>
+ public int Priority => 100; // Low priority - runs as fallback
+
+ /// <inheritdoc/>
+ public bool CanSearch(SearchProviderQuery query)
+ {
+ // SQL search can always handle any query
+ return true;
+ }
+
+ /// <inheritdoc/>
+ public async Task<IReadOnlyList<SearchResult>> SearchAsync(SearchProviderQuery query, CancellationToken cancellationToken)
+ {
+ ArgumentNullException.ThrowIfNull(query);
+ ArgumentException.ThrowIfNullOrWhiteSpace(query.SearchTerm);
+
+ var rawSearchTerm = query.SearchTerm.Trim().RemoveDiacritics();
+ if (string.IsNullOrEmpty(rawSearchTerm))
+ {
+ return [];
+ }
+
+ var cleanSearchTerm = rawSearchTerm.GetCleanValue();
+ if (string.IsNullOrEmpty(cleanSearchTerm))
+ {
+ return [];
+ }
+
+ var cleanPrefix = cleanSearchTerm + " ";
+ // OriginalTitle is stored mixed-case and isn't pre-normalized like CleanName,
+ // so match it via a case-insensitive LIKE rather than a per-row case conversion
+ // that may not translate to SQL on every provider.
+ var likeOriginal = $"%{rawSearchTerm}%";
+ var limit = query.Limit ?? DefaultSearchLimit;
+
+ var dbContext = await _dbProvider.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
+ await using (dbContext.ConfigureAwait(false))
+ {
+ // Lightweight projection: select only what's needed to score and identify items.
+ var dbQuery = dbContext.BaseItems
+ .AsNoTracking()
+ .Where(e => e.Id != _placeholderId)
+ .Where(e => !e.IsVirtualItem)
+ .Where(e => e.CleanName!.Contains(cleanSearchTerm)
+ || (e.OriginalTitle != null && EF.Functions.Like(e.OriginalTitle, likeOriginal)));
+
+ dbQuery = ApplyTypeFilter(dbQuery, query.IncludeItemTypes, query.ExcludeItemTypes);
+ dbQuery = ApplyMediaTypeFilter(dbQuery, query.MediaTypes);
+ dbQuery = ApplyParentFilter(dbQuery, query.ParentId);
+ dbQuery = ApplyUserAccessFilter(dbContext, dbQuery, query.UserId);
+
+ // Compute the score in SQL: the ternary translates to a CASE WHEN. CleanName is
+ // the pre-normalized (lowercase, diacritic-stripped) form, so we score against it
+ // directly without any per-row case conversion. Items that match only via
+ // OriginalTitle fall through to the Contains tier.
+ // Tie-break by Id for deterministic ordering so the explicit OrderBy + Take
+ // satisfies EF Core's row-limiting-with-OrderBy requirement.
+ var scored = dbQuery.Select(e => new
+ {
+ e.Id,
+ Score =
+ (e.CleanName == cleanSearchTerm) ? ExactMatchScore
+ : e.CleanName!.StartsWith(cleanSearchTerm) ? PrefixMatchScore
+ : e.CleanName!.Contains(cleanPrefix) ? WordPrefixMatchScore
+ : ContainsMatchScore
+ });
+
+ return await scored
+ .OrderByDescending(x => x.Score)
+ .ThenBy(x => x.Id)
+ .Take(limit)
+ .Select(x => new SearchResult(x.Id, x.Score))
+ .ToArrayAsync(cancellationToken)
+ .ConfigureAwait(false);
+ }
+ }
+
+ private IQueryable<BaseItemEntity> ApplyTypeFilter(
+ IQueryable<BaseItemEntity> query,
+ BaseItemKind[] includeItemTypes,
+ BaseItemKind[] excludeItemTypes)
+ {
+ if (includeItemTypes.Length > 0)
+ {
+ var includeTypeNames = MapKindsToTypeNames(includeItemTypes);
+ if (includeTypeNames.Count > 0)
+ {
+ query = query.Where(e => includeTypeNames.Contains(e.Type));
+ }
+ }
+ else if (excludeItemTypes.Length > 0)
+ {
+ var excludeTypeNames = MapKindsToTypeNames(excludeItemTypes);
+ if (excludeTypeNames.Count > 0)
+ {
+ query = query.Where(e => !excludeTypeNames.Contains(e.Type));
+ }
+ }
+
+ return query;
+ }
+
+ private static IQueryable<BaseItemEntity> ApplyMediaTypeFilter(
+ IQueryable<BaseItemEntity> query,
+ MediaType[] mediaTypes)
+ {
+ if (mediaTypes.Length == 0)
+ {
+ return query;
+ }
+
+ var mediaTypeNames = mediaTypes.Select(m => m.ToString()).ToArray();
+ return query.Where(e => e.MediaType != null && mediaTypeNames.Contains(e.MediaType));
+ }
+
+ private static IQueryable<BaseItemEntity> ApplyParentFilter(
+ IQueryable<BaseItemEntity> query,
+ Guid? parentId)
+ {
+ if (!parentId.HasValue || parentId.Value.IsEmpty())
+ {
+ return query;
+ }
+
+ var pid = parentId.Value;
+ return query.Where(e => e.ParentId == pid || e.Parents!.Any(p => p.ParentItemId == pid));
+ }
+
+ private IQueryable<BaseItemEntity> ApplyUserAccessFilter(
+ JellyfinDbContext dbContext,
+ IQueryable<BaseItemEntity> query,
+ Guid? userId)
+ {
+ if (!userId.HasValue || userId.Value.IsEmpty())
+ {
+ return query;
+ }
+
+ var user = _userManager.GetUserById(userId.Value);
+ if (user is null)
+ {
+ return query;
+ }
+
+ var accessFilter = new InternalItemsQuery(user);
+ _libraryManager.ConfigureUserAccess(accessFilter, user);
+ return _queryHelpers.ApplyAccessFiltering(dbContext, query, accessFilter);
+ }
+
+ private List<string> MapKindsToTypeNames(BaseItemKind[] kinds)
+ {
+ var list = new List<string>(kinds.Length);
+ foreach (var kind in kinds)
+ {
+ if (_itemTypeLookup.BaseItemKindNames.TryGetValue(kind, out var name) && name is not null)
+ {
+ list.Add(name);
+ }
+ }
+
+ return list;
+ }
+}
diff --git a/Emby.Server.Implementations/Library/SearchEngine.cs b/Emby.Server.Implementations/Library/SearchEngine.cs
deleted file mode 100644
index c682118597..0000000000
--- a/Emby.Server.Implementations/Library/SearchEngine.cs
+++ /dev/null
@@ -1,200 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using Jellyfin.Data.Enums;
-using Jellyfin.Database.Implementations.Entities;
-using Jellyfin.Database.Implementations.Enums;
-using Jellyfin.Extensions;
-using MediaBrowser.Controller.Dto;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Querying;
-using MediaBrowser.Model.Search;
-
-namespace Emby.Server.Implementations.Library
-{
- public class SearchEngine : ISearchEngine
- {
- private readonly ILibraryManager _libraryManager;
- private readonly IUserManager _userManager;
-
- public SearchEngine(ILibraryManager libraryManager, IUserManager userManager)
- {
- _libraryManager = libraryManager;
- _userManager = userManager;
- }
-
- public QueryResult<SearchHintInfo> GetSearchHints(SearchQuery query)
- {
- User? user = null;
- if (!query.UserId.IsEmpty())
- {
- user = _userManager.GetUserById(query.UserId);
- }
-
- var results = GetSearchHints(query, user);
- var totalRecordCount = results.Count;
-
- if (query.StartIndex.HasValue)
- {
- results = results.GetRange(query.StartIndex.Value, totalRecordCount - query.StartIndex.Value);
- }
-
- if (query.Limit.HasValue && query.Limit.Value > 0)
- {
- results = results.GetRange(0, Math.Min(query.Limit.Value, results.Count));
- }
-
- return new QueryResult<SearchHintInfo>(
- query.StartIndex,
- totalRecordCount,
- results);
- }
-
- private static void AddIfMissing(List<BaseItemKind> list, BaseItemKind value)
- {
- if (!list.Contains(value))
- {
- list.Add(value);
- }
- }
-
- /// <summary>
- /// Gets the search hints.
- /// </summary>
- /// <param name="query">The query.</param>
- /// <param name="user">The user.</param>
- /// <returns>IEnumerable{SearchHintResult}.</returns>
- /// <exception cref="ArgumentException"><c>query.SearchTerm</c> is <c>null</c> or empty.</exception>
- private List<SearchHintInfo> GetSearchHints(SearchQuery query, User? user)
- {
- var searchTerm = query.SearchTerm;
-
- ArgumentException.ThrowIfNullOrEmpty(searchTerm);
-
- searchTerm = searchTerm.Trim().RemoveDiacritics();
-
- var excludeItemTypes = query.ExcludeItemTypes.ToList();
- var includeItemTypes = query.IncludeItemTypes.ToList();
-
- excludeItemTypes.Add(BaseItemKind.Year);
- excludeItemTypes.Add(BaseItemKind.Folder);
-
- if (query.IncludeGenres && (includeItemTypes.Count == 0 || includeItemTypes.Contains(BaseItemKind.Genre)))
- {
- if (!query.IncludeMedia)
- {
- AddIfMissing(includeItemTypes, BaseItemKind.Genre);
- AddIfMissing(includeItemTypes, BaseItemKind.MusicGenre);
- }
- }
- else
- {
- AddIfMissing(excludeItemTypes, BaseItemKind.Genre);
- AddIfMissing(excludeItemTypes, BaseItemKind.MusicGenre);
- }
-
- if (query.IncludePeople && (includeItemTypes.Count == 0 || includeItemTypes.Contains(BaseItemKind.Person)))
- {
- if (!query.IncludeMedia)
- {
- AddIfMissing(includeItemTypes, BaseItemKind.Person);
- }
- }
- else
- {
- AddIfMissing(excludeItemTypes, BaseItemKind.Person);
- }
-
- if (query.IncludeStudios && (includeItemTypes.Count == 0 || includeItemTypes.Contains(BaseItemKind.Studio)))
- {
- if (!query.IncludeMedia)
- {
- AddIfMissing(includeItemTypes, BaseItemKind.Studio);
- }
- }
- else
- {
- AddIfMissing(excludeItemTypes, BaseItemKind.Studio);
- }
-
- if (query.IncludeArtists && (includeItemTypes.Count == 0 || includeItemTypes.Contains(BaseItemKind.MusicArtist)))
- {
- if (!query.IncludeMedia)
- {
- AddIfMissing(includeItemTypes, BaseItemKind.MusicArtist);
- }
- }
- else
- {
- AddIfMissing(excludeItemTypes, BaseItemKind.MusicArtist);
- }
-
- AddIfMissing(excludeItemTypes, BaseItemKind.CollectionFolder);
- AddIfMissing(excludeItemTypes, BaseItemKind.Folder);
- var mediaTypes = query.MediaTypes.ToList();
-
- if (includeItemTypes.Count > 0)
- {
- excludeItemTypes.Clear();
- mediaTypes.Clear();
- }
-
- var searchQuery = new InternalItemsQuery(user)
- {
- SearchTerm = searchTerm,
- ExcludeItemTypes = excludeItemTypes.ToArray(),
- IncludeItemTypes = includeItemTypes.ToArray(),
- Limit = query.Limit,
- IncludeItemsByName = !query.ParentId.HasValue,
- ParentId = query.ParentId ?? Guid.Empty,
- OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) },
- Recursive = true,
-
- IsKids = query.IsKids,
- IsMovie = query.IsMovie,
- IsNews = query.IsNews,
- IsSeries = query.IsSeries,
- IsSports = query.IsSports,
- MediaTypes = mediaTypes.ToArray(),
-
- DtoOptions = new DtoOptions
- {
- Fields = new ItemFields[]
- {
- ItemFields.AirTime,
- ItemFields.DateCreated,
- ItemFields.ChannelInfo,
- ItemFields.ParentId
- }
- }
- };
-
- IReadOnlyList<BaseItem> mediaItems;
-
- if (searchQuery.IncludeItemTypes.Length == 1 && searchQuery.IncludeItemTypes[0] == BaseItemKind.MusicArtist)
- {
- if (!searchQuery.ParentId.IsEmpty())
- {
- searchQuery.AncestorIds = [searchQuery.ParentId];
- searchQuery.ParentId = Guid.Empty;
- }
-
- searchQuery.IncludeItemsByName = true;
- searchQuery.IncludeItemTypes = Array.Empty<BaseItemKind>();
- mediaItems = _libraryManager.GetAllArtists(searchQuery).Items.Select(i => i.Item).ToList();
- }
- else
- {
- mediaItems = _libraryManager.GetItemList(searchQuery);
- }
-
- return mediaItems.Select(i => new SearchHintInfo
- {
- Item = i
- }).ToList();
- }
- }
-}
diff --git a/Emby.Server.Implementations/Library/SimilarItems/AudioSimilarItemsProvider.cs b/Emby.Server.Implementations/Library/SimilarItems/AudioSimilarItemsProvider.cs
new file mode 100644
index 0000000000..1cc670b8ee
--- /dev/null
+++ b/Emby.Server.Implementations/Library/SimilarItems/AudioSimilarItemsProvider.cs
@@ -0,0 +1,55 @@
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using Jellyfin.Data.Enums;
+using Jellyfin.Database.Implementations.Enums;
+using MediaBrowser.Controller.Dto;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.Configuration;
+
+namespace Emby.Server.Implementations.Library.SimilarItems;
+
+/// <summary>
+/// Provides similar items for audio tracks.
+/// </summary>
+public class AudioSimilarItemsProvider : ILocalSimilarItemsProvider<Audio>
+{
+ private readonly ILibraryManager _libraryManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="AudioSimilarItemsProvider"/> class.
+ /// </summary>
+ /// <param name="libraryManager">The library manager.</param>
+ public AudioSimilarItemsProvider(ILibraryManager libraryManager)
+ {
+ _libraryManager = libraryManager;
+ }
+
+ /// <inheritdoc/>
+ public string Name => "Local Genre/Tag";
+
+ /// <inheritdoc/>
+ public MetadataPluginType Type => MetadataPluginType.LocalSimilarityProvider;
+
+ /// <inheritdoc/>
+ public Task<IReadOnlyList<BaseItem>> GetSimilarItemsAsync(Audio item, SimilarItemsQuery query, CancellationToken cancellationToken)
+ {
+ var internalQuery = new InternalItemsQuery(query.User)
+ {
+ Genres = item.Genres,
+ Tags = item.Tags,
+ Limit = query.Limit,
+ DtoOptions = query.DtoOptions ?? new DtoOptions(),
+ ExcludeItemIds = [.. query.ExcludeItemIds],
+ ExcludeArtistIds = [.. query.ExcludeArtistIds],
+ IncludeItemTypes = [BaseItemKind.Audio],
+ EnableGroupByMetadataKey = false,
+ EnableTotalRecordCount = true,
+ OrderBy = [(ItemSortBy.Random, SortOrder.Ascending)]
+ };
+
+ return Task.FromResult(_libraryManager.GetItemList(internalQuery));
+ }
+}
diff --git a/Emby.Server.Implementations/Library/SimilarItems/LiveTvProgramSimilarItemsProvider.cs b/Emby.Server.Implementations/Library/SimilarItems/LiveTvProgramSimilarItemsProvider.cs
new file mode 100644
index 0000000000..7665ee2f79
--- /dev/null
+++ b/Emby.Server.Implementations/Library/SimilarItems/LiveTvProgramSimilarItemsProvider.cs
@@ -0,0 +1,94 @@
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using Jellyfin.Data.Enums;
+using Jellyfin.Database.Implementations.Enums;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Dto;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.LiveTv;
+using MediaBrowser.Model.Configuration;
+
+namespace Emby.Server.Implementations.Library.SimilarItems;
+
+/// <summary>
+/// Provides similar items for Live TV programs.
+/// </summary>
+public class LiveTvProgramSimilarItemsProvider : ILocalSimilarItemsProvider<LiveTvProgram>
+{
+ private readonly ILibraryManager _libraryManager;
+ private readonly IServerConfigurationManager _serverConfigurationManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="LiveTvProgramSimilarItemsProvider"/> class.
+ /// </summary>
+ /// <param name="libraryManager">The library manager.</param>
+ /// <param name="serverConfigurationManager">The server configuration manager.</param>
+ public LiveTvProgramSimilarItemsProvider(
+ ILibraryManager libraryManager,
+ IServerConfigurationManager serverConfigurationManager)
+ {
+ _libraryManager = libraryManager;
+ _serverConfigurationManager = serverConfigurationManager;
+ }
+
+ /// <inheritdoc/>
+ public string Name => "Local Genre/Tag";
+
+ /// <inheritdoc/>
+ public MetadataPluginType Type => MetadataPluginType.LocalSimilarityProvider;
+
+ /// <inheritdoc/>
+ public Task<IReadOnlyList<BaseItem>> GetSimilarItemsAsync(LiveTvProgram item, SimilarItemsQuery query, CancellationToken cancellationToken)
+ {
+ BaseItemKind[] includeItemTypes;
+ bool enableGroupByMetadataKey;
+ bool enableTotalRecordCount;
+
+ if (item.IsMovie)
+ {
+ // Movie-like program
+ var itemTypes = new List<BaseItemKind> { BaseItemKind.Movie };
+
+ if (_serverConfigurationManager.Configuration.EnableExternalContentInSuggestions)
+ {
+ itemTypes.Add(BaseItemKind.Trailer);
+ itemTypes.Add(BaseItemKind.LiveTvProgram);
+ }
+
+ includeItemTypes = [.. itemTypes];
+ enableGroupByMetadataKey = true;
+ enableTotalRecordCount = false;
+ }
+ else if (item.IsSeries)
+ {
+ // Series-like program
+ includeItemTypes = [BaseItemKind.Series];
+ enableGroupByMetadataKey = false;
+ enableTotalRecordCount = true;
+ }
+ else
+ {
+ // Default - match same type
+ includeItemTypes = [item.GetBaseItemKind()];
+ enableGroupByMetadataKey = false;
+ enableTotalRecordCount = true;
+ }
+
+ var internalQuery = new InternalItemsQuery(query.User)
+ {
+ Genres = item.Genres,
+ Tags = item.Tags,
+ Limit = query.Limit,
+ DtoOptions = query.DtoOptions ?? new DtoOptions(),
+ ExcludeItemIds = [.. query.ExcludeItemIds],
+ IncludeItemTypes = includeItemTypes,
+ EnableGroupByMetadataKey = enableGroupByMetadataKey,
+ EnableTotalRecordCount = enableTotalRecordCount,
+ OrderBy = [(ItemSortBy.Random, SortOrder.Ascending)]
+ };
+
+ return Task.FromResult(_libraryManager.GetItemList(internalQuery));
+ }
+}
diff --git a/Emby.Server.Implementations/Library/SimilarItems/MovieSimilarItemsProvider.cs b/Emby.Server.Implementations/Library/SimilarItems/MovieSimilarItemsProvider.cs
new file mode 100644
index 0000000000..57d1f7c770
--- /dev/null
+++ b/Emby.Server.Implementations/Library/SimilarItems/MovieSimilarItemsProvider.cs
@@ -0,0 +1,342 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Jellyfin.Data.Enums;
+using Jellyfin.Database.Implementations;
+using Jellyfin.Database.Implementations.Entities;
+using Jellyfin.Extensions;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Dto;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Movies;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Persistence;
+using MediaBrowser.Model.Configuration;
+using Microsoft.EntityFrameworkCore;
+using BaseItemDto = MediaBrowser.Controller.Entities.BaseItem;
+
+namespace Emby.Server.Implementations.Library.SimilarItems;
+
+/// <summary>
+/// Provides similar items for movies and trailers using weighted scoring.
+/// </summary>
+public sealed class MovieSimilarItemsProvider : ILocalSimilarItemsProvider<Movie>, ILocalSimilarItemsProvider<Trailer>, IBatchLocalSimilarItemsProvider
+{
+ private const int GenreWeight = 10;
+ private const int TagWeight = 5;
+ private const int StudioWeight = 5;
+ private const int DirectorWeight = 50;
+ private const int ActorWeight = 15;
+
+ // Caps the batch fan-out so downstream IN-list sizes (per-source scores, accessible-id
+ // load, navigation includes) stay bounded regardless of caller input.
+ private const int MaxBatchSourceItems = 64;
+
+ private static readonly (ItemValueType Type, int Weight)[] _itemValueDimensions =
+ [
+ (ItemValueType.Genre, GenreWeight),
+ (ItemValueType.Tags, TagWeight),
+ (ItemValueType.Studios, StudioWeight)
+ ];
+
+ private static readonly Dictionary<string, int> _personTypeWeights = new(StringComparer.Ordinal)
+ {
+ [nameof(PersonKind.Director)] = DirectorWeight,
+ [nameof(PersonKind.Actor)] = ActorWeight,
+ [nameof(PersonKind.GuestStar)] = ActorWeight,
+ };
+
+ private static readonly string[] _scoredPersonTypes = [.. _personTypeWeights.Keys];
+
+ private readonly IDbContextFactory<JellyfinDbContext> _dbProvider;
+ private readonly IItemQueryHelpers _queryHelpers;
+ private readonly IServerConfigurationManager _serverConfigurationManager;
+ private readonly ILibraryManager _libraryManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MovieSimilarItemsProvider"/> class.
+ /// </summary>
+ /// <param name="dbProvider">The database context factory.</param>
+ /// <param name="queryHelpers">The shared query helpers.</param>
+ /// <param name="serverConfigurationManager">The server configuration manager.</param>
+ /// <param name="libraryManager">The library manager.</param>
+ public MovieSimilarItemsProvider(
+ IDbContextFactory<JellyfinDbContext> dbProvider,
+ IItemQueryHelpers queryHelpers,
+ IServerConfigurationManager serverConfigurationManager,
+ ILibraryManager libraryManager)
+ {
+ _dbProvider = dbProvider;
+ _queryHelpers = queryHelpers;
+ _serverConfigurationManager = serverConfigurationManager;
+ _libraryManager = libraryManager;
+ }
+
+ /// <inheritdoc/>
+ public string Name => "Local Genre/Tag";
+
+ /// <inheritdoc/>
+ public MetadataPluginType Type => MetadataPluginType.LocalSimilarityProvider;
+
+ /// <inheritdoc/>
+ public async Task<IReadOnlyList<BaseItemDto>> GetSimilarItemsAsync(Movie item, SimilarItemsQuery query, CancellationToken cancellationToken)
+ {
+ var results = await GetBatchSimilarItemsAsync([item], query, cancellationToken).ConfigureAwait(false);
+ return results.TryGetValue(item.Id, out var items) ? items : [];
+ }
+
+ /// <inheritdoc/>
+ public async Task<IReadOnlyList<BaseItemDto>> GetSimilarItemsAsync(Trailer item, SimilarItemsQuery query, CancellationToken cancellationToken)
+ {
+ var results = await GetBatchSimilarItemsAsync([item], query, cancellationToken).ConfigureAwait(false);
+ return results.TryGetValue(item.Id, out var items) ? items : [];
+ }
+
+ bool ILocalSimilarItemsProvider.Supports(Type itemType)
+ => typeof(Movie).IsAssignableFrom(itemType) || typeof(Trailer).IsAssignableFrom(itemType);
+
+ Task<IReadOnlyList<BaseItem>> ILocalSimilarItemsProvider.GetSimilarItemsAsync(BaseItem item, SimilarItemsQuery query, CancellationToken cancellationToken)
+ => item switch
+ {
+ Movie movie => GetSimilarItemsAsync(movie, query, cancellationToken),
+ Trailer trailer => GetSimilarItemsAsync(trailer, query, cancellationToken),
+ _ => throw new ArgumentException($"Unsupported item type {item.GetType()}", nameof(item))
+ };
+
+ /// <inheritdoc/>
+ public async Task<Dictionary<Guid, IReadOnlyList<BaseItemDto>>> GetBatchSimilarItemsAsync(
+ IReadOnlyList<BaseItemDto> sourceItems,
+ SimilarItemsQuery query,
+ CancellationToken cancellationToken)
+ {
+ var includeItemTypes = new List<BaseItemKind> { BaseItemKind.Movie };
+ if (_serverConfigurationManager.Configuration.EnableExternalContentInSuggestions)
+ {
+ includeItemTypes.Add(BaseItemKind.Trailer);
+ includeItemTypes.Add(BaseItemKind.LiveTvProgram);
+ }
+
+ var limit = query.Limit ?? 50;
+ var dtoOptions = query.DtoOptions ?? new DtoOptions();
+
+ if (sourceItems.Count > MaxBatchSourceItems)
+ {
+ sourceItems = sourceItems.Take(MaxBatchSourceItems).ToList();
+ }
+
+ var context = await _dbProvider.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
+ await using (context.ConfigureAwait(false))
+ {
+ // Phase 1: Score all candidates per source item
+ var sourceIds = sourceItems.Select(i => i.Id).ToList();
+ var perSourceScores = await ComputeBatchScoresAsync(sourceIds, context, cancellationToken).ConfigureAwait(false);
+
+ var allCandidateIds = new HashSet<Guid>();
+ foreach (var (_, scores) in perSourceScores)
+ {
+ allCandidateIds.UnionWith(
+ scores.OrderByDescending(kvp => kvp.Value)
+ .Take(limit * 3)
+ .Select(kvp => kvp.Key));
+ }
+
+ var result = new Dictionary<Guid, IReadOnlyList<BaseItemDto>>();
+ if (allCandidateIds.Count == 0)
+ {
+ return result;
+ }
+
+ // Phase 2: One access filter for all candidates
+ var filter = new InternalItemsQuery(query.User)
+ {
+ IncludeItemTypes = [.. includeItemTypes],
+ ExcludeItemIds = [.. query.ExcludeItemIds],
+ DtoOptions = dtoOptions,
+ EnableGroupByMetadataKey = true,
+ EnableTotalRecordCount = false,
+ IsMovie = true,
+ IsPlayed = false
+ };
+
+ if (query.User is not null)
+ {
+ _libraryManager.ConfigureUserAccess(filter, query.User);
+ }
+
+ _queryHelpers.PrepareFilterQuery(filter);
+ var baseQuery = _queryHelpers.PrepareItemQuery(context, filter);
+ baseQuery = _queryHelpers.TranslateQuery(baseQuery, context, filter);
+
+ var allCandidateIdsList = allCandidateIds.ToList();
+ var accessibleItems = await baseQuery
+ .WhereOneOrMany(allCandidateIdsList, e => e.Id)
+ .Select(e => new { e.Id, e.PresentationUniqueKey })
+ .ToListAsync(cancellationToken).ConfigureAwait(false);
+
+ // Phase 3: Pick top IDs per source, dedup by PresentationUniqueKey
+ var allOrderedIds = new HashSet<Guid>();
+ var perSourceOrderedIds = new Dictionary<Guid, List<Guid>>();
+
+ foreach (var item in sourceItems)
+ {
+ if (!perSourceScores.TryGetValue(item.Id, out var scores))
+ {
+ continue;
+ }
+
+ var orderedIds = accessibleItems
+ .Where(x => scores.ContainsKey(x.Id))
+ .OrderByDescending(x => scores.GetValueOrDefault(x.Id))
+ .DistinctBy(x => x.PresentationUniqueKey)
+ .Take(limit)
+ .Select(x => x.Id)
+ .ToList();
+
+ if (orderedIds.Count > 0)
+ {
+ perSourceOrderedIds[item.Id] = orderedIds;
+ allOrderedIds.UnionWith(orderedIds);
+ }
+ }
+
+ if (allOrderedIds.Count == 0)
+ {
+ return result;
+ }
+
+ // Phase 4: One entity load for all results
+ var allOrderedIdsList = allOrderedIds.ToList();
+ var entities = await _queryHelpers.ApplyNavigations(
+ context.BaseItems.AsNoTracking().WhereOneOrMany(allOrderedIdsList, e => e.Id),
+ filter)
+ .AsSplitQuery()
+ .ToListAsync(cancellationToken).ConfigureAwait(false);
+
+ var entitiesById = entities
+ .Select(e => _queryHelpers.DeserializeBaseItem(e, filter.SkipDeserialization))
+ .Where(dto => dto is not null)
+ .ToDictionary(i => i!.Id);
+
+ // Phase 5: Split by source, preserving score order
+ foreach (var (sourceId, orderedIds) in perSourceOrderedIds)
+ {
+ var items = orderedIds
+ .Where(entitiesById.ContainsKey)
+ .Select(id => entitiesById[id]!)
+ .ToList();
+
+ if (items.Count > 0)
+ {
+ result[sourceId] = items;
+ }
+ }
+
+ return result;
+ }
+ }
+
+ private static async Task<Dictionary<Guid, Dictionary<Guid, int>>> ComputeBatchScoresAsync(List<Guid> sourceIds, JellyfinDbContext context, CancellationToken cancellationToken)
+ {
+ var result = new Dictionary<Guid, Dictionary<Guid, int>>();
+ foreach (var id in sourceIds)
+ {
+ result[id] = [];
+ }
+
+ foreach (var (valueType, weight) in _itemValueDimensions)
+ {
+ var sourceRows = await context.ItemValuesMap.AsNoTracking()
+ .Where(m => sourceIds.Contains(m.ItemId) && m.ItemValue.Type == valueType)
+ .Select(m => new { m.ItemId, Key = m.ItemValue.CleanValue })
+ .ToListAsync(cancellationToken).ConfigureAwait(false);
+
+ var sourceMap = sourceRows.GroupBy(r => r.ItemId).ToDictionary(g => g.Key, g => g.Select(x => x.Key).ToHashSet());
+ var allKeys = sourceMap.Values.SelectMany(v => v).Distinct().ToList();
+ if (allKeys.Count == 0)
+ {
+ continue;
+ }
+
+ var candidateRows = await context.ItemValuesMap.AsNoTracking()
+ .Where(m => m.ItemValue.Type == valueType && allKeys.Contains(m.ItemValue.CleanValue))
+ .Select(m => new { m.ItemId, Key = m.ItemValue.CleanValue })
+ .ToListAsync(cancellationToken).ConfigureAwait(false);
+
+ var keyToCandidates = candidateRows.GroupBy(r => r.Key).ToDictionary(g => g.Key, g => g.Select(x => x.ItemId).ToList());
+ ApplyDimensionScores(sourceIds, sourceMap, keyToCandidates, weight, result);
+ }
+
+ var personSourceRows = await context.PeopleBaseItemMap.AsNoTracking()
+ .Where(m => sourceIds.Contains(m.ItemId) && _scoredPersonTypes.Contains(m.People.PersonType))
+ .Select(m => new { m.ItemId, m.PeopleId, m.People.PersonType })
+ .ToListAsync(cancellationToken).ConfigureAwait(false);
+
+ if (personSourceRows.Count > 0)
+ {
+ var personCandidateRows = await context.PeopleBaseItemMap.AsNoTracking()
+ .Where(m => context.PeopleBaseItemMap
+ .Where(s => sourceIds.Contains(s.ItemId) && _scoredPersonTypes.Contains(s.People.PersonType))
+ .Select(s => s.PeopleId)
+ .Contains(m.PeopleId))
+ .Select(m => new { m.ItemId, m.PeopleId })
+ .ToListAsync(cancellationToken).ConfigureAwait(false);
+
+ var personToCandidates = personCandidateRows
+ .GroupBy(r => r.PeopleId)
+ .ToDictionary(g => g.Key, g => g.Select(x => x.ItemId).ToList());
+
+ foreach (var weightGroup in personSourceRows.GroupBy(r => _personTypeWeights[r.PersonType!]))
+ {
+ var sourceMap = weightGroup
+ .GroupBy(r => r.ItemId)
+ .ToDictionary(g => g.Key, g => g.Select(x => x.PeopleId).ToHashSet());
+ ApplyDimensionScores(sourceIds, sourceMap, personToCandidates, weightGroup.Key, result);
+ }
+ }
+
+ foreach (var sourceId in sourceIds)
+ {
+ var scoreMap = result[sourceId];
+ scoreMap.Remove(sourceId);
+ if (scoreMap.Count == 0)
+ {
+ result.Remove(sourceId);
+ }
+ }
+
+ return result;
+ }
+
+ private static void ApplyDimensionScores<TKey>(
+ List<Guid> sourceIds,
+ Dictionary<Guid, HashSet<TKey>> sourceMap,
+ Dictionary<TKey, List<Guid>> keyToCandidates,
+ int weight,
+ Dictionary<Guid, Dictionary<Guid, int>> result)
+ where TKey : notnull
+ {
+ foreach (var sourceId in sourceIds)
+ {
+ if (!sourceMap.TryGetValue(sourceId, out var sourceKeys))
+ {
+ continue;
+ }
+
+ var scoreMap = result[sourceId];
+ foreach (var key in sourceKeys)
+ {
+ if (!keyToCandidates.TryGetValue(key, out var candidates))
+ {
+ continue;
+ }
+
+ foreach (var candidateId in candidates)
+ {
+ scoreMap[candidateId] = scoreMap.GetValueOrDefault(candidateId) + weight;
+ }
+ }
+ }
+ }
+}
diff --git a/Emby.Server.Implementations/Library/SimilarItems/MusicAlbumSimilarItemsProvider.cs b/Emby.Server.Implementations/Library/SimilarItems/MusicAlbumSimilarItemsProvider.cs
new file mode 100644
index 0000000000..c13045deda
--- /dev/null
+++ b/Emby.Server.Implementations/Library/SimilarItems/MusicAlbumSimilarItemsProvider.cs
@@ -0,0 +1,55 @@
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using Jellyfin.Data.Enums;
+using Jellyfin.Database.Implementations.Enums;
+using MediaBrowser.Controller.Dto;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.Configuration;
+
+namespace Emby.Server.Implementations.Library.SimilarItems;
+
+/// <summary>
+/// Provides similar items for music albums.
+/// </summary>
+public class MusicAlbumSimilarItemsProvider : ILocalSimilarItemsProvider<MusicAlbum>
+{
+ private readonly ILibraryManager _libraryManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MusicAlbumSimilarItemsProvider"/> class.
+ /// </summary>
+ /// <param name="libraryManager">The library manager.</param>
+ public MusicAlbumSimilarItemsProvider(ILibraryManager libraryManager)
+ {
+ _libraryManager = libraryManager;
+ }
+
+ /// <inheritdoc/>
+ public string Name => "Local Genre/Tag";
+
+ /// <inheritdoc/>
+ public MetadataPluginType Type => MetadataPluginType.LocalSimilarityProvider;
+
+ /// <inheritdoc/>
+ public Task<IReadOnlyList<BaseItem>> GetSimilarItemsAsync(MusicAlbum item, SimilarItemsQuery query, CancellationToken cancellationToken)
+ {
+ var internalQuery = new InternalItemsQuery(query.User)
+ {
+ Genres = item.Genres,
+ Tags = item.Tags,
+ Limit = query.Limit,
+ DtoOptions = query.DtoOptions ?? new DtoOptions(),
+ ExcludeItemIds = [.. query.ExcludeItemIds],
+ ExcludeArtistIds = [.. query.ExcludeArtistIds],
+ IncludeItemTypes = [BaseItemKind.MusicAlbum],
+ EnableGroupByMetadataKey = false,
+ EnableTotalRecordCount = true,
+ OrderBy = [(ItemSortBy.Random, SortOrder.Ascending)]
+ };
+
+ return Task.FromResult(_libraryManager.GetItemList(internalQuery));
+ }
+}
diff --git a/Emby.Server.Implementations/Library/SimilarItems/MusicArtistSimilarItemsProvider.cs b/Emby.Server.Implementations/Library/SimilarItems/MusicArtistSimilarItemsProvider.cs
new file mode 100644
index 0000000000..3331419442
--- /dev/null
+++ b/Emby.Server.Implementations/Library/SimilarItems/MusicArtistSimilarItemsProvider.cs
@@ -0,0 +1,55 @@
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using Jellyfin.Data.Enums;
+using Jellyfin.Database.Implementations.Enums;
+using MediaBrowser.Controller.Dto;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.Configuration;
+
+namespace Emby.Server.Implementations.Library.SimilarItems;
+
+/// <summary>
+/// Provides similar items for music artists.
+/// </summary>
+public class MusicArtistSimilarItemsProvider : ILocalSimilarItemsProvider<MusicArtist>
+{
+ private readonly ILibraryManager _libraryManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MusicArtistSimilarItemsProvider"/> class.
+ /// </summary>
+ /// <param name="libraryManager">The library manager.</param>
+ public MusicArtistSimilarItemsProvider(ILibraryManager libraryManager)
+ {
+ _libraryManager = libraryManager;
+ }
+
+ /// <inheritdoc/>
+ public string Name => "Local Genre/Tag";
+
+ /// <inheritdoc/>
+ public MetadataPluginType Type => MetadataPluginType.LocalSimilarityProvider;
+
+ /// <inheritdoc/>
+ public Task<IReadOnlyList<BaseItem>> GetSimilarItemsAsync(MusicArtist item, SimilarItemsQuery query, CancellationToken cancellationToken)
+ {
+ var internalQuery = new InternalItemsQuery(query.User)
+ {
+ Genres = item.Genres,
+ Tags = item.Tags,
+ Limit = query.Limit,
+ DtoOptions = query.DtoOptions ?? new DtoOptions(),
+ ExcludeItemIds = [.. query.ExcludeItemIds],
+ ExcludeArtistIds = [.. query.ExcludeArtistIds],
+ IncludeItemTypes = [BaseItemKind.MusicArtist],
+ EnableGroupByMetadataKey = false,
+ EnableTotalRecordCount = true,
+ OrderBy = [(ItemSortBy.Random, SortOrder.Ascending)]
+ };
+
+ return Task.FromResult(_libraryManager.GetItemList(internalQuery));
+ }
+}
diff --git a/Emby.Server.Implementations/Library/SimilarItems/SeriesSimilarItemsProvider.cs b/Emby.Server.Implementations/Library/SimilarItems/SeriesSimilarItemsProvider.cs
new file mode 100644
index 0000000000..0366fb752e
--- /dev/null
+++ b/Emby.Server.Implementations/Library/SimilarItems/SeriesSimilarItemsProvider.cs
@@ -0,0 +1,54 @@
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using Jellyfin.Data.Enums;
+using Jellyfin.Database.Implementations.Enums;
+using MediaBrowser.Controller.Dto;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.Configuration;
+
+namespace Emby.Server.Implementations.Library.SimilarItems;
+
+/// <summary>
+/// Provides similar items for TV series.
+/// </summary>
+public class SeriesSimilarItemsProvider : ILocalSimilarItemsProvider<Series>
+{
+ private readonly ILibraryManager _libraryManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SeriesSimilarItemsProvider"/> class.
+ /// </summary>
+ /// <param name="libraryManager">The library manager.</param>
+ public SeriesSimilarItemsProvider(ILibraryManager libraryManager)
+ {
+ _libraryManager = libraryManager;
+ }
+
+ /// <inheritdoc/>
+ public string Name => "Local Genre/Tag";
+
+ /// <inheritdoc/>
+ public MetadataPluginType Type => MetadataPluginType.LocalSimilarityProvider;
+
+ /// <inheritdoc/>
+ public Task<IReadOnlyList<BaseItem>> GetSimilarItemsAsync(Series item, SimilarItemsQuery query, CancellationToken cancellationToken)
+ {
+ var internalQuery = new InternalItemsQuery(query.User)
+ {
+ Genres = item.Genres,
+ Tags = item.Tags,
+ Limit = query.Limit,
+ DtoOptions = query.DtoOptions ?? new DtoOptions(),
+ ExcludeItemIds = [.. query.ExcludeItemIds],
+ IncludeItemTypes = [BaseItemKind.Series],
+ EnableGroupByMetadataKey = false,
+ EnableTotalRecordCount = true,
+ OrderBy = [(ItemSortBy.Random, SortOrder.Ascending)]
+ };
+
+ return Task.FromResult(_libraryManager.GetItemList(internalQuery));
+ }
+}
diff --git a/Emby.Server.Implementations/Library/SimilarItems/SimilarItemsManager.cs b/Emby.Server.Implementations/Library/SimilarItems/SimilarItemsManager.cs
new file mode 100644
index 0000000000..d923cff07e
--- /dev/null
+++ b/Emby.Server.Implementations/Library/SimilarItems/SimilarItemsManager.cs
@@ -0,0 +1,638 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Text.Json;
+using System.Threading;
+using System.Threading.Tasks;
+using Jellyfin.Data.Enums;
+using Jellyfin.Database.Implementations.Entities;
+using Jellyfin.Database.Implementations.Enums;
+using Jellyfin.Extensions.Json;
+using MediaBrowser.Common.Extensions;
+using MediaBrowser.Controller;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Dto;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.Configuration;
+using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Querying;
+using Microsoft.Extensions.Logging;
+
+namespace Emby.Server.Implementations.Library.SimilarItems;
+
+/// <summary>
+/// Manages similar items providers and orchestrates similar items operations.
+/// </summary>
+public class SimilarItemsManager : ISimilarItemsManager
+{
+ private readonly ILogger<SimilarItemsManager> _logger;
+ private readonly IServerApplicationPaths _appPaths;
+ private readonly ILibraryManager _libraryManager;
+ private readonly IFileSystem _fileSystem;
+ private readonly IServerConfigurationManager _serverConfigurationManager;
+ private ISimilarItemsProvider[] _similarItemsProviders = [];
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SimilarItemsManager"/> class.
+ /// </summary>
+ /// <param name="logger">The logger.</param>
+ /// <param name="appPaths">The server application paths.</param>
+ /// <param name="libraryManager">The library manager.</param>
+ /// <param name="fileSystem">The file system.</param>
+ /// <param name="serverConfigurationManager">The server configuration manager.</param>
+ public SimilarItemsManager(
+ ILogger<SimilarItemsManager> logger,
+ IServerApplicationPaths appPaths,
+ ILibraryManager libraryManager,
+ IFileSystem fileSystem,
+ IServerConfigurationManager serverConfigurationManager)
+ {
+ _logger = logger;
+ _appPaths = appPaths;
+ _libraryManager = libraryManager;
+ _fileSystem = fileSystem;
+ _serverConfigurationManager = serverConfigurationManager;
+ }
+
+ /// <inheritdoc/>
+ public void AddParts(IEnumerable<ISimilarItemsProvider> providers)
+ {
+ _similarItemsProviders = providers.ToArray();
+ }
+
+ /// <inheritdoc/>
+ public IReadOnlyList<ISimilarItemsProvider> GetSimilarItemsProviders<T>()
+ where T : BaseItem
+ {
+ var itemType = typeof(T);
+ return _similarItemsProviders
+ .Where(p => (p is ILocalSimilarItemsProvider local && local.Supports(itemType))
+ || (p is IRemoteSimilarItemsProvider remote && remote.Supports(itemType)))
+ .ToList();
+ }
+
+ /// <inheritdoc/>
+ public async Task<IReadOnlyList<BaseItem>> GetSimilarItemsAsync(
+ BaseItem item,
+ IReadOnlyList<Guid> excludeArtistIds,
+ User? user,
+ DtoOptions dtoOptions,
+ int? limit,
+ LibraryOptions? libraryOptions,
+ CancellationToken cancellationToken)
+ {
+ ArgumentNullException.ThrowIfNull(item);
+ ArgumentNullException.ThrowIfNull(excludeArtistIds);
+
+ var itemType = item.GetType();
+ var requestedLimit = limit ?? 50;
+ var itemKind = item.GetBaseItemKind();
+
+ // Ensure ProviderIds is included in DtoOptions for matching remote provider responses
+ if (!dtoOptions.Fields.Contains(ItemFields.ProviderIds))
+ {
+ dtoOptions.Fields = dtoOptions.Fields.Concat([ItemFields.ProviderIds]).ToArray();
+ }
+
+ // Local providers are always enabled. Remote providers must be explicitly enabled.
+ var localProviders = _similarItemsProviders
+ .OfType<ILocalSimilarItemsProvider>()
+ .Where(p => p.Supports(itemType))
+ .ToList();
+ var remoteProviders = _similarItemsProviders
+ .OfType<IRemoteSimilarItemsProvider>()
+ .Where(p => p.Supports(itemType));
+ var matchingProviders = new List<ISimilarItemsProvider>(localProviders);
+
+ var typeOptions = libraryOptions?.GetTypeOptions(itemType.Name);
+ if (typeOptions?.SimilarItemProviders?.Length > 0)
+ {
+ matchingProviders.AddRange(remoteProviders
+ .Where(p => typeOptions.SimilarItemProviders.Contains(p.Name, StringComparer.OrdinalIgnoreCase)));
+ }
+
+ var orderConfig = typeOptions?.SimilarItemProviderOrder is { Length: > 0 } order
+ ? order
+ : typeOptions?.SimilarItemProviders;
+ var orderedProviders = matchingProviders
+ .OrderBy(p => GetConfiguredSimilarProviderOrder(orderConfig, p.Name))
+ .ToList();
+
+ var allResults = new List<(BaseItem Item, float Score)>();
+ var excludeIds = new HashSet<Guid> { item.Id };
+ var excludeKeys = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { item.GetPresentationUniqueKey() };
+ foreach (var (providerOrder, provider) in orderedProviders.Index())
+ {
+ if (allResults.Count >= requestedLimit || cancellationToken.IsCancellationRequested)
+ {
+ break;
+ }
+
+ try
+ {
+ if (provider is ILocalSimilarItemsProvider localProvider)
+ {
+ var query = new SimilarItemsQuery
+ {
+ User = user,
+ Limit = requestedLimit - allResults.Count,
+ DtoOptions = dtoOptions,
+ ExcludeItemIds = [.. excludeIds],
+ ExcludeArtistIds = excludeArtistIds
+ };
+
+ var items = await localProvider.GetSimilarItemsAsync(item, query, cancellationToken).ConfigureAwait(false);
+
+ foreach (var (position, resultItem) in items.Index())
+ {
+ var isNewId = excludeIds.Add(resultItem.Id);
+ var isNewKey = excludeKeys.Add(resultItem.GetPresentationUniqueKey());
+ if (isNewId && isNewKey)
+ {
+ var score = CalculateScore(null, providerOrder, position);
+ allResults.Add((resultItem, score));
+ }
+ }
+ }
+ else if (provider is IRemoteSimilarItemsProvider remoteProvider)
+ {
+ var cachePath = GetSimilarItemsCachePath(provider.Name, itemType.Name, item.Id);
+
+ var cachedReferences = await TryReadSimilarItemsCacheAsync(cachePath, cancellationToken).ConfigureAwait(false);
+ if (cachedReferences is not null)
+ {
+ var resolvedItems = ResolveRemoteReferences(cachedReferences, providerOrder, user, dtoOptions, itemKind, excludeIds, excludeKeys);
+ allResults.AddRange(resolvedItems);
+ continue;
+ }
+
+ var query = new SimilarItemsQuery
+ {
+ User = user,
+ Limit = requestedLimit - allResults.Count,
+ DtoOptions = dtoOptions,
+ ExcludeItemIds = [.. excludeIds],
+ ExcludeArtistIds = excludeArtistIds
+ };
+
+ // Collect references in batches and resolve against local library.
+ // Stop fetching once we have enough resolved local items.
+ const int BatchSize = 20;
+ var remaining = requestedLimit - allResults.Count;
+ var collectedReferences = new List<SimilarItemReference>();
+ var pendingBatch = new List<SimilarItemReference>();
+
+ await foreach (var reference in remoteProvider.GetSimilarItemsAsync(item, query, cancellationToken).ConfigureAwait(false))
+ {
+ collectedReferences.Add(reference);
+ pendingBatch.Add(reference);
+
+ if (pendingBatch.Count >= BatchSize)
+ {
+ var resolvedItems = ResolveRemoteReferences(pendingBatch, providerOrder, user, dtoOptions, itemKind, excludeIds, excludeKeys);
+ allResults.AddRange(resolvedItems);
+ remaining -= resolvedItems.Count;
+ pendingBatch.Clear();
+
+ if (remaining <= 0)
+ {
+ break;
+ }
+ }
+ }
+
+ // Resolve any remaining references in the last partial batch
+ if (pendingBatch.Count > 0)
+ {
+ var resolvedItems = ResolveRemoteReferences(pendingBatch, providerOrder, user, dtoOptions, itemKind, excludeIds, excludeKeys);
+ allResults.AddRange(resolvedItems);
+ }
+
+ if (collectedReferences.Count > 0 && provider.CacheDuration is not null)
+ {
+ await SaveSimilarItemsCacheAsync(cachePath, collectedReferences, provider.CacheDuration.Value, cancellationToken).ConfigureAwait(false);
+ }
+ }
+ }
+ catch (OperationCanceledException)
+ {
+ break;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogWarning(ex, "Similar items provider {ProviderName} failed for item {ItemId}", provider.Name, item.Id);
+ }
+ }
+
+ return allResults
+ .OrderByDescending(x => x.Score)
+ .Select(x => x.Item)
+ .Take(requestedLimit)
+ .ToList();
+ }
+
+ /// <inheritdoc/>
+ public async Task<IReadOnlyList<SimilarItemsRecommendation>> GetMovieRecommendationsAsync(
+ User? user,
+ Guid parentId,
+ int categoryLimit,
+ int itemLimit,
+ DtoOptions dtoOptions,
+ CancellationToken cancellationToken)
+ {
+ ArgumentNullException.ThrowIfNull(dtoOptions);
+
+ var recentlyPlayedMovies = _libraryManager.GetItemList(new InternalItemsQuery(user)
+ {
+ IncludeItemTypes = [BaseItemKind.Movie],
+ OrderBy = [(ItemSortBy.DatePlayed, SortOrder.Descending), (ItemSortBy.Random, SortOrder.Descending)],
+ Limit = 7,
+ ParentId = parentId,
+ Recursive = true,
+ IsPlayed = true,
+ EnableGroupByMetadataKey = true,
+ DtoOptions = dtoOptions
+ });
+
+ var itemTypes = new List<BaseItemKind> { BaseItemKind.Movie };
+ if (_serverConfigurationManager.Configuration.EnableExternalContentInSuggestions)
+ {
+ itemTypes.Add(BaseItemKind.Trailer);
+ itemTypes.Add(BaseItemKind.LiveTvProgram);
+ }
+
+ var likedMovies = _libraryManager.GetItemList(new InternalItemsQuery(user)
+ {
+ IncludeItemTypes = itemTypes.ToArray(),
+ IsMovie = true,
+ OrderBy = [(ItemSortBy.Random, SortOrder.Descending)],
+ Limit = 10,
+ IsFavoriteOrLiked = true,
+ ExcludeItemIds = recentlyPlayedMovies.Select(i => i.Id).ToArray(),
+ EnableGroupByMetadataKey = true,
+ ParentId = parentId,
+ Recursive = true,
+ DtoOptions = dtoOptions
+ });
+
+ var mostRecentMovies = recentlyPlayedMovies.Take(Math.Min(recentlyPlayedMovies.Count, 6)).ToList();
+ var recentDirectors = GetPeopleNames(mostRecentMovies, [PersonType.Director]);
+ var recentActors = GetPeopleNames(mostRecentMovies, [PersonType.Actor, PersonType.GuestStar]);
+
+ // Cap baseline items to categoryLimit - the round-robin can't use more categories than that.
+ var recentlyPlayedBaseline = recentlyPlayedMovies.Count > categoryLimit
+ ? recentlyPlayedMovies.Take(categoryLimit).ToList()
+ : recentlyPlayedMovies;
+ var likedBaseline = likedMovies.Count > categoryLimit
+ ? likedMovies.Take(categoryLimit).ToList()
+ : likedMovies;
+
+ var batchQuery = new SimilarItemsQuery
+ {
+ User = user,
+ Limit = itemLimit,
+ DtoOptions = dtoOptions
+ };
+
+ var similarToRecentlyPlayed = await GetSimilarItemsRecommendationsAsync(
+ recentlyPlayedBaseline,
+ RecommendationType.SimilarToRecentlyPlayed,
+ batchQuery,
+ cancellationToken).ConfigureAwait(false);
+
+ var similarToLiked = await GetSimilarItemsRecommendationsAsync(
+ likedBaseline,
+ RecommendationType.SimilarToLikedItem,
+ batchQuery,
+ cancellationToken).ConfigureAwait(false);
+
+ var hasDirectorFromRecentlyPlayed = GetPersonRecommendations(user, recentDirectors, itemLimit, dtoOptions, RecommendationType.HasDirectorFromRecentlyPlayed, itemTypes);
+ var hasActorFromRecentlyPlayed = GetPersonRecommendations(user, recentActors, itemLimit, dtoOptions, RecommendationType.HasActorFromRecentlyPlayed, itemTypes);
+
+ // Use a single enumerator per list, listed twice so MoveNext advances it
+ // twice per round-robin pass (giving these categories double weight).
+ // IMPORTANT: Declare as IEnumerator<T> to box the List<T>.Enumerator struct once;
+ // using var would box separately per list insertion, creating independent copies.
+ IEnumerator<SimilarItemsRecommendation> similarToRecentlyPlayedEnum = similarToRecentlyPlayed.GetEnumerator();
+ IEnumerator<SimilarItemsRecommendation> similarToLikedEnum = similarToLiked.GetEnumerator();
+
+ var categoryTypes = new List<IEnumerator<SimilarItemsRecommendation>>
+ {
+ similarToRecentlyPlayedEnum,
+ similarToRecentlyPlayedEnum,
+ similarToLikedEnum,
+ similarToLikedEnum,
+ hasDirectorFromRecentlyPlayed.GetEnumerator(),
+ hasActorFromRecentlyPlayed.GetEnumerator()
+ };
+
+ var categories = new List<SimilarItemsRecommendation>();
+ while (categories.Count < categoryLimit)
+ {
+ var allEmpty = true;
+ foreach (var category in categoryTypes)
+ {
+ if (category.MoveNext())
+ {
+ categories.Add(category.Current);
+ allEmpty = false;
+
+ if (categories.Count >= categoryLimit)
+ {
+ break;
+ }
+ }
+ }
+
+ if (allEmpty)
+ {
+ break;
+ }
+ }
+
+ return [.. categories.OrderBy(i => i.RecommendationType)];
+ }
+
+ private async Task<IReadOnlyList<SimilarItemsRecommendation>> GetSimilarItemsRecommendationsAsync(
+ IReadOnlyList<BaseItem> baselineItems,
+ RecommendationType recommendationType,
+ SimilarItemsQuery query,
+ CancellationToken cancellationToken)
+ {
+ var batchProvider = _similarItemsProviders
+ .OfType<IBatchLocalSimilarItemsProvider>()
+ .FirstOrDefault();
+
+ if (batchProvider is null || baselineItems.Count == 0)
+ {
+ return [];
+ }
+
+ var batchResults = await batchProvider.GetBatchSimilarItemsAsync(baselineItems, query, cancellationToken).ConfigureAwait(false);
+
+ var recommendations = new List<SimilarItemsRecommendation>(baselineItems.Count);
+ foreach (var baseline in baselineItems)
+ {
+ if (batchResults.TryGetValue(baseline.Id, out var similar) && similar.Count > 0)
+ {
+ recommendations.Add(new SimilarItemsRecommendation
+ {
+ BaselineItemName = baseline.Name,
+ CategoryId = baseline.Id,
+ RecommendationType = recommendationType,
+ Items = similar
+ });
+ }
+ }
+
+ return recommendations;
+ }
+
+ private IEnumerable<SimilarItemsRecommendation> GetPersonRecommendations(
+ User? user,
+ IReadOnlyList<string> names,
+ int itemLimit,
+ DtoOptions dtoOptions,
+ RecommendationType type,
+ IReadOnlyList<BaseItemKind> itemTypes)
+ {
+ var personTypes = type == RecommendationType.HasDirectorFromRecentlyPlayed
+ ? [PersonType.Director]
+ : Array.Empty<string>();
+
+ foreach (var name in names)
+ {
+ var items = _libraryManager.GetItemList(new InternalItemsQuery(user)
+ {
+ Person = name,
+ Limit = itemLimit + 2,
+ PersonTypes = personTypes,
+ IncludeItemTypes = itemTypes.ToArray(),
+ IsMovie = true,
+ IsPlayed = false,
+ EnableGroupByMetadataKey = true,
+ DtoOptions = dtoOptions
+ })
+ .DistinctBy(i => i.GetProviderId(MetadataProvider.Imdb) ?? Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture))
+ .Take(itemLimit)
+ .ToList();
+
+ if (items.Count > 0)
+ {
+ yield return new SimilarItemsRecommendation
+ {
+ BaselineItemName = name,
+ CategoryId = name.GetMD5(),
+ RecommendationType = type,
+ Items = items
+ };
+ }
+ }
+ }
+
+ private IReadOnlyList<string> GetPeopleNames(IReadOnlyList<BaseItem> items, IReadOnlyList<string> personTypes)
+ {
+ var itemIds = items.Select(i => i.Id).ToArray();
+ return _libraryManager.GetPeopleNamesByItems(itemIds, personTypes)
+ .Values
+ .SelectMany(names => names)
+ .Distinct()
+ .ToArray();
+ }
+
+ private List<(BaseItem Item, float Score)> ResolveRemoteReferences(
+ IReadOnlyList<SimilarItemReference> references,
+ int providerOrder,
+ User? user,
+ DtoOptions dtoOptions,
+ BaseItemKind itemKind,
+ HashSet<Guid> excludeIds,
+ HashSet<string> excludeKeys)
+ {
+ if (references.Count == 0)
+ {
+ return [];
+ }
+
+ var resolvedByKey = new Dictionary<string, (BaseItem Item, float Score)>(StringComparer.OrdinalIgnoreCase);
+ var providerLookup = new Dictionary<(string ProviderName, string ProviderId), (float? Score, int Position)>(StringTupleComparer.Instance);
+
+ foreach (var (position, match) in references.Index())
+ {
+ var lookupKey = (match.ProviderName, match.ProviderId);
+ if (!providerLookup.TryGetValue(lookupKey, out var existing))
+ {
+ providerLookup[lookupKey] = (match.Score, position);
+ }
+ else if (match.Score > existing.Score || (match.Score == existing.Score && position < existing.Position))
+ {
+ providerLookup[lookupKey] = (match.Score, position);
+ }
+ }
+
+ var allProviderIds = providerLookup
+ .GroupBy(kvp => kvp.Key.ProviderName)
+ .ToDictionary(g => g.Key, g => g.Select(x => x.Key.ProviderId).ToArray());
+
+ var query = new InternalItemsQuery(user)
+ {
+ HasAnyProviderIds = allProviderIds,
+ IncludeItemTypes = [itemKind],
+ DtoOptions = dtoOptions
+ };
+
+ var items = _libraryManager.GetItemList(query);
+
+ foreach (var item in items)
+ {
+ if (excludeIds.Contains(item.Id))
+ {
+ continue;
+ }
+
+ var presentationKey = item.GetPresentationUniqueKey();
+ if (excludeKeys.Contains(presentationKey))
+ {
+ continue;
+ }
+
+ foreach (var providerName in allProviderIds.Keys)
+ {
+ if (item.TryGetProviderId(providerName, out var itemProviderId) && providerLookup.TryGetValue((providerName, itemProviderId), out var matchInfo))
+ {
+ var score = CalculateScore(matchInfo.Score, providerOrder, matchInfo.Position);
+ if (!resolvedByKey.TryGetValue(presentationKey, out var existing) || existing.Score < score)
+ {
+ resolvedByKey[presentationKey] = (item, score);
+ }
+
+ break;
+ }
+ }
+ }
+
+ foreach (var (key, entry) in resolvedByKey)
+ {
+ excludeIds.Add(entry.Item.Id);
+ excludeKeys.Add(key);
+ }
+
+ return [.. resolvedByKey.Values];
+ }
+
+ private static float CalculateScore(float? matchScore, int providerOrder, int position)
+ {
+ // Use provider-supplied score if available, otherwise derive from position
+ var baseScore = matchScore ?? (1.0f - (position * 0.02f));
+
+ // Apply small boost based on provider order (higher priority providers get small bonus)
+ var priorityBoost = Math.Max(0, 10 - providerOrder) * 0.005f;
+
+ return Math.Clamp(baseScore + priorityBoost, 0f, 1f);
+ }
+
+ private static int GetConfiguredSimilarProviderOrder(string[]? orderConfig, string providerName)
+ {
+ if (orderConfig is null || orderConfig.Length == 0)
+ {
+ return int.MaxValue;
+ }
+
+ var index = Array.FindIndex(orderConfig, name => string.Equals(name, providerName, StringComparison.OrdinalIgnoreCase));
+ return index >= 0 ? index : int.MaxValue;
+ }
+
+ private string GetSimilarItemsCachePath(string providerName, string baseItemType, Guid itemId)
+ {
+ var dataPath = Path.Combine(
+ _appPaths.CachePath,
+ $"{providerName.ToLowerInvariant()}-similar-{baseItemType.ToLowerInvariant()}");
+ return Path.Combine(dataPath, $"{itemId.ToString("N", CultureInfo.InvariantCulture)}.json");
+ }
+
+ private async Task<List<SimilarItemReference>?> TryReadSimilarItemsCacheAsync(string cachePath, CancellationToken cancellationToken)
+ {
+ var fileInfo = _fileSystem.GetFileSystemInfo(cachePath);
+ if (!fileInfo.Exists || fileInfo.Length == 0)
+ {
+ return null;
+ }
+
+ try
+ {
+ var stream = File.OpenRead(cachePath);
+ await using (stream.ConfigureAwait(false))
+ {
+ var cache = await JsonSerializer.DeserializeAsync<SimilarItemsCache>(stream, JsonDefaults.Options, cancellationToken).ConfigureAwait(false);
+ if (cache?.References is not null && DateTime.UtcNow < cache.ExpiresAt)
+ {
+ return cache.References;
+ }
+ }
+ }
+ catch (IOException ex)
+ {
+ _logger.LogWarning(ex, "Failed to read similar items cache from {CachePath}", cachePath);
+ }
+ catch (JsonException ex)
+ {
+ _logger.LogWarning(ex, "Failed to parse similar items cache from {CachePath}", cachePath);
+ }
+
+ return null;
+ }
+
+ private async Task SaveSimilarItemsCacheAsync(string cachePath, List<SimilarItemReference> references, TimeSpan cacheDuration, CancellationToken cancellationToken)
+ {
+ try
+ {
+ var directory = Path.GetDirectoryName(cachePath);
+ if (!string.IsNullOrEmpty(directory))
+ {
+ Directory.CreateDirectory(directory);
+ }
+
+ var cache = new SimilarItemsCache
+ {
+ References = references,
+ ExpiresAt = DateTime.UtcNow.Add(cacheDuration)
+ };
+
+ var stream = File.Create(cachePath);
+ await using (stream.ConfigureAwait(false))
+ {
+ await JsonSerializer.SerializeAsync(stream, cache, JsonDefaults.Options, cancellationToken).ConfigureAwait(false);
+ }
+ }
+ catch (IOException ex)
+ {
+ _logger.LogWarning(ex, "Failed to save similar items cache to {CachePath}", cachePath);
+ }
+ }
+
+ private sealed class SimilarItemsCache
+ {
+ public List<SimilarItemReference>? References { get; set; }
+
+ public DateTime ExpiresAt { get; set; }
+ }
+
+ private sealed class StringTupleComparer : IEqualityComparer<(string Key, string Value)>
+ {
+ public static readonly StringTupleComparer Instance = new();
+
+ public bool Equals((string Key, string Value) x, (string Key, string Value) y)
+ => string.Equals(x.Key, y.Key, StringComparison.OrdinalIgnoreCase) &&
+ string.Equals(x.Value, y.Value, StringComparison.OrdinalIgnoreCase);
+
+ public int GetHashCode((string Key, string Value) obj)
+ => HashCode.Combine(
+ StringComparer.OrdinalIgnoreCase.GetHashCode(obj.Key),
+ StringComparer.OrdinalIgnoreCase.GetHashCode(obj.Value));
+ }
+}
diff --git a/Emby.Server.Implementations/Library/SplashscreenPostScanTask.cs b/Emby.Server.Implementations/Library/SplashscreenPostScanTask.cs
index 71ce3b6012..7c605036cf 100644
--- a/Emby.Server.Implementations/Library/SplashscreenPostScanTask.cs
+++ b/Emby.Server.Implementations/Library/SplashscreenPostScanTask.cs
@@ -80,7 +80,7 @@ public class SplashscreenPostScanTask : ILibraryPostScanTask
ImageTypes = [imageType],
Limit = 30,
// TODO max parental rating configurable
- MaxParentalRating = new(10, null),
+ MaxParentalRating = new(13, null),
OrderBy =
[
(ItemSortBy.Random, SortOrder.Ascending)
diff --git a/Emby.Server.Implementations/Library/UserDataManager.cs b/Emby.Server.Implementations/Library/UserDataManager.cs
index 72c8d7a9d2..40cd2bb69c 100644
--- a/Emby.Server.Implementations/Library/UserDataManager.cs
+++ b/Emby.Server.Implementations/Library/UserDataManager.cs
@@ -177,53 +177,171 @@ namespace Emby.Server.Implementations.Library
};
}
- private UserItemData? GetUserData(User user, Guid itemId, List<string> keys)
+ /// <inheritdoc />
+ public Dictionary<Guid, UserItemData> GetUserDataBatch(IReadOnlyList<BaseItem> items, User user)
{
- var cacheKey = GetCacheKey(user.InternalId, itemId);
+ var result = new Dictionary<Guid, UserItemData>(items.Count);
+ var itemsNeedingQuery = new List<(BaseItem Item, List<string> Keys)>();
- if (_cache.TryGet(cacheKey, out var data))
+ foreach (var item in items)
{
- return data;
+ var cacheKey = GetCacheKey(user.InternalId, item.Id);
+ if (_cache.TryGet(cacheKey, out var cachedData))
+ {
+ result[item.Id] = cachedData;
+ }
+ else
+ {
+ var userData = item.UserData?.Where(e => e.UserId.Equals(user.Id)).Select(Map).FirstOrDefault();
+ if (userData is not null)
+ {
+ result[item.Id] = userData;
+ _cache.AddOrUpdate(cacheKey, userData);
+ }
+ else
+ {
+ var keys = item.GetUserDataKeys();
+ itemsNeedingQuery.Add((item, keys));
+ }
+ }
}
- data = GetUserDataInternal(user.Id, itemId, keys);
+ if (itemsNeedingQuery.Count == 0)
+ {
+ return result;
+ }
- if (data is null)
+ // Build a single query for all missing items
+ var allItemIds = itemsNeedingQuery.Select(x => x.Item.Id).ToList();
+ var allKeys = itemsNeedingQuery.SelectMany(x => x.Keys).Distinct().ToList();
+ if (allKeys.Count > 0)
{
- return new UserItemData()
+ using var context = _repository.CreateDbContext();
+ var userDataArray = context.UserData
+ .AsNoTracking()
+ .Where(e => e.UserId.Equals(user.Id))
+ .WhereOneOrMany(allItemIds, e => e.ItemId)
+ .WhereOneOrMany(allKeys, e => e.CustomDataKey)
+ .ToArray();
+
+ var userDataByItem = userDataArray.GroupBy(e => e.ItemId).ToDictionary(g => g.Key, g => g.ToArray());
+ foreach (var (item, keys) in itemsNeedingQuery)
{
- Key = keys[0],
- };
+ UserItemData userData;
+ if (userDataByItem.TryGetValue(item.Id, out var itemUserData) && itemUserData.Length > 0)
+ {
+ var directDataReference = itemUserData.FirstOrDefault(e => e.CustomDataKey == item.Id.ToString("N"));
+ userData = directDataReference is not null ? Map(directDataReference) : Map(itemUserData.First());
+ }
+ else
+ {
+ userData = new UserItemData { Key = keys.Count > 0 ? keys[0] : string.Empty };
+ }
+
+ result[item.Id] = userData;
+ var cacheKey = GetCacheKey(user.InternalId, item.Id);
+ _cache.AddOrUpdate(cacheKey, userData);
+ }
}
- return _cache.GetOrAdd(cacheKey, _ => data);
+ return result;
}
- private UserItemData? GetUserDataInternal(Guid userId, Guid itemId, List<string> keys)
+ /// <inheritdoc />
+ public VersionResumeData? GetResumeUserData(User user, BaseItem item)
{
- if (keys.Count == 0)
+ return GetResumeUserDataBatch([item], user).GetValueOrDefault(item.Id);
+ }
+
+ /// <inheritdoc />
+ public IReadOnlyDictionary<Guid, VersionResumeData> GetResumeUserDataBatch(IReadOnlyList<BaseItem> items, User user)
+ {
+ ArgumentNullException.ThrowIfNull(user);
+
+ var result = new Dictionary<Guid, VersionResumeData>();
+
+ // Candidate primaries: a directly queried version (PrimaryVersionId set) keeps its own data.
+ // Linked alternates are already known in memory; only the local-alternate existence check
+ // would otherwise hit the database (one query per item via Video.HasLocalAlternateVersions),
+ // so collect those ids and resolve them all in a single query below.
+ List<Video>? candidates = null;
+ List<Guid>? localProbeIds = null;
+ foreach (var item in items)
{
- return null;
+ if (item is not Video video || video.PrimaryVersionId.HasValue)
+ {
+ continue;
+ }
+
+ (candidates ??= []).Add(video);
+
+ if (video.LinkedAlternateVersions.Length == 0)
+ {
+ (localProbeIds ??= []).Add(video.Id);
+ }
+ }
+
+ if (candidates is null)
+ {
+ return result;
+ }
+
+ HashSet<Guid>? withLocalAlternates = null;
+ if (localProbeIds is not null)
+ {
+ using var dbContext = _repository.CreateDbContext();
+ withLocalAlternates = dbContext.LinkedChildren
+ .Where(lc => lc.ChildType == Jellyfin.Database.Implementations.Entities.LinkedChildType.LocalAlternateVersion
+ && localProbeIds.Contains(lc.ParentId))
+ .Select(lc => lc.ParentId)
+ .Distinct()
+ .ToHashSet();
}
- using var context = _repository.CreateDbContext();
- var userData = context.UserData.AsNoTracking().Where(e => e.ItemId == itemId && keys.Contains(e.CustomDataKey) && e.UserId.Equals(userId)).ToArray();
+ List<(Guid PrimaryId, IReadOnlyList<Video> Versions)>? versionGroups = null;
+ List<BaseItem>? allVersions = null;
- if (userData.Length > 0)
+ foreach (var video in candidates)
{
- var directDataReference = userData.FirstOrDefault(e => e.CustomDataKey == itemId.ToString("N"));
- if (directDataReference is not null)
+ // Only items that actually have alternate versions aggregate over them.
+ if (video.LinkedAlternateVersions.Length == 0
+ && (withLocalAlternates is null || !withLocalAlternates.Contains(video.Id)))
+ {
+ continue;
+ }
+
+ var versions = video.GetAllVersions();
+ if (versions.Count < 2)
{
- return Map(directDataReference);
+ continue;
}
- return Map(userData.First());
+ (versionGroups ??= []).Add((video.Id, versions));
+ (allVersions ??= []).AddRange(versions);
}
- return new UserItemData
+ if (versionGroups is null)
{
- Key = keys.Last()!
- };
+ return result;
+ }
+
+ var userDataByVersion = GetUserDataBatch(allVersions!.DistinctBy(i => i.Id).ToList(), user);
+
+ foreach (var (primaryId, versions) in versionGroups)
+ {
+ // Consider both in-progress and completed versions so a finished alternate still marks the primary as played.
+ var resumeVersion = VersionPlaybackSelector.SelectMostRecentlyPlayed(
+ versions,
+ version => userDataByVersion.GetValueOrDefault(version.Id),
+ data => data.PlaybackPositionTicks > 0 || data.Played);
+
+ if (resumeVersion is not null)
+ {
+ result[primaryId] = new VersionResumeData(resumeVersion.Id, userDataByVersion[resumeVersion.Id]);
+ }
+ }
+
+ return result;
}
/// <summary>
@@ -260,6 +378,10 @@ namespace Emby.Server.Implementations.Library
var dto = GetUserItemDataDto(userData, item.Id);
item.FillUserDataDtoValues(dto, userData, itemDto, user, options);
+
+ // For an item with alternate versions, surface the most recently played version's resume point.
+ GetResumeUserData(user, item)?.ApplyTo(dto);
+
return dto;
}
@@ -364,5 +486,41 @@ namespace Emby.Server.Implementations.Library
return playedToCompletion;
}
+
+ /// <inheritdoc />
+ public void ResetPlaybackStreamSelections(User user, BaseItem item)
+ {
+ ArgumentNullException.ThrowIfNull(user);
+ ArgumentNullException.ThrowIfNull(item);
+
+ using var dbContext = _repository.CreateDbContext();
+ var rows = dbContext.UserData
+ .Where(e => e.ItemId == item.Id && e.UserId == user.Id
+ && (e.AudioStreamIndex != null || e.SubtitleStreamIndex != null))
+ .ToList();
+
+ if (rows.Count == 0)
+ {
+ return;
+ }
+
+ foreach (var row in rows)
+ {
+ row.AudioStreamIndex = null;
+ row.SubtitleStreamIndex = null;
+ }
+
+ dbContext.SaveChanges();
+
+ var cacheKey = GetCacheKey(user.InternalId, item.Id);
+ if (_cache.TryGet(cacheKey, out var cached))
+ {
+ cached.AudioStreamIndex = null;
+ cached.SubtitleStreamIndex = null;
+ _cache.AddOrUpdate(cacheKey, cached);
+ }
+
+ item.UserData = dbContext.UserData.Where(e => e.ItemId == item.Id).AsNoTracking().ToArray();
+ }
}
}
diff --git a/Emby.Server.Implementations/Library/UserViewManager.cs b/Emby.Server.Implementations/Library/UserViewManager.cs
index 6fb53ff15d..9512b0ffd7 100644
--- a/Emby.Server.Implementations/Library/UserViewManager.cs
+++ b/Emby.Server.Implementations/Library/UserViewManager.cs
@@ -59,8 +59,8 @@ namespace Emby.Server.Implementations.Library
var collectionFolder = folder as ICollectionFolder;
var folderViewType = collectionFolder?.CollectionType;
- // Playlist library requires special handling because the folder only references user playlists
- if (folderViewType == CollectionType.playlists)
+ // Playlist and BoxSet libraries require special handling because the folder only references linked items
+ if (folderViewType == CollectionType.playlists || folderViewType == CollectionType.boxsets)
{
var items = folder.GetItemList(new InternalItemsQuery(user)
{
@@ -138,7 +138,7 @@ namespace Emby.Server.Implementations.Library
list = list.Where(i => !user.GetPreferenceValues<Guid>(PreferenceKind.MyMediaExcludes).Contains(i.Id)).ToList();
}
- var sorted = _libraryManager.Sort(list, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending).ToList();
+ var sorted = _libraryManager.Sort(list, user, [ItemSortBy.SortName], SortOrder.Ascending).ToList();
var orders = user.GetPreferenceValues<Guid>(PreferenceKind.OrderedViews);
return list
@@ -205,7 +205,7 @@ namespace Emby.Server.Implementations.Library
var libraryItems = GetItemsForLatestItems(request.User, request, options);
var list = new List<Tuple<BaseItem, List<BaseItem>>>();
-
+ var containerIndexMap = new Dictionary<Guid, int>();
foreach (var item in libraryItems)
{
// Only grab the index container for media
@@ -213,20 +213,16 @@ namespace Emby.Server.Implementations.Library
if (container is null)
{
- list.Add(new Tuple<BaseItem, List<BaseItem>>(null, new List<BaseItem> { item }));
+ list.Add(new Tuple<BaseItem, List<BaseItem>>(null!, new List<BaseItem> { item }));
+ }
+ else if (containerIndexMap.TryGetValue(container.Id, out var existingIndex))
+ {
+ list[existingIndex].Item2.Add(item);
}
else
{
- var current = list.FirstOrDefault(i => i.Item1 is not null && i.Item1.Id.Equals(container.Id));
-
- if (current is not null)
- {
- current.Item2.Add(item);
- }
- else
- {
- list.Add(new Tuple<BaseItem, List<BaseItem>>(container, new List<BaseItem> { item }));
- }
+ containerIndexMap[container.Id] = list.Count;
+ list.Add(new Tuple<BaseItem, List<BaseItem>>(container, new List<BaseItem> { item }));
}
if (list.Count >= request.Limit)
@@ -255,7 +251,7 @@ namespace Emby.Server.Implementations.Library
return _channelManager.GetLatestChannelItemsInternal(
new InternalItemsQuery(user)
{
- ChannelIds = new[] { parentId },
+ ChannelIds = [parentId],
IsPlayed = request.IsPlayed,
StartIndex = request.StartIndex,
Limit = request.Limit,
@@ -301,11 +297,11 @@ namespace Emby.Server.Implementations.Library
{
if (hasCollectionType.All(i => i.CollectionType == CollectionType.movies))
{
- includeItemTypes = new[] { BaseItemKind.Movie };
+ includeItemTypes = [BaseItemKind.Movie];
}
else if (hasCollectionType.All(i => i.CollectionType == CollectionType.tvshows))
{
- includeItemTypes = new[] { BaseItemKind.Episode };
+ includeItemTypes = [BaseItemKind.Episode];
}
}
}
@@ -344,29 +340,29 @@ namespace Emby.Server.Implementations.Library
}
var excludeItemTypes = includeItemTypes.Length == 0 && mediaTypes.Length == 0
- ? new[]
- {
+ ?
+ [
BaseItemKind.Person,
BaseItemKind.Studio,
BaseItemKind.Year,
BaseItemKind.MusicGenre,
BaseItemKind.Genre
- }
+ ]
: Array.Empty<BaseItemKind>();
var query = new InternalItemsQuery(user)
{
IncludeItemTypes = includeItemTypes,
- OrderBy = new[]
- {
+ OrderBy =
+ [
(ItemSortBy.DateCreated, SortOrder.Descending),
(ItemSortBy.SortName, SortOrder.Descending),
(ItemSortBy.ProductionYear, SortOrder.Descending)
- },
+ ],
IsFolder = includeItemTypes.Length == 0 ? false : null,
ExcludeItemTypes = excludeItemTypes,
IsVirtualItem = false,
- Limit = limit * 5,
+ Limit = limit * 2,
IsPlayed = isPlayed,
DtoOptions = options,
MediaTypes = mediaTypes
@@ -394,6 +390,12 @@ namespace Emby.Server.Implementations.Library
query.Limit = limit;
return _libraryManager.GetLatestItemList(query, parents, CollectionType.music);
}
+
+ if (collectionType == CollectionType.movies)
+ {
+ query.Limit = limit;
+ return _libraryManager.GetLatestItemList(query, parents, CollectionType.movies);
+ }
}
return _libraryManager.GetItemList(query, parents);
diff --git a/Emby.Server.Implementations/Library/Validators/ArtistsValidator.cs b/Emby.Server.Implementations/Library/Validators/ArtistsValidator.cs
index 7cc851b73b..fa7112eb90 100644
--- a/Emby.Server.Implementations/Library/Validators/ArtistsValidator.cs
+++ b/Emby.Server.Implementations/Library/Validators/ArtistsValidator.cs
@@ -50,21 +50,40 @@ public class ArtistsValidator
public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{
var names = _itemRepo.GetAllArtistNames();
+ var existingArtistIds = _libraryManager.GetItemIds(new InternalItemsQuery
+ {
+ IncludeItemTypes = [BaseItemKind.MusicArtist]
+ }).ToHashSet();
+
+ var existingArtists = _libraryManager.GetArtists(names);
var numComplete = 0;
var count = names.Count;
+ var refreshed = 0;
foreach (var name in names)
{
try
{
- var item = _libraryManager.GetArtist(name);
+ MusicArtist? item = null;
+ if (existingArtists.TryGetValue(name, out var artists) && artists.Length > 0)
+ {
+ item = artists.OrderBy(i => i.IsAccessedByName ? 1 : 0).First();
+ }
- await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
+ // Fall back to GetArtist if not found (creates new item if needed)
+ item ??= _libraryManager.GetArtist(name);
+ var isNew = !existingArtistIds.Contains(item.Id);
+ var neverRefreshed = item.DateLastRefreshed == default;
+
+ if (isNew || neverRefreshed)
+ {
+ await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
+ refreshed++;
+ }
}
catch (OperationCanceledException)
{
- // Don't clutter the log
throw;
}
catch (Exception ex)
@@ -80,31 +99,24 @@ public class ArtistsValidator
progress.Report(percent);
}
+ _logger.LogInformation("Refreshed metadata for {RefreshedCount} new artists out of {TotalCount} total", refreshed, count);
+
var deadEntities = _libraryManager.GetItemList(new InternalItemsQuery
{
IncludeItemTypes = [BaseItemKind.MusicArtist],
IsDeadArtist = true,
IsLocked = false
- }).Cast<MusicArtist>().ToList();
+ }).Cast<MusicArtist>()
+ .Where(item => item.IsAccessedByName)
+ .ToList();
foreach (var item in deadEntities)
{
- if (!item.IsAccessedByName)
- {
- continue;
- }
-
_logger.LogInformation("Deleting dead {ItemType} {ItemId} {ItemName}", item.GetType().Name, item.Id.ToString("N", CultureInfo.InvariantCulture), item.Name);
-
- _libraryManager.DeleteItem(
- item,
- new DeleteOptions
- {
- DeleteFileLocation = false
- },
- false);
}
+ _libraryManager.DeleteItemsUnsafeFast(deadEntities, deleteSourceFiles: true);
+
progress.Report(100);
}
}
diff --git a/Emby.Server.Implementations/Library/Validators/CollectionPostScanTask.cs b/Emby.Server.Implementations/Library/Validators/CollectionPostScanTask.cs
index e62c638ed6..e3ef75b9ee 100644
--- a/Emby.Server.Implementations/Library/Validators/CollectionPostScanTask.cs
+++ b/Emby.Server.Implementations/Library/Validators/CollectionPostScanTask.cs
@@ -74,7 +74,7 @@ public class CollectionPostScanTask : ILibraryPostScanTask
foreach (var m in movies)
{
- if (m is Movie movie && !string.IsNullOrEmpty(movie.CollectionName))
+ if (m is Movie movie && !string.IsNullOrEmpty(movie.CollectionName) && !movie.PrimaryVersionId.HasValue)
{
if (collectionNameMoviesMap.TryGetValue(movie.CollectionName, out var movieList))
{
diff --git a/Emby.Server.Implementations/Library/Validators/GenresValidator.cs b/Emby.Server.Implementations/Library/Validators/GenresValidator.cs
index fbfc9f7d54..fc5a2fa0c5 100644
--- a/Emby.Server.Implementations/Library/Validators/GenresValidator.cs
+++ b/Emby.Server.Implementations/Library/Validators/GenresValidator.cs
@@ -1,5 +1,6 @@
using System;
using System.Globalization;
+using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Enums;
@@ -48,17 +49,40 @@ public class GenresValidator
public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{
var names = _itemRepo.GetGenreNames();
+ var existingGenreIds = _libraryManager.GetItemIds(new InternalItemsQuery
+ {
+ IncludeItemTypes = [BaseItemKind.Genre]
+ }).ToHashSet();
+
+ var existingGenres = _libraryManager.GetItemList(new InternalItemsQuery
+ {
+ IncludeItemTypes = [BaseItemKind.Genre]
+ }).Cast<Genre>()
+ .GroupBy(g => g.Name, StringComparer.OrdinalIgnoreCase)
+ .ToDictionary(g => g.Key, g => g.First(), StringComparer.OrdinalIgnoreCase);
var numComplete = 0;
var count = names.Count;
+ var refreshed = 0;
foreach (var name in names)
{
try
{
- var item = _libraryManager.GetGenre(name);
+ Genre? item = null;
+ if (existingGenres.TryGetValue(name, out var existingGenre))
+ {
+ item = existingGenre;
+ }
+
+ // Fall back to GetGenre if not found (creates new item if needed)
+ item ??= _libraryManager.GetGenre(name);
- await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
+ if (!existingGenreIds.Contains(item.Id))
+ {
+ await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
+ refreshed++;
+ }
}
catch (OperationCanceledException)
{
@@ -78,6 +102,8 @@ public class GenresValidator
progress.Report(percent);
}
+ _logger.LogInformation("Refreshed metadata for {RefreshedCount} new genres out of {TotalCount} total", refreshed, count);
+
var deadEntities = _libraryManager.GetItemList(new InternalItemsQuery
{
IncludeItemTypes = [BaseItemKind.Genre, BaseItemKind.MusicGenre],
@@ -88,16 +114,10 @@ public class GenresValidator
foreach (var item in deadEntities)
{
_logger.LogInformation("Deleting dead {ItemType} {ItemId} {ItemName}", item.GetType().Name, item.Id.ToString("N", CultureInfo.InvariantCulture), item.Name);
-
- _libraryManager.DeleteItem(
- item,
- new DeleteOptions
- {
- DeleteFileLocation = false
- },
- false);
}
+ _libraryManager.DeleteItemsUnsafeFast(deadEntities, deleteSourceFiles: true);
+
progress.Report(100);
}
}
diff --git a/Emby.Server.Implementations/Library/Validators/MusicGenresValidator.cs b/Emby.Server.Implementations/Library/Validators/MusicGenresValidator.cs
index 6203bce2bc..4365707529 100644
--- a/Emby.Server.Implementations/Library/Validators/MusicGenresValidator.cs
+++ b/Emby.Server.Implementations/Library/Validators/MusicGenresValidator.cs
@@ -1,6 +1,9 @@
using System;
+using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Enums;
+using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using Microsoft.Extensions.Logging;
@@ -45,17 +48,25 @@ public class MusicGenresValidator
public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{
var names = _itemRepo.GetMusicGenreNames();
+ var existingMusicGenreIds = _libraryManager.GetItemIds(new InternalItemsQuery
+ {
+ IncludeItemTypes = [BaseItemKind.MusicGenre]
+ }).ToHashSet();
var numComplete = 0;
var count = names.Count;
+ var refreshed = 0;
foreach (var name in names)
{
try
{
var item = _libraryManager.GetMusicGenre(name);
-
- await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
+ if (!existingMusicGenreIds.Contains(item.Id))
+ {
+ await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
+ refreshed++;
+ }
}
catch (OperationCanceledException)
{
@@ -75,6 +86,8 @@ public class MusicGenresValidator
progress.Report(percent);
}
+ _logger.LogInformation("Refreshed metadata for {RefreshedCount} new music genres out of {TotalCount} total", refreshed, count);
+
progress.Report(100);
}
}
diff --git a/Emby.Server.Implementations/Library/Validators/PeopleValidator.cs b/Emby.Server.Implementations/Library/Validators/PeopleValidator.cs
index f9a6f0d19e..dacef102dd 100644
--- a/Emby.Server.Implementations/Library/Validators/PeopleValidator.cs
+++ b/Emby.Server.Implementations/Library/Validators/PeopleValidator.cs
@@ -109,7 +109,7 @@ public class PeopleValidator
var i = 0;
foreach (var item in deadEntities.Chunk(500))
{
- _libraryManager.DeleteItemsUnsafeFast(item);
+ _libraryManager.DeleteItemsUnsafeFast(item, true);
subProgress.Report(100f / deadEntities.Count * (i++ * 100));
}
diff --git a/Emby.Server.Implementations/Library/Validators/StudiosValidator.cs b/Emby.Server.Implementations/Library/Validators/StudiosValidator.cs
index 5b87e4d9d0..88f86ae6ca 100644
--- a/Emby.Server.Implementations/Library/Validators/StudiosValidator.cs
+++ b/Emby.Server.Implementations/Library/Validators/StudiosValidator.cs
@@ -1,5 +1,6 @@
using System;
using System.Globalization;
+using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Enums;
@@ -49,17 +50,40 @@ public class StudiosValidator
public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{
var names = _itemRepo.GetStudioNames();
+ var existingStudioIds = _libraryManager.GetItemIds(new InternalItemsQuery
+ {
+ IncludeItemTypes = [BaseItemKind.Studio]
+ }).ToHashSet();
+
+ var existingStudios = _libraryManager.GetItemList(new InternalItemsQuery
+ {
+ IncludeItemTypes = [BaseItemKind.Studio]
+ }).Cast<Studio>()
+ .GroupBy(s => s.Name, StringComparer.OrdinalIgnoreCase)
+ .ToDictionary(g => g.Key, g => g.First(), StringComparer.OrdinalIgnoreCase);
var numComplete = 0;
var count = names.Count;
+ var refreshed = 0;
foreach (var name in names)
{
try
{
- var item = _libraryManager.GetStudio(name);
+ Studio? item = null;
+ if (existingStudios.TryGetValue(name, out var existingStudio))
+ {
+ item = existingStudio;
+ }
+
+ // Fall back to GetStudio if not found (creates new item if needed)
+ item ??= _libraryManager.GetStudio(name);
- await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
+ if (!existingStudioIds.Contains(item.Id))
+ {
+ await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
+ refreshed++;
+ }
}
catch (OperationCanceledException)
{
@@ -79,6 +103,8 @@ public class StudiosValidator
progress.Report(percent);
}
+ _logger.LogInformation("Refreshed metadata for {RefreshedCount} new studios out of {TotalCount} total", refreshed, count);
+
var deadEntities = _libraryManager.GetItemList(new InternalItemsQuery
{
IncludeItemTypes = [BaseItemKind.Studio],
@@ -89,16 +115,10 @@ public class StudiosValidator
foreach (var item in deadEntities)
{
_logger.LogInformation("Deleting dead {ItemType} {ItemId} {ItemName}", item.GetType().Name, item.Id.ToString("N", CultureInfo.InvariantCulture), item.Name);
-
- _libraryManager.DeleteItem(
- item,
- new DeleteOptions
- {
- DeleteFileLocation = false
- },
- false);
}
+ _libraryManager.DeleteItemsUnsafeFast(deadEntities, deleteSourceFiles: true);
+
progress.Report(100);
}
}
diff --git a/Emby.Server.Implementations/Localization/Core/ab.json b/Emby.Server.Implementations/Localization/Core/ab.json
index bc6062f429..d67f2d67e9 100644
--- a/Emby.Server.Implementations/Localization/Core/ab.json
+++ b/Emby.Server.Implementations/Localization/Core/ab.json
@@ -1,3 +1,3 @@
{
- "Albums": "аальбомқәа"
+ "AppDeviceValues": "Апп: {0}, Априбор: {1}"
}
diff --git a/Emby.Server.Implementations/Localization/Core/af.json b/Emby.Server.Implementations/Localization/Core/af.json
index 1dce589234..308341ad49 100644
--- a/Emby.Server.Implementations/Localization/Core/af.json
+++ b/Emby.Server.Implementations/Localization/Core/af.json
@@ -1,11 +1,8 @@
{
"Artists": "Kunstenare",
- "Channels": "Kanale",
"Folders": "Lêergidse",
"Favorites": "Gunstelinge",
"HeaderFavoriteShows": "Gunsteling Vertonings",
- "ValueSpecialEpisodeName": "Spesiale - {0}",
- "HeaderAlbumArtists": "Album kunstenaars",
"Books": "Boeke",
"HeaderNextUp": "Volgende",
"Movies": "Flieks",
@@ -13,24 +10,13 @@
"HeaderContinueWatching": "Hou aan kyk",
"HeaderFavoriteEpisodes": "Gunsteling Episodes",
"Photos": "Foto's",
- "Playlists": "Snitlyste",
- "HeaderFavoriteArtists": "Gunsteling Kunstenaars",
- "HeaderFavoriteAlbums": "Gunsteling Albums",
- "Sync": "Sinkroniseer",
- "HeaderFavoriteSongs": "Gunsteling Liedjies",
- "Songs": "Liedjies",
- "DeviceOnlineWithName": "{0} is aanlyn",
- "DeviceOfflineWithName": "{0} is ontkoppel",
"Collections": "Versamelings",
"Inherit": "Ontvang",
"HeaderLiveTV": "Lewendige TV",
- "Application": "Program",
"AppDeviceValues": "App: {0}, Toestel: {1}",
"VersionNumber": "Weergawe {0}",
- "ValueHasBeenAddedToLibrary": "{0} is by jou media biblioteek bygevoeg",
"UserStoppedPlayingItemWithValues": "{0} het klaar {1} op {2} gespeel",
"UserStartedPlayingItemWithValues": "{0} is besig om {1} op {2} te speel",
- "UserPolicyUpdatedWithName": "Gebruiker beleid is verander vir {0}",
"UserPasswordChangedWithName": "Gebruiker {0} se wagwoord is verander",
"UserOnlineFromDevice": "{0} is aanlyn van {1}",
"UserOfflineFromDevice": "{0} is ontkoppel van {1}",
@@ -38,19 +24,13 @@
"UserDownloadingItemWithValues": "{0} is besig om {1} af te laai",
"UserDeletedWithName": "Gebruiker {0} is verwyder",
"UserCreatedWithName": "Gebruiker {0} is geskep",
- "User": "Gebruiker",
"TvShows": "TV Programme",
- "System": "Stelsel",
"SubtitleDownloadFailureFromForItem": "Ondertitels het misluk om af te laai van {0} vir {1}",
"StartupEmbyServerIsLoading": "Jellyfin Bediener is besig om te laai. Probeer weer in 'n kort tyd.",
- "ServerNameNeedsToBeRestarted": "{0} moet herbegin word",
- "ScheduledTaskStartedWithName": "{0} het begin",
"ScheduledTaskFailedWithName": "{0} het misluk",
- "ProviderValue": "Voorsiener: {0}",
"PluginUpdatedWithName": "{0} was opgedateer",
"PluginUninstalledWithName": "{0} was verwyder",
"PluginInstalledWithName": "{0} is geïnstalleer",
- "Plugin": "Inprop module",
"NotificationOptionVideoPlaybackStopped": "Video terugspeel het gestop",
"NotificationOptionVideoPlayback": "Video terugspeel het begin",
"NotificationOptionUserLockedOut": "Gebruiker uitgeslyt",
@@ -74,23 +54,14 @@
"MusicVideos": "Musiek Videos",
"Music": "Musiek",
"MixedContent": "Gemengde inhoud",
- "MessageServerConfigurationUpdated": "Bediener konfigurasie is opgedateer",
- "MessageNamedServerConfigurationUpdatedWithValue": "Bediener konfigurasie seksie {0} is opgedateer",
- "MessageApplicationUpdatedTo": "Jellyfin Bediener is opgedateer na {0}",
- "MessageApplicationUpdated": "Jellyfin Bediener is opgedateer",
"Latest": "Nuutste",
"LabelRunningTimeValue": "Werktyd: {0}",
"LabelIpAddressValue": "IP adres: {0}",
- "ItemRemovedWithName": "{0} is uit versameling verwyder",
- "ItemAddedWithName": "{0} is by die versameling gevoeg",
"HomeVideos": "Tuis Videos",
- "HeaderRecordingGroups": "Groep Opnames",
"Genres": "Genres",
"FailedLoginAttemptWithUserName": "Mislukte aanmeldpoging van {0}",
"ChapterNameValue": "Hoofstuk {0}",
- "CameraImageUploadedFrom": "'n Nuwe kamera foto is opgelaai vanaf {0}",
"AuthenticationSucceededWithUserName": "{0} suksesvol geverifieer",
- "Albums": "Albums",
"TasksChannelsCategory": "Internet kanale",
"TasksApplicationCategory": "aansoek",
"TasksLibraryCategory": "biblioteek",
@@ -128,12 +99,12 @@
"TaskRefreshTrickplayImagesDescription": "Skep fopspeel voorskou vir videos in aangeskakelde media versameling.",
"TaskAudioNormalizationDescription": "Skandeer lêers vir oudio-normaliseringsdata.",
"TaskAudioNormalization": "Odio Normalisering",
- "TaskCleanCollectionsAndPlaylists": "Maak versamelings en snitlyste skoon",
- "TaskCleanCollectionsAndPlaylistsDescription": "Verwyder items uit versamelings en snitlyste wat nie meer bestaan nie.",
"TaskDownloadMissingLyrics": "Laai tekorte lirieke af",
"TaskDownloadMissingLyricsDescription": "Laai lirieke af vir liedjies",
"TaskExtractMediaSegments": "Media Segment Skandeer",
"TaskExtractMediaSegmentsDescription": "Onttrek of verkry mediasegmente van MediaSegment-geaktiveerde inproppe.",
"TaskMoveTrickplayImages": "Migreer Trickplay Beeldligging",
- "TaskMoveTrickplayImagesDescription": "Skuif ontstaande trickplay lêers volgens die biblioteekinstellings."
+ "TaskMoveTrickplayImagesDescription": "Skuif ontstaande trickplay lêers volgens die biblioteekinstellings.",
+ "CleanupUserDataTask": "Gebruikers data skoon maak taak",
+ "CleanupUserDataTaskDescription": "Maak alle gebruikers data (kykstatus, gunstelingstatus, ens.) skoon van media wat nie meer vir ten minste 90 dae teenwoordig is nie."
}
diff --git a/Emby.Server.Implementations/Localization/Core/ar.json b/Emby.Server.Implementations/Localization/Core/ar.json
index 7ce8baef59..17af935562 100644
--- a/Emby.Server.Implementations/Localization/Core/ar.json
+++ b/Emby.Server.Implementations/Localization/Core/ar.json
@@ -1,141 +1,112 @@
{
- "Albums": "ألبومات",
- "AppDeviceValues": "تطبيق: {0}, جهاز: {1}",
- "Application": "تطبيق",
- "Artists": "فنانون",
- "AuthenticationSucceededWithUserName": "نجحت عملية التوثيق بـ {0}",
+ "AppDeviceValues": "التطبيق: {0}، الجهاز: {1}",
+ "Artists": "الفنانون",
+ "AuthenticationSucceededWithUserName": "تمت مصادقة {0} بنجاح",
"Books": "الكتب",
- "CameraImageUploadedFrom": "رُفعت صورة الكاميرا الجديدة من {0}",
- "Channels": "القنوات",
"ChapterNameValue": "الفصل {0}",
- "Collections": "مجموعات",
- "DeviceOfflineWithName": "قُطِع الاتصال ب{0}",
- "DeviceOnlineWithName": "{0} متصل",
- "FailedLoginAttemptWithUserName": "محاولة تسجيل الدخول فاشلة من {0}",
+ "Collections": "المجموعات",
+ "FailedLoginAttemptWithUserName": "محاولة تسجيل دخول فاشلة من {0}",
"Favorites": "المفضلة",
"Folders": "المجلدات",
- "Genres": "التصنيفات",
- "HeaderAlbumArtists": "فناني الألبوم",
+ "Genres": "الأنواع",
"HeaderContinueWatching": "متابعة المشاهدة",
- "HeaderFavoriteAlbums": "الألبومات المفضلة",
- "HeaderFavoriteArtists": "الفنانون المفضلون",
"HeaderFavoriteEpisodes": "الحلقات المفضلة",
"HeaderFavoriteShows": "المسلسلات المفضلة",
- "HeaderFavoriteSongs": "الأغاني المفضلة",
- "HeaderLiveTV": "التلفاز المباشر",
+ "HeaderLiveTV": "البث التلفزيوني المباشر",
"HeaderNextUp": "التالي",
- "HeaderRecordingGroups": "مجموعات التسجيل",
- "HomeVideos": "الفيديوهات الشخصية",
- "Inherit": "توريث",
- "ItemAddedWithName": "أُضيف {0} للمكتبة",
- "ItemRemovedWithName": "أُزيل {0} من المكتبة",
- "LabelIpAddressValue": "عنوان الآي بي: {0}",
+ "HomeVideos": "فيديوهات منزلية",
+ "Inherit": "وراثة",
+ "LabelIpAddressValue": "عنوان IP: {0}",
"LabelRunningTimeValue": "مدة التشغيل: {0}",
"Latest": "الأحدث",
- "MessageApplicationUpdated": "حُدث خادم Jellyfin",
- "MessageApplicationUpdatedTo": "حُدث خادم Jellyfin إلى {0}",
- "MessageNamedServerConfigurationUpdatedWithValue": "حُدثت إعدادات الخادم في قسم {0}",
- "MessageServerConfigurationUpdated": "حُدثت إعدادات الخادم",
"MixedContent": "محتوى مختلط",
"Movies": "الأفلام",
"Music": "الموسيقى",
"MusicVideos": "الفيديوهات الموسيقية",
"NameInstallFailed": "فشل تثبيت {0}",
"NameSeasonNumber": "الموسم {0}",
- "NameSeasonUnknown": "الموسم غير معروف",
- "NewVersionIsAvailable": "نسخة جديدة من خادم Jellyfin متوفرة للتحميل.",
- "NotificationOptionApplicationUpdateAvailable": "يوجد تحديث للتطبيق",
- "NotificationOptionApplicationUpdateInstalled": "نُصب تحديث التطبيق",
- "NotificationOptionAudioPlayback": "بدأ تشغيل المقطع الصوتي",
- "NotificationOptionAudioPlaybackStopped": "أُوقف تشغيل المقطع الصوتي",
- "NotificationOptionCameraImageUploaded": "رُفعت صورة الكاميرا",
- "NotificationOptionInstallationFailed": "فشل في التثبيت",
- "NotificationOptionNewLibraryContent": "أُضيف محتوى جديدا",
- "NotificationOptionPluginError": "فشل في الملحق",
- "NotificationOptionPluginInstalled": "ثُبتت الملحق",
+ "NameSeasonUnknown": "موسم غير معروف",
+ "NewVersionIsAvailable": "يتوفر إصدار جديد من خادم Jellyfin للتنزيل.",
+ "NotificationOptionApplicationUpdateAvailable": "تحديث التطبيق متاح",
+ "NotificationOptionApplicationUpdateInstalled": "تم تثبيت تحديث التطبيق",
+ "NotificationOptionAudioPlayback": "بدأ تشغيل الصوت",
+ "NotificationOptionAudioPlaybackStopped": "توقف تشغيل الصوت",
+ "NotificationOptionCameraImageUploaded": "تم رفع صورة كاميرا",
+ "NotificationOptionInstallationFailed": "فشل التثبيت",
+ "NotificationOptionNewLibraryContent": "تمت إضافة محتوى جديد",
+ "NotificationOptionPluginError": "خطأ في الملحق",
+ "NotificationOptionPluginInstalled": "تم تثبيت الملحق",
"NotificationOptionPluginUninstalled": "تمت إزالة الملحق",
- "NotificationOptionPluginUpdateInstalled": "تم تثبيت تحديثات الملحق",
- "NotificationOptionServerRestartRequired": "يجب إعادة تشغيل الخادم",
- "NotificationOptionTaskFailed": "فشل في المهمة المجدولة",
- "NotificationOptionUserLockedOut": "تم إقفال حساب المستخدم",
+ "NotificationOptionPluginUpdateInstalled": "تم تحديث الملحق",
+ "NotificationOptionServerRestartRequired": "مطلوب إعادة تشغيل الخادم",
+ "NotificationOptionTaskFailed": "فشل المهمة المجدولة",
+ "NotificationOptionUserLockedOut": "تم قفل حساب المستخدم",
"NotificationOptionVideoPlayback": "بدأ تشغيل الفيديو",
- "NotificationOptionVideoPlaybackStopped": "تم إيقاف تشغيل الفيديو",
+ "NotificationOptionVideoPlaybackStopped": "توقف تشغيل الفيديو",
"Photos": "الصور",
- "Playlists": "قوائم التشغيل",
- "Plugin": "الملحق",
"PluginInstalledWithName": "تم تثبيت {0}",
"PluginUninstalledWithName": "تمت إزالة {0}",
"PluginUpdatedWithName": "تم تحديث {0}",
- "ProviderValue": "المزود: {0}",
- "ScheduledTaskFailedWithName": "فشلت العملية {0}",
- "ScheduledTaskStartedWithName": "تم بدء العملية {0}",
- "ServerNameNeedsToBeRestarted": "يحتاج {0} لإعادة التشغيل",
- "Shows": "العروض",
- "Songs": "الأغاني",
- "StartupEmbyServerIsLoading": "يتم تحميل خادم Jellyfin . الرجاء المحاولة بعد قليل.",
- "SubtitleDownloadFailureFromForItem": "فشل تحميل الترجمات من {0} ل {1}",
- "Sync": "مزامنة",
- "System": "النظام",
+ "ScheduledTaskFailedWithName": "فشلت {0}",
+ "Shows": "المسلسلات",
+ "StartupEmbyServerIsLoading": "يتم الآن تحميل خادم Jellyfin. يرجى المحاولة مرة أخرى بعد قليل.",
+ "SubtitleDownloadFailureFromForItem": "فشل تنزيل الترجمات من {0} لـ {1}",
"TvShows": "البرامج التلفزيونية",
- "User": "المستخدم",
"UserCreatedWithName": "تم إنشاء المستخدم {0}",
"UserDeletedWithName": "تم حذف المستخدم {0}",
- "UserDownloadingItemWithValues": "يقوم {0} بتنزيل {1}",
- "UserLockedOutWithName": "تم منع المستخدم {0} من الدخول",
- "UserOfflineFromDevice": "تم قطع اتصال {0} من {1}",
- "UserOnlineFromDevice": "{0} متصل عبر {1}",
- "UserPasswordChangedWithName": "تم تغيير كلمة السر للمستخدم {0}",
- "UserPolicyUpdatedWithName": "تم تحديث سياسة المستخدم {0}",
- "UserStartedPlayingItemWithValues": "قام {0} ببدء تشغيل {1} على {2}",
- "UserStoppedPlayingItemWithValues": "قام {0} بإيقاف تشغيل {1} على {2}",
- "ValueHasBeenAddedToLibrary": "تمت اضافت {0} إلى مكتبة الوسائط",
- "ValueSpecialEpisodeName": "حلقة خاصه - {0}",
+ "UserDownloadingItemWithValues": "{0} يقوم بتنزيل {1}",
+ "UserLockedOutWithName": "تم قفل حساب المستخدم {0}",
+ "UserOfflineFromDevice": "انقطع اتصال {0} من {1}",
+ "UserOnlineFromDevice": "{0} متصل من {1}",
+ "UserPasswordChangedWithName": "تم تغيير كلمة المرور للمستخدم {0}",
+ "UserStartedPlayingItemWithValues": "{0} يقوم بتشغيل {1} على {2}",
+ "UserStoppedPlayingItemWithValues": "أنهى {0} تشغيل {1} على {2}",
"VersionNumber": "الإصدار {0}",
- "TaskCleanCacheDescription": "يحذف الملفات المؤقتة التي لم يعد النظام بحاجة إليها.",
- "TaskCleanCache": "حذف الملفات المؤقتة",
+ "TaskCleanCacheDescription": "يحذف ملفات ذاكرة التخزين المؤقت التي لم يعد النظام بحاجة إليها.",
+ "TaskCleanCache": "تنظيف مجلد ذاكرة التخزين المؤقت",
"TasksChannelsCategory": "قنوات الإنترنت",
- "TasksLibraryCategory": "مكتبة",
- "TasksMaintenanceCategory": "صيانة",
- "TaskRefreshLibraryDescription": "يفحص مكتبة الوسائط الخاصة بك باحثا عن ملفات جديدة، ومن ثم يُحدث البيانات الوصفية.",
- "TaskRefreshLibrary": "افحص مكتبة الوسائط",
- "TaskRefreshChapterImagesDescription": "يُنشئ صور مصغرة لمقاطع الفيديو التي تحتوي على فصول.",
- "TaskRefreshChapterImages": "استخراج صور الفصل",
- "TasksApplicationCategory": "تطبيق",
- "TaskDownloadMissingSubtitlesDescription": "يبحث في الإنترنت على الترجمات الناقصة استنادا على البيانات الوصفية.",
- "TaskDownloadMissingSubtitles": "تحميل الترجمات الناقصة",
- "TaskRefreshChannelsDescription": "يحدث معلومات قنوات الإنترنت.",
- "TaskRefreshChannels": "إعادة تحديث القنوات",
- "TaskCleanTranscodeDescription": "يحذف ملفات الترميز الأقدم من يوم واحد.",
- "TaskCleanTranscode": "حذف ما بمجلد الترميز",
- "TaskUpdatePluginsDescription": "تحميل وتثبيت الإضافات التي تم تفعيل التحديث التلقائي لها.",
- "TaskUpdatePlugins": "تحديث الإضافات",
- "TaskRefreshPeopleDescription": "يقوم بتحديث البيانات الوصفية للممثلين والمخرجين في مكتبة الوسائط الخاصة بك.",
- "TaskRefreshPeople": "إعادة تحميل الأشخاص",
- "TaskCleanLogsDescription": "يحذف السجلات الأقدم من {0} يوم.",
- "TaskCleanLogs": "حذف مسار السجل",
- "TaskCleanActivityLogDescription": "يحذف سجل الأنشطة الأقدم من الوقت الذي تم تحديده.",
- "TaskCleanActivityLog": "حذف سجل الأنشطة",
- "Default": "افتراضي",
- "Undefined": "غير معرف",
- "Forced": "ملحقة",
- "TaskOptimizeDatabaseDescription": "يضغط قاعدة البيانات ويقتطع المساحة الحرة. تشغيل هذه المهمة بعد فحص المكتبة أو إجراء تغييرات أخرى تتضمن تعديلات في قاعدة البيانات قد تؤدي إلى تحسين الأداء.",
+ "TasksLibraryCategory": "المكتبة",
+ "TasksMaintenanceCategory": "الصيانة",
+ "TaskRefreshLibraryDescription": "يفحص مكتبة المحتوى الخاصة بك بحثاً عن ملفات جديدة ويحدّث البيانات الوصفية.",
+ "TaskRefreshLibrary": "فحص مكتبة المحتوى",
+ "TaskRefreshChapterImagesDescription": "ينشئ صوراً مصغرة للفيديوهات التي تحتوي على فصول.",
+ "TaskRefreshChapterImages": "استخراج صور الفصول",
+ "TasksApplicationCategory": "التطبيق",
+ "TaskDownloadMissingSubtitlesDescription": "يبحث في الإنترنت عن الترجمات المفقودة بناءً على إعدادات البيانات الوصفية.",
+ "TaskDownloadMissingSubtitles": "تنزيل الترجمات المفقودة",
+ "TaskRefreshChannelsDescription": "يحدّث معلومات قنوات الإنترنت.",
+ "TaskRefreshChannels": "تحديث القنوات",
+ "TaskCleanTranscodeDescription": "يحذف ملفات تحويل الترميز التي مر عليها أكثر من يوم واحد.",
+ "TaskCleanTranscode": "تنظيف مجلد تحويل الترميز",
+ "TaskUpdatePluginsDescription": "ينزّل ويثبّت التحديثات للملحقات المهيأة للتحديث التلقائي.",
+ "TaskUpdatePlugins": "تحديث الملحقات",
+ "TaskRefreshPeopleDescription": "يحدّث البيانات الوصفية للممثلين والمخرجين في مكتبة المحتوى الخاصة بك.",
+ "TaskRefreshPeople": "تحديث الأشخاص",
+ "TaskCleanLogsDescription": "يحذف ملفات السجل التي يزيد عمرها عن {0} أيام.",
+ "TaskCleanLogs": "تنظيف مجلد السجلات",
+ "TaskCleanActivityLogDescription": "يحذف إدخالات سجل النشاط الأقدم من العمر المحدد.",
+ "TaskCleanActivityLog": "تنظيف سجل النشاط",
+ "Default": "الافتراضي",
+ "Undefined": "غير محدد",
+ "Forced": "إجباري",
+ "TaskOptimizeDatabaseDescription": "يضغط قاعدة البيانات ويقلل المساحة الحرة. قد يؤدي تشغيل هذه المهمة بعد فحص المكتبة أو إجراء تغييرات أخرى تتضمن تعديلات على قاعدة البيانات إلى تحسين الأداء.",
"TaskOptimizeDatabase": "تحسين قاعدة البيانات",
- "TaskKeyframeExtractorDescription": "يستخرج الإطارات الرئيسية من ملفات الفيديو لكي ينشئ قوائم تشغيل بث HTTP المباشر. قد تستمر هذه العملية لوقت طويل.",
- "TaskKeyframeExtractor": "مستخرج الإطار الرئيسي",
+ "TaskKeyframeExtractorDescription": "يستخرج الإطارات الرئيسية من ملفات الفيديو لإنشاء قوائم تشغيل HLS أكثر دقة. قد يستغرق تشغيل هذه المهمة وقتاً طويلاً.",
+ "TaskKeyframeExtractor": "مستخرج الإطارات الرئيسية",
"External": "خارجي",
- "HearingImpaired": "ضعاف السمع",
- "TaskRefreshTrickplayImages": "توليد صور المعاينة السريعة",
- "TaskRefreshTrickplayImagesDescription": "يُولّد معاينات تنقل سريع لمقاطع الفيديو ضمن المكتبات المفعّلة.",
- "TaskCleanCollectionsAndPlaylists": "حذف المجموعات وقوائم التشغيل",
- "TaskCleanCollectionsAndPlaylistsDescription": "حذف عناصر من المجموعات وقوائم التشغيل التي لم تعد موجودة.",
- "TaskAudioNormalization": "تسوية الصوت",
- "TaskAudioNormalizationDescription": "مسح الملفات لتطبيع بيانات الصوت.",
- "TaskDownloadMissingLyrics": "تنزيل عبارات القصيدة",
- "TaskDownloadMissingLyricsDescription": "كلمات",
- "TaskExtractMediaSegments": "فحص مقاطع الوسائط",
- "TaskExtractMediaSegmentsDescription": "يستخرج مقاطع وسائط من إضافات MediaSegment المُفعّلة.",
- "TaskMoveTrickplayImages": "تغيير مكان صور المعاينة السريعة",
- "TaskMoveTrickplayImagesDescription": "تُنقل ملفات التشغيل السريع الحالية بناءً على إعدادات المكتبة.",
+ "HearingImpaired": "لضعاف السمع",
+ "TaskRefreshTrickplayImages": "إنشاء صور معاينات التنقل (Trickplay)",
+ "TaskRefreshTrickplayImagesDescription": "ينشئ صور معاينات التنقل السريع للفيديوهات في المكتبات المفعّلة.",
+ "TaskAudioNormalization": "تطبيع الصوت",
+ "TaskAudioNormalizationDescription": "يفحص الملفات لجمع بيانات تطبيع الصوت.",
+ "TaskDownloadMissingLyrics": "تنزيل الكلمات المفقودة",
+ "TaskDownloadMissingLyricsDescription": "ينزّل الكلمات للأغاني.",
+ "TaskExtractMediaSegments": "فحص مقاطع المحتوى",
+ "TaskExtractMediaSegmentsDescription": "يستخرج أو يحصل على مقاطع المحتوى من الملحقات المفعّلة لمقاطع المحتوى (MediaSegment).",
+ "TaskMoveTrickplayImages": "نقل موقع صور معاينات التنقل",
+ "TaskMoveTrickplayImagesDescription": "ينقل ملفات معاينات التنقل الحالية وفقاً لإعدادات المكتبة.",
"CleanupUserDataTask": "مهمة تنظيف بيانات المستخدم",
- "CleanupUserDataTaskDescription": "مسح جميع بيانات المستخدم (حالة المشاهدة، والحالة المفضلة وما إلى ذلك) من الوسائط التي لم تعد موجودة لمدة 90 يومًا على الأقل."
+ "CleanupUserDataTaskDescription": "ينظف جميع بيانات المستخدم (مثل حالة المشاهدة وحالة المفضلة وغيرها) للمحتوى الذي لم يعد موجوداً لمدة 90 يوماً على الأقل.",
+ "Original": "فريد",
+ "LyricDownloadFailureFromForItem": "فشل تحميل الكلمات من {0} إلى {1}"
}
diff --git a/Emby.Server.Implementations/Localization/Core/enm.json b/Emby.Server.Implementations/Localization/Core/ar_SA.json
index 0967ef424b..0967ef424b 100644
--- a/Emby.Server.Implementations/Localization/Core/enm.json
+++ b/Emby.Server.Implementations/Localization/Core/ar_SA.json
diff --git a/Emby.Server.Implementations/Localization/Core/as.json b/Emby.Server.Implementations/Localization/Core/as.json
index 7c7dd26e92..bc0c2c5ff5 100644
--- a/Emby.Server.Implementations/Localization/Core/as.json
+++ b/Emby.Server.Implementations/Localization/Core/as.json
@@ -1,18 +1,13 @@
{
- "Albums": "এলবাম",
- "Application": "আবেদন",
"AppDeviceValues": "এপ্‌: {0}, ডিভাইচ: {1}",
"Artists": "শিল্পী",
- "Channels": "চেনেলস",
"Default": "ডিফল্ট",
"AuthenticationSucceededWithUserName": "{0} সফলভাবে প্রমাণিত",
"Books": "পুস্তক",
"Movies": "চলচ্চিত্ৰ",
- "CameraImageUploadedFrom": "একটি নতুন ক্যামেরা চিত্র আপলোড করা হয়েছে {0}",
"Collections": "সংগ্রহ",
"HeaderFavoriteShows": "প্রিয় শোসমূহ",
"Latest": "শেহতীয়া",
- "MessageApplicationUpdated": "জেলিফিন চাইভাৰ আপডেট কৰা হৈছে",
"MixedContent": "মিশ্ৰিত সমগ্ৰতা",
"NewVersionIsAvailable": "ডাউনলোড কৰিবলৈ জেলিফিন চাইভাৰৰ এটা নতুন সংস্কৰণ উপলব্ধ আছে.",
"NotificationOptionCameraImageUploaded": "কেমেৰাৰ চিত্ৰ আপল'ড কৰা হ'ল",
@@ -21,20 +16,14 @@
"Folders": "ফোল্ডাৰ",
"Forced": "বলপূর্বক",
"Genres": "শ্রেণী",
- "HeaderAlbumArtists": "অ্যালবাম শিল্পী",
"HeaderContinueWatching": "দেখা চালিয়ে যান",
"FailedLoginAttemptWithUserName": "লগইন ব্যর্থ চেষ্টা কৰা হৈছে থেকে {0}",
- "HeaderFavoriteAlbums": "প্রিয় অ্যালবামসমূহ",
- "HeaderFavoriteArtists": "প্রিয় শিল্পীসমূহ",
"HeaderFavoriteEpisodes": "প্রিয় পর্বসমূহ",
- "HeaderFavoriteSongs": "প্ৰিয় গীত",
"HeaderLiveTV": "প্ৰতিবেদন টিভি",
"HeaderNextUp": "পৰৱৰ্তী অংশ",
- "HeaderRecordingGroups": "অলংকৰণ গোষ্ঠীসমূহ",
"HearingImpaired": "শ্ৰবণ অক্ষম",
"HomeVideos": "ঘৰৰ ভিডিঅ'সমূহ",
"Inherit": "উত্তপ্ত কৰা",
- "MessageServerConfigurationUpdated": "চাইভাৰ কনফিগাৰেশ্যন আপডেট কৰা হৈছে",
"NotificationOptionApplicationUpdateAvailable": "অ্যাপ্লিকেশ্যন আপডেট উপলব্ধ",
"NotificationOptionApplicationUpdateInstalled": "অ্যাপ্লিকেশ্যন আপডেট ইনষ্টল কৰা হ'ল",
"NotificationOptionAudioPlayback": "অডিঅ' প্লেবেক আৰম্ভ হ'ল",
diff --git a/Emby.Server.Implementations/Localization/Core/az.json b/Emby.Server.Implementations/Localization/Core/az.json
new file mode 100644
index 0000000000..6ab18c8534
--- /dev/null
+++ b/Emby.Server.Implementations/Localization/Core/az.json
@@ -0,0 +1,19 @@
+{
+ "Books": "Kitablar",
+ "HomeVideos": "Ev Videoları",
+ "Latest": "Ən son",
+ "MixedContent": "Qarışıq məzmun",
+ "Movies": "Filmlər",
+ "Music": "Musiqi",
+ "MusicVideos": "Musiqi Videoları",
+ "NameSeasonUnknown": "Mövsüm Naməlum",
+ "NewVersionIsAvailable": "Jellyfin Serverin yeni versiyası yükləmək üçün əlçatandır.",
+ "NotificationOptionApplicationUpdateAvailable": "Tətbiq yeniləməsi mövcuddur",
+ "NotificationOptionApplicationUpdateInstalled": "Tətbiq yeniləməsi quraşdırılıb",
+ "NotificationOptionAudioPlayback": "Audio oxutma başladı",
+ "NotificationOptionAudioPlaybackStopped": "Audio oxutma dayandırıldı",
+ "NotificationOptionCameraImageUploaded": "Kamera şəkli yükləndi",
+ "NotificationOptionInstallationFailed": "Quraşdırma uğursuzluğu",
+ "NotificationOptionNewLibraryContent": "Yeni məzmun əlavə edildi",
+ "NotificationOptionPluginError": "Plugin uğursuzluğu"
+}
diff --git a/Emby.Server.Implementations/Localization/Core/be.json b/Emby.Server.Implementations/Localization/Core/be.json
index cb11cc0894..5d0ef65842 100644
--- a/Emby.Server.Implementations/Localization/Core/be.json
+++ b/Emby.Server.Implementations/Localization/Core/be.json
@@ -1,17 +1,10 @@
{
- "Sync": "Сінхранізаваць",
- "Playlists": "Плэй-лісты",
"Latest": "Апошняе",
"LabelIpAddressValue": "IP-адрас: {0}",
- "ItemAddedWithName": "{0} дададзены ў бібліятэку",
- "MessageApplicationUpdated": "Сервер Jellyfin абноўлены",
"NotificationOptionApplicationUpdateInstalled": "Абнаўленне праграмы ўсталявана",
"PluginInstalledWithName": "{0} быў усталяваны",
"UserCreatedWithName": "Карыстальнік {0} быў створаны",
- "Albums": "Альбомы",
- "Application": "Праграма",
"AuthenticationSucceededWithUserName": "{0} паспяхова аўтарызаваны",
- "Channels": "Каналы",
"ChapterNameValue": "Раздзел {0}",
"Collections": "Калекцыі",
"Default": "Прадвызначана",
@@ -21,16 +14,11 @@
"External": "Знешні",
"Genres": "Жанры",
"HeaderContinueWatching": "Працягнуць прагляд",
- "HeaderFavoriteAlbums": "Абраныя альбомы",
"HeaderFavoriteEpisodes": "Абраныя серыі",
"HeaderFavoriteShows": "Абраныя шоу",
- "HeaderFavoriteSongs": "Абраныя песні",
"HeaderLiveTV": "Прамы эфір",
- "HeaderAlbumArtists": "Выканаўцы альбома",
"LabelRunningTimeValue": "Працягласць: {0}",
"HomeVideos": "Хатнія відэа",
- "ItemRemovedWithName": "{0} выдалены з бібліятэкі",
- "MessageApplicationUpdatedTo": "Сервер Jellyfin абноўлены да версіі {0}",
"Movies": "Фільмы",
"Music": "Музыка",
"MusicVideos": "Музычныя кліпы",
@@ -41,19 +29,13 @@
"NotificationOptionPluginUpdateInstalled": "Абнаўленне плагіна ўсталявана",
"NotificationOptionServerRestartRequired": "Патрабуецца перазапуск сервера",
"Photos": "Фотаздымкі",
- "Plugin": "Плагін",
"PluginUninstalledWithName": "{0} быў выдалены",
"PluginUpdatedWithName": "{0} быў абноўлены",
- "ProviderValue": "Пастаўшчык: {0}",
- "Songs": "Песні",
- "System": "Сістэма",
- "User": "Карыстальнік",
"UserDeletedWithName": "Карыстальнік {0} быў выдалены",
"UserDownloadingItemWithValues": "{0} спампоўваецца {1}",
"TaskOptimizeDatabase": "Аптымізацыя базы даных",
"Artists": "Выканаўцы",
"UserOfflineFromDevice": "{0} адлучыўся ад {1}",
- "UserPolicyUpdatedWithName": "Палітыка карыстальніка абноўлена для {0}",
"TaskCleanActivityLogDescription": "Выдаляе запісы старэйшыя за зададзены ўзрост ў журнале актыўнасці.",
"TaskRefreshChapterImagesDescription": "Стварае мініяцюры для відэа, якія маюць раздзелы.",
"TaskCleanLogsDescription": "Выдаляе файлы журналу, якім больш за {0} дзён.",
@@ -65,17 +47,10 @@
"TasksApplicationCategory": "Праграма",
"AppDeviceValues": "Праграма: {0}, Прылада: {1}",
"Books": "Кнігі",
- "CameraImageUploadedFrom": "Новая выява камеры была загружана з {0}",
- "DeviceOfflineWithName": "{0} адлучыўся",
- "DeviceOnlineWithName": "{0} падлучаны",
"Forced": "Прымусова",
- "HeaderRecordingGroups": "Групы запісаў",
"HeaderNextUp": "Наступнае",
- "HeaderFavoriteArtists": "Абраныя выканаўцы",
"HearingImpaired": "Са слабым слыхам",
"Inherit": "Атрымаць у спадчыну",
- "MessageNamedServerConfigurationUpdatedWithValue": "Канфігурацыя сервера (секцыя {0}) абноўлена",
- "MessageServerConfigurationUpdated": "Канфігурацыя сервера абноўлена",
"MixedContent": "Змешаны змест",
"NameSeasonUnknown": "Невядомы сезон",
"NotificationOptionInstallationFailed": "Збой усталёўкі",
@@ -91,8 +66,6 @@
"NotificationOptionVideoPlayback": "Пачалося прайграванне відэа",
"NotificationOptionVideoPlaybackStopped": "Прайграванне відэа спынена",
"ScheduledTaskFailedWithName": "{0} не атрымалася",
- "ScheduledTaskStartedWithName": "{0} пачалося",
- "ServerNameNeedsToBeRestarted": "{0} патрабуе перазапуску",
"Shows": "Шоу",
"StartupEmbyServerIsLoading": "Jellyfin Server загружаецца. Калі ласка, паўтарыце спробу крыху пазней.",
"SubtitleDownloadFailureFromForItem": "Субцітры для {1} не ўдалося спампаваць з {0}",
@@ -103,8 +76,6 @@
"UserPasswordChangedWithName": "Пароль быў зменены для карыстальніка {0}",
"UserStartedPlayingItemWithValues": "{0} прайграваецца {1} на {2}",
"UserStoppedPlayingItemWithValues": "{0} скончыў прайграванне {1} на {2}",
- "ValueHasBeenAddedToLibrary": "{0} быў дададзены ў вашу медыятэку",
- "ValueSpecialEpisodeName": "Спецвыпуск - {0}",
"VersionNumber": "Версія {0}",
"TasksMaintenanceCategory": "Абслугоўванне",
"TasksLibraryCategory": "Бібліятэка",
@@ -123,11 +94,9 @@
"TaskCleanTranscodeDescription": "Выдаляе перакадзіраваныя файлы, старэйшыя за адзін дзень.",
"TaskRefreshChannels": "Абнавіць каналы",
"TaskDownloadMissingSubtitles": "Спампаваць адсутныя субцітры",
- "TaskKeyframeExtractorDescription": "Выдае ключавыя кадры з відэафайлаў для стварэння больш дакладных плэй-лістоў HLS. Гэта задача можа працягнуцца шмат часу.",
+ "TaskKeyframeExtractorDescription": "Выдае ключавыя кадры з відэафайлаў для стварэння больш дакладных плэй-лістоў HLS. Гэта задача можа выконвацца доўга.",
"TaskRefreshTrickplayImages": "Стварыць выявы Trickplay",
"TaskRefreshTrickplayImagesDescription": "Стварае перадпрагляды відэаролікаў для Trickplay у падключаных бібліятэках.",
- "TaskCleanCollectionsAndPlaylists": "Ачысціце калекцыі і плэй-лісты",
- "TaskCleanCollectionsAndPlaylistsDescription": "Выдаляе элементы з калекцый і плэй-лістоў, якія больш не існуюць.",
"TaskAudioNormalizationDescription": "Скануе файлы на прадмет нармалізацыі гуку.",
"TaskAudioNormalization": "Нармалізацыя гуку",
"TaskExtractMediaSegmentsDescription": "Выдае або атрымлівае медыясегменты з убудоў з падтрымкай MediaSegment.",
diff --git a/Emby.Server.Implementations/Localization/Core/bg-BG.json b/Emby.Server.Implementations/Localization/Core/bg-BG.json
index 92b8e5d565..0710a39708 100644
--- a/Emby.Server.Implementations/Localization/Core/bg-BG.json
+++ b/Emby.Server.Implementations/Localization/Core/bg-BG.json
@@ -1,41 +1,24 @@
{
- "Albums": "Албуми",
"AppDeviceValues": "Програма: {0}, Устройство: {1}",
- "Application": "Програма",
"Artists": "Артисти",
"AuthenticationSucceededWithUserName": "{0} се удостовери успешно",
"Books": "Книги",
- "CameraImageUploadedFrom": "Нова снимка от камера беше качена от {0}",
- "Channels": "Канали",
"ChapterNameValue": "Глава {0}",
"Collections": "Колекции",
- "DeviceOfflineWithName": "{0} се разкачи",
- "DeviceOnlineWithName": "{0} е свързан",
"FailedLoginAttemptWithUserName": "Неуспешен опит за влизане от {0}",
"Favorites": "Любими",
"Folders": "Папки",
"Genres": "Жанрове",
- "HeaderAlbumArtists": "Изпълнители на албуми",
"HeaderContinueWatching": "Продължаване на гледането",
- "HeaderFavoriteAlbums": "Любими албуми",
- "HeaderFavoriteArtists": "Любими изпълнители",
"HeaderFavoriteEpisodes": "Любими епизоди",
"HeaderFavoriteShows": "Любими сериали",
- "HeaderFavoriteSongs": "Любими песни",
"HeaderLiveTV": "Телевизия на живо",
"HeaderNextUp": "Следва",
- "HeaderRecordingGroups": "Запис групи",
"HomeVideos": "Домашни Клипове",
"Inherit": "Наследяване",
- "ItemAddedWithName": "{0} е добавено към библиотеката",
- "ItemRemovedWithName": "{0} е премахнато от библиотеката",
"LabelIpAddressValue": "IP адрес: {0}",
"LabelRunningTimeValue": "Продължителност: {0}",
"Latest": "Последни",
- "MessageApplicationUpdated": "Сървърът беше обновен",
- "MessageApplicationUpdatedTo": "Сървърът беше обновен до {0}",
- "MessageNamedServerConfigurationUpdatedWithValue": "Секцията {0} от сървърната конфигурация беше актуализирана",
- "MessageServerConfigurationUpdated": "Конфигурацията на сървъра беше актуализирана",
"MixedContent": "Смесено съдържание",
"Movies": "Филми",
"Music": "Музика",
@@ -61,23 +44,14 @@
"NotificationOptionVideoPlayback": "Възпроизвеждането на видео започна",
"NotificationOptionVideoPlaybackStopped": "Възпроизвеждането на видео е спряно",
"Photos": "Снимки",
- "Playlists": "Списъци",
- "Plugin": "Добавка",
"PluginInstalledWithName": "{0} е инсталиранa",
"PluginUninstalledWithName": "{0} е деинсталиранa",
"PluginUpdatedWithName": "{0} е обновенa",
- "ProviderValue": "Доставчик: {0}",
"ScheduledTaskFailedWithName": "{0} се провали",
- "ScheduledTaskStartedWithName": "{0} започна",
- "ServerNameNeedsToBeRestarted": "{0} трябва да се рестартира",
"Shows": "Сериали",
- "Songs": "Песни",
"StartupEmbyServerIsLoading": "Сървърът зарежда. Моля, опитайте отново след малко.",
"SubtitleDownloadFailureFromForItem": "Субтитрите за {1} от {0} не можаха да бъдат изтеглени",
- "Sync": "Синхронизиране",
- "System": "Система",
"TvShows": "Телевизионни сериали",
- "User": "Потребител",
"UserCreatedWithName": "Потребителят {0} е създаден",
"UserDeletedWithName": "Потребителят {0} е изтрит",
"UserDownloadingItemWithValues": "{0} изтегля {1}",
@@ -85,11 +59,8 @@
"UserOfflineFromDevice": "{0} се разкачи от {1}",
"UserOnlineFromDevice": "{0} е на линия от {1}",
"UserPasswordChangedWithName": "Паролата на потребителя {0} е променена",
- "UserPolicyUpdatedWithName": "Потребителската политика за {0} се актуализира",
"UserStartedPlayingItemWithValues": "{0} пусна {1}",
"UserStoppedPlayingItemWithValues": "{0} спря {1}",
- "ValueHasBeenAddedToLibrary": "{0} беше добавен във Вашата библиотека",
- "ValueSpecialEpisodeName": "Специални - {0}",
"VersionNumber": "Версия {0}",
"TaskDownloadMissingSubtitlesDescription": "Търси Интернет за липсващи субтитри, на база конфигурацията за мета-данни.",
"TaskDownloadMissingSubtitles": "Изтегляне на липсващи субтитри",
@@ -128,8 +99,6 @@
"TaskRefreshTrickplayImagesDescription": "Създава прегледи на Trickplay за видеа в активирани библиотеки.",
"TaskDownloadMissingLyrics": "Свали липсващи текстове",
"TaskDownloadMissingLyricsDescription": "Свали текстове за песни",
- "TaskCleanCollectionsAndPlaylists": "Изчисти колекциите и плейлистите",
- "TaskCleanCollectionsAndPlaylistsDescription": "Премахни несъществуващи файлове в колекциите и плейлистите.",
"TaskAudioNormalization": "Нормализиране на звука",
"TaskAudioNormalizationDescription": "Сканирай файловете за нормализация на звука.",
"TaskExtractMediaSegmentsDescription": "Изважда медиини сегменти от MediaSegment плъгини.",
diff --git a/Emby.Server.Implementations/Localization/Core/bn.json b/Emby.Server.Implementations/Localization/Core/bn.json
index 876773778b..eb7fdf2b68 100644
--- a/Emby.Server.Implementations/Localization/Core/bn.json
+++ b/Emby.Server.Implementations/Localization/Core/bn.json
@@ -1,31 +1,19 @@
{
- "DeviceOnlineWithName": "{0}-এর সাথে সংযুক্ত হয়েছে",
- "DeviceOfflineWithName": "{0}-এর সাথে সংযোগ বিচ্ছিন্ন হয়েছে",
"Collections": "সংগ্রহশালা",
"ChapterNameValue": "অধ্যায় {0}",
- "Channels": "চ্যানেলসমূহ",
- "CameraImageUploadedFrom": "{0} থেকে একটি নতুন ক্যামেরার চিত্র আপলোড করা হয়েছে",
"Books": "পুস্তকসমূহ",
"AuthenticationSucceededWithUserName": "{0} সফলভাবে অথেন্টিকেট করেছেন",
"Artists": "শিল্পীগণ",
- "Application": "অ্যাপ্লিকেশন",
- "Albums": "অ্যালবামসমূহ",
"HeaderFavoriteEpisodes": "প্রিয় পর্বগুলো",
- "HeaderFavoriteArtists": "প্রিয় শিল্পীরা",
- "HeaderFavoriteAlbums": "প্রিয় এলবামগুলো",
"HeaderContinueWatching": "দেখতে থাকুন",
- "HeaderAlbumArtists": "অ্যালবাম শিল্পীবৃন্দ",
"Genres": "ধরণ",
"Folders": "ফোল্ডারসমূহ",
"Favorites": "পছন্দসমূহ",
"FailedLoginAttemptWithUserName": "{0} লগিন করতে ব্যর্থ হয়েছে",
"AppDeviceValues": "অ্যাপ: {0}, ডিভাইস: {1}",
"VersionNumber": "সংস্করণ {0}",
- "ValueSpecialEpisodeName": "বিশেষ পর্ব - {0}",
- "ValueHasBeenAddedToLibrary": "আপনার লাইব্রেরিতে {0} যোগ করা হয়েছে",
"UserStoppedPlayingItemWithValues": "{2}তে {1} প্লে শেষ করেছেন {0}",
"UserStartedPlayingItemWithValues": "{2}তে {1} প্লে করেছেন {0}",
- "UserPolicyUpdatedWithName": "{0} এর জন্য ব্যবহার নীতি আপডেট করা হয়েছে",
"UserPasswordChangedWithName": "ব্যবহারকারী {0} এর পাসওয়ার্ড পরিবর্তিত হয়েছে",
"UserOnlineFromDevice": "{0}, {1} থেকে অনলাইন আছে",
"UserOfflineFromDevice": "{0} {1} থেকে বিচ্ছিন্ন হয়ে গেছে",
@@ -33,23 +21,14 @@
"UserDownloadingItemWithValues": "{0}, {1} ডাউনলোড করছে",
"UserDeletedWithName": "ব্যবহারকারী {0}কে বাদ দেয়া হয়েছে",
"UserCreatedWithName": "ব্যবহারকারী {0} সৃষ্টি করা হয়েছে",
- "User": "ব্যবহারকারী",
"TvShows": "টিভি শোগুলো",
- "System": "সিস্টেম",
- "Sync": "সমন্বয় করুন",
"SubtitleDownloadFailureFromForItem": "{0} থেকে {1} এর জন্য সাবটাইটেল ডাউনলোড ব্যর্থ হয়েছে",
"StartupEmbyServerIsLoading": "জেলিফিন সার্ভার লোড হচ্ছে। দয়া করে একটু পরে আবার চেষ্টা করুন।",
- "Songs": "সঙ্গীত সমূহ",
"Shows": "শো সমূহ",
- "ServerNameNeedsToBeRestarted": "{0} রিস্টার্ট করা প্রয়োজন",
- "ScheduledTaskStartedWithName": "{0} শুরু হয়েছে",
"ScheduledTaskFailedWithName": "{0} ব্যর্থ",
- "ProviderValue": "প্রদানকারী: {0}",
"PluginUpdatedWithName": "{0} আপডেট করা হয়েছে",
"PluginUninstalledWithName": "{0} আনইন্সটল হয়েছে",
"PluginInstalledWithName": "{0} ইন্সটল হয়েছে",
- "Plugin": "প্লাগিন",
- "Playlists": "প্লে লিস্ট সমূহ",
"Photos": "ছবিসমূহ",
"NotificationOptionVideoPlaybackStopped": "ভিডিও প্লেব্যাক বন্ধ হয়েছে",
"NotificationOptionVideoPlayback": "ভিডিও প্লেব্যাক শুরু হয়েছে",
@@ -75,21 +54,13 @@
"Music": "গান",
"Movies": "চলচ্চিত্রসমূহ",
"MixedContent": "মিশ্র কন্টেন্ট",
- "MessageServerConfigurationUpdated": "সার্ভারের কনফিগারেশন আপডেট করা হয়েছে",
- "HeaderRecordingGroups": "রেকর্ডিং গ্রুপগুলো",
- "MessageNamedServerConfigurationUpdatedWithValue": "সার্ভার কনফিগারেশন সেকশন {0} আপডেট করা হয়েছে",
- "MessageApplicationUpdatedTo": "জেলিফিন সার্ভার {0} তে আপডেট করা হয়েছে",
- "MessageApplicationUpdated": "জেলিফিন সার্ভার আপডেট করা হয়েছে",
"Latest": "সর্বশেষ",
"LabelRunningTimeValue": "চলার সময়: {0}",
"LabelIpAddressValue": "আইপি এড্রেস: {0}",
- "ItemRemovedWithName": "{0} লাইব্রেরি থেকে বাদ দেয়া হয়েছে",
- "ItemAddedWithName": "{0} লাইব্রেরিতে যোগ করা হয়েছে",
"Inherit": "উত্তরাধিকারসূত্র থেকে গ্রহণ করুন",
"HomeVideos": "হোম ভিডিও",
"HeaderNextUp": "এরপরে আসছে",
"HeaderLiveTV": "লাইভ টিভি",
- "HeaderFavoriteSongs": "প্রিয় গানগুলো",
"HeaderFavoriteShows": "প্রিয় শোগুলো",
"TasksLibraryCategory": "লাইব্রেরি",
"TasksMaintenanceCategory": "রক্ষণাবেক্ষণ",
@@ -127,8 +98,6 @@
"TaskRefreshTrickplayImages": "ট্রিকপ্লে ইমেজ তৈরি",
"TaskRefreshTrickplayImagesDescription": "সক্ষম লাইব্রেরিতে ভিডিওর জন্য ট্রিকপ্লে প্রিভিউ তৈরি করে।",
"TaskDownloadMissingLyricsDescription": "গানের জন্য লিরিকস ডাউনলোড করুন",
- "TaskCleanCollectionsAndPlaylists": "কালেকশন এবং প্লেলিস্ট পরিষ্কার করুন",
- "TaskCleanCollectionsAndPlaylistsDescription": "কালেকশন এবং প্লেলিস্ট থেকে আইটেমগুলি সরিয়ে দেয় যা আর বিদ্যমান নেই।",
"TaskExtractMediaSegments": "মিডিয়া সেগমেন্ট স্ক্যান",
"TaskExtractMediaSegmentsDescription": "মিডিয়া সেগমেন্ট সক্ষম প্লাগইনগুলি থেকে মিডিয়া সেগমেন্ট বের করে বা অর্জন করে।",
"TaskDownloadMissingLyrics": "অনুপস্থিত গান ডাউনলোড করুন",
diff --git a/Emby.Server.Implementations/Localization/Core/br.json b/Emby.Server.Implementations/Localization/Core/br.json
new file mode 100644
index 0000000000..cedc87e5a6
--- /dev/null
+++ b/Emby.Server.Implementations/Localization/Core/br.json
@@ -0,0 +1,63 @@
+{
+ "Artists": "Arzourien",
+ "AuthenticationSucceededWithUserName": "{0} kennasket gant berzh",
+ "Books": "Levrioù",
+ "ChapterNameValue": "Pennad {0}",
+ "Collections": "Dastumadegoù",
+ "Default": "Dre ziouer",
+ "External": "Diavaez",
+ "FailedLoginAttemptWithUserName": "Kennaskañ c'hwitet gant {0}",
+ "Favorites": "Sinedoù",
+ "Folders": "Teuliadoù",
+ "Forced": "Rediet",
+ "Genres": "Doareoù",
+ "HeaderContinueWatching": "Kenderc'hel da sellet",
+ "HeaderFavoriteEpisodes": "Rannoù Karetañ",
+ "HeaderFavoriteShows": "Heuliadennoù Karetañ",
+ "HeaderLiveTV": "TV war-eeun",
+ "HeaderNextUp": "Da c'houde",
+ "HearingImpaired": "Tud fall o C'hleved",
+ "HomeVideos": "Videoioù Personel",
+ "Inherit": "Hêrezhiñ",
+ "LabelIpAddressValue": "Chomlec'h IP : {0}",
+ "LabelRunningTimeValue": "Padelezh : {0}",
+ "Latest": "Diwezhañ",
+ "AppDeviceValues": "Arload : {0}, Trobarzhell : {1}",
+ "LyricDownloadFailureFromForItem": "C'hwitet eo pellgargañ ar c'homzoù eus {0} evit {1}",
+ "MixedContent": "Danvez mesket",
+ "Movies": "Filmoù",
+ "Music": "Sonerezh",
+ "MusicVideos": "Videoioù Sonerezh",
+ "NameInstallFailed": "{0} c'hwitet war ar staliadur",
+ "NameSeasonNumber": "Koulzad {0}",
+ "NameSeasonUnknown": "Koulzad Dianav",
+ "NewVersionIsAvailable": "Ur stumm Servijer Jellyfin nevez a c'haller pellgargañ.",
+ "NotificationOptionApplicationUpdateAvailable": "Hizivadur an arload zo da gaout",
+ "NotificationOptionApplicationUpdateInstalled": "Hizivadur an arload staliet",
+ "NotificationOptionAudioPlayback": "Lenn aodio lañset",
+ "NotificationOptionAudioPlaybackStopped": "Lenn aodio ehanet",
+ "Original": "Orin",
+ "Photos": "Fotoioù",
+ "Shows": "Heuliadennoù",
+ "Undefined": "Dianav",
+ "TasksMaintenanceCategory": "Trezalc’h",
+ "TasksLibraryCategory": "Levraoueg",
+ "TasksApplicationCategory": "Arload",
+ "NotificationOptionInstallationFailed": "C'hwitet war staliañ",
+ "NotificationOptionPluginError": "Fazi Askouezh",
+ "NotificationOptionPluginInstalled": "Askouezh staliet",
+ "NotificationOptionPluginUninstalled": "Askouezh distaliet",
+ "ScheduledTaskFailedWithName": "c'hwitadenn war {0}",
+ "TvShows": "Heuliadennoù TV",
+ "VersionNumber": "Stumm {0}",
+ "TasksChannelsCategory": "Chadennoù enlinenn",
+ "TaskAudioNormalization": "Normalizadur an aodio",
+ "TaskRefreshPeople": "Freskaat ar gomedianed",
+ "TaskUpdatePlugins": "Hizivaat an askouezhioù",
+ "TaskRefreshChannels": "Freskaat ar chadennoù",
+ "TaskOptimizeDatabase": "Gwellekaat an diaz roadennoù",
+ "TaskKeyframeExtractor": "Eztenner skeudennoù-alc'hwez",
+ "NotificationOptionCameraImageUploaded": "Karget eo skeudenn ar benveg",
+ "NotificationOptionNewLibraryContent": "Danvez nevez ouzhpennet",
+ "NotificationOptionPluginUpdateInstalled": "Staliet eo hizivadur an askouezh"
+}
diff --git a/Emby.Server.Implementations/Localization/Core/bs.json b/Emby.Server.Implementations/Localization/Core/bs.json
new file mode 100644
index 0000000000..aa7fe4eb24
--- /dev/null
+++ b/Emby.Server.Implementations/Localization/Core/bs.json
@@ -0,0 +1,110 @@
+{
+ "Artists": "Umjetnici",
+ "Books": "Knjige",
+ "Collections": "Zbirke",
+ "Default": "Zadano",
+ "Favorites": "Omiljeni",
+ "Folders": "Mape",
+ "Genres": "Žanrovi",
+ "HeaderContinueWatching": "Nastavi gledati",
+ "Movies": "Filmovi",
+ "MusicVideos": "Muzički spotovi",
+ "Photos": "Slike",
+ "Shows": "Pokazuje",
+ "AppDeviceValues": "Aplikacija: {0}, Uređaj: {1}",
+ "AuthenticationSucceededWithUserName": "{0} uspješno autentificirano",
+ "ChapterNameValue": "Poglavlje {0}",
+ "External": "Vanjsko",
+ "FailedLoginAttemptWithUserName": "Neuspjeli pokušaj prijave sa {0}",
+ "Forced": "Prisilno",
+ "HeaderFavoriteEpisodes": "Omiljene epizode",
+ "HeaderFavoriteShows": "Omiljene emisije",
+ "HeaderLiveTV": "TV uživo",
+ "HeaderNextUp": "Slijedi",
+ "HearingImpaired": "Oštećen sluh",
+ "HomeVideos": "Kućni videozapisi",
+ "Inherit": "Nasljedi",
+ "LabelIpAddressValue": "IP adresa: {0}",
+ "LabelRunningTimeValue": "Trajanje: {0}",
+ "Latest": "Posljednje dodano",
+ "MixedContent": "Miješani sadržaj",
+ "Music": "Muzika",
+ "NameInstallFailed": "{0} instalacija je propala",
+ "NameSeasonNumber": "Sezona {0}",
+ "NameSeasonUnknown": "Sezona nepoznata",
+ "NewVersionIsAvailable": "Dostupna je nova verzija Jellyfin Servera za preuzimanje.",
+ "NotificationOptionApplicationUpdateAvailable": "Dostupno ažuriranje aplikacije",
+ "NotificationOptionApplicationUpdateInstalled": "Ažuriranje aplikacije instalirano",
+ "NotificationOptionAudioPlayback": "Pokrenuto je reproduciranje zvuka",
+ "NotificationOptionAudioPlaybackStopped": "Zaustavljeno je reproduciranje zvuka",
+ "NotificationOptionCameraImageUploaded": "Učitana slika s kamere",
+ "NotificationOptionInstallationFailed": "Neuspjeh instalacije",
+ "NotificationOptionNewLibraryContent": "Dodan novi sadržaj",
+ "NotificationOptionPluginError": "Neuspjeh dodatka",
+ "NotificationOptionPluginInstalled": "Dodatak je instaliran",
+ "NotificationOptionPluginUninstalled": "Dodatak je deinstaliran",
+ "NotificationOptionPluginUpdateInstalled": "Ažuriranje dodatka je instalirano",
+ "NotificationOptionServerRestartRequired": "Potreban je ponovni pokret servera",
+ "NotificationOptionTaskFailed": "Neuspjeh zakazane zadatke",
+ "NotificationOptionUserLockedOut": "Korisnik je zaključan",
+ "NotificationOptionVideoPlayback": "Pokrenuto je reproduciranje videa",
+ "NotificationOptionVideoPlaybackStopped": "Reprodukcija videa je zaustavljena",
+ "PluginInstalledWithName": "{0} je instaliran",
+ "PluginUninstalledWithName": "{0} je deinstaliran",
+ "PluginUpdatedWithName": "{0} je ažurirano",
+ "ScheduledTaskFailedWithName": "{0} nije uspjelo",
+ "StartupEmbyServerIsLoading": "Jellyfin Server se učitava. Molimo pokušajte ponovo za kratko vrijeme.",
+ "SubtitleDownloadFailureFromForItem": "Podtitlovi nisu uspjeli preuzeti sa {0} za {1}",
+ "TvShows": "TV serije",
+ "Undefined": "Nedefinirano",
+ "UserCreatedWithName": "Korisnik {0} je kreiran",
+ "UserDeletedWithName": "Korisnik {0} je izbrisan",
+ "UserDownloadingItemWithValues": "{0} preuzima {1}",
+ "UserLockedOutWithName": "Korisnik {0} je zaključan",
+ "UserOfflineFromDevice": "{0} se odspojio od {1}",
+ "UserOnlineFromDevice": "{0} je online od {1}",
+ "UserPasswordChangedWithName": "Lozinka je promijenjena za korisnika {0}",
+ "UserStartedPlayingItemWithValues": "{0} igra protiv {1} na {2}",
+ "UserStoppedPlayingItemWithValues": "{0} je završio igru protiv {1} na {2}",
+ "VersionNumber": "Verzija {0}",
+ "TasksMaintenanceCategory": "Održavanje",
+ "TasksLibraryCategory": "Biblioteka",
+ "TasksApplicationCategory": "Prijava",
+ "TasksChannelsCategory": "Internetski kanali",
+ "TaskCleanActivityLog": "Očisti dnevnik aktivnosti",
+ "TaskCleanActivityLogDescription": "Brisanje unosa u dnevnik aktivnosti starijih od konfigurisane starosti.",
+ "TaskCleanCache": "Očistite direktorij keša",
+ "TaskCleanCacheDescription": "Brisanje keš datoteka koje sistemu više nisu potrebne.",
+ "TaskRefreshChapterImages": "Izvadi slike iz poglavlja",
+ "TaskRefreshChapterImagesDescription": "Stvara minijature za videozapise koji imaju poglavlja.",
+ "TaskAudioNormalization": "Normalizacija zvuka",
+ "TaskAudioNormalizationDescription": "Skeneriše datoteke radi podataka za normalizaciju zvuka.",
+ "TaskRefreshLibrary": "Skenerisati medijsku biblioteku",
+ "TaskRefreshLibraryDescription": "Skenerira vašu medijsku biblioteku na nove datoteke i osvježava metapodatke.",
+ "TaskCleanLogs": "Očisti direktorij dnevnika",
+ "TaskCleanLogsDescription": "Brisanje dnevničkih datoteka starijih od {0} dana.",
+ "TaskRefreshPeople": "Osvježite ljude",
+ "TaskRefreshPeopleDescription": "Ažurira metapodatke za glumce i režisere u vašoj medijskoj biblioteci.",
+ "TaskRefreshTrickplayImages": "Generirajte Trickplay slike",
+ "TaskRefreshTrickplayImagesDescription": "Stvara pregled trik-igara za videozapise u omogućenim bibliotekama.",
+ "TaskUpdatePlugins": "Ažuriraj dodatke",
+ "TaskUpdatePluginsDescription": "Preuzima i instalira ažuriranja dodataka koji su konfigurisani da se automatski ažuriraju.",
+ "TaskCleanTranscode": "Očisti Transcode direktorij",
+ "TaskCleanTranscodeDescription": "Brisanje transkodiranih datoteka starijih od jednog dana.",
+ "TaskRefreshChannels": "Osvježi kanale",
+ "TaskRefreshChannelsDescription": "Osvježava informacije o internetskom kanalu.",
+ "TaskDownloadMissingLyrics": "Preuzmi nedostajuće tekstove",
+ "TaskDownloadMissingLyricsDescription": "Preuzmi tekstove pjesama",
+ "TaskDownloadMissingSubtitles": "Preuzmite nedostajuće titlove",
+ "TaskDownloadMissingSubtitlesDescription": "Pretražuje internet u potrazi za nedostajućim titlovima na osnovu konfiguracije metapodataka.",
+ "TaskOptimizeDatabase": "Optimizirajte bazu podataka",
+ "TaskOptimizeDatabaseDescription": "Komprimira bazu podataka i čisti slobodan prostor. Pokretanje ovog zadatka nakon skeniranja biblioteke ili izvođenja drugih promjena koje podrazumijevaju izmjene baze podataka može poboljšati performanse.",
+ "TaskKeyframeExtractor": "Izvađač ključnih sličica",
+ "TaskKeyframeExtractorDescription": "Izvlači ključne okvire iz video datoteka kako bi kreirao preciznije HLS playliste. Ovaj zadatak može trajati dugo.",
+ "TaskExtractMediaSegments": "Analiza medijskog segmenta",
+ "TaskExtractMediaSegmentsDescription": "Izvlači ili dobija medijske segmente iz dodataka koji podržavaju MediaSegment.",
+ "TaskMoveTrickplayImages": "Migracija lokacije slike Trickplay",
+ "TaskMoveTrickplayImagesDescription": "Premješta postojeće datoteke trik-igara prema postavkama biblioteke.",
+ "CleanupUserDataTask": "Zadatak čišćenja korisničkih podataka",
+ "CleanupUserDataTaskDescription": "Čisti sve korisničke podatke (stanje praćenja, status omiljenog itd.) sa medija koji više nije prisutan najmanje 90 dana."
+}
diff --git a/Emby.Server.Implementations/Localization/Core/ca.json b/Emby.Server.Implementations/Localization/Core/ca.json
index 1e7279be83..6c81726ee6 100644
--- a/Emby.Server.Implementations/Localization/Core/ca.json
+++ b/Emby.Server.Implementations/Localization/Core/ca.json
@@ -1,41 +1,24 @@
{
- "Albums": "Àlbums",
"AppDeviceValues": "Aplicació: {0}, Dispositiu: {1}",
- "Application": "Aplicació",
"Artists": "Artistes",
"AuthenticationSucceededWithUserName": "{0} s'ha autenticat correctament",
"Books": "Llibres",
- "CameraImageUploadedFrom": "S'ha pujat una nova imatge de càmera des de {0}",
- "Channels": "Canals",
"ChapterNameValue": "Capítol {0}",
"Collections": "Col·leccions",
- "DeviceOfflineWithName": "{0} s'ha desconnectat",
- "DeviceOnlineWithName": "{0} està connectat",
"FailedLoginAttemptWithUserName": "Intent de connexió fallit des de {0}",
"Favorites": "Preferits",
"Folders": "Directoris",
"Genres": "Gèneres",
- "HeaderAlbumArtists": "Artistes de l'àlbum",
"HeaderContinueWatching": "Continueu mirant",
- "HeaderFavoriteAlbums": "Àlbums preferits",
- "HeaderFavoriteArtists": "Artistes preferits",
"HeaderFavoriteEpisodes": "Episodis preferits",
"HeaderFavoriteShows": "Sèries preferides",
- "HeaderFavoriteSongs": "Cançons preferides",
"HeaderLiveTV": "TV en directe",
"HeaderNextUp": "A continuació",
- "HeaderRecordingGroups": "Grups musicals",
"HomeVideos": "Vídeos domèstics",
"Inherit": "Heretat",
- "ItemAddedWithName": "{0} s'ha afegit a la mediateca",
- "ItemRemovedWithName": "{0} s'ha eliminat de la mediateca",
"LabelIpAddressValue": "Adreça IP: {0}",
"LabelRunningTimeValue": "Temps en marxa: {0}",
"Latest": "Darrers",
- "MessageApplicationUpdated": "El servidor de Jellyfin ha estat actualitzat",
- "MessageApplicationUpdatedTo": "El servidor de Jellyfin ha estat actualitzat a {0}",
- "MessageNamedServerConfigurationUpdatedWithValue": "La secció {0} de la configuració del servidor ha estat actualitzada",
- "MessageServerConfigurationUpdated": "S'ha actualitzat la configuració del servidor",
"MixedContent": "Contingut barrejat",
"Movies": "Pel·lícules",
"Music": "Música",
@@ -61,23 +44,14 @@
"NotificationOptionVideoPlayback": "Reproducció de vídeo iniciada",
"NotificationOptionVideoPlaybackStopped": "Reproducció de vídeo aturada",
"Photos": "Fotos",
- "Playlists": "Llistes de reproducció",
- "Plugin": "Complement",
- "PluginInstalledWithName": "{0} ha estat instal·lat",
- "PluginUninstalledWithName": "S'ha instal·lat {0}",
+ "PluginInstalledWithName": "S'ha instal·lat {0}",
+ "PluginUninstalledWithName": "S'ha desinstal·lat {0}",
"PluginUpdatedWithName": "S'ha actualitzat {0}",
- "ProviderValue": "Proveïdor: {0}",
"ScheduledTaskFailedWithName": "{0} ha fallat",
- "ScheduledTaskStartedWithName": "S'ha iniciat {0}",
- "ServerNameNeedsToBeRestarted": "S'ha de reiniciar {0}",
"Shows": "Sèries",
- "Songs": "Cançons",
"StartupEmbyServerIsLoading": "El servidor de Jellyfin s'està carregant. Proveu-ho de nou en una estona.",
"SubtitleDownloadFailureFromForItem": "Els subtítols per a {1} no s'han pogut baixar de {0}",
- "Sync": "Sincronitza",
- "System": "Sistema",
"TvShows": "Sèries de TV",
- "User": "Usuari",
"UserCreatedWithName": "S'ha creat l'usuari {0}",
"UserDeletedWithName": "S'ha eliminat l'usuari {0}",
"UserDownloadingItemWithValues": "{0} està descarregant {1}",
@@ -85,11 +59,8 @@
"UserOfflineFromDevice": "{0} s'ha desconnectat de {1}",
"UserOnlineFromDevice": "{0} està connectat des de {1}",
"UserPasswordChangedWithName": "S'ha canviat la contrasenya per a l'usuari {0}",
- "UserPolicyUpdatedWithName": "La política d'usuari s'ha actualitzat per a {0}",
"UserStartedPlayingItemWithValues": "{0} ha començat a reproduir {1} a {2}",
"UserStoppedPlayingItemWithValues": "{0} ha parat de reproduir {1} a {2}",
- "ValueHasBeenAddedToLibrary": "S'ha afegit {0} a la mediateca",
- "ValueSpecialEpisodeName": "Especial - {0}",
"VersionNumber": "Versió {0}",
"TaskDownloadMissingSubtitlesDescription": "Cerca a internet els subtítols que faltin a partir de la configuració de metadades.",
"TaskDownloadMissingSubtitles": "Descàrrega dels subtítols que faltin",
@@ -126,8 +97,6 @@
"HearingImpaired": "Discapacitat auditiva",
"TaskRefreshTrickplayImages": "Generació d'imatges de previsualització",
"TaskRefreshTrickplayImagesDescription": "Creació d'imatges de previsualització per a vídeos en les mediateques habilitades.",
- "TaskCleanCollectionsAndPlaylistsDescription": "Esborra elements de col·leccions i llistes de reproducció que ja no existeixen.",
- "TaskCleanCollectionsAndPlaylists": "Neteja de les col·leccions i llistes de reproducció",
"TaskAudioNormalization": "Estabilització de l'àudio",
"TaskAudioNormalizationDescription": "Escaneja els fitxer per a obtenir dades de normalització de l'àudio.",
"TaskDownloadMissingLyricsDescription": "Descàrrega de les lletres de les cançons",
@@ -137,5 +106,7 @@
"TaskMoveTrickplayImages": "Migració de la ubicació de la imatge de previsualització",
"TaskMoveTrickplayImagesDescription": "Mou els fitxers existents d'imatges de previsualització segons la configuració de la mediateca.",
"CleanupUserDataTaskDescription": "Neteja totes les dades d'usuari (estat de la visualització, estat dels preferits, etc.) del contingut multimèdia que no ha estat present durant almenys 90 dies.",
- "CleanupUserDataTask": "Tasca de neteja de dades d'usuari"
+ "CleanupUserDataTask": "Tasca de neteja de dades d'usuari",
+ "Original": "Original",
+ "LyricDownloadFailureFromForItem": "No s'han pogut descarregar les lletres des de {0} per a {1}"
}
diff --git a/Emby.Server.Implementations/Localization/Core/chr.json b/Emby.Server.Implementations/Localization/Core/chr.json
index 85d1f4c881..7e039bafc2 100644
--- a/Emby.Server.Implementations/Localization/Core/chr.json
+++ b/Emby.Server.Implementations/Localization/Core/chr.json
@@ -1,44 +1,30 @@
{
"ChapterNameValue": "Didanedi {0}",
- "HeaderAlbumArtists": "Didanidanolisgisgi",
- "HeaderFavoriteAlbums": "Dvganidi didanidisgisgi",
"HeaderLiveTV": "Anigadi didanidisgosgi",
- "HeaderRecordingGroups": "Didanisquodiisgisgi",
"HomeVideos": "Diganadi dinagadisgisgi",
"Inherit": "Anigwe",
- "MessageApplicationUpdatedTo": "Tsenigwidinonvhi Jellyfin Server tsadanidigwe anigadi {0}",
"MixedContent": "Ganinidi dininoladisgisgi",
"Movies": "Anidvnisgisgi",
"MusicVideos": "Danodisgisgi didanidisgosgi",
"NotificationOptionAudioPlayback": "Didanidigwe diganuyisgisgi anigadi",
"NotificationOptionInstallationFailed": "Diudvdi anadvnatisgisgi",
"NotificationOptionPluginUninstalled": "Ditsigvhnidv anawvdisgisgi",
- "Albums": "Anigawidaniyv",
- "Application": "Didanvyi",
"Artists": "Dinidaniyi",
"AuthenticationSucceededWithUserName": "{0} Sesoquonisdi nagadani",
"Books": "Didanedi",
- "CameraImageUploadedFrom": "Anigawidaniyv nasgi didagwalanvyi {0}",
- "Channels": "Diganadasgi",
"Collections": "Diganadisgi",
"Default": "Dinadi",
- "DeviceOfflineWithName": "{0} Aniyvolehvi nasgi",
"External": "Amohdi",
"Favorites": "Nvdayelvdisgi",
"Folders": "Didanididisgi",
"Forced": "Ganedi",
"Genres": "Diganadisgi",
"HeaderContinueWatching": "Uwoditsu asdanidisgisgi",
- "HeaderFavoriteArtists": "Dvganidi dinidanolisgisgi",
"HeaderFavoriteEpisodes": "Dvganidi didanidilisgadisgisgi",
"HeaderFavoriteShows": "Dvganidi didanididanolisgisgi)",
- "HeaderFavoriteSongs": "Dvganidi danodisgisgi",
"HeaderNextUp": "Anidvli uwodoli",
"HearingImpaired": "Anitsunidi talunidisgisgi",
- "ItemAddedWithName": "{0} Dinigwe anididanidisgi",
"Latest": "Uwodoli",
- "MessageApplicationUpdated": "Tsenigwidinonvhi Jellyfin Server tsadanidigwe",
- "MessageServerConfigurationUpdated": "Sedanidvdi anigadi diganidinonvhi",
"Music": "Danodisgisgi",
"NameSeasonUnknown": "Tsunita anidvdisgi",
"NewVersionIsAvailable": "Danodigwe anigadi Jellyfin Server tsadanidigwe adisdi uwodvdi diganidinonvhi.",
diff --git a/Emby.Server.Implementations/Localization/Core/cs.json b/Emby.Server.Implementations/Localization/Core/cs.json
index 4d2477044f..28f0e2df97 100644
--- a/Emby.Server.Implementations/Localization/Core/cs.json
+++ b/Emby.Server.Implementations/Localization/Core/cs.json
@@ -1,41 +1,24 @@
{
- "Albums": "Alba",
"AppDeviceValues": "Aplikace: {0}, Zařízení: {1}",
- "Application": "Aplikace",
"Artists": "Umělci",
"AuthenticationSucceededWithUserName": "{0} úspěšně ověřen",
"Books": "Knihy",
- "CameraImageUploadedFrom": "Z {0} byla nahrána nová fotografie z fotoaparátu",
- "Channels": "Kanály",
"ChapterNameValue": "Kapitola {0}",
"Collections": "Kolekce",
- "DeviceOfflineWithName": "{0} se odpojil",
- "DeviceOnlineWithName": "{0} je připojen",
"FailedLoginAttemptWithUserName": "Neúspěšný pokus o přihlášení z {0}",
"Favorites": "Oblíbené",
"Folders": "Složky",
"Genres": "Žánry",
- "HeaderAlbumArtists": "Umělci alba",
"HeaderContinueWatching": "Pokračovat ve sledování",
- "HeaderFavoriteAlbums": "Oblíbená alba",
- "HeaderFavoriteArtists": "Oblíbení interpreti",
"HeaderFavoriteEpisodes": "Oblíbené epizody",
"HeaderFavoriteShows": "Oblíbené seriály",
- "HeaderFavoriteSongs": "Oblíbená hudba",
"HeaderLiveTV": "TV vysílání",
"HeaderNextUp": "Další díly",
- "HeaderRecordingGroups": "Skupiny nahrávek",
"HomeVideos": "Domácí videa",
"Inherit": "Zdědit",
- "ItemAddedWithName": "{0} byl přidán do knihovny",
- "ItemRemovedWithName": "{0} byl odstraněn z knihovny",
"LabelIpAddressValue": "IP adresa: {0}",
"LabelRunningTimeValue": "Délka média: {0}",
"Latest": "Nejnovější",
- "MessageApplicationUpdated": "Jellyfin Server byl aktualizován",
- "MessageApplicationUpdatedTo": "Jellyfin server byl aktualizován na verzi {0}",
- "MessageNamedServerConfigurationUpdatedWithValue": "Konfigurace sekce {0} na serveru byla aktualizována",
- "MessageServerConfigurationUpdated": "Konfigurace serveru aktualizována",
"MixedContent": "Smíšený obsah",
"Movies": "Filmy",
"Music": "Hudba",
@@ -61,23 +44,14 @@
"NotificationOptionVideoPlayback": "Přehrávání videa zahájeno",
"NotificationOptionVideoPlaybackStopped": "Přehrávání videa ukončeno",
"Photos": "Fotky",
- "Playlists": "Seznamy skladeb",
- "Plugin": "Zásuvný modul",
"PluginInstalledWithName": "{0} byl nainstalován",
"PluginUninstalledWithName": "{0} byl odinstalován",
"PluginUpdatedWithName": "{0} byl aktualizován",
- "ProviderValue": "Poskytl: {0}",
"ScheduledTaskFailedWithName": "{0} selhalo",
- "ScheduledTaskStartedWithName": "{0} zahájeno",
- "ServerNameNeedsToBeRestarted": "{0} vyžaduje restart",
"Shows": "Seriály",
- "Songs": "Skladby",
"StartupEmbyServerIsLoading": "Jellyfin Server je spouštěn. Zkuste to prosím v brzké době znovu.",
"SubtitleDownloadFailureFromForItem": "Stažení titulků pro {1} z {0} selhalo",
- "Sync": "Synchronizace",
- "System": "Systém",
"TvShows": "Seriály",
- "User": "Uživatel",
"UserCreatedWithName": "Uživatel {0} byl vytvořen",
"UserDeletedWithName": "Uživatel {0} byl smazán",
"UserDownloadingItemWithValues": "{0} stahuje {1}",
@@ -85,11 +59,8 @@
"UserOfflineFromDevice": "{0} se odpojil ze zařízení {1}",
"UserOnlineFromDevice": "{0} se připojil ze zařízení {1}",
"UserPasswordChangedWithName": "Provedena změna hesla pro uživatele {0}",
- "UserPolicyUpdatedWithName": "Zásady uživatele pro {0} byly aktualizovány",
"UserStartedPlayingItemWithValues": "{0} spustil přehrávání {1}",
"UserStoppedPlayingItemWithValues": "{0} zastavil přehrávání {1}",
- "ValueHasBeenAddedToLibrary": "{0} byl přidán do vaší knihovny médií",
- "ValueSpecialEpisodeName": "Speciál - {0}",
"VersionNumber": "Verze {0}",
"TaskDownloadMissingSubtitlesDescription": "Vyhledá na internetu chybějící titulky na základě nastavení metadat.",
"TaskDownloadMissingSubtitles": "Stáhnout chybějící titulky",
@@ -126,8 +97,6 @@
"HearingImpaired": "Sluchově postižení",
"TaskRefreshTrickplayImages": "Generovat obrázky pro Trickplay",
"TaskRefreshTrickplayImagesDescription": "Obrázky Trickplay se používají k zobrazení náhledů u videí v knihovnách, kde je to povoleno.",
- "TaskCleanCollectionsAndPlaylists": "Pročistit kolekce a seznamy přehrávání",
- "TaskCleanCollectionsAndPlaylistsDescription": "Odstraní neexistující položky z kolekcí a seznamů přehrávání.",
"TaskAudioNormalization": "Normalizace zvuku",
"TaskAudioNormalizationDescription": "Skenovat soubory za účelem normalizace zvuku.",
"TaskDownloadMissingLyrics": "Stáhnout chybějící texty k písni",
@@ -137,5 +106,7 @@
"TaskMoveTrickplayImages": "Přesunout úložiště obrázků Trickplay",
"TaskMoveTrickplayImagesDescription": "Přesune existující soubory Trickplay podle nastavení knihovny.",
"CleanupUserDataTaskDescription": "Odstraní všechna uživatelská data (stav zhlédnutí, oblíbené atd.) z médií, které již neexistují více než 90 dní.",
- "CleanupUserDataTask": "Pročistit uživatelská data"
+ "CleanupUserDataTask": "Pročistit uživatelská data",
+ "Original": "Originál",
+ "LyricDownloadFailureFromForItem": "Nepodařilo se stáhnout texty pro {1} ze služby {0}"
}
diff --git a/Emby.Server.Implementations/Localization/Core/cy.json b/Emby.Server.Implementations/Localization/Core/cy.json
index d9ebd13f07..e4d8ee7731 100644
--- a/Emby.Server.Implementations/Localization/Core/cy.json
+++ b/Emby.Server.Implementations/Localization/Core/cy.json
@@ -1,16 +1,11 @@
{
- "DeviceOnlineWithName": "Mae {0} wedi'i gysylltu",
- "DeviceOfflineWithName": "Mae {0} wedi datgysylltu",
"Default": "Diofyn",
"Collections": "Casgliadau",
"ChapterNameValue": "Pennod {0}",
- "Channels": "Sianeli",
- "CameraImageUploadedFrom": "Mae delwedd camera newydd wedi'i lanlwytho o {0}",
"Books": "Llyfrau",
"AuthenticationSucceededWithUserName": "{0} wedi’i ddilysu’n llwyddiannus",
"Artists": "Crewyr",
"AppDeviceValues": "Ap: {0}, Dyfais: {1}",
- "Albums": "Albwmau",
"Genres": "Genres",
"Folders": "Ffolderi",
"Favorites": "Ffefrynnau",
@@ -20,9 +15,7 @@
"TaskRefreshPeople": "Adnewyddu Pobl",
"TasksChannelsCategory": "Sianeli Internet",
"VersionNumber": "Fersiwn {0}",
- "ScheduledTaskStartedWithName": "{0} wedi dechrau",
"ScheduledTaskFailedWithName": "{0} wedi methu",
- "ProviderValue": "Darparwr: {0}",
"NotificationOptionInstallationFailed": "Fethu Gosod",
"NameSeasonUnknown": "Tymor Anhysbys",
"NameSeasonNumber": "Tymor {0}",
@@ -30,31 +23,20 @@
"MixedContent": "Cynnwys amrywiol",
"HomeVideos": "Genres",
"HeaderNextUp": "Nesaf i Fyny",
- "HeaderFavoriteArtists": "Ffefryn Artistiaid",
- "HeaderFavoriteAlbums": "Ffefryn Albwmau",
"HeaderContinueWatching": "Parhewch i Wylio",
"TasksApplicationCategory": "Rhaglen",
"TasksLibraryCategory": "Llyfrgell",
"TasksMaintenanceCategory": "Cynnal a Chadw",
- "System": "System",
- "Plugin": "Ategyn",
"Music": "Cerddoriaeth",
"Latest": "Diweddaraf",
"Inherit": "Etifeddu",
"Forced": "Orfodi",
- "Application": "Rhaglen",
- "HeaderAlbumArtists": "Artistiaid albwm",
- "Sync": "Cysoni",
- "Songs": "Caneuon",
"Shows": "Rhaglenni",
- "Playlists": "Rhestri Chwarae",
"Photos": "Lluniau",
- "ValueSpecialEpisodeName": "Arbennig - {0}",
"Movies": "Ffilmiau",
"Undefined": "Heb ddiffiniad",
"TvShows": "Rhaglenni teledu",
"HeaderLiveTV": "Teledu Byw",
- "User": "Defnyddiwr",
"TaskCleanLogsDescription": "Dileu ffeiliau log sy'n fwy na {0} diwrnod oed.",
"TaskCleanLogs": "Glanhau ffolder log",
"TaskRefreshLibraryDescription": "Sganio'ch llyfrgell gyfryngau am ffeiliau newydd ac yn adnewyddu metaddata.",
@@ -65,13 +47,9 @@
"NotificationOptionPluginError": "Methodd ategyn",
"NotificationOptionAudioPlaybackStopped": "Stopiwyd chwarae sain",
"NotificationOptionAudioPlayback": "Dechreuwyd chwarae sain",
- "MessageServerConfigurationUpdated": "Mae gosodiadau gweinydd wedi'i ddiweddaru",
- "MessageNamedServerConfigurationUpdatedWithValue": "Mae adran gosodiadau gweinydd {0} wedi'i diweddaru",
"FailedLoginAttemptWithUserName": "Cais mewngofnodi wedi methu o {0}",
- "ValueHasBeenAddedToLibrary": "{0} wedi'i hychwanegu at eich llyfrgell gyfryngau",
"UserStoppedPlayingItemWithValues": "{0} wedi gorffen chwarae {1} ar {2}",
"UserStartedPlayingItemWithValues": "{0} yn chwarae {1} ar {2}",
- "UserPolicyUpdatedWithName": "Polisi defnyddiwr wedi'i newid ar gyfer {0}",
"UserPasswordChangedWithName": "Cyfrinair wedi'i newid ar gyfer defnyddiwr {0}",
"UserOnlineFromDevice": "Mae {0} ar-lein o {1}",
"UserOfflineFromDevice": "Mae {0} wedi datgysylltu o {1}",
@@ -80,7 +58,6 @@
"UserDeletedWithName": "Defnyddiwr {0} wedi'i ddileu",
"UserCreatedWithName": "Defnyddiwr {0} wedi'i greu",
"StartupEmbyServerIsLoading": "Gweinydd Jellyfin yn llwytho. Triwch eto mewn ychydig.",
- "ServerNameNeedsToBeRestarted": "Mae angen ailddechrau {0}",
"PluginUpdatedWithName": "{0} wedi'i ddiweddaru",
"PluginUninstalledWithName": "{0} wedi'i ddadosod",
"PluginInstalledWithName": "{0} wedi'i osod",
@@ -98,13 +75,7 @@
"NotificationOptionApplicationUpdateAvailable": "Diweddariad ap ar gael",
"NewVersionIsAvailable": "Mae fersiwn diweddarach o'r gweinydd Jellyfin ar gael.",
"NameInstallFailed": "Gosodiad {0} wedi methu",
- "MessageApplicationUpdatedTo": "Gweinydd Jellyfin wedi'i ddiweddaru i {0}",
- "MessageApplicationUpdated": "Gweinydd Jellyfin wedi'i ddiweddaru",
"LabelIpAddressValue": "Cyfeiriad IP: {0}",
- "ItemRemovedWithName": "{0} wedi'i dynnu o'r llyfrgell",
- "ItemAddedWithName": "{0} wedi'i adio i'r llyfrgell",
- "HeaderRecordingGroups": "Grwpiau Recordio",
- "HeaderFavoriteSongs": "Ffefryn Ganeuon",
"HeaderFavoriteShows": "Ffefryn Shoeau",
"HeaderFavoriteEpisodes": "Ffefryn Rhaglenni",
"TaskDownloadMissingSubtitlesDescription": "Chwilio'r rhyngrwyd am is-deitlau coll yn seiliedig ar gosodiadau metaddata.",
@@ -130,7 +101,5 @@
"TaskRefreshTrickplayImagesDescription": "Creu rhagolygon Trickplay ar gyfer fideos mewn llyfrgelloedd gweithredol.",
"TaskDownloadMissingLyrics": "Lawrlwytho geiriau coll",
"TaskDownloadMissingLyricsDescription": "Lawrlwytho geiriau caneuon",
- "TaskCleanCollectionsAndPlaylists": "Glanhau casgliadau a rhestrau chwarae",
- "TaskCleanCollectionsAndPlaylistsDescription": "Dileu eitemau o gasgliadau a rhestrau chwarae sydd ddim yn bodoli bellach.",
"TaskExtractMediaSegments": "Sganio Darnau Cyfryngau"
}
diff --git a/Emby.Server.Implementations/Localization/Core/da.json b/Emby.Server.Implementations/Localization/Core/da.json
index 8b0d8745dc..de56b6fd66 100644
--- a/Emby.Server.Implementations/Localization/Core/da.json
+++ b/Emby.Server.Implementations/Localization/Core/da.json
@@ -1,41 +1,24 @@
{
- "Albums": "Albummer",
"AppDeviceValues": "App: {0}, Enhed: {1}",
- "Application": "Applikation",
"Artists": "Kunstnere",
"AuthenticationSucceededWithUserName": "{0} er logget ind",
"Books": "Bøger",
- "CameraImageUploadedFrom": "Et nyt kamerabillede er blevet uploadet fra {0}",
- "Channels": "Kanaler",
"ChapterNameValue": "Kapitel {0}",
"Collections": "Samlinger",
- "DeviceOfflineWithName": "{0} har afbrudt forbindelsen",
- "DeviceOnlineWithName": "{0} er forbundet",
"FailedLoginAttemptWithUserName": "Mislykket loginforsøg fra {0}",
"Favorites": "Favoritter",
"Folders": "Mapper",
"Genres": "Genrer",
- "HeaderAlbumArtists": "Albumkunstnere",
- "HeaderContinueWatching": "Fortsæt afspilning",
- "HeaderFavoriteAlbums": "Favoritalbum",
- "HeaderFavoriteArtists": "Favoritkunstnere",
+ "HeaderContinueWatching": "Fortsæt med at se",
"HeaderFavoriteEpisodes": "Yndlingsafsnit",
"HeaderFavoriteShows": "Yndlingsserier",
- "HeaderFavoriteSongs": "Yndlingssange",
"HeaderLiveTV": "Live-TV",
"HeaderNextUp": "Næste",
- "HeaderRecordingGroups": "Optagelsesgrupper",
"HomeVideos": "Hjemmevideoer",
"Inherit": "Nedarv",
- "ItemAddedWithName": "{0} blev tilføjet til biblioteket",
- "ItemRemovedWithName": "{0} blev fjernet fra biblioteket",
"LabelIpAddressValue": "IP-adresse: {0}",
"LabelRunningTimeValue": "Spilletid: {0}",
"Latest": "Seneste",
- "MessageApplicationUpdated": "Jellyfin Server er blevet opdateret",
- "MessageApplicationUpdatedTo": "Jellyfin Server er blevet opdateret til {0}",
- "MessageNamedServerConfigurationUpdatedWithValue": "Serverkonfiguration sektion {0} er blevet opdateret",
- "MessageServerConfigurationUpdated": "Serverkonfigurationen er blevet opdateret",
"MixedContent": "Blandet indhold",
"Movies": "Film",
"Music": "Musik",
@@ -61,23 +44,14 @@
"NotificationOptionVideoPlayback": "Videoafspilning påbegyndt",
"NotificationOptionVideoPlaybackStopped": "Videoafspilning blev stoppet",
"Photos": "Fotos",
- "Playlists": "Afspilningslister",
- "Plugin": "Plugin",
"PluginInstalledWithName": "{0} blev installeret",
"PluginUninstalledWithName": "{0} blev afinstalleret",
"PluginUpdatedWithName": "{0} blev opdateret",
- "ProviderValue": "Udbyder: {0}",
"ScheduledTaskFailedWithName": "{0} mislykkedes",
- "ScheduledTaskStartedWithName": "{0} påbegyndte",
- "ServerNameNeedsToBeRestarted": "{0} skal genstartes",
"Shows": "Serier",
- "Songs": "Sange",
"StartupEmbyServerIsLoading": "Jellyfin er i gang med at starte. Prøv igen om et øjeblik.",
"SubtitleDownloadFailureFromForItem": "Undertekster kunne ikke hentes fra {0} til {1}",
- "Sync": "Synkroniser",
- "System": "System",
"TvShows": "TV-serier",
- "User": "Bruger",
"UserCreatedWithName": "Bruger {0} er blevet oprettet",
"UserDeletedWithName": "Brugeren {0} er nu slettet",
"UserDownloadingItemWithValues": "{0} henter {1}",
@@ -85,16 +59,13 @@
"UserOfflineFromDevice": "{0} har afbrudt fra {1}",
"UserOnlineFromDevice": "{0} er online fra {1}",
"UserPasswordChangedWithName": "Adgangskode er ændret for brugeren {0}",
- "UserPolicyUpdatedWithName": "Brugerpolitikken er blevet opdateret for {0}",
"UserStartedPlayingItemWithValues": "{0} afspiller {1} på {2}",
"UserStoppedPlayingItemWithValues": "{0} har afsluttet afspilning af {1} på {2}",
- "ValueHasBeenAddedToLibrary": "{0} er blevet tilføjet til dit mediebibliotek",
- "ValueSpecialEpisodeName": "Special - {0}",
"VersionNumber": "Version {0}",
"TaskDownloadMissingSubtitlesDescription": "Søger på internettet efter manglende undertekster baseret på metadata-konfigurationen.",
"TaskDownloadMissingSubtitles": "Hent manglende undertekster",
"TaskUpdatePluginsDescription": "Henter og installerer opdateringer for plugins, som er konfigurerede til at blive opdateret automatisk.",
- "TaskUpdatePlugins": "Opdater plugins",
+ "TaskUpdatePlugins": "Opdatér plugins",
"TaskCleanLogsDescription": "Sletter log-filer som er mere end {0} dage gamle.",
"TaskCleanLogs": "Ryd log-mappe",
"TaskRefreshLibraryDescription": "Scanner dit mediebibliotek for nye filer og opdateret metadata.",
@@ -108,10 +79,10 @@
"TaskRefreshChapterImages": "Udtræk kapitelbilleder",
"TaskRefreshChapterImagesDescription": "Laver miniaturebilleder for videoer, der har kapitler.",
"TaskRefreshChannelsDescription": "Opdaterer information for internetkanaler.",
- "TaskRefreshChannels": "Opdater kanaler",
+ "TaskRefreshChannels": "Opdatér kanaler",
"TaskCleanTranscodeDescription": "Fjerner omkodningsfiler, som er mere end 1 dag gamle.",
"TaskCleanTranscode": "Tøm omkodningsmappen",
- "TaskRefreshPeople": "Opdater personer",
+ "TaskRefreshPeople": "Opdatér personer",
"TaskRefreshPeopleDescription": "Opdaterer metadata for skuespillere og instruktører i dit mediebibliotek.",
"TaskCleanActivityLogDescription": "Sletter linjer i aktivitetsloggen ældre end den konfigurerede alder.",
"TaskCleanActivityLog": "Ryd aktivitetslog",
@@ -119,23 +90,23 @@
"Forced": "Tvunget",
"Default": "Standard",
"TaskOptimizeDatabaseDescription": "Komprimerer databasen for at frigøre plads. Denne handling køres efter at have scannet mediebiblioteket, eller efter at have lavet ændringer til databasen.",
- "TaskOptimizeDatabase": "Optimer database",
+ "TaskOptimizeDatabase": "Optimér database",
"TaskKeyframeExtractorDescription": "Udtrækker rammer fra videofiler for at lave mere præcise HLS-playlister. Denne opgave kan tage lang tid.",
"TaskKeyframeExtractor": "Udtræk nøglerammer",
"External": "Ekstern",
"HearingImpaired": "Hørehæmmet",
"TaskRefreshTrickplayImages": "Generer trickplay-billeder",
"TaskRefreshTrickplayImagesDescription": "Laver trickplay-billeder for videoer i aktiverede biblioteker.",
- "TaskCleanCollectionsAndPlaylists": "Ryd op i samlinger og afspilningslister",
- "TaskCleanCollectionsAndPlaylistsDescription": "Fjerner elementer fra samlinger og afspilningslister der ikke eksisterer længere.",
"TaskAudioNormalizationDescription": "Skanner filer for data vedrørende lydnormalisering.",
"TaskAudioNormalization": "Lydnormalisering",
- "TaskDownloadMissingLyricsDescription": "Søger på internettet efter manglende sangtekster baseret på metadata-konfigurationen",
+ "TaskDownloadMissingLyricsDescription": "Download sangtekster",
"TaskDownloadMissingLyrics": "Hent manglende sangtekster",
"TaskExtractMediaSegments": "Scan for mediesegmenter",
"TaskMoveTrickplayImages": "Migrer billedelokationer for trickplay-billeder",
"TaskMoveTrickplayImagesDescription": "Flyt eksisterende trickplay-billeder jævnfør biblioteksindstillinger.",
"TaskExtractMediaSegmentsDescription": "Udtrækker eller henter mediesegmenter fra plugins som understøtter MediaSegment.",
"CleanupUserDataTask": "Brugerdata oprydningsopgave",
- "CleanupUserDataTaskDescription": "Rydder alle brugerdata (eks. visning- og favoritstatus) fra medier, der har været utilgængelige i mindst 90 dage."
+ "CleanupUserDataTaskDescription": "Rydder alle brugerdata (eks. visning- og favoritstatus) fra medier, der har været utilgængelige i mindst 90 dage.",
+ "LyricDownloadFailureFromForItem": "Sangtekster kunne ikke downloades fra {0} til {1}",
+ "Original": "Original"
}
diff --git a/Emby.Server.Implementations/Localization/Core/de.json b/Emby.Server.Implementations/Localization/Core/de.json
index e9a1630d9d..8ac5fdf6fc 100644
--- a/Emby.Server.Implementations/Localization/Core/de.json
+++ b/Emby.Server.Implementations/Localization/Core/de.json
@@ -1,41 +1,24 @@
{
- "Albums": "Alben",
"AppDeviceValues": "App: {0}, Gerät: {1}",
- "Application": "Anwendung",
"Artists": "Interpreten",
"AuthenticationSucceededWithUserName": "{0} erfolgreich authentifiziert",
"Books": "Bücher",
- "CameraImageUploadedFrom": "Ein neues Kamerabild wurde von {0} hochgeladen",
- "Channels": "Kanäle",
"ChapterNameValue": "Kapitel {0}",
"Collections": "Sammlungen",
- "DeviceOfflineWithName": "{0} ist offline",
- "DeviceOnlineWithName": "{0} ist online",
"FailedLoginAttemptWithUserName": "Anmeldung von {0} fehlgeschlagen",
"Favorites": "Favoriten",
"Folders": "Verzeichnisse",
"Genres": "Genres",
- "HeaderAlbumArtists": "Album-Interpreten",
"HeaderContinueWatching": "Weiterschauen",
- "HeaderFavoriteAlbums": "Lieblingsalben",
- "HeaderFavoriteArtists": "Lieblingsinterpreten",
- "HeaderFavoriteEpisodes": "Lieblingsepisoden",
+ "HeaderFavoriteEpisodes": "Lieblingsfolgen",
"HeaderFavoriteShows": "Lieblingsserien",
- "HeaderFavoriteSongs": "Lieblingssongs",
"HeaderLiveTV": "Live TV",
"HeaderNextUp": "Als Nächstes",
- "HeaderRecordingGroups": "Aufnahme-Gruppen",
"HomeVideos": "Heimvideos",
"Inherit": "Vererben",
- "ItemAddedWithName": "{0} wurde der Bibliothek hinzugefügt",
- "ItemRemovedWithName": "{0} wurde aus der Bibliothek entfernt",
"LabelIpAddressValue": "IP-Adresse: {0}",
"LabelRunningTimeValue": "Laufzeit: {0}",
"Latest": "Neueste",
- "MessageApplicationUpdated": "Jellyfin-Server wurde aktualisiert",
- "MessageApplicationUpdatedTo": "Jellyfin-Server wurde auf Version {0} aktualisiert",
- "MessageNamedServerConfigurationUpdatedWithValue": "Der Server-Einstellungsbereich {0} wurde aktualisiert",
- "MessageServerConfigurationUpdated": "Servereinstellungen wurden aktualisiert",
"MixedContent": "Gemischte Inhalte",
"Movies": "Filme",
"Music": "Musik",
@@ -61,23 +44,14 @@
"NotificationOptionVideoPlayback": "Video wird abgespielt",
"NotificationOptionVideoPlaybackStopped": "Videowiedergabe gestoppt",
"Photos": "Fotos",
- "Playlists": "Wiedergabelisten",
- "Plugin": "Plugin",
"PluginInstalledWithName": "{0} wurde installiert",
"PluginUninstalledWithName": "{0} wurde deinstalliert",
"PluginUpdatedWithName": "{0} wurde aktualisiert",
- "ProviderValue": "Anbieter: {0}",
"ScheduledTaskFailedWithName": "{0} ist fehlgeschlagen",
- "ScheduledTaskStartedWithName": "{0} wurde gestartet",
- "ServerNameNeedsToBeRestarted": "{0} muss neu gestartet werden",
"Shows": "Serien",
- "Songs": "Lieder",
"StartupEmbyServerIsLoading": "Jellyfin-Server lädt. Bitte versuche es gleich noch einmal.",
"SubtitleDownloadFailureFromForItem": "Untertitel von {0} für {1} konnten nicht heruntergeladen werden",
- "Sync": "Synchronisation",
- "System": "System",
"TvShows": "Serien",
- "User": "Benutzer",
"UserCreatedWithName": "Benutzer {0} wurde erstellt",
"UserDeletedWithName": "Benutzer {0} wurde gelöscht",
"UserDownloadingItemWithValues": "{0} lädt {1} herunter",
@@ -85,11 +59,8 @@
"UserOfflineFromDevice": "{0} wurde getrennt von {1}",
"UserOnlineFromDevice": "{0} ist online von {1}",
"UserPasswordChangedWithName": "Das Passwort für Benutzer {0} wurde geändert",
- "UserPolicyUpdatedWithName": "Benutzerrichtlinie von {0} wurde aktualisiert",
"UserStartedPlayingItemWithValues": "{0} hat die Wiedergabe von {1} auf {2} gestartet",
"UserStoppedPlayingItemWithValues": "{0} hat die Wiedergabe von {1} auf {2} beendet",
- "ValueHasBeenAddedToLibrary": "{0} wurde deiner Bibliothek hinzugefügt",
- "ValueSpecialEpisodeName": "Extra – {0}",
"VersionNumber": "Version {0}",
"TaskDownloadMissingSubtitlesDescription": "Sucht im Internet basierend auf den Metadaten-Einstellungen nach fehlenden Untertiteln.",
"TaskDownloadMissingSubtitles": "Fehlende Untertitel herunterladen",
@@ -126,8 +97,6 @@
"HearingImpaired": "Hörgeschädigt",
"TaskRefreshTrickplayImages": "Trickplay-Bilder generieren",
"TaskRefreshTrickplayImagesDescription": "Erstellt ein Trickplay-Vorschauen für Videos in aktivierten Bibliotheken.",
- "TaskCleanCollectionsAndPlaylists": "Sammlungen und Playlisten aufräumen",
- "TaskCleanCollectionsAndPlaylistsDescription": "Löscht nicht mehr vorhandene Einträge aus den Sammlungen und Playlisten.",
"TaskAudioNormalization": "Audio Normalisierung",
"TaskAudioNormalizationDescription": "Durchsucht Dateien nach Audionormalisierungsdaten.",
"TaskDownloadMissingLyricsDescription": "Lädt Songtexte herunter",
@@ -137,5 +106,7 @@
"TaskMoveTrickplayImages": "Verzeichnis für Trickplay-Bilder migrieren",
"TaskMoveTrickplayImagesDescription": "Trickplay-Bilder werden entsprechend der Bibliothekseinstellungen verschoben.",
"CleanupUserDataTask": "Aufgabe zur Bereinigung von Benutzerdaten",
- "CleanupUserDataTaskDescription": "Löscht alle Benutzerdaten (Abspielstatus, Favoritenstatus, usw.) von Medien, die seit mindestens 90 Tagen nicht mehr vorhanden sind."
+ "CleanupUserDataTaskDescription": "Löscht alle Benutzerdaten (Abspielstatus, Favoritenstatus, usw.) von Medien, die seit mindestens 90 Tagen nicht mehr vorhanden sind.",
+ "Original": "Original",
+ "LyricDownloadFailureFromForItem": "Fehler beim Download der Songtexte von {0} für {1}"
}
diff --git a/Emby.Server.Implementations/Localization/Core/el.json b/Emby.Server.Implementations/Localization/Core/el.json
index 87362ff8e0..c0ad2c165a 100644
--- a/Emby.Server.Implementations/Localization/Core/el.json
+++ b/Emby.Server.Implementations/Localization/Core/el.json
@@ -1,41 +1,24 @@
{
- "Albums": "Άλμπουμ",
"AppDeviceValues": "Εφαρμογή: {0}, Συσκευή: {1}",
- "Application": "Εφαρμογή",
"Artists": "Καλλιτέχνες",
"AuthenticationSucceededWithUserName": "Ο χρήστης {0} επαληθεύτηκε επιτυχώς",
"Books": "Βιβλία",
- "CameraImageUploadedFrom": "Μια νέα φωτογραφία φορτώθηκε από {0}",
- "Channels": "Κανάλια",
"ChapterNameValue": "Κεφάλαιο {0}",
"Collections": "Συλλογές",
- "DeviceOfflineWithName": "Ο/Η {0} αποσυνδέθηκε",
- "DeviceOnlineWithName": "Ο/Η {0} συνδέθηκε",
"FailedLoginAttemptWithUserName": "Αποτυχία προσπάθειας σύνδεσης από {0}",
"Favorites": "Αγαπημένα",
"Folders": "Φάκελοι",
"Genres": "Είδη",
- "HeaderAlbumArtists": "Καλλιτέχνες άλμπουμ",
"HeaderContinueWatching": "Συνεχίστε την παρακολούθηση",
- "HeaderFavoriteAlbums": "Αγαπημένα Άλμπουμ",
- "HeaderFavoriteArtists": "Αγαπημένοι Καλλιτέχνες",
"HeaderFavoriteEpisodes": "Αγαπημένα Επεισόδια",
"HeaderFavoriteShows": "Αγαπημένες Σειρές",
- "HeaderFavoriteSongs": "Αγαπημένα Τραγούδια",
"HeaderLiveTV": "Ζωντανή Τηλεόραση",
"HeaderNextUp": "Επόμενο",
- "HeaderRecordingGroups": "Ομάδες Ηχογράφησης",
"HomeVideos": "Προσωπικά Βίντεο",
"Inherit": "Κληρονόμηση",
- "ItemAddedWithName": "Το {0} προστέθηκε στη βιβλιοθήκη",
- "ItemRemovedWithName": "Το {0} διαγράφτηκε από τη βιβλιοθήκη",
"LabelIpAddressValue": "Διεύθυνση IP: {0}",
"LabelRunningTimeValue": "Διάρκεια: {0}",
"Latest": "Πρόσφατα",
- "MessageApplicationUpdated": "Ο διακομιστής Jellyfin έχει ενημερωθεί",
- "MessageApplicationUpdatedTo": "Ο διακομιστής Jellyfin αναβαθμίστηκε στην έκδοση {0}",
- "MessageNamedServerConfigurationUpdatedWithValue": "Η ενότητα {0} ρύθμισης παραμέτρων του διακομιστή έχει ενημερωθεί",
- "MessageServerConfigurationUpdated": "Η ρύθμιση παραμέτρων του διακομιστή έχει ενημερωθεί",
"MixedContent": "Ανάμεικτο Περιεχόμενο",
"Movies": "Ταινίες",
"Music": "Μουσική",
@@ -61,23 +44,14 @@
"NotificationOptionVideoPlayback": "Η αναπαραγωγή βίντεο ξεκίνησε",
"NotificationOptionVideoPlaybackStopped": "Η αναπαραγωγή βίντεο σταμάτησε",
"Photos": "Φωτογραφίες",
- "Playlists": "Λίστες αναπαραγωγής",
- "Plugin": "Πρόσθετο",
"PluginInstalledWithName": "Το {0} εγκαταστάθηκε",
"PluginUninstalledWithName": "Το {0} έχει απεγκατασταθεί",
"PluginUpdatedWithName": "Το {0} ενημερώθηκε",
- "ProviderValue": "Πάροχος: {0}",
"ScheduledTaskFailedWithName": "{0} αποτυχία",
- "ScheduledTaskStartedWithName": "{0} ξεκίνησε",
- "ServerNameNeedsToBeRestarted": "{0} χρειάζεται επανεκκίνηση",
"Shows": "Σειρές",
- "Songs": "Τραγούδια",
"StartupEmbyServerIsLoading": "Ο διακομιστής Jellyfin φορτώνει. Περιμένετε λίγο και δοκιμάστε ξανά.",
- "SubtitleDownloadFailureFromForItem": "Αποτυχίες μεταφόρτωσης υποτίτλων από {0} για {1}",
- "Sync": "Συγχρονισμός",
- "System": "Σύστημα",
+ "SubtitleDownloadFailureFromForItem": "Αποτυχία λήψης υποτίτλων από {0} για {1}",
"TvShows": "Τηλεοπτικές Σειρές",
- "User": "Χρήστης",
"UserCreatedWithName": "Ο χρήστης {0} δημιουργήθηκε",
"UserDeletedWithName": "Ο χρήστης {0} έχει διαγραφεί",
"UserDownloadingItemWithValues": "{0} κατεβάζει {1}",
@@ -85,11 +59,8 @@
"UserOfflineFromDevice": "{0} αποσυνδέθηκε από {1}",
"UserOnlineFromDevice": "{0} είναι online απο {1}",
"UserPasswordChangedWithName": "Ο κωδικός του χρήστη {0} έχει αλλάξει",
- "UserPolicyUpdatedWithName": "Η πολιτική χρήστη έχει ενημερωθεί για {0}",
"UserStartedPlayingItemWithValues": "{0} παίζει {1} σε {2}",
"UserStoppedPlayingItemWithValues": "{0} τελείωσε να παίζει {1} σε {2}",
- "ValueHasBeenAddedToLibrary": "{0} προστέθηκαν στη βιβλιοθήκη πολυμέσων σας",
- "ValueSpecialEpisodeName": "Σπέσιαλ - {0}",
"VersionNumber": "Έκδοση {0}",
"TaskRefreshPeople": "Ανανέωση Ατόμων",
"TaskCleanLogsDescription": "Διαγράφει αρχεία καταγραφής που είναι πάνω από {0} ημέρες.",
@@ -128,8 +99,6 @@
"TaskRefreshTrickplayImagesDescription": "Δημιουργεί προεπισκοπήσεις trickplay για βίντεο σε ενεργοποιημένες βιβλιοθήκες.",
"TaskAudioNormalization": "Ομοιομορφία ήχου",
"TaskAudioNormalizationDescription": "Ανίχνευση αρχείων για δεδομένα ομοιομορφίας ήχου.",
- "TaskCleanCollectionsAndPlaylists": "Καθαρισμός συλλογών και λιστών αναπαραγωγής",
- "TaskCleanCollectionsAndPlaylistsDescription": "Αφαιρούνται στοιχεία από τις συλλογές και τις λίστες αναπαραγωγής που δεν υπάρχουν πλέον.",
"TaskMoveTrickplayImages": "Αλλαγή τοποθεσίας εικόνων Trickplay",
"TaskDownloadMissingLyrics": "Λήψη στίχων που λείπουν",
"TaskMoveTrickplayImagesDescription": "Μετακινεί τα υπάρχοντα αρχεία trickplay σύμφωνα με τις ρυθμίσεις της βιβλιοθήκης.",
@@ -137,5 +106,7 @@
"TaskExtractMediaSegments": "Σάρωση τμημάτων πολυμέσων",
"TaskExtractMediaSegmentsDescription": "Εξάγει ή βρίσκει τμήματα πολυμέσων από επεκτάσεις που χρησιμοποιούν το MediaSegment.",
"CleanupUserDataTaskDescription": "Καθαρίζει όλα τα δεδομένα χρήστη (κατάσταση παρακολούθησης, κατάσταση αγαπημένων κ.λπ.) από πολυμέσα που δεν υπάρχουν πλέον για τουλάχιστον 90 ημέρες.",
- "CleanupUserDataTask": "Εργασία εκκαθάρισης δεδομένων χρήστη"
+ "CleanupUserDataTask": "Εργασία εκκαθάρισης δεδομένων χρήστη",
+ "LyricDownloadFailureFromForItem": "Αποτυχία λήψης στίχων από {0} για {1}",
+ "Original": "Πρωτότυπο"
}
diff --git a/Emby.Server.Implementations/Localization/Core/en-GB.json b/Emby.Server.Implementations/Localization/Core/en-GB.json
index bd5be0b1fc..298d60d277 100644
--- a/Emby.Server.Implementations/Localization/Core/en-GB.json
+++ b/Emby.Server.Implementations/Localization/Core/en-GB.json
@@ -1,41 +1,24 @@
{
- "Albums": "Albums",
"AppDeviceValues": "App: {0}, Device: {1}",
- "Application": "Application",
"Artists": "Artists",
"AuthenticationSucceededWithUserName": "{0} successfully authenticated",
"Books": "Books",
- "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
- "Channels": "Channels",
"ChapterNameValue": "Chapter {0}",
"Collections": "Collections",
- "DeviceOfflineWithName": "{0} has disconnected",
- "DeviceOnlineWithName": "{0} is connected",
"FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
"Favorites": "Favourites",
"Folders": "Folders",
"Genres": "Genres",
- "HeaderAlbumArtists": "Album artists",
"HeaderContinueWatching": "Continue Watching",
- "HeaderFavoriteAlbums": "Favourite Albums",
- "HeaderFavoriteArtists": "Favourite Artists",
"HeaderFavoriteEpisodes": "Favourite Episodes",
"HeaderFavoriteShows": "Favourite Shows",
- "HeaderFavoriteSongs": "Favourite Songs",
"HeaderLiveTV": "Live TV",
"HeaderNextUp": "Next Up",
- "HeaderRecordingGroups": "Recording Groups",
"HomeVideos": "Home Videos",
"Inherit": "Inherit",
- "ItemAddedWithName": "{0} was added to the library",
- "ItemRemovedWithName": "{0} was removed from the library",
"LabelIpAddressValue": "IP address: {0}",
"LabelRunningTimeValue": "Running time: {0}",
"Latest": "Latest",
- "MessageApplicationUpdated": "Jellyfin Server has been updated",
- "MessageApplicationUpdatedTo": "Jellyfin Server has been updated to {0}",
- "MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated",
- "MessageServerConfigurationUpdated": "Server configuration has been updated",
"MixedContent": "Mixed content",
"Movies": "Movies",
"Music": "Music",
@@ -61,23 +44,14 @@
"NotificationOptionVideoPlayback": "Video playback started",
"NotificationOptionVideoPlaybackStopped": "Video playback stopped",
"Photos": "Photos",
- "Playlists": "Playlists",
- "Plugin": "Plugin",
"PluginInstalledWithName": "{0} was installed",
"PluginUninstalledWithName": "{0} was uninstalled",
"PluginUpdatedWithName": "{0} was updated",
- "ProviderValue": "Provider: {0}",
"ScheduledTaskFailedWithName": "{0} failed",
- "ScheduledTaskStartedWithName": "{0} started",
- "ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
"Shows": "Shows",
- "Songs": "Songs",
"StartupEmbyServerIsLoading": "Jellyfin Server is loading. Please try again shortly.",
"SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
- "Sync": "Sync",
- "System": "System",
"TvShows": "TV Shows",
- "User": "User",
"UserCreatedWithName": "User {0} has been created",
"UserDeletedWithName": "User {0} has been deleted",
"UserDownloadingItemWithValues": "{0} is downloading {1}",
@@ -85,11 +59,8 @@
"UserOfflineFromDevice": "{0} has disconnected from {1}",
"UserOnlineFromDevice": "{0} is online from {1}",
"UserPasswordChangedWithName": "Password has been changed for user {0}",
- "UserPolicyUpdatedWithName": "User policy has been updated for {0}",
"UserStartedPlayingItemWithValues": "{0} has started playing {1}",
"UserStoppedPlayingItemWithValues": "{0} has finished playing {1} on {2}",
- "ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
- "ValueSpecialEpisodeName": "Special - {0}",
"VersionNumber": "Version {0}",
"TaskDownloadMissingSubtitlesDescription": "Searches the internet for missing subtitles based on metadata configuration.",
"TaskDownloadMissingSubtitles": "Download missing subtitles",
@@ -126,8 +97,6 @@
"HearingImpaired": "Hearing Impaired",
"TaskRefreshTrickplayImages": "Generate Trickplay Images",
"TaskRefreshTrickplayImagesDescription": "Creates trickplay previews for videos in enabled libraries.",
- "TaskCleanCollectionsAndPlaylists": "Clean up collections and playlists",
- "TaskCleanCollectionsAndPlaylistsDescription": "Removes items from collections and playlists that no longer exist.",
"TaskAudioNormalization": "Audio Normalisation",
"TaskAudioNormalizationDescription": "Scans files for audio normalisation data.",
"TaskDownloadMissingLyrics": "Download missing lyrics",
@@ -137,5 +106,7 @@
"TaskMoveTrickplayImages": "Migrate Trickplay Image Location",
"TaskMoveTrickplayImagesDescription": "Moves existing trickplay files according to the library settings.",
"CleanupUserDataTask": "User data cleanup task",
- "CleanupUserDataTaskDescription": "Cleans all user data (Watch state, favourite status etc) from media that is no longer present for at least 90 days."
+ "CleanupUserDataTaskDescription": "Cleans all user data (Watch state, favourite status etc) from media that is no longer present for at least 90 days.",
+ "LyricDownloadFailureFromForItem": "Lyrics failed to download from {0} for {1}",
+ "Original": "Original"
}
diff --git a/Emby.Server.Implementations/Localization/Core/en-US.json b/Emby.Server.Implementations/Localization/Core/en-US.json
index c09d5af96c..856941c61a 100644
--- a/Emby.Server.Implementations/Localization/Core/en-US.json
+++ b/Emby.Server.Implementations/Localization/Core/en-US.json
@@ -1,45 +1,29 @@
{
- "Albums": "Albums",
"AppDeviceValues": "App: {0}, Device: {1}",
- "Application": "Application",
"Artists": "Artists",
"AuthenticationSucceededWithUserName": "{0} successfully authenticated",
"Books": "Books",
- "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}",
- "Channels": "Channels",
"ChapterNameValue": "Chapter {0}",
"Collections": "Collections",
"Default": "Default",
- "DeviceOfflineWithName": "{0} has disconnected",
- "DeviceOnlineWithName": "{0} is connected",
"External": "External",
"FailedLoginAttemptWithUserName": "Failed login attempt from {0}",
"Favorites": "Favorites",
"Folders": "Folders",
"Forced": "Forced",
"Genres": "Genres",
- "HeaderAlbumArtists": "Album artists",
"HeaderContinueWatching": "Continue Watching",
- "HeaderFavoriteAlbums": "Favorite Albums",
- "HeaderFavoriteArtists": "Favorite Artists",
"HeaderFavoriteEpisodes": "Favorite Episodes",
"HeaderFavoriteShows": "Favorite Shows",
- "HeaderFavoriteSongs": "Favorite Songs",
"HeaderLiveTV": "Live TV",
"HeaderNextUp": "Next Up",
- "HeaderRecordingGroups": "Recording Groups",
"HearingImpaired": "Hearing Impaired",
"HomeVideos": "Home Videos",
"Inherit": "Inherit",
- "ItemAddedWithName": "{0} was added to the library",
- "ItemRemovedWithName": "{0} was removed from the library",
"LabelIpAddressValue": "IP address: {0}",
"LabelRunningTimeValue": "Running time: {0}",
"Latest": "Latest",
- "MessageApplicationUpdated": "Jellyfin Server has been updated",
- "MessageApplicationUpdatedTo": "Jellyfin Server has been updated to {0}",
- "MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated",
- "MessageServerConfigurationUpdated": "Server configuration has been updated",
+ "LyricDownloadFailureFromForItem": "Lyrics failed to download from {0} for {1}",
"MixedContent": "Mixed content",
"Movies": "Movies",
"Music": "Music",
@@ -64,25 +48,17 @@
"NotificationOptionUserLockedOut": "User locked out",
"NotificationOptionVideoPlayback": "Video playback started",
"NotificationOptionVideoPlaybackStopped": "Video playback stopped",
+ "Original": "Original",
"Photos": "Photos",
- "Playlists": "Playlists",
- "Plugin": "Plugin",
"PluginInstalledWithName": "{0} was installed",
"PluginUninstalledWithName": "{0} was uninstalled",
"PluginUpdatedWithName": "{0} was updated",
- "ProviderValue": "Provider: {0}",
"ScheduledTaskFailedWithName": "{0} failed",
- "ScheduledTaskStartedWithName": "{0} started",
- "ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
"Shows": "Shows",
- "Songs": "Songs",
"StartupEmbyServerIsLoading": "Jellyfin Server is loading. Please try again shortly.",
"SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
- "Sync": "Sync",
- "System": "System",
"TvShows": "TV Shows",
"Undefined": "Undefined",
- "User": "User",
"UserCreatedWithName": "User {0} has been created",
"UserDeletedWithName": "User {0} has been deleted",
"UserDownloadingItemWithValues": "{0} is downloading {1}",
@@ -90,11 +66,8 @@
"UserOfflineFromDevice": "{0} has disconnected from {1}",
"UserOnlineFromDevice": "{0} is online from {1}",
"UserPasswordChangedWithName": "Password has been changed for user {0}",
- "UserPolicyUpdatedWithName": "User policy has been updated for {0}",
"UserStartedPlayingItemWithValues": "{0} is playing {1} on {2}",
"UserStoppedPlayingItemWithValues": "{0} has finished playing {1} on {2}",
- "ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
- "ValueSpecialEpisodeName": "Special - {0}",
"VersionNumber": "Version {0}",
"TasksMaintenanceCategory": "Maintenance",
"TasksLibraryCategory": "Library",
@@ -130,8 +103,6 @@
"TaskOptimizeDatabaseDescription": "Compacts database and truncates free space. Running this task after scanning the library or doing other changes that imply database modifications might improve performance.",
"TaskKeyframeExtractor": "Keyframe Extractor",
"TaskKeyframeExtractorDescription": "Extracts keyframes from video files to create more precise HLS playlists. This task may run for a long time.",
- "TaskCleanCollectionsAndPlaylists": "Clean up collections and playlists",
- "TaskCleanCollectionsAndPlaylistsDescription": "Removes items from collections and playlists that no longer exist.",
"TaskExtractMediaSegments": "Media Segment Scan",
"TaskExtractMediaSegmentsDescription": "Extracts or obtains media segments from MediaSegment enabled plugins.",
"TaskMoveTrickplayImages": "Migrate Trickplay Image Location",
diff --git a/Emby.Server.Implementations/Localization/Core/eo.json b/Emby.Server.Implementations/Localization/Core/eo.json
index 42cce1096f..133a2755a8 100644
--- a/Emby.Server.Implementations/Localization/Core/eo.json
+++ b/Emby.Server.Implementations/Localization/Core/eo.json
@@ -7,35 +7,22 @@
"NameInstallFailed": "{0} instalado fiaskis",
"Music": "Muziko",
"Movies": "Filmoj",
- "ItemRemovedWithName": "{0} forigis el la plurmediteko",
- "ItemAddedWithName": "{0} aldonis al la plurmediteko",
"HeaderLiveTV": "TV-etero",
"HeaderContinueWatching": "Daŭrigi Spektadon",
- "HeaderAlbumArtists": "Artistoj de albumo",
"Folders": "Dosierujoj",
- "DeviceOnlineWithName": "{0} estas konektita",
"Default": "Defaŭlte",
"Collections": "Kolektoj",
"ChapterNameValue": "Ĉapitro {0}",
- "Channels": "Kanaloj",
"Books": "Libroj",
"Artists": "Artistoj",
- "Application": "Aplikaĵo",
"AppDeviceValues": "Aplikaĵo: {0}, Aparato: {1}",
- "Albums": "Albumoj",
"TasksLibraryCategory": "Plurmediteko",
"VersionNumber": "Versio {0}",
"UserDownloadingItemWithValues": "{0} elŝutas {1}",
"UserCreatedWithName": "Uzanto {0} kreiĝis",
- "User": "Uzanto",
- "System": "Sistemo",
- "Songs": "Kantoj",
- "ScheduledTaskStartedWithName": "{0} lanĉis",
"ScheduledTaskFailedWithName": "{0} malsukcesis",
"PluginUninstalledWithName": "{0} malinstaliĝis",
"PluginInstalledWithName": "{0} instaliĝis",
- "Plugin": "Kromprogramo",
- "Playlists": "Ludlistoj",
"Photos": "Fotoj",
"NotificationOptionPluginUninstalled": "Kromprogramo malinstaliĝis",
"NotificationOptionNewLibraryContent": "Nova enhavo aldoniĝis",
@@ -43,36 +30,28 @@
"MusicVideos": "Muzikvideoj",
"LabelIpAddressValue": "IP-adreso: {0}",
"Genres": "Ĝenroj",
- "DeviceOfflineWithName": "{0} malkonektis",
- "HeaderFavoriteArtists": "Favorataj Artistoj",
"Shows": "Serioj",
"HeaderFavoriteShows": "Favorataj Serioj",
"TvShows": "TV-serioj",
"Favorites": "Favorataj",
"TaskCleanLogs": "Purigi Ĵurnalan Katalogon",
"TaskRefreshLibrary": "Skani Plurmeditekon",
- "ValueSpecialEpisodeName": "Speciala - {0}",
"TaskOptimizeDatabase": "Optimumigi datenbazon",
"TaskRefreshChannels": "Refreŝigi Kanalojn",
"TaskUpdatePlugins": "Ĝisdatigi Kromprogramojn",
"TaskRefreshPeople": "Refreŝigi Homojn",
"TasksChannelsCategory": "Interretaj Kanaloj",
- "ProviderValue": "Provizanto: {0}",
"NotificationOptionPluginError": "Kromprogramo malsukcesis",
"MixedContent": "Miksita enhavo",
"TasksApplicationCategory": "Aplikaĵo",
"TasksMaintenanceCategory": "Prizorgado",
"Undefined": "Nedifinita",
- "Sync": "Sinkronigo",
"Latest": "Plej novaj",
"Inherit": "Hereda",
"HomeVideos": "Hejmaj Videoj",
"HeaderNextUp": "Sekva Plue",
- "HeaderFavoriteSongs": "Favorataj Kantoj",
"HeaderFavoriteEpisodes": "Favorataj Epizodoj",
- "HeaderFavoriteAlbums": "Favorataj Albumoj",
"Forced": "Forcita",
- "ServerNameNeedsToBeRestarted": "{0} devas esti relanĉita",
"NotificationOptionVideoPlayback": "La videoludado lanĉis",
"NotificationOptionServerRestartRequired": "Servila relanĉigo bezonata",
"TaskOptimizeDatabaseDescription": "Kompaktigas datenbazon kaj trunkas liberan lokon. Lanĉi ĉi tiun taskon post la plurmediteka skanado aŭ fari aliajn ŝanĝojn, kiuj implicas datenbazajn modifojn, povus plibonigi rendimenton.",
@@ -85,22 +64,16 @@
"TaskCleanCacheDescription": "Forigas stapla dosierojn ne plu necesajn de la sistemo.",
"TaskCleanActivityLogDescription": "Forigas aktivecan ĵurnalaĵojn pli malnovajn ol la agordita aĝo.",
"TaskCleanTranscodeDescription": "Forigas transkodajn dosierojn aĝajn pli ol unu tagon.",
- "ValueHasBeenAddedToLibrary": "{0} estis aldonita al via plurmediteko",
"SubtitleDownloadFailureFromForItem": "Subtekstoj malsukcesis elŝuti de {0} por {1}",
"StartupEmbyServerIsLoading": "Jellyfin Server ŝarĝas. Provi denove baldaŭ.",
"TaskRefreshChapterImagesDescription": "Kreas bildetojn por videoj kiuj havas ĉapitrojn.",
"UserStoppedPlayingItemWithValues": "{0} finis ludi {1} ĉe {2}",
- "UserPolicyUpdatedWithName": "Uzanta politiko estis ĝisdatigita por {0}",
"UserPasswordChangedWithName": "Pasvorto estis ŝanĝita por uzanto {0}",
"UserStartedPlayingItemWithValues": "{0} ludas {1} ĉe {2}",
"UserLockedOutWithName": "Uzanto {0} estas elŝlosita",
"UserOnlineFromDevice": "{0} estas enreta de {1}",
"UserOfflineFromDevice": "{0} malkonektis de {1}",
"UserDeletedWithName": "Uzanto {0} estis forigita",
- "MessageServerConfigurationUpdated": "Servila agordaro estis ĝisdatigita",
- "MessageNamedServerConfigurationUpdatedWithValue": "Servila agorda sekcio {0} estis ĝisdatigita",
- "MessageApplicationUpdatedTo": "Jellyfin Server estis ĝisdatigita al {0}",
- "MessageApplicationUpdated": "Jellyfin Server estis ĝisdatigita",
"TaskRefreshChannelsDescription": "Refreŝigas informon pri interretaj kanaloj.",
"TaskDownloadMissingSubtitles": "Elŝuti mankantajn subtekstojn",
"TaskCleanTranscode": "Malplenigi Transkodadan Katalogon",
@@ -116,9 +89,7 @@
"NotificationOptionApplicationUpdateInstalled": "Aplikaĵa ĝisdatigo instalita",
"NotificationOptionApplicationUpdateAvailable": "Ĝisdatigo de aplikaĵo havebla",
"LabelRunningTimeValue": "Ludada tempo: {0}",
- "HeaderRecordingGroups": "Rikordadaj Grupoj",
"FailedLoginAttemptWithUserName": "Malsukcesa ensaluta provo de {0}",
- "CameraImageUploadedFrom": "Nova kamera bildo estis alŝutita de {0}",
"AuthenticationSucceededWithUserName": "{0} sukcese aŭtentikigis",
"TaskKeyframeExtractorDescription": "Eltiras ĉefkadrojn el videodosieroj por krei pli precizajn HLS-ludlistojn. Ĉi tiu tasko povas funkcii dum longa tempo.",
"TaskKeyframeExtractor": "Eltiri Ĉefkadrojn",
diff --git a/Emby.Server.Implementations/Localization/Core/es-AR.json b/Emby.Server.Implementations/Localization/Core/es-AR.json
index 2bbf0d5140..bccfdd4c19 100644
--- a/Emby.Server.Implementations/Localization/Core/es-AR.json
+++ b/Emby.Server.Implementations/Localization/Core/es-AR.json
@@ -1,41 +1,24 @@
{
- "Albums": "Álbumes",
"AppDeviceValues": "Aplicación: {0}, Dispositivo: {1}",
- "Application": "Aplicación",
"Artists": "Artistas",
"AuthenticationSucceededWithUserName": "{0} autenticado correctamente",
"Books": "Libros",
- "CameraImageUploadedFrom": "Se ha subido una nueva imagen de cámara desde {0}",
- "Channels": "Canales",
"ChapterNameValue": "Capítulo {0}",
"Collections": "Colecciones",
- "DeviceOfflineWithName": "{0} se ha desconectado",
- "DeviceOnlineWithName": "{0} está conectado",
"FailedLoginAttemptWithUserName": "Error al intentar iniciar sesión de {0}",
"Favorites": "Favoritos",
"Folders": "Carpetas",
"Genres": "Géneros",
- "HeaderAlbumArtists": "Artistas del álbum",
"HeaderContinueWatching": "Seguir viendo",
- "HeaderFavoriteAlbums": "Álbumes favoritos",
- "HeaderFavoriteArtists": "Artistas favoritos",
"HeaderFavoriteEpisodes": "Capítulos favoritos",
"HeaderFavoriteShows": "Series favoritas",
- "HeaderFavoriteSongs": "Canciones favoritas",
"HeaderLiveTV": "TV en vivo",
"HeaderNextUp": "Siguiente",
- "HeaderRecordingGroups": "Grupos de grabación",
"HomeVideos": "Videos caseros",
"Inherit": "Heredar",
- "ItemAddedWithName": "{0} se ha añadido a la biblioteca",
- "ItemRemovedWithName": "{0} ha sido eliminado de la biblioteca",
"LabelIpAddressValue": "Dirección IP: {0}",
"LabelRunningTimeValue": "Tiempo de funcionamiento: {0}",
"Latest": "Últimos",
- "MessageApplicationUpdated": "El servidor Jellyfin fue actualizado",
- "MessageApplicationUpdatedTo": "Se ha actualizado el servidor Jellyfin a la versión {0}",
- "MessageNamedServerConfigurationUpdatedWithValue": "Se ha actualizado la sección {0} de la configuración del servidor",
- "MessageServerConfigurationUpdated": "Se ha actualizado la configuración del servidor",
"MixedContent": "Contenido mezclado",
"Movies": "Películas",
"Music": "Música",
@@ -61,23 +44,14 @@
"NotificationOptionVideoPlayback": "Se inició la reproducción de video",
"NotificationOptionVideoPlaybackStopped": "Reproducción de video detenida",
"Photos": "Fotos",
- "Playlists": "Listas de reproducción",
- "Plugin": "Complemento",
"PluginInstalledWithName": "{0} fue instalado",
"PluginUninstalledWithName": "{0} fue desinstalado",
"PluginUpdatedWithName": "{0} fue actualizado",
- "ProviderValue": "Proveedor: {0}",
"ScheduledTaskFailedWithName": "{0} falló",
- "ScheduledTaskStartedWithName": "{0} iniciado",
- "ServerNameNeedsToBeRestarted": "{0} necesita ser reiniciado",
"Shows": "Series",
- "Songs": "Canciones",
"StartupEmbyServerIsLoading": "El servidor Jellyfin se está cargando. Vuelve a intentarlo en breve.",
"SubtitleDownloadFailureFromForItem": "Falló la descarga de subtitulos desde {0} para {1}",
- "Sync": "Sincronizar",
- "System": "Sistema",
"TvShows": "Series de TV",
- "User": "Usuario",
"UserCreatedWithName": "El usuario {0} ha sido creado",
"UserDeletedWithName": "El usuario {0} ha sido borrado",
"UserDownloadingItemWithValues": "{0} está descargando {1}",
@@ -85,11 +59,8 @@
"UserOfflineFromDevice": "{0} se ha desconectado de {1}",
"UserOnlineFromDevice": "{0} está en línea desde {1}",
"UserPasswordChangedWithName": "Se ha cambiado la contraseña para el usuario {0}",
- "UserPolicyUpdatedWithName": "Las política de usuario ha sido actualizada para {0}",
"UserStartedPlayingItemWithValues": "{0} está reproduciendo {1} en {2}",
"UserStoppedPlayingItemWithValues": "{0} ha terminado de reproducir {1} en {2}",
- "ValueHasBeenAddedToLibrary": "{0} ha sido añadido a tu biblioteca multimedia",
- "ValueSpecialEpisodeName": "Especial - {0}",
"VersionNumber": "Versión {0}",
"TaskDownloadMissingSubtitlesDescription": "Busca en internet los subtítulos que falten basándose en la configuración de los metadatos.",
"TaskDownloadMissingSubtitles": "Descargar subtítulos faltantes",
@@ -128,8 +99,6 @@
"TaskRefreshTrickplayImagesDescription": "Crea vistas previas de reproducción engañosa para videos en bibliotecas habilitadas.",
"TaskAudioNormalization": "Normalización de audio",
"TaskAudioNormalizationDescription": "Escanea archivos en busca de datos de normalización de audio.",
- "TaskCleanCollectionsAndPlaylists": "Limpiar colecciones y listas de reproducción",
- "TaskCleanCollectionsAndPlaylistsDescription": "Elimina elementos de colecciones y listas de reproducción que ya no existen.",
"TaskDownloadMissingLyrics": "Descargar letra faltante",
"TaskDownloadMissingLyricsDescription": "Descarga letras de canciones",
"TaskExtractMediaSegments": "Escanear Segmentos de Media",
@@ -137,5 +106,7 @@
"TaskMoveTrickplayImagesDescription": "Mueve archivos existentes de trickplay de acuerdo a la configuración de la biblioteca.",
"TaskMoveTrickplayImages": "Migrar Ubicación de Imagen de Trickplay",
"CleanupUserDataTaskDescription": "Limpia todos los datos del usuario (estado de visualización, estado de los favoritos, etc.) que no están presentes en la biblioteca por al menos 90 días.",
- "CleanupUserDataTask": "Tarea de limpieza de datos de usuarios"
+ "CleanupUserDataTask": "Tarea de limpieza de datos de usuarios",
+ "LyricDownloadFailureFromForItem": "No se pudo descargar la letra desde {0} para {1}",
+ "Original": "Original"
}
diff --git a/Emby.Server.Implementations/Localization/Core/es-MX.json b/Emby.Server.Implementations/Localization/Core/es-MX.json
index 6748fff4cc..ac489b9e77 100644
--- a/Emby.Server.Implementations/Localization/Core/es-MX.json
+++ b/Emby.Server.Implementations/Localization/Core/es-MX.json
@@ -1,41 +1,24 @@
{
- "Albums": "Álbumes",
"AppDeviceValues": "App: {0}, Dispositivo: {1}",
- "Application": "Aplicación",
"Artists": "Artistas",
"AuthenticationSucceededWithUserName": "{0} autenticado con éxito",
"Books": "Libros",
- "CameraImageUploadedFrom": "Una nueva imagen de cámara ha sido subida desde {0}",
- "Channels": "Canales",
"ChapterNameValue": "Capítulo {0}",
"Collections": "Colecciones",
- "DeviceOfflineWithName": "{0} se ha desconectado",
- "DeviceOnlineWithName": "{0} está conectado",
"FailedLoginAttemptWithUserName": "Intento fallido de inicio de sesión de {0}",
"Favorites": "Favoritos",
"Folders": "Carpetas",
"Genres": "Géneros",
- "HeaderAlbumArtists": "Artistas del Álbum",
"HeaderContinueWatching": "Continuar viendo",
- "HeaderFavoriteAlbums": "Álbumes favoritos",
- "HeaderFavoriteArtists": "Artistas favoritos",
"HeaderFavoriteEpisodes": "Episodios favoritos",
"HeaderFavoriteShows": "Programas favoritos",
- "HeaderFavoriteSongs": "Canciones favoritas",
"HeaderLiveTV": "TV en vivo",
"HeaderNextUp": "A continuación",
- "HeaderRecordingGroups": "Grupos de grabación",
"HomeVideos": "Videos Caseros",
"Inherit": "Heredar",
- "ItemAddedWithName": "{0} fue agregado a la biblioteca",
- "ItemRemovedWithName": "{0} fue removido de la biblioteca",
"LabelIpAddressValue": "Dirección IP: {0}",
"LabelRunningTimeValue": "Tiempo corriendo: {0}",
"Latest": "Recientes",
- "MessageApplicationUpdated": "El servidor Jellyfin ha sido actualizado",
- "MessageApplicationUpdatedTo": "El servidor Jellyfin ha sido actualizado a {0}",
- "MessageNamedServerConfigurationUpdatedWithValue": "Se ha actualizado la sección {0} de la configuración del servidor",
- "MessageServerConfigurationUpdated": "Se ha actualizado la configuración del servidor",
"MixedContent": "Contenido mezclado",
"Movies": "Películas",
"Music": "Música",
@@ -61,23 +44,14 @@
"NotificationOptionVideoPlayback": "Reproducción de video iniciada",
"NotificationOptionVideoPlaybackStopped": "Reproducción de video detenida",
"Photos": "Fotos",
- "Playlists": "Listas de reproducción",
- "Plugin": "Complemento",
"PluginInstalledWithName": "{0} fue instalado",
"PluginUninstalledWithName": "{0} fue desinstalado",
"PluginUpdatedWithName": "{0} fue actualizado",
- "ProviderValue": "Proveedor: {0}",
"ScheduledTaskFailedWithName": "{0} falló",
- "ScheduledTaskStartedWithName": "{0} iniciado",
- "ServerNameNeedsToBeRestarted": "{0} necesita ser reiniciado",
"Shows": "Programas",
- "Songs": "Canciones",
"StartupEmbyServerIsLoading": "El servidor Jellyfin está cargando. Por favor, intente de nuevo pronto.",
"SubtitleDownloadFailureFromForItem": "Falló la descarga de subtítulos desde {0} para {1}",
- "Sync": "Sincronizar",
- "System": "Sistema",
"TvShows": "Programas de TV",
- "User": "Usuario",
"UserCreatedWithName": "El usuario {0} ha sido creado",
"UserDeletedWithName": "El usuario {0} ha sido eliminado",
"UserDownloadingItemWithValues": "{0} está descargando {1}",
@@ -85,11 +59,8 @@
"UserOfflineFromDevice": "{0} se ha desconectado desde {1}",
"UserOnlineFromDevice": "{0} está en línea desde {1}",
"UserPasswordChangedWithName": "Se ha cambiado la contraseña para el usuario {0}",
- "UserPolicyUpdatedWithName": "La política de usuario ha sido actualizada para {0}",
"UserStartedPlayingItemWithValues": "{0} está reproduciendo {1} en {2}",
"UserStoppedPlayingItemWithValues": "{0} ha terminado de reproducir {1} en {2}",
- "ValueHasBeenAddedToLibrary": "{0} se ha añadido a tu biblioteca de medios",
- "ValueSpecialEpisodeName": "Especial - {0}",
"VersionNumber": "Versión {0}",
"TaskDownloadMissingSubtitlesDescription": "Busca subtítulos faltantes en Internet basándose en la configuración de metadatos.",
"TaskDownloadMissingSubtitles": "Descargar subtítulos faltantes",
@@ -128,8 +99,6 @@
"TaskRefreshTrickplayImages": "Generar imágenes de la barra de reproducción",
"TaskAudioNormalization": "Normalización de audio",
"TaskAudioNormalizationDescription": "Analiza los archivos para normalizar el audio.",
- "TaskCleanCollectionsAndPlaylists": "Limpieza de colecciones y listas de reproducción",
- "TaskCleanCollectionsAndPlaylistsDescription": "Quita elementos que ya no existen de colecciones y listas de reproducción.",
"TaskDownloadMissingLyrics": "descargar letras que faltan",
"TaskDownloadMissingLyricsDescription": "Descargar letras de canciones",
"TaskExtractMediaSegments": "Escaneo de segmentos de medios",
diff --git a/Emby.Server.Implementations/Localization/Core/es.json b/Emby.Server.Implementations/Localization/Core/es.json
index b9c57afe6d..563dce8fe6 100644
--- a/Emby.Server.Implementations/Localization/Core/es.json
+++ b/Emby.Server.Implementations/Localization/Core/es.json
@@ -1,41 +1,24 @@
{
- "Albums": "Álbumes",
"AppDeviceValues": "Aplicación: {0}, Dispositivo: {1}",
- "Application": "Aplicación",
"Artists": "Artistas",
"AuthenticationSucceededWithUserName": "{0} autenticado correctamente",
"Books": "Libros",
- "CameraImageUploadedFrom": "Se ha subido una nueva imagen por cámara desde {0}",
- "Channels": "Canales",
"ChapterNameValue": "Capítulo {0}",
"Collections": "Colecciones",
- "DeviceOfflineWithName": "{0} se ha desconectado",
- "DeviceOnlineWithName": "{0} está conectado",
"FailedLoginAttemptWithUserName": "Intento fallido de inicio de sesión de {0}",
"Favorites": "Favoritos",
"Folders": "Carpetas",
"Genres": "Géneros",
- "HeaderAlbumArtists": "Artistas del álbum",
"HeaderContinueWatching": "Seguir viendo",
- "HeaderFavoriteAlbums": "Álbumes favoritos",
- "HeaderFavoriteArtists": "Artistas favoritos",
"HeaderFavoriteEpisodes": "Episodios favoritos",
"HeaderFavoriteShows": "Series favoritas",
- "HeaderFavoriteSongs": "Canciones favoritas",
"HeaderLiveTV": "Televisión en directo",
"HeaderNextUp": "Siguiente",
- "HeaderRecordingGroups": "Grupos de grabación",
"HomeVideos": "Vídeos caseros",
"Inherit": "Heredar",
- "ItemAddedWithName": "{0} se ha añadido a la biblioteca",
- "ItemRemovedWithName": "{0} ha sido eliminado de la biblioteca",
"LabelIpAddressValue": "Dirección IP: {0}",
"LabelRunningTimeValue": "Duración: {0}",
"Latest": "Últimas",
- "MessageApplicationUpdated": "Se ha actualizado el servidor Jellyfin",
- "MessageApplicationUpdatedTo": "Se ha actualizado el servidor Jellyfin a la versión {0}",
- "MessageNamedServerConfigurationUpdatedWithValue": "La sección {0} de configuración del servidor ha sido actualizada",
- "MessageServerConfigurationUpdated": "Se ha actualizado la configuración del servidor",
"MixedContent": "Contenido mixto",
"Movies": "Películas",
"Music": "Música",
@@ -61,23 +44,14 @@
"NotificationOptionVideoPlayback": "Se inició la reproducción de vídeo",
"NotificationOptionVideoPlaybackStopped": "Reproducción de vídeo detenida",
"Photos": "Fotos",
- "Playlists": "Listas de reproducción",
- "Plugin": "Plugin",
"PluginInstalledWithName": "{0} se ha instalado",
"PluginUninstalledWithName": "{0} se ha desinstalado",
"PluginUpdatedWithName": "{0} se actualizó",
- "ProviderValue": "Proveedor: {0}",
"ScheduledTaskFailedWithName": "{0} falló",
- "ScheduledTaskStartedWithName": "{0} iniciada",
- "ServerNameNeedsToBeRestarted": "{0} necesita ser reiniciado",
"Shows": "Series",
- "Songs": "Canciones",
"StartupEmbyServerIsLoading": "Jellyfin Server se está cargando. Vuelve a intentarlo en breve.",
"SubtitleDownloadFailureFromForItem": "Fallo en la descarga de subtítulos desde {0} para {1}",
- "Sync": "Sincronizar",
- "System": "Sistema",
"TvShows": "Series",
- "User": "Usuario",
"UserCreatedWithName": "El usuario {0} ha sido creado",
"UserDeletedWithName": "El usuario {0} ha sido borrado",
"UserDownloadingItemWithValues": "{0} está descargando {1}",
@@ -85,11 +59,8 @@
"UserOfflineFromDevice": "{0} se ha desconectado desde {1}",
"UserOnlineFromDevice": "{0} está en línea desde {1}",
"UserPasswordChangedWithName": "Se ha cambiado la contraseña para el usuario {0}",
- "UserPolicyUpdatedWithName": "Actualizada política de usuario para {0}",
"UserStartedPlayingItemWithValues": "{0} está reproduciendo {1} en {2}",
"UserStoppedPlayingItemWithValues": "{0} ha terminado de reproducir {1} en {2}",
- "ValueHasBeenAddedToLibrary": "{0} ha sido añadido a tu biblioteca multimedia",
- "ValueSpecialEpisodeName": "Especial - {0}",
"VersionNumber": "Versión {0}",
"TasksMaintenanceCategory": "Mantenimiento",
"TasksLibraryCategory": "Biblioteca",
@@ -126,8 +97,6 @@
"HearingImpaired": "Discapacidad Auditiva",
"TaskRefreshTrickplayImages": "Generar miniaturas de línea de tiempo",
"TaskRefreshTrickplayImagesDescription": "Crear miniaturas de tiempo para videos en las librerías habilitadas.",
- "TaskCleanCollectionsAndPlaylists": "Limpiar colecciones y listas de reproducción",
- "TaskCleanCollectionsAndPlaylistsDescription": "Elimina elementos de colecciones y listas de reproducción que ya no existen.",
"TaskAudioNormalization": "Normalización de audio",
"TaskAudioNormalizationDescription": "Escanear archivos para obtener datos de normalización.",
"TaskDownloadMissingLyricsDescription": "Descargar letras para las canciones",
@@ -137,5 +106,7 @@
"TaskExtractMediaSegmentsDescription": "Extrae u obtiene segmentos de medios de plugins habilitados para MediaSegment.",
"TaskMoveTrickplayImages": "Migrar la ubicación de la imagen de Trickplay",
"CleanupUserDataTask": "Tarea de limpieza de datos del usuario",
- "CleanupUserDataTaskDescription": "Limpia todos los datos del usuario (estado de visualización, favoritos, etc.) de los medios que ya no están disponibles desde hace al menos 90 días."
+ "CleanupUserDataTaskDescription": "Limpia todos los datos del usuario (estado de visualización, favoritos, etc.) de los medios que ya no están disponibles desde hace al menos 90 días.",
+ "Original": "Original",
+ "LyricDownloadFailureFromForItem": "No se pudieron descargar las letras desde {0} para {1}"
}
diff --git a/Emby.Server.Implementations/Localization/Core/es_419.json b/Emby.Server.Implementations/Localization/Core/es_419.json
index 34c68c33bb..4404354a88 100644
--- a/Emby.Server.Implementations/Localization/Core/es_419.json
+++ b/Emby.Server.Implementations/Localization/Core/es_419.json
@@ -1,29 +1,19 @@
{
"LabelRunningTimeValue": "Tiempo en ejecución: {0}",
- "ValueSpecialEpisodeName": "Especial - {0}",
- "Sync": "Sincronizar",
- "Songs": "Canciones",
"Shows": "Programas",
- "Playlists": "Listas de reproducción",
"Photos": "Fotos",
"Movies": "Películas",
"HeaderNextUp": "A continuación",
"HeaderLiveTV": "TV en vivo",
- "HeaderFavoriteSongs": "Canciones favoritas",
- "HeaderFavoriteArtists": "Artistas favoritos",
- "HeaderFavoriteAlbums": "Álbumes favoritos",
"HeaderFavoriteEpisodes": "Episodios favoritos",
"HeaderFavoriteShows": "Programas favoritos",
"HeaderContinueWatching": "Continuar viendo",
- "HeaderAlbumArtists": "Artistas de álbum",
"Genres": "Géneros",
"Folders": "Carpetas",
"Favorites": "Favoritos",
"Collections": "Colecciones",
- "Channels": "Canales",
"Books": "Libros",
"Artists": "Artistas",
- "Albums": "Álbumes",
"TaskDownloadMissingSubtitlesDescription": "Busca subtítulos faltantes en Internet basándose en la configuración de metadatos.",
"TaskDownloadMissingSubtitles": "Descargar subtítulos faltantes",
"TaskRefreshChannelsDescription": "Actualiza la información de canales de Internet.",
@@ -47,10 +37,8 @@
"TasksLibraryCategory": "Biblioteca",
"TasksMaintenanceCategory": "Mantenimiento",
"VersionNumber": "Versión {0}",
- "ValueHasBeenAddedToLibrary": "{0} se ha añadido a tu biblioteca de medios",
"UserStoppedPlayingItemWithValues": "{0} ha terminado de reproducir {1} en {2}",
"UserStartedPlayingItemWithValues": "{0} está reproduciendo {1} en {2}",
- "UserPolicyUpdatedWithName": "La política de usuario ha sido actualizada para {0}",
"UserPasswordChangedWithName": "Se ha cambiado la contraseña para el usuario {0}",
"UserOnlineFromDevice": "{0} está en línea desde {1}",
"UserOfflineFromDevice": "{0} se ha desconectado desde {1}",
@@ -58,19 +46,13 @@
"UserDownloadingItemWithValues": "{0} está descargando {1}",
"UserDeletedWithName": "El usuario {0} ha sido eliminado",
"UserCreatedWithName": "El usuario {0} ha sido creado",
- "User": "Usuario",
"TvShows": "Programas de TV",
- "System": "Sistema",
"SubtitleDownloadFailureFromForItem": "Falló la descarga de subtítulos desde {0} para {1}",
"StartupEmbyServerIsLoading": "El servidor Jellyfin está cargando. Por favor, intente de nuevo pronto.",
- "ServerNameNeedsToBeRestarted": "{0} debe ser reiniciado",
- "ScheduledTaskStartedWithName": "{0} iniciado",
"ScheduledTaskFailedWithName": "{0} falló",
- "ProviderValue": "Proveedor: {0}",
"PluginUpdatedWithName": "{0} fue actualizado",
"PluginUninstalledWithName": "{0} fue desinstalado",
"PluginInstalledWithName": "{0} fue instalado",
- "Plugin": "Complemento",
"NotificationOptionVideoPlaybackStopped": "Reproducción de video detenida",
"NotificationOptionVideoPlayback": "Reproducción de video iniciada",
"NotificationOptionUserLockedOut": "Usuario bloqueado",
@@ -94,24 +76,13 @@
"MusicVideos": "Videos musicales",
"Music": "Música",
"MixedContent": "Contenido mezclado",
- "MessageServerConfigurationUpdated": "Se ha actualizado la configuración del servidor",
- "MessageNamedServerConfigurationUpdatedWithValue": "Se ha actualizado la sección {0} de la configuración del servidor",
- "MessageApplicationUpdatedTo": "El servidor Jellyfin ha sido actualizado a {0}",
- "MessageApplicationUpdated": "El servidor Jellyfin ha sido actualizado",
"Latest": "Recientes",
"LabelIpAddressValue": "Dirección IP: {0}",
- "ItemRemovedWithName": "{0} fue removido de la biblioteca",
- "ItemAddedWithName": "{0} fue agregado a la biblioteca",
"Inherit": "Heredar",
"HomeVideos": "Videos caseros",
- "HeaderRecordingGroups": "Grupos de grabación",
"FailedLoginAttemptWithUserName": "Intento de inicio de sesión fallido desde {0}",
- "DeviceOnlineWithName": "{0} está conectado",
- "DeviceOfflineWithName": "{0} se ha desconectado",
"ChapterNameValue": "Capítulo {0}",
- "CameraImageUploadedFrom": "Una nueva imagen de cámara ha sido subida desde {0}",
"AuthenticationSucceededWithUserName": "{0} autenticado con éxito",
- "Application": "Aplicación",
"AppDeviceValues": "Aplicación: {0}, Dispositivo: {1}",
"TaskCleanActivityLogDescription": "Elimina las entradas del registro de actividad anteriores al periodo configurado.",
"TaskCleanActivityLog": "Limpiar registro de actividades",
@@ -127,9 +98,7 @@
"TaskRefreshTrickplayImagesDescription": "Crea previsualizaciones para la barra de reproducción en las bibliotecas habilitadas.",
"TaskRefreshTrickplayImages": "Generar imágenes de la barra de reproducción",
"TaskAudioNormalization": "Normalización de audio",
- "TaskCleanCollectionsAndPlaylistsDescription": "Quita elementos que ya no existen de colecciones y listas de reproducción.",
"TaskAudioNormalizationDescription": "Analiza los archivos para normalizar el audio.",
- "TaskCleanCollectionsAndPlaylists": "Limpieza de colecciones y listas de reproducción",
"TaskDownloadMissingLyrics": "Descargar letra faltante",
"TaskDownloadMissingLyricsDescription": "Descarga letras de canciones",
"TaskExtractMediaSegmentsDescription": "Extrae u obtiene segmentos de medios de complementos habilitados para MediaSegment.",
@@ -137,5 +106,7 @@
"TaskExtractMediaSegments": "Escaneo de segmentos de medios",
"TaskMoveTrickplayImages": "Migrar la ubicación de la imagen de Trickplay",
"CleanupUserDataTask": "Tarea de limpieza de datos de usuario",
- "CleanupUserDataTaskDescription": "Limpia todos los datos de usuario (estado de visualización, favoritos, etc.) que no están presentes en la biblioteca por al menos 90 días."
+ "CleanupUserDataTaskDescription": "Limpia todos los datos de usuario (estado de visualización, favoritos, etc.) que no están presentes en la biblioteca por al menos 90 días.",
+ "LyricDownloadFailureFromForItem": "No se pudo descargar las letras de {0} para {1}",
+ "Original": "Original"
}
diff --git a/Emby.Server.Implementations/Localization/Core/es_DO.json b/Emby.Server.Implementations/Localization/Core/es_DO.json
index f98a5e5b2c..a1b9944125 100644
--- a/Emby.Server.Implementations/Localization/Core/es_DO.json
+++ b/Emby.Server.Implementations/Localization/Core/es_DO.json
@@ -1,35 +1,24 @@
{
- "Channels": "Canales",
"Books": "Libros",
- "Albums": "Álbumes",
"Collections": "Colecciones",
"Artists": "Artistas",
- "DeviceOnlineWithName": "{0} está conectado",
- "DeviceOfflineWithName": "{0} se ha desconectado",
"ChapterNameValue": "Capítulo {0}",
- "CameraImageUploadedFrom": "Se ha subido una nueva imagen de cámara desde {0}",
"AuthenticationSucceededWithUserName": "{0} autenticado con éxito",
- "Application": "Aplicación",
"AppDeviceValues": "App: {0}, Dispositivo: {1}",
"HeaderContinueWatching": "Continuar Viendo",
- "HeaderAlbumArtists": "Artistas del álbum",
"Genres": "Géneros",
"Folders": "Carpetas",
"Favorites": "Favoritos",
"FailedLoginAttemptWithUserName": "Intento de inicio de sesión fallido desde {0}",
- "HeaderFavoriteSongs": "Canciones Favoritas",
"HeaderFavoriteEpisodes": "Episodios Favoritos",
- "HeaderFavoriteArtists": "Artistas Favoritos",
"External": "Externo",
"Default": "Predeterminado",
"Movies": "Películas",
- "MessageNamedServerConfigurationUpdatedWithValue": "La sección {0} de la configuración ha sido actualizada",
"MixedContent": "Contenido mixto",
"Music": "Música",
"NotificationOptionCameraImageUploaded": "Imagen de la cámara subida",
"NotificationOptionServerRestartRequired": "Se necesita reiniciar el servidor",
"NotificationOptionVideoPlayback": "Reproducción de video iniciada",
- "Sync": "Sincronizar",
"Shows": "Series",
"UserDownloadingItemWithValues": "{0} está descargando {1}",
"UserOfflineFromDevice": "{0} se ha desconectado desde {1}",
@@ -41,8 +30,6 @@
"TaskKeyframeExtractorDescription": "Extrae Fotogramas Clave de los archivos de vídeo para crear Listas de Reproducción HLS más precisas. Esta tarea puede durar mucho tiempo.",
"TaskAudioNormalization": "Normalización de audio",
"TaskAudioNormalizationDescription": "Escanear archivos para la normalización de data.",
- "TaskCleanCollectionsAndPlaylists": "Limpiar colecciones y listas de reproducción",
- "TaskCleanCollectionsAndPlaylistsDescription": "Remover elementos de colecciones y listas de reproducción que no existen.",
"TvShows": "Series de TV",
"UserStartedPlayingItemWithValues": "{0} está reproduciendo {1} en {2}",
"TaskRefreshChannels": "Actualizar canales",
@@ -50,17 +37,12 @@
"HeaderFavoriteShows": "Programas favoritos",
"TaskCleanActivityLog": "Limpiar registro de actividades",
"UserPasswordChangedWithName": "Se ha cambiado la contraseña para el usuario {0}",
- "System": "Sistema",
- "User": "Usuario",
"Forced": "Forzado",
"PluginInstalledWithName": "{0} ha sido instalado",
- "HeaderFavoriteAlbums": "Álbumes favoritos",
"TaskUpdatePlugins": "Actualizar Plugins",
"Latest": "Recientes",
"UserStoppedPlayingItemWithValues": "{0} ha terminado de reproducir {1} en {2}",
- "Songs": "Canciones",
"NotificationOptionPluginError": "Falla de plugin",
- "ScheduledTaskStartedWithName": "{0} iniciado",
"TasksApplicationCategory": "Aplicación",
"UserDeletedWithName": "El usuario {0} ha sido eliminado",
"TaskRefreshChapterImages": "Extraer imágenes de los capítulos",
@@ -73,34 +55,26 @@
"NotificationOptionAudioPlaybackStopped": "Reproducción de audio detenida",
"TasksLibraryCategory": "Biblioteca",
"NotificationOptionPluginInstalled": "Plugin instalado",
- "UserPolicyUpdatedWithName": "La política de usuario ha sido actualizada para {0}",
"VersionNumber": "Versión {0}",
"HeaderNextUp": "A continuación",
- "ValueHasBeenAddedToLibrary": "{0} se ha añadido a tu biblioteca",
"LabelIpAddressValue": "Dirección IP: {0}",
"NameSeasonNumber": "Temporada {0}",
"NotificationOptionNewLibraryContent": "Nuevo contenido agregado",
- "Plugin": "Plugin",
"NotificationOptionAudioPlayback": "Reproducción de audio iniciada",
"NotificationOptionTaskFailed": "Falló la tarea programada",
"LabelRunningTimeValue": "Tiempo en ejecución: {0}",
"SubtitleDownloadFailureFromForItem": "Falló la descarga de subtítulos desde {0} para {1}",
"TaskRefreshLibrary": "Escanear biblioteca de medios",
- "ServerNameNeedsToBeRestarted": "{0} debe ser reiniciado",
"TasksMaintenanceCategory": "Mantenimiento",
- "ProviderValue": "Proveedor: {0}",
"UserCreatedWithName": "El usuario {0} ha sido creado",
"PluginUninstalledWithName": "{0} ha sido desinstalado",
- "ValueSpecialEpisodeName": "Especial - {0}",
"ScheduledTaskFailedWithName": "{0} falló",
"TaskCleanLogs": "Limpiar directorio de registros",
"NameInstallFailed": "Falló la instalación de {0}",
"UserLockedOutWithName": "El usuario {0} ha sido bloqueado",
"TaskRefreshLibraryDescription": "Escanea tu biblioteca de medios para encontrar archivos nuevos y actualizar los metadatos.",
"StartupEmbyServerIsLoading": "El servidor Jellyfin está cargando. Por favor, intente de nuevo en un momento.",
- "Playlists": "Listas de reproducción",
"TaskDownloadMissingSubtitlesDescription": "Busca subtítulos faltantes en Internet basándose en la configuración de metadatos.",
- "MessageServerConfigurationUpdated": "Se ha actualizado la configuración del servidor",
"TaskRefreshPeople": "Actualizar personas",
"NotificationOptionVideoPlaybackStopped": "Reproducción de video detenida",
"HeaderLiveTV": "TV en vivo",
@@ -110,15 +84,10 @@
"TaskCleanCache": "Limpiar directorio caché",
"TaskRefreshChapterImagesDescription": "Crea miniaturas para videos que tienen capítulos.",
"Inherit": "Heredar",
- "HeaderRecordingGroups": "Grupos de grabación",
- "ItemAddedWithName": "{0} fue agregado a la biblioteca",
"TaskOptimizeDatabase": "Optimizar base de datos",
"TaskKeyframeExtractor": "Extractor de Fotogramas Clave",
"HearingImpaired": "Discapacidad auditiva",
"HomeVideos": "Videos caseros",
- "ItemRemovedWithName": "{0} fue removido de la biblioteca",
- "MessageApplicationUpdated": "El servidor Jellyfin ha sido actualizado",
- "MessageApplicationUpdatedTo": "El servidor Jellyfin ha sido actualizado a {0}",
"MusicVideos": "Videos musicales",
"NewVersionIsAvailable": "Una nueva versión de Jellyfin está disponible para descargar.",
"PluginUpdatedWithName": "{0} ha sido actualizado",
diff --git a/Emby.Server.Implementations/Localization/Core/et.json b/Emby.Server.Implementations/Localization/Core/et.json
index 91a0aa6639..e6bf1f25b5 100644
--- a/Emby.Server.Implementations/Localization/Core/et.json
+++ b/Emby.Server.Implementations/Localization/Core/et.json
@@ -1,7 +1,6 @@
{
"TaskCleanActivityLogDescription": "Kustutab määratud ajast vanemad tegevuslogi kirjed.",
"UserDownloadingItemWithValues": "{0} laadib alla {1}",
- "HeaderRecordingGroups": "Salvestusrühmad",
"TaskOptimizeDatabaseDescription": "Tihendab ja puhastab andmebaasi. Selle toimingu tegemine pärast meediakogu andmebaasiga seotud muudatuste skannimist võib jõudlust parandada.",
"TaskOptimizeDatabase": "Optimeeri andmebaasi",
"TaskDownloadMissingSubtitlesDescription": "Otsib veebist puuduvaid subtiitreid vastavalt määratud metaandmete seadetele.",
@@ -29,30 +28,19 @@
"TasksLibraryCategory": "Meediakogu",
"TasksMaintenanceCategory": "Hooldus",
"VersionNumber": "Versioon {0}",
- "ValueSpecialEpisodeName": "Eriepisood - {0}",
- "ValueHasBeenAddedToLibrary": "{0} lisati meediakogusse",
"UserStartedPlayingItemWithValues": "{0} taasesitab {1} seadmes {2}",
"UserPasswordChangedWithName": "Kasutaja {0} parool muudeti",
"UserLockedOutWithName": "Kasutaja {0} lukustati",
"UserDeletedWithName": "Kasutaja {0} kustutati",
"UserCreatedWithName": "Kasutaja {0} on loodud",
- "ScheduledTaskStartedWithName": "{0} käivitati",
- "ProviderValue": "Allikas: {0}",
"StartupEmbyServerIsLoading": "Jellyfin server laadib. Proovi varsti uuesti.",
- "User": "Kasutaja",
"Undefined": "Määratlemata",
"TvShows": "Sarjad",
- "System": "Süsteem",
- "Sync": "Sünkrooni",
- "Songs": "Lood",
"Shows": "Sarjad",
- "ServerNameNeedsToBeRestarted": "{0} tuleb taaskäivitada",
"ScheduledTaskFailedWithName": "{0} nurjus",
"PluginUpdatedWithName": "{0} uuendati",
"PluginUninstalledWithName": "{0} eemaldati",
"PluginInstalledWithName": "{0} paigaldati",
- "Plugin": "Plugin",
- "Playlists": "Esitusloendid",
"Photos": "Fotod",
"NotificationOptionVideoPlaybackStopped": "Video taasesitus lõppes",
"NotificationOptionVideoPlayback": "Video taasesitus algas",
@@ -78,46 +66,29 @@
"Music": "Muusika",
"Movies": "Filmid",
"MixedContent": "Segatud sisu",
- "MessageServerConfigurationUpdated": "Serveri seadistust uuendati",
- "MessageNamedServerConfigurationUpdatedWithValue": "Serveri seadistusosa {0} uuendati",
- "MessageApplicationUpdatedTo": "Jellyfin server uuendati versioonile {0}",
- "MessageApplicationUpdated": "Jellyfin server uuendati",
"Latest": "Uusimad",
"LabelRunningTimeValue": "Kestus: {0}",
"LabelIpAddressValue": "IP aadress: {0}",
- "ItemRemovedWithName": "{0} eemaldati meediakogust",
- "ItemAddedWithName": "{0} lisati meediakogusse",
"Inherit": "Päri",
"HomeVideos": "Koduvideod",
"HeaderNextUp": "Järgmisena",
"HeaderLiveTV": "Otse TV",
- "HeaderFavoriteSongs": "Lemmiklood",
"HeaderFavoriteShows": "Lemmiksarjad",
"HeaderFavoriteEpisodes": "Lemmikepisoodid",
- "HeaderFavoriteArtists": "Lemmikesitajad",
- "HeaderFavoriteAlbums": "Lemmikalbumid",
"HeaderContinueWatching": "Jätka vaatamist",
- "HeaderAlbumArtists": "Albumi esitajad",
"Genres": "Žanrid",
"Forced": "Sunnitud",
"Folders": "Kaustad",
"Favorites": "Lemmikud",
"FailedLoginAttemptWithUserName": "Sisselogimine nurjus aadressilt {0}",
- "DeviceOnlineWithName": "{0} on ühendatud",
- "DeviceOfflineWithName": "{0} katkestas ühenduse",
"Default": "Vaikimisi",
"ChapterNameValue": "Peatükk {0}",
- "Channels": "Kanalid",
- "CameraImageUploadedFrom": "Uus kaamera pilt laaditi üles allikalt {0}",
"Books": "Raamatud",
"AuthenticationSucceededWithUserName": "{0} autentimine õnnestus",
"Artists": "Esitajad",
- "Application": "Rakendus",
"AppDeviceValues": "Rakendus: {0}, seade: {1}",
- "Albums": "Albumid",
"UserOfflineFromDevice": "{0} katkestas ühenduse seadmega {1}",
"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}",
"External": "Väline",
@@ -128,14 +99,14 @@
"TaskRefreshTrickplayImagesDescription": "Loob trickplay eelvaated videotele lubatud meediakogudes.",
"TaskAudioNormalization": "Normaliseeri helitugevus",
"TaskAudioNormalizationDescription": "Otsib failidest helitugevuse normaliseerimise teavet.",
- "TaskCleanCollectionsAndPlaylistsDescription": "Eemaldab kogumikest ja esitusloenditest üksused, mida enam ei eksisteeri.",
- "TaskCleanCollectionsAndPlaylists": "Puhasta kogumikud ja esitusloendid",
"TaskDownloadMissingLyrics": "Hangi puuduvad laulusõnad",
"TaskDownloadMissingLyricsDescription": "Laulusõnade allalaadimine",
"TaskMoveTrickplayImagesDescription": "Liigutab trickplay pildid meediakogu sätete kohaselt.",
- "TaskExtractMediaSegments": "Skaneeri meediasegmente",
- "TaskExtractMediaSegmentsDescription": "Eraldab või võtab meediasegmendid MediaSegment'i lubavatest pluginatest.",
+ "TaskExtractMediaSegments": "Skaneeri meedialõike",
+ "TaskExtractMediaSegmentsDescription": "Eraldab või võtab meedialõigud MediaSegment'i toega pluginatest.",
"TaskMoveTrickplayImages": "Muuda trickplay piltide asukoht",
"CleanupUserDataTask": "Puhasta kasutajaandmed",
- "CleanupUserDataTaskDescription": "Puhastab kõik kasutajaandmed (vaatamise olek, lemmikute olek jne) meediast, mida pole enam vähemalt 90 päeva saadaval olnud."
+ "CleanupUserDataTaskDescription": "Puhastab kõik kasutajaandmed (vaatamise olek, lemmikute olek jne) meediast, mida pole enam vähemalt 90 päeva saadaval olnud.",
+ "LyricDownloadFailureFromForItem": "Laulusõnade hankimine teenusest {0} loole {1} nurjus",
+ "Original": "Algne"
}
diff --git a/Emby.Server.Implementations/Localization/Core/eu.json b/Emby.Server.Implementations/Localization/Core/eu.json
index c9a798cacd..71c351adcd 100644
--- a/Emby.Server.Implementations/Localization/Core/eu.json
+++ b/Emby.Server.Implementations/Localization/Core/eu.json
@@ -1,23 +1,16 @@
{
- "ValueSpecialEpisodeName": "Berezia - {0}",
- "Sync": "Sinkronizatu",
- "Songs": "Abestiak",
"Shows": "Serieak",
- "Playlists": "Erreprodukzio-zerrendak",
"Photos": "Argazkiak",
"MusicVideos": "Bideo musikalak",
"Movies": "Filmak",
"HeaderContinueWatching": "Ikusten jarraitu",
- "HeaderAlbumArtists": "Albumeko artistak",
"Genres": "Generoak",
"Folders": "Karpetak",
"Favorites": "Gogokoak",
"Default": "Lehenetsia",
"Collections": "Bildumak",
- "Channels": "Kanalak",
"Books": "Liburuak",
"Artists": "Artistak",
- "Albums": "Albumak",
"TaskOptimizeDatabase": "Datu basea optimizatu",
"TaskDownloadMissingSubtitlesDescription": "Falta diren azpitituluak bilatzen ditu interneten metadatuen konfigurazioaren arabera.",
"TaskDownloadMissingSubtitles": "Falta diren azpitituluak deskargatu",
@@ -44,10 +37,8 @@
"TasksLibraryCategory": "Liburutegia",
"TasksMaintenanceCategory": "Mantenua",
"VersionNumber": "Bertsioa {0}",
- "ValueHasBeenAddedToLibrary": "{0} zure multimedia liburutegian gehitu da",
"UserStoppedPlayingItemWithValues": "{0} {1} ikusten bukatu du {2}-(e)n",
"UserStartedPlayingItemWithValues": "{0} {1} ikusten ari da {2}-(e)n",
- "UserPolicyUpdatedWithName": "{0} erabiltzailearen politikak aldatu dira",
"UserPasswordChangedWithName": "{0} erabiltzailearen pasahitza aldatu da",
"UserOnlineFromDevice": "{0} online dago {1}-(e)tik",
"UserOfflineFromDevice": "{0} {1}-(e)tik deskonektatu da",
@@ -55,19 +46,14 @@
"UserDownloadingItemWithValues": "{0} {1} deskargatzen ari da",
"UserDeletedWithName": "{0} Erabiltzailea ezabatu da",
"UserCreatedWithName": "{0} Erabiltzailea sortu da",
- "User": "Erabiltzailea",
"Undefined": "Ezezaguna",
"TvShows": "TB serieak",
- "System": "Sistema",
"SubtitleDownloadFailureFromForItem": "{1}-en azpitutuluak {0}-tik deskargatzeak huts egin du",
"StartupEmbyServerIsLoading": "Jellyfin zerbitzaria kargatzen. Saiatu berriro beranduago.",
- "ServerNameNeedsToBeRestarted": "{0} berrabiarazi behar da",
- "ScheduledTaskStartedWithName": "{0} hasi da",
"ScheduledTaskFailedWithName": "{0} huts egin du",
"PluginUpdatedWithName": "{0} eguneratu da",
"PluginUninstalledWithName": "{0} desinstalatu da",
"PluginInstalledWithName": "{0} instalatu da",
- "Plugin": "Plugin",
"NotificationOptionVideoPlaybackStopped": "Bideoa geldituta",
"NotificationOptionVideoPlayback": "Bideoa martxan",
"NotificationOptionUserLockedOut": "Erabiltzailea blokeatua",
@@ -90,37 +76,22 @@
"NameInstallFailed": "{0} instalazioak huts egin du",
"Music": "Musika",
"MixedContent": "Eduki mistoa",
- "MessageServerConfigurationUpdated": "Zerbitzariaren konfigurazioa eguneratu da",
- "MessageNamedServerConfigurationUpdatedWithValue": "Zerbitzariaren {0} konfigurazio atala eguneratu da",
- "MessageApplicationUpdatedTo": "Jellyfin zerbitzaria {0}-ra eguneratu da",
- "MessageApplicationUpdated": "Jellyfin zerbitzaria eguneratu da",
"Latest": "Azkena",
"LabelRunningTimeValue": "Iraupena: {0}",
"LabelIpAddressValue": "IP helbidea: {0}",
- "ItemRemovedWithName": "{0} liburutegitik kendu da",
- "ItemAddedWithName": "{0} liburutegira gehitu da",
"HomeVideos": "Etxeko bideoak",
"HeaderNextUp": "Hurrengoa",
"HeaderLiveTV": "Zuzeneko TB",
- "HeaderFavoriteSongs": "Gogoko abestiak",
"HeaderFavoriteShows": "Gogoko serieak",
"HeaderFavoriteEpisodes": "Gogoko atalak",
- "HeaderFavoriteArtists": "Gogoko artistak",
- "HeaderFavoriteAlbums": "Gogoko albumak",
"Forced": "Behartuta",
"FailedLoginAttemptWithUserName": "{0}-tik saioa hasteak huts egin du",
"External": "Kanpokoa",
- "DeviceOnlineWithName": "{0} konektatu da",
- "DeviceOfflineWithName": "{0} deskonektatu da",
"ChapterNameValue": "{0} Kapitulua",
- "CameraImageUploadedFrom": "{0}-tik kamera irudi berri bat igo da",
"AuthenticationSucceededWithUserName": "{0} ongi autentifikatu da",
- "Application": "Aplikazioa",
"AppDeviceValues": "App: {0}, Gailua: {1}",
"HearingImpaired": "Entzumen urritasuna",
- "ProviderValue": "Hornitzailea: {0}",
"TaskKeyframeExtractorDescription": "Bideo fitxategietako fotograma gakoak ateratzen ditu HLS erreprodukzio-zerrenda zehatzagoak sortzeko. Zeregin honek denbora asko iraun dezake.",
- "HeaderRecordingGroups": "Grabaketa taldeak",
"Inherit": "Oinordetu",
"TaskOptimizeDatabaseDescription": "Datu-basea trinkotu eta bertatik espazioa askatzen du. Liburutegia eskaneatu ondoren edo datu-basean aldaketak egin ondoren ataza hau exekutatzeak errendimendua hobetu lezake.",
"TaskKeyframeExtractor": "Fotograma gakoen erauzgailua",
@@ -130,12 +101,12 @@
"TaskDownloadMissingLyrics": "Deskargatu falta diren letrak",
"TaskDownloadMissingLyricsDescription": "Deskargatu abestientzako letrak",
"TaskExtractMediaSegments": "Multimedia segmentuen eskaneoa",
- "TaskCleanCollectionsAndPlaylistsDescription": "Jada existitzen ez diren bildumak eta erreprodukzio-zerrendak kentzen ditu.",
- "TaskCleanCollectionsAndPlaylists": "Garbitu bildumak eta erreprodukzio-zerrendak",
"TaskExtractMediaSegmentsDescription": "Media segmentuak atera edo lortzen ditu MediaSegment gaituta duten pluginetik.",
"TaskMoveTrickplayImages": "Aldatu Trickplay irudien kokalekua",
"TaskMoveTrickplayImagesDescription": "Lehendik dauden trickplay fitxategiak liburutegiaren ezarpenen arabera mugitzen dira.",
"TaskAudioNormalizationDescription": "Audio normalizazio datuak lortzeko fitxategiak eskaneatzen ditu.",
"CleanupUserDataTaskDescription": "Gutxienez 90 egunez dagoeneko existitzen ez den multimediatik erabiltzaile-datu guztiak (ikusteko egoera, gogokoen egoera, etab.) garbitzen ditu.",
- "CleanupUserDataTask": "Erabiltzaileen datuak garbitzeko zeregina"
+ "CleanupUserDataTask": "Erabiltzaileen datuak garbitzeko zeregina",
+ "LyricDownloadFailureFromForItem": "Ezin izan dira {1}-ren letrak deskargatu {0}-tik",
+ "Original": "Jatorrizkoa"
}
diff --git a/Emby.Server.Implementations/Localization/Core/fa.json b/Emby.Server.Implementations/Localization/Core/fa.json
index 90cd3a58e9..17ed54b117 100644
--- a/Emby.Server.Implementations/Localization/Core/fa.json
+++ b/Emby.Server.Implementations/Localization/Core/fa.json
@@ -1,41 +1,24 @@
{
- "Albums": "آلبوم‌ها",
"AppDeviceValues": "برنامه: {0} ، دستگاه: {1}",
- "Application": "برنامه",
"Artists": "هنرمندان",
"AuthenticationSucceededWithUserName": "{0} با موفقیت تایید اعتبار شد",
"Books": "کتاب‌ها",
- "CameraImageUploadedFrom": "یک عکس جدید از دوربین ارسال شده است {0}",
- "Channels": "کانالها",
"ChapterNameValue": "قسمت {0}",
"Collections": "مجموعه‌ها",
- "DeviceOfflineWithName": "ارتباط {0} قطع شد",
- "DeviceOnlineWithName": "{0} متصل شد",
"FailedLoginAttemptWithUserName": "تلاش برای ورود از {0} ناموفق بود",
"Favorites": "مورد علاقه‌ها",
"Folders": "پوشه‌ها",
"Genres": "ژانرها",
- "HeaderAlbumArtists": "هنرمندان آلبوم",
"HeaderContinueWatching": "ادامه تماشا",
- "HeaderFavoriteAlbums": "آلبوم‌های مورد علاقه",
- "HeaderFavoriteArtists": "هنرمندان مورد علاقه",
"HeaderFavoriteEpisodes": "قسمت‌های مورد علاقه",
"HeaderFavoriteShows": "سریال‌های مورد علاقه",
- "HeaderFavoriteSongs": "آهنگ‌های مورد علاقه",
"HeaderLiveTV": "پخش زنده",
"HeaderNextUp": "قسمت بعدی",
- "HeaderRecordingGroups": "گروه‌های ضبط",
"HomeVideos": "ویدیوهای خانگی",
"Inherit": "به ارث برده",
- "ItemAddedWithName": "{0} به کتابخانه افزوده شد",
- "ItemRemovedWithName": "{0} از کتابخانه حذف شد",
"LabelIpAddressValue": "آدرس آی پی: {0}",
"LabelRunningTimeValue": "زمان اجرا: {0}",
"Latest": "جدیدترین‌ها",
- "MessageApplicationUpdated": "سرور Jellyfin بروزرسانی شد",
- "MessageApplicationUpdatedTo": "سرور Jellyfin به نسخه {0} بروزرسانی شد",
- "MessageNamedServerConfigurationUpdatedWithValue": "پکربندی بخش {0} سرور بروزرسانی شد",
- "MessageServerConfigurationUpdated": "پیکربندی سرور بروزرسانی شد",
"MixedContent": "محتوای مخلوط",
"Movies": "فیلم ها",
"Music": "موسیقی",
@@ -61,23 +44,14 @@
"NotificationOptionVideoPlayback": "پخش ویدیو آغاز شد",
"NotificationOptionVideoPlaybackStopped": "پخش ویدیو متوقف شد",
"Photos": "عکس‌ها",
- "Playlists": "لیست‌های پخش",
- "Plugin": "افزونه",
"PluginInstalledWithName": "{0} نصب شد",
"PluginUninstalledWithName": "{0} حذف شد",
"PluginUpdatedWithName": "{0} آپدیت شد",
- "ProviderValue": "ارائه دهنده: {0}",
"ScheduledTaskFailedWithName": "{0} شکست خورد",
- "ScheduledTaskStartedWithName": "{0} شروع شد",
- "ServerNameNeedsToBeRestarted": "{0} نیاز به راه اندازی مجدد دارد",
"Shows": "سریال‌ها",
- "Songs": "موسیقی‌ها",
"StartupEmbyServerIsLoading": "سرور Jellyfin در حال بارگیری است. لطفا کمی بعد دوباره تلاش کنید.",
"SubtitleDownloadFailureFromForItem": "بارگیری زیرنویس برای {1} از {0} شکست خورد",
- "Sync": "همگام‌سازی",
- "System": "سیستم",
"TvShows": "سریال‌های تلویزیونی",
- "User": "کاربر",
"UserCreatedWithName": "کاربر {0} ایجاد شد",
"UserDeletedWithName": "کاربر {0} حذف شد",
"UserDownloadingItemWithValues": "{0} در حال بارگیری {1} می‌باشد",
@@ -85,11 +59,8 @@
"UserOfflineFromDevice": "ارتباط {0} از {1} قطع شد",
"UserOnlineFromDevice": "{0} از {1} آنلاین می‌باشد",
"UserPasswordChangedWithName": "گذرواژه برای کاربر {0} تغییر کرد",
- "UserPolicyUpdatedWithName": "سیاست کاربری برای {0} بروزرسانی شد",
"UserStartedPlayingItemWithValues": "{0} در حال پخش {1} بر روی {2} است",
"UserStoppedPlayingItemWithValues": "{0} پخش {1} را بر روی {2} به پایان رساند",
- "ValueHasBeenAddedToLibrary": "{0} به کتابخانه‌ی رسانه‌ی شما افزوده شد",
- "ValueSpecialEpisodeName": "ویژه - {0}",
"VersionNumber": "نسخه {0}",
"TaskCleanTranscodeDescription": "فایل‌های کدگذاری که قدیمی‌تر از یک روز هستند را حذف می‌کند.",
"TaskCleanTranscode": "پاکسازی مسیر کد گذاری",
@@ -126,8 +97,6 @@
"HearingImpaired": "مشکل شنوایی",
"TaskRefreshTrickplayImages": "تولید تصاویر Trickplay",
"TaskRefreshTrickplayImagesDescription": "تولید پیش‌نمایش های trickplay برای ویدیو های فعال شده در کتابخانه.",
- "TaskCleanCollectionsAndPlaylists": "پاکسازی مجموعه ها و لیست پخش",
- "TaskCleanCollectionsAndPlaylistsDescription": "موارد را از مجموعه ها و لیست پخش هایی که دیگر وجود ندارند حذف میکند.",
"TaskAudioNormalizationDescription": "بررسی فایل برای داده‌های نرمال کردن صدا.",
"TaskDownloadMissingLyrics": "دانلود متن‌های ناموجود",
"TaskDownloadMissingLyricsDescription": "دانلود متن شعر‌ها",
diff --git a/Emby.Server.Implementations/Localization/Core/fi.json b/Emby.Server.Implementations/Localization/Core/fi.json
index 15a04d22cd..d080eb0236 100644
--- a/Emby.Server.Implementations/Localization/Core/fi.json
+++ b/Emby.Server.Implementations/Localization/Core/fi.json
@@ -8,57 +8,33 @@
"Music": "Musiikki",
"Movies": "Elokuvat",
"MixedContent": "Sekalainen sisältö",
- "MessageServerConfigurationUpdated": "Palvelimen asetukset on päivitetty",
- "MessageNamedServerConfigurationUpdatedWithValue": "Palvelimen asetusten osio {0} on päivitetty",
- "MessageApplicationUpdatedTo": "Jellyfin-palvelin on päivitetty versioon {0}",
- "MessageApplicationUpdated": "Jellyfin-palvelin on päivitetty",
"Latest": "Viimeisimmät",
"LabelRunningTimeValue": "Kesto: {0}",
"LabelIpAddressValue": "IP-osoite: {0}",
- "ItemRemovedWithName": "{0} poistettiin kirjastosta",
- "ItemAddedWithName": "{0} lisättiin kirjastoon",
"Inherit": "Peri",
"HomeVideos": "Kotivideot",
- "HeaderRecordingGroups": "Tallennusryhmät",
"HeaderNextUp": "Seuraavaksi",
- "HeaderFavoriteSongs": "Suosikkikappaleet",
"HeaderFavoriteShows": "Suosikkisarjat",
"HeaderFavoriteEpisodes": "Suosikkijaksot",
- "HeaderFavoriteArtists": "Suosikkiesittäjät",
- "HeaderFavoriteAlbums": "Suosikkialbumit",
"HeaderContinueWatching": "Jatka katselua",
- "HeaderAlbumArtists": "Albumin esittäjät",
"Genres": "Tyylilajit",
"Folders": "Kansiot",
"Favorites": "Suosikit",
"FailedLoginAttemptWithUserName": "Epäonnistunut kirjautumisyritys lähteestä \"{0}\"",
- "DeviceOnlineWithName": "{0} on yhdistetty",
- "DeviceOfflineWithName": "{0} on katkaissut yhteyden",
"Collections": "Kokoelmat",
"ChapterNameValue": "Kappale {0}",
- "Channels": "Kanavat",
- "CameraImageUploadedFrom": "Uusi kameran kuva on sirretty lähteestä {0}",
"Books": "Kirjat",
- "AuthenticationSucceededWithUserName": "{0} on todennettu",
- "Artists": "Esittäjät",
- "Application": "Sovellus",
+ "AuthenticationSucceededWithUserName": "{0} todennus onnistunut",
+ "Artists": "Artistit",
"AppDeviceValues": "Sovellus: {0}, Laite: {1}",
- "Albums": "Albumit",
- "User": "Käyttäjä",
- "System": "Järjestelmä",
"ScheduledTaskFailedWithName": "{0} epäonnistui",
"PluginUpdatedWithName": "{0} päivitettiin",
"PluginInstalledWithName": "{0} asennettiin",
"Photos": "Valokuvat",
- "ScheduledTaskStartedWithName": "\"{0}\" käynnistetty",
"PluginUninstalledWithName": "{0} poistettiin",
- "Playlists": "Soittolistat",
"VersionNumber": "Versio {0}",
- "ValueSpecialEpisodeName": "Erikoisjakso - {0}",
- "ValueHasBeenAddedToLibrary": "\"{0}\" on lisätty mediakirjastoon",
"UserStoppedPlayingItemWithValues": "{0} lopetti kohteen \"{1}\" toiston sijainnissa \"{2}\"",
"UserStartedPlayingItemWithValues": "{0} toistaa kohdetta \"{1}\" sijainnissa \"{2}\"",
- "UserPolicyUpdatedWithName": "Käyttäjän {0} käyttöoikeudet on päivitetty",
"UserPasswordChangedWithName": "Käyttäjän {0} salasana on vaihdettu",
"UserOnlineFromDevice": "{0} on yhdistänyt sijainnista \"{1}\"",
"UserOfflineFromDevice": "{0} on katkaissut yhteyden sijainnista \"{1}\"",
@@ -67,14 +43,9 @@
"UserDeletedWithName": "Käyttäjä {0} on poistettu",
"UserCreatedWithName": "Käyttäjä {0} on luotu",
"TvShows": "Sarjat",
- "Sync": "Synkronointi",
"SubtitleDownloadFailureFromForItem": "Tekstityksen lataus lähteestä \"{0}\" kohteelle \"{1}\" epäonnistui",
"StartupEmbyServerIsLoading": "Jellyfin-palvelin on latautumassa. Yritä hetken kuluttua uudelleen.",
- "Songs": "Kappaleet",
"Shows": "Sarjat",
- "ServerNameNeedsToBeRestarted": "\"{0}\" on käynnistettävä uudelleen",
- "ProviderValue": "Lähde: {0}",
- "Plugin": "Lisäosa",
"NotificationOptionVideoPlaybackStopped": "Videon toisto lopetettu",
"NotificationOptionVideoPlayback": "Videon toisto aloitettu",
"NotificationOptionUserLockedOut": "Käyttäjä on lukittu",
@@ -126,8 +97,6 @@
"HearingImpaired": "Kuulorajoitteinen",
"TaskRefreshTrickplayImages": "Luo Trickplay-kuvat",
"TaskRefreshTrickplayImagesDescription": "Luo Trickplay-esikatselut käytössä olevien kirjastojen videoista.",
- "TaskCleanCollectionsAndPlaylistsDescription": "Poistaa kohteet kokoelmista ja soittolistoista joita ei ole enää olemassa.",
- "TaskCleanCollectionsAndPlaylists": "Puhdista kokoelmat ja soittolistat",
"TaskAudioNormalization": "Äänenvoimakkuuden normalisointi",
"TaskAudioNormalizationDescription": "Etsii tiedostoista äänenvoimakkuuden normalisointitietoja.",
"TaskDownloadMissingLyrics": "Lataa puuttuva lyriikka",
@@ -137,5 +106,7 @@
"TaskMoveTrickplayImages": "Siirrä Trickplay-kuvien sijainti",
"TaskMoveTrickplayImagesDescription": "Siirtää olemassa olevia trickplay-tiedostoja kirjaston asetusten mukaan.",
"CleanupUserDataTask": "Käyttäjätietojen puhdistustehtävä",
- "CleanupUserDataTaskDescription": "Puhdistaa kaikki käyttäjätiedot (katselutila, suosikit ym.) medioista, joita ei ole ollut saatavilla yli 90 päivään."
+ "CleanupUserDataTaskDescription": "Puhdistaa kaikki käyttäjätiedot (katselutila, suosikit ym.) medioista, joita ei ole ollut saatavilla yli 90 päivään.",
+ "LyricDownloadFailureFromForItem": "Sanoitusten lataus kohteesta {0} kappaleelle {1} epäonnistui",
+ "Original": "Alkuperäinen"
}
diff --git a/Emby.Server.Implementations/Localization/Core/fil.json b/Emby.Server.Implementations/Localization/Core/fil.json
index 28c1d2be5e..30d90e9f98 100644
--- a/Emby.Server.Implementations/Localization/Core/fil.json
+++ b/Emby.Server.Implementations/Localization/Core/fil.json
@@ -1,10 +1,7 @@
{
"VersionNumber": "Bersyon {0}",
- "ValueSpecialEpisodeName": "Espesyal - {0}",
- "ValueHasBeenAddedToLibrary": "Naidagdag na ang {0} sa iyong librerya ng medya",
"UserStoppedPlayingItemWithValues": "Natapos ni {0} ang {1} sa {2}",
"UserStartedPlayingItemWithValues": "Si {0} ay nagpla-play ng {1} sa {2}",
- "UserPolicyUpdatedWithName": "Ang user policy ay nai-update para kay {0}",
"UserPasswordChangedWithName": "Napalitan na ang password ni {0}",
"UserOnlineFromDevice": "Si {0} ay naka-konekta galing sa {1}",
"UserOfflineFromDevice": "Si {0} ay na-diskonekta galing sa {1}",
@@ -12,23 +9,14 @@
"UserDownloadingItemWithValues": "Nagdadownload si {0} ng {1}",
"UserDeletedWithName": "Natanggal na is user {0}",
"UserCreatedWithName": "Nagawa na si user {0}",
- "User": "User",
"TvShows": "Mga Palabas sa Telebisyon",
- "System": "Sistema",
- "Sync": "Pag-sync",
"SubtitleDownloadFailureFromForItem": "Hindi nai-download ang subtitles {0} para sa {1}",
"StartupEmbyServerIsLoading": "Naglo-load ang Jellyfin Server. Mangyaring subukan ulit sandali.",
- "Songs": "Mga Kanta",
"Shows": "Mga Pelikula",
- "ServerNameNeedsToBeRestarted": "Kailangan irestart ang {0}",
- "ScheduledTaskStartedWithName": "Nagsimula na ang {0}",
"ScheduledTaskFailedWithName": "Hindi gumana ang {0}",
- "ProviderValue": "Tagapagtustos: {0}",
"PluginUpdatedWithName": "Naiupdate na ang {0}",
"PluginUninstalledWithName": "Naiuninstall na ang {0}",
"PluginInstalledWithName": "Nainstall na ang {0}",
- "Plugin": "Plugin",
- "Playlists": "Mga Playlist",
"Photos": "Mga Larawan",
"NotificationOptionVideoPlaybackStopped": "Huminto na ang pelikula",
"NotificationOptionVideoPlayback": "Nagsimula na ang pelikula",
@@ -54,42 +42,25 @@
"Music": "Mga Kanta",
"Movies": "Mga Pelikula",
"MixedContent": "Halo-halong content",
- "MessageServerConfigurationUpdated": "Naiupdate na ang server configuration",
- "MessageNamedServerConfigurationUpdatedWithValue": "Naiupdate na ang server configuration section {0}",
- "MessageApplicationUpdatedTo": "Ang bersyon ng Jellyfin Server ay naiupdate sa {0}",
- "MessageApplicationUpdated": "Naiupdate na ang Jellyfin Server",
"Latest": "Pinakabago",
"LabelRunningTimeValue": "Oras: {0}",
"LabelIpAddressValue": "IP address: {0}",
- "ItemRemovedWithName": "Naitanggal ang {0} sa librerya",
- "ItemAddedWithName": "Naidagdag ang {0} sa librerya",
"Inherit": "Manahin",
- "HeaderRecordingGroups": "Pagtatalang Grupo",
"HeaderNextUp": "Susunod",
"HeaderLiveTV": "Live TV",
- "HeaderFavoriteSongs": "Mga Paboritong Kanta",
"HeaderFavoriteShows": "Mga Paboritong Pelikula",
"HeaderFavoriteEpisodes": "Mga Paboritong Yugto",
- "HeaderFavoriteArtists": "Mga Paboritong Artista",
- "HeaderFavoriteAlbums": "Mga Paboritong Album",
"HeaderContinueWatching": "Magpatuloy sa Panonood",
- "HeaderAlbumArtists": "Mga Artista ng Album",
"Genres": "Mga Kategorya",
"Folders": "Mga Folder",
"Favorites": "Mga Paborito",
"FailedLoginAttemptWithUserName": "Maling login galing kay/sa {0}",
- "DeviceOnlineWithName": "Nakakonekta si/ang {0}",
- "DeviceOfflineWithName": "Nadiskonekta si/ang {0}",
"Collections": "Mga Koleksyon",
"ChapterNameValue": "Kabanata {0}",
- "Channels": "Mga Channel",
- "CameraImageUploadedFrom": "May bagong larawan na naupload galing sa/kay {0}",
"Books": "Mga Libro",
"AuthenticationSucceededWithUserName": "Napatunayan si/ang {0}",
"Artists": "Mga Artista",
- "Application": "Aplikasyon",
"AppDeviceValues": "Aplikasyon: {0}, Aparato: {1}",
- "Albums": "Mga Album",
"TaskRefreshLibrary": "Suriin and Librerya ng Medya",
"TaskRefreshChapterImagesDescription": "Gumawa ng larawan para sa mga pelikula na may kabanata.",
"TaskRefreshChapterImages": "Kunin ang mga larawan ng kabanata",
diff --git a/Emby.Server.Implementations/Localization/Core/fo.json b/Emby.Server.Implementations/Localization/Core/fo.json
index 40aa5f71a4..4fb9f4329c 100644
--- a/Emby.Server.Implementations/Localization/Core/fo.json
+++ b/Emby.Server.Implementations/Localization/Core/fo.json
@@ -2,17 +2,15 @@
"Artists": "Listafólk",
"Collections": "Søvn",
"Default": "Sjálvgildi",
- "DeviceOfflineWithName": "{0} hevur slitið sambandið",
"External": "Ytri",
"Genres": "Greinar",
- "Albums": "Album",
"AppDeviceValues": "App: {0}, Eind: {1}",
- "Application": "Nýtsluskipan",
"Books": "Bøkur",
- "Channels": "Rásir",
"ChapterNameValue": "Kapittul {0}",
- "DeviceOnlineWithName": "{0} er sambundið",
"Favorites": "Yndis",
"Folders": "Mappur",
- "Forced": "Kravt"
+ "Forced": "Kravt",
+ "FailedLoginAttemptWithUserName": "Miseydnað innritanarroynd frá {0}",
+ "HeaderFavoriteEpisodes": "Yndispartar",
+ "LabelIpAddressValue": "IP atsetur: {0}"
}
diff --git a/Emby.Server.Implementations/Localization/Core/fr-CA.json b/Emby.Server.Implementations/Localization/Core/fr-CA.json
index a8964e8b62..e05cce47b0 100644
--- a/Emby.Server.Implementations/Localization/Core/fr-CA.json
+++ b/Emby.Server.Implementations/Localization/Core/fr-CA.json
@@ -1,41 +1,24 @@
{
- "Albums": "Albums",
"AppDeviceValues": "App : {0}, Appareil : {1}",
- "Application": "Application",
"Artists": "Artistes",
"AuthenticationSucceededWithUserName": "{0} authentifié avec succès",
"Books": "Livres",
- "CameraImageUploadedFrom": "Une nouvelle photo a été téléversée depuis {0}",
- "Channels": "Chaînes",
"ChapterNameValue": "Chapitre {0}",
"Collections": "Collections",
- "DeviceOfflineWithName": "{0} s'est déconnecté",
- "DeviceOnlineWithName": "{0} est connecté",
"FailedLoginAttemptWithUserName": "Tentative de connexion échouée par {0}",
"Favorites": "Favoris",
"Folders": "Dossiers",
"Genres": "Genres",
- "HeaderAlbumArtists": "Artistes de l'album",
"HeaderContinueWatching": "Reprendre le visionnement",
- "HeaderFavoriteAlbums": "Albums favoris",
- "HeaderFavoriteArtists": "Artistes favoris",
"HeaderFavoriteEpisodes": "Épisodes favoris",
"HeaderFavoriteShows": "Séries favorites",
- "HeaderFavoriteSongs": "Chansons favorites",
"HeaderLiveTV": "TV en direct",
"HeaderNextUp": "À Suivre",
- "HeaderRecordingGroups": "Groupes d'enregistrements",
"HomeVideos": "Vidéos personnelles",
"Inherit": "Hérite",
- "ItemAddedWithName": "{0} a été ajouté à la médiathèque",
- "ItemRemovedWithName": "{0} a été supprimé de la médiathèque",
"LabelIpAddressValue": "Adresse IP : {0}",
"LabelRunningTimeValue": "Durée : {0}",
"Latest": "Plus récent",
- "MessageApplicationUpdated": "Le serveur Jellyfin a été mis à jour",
- "MessageApplicationUpdatedTo": "Le serveur Jellyfin a été mis à jour vers la version {0}",
- "MessageNamedServerConfigurationUpdatedWithValue": "La configuration de la section {0} du serveur a été mise à jour",
- "MessageServerConfigurationUpdated": "La configuration du serveur a été mise à jour",
"MixedContent": "Contenu mixte",
"Movies": "Films",
"Music": "Musique",
@@ -61,23 +44,14 @@
"NotificationOptionVideoPlayback": "Lecture vidéo démarrée",
"NotificationOptionVideoPlaybackStopped": "Lecture vidéo arrêtée",
"Photos": "Photos",
- "Playlists": "Listes de lecture",
- "Plugin": "Extension",
"PluginInstalledWithName": "{0} a été installé",
"PluginUninstalledWithName": "{0} a été désinstallé",
"PluginUpdatedWithName": "{0} a été mis à jour",
- "ProviderValue": "Fournisseur : {0}",
"ScheduledTaskFailedWithName": "{0} a échoué",
- "ScheduledTaskStartedWithName": "{0} a commencé",
- "ServerNameNeedsToBeRestarted": "{0} doit être redémarré",
"Shows": "Séries",
- "Songs": "Chansons",
"StartupEmbyServerIsLoading": "Serveur Jellyfin en cours de chargement. Réessayez dans quelques instants.",
"SubtitleDownloadFailureFromForItem": "Échec du téléchargement des sous-titres depuis {0} pour {1}",
- "Sync": "Synchroniser",
- "System": "Système",
"TvShows": "Séries Télé",
- "User": "Utilisateur",
"UserCreatedWithName": "L'utilisateur {0} a été créé",
"UserDeletedWithName": "L'utilisateur {0} supprimé",
"UserDownloadingItemWithValues": "{0} télécharge {1}",
@@ -85,11 +59,8 @@
"UserOfflineFromDevice": "{0} s'est déconnecté de {1}",
"UserOnlineFromDevice": "{0} s'est connecté de {1}",
"UserPasswordChangedWithName": "Le mot de passe de utilisateur {0} a été modifié",
- "UserPolicyUpdatedWithName": "La politique de l'utilisateur a été mise à jour pour {0}",
"UserStartedPlayingItemWithValues": "{0} joue {1} sur {2}",
"UserStoppedPlayingItemWithValues": "{0} a terminé la lecture de {1} sur {2}",
- "ValueHasBeenAddedToLibrary": "{0} a été ajouté à votre médiathèque",
- "ValueSpecialEpisodeName": "Spécial - {0}",
"VersionNumber": "Version {0}",
"TasksLibraryCategory": "Médiathèque",
"TasksMaintenanceCategory": "Entretien",
@@ -126,8 +97,6 @@
"HearingImpaired": "Malentendants",
"TaskRefreshTrickplayImages": "Générer des images Trickplay",
"TaskRefreshTrickplayImagesDescription": "Crée des aperçus Trickplay pour les vidéos dans les médiathèques activées.",
- "TaskCleanCollectionsAndPlaylists": "Nettoyer les collections et les listes de lecture",
- "TaskCleanCollectionsAndPlaylistsDescription": "Supprime les éléments des collections et des listes de lecture qui n'existent plus.",
"TaskAudioNormalization": "Normalisation audio",
"TaskAudioNormalizationDescription": "Analyse les fichiers à la recherche de données de normalisation audio.",
"TaskExtractMediaSegments": "Analyse des segments de média",
@@ -137,5 +106,7 @@
"TaskMoveTrickplayImages": "Changer l'emplacement des images Trickplay",
"TaskExtractMediaSegmentsDescription": "Extrait ou obtient des segments de média à partir des plugins compatibles avec MediaSegment.",
"CleanupUserDataTaskDescription": "Nettoie toutes les données utilisateur (état de la montre, statut favori, etc.) des supports qui ne sont plus présents depuis au moins 90 jours.",
- "CleanupUserDataTask": "Tâche de nettoyage des données utilisateur"
+ "CleanupUserDataTask": "Tâche de nettoyage des données utilisateur",
+ "LyricDownloadFailureFromForItem": "Le téléchargement des paroles a échoué de {0} pour {1}",
+ "Original": "Original"
}
diff --git a/Emby.Server.Implementations/Localization/Core/fr.json b/Emby.Server.Implementations/Localization/Core/fr.json
index b2a2e502ab..ceba1dcb41 100644
--- a/Emby.Server.Implementations/Localization/Core/fr.json
+++ b/Emby.Server.Implementations/Localization/Core/fr.json
@@ -1,41 +1,24 @@
{
- "Albums": "Albums",
"AppDeviceValues": "Application : {0}, Appareil : {1}",
- "Application": "Application",
"Artists": "Artistes",
"AuthenticationSucceededWithUserName": "{0} authentifié avec succès",
"Books": "Livres",
- "CameraImageUploadedFrom": "Une photo a été téléchargée depuis {0}",
- "Channels": "Chaînes",
"ChapterNameValue": "Chapitre {0}",
"Collections": "Collections",
- "DeviceOfflineWithName": "{0} s'est déconnecté",
- "DeviceOnlineWithName": "{0} est connecté",
"FailedLoginAttemptWithUserName": "Échec de connexion depuis {0}",
"Favorites": "Favoris",
"Folders": "Dossiers",
"Genres": "Genres",
- "HeaderAlbumArtists": "Artistes d'albums",
"HeaderContinueWatching": "Continuer de regarder",
- "HeaderFavoriteAlbums": "Albums favoris",
- "HeaderFavoriteArtists": "Artistes préférés",
"HeaderFavoriteEpisodes": "Épisodes favoris",
"HeaderFavoriteShows": "Séries favorites",
- "HeaderFavoriteSongs": "Chansons préférées",
"HeaderLiveTV": "TV en direct",
"HeaderNextUp": "Prochain à venir",
- "HeaderRecordingGroups": "Groupes d'enregistrements",
"HomeVideos": "Vidéos personnelles",
"Inherit": "Hériter",
- "ItemAddedWithName": "{0} a été ajouté à la médiathèque",
- "ItemRemovedWithName": "{0} a été supprimé de la médiathèque",
"LabelIpAddressValue": "Adresse IP : {0}",
"LabelRunningTimeValue": "Durée : {0}",
"Latest": "Derniers",
- "MessageApplicationUpdated": "Le serveur Jellyfin a été mis à jour",
- "MessageApplicationUpdatedTo": "Le serveur Jellyfin a été mis à jour en version {0}",
- "MessageNamedServerConfigurationUpdatedWithValue": "La configuration de la section {0} du serveur a été mise à jour",
- "MessageServerConfigurationUpdated": "La configuration du serveur a été mise à jour",
"MixedContent": "Contenu mixte",
"Movies": "Films",
"Music": "Musique",
@@ -61,23 +44,14 @@
"NotificationOptionVideoPlayback": "Lecture vidéo démarrée",
"NotificationOptionVideoPlaybackStopped": "Lecture vidéo arrêtée",
"Photos": "Photos",
- "Playlists": "Listes de lecture",
- "Plugin": "Extension",
"PluginInstalledWithName": "{0} a été installé",
"PluginUninstalledWithName": "{0} a été désinstallé",
"PluginUpdatedWithName": "{0} a été mis à jour",
- "ProviderValue": "Fournisseur : {0}",
"ScheduledTaskFailedWithName": "{0} a échoué",
- "ScheduledTaskStartedWithName": "{0} a démarré",
- "ServerNameNeedsToBeRestarted": "{0} doit être redémarré",
"Shows": "Séries",
- "Songs": "Chansons",
"StartupEmbyServerIsLoading": "Le serveur Jellyfin est en cours de chargement. Veuillez réessayer dans quelques instants.",
"SubtitleDownloadFailureFromForItem": "Échec du téléchargement des sous-titres depuis {0} pour {1}",
- "Sync": "Synchroniser",
- "System": "Système",
"TvShows": "Séries TV",
- "User": "Utilisateur",
"UserCreatedWithName": "L'utilisateur {0} a été créé",
"UserDeletedWithName": "L'utilisateur {0} a été supprimé",
"UserDownloadingItemWithValues": "{0} est en train de télécharger {1}",
@@ -85,11 +59,8 @@
"UserOfflineFromDevice": "{0} s'est déconnecté depuis {1}",
"UserOnlineFromDevice": "{0} s'est connecté depuis {1}",
"UserPasswordChangedWithName": "Le mot de passe pour l'utilisateur {0} a été modifié",
- "UserPolicyUpdatedWithName": "La politique de l'utilisateur a été mise à jour pour {0}",
"UserStartedPlayingItemWithValues": "{0} est en train de lire {1} sur {2}",
"UserStoppedPlayingItemWithValues": "{0} vient d'arrêter la lecture de {1} sur {2}",
- "ValueHasBeenAddedToLibrary": "{0} a été ajouté à votre médiathèque",
- "ValueSpecialEpisodeName": "Spécial - {0}",
"VersionNumber": "Version {0}",
"TasksChannelsCategory": "Chaînes en ligne",
"TaskDownloadMissingSubtitlesDescription": "Recherche les sous-titres manquants sur Internet en se basant sur la configuration des métadonnées.",
@@ -126,8 +97,6 @@
"HearingImpaired": "Malentendants",
"TaskRefreshTrickplayImages": "Générer des images Trickplay",
"TaskRefreshTrickplayImagesDescription": "Crée des aperçus Trickplay pour les vidéos dans les médiathèques activées.",
- "TaskCleanCollectionsAndPlaylists": "Nettoyer les collections et les listes de lecture",
- "TaskCleanCollectionsAndPlaylistsDescription": "Supprime les éléments des collections et des listes de lecture qui n'existent plus.",
"TaskAudioNormalization": "Normalisation audio",
"TaskAudioNormalizationDescription": "Analyse les fichiers à la recherche de données de normalisation audio.",
"TaskDownloadMissingLyricsDescription": "Téléchargement des paroles des chansons",
@@ -137,5 +106,7 @@
"TaskExtractMediaSegmentsDescription": "Extrait ou obtient des segments de média à partir des plugins compatibles avec MediaSegment.",
"TaskMoveTrickplayImagesDescription": "Déplace les fichiers trickplay existants en fonction des paramètres de la bibliothèque.",
"CleanupUserDataTaskDescription": "Nettoie toutes les données utilisateur (état de la montre, statut favori, etc.) des supports qui ne sont plus présents depuis au moins 90 jours.",
- "CleanupUserDataTask": "Tâche de nettoyage des données utilisateur"
+ "CleanupUserDataTask": "Tâche de nettoyage des données utilisateur",
+ "LyricDownloadFailureFromForItem": "Le téléchargement des paroles à échoué de {0} pour {1}",
+ "Original": "Original"
}
diff --git a/Emby.Server.Implementations/Localization/Core/ga.json b/Emby.Server.Implementations/Localization/Core/ga.json
index 8c0ae8922a..1ee606cc64 100644
--- a/Emby.Server.Implementations/Localization/Core/ga.json
+++ b/Emby.Server.Implementations/Localization/Core/ga.json
@@ -1,15 +1,10 @@
{
- "Albums": "Albaim",
"Artists": "Ealaíontóirí",
"AuthenticationSucceededWithUserName": "D'éirigh le fíordheimhniú {0}",
"Books": "Leabhair",
- "CameraImageUploadedFrom": "Uaslódáladh íomhá ceamara nua ó {0}",
- "Channels": "Cainéil",
"ChapterNameValue": "Caibidil {0}",
"Collections": "Bailiúcháin",
"Default": "Réamhshocrú",
- "DeviceOfflineWithName": "Tá {0} dícheangailte",
- "DeviceOnlineWithName": "Tá {0} nasctha",
"External": "Seachtrach",
"FailedLoginAttemptWithUserName": "Theip ar iarracht logáil isteach ó {0}",
"Favorites": "Ceanáin",
@@ -29,41 +24,27 @@
"TaskRefreshChannelsDescription": "Athnuachan eolas faoi chainéil idirlín.",
"TaskOptimizeDatabase": "Bunachar sonraí a bharrfheabhsú",
"TaskKeyframeExtractorDescription": "Baintear eochairfhrámaí as comhaid físe chun seinmliostaí HLS níos cruinne a chruthú. Féadfaidh an tasc seo a bheith ar siúl ar feadh i bhfad.",
- "TaskCleanCollectionsAndPlaylistsDescription": "Baintear míreanna as bailiúcháin agus seinmliostaí nach ann dóibh a thuilleadh.",
"TaskDownloadMissingLyricsDescription": "Íosluchtaigh liricí do na hamhráin",
"TaskUpdatePluginsDescription": "Íoslódálann agus suiteálann nuashonruithe do bhreiseáin atá cumraithe le nuashonrú go huathoibríoch.",
"TaskDownloadMissingSubtitlesDescription": "Déanann sé cuardach ar an idirlíon le haghaidh fotheidil atá ar iarraidh bunaithe ar chumraíocht meiteashonraí.",
"TaskExtractMediaSegmentsDescription": "Sliocht nó faigheann codanna meán ó bhreiseáin chumasaithe MediaSegment.",
- "TaskCleanCollectionsAndPlaylists": "Glan suas bailiúcháin agus seinmliostaí",
"TaskOptimizeDatabaseDescription": "Comhdhlúthaíonn bunachar sonraí agus gearrtar spás saor in aisce. Má ritheann tú an tasc seo tar éis scanadh a dhéanamh ar an leabharlann nó athruithe eile a dhéanamh a thugann le tuiscint gur cheart go bhfeabhsófaí an fheidhmíocht.",
"TaskMoveTrickplayImagesDescription": "Bogtar comhaid trickplay atá ann cheana de réir socruithe na leabharlainne.",
"AppDeviceValues": "Aip: {0}, Gléas: {1}",
- "Application": "Feidhmchlár",
"Folders": "Fillteáin",
"Forced": "Éigean",
"Genres": "Seánraí",
- "HeaderAlbumArtists": "Ealaíontóirí albam",
"HeaderContinueWatching": "Leanúint ar aghaidh ag Breathnú",
- "HeaderFavoriteAlbums": "Albam is fearr leat",
- "HeaderFavoriteArtists": "Ealaíontóirí is Fearr",
"HeaderFavoriteEpisodes": "Eipeasóid is fearr leat",
"HeaderFavoriteShows": "Seónna is Fearr",
- "HeaderFavoriteSongs": "Amhráin is fearr leat",
"HeaderLiveTV": "Teilifís beo",
"HeaderNextUp": "Ar Aghaidh Suas",
- "HeaderRecordingGroups": "Grúpaí Taifeadta",
"HearingImpaired": "Lag éisteachta",
"HomeVideos": "Físeáin Baile",
"Inherit": "Oidhreacht",
- "ItemAddedWithName": "Cuireadh {0} leis an leabharlann",
- "ItemRemovedWithName": "Baineadh {0} den leabharlann",
"LabelIpAddressValue": "Seoladh IP: {0}",
"LabelRunningTimeValue": "Am rite: {0}",
"Latest": "Is déanaí",
- "MessageApplicationUpdated": "Tá Freastalaí Jellyfin nuashonraithe",
- "MessageApplicationUpdatedTo": "Nuashonraíodh Freastalaí Jellyfin go {0}",
- "MessageNamedServerConfigurationUpdatedWithValue": "Nuashonraíodh an chuid cumraíochta freastalaí {0}",
- "MessageServerConfigurationUpdated": "Nuashonraíodh cumraíocht an fhreastalaí",
"MixedContent": "Ábhar measctha",
"Movies": "Scannáin",
"Music": "Ceol",
@@ -89,24 +70,15 @@
"NotificationOptionVideoPlayback": "Cuireadh tús le hathsheinm físe",
"NotificationOptionVideoPlaybackStopped": "Cuireadh deireadh le hathsheinm físe",
"Photos": "Grianghraif",
- "Playlists": "Seinmliostaí",
- "Plugin": "Breiseán",
"PluginInstalledWithName": "Suiteáladh {0}",
"PluginUninstalledWithName": "Díshuiteáladh {0}",
"PluginUpdatedWithName": "Nuashonraíodh {0}",
- "ProviderValue": "Soláthraí: {0}",
"ScheduledTaskFailedWithName": "Theip ar {0}",
- "ScheduledTaskStartedWithName": "Thosaigh {0}",
- "ServerNameNeedsToBeRestarted": "Ní mór {0} a atosú",
"Shows": "Seónna",
- "Songs": "Amhráin",
"StartupEmbyServerIsLoading": "Tá freastalaí Jellyfin á luchtú. Bain triail eile as gan mhoill.",
"SubtitleDownloadFailureFromForItem": "Theip ar fhotheidil a íoslódáil ó {0} le haghaidh {1}",
- "Sync": "Sioncrónaigh",
- "System": "Córas",
"TvShows": "Seónna Teilifíse",
"Undefined": "Neamhshainithe",
- "User": "Úsáideoir",
"UserCreatedWithName": "Cruthaíodh úsáideoir {0}",
"UserDeletedWithName": "Scriosadh úsáideoir {0}",
"UserDownloadingItemWithValues": "Tá {0} á íoslódáil {1}",
@@ -114,11 +86,8 @@
"UserOfflineFromDevice": "Tá {0} dícheangailte ó {1}",
"UserOnlineFromDevice": "Tá {0} ar líne ó {1}",
"UserPasswordChangedWithName": "Athraíodh pasfhocal don úsáideoir {0}",
- "UserPolicyUpdatedWithName": "Nuashonraíodh polasaí úsáideora le haghaidh {0}",
"UserStartedPlayingItemWithValues": "Tá {0} ag seinnt {1} ar {2}",
"UserStoppedPlayingItemWithValues": "Chríochnaigh {0} ag imirt {1} ar {2}",
- "ValueHasBeenAddedToLibrary": "Cuireadh {0} le do leabharlann meán",
- "ValueSpecialEpisodeName": "Speisialta - {0}",
"VersionNumber": "Leagan {0}",
"TasksMaintenanceCategory": "Cothabháil",
"TasksLibraryCategory": "Leabharlann",
@@ -137,5 +106,7 @@
"TaskCleanTranscode": "Eolaire Transcode Glan",
"TaskDownloadMissingSubtitles": "Íosluchtaigh fotheidil ar iarraidh",
"CleanupUserDataTask": "Tasc glantacháin sonraí úsáideora",
- "CleanupUserDataTaskDescription": "Glanann sé gach sonraí úsáideora (stádas faire, stádas is fearr leat srl.) ó mheáin nach bhfuil i láthair a thuilleadh ar feadh 90 lá ar a laghad."
+ "CleanupUserDataTaskDescription": "Glanann sé gach sonraí úsáideora (stádas faire, stádas is fearr leat srl.) ó mheáin nach bhfuil i láthair a thuilleadh ar feadh 90 lá ar a laghad.",
+ "Original": "Bunaidh",
+ "LyricDownloadFailureFromForItem": "Theip ar liricí a íoslódáil ó {0} do {1}"
}
diff --git a/Emby.Server.Implementations/Localization/Core/gl.json b/Emby.Server.Implementations/Localization/Core/gl.json
index b3f137febd..a68db0076a 100644
--- a/Emby.Server.Implementations/Localization/Core/gl.json
+++ b/Emby.Server.Implementations/Localization/Core/gl.json
@@ -1,13 +1,9 @@
{
- "Albums": "Álbums",
"Collections": "Coleccións",
"ChapterNameValue": "Capítulo {0}",
- "Channels": "Canles",
- "CameraImageUploadedFrom": "Cargouse unha nova imaxe de cámara dende {0}",
"Books": "Libros",
"AuthenticationSucceededWithUserName": "{0} autenticouse correctamente",
"Artists": "Artistas",
- "Application": "Aplicación",
"NotificationOptionServerRestartRequired": "Necesario o reinicio do servidor",
"NotificationOptionPluginUpdateInstalled": "Actualización do plugin instalada",
"NotificationOptionPluginUninstalled": "Plugin desinstalado",
@@ -28,63 +24,41 @@
"Music": "Música",
"Movies": "Películas",
"MixedContent": "Contido mixto",
- "MessageServerConfigurationUpdated": "Actualizouse a configuración do servidor",
- "MessageNamedServerConfigurationUpdatedWithValue": "Actualizouse a sección de configuración {0} do servidor",
- "MessageApplicationUpdatedTo": "O servidor Jellyfin actualizouse a {0}",
- "MessageApplicationUpdated": "O servidor Jellyfin actualizouse",
"Latest": "Último",
"LabelRunningTimeValue": "Tempo en execución: {0}",
"LabelIpAddressValue": "Enderezo IP: {0}",
- "ItemRemovedWithName": "{0} eliminouse da biblioteca",
- "ItemAddedWithName": "{0} engadiuse á biblioteca",
"Inherit": "Herdar",
"HomeVideos": "Videos caseiros",
- "HeaderRecordingGroups": "Grupos de grabación",
"HeaderNextUp": "De seguido",
"HeaderLiveTV": "TV en directo",
- "HeaderFavoriteSongs": "Cancións favoritas",
"HeaderFavoriteShows": "Series de TV favoritas",
"HeaderFavoriteEpisodes": "Episodios favoritos",
- "HeaderFavoriteArtists": "Artistas favoritos",
- "HeaderFavoriteAlbums": "Álbums favoritos",
"HeaderContinueWatching": "Seguir vendo",
- "HeaderAlbumArtists": "Artistas do álbum",
"Genres": "Xéneros",
"Forced": "Forzado",
"Folders": "Cartafoles",
"Favorites": "Favoritos",
"FailedLoginAttemptWithUserName": "Fallo de intento de inicio de sesión dende {0}",
- "DeviceOnlineWithName": "{0} conectouse",
- "DeviceOfflineWithName": "{0} desconectouse",
"Default": "Por defecto",
"AppDeviceValues": "Aplicación: {0}, Dispositivo: {1}",
"TaskCleanLogs": "Limpar directorio de rexistros",
"TaskCleanActivityLog": "Limpar rexistro de actividade",
"TasksChannelsCategory": "Canles da Internet",
"TaskUpdatePlugins": "Actualizar plugins",
- "User": "Usuario",
"Undefined": "Sen definir",
"TvShows": "Programas de TV",
- "System": "Sistema",
- "Sync": "Sincronizar",
"SubtitleDownloadFailureFromForItem": "Fallou a descarga de subtítulos para {1} dende {0}",
"StartupEmbyServerIsLoading": "O servidor Jellyfin está cargando. Por favor, ténteo axiña outra vez.",
- "Songs": "Cancións",
"Shows": "Programas",
- "ServerNameNeedsToBeRestarted": "{0} precisa ser reiniciado",
- "ScheduledTaskStartedWithName": "{0} comezou",
"ScheduledTaskFailedWithName": "{0} fallou",
- "ProviderValue": "Provedor: {0}",
"PluginUpdatedWithName": "{0} foi actualizado",
"PluginUninstalledWithName": "{0} foi desinstalado",
"PluginInstalledWithName": "{0} foi instalado",
- "Playlists": "Listas de reproducción",
"Photos": "Fotos",
"UserLockedOutWithName": "O usuario {0} foi bloqueado",
"UserDownloadingItemWithValues": "{0} está a ser transferido {1}",
"UserDeletedWithName": "O usuario {0} foi borrado",
"UserCreatedWithName": "O usuario {0} foi creado",
- "Plugin": "Plugin",
"NotificationOptionVideoPlaybackStopped": "Reproducción de vídeo detida",
"NotificationOptionVideoPlayback": "Reproducción de vídeo iniciada",
"NotificationOptionUserLockedOut": "Usuario bloqueado",
@@ -109,12 +83,9 @@
"TaskCleanCache": "Limpar directorio de caché",
"TaskCleanActivityLogDescription": "Borra do rexistro de actividade as entradas anteriores á data configurada.",
"TasksApplicationCategory": "Aplicación",
- "ValueSpecialEpisodeName": "Especial - {0}",
- "ValueHasBeenAddedToLibrary": "{0} engadiuse á túa biblioteca de medios",
"TasksLibraryCategory": "Biblioteca",
"TasksMaintenanceCategory": "Mantemento",
"VersionNumber": "Versión {0}",
- "UserPolicyUpdatedWithName": "A política de usuario foi actualizada para {0}",
"UserPasswordChangedWithName": "Cambiouse o contrasinal para o usuario {0}",
"UserOnlineFromDevice": "{0} está en liña desde {1}",
"UserOfflineFromDevice": "{0} desconectouse dende {1}",
@@ -128,8 +99,6 @@
"TaskRefreshTrickplayImagesDescription": "Crea miniaturas de previsualización para os vídeos nas bibliotecas habilitadas.",
"TaskDownloadMissingLyrics": "Descargar letras que faltan",
"TaskDownloadMissingLyricsDescription": "Descarga as letras das cancións",
- "TaskCleanCollectionsAndPlaylists": "Limpar coleccións e listas de reprodución",
- "TaskCleanCollectionsAndPlaylistsDescription": "Quita ítems que xa non existen das coleccións e listas de reprodución.",
"TaskExtractMediaSegmentsDescription": "Procura segmentos de medios cos plugins habilitados.",
"TaskExtractMediaSegments": "Escaneo de segmentos de medios",
"TaskMoveTrickplayImages": "Migrar as miniaturas de previsualización a outra ubicación",
@@ -137,5 +106,6 @@
"TaskRefreshTrickplayImages": "Xerar miniaturas de previsualización",
"TaskAudioNormalizationDescription": "Escanea ficheiros á procura de datos de normalización de volume.",
"CleanupUserDataTask": "Tarefa de limpeza de datos dos usuarios",
- "CleanupUserDataTaskDescription": "Limpa todos os datos do usuario (estado de visualización, de favorito etc.) dos medios ausentes polo menos 90 días."
+ "CleanupUserDataTaskDescription": "Limpa todos os datos do usuario (estado de visualización, de favorito etc.) dos medios ausentes polo menos 90 días.",
+ "Original": "Orixinal"
}
diff --git a/Emby.Server.Implementations/Localization/Core/gsw.json b/Emby.Server.Implementations/Localization/Core/gsw.json
index 9be6f05ee1..b95e5edecc 100644
--- a/Emby.Server.Implementations/Localization/Core/gsw.json
+++ b/Emby.Server.Implementations/Localization/Core/gsw.json
@@ -1,41 +1,24 @@
{
- "Albums": "Alben",
"AppDeviceValues": "App: {0}, Gerät: {1}",
- "Application": "Applikation",
"Artists": "Künstler",
"AuthenticationSucceededWithUserName": "{0} hat sich angemeldet",
"Books": "Bücher",
- "CameraImageUploadedFrom": "Ein neues Foto wurde von {0} hochgeladen",
- "Channels": "Kanäle",
"ChapterNameValue": "Kapitel {0}",
"Collections": "Sammlungen",
- "DeviceOfflineWithName": "{0} wurde getrennt",
- "DeviceOnlineWithName": "{0} ist verbunden",
"FailedLoginAttemptWithUserName": "Fählgschlagene Ameldeversuech vo {0}",
"Favorites": "Favorite",
"Folders": "Ordner",
"Genres": "Genre",
- "HeaderAlbumArtists": "Album-Künschtler",
"HeaderContinueWatching": "weiter schauen",
- "HeaderFavoriteAlbums": "Lieblingsalben",
- "HeaderFavoriteArtists": "Lieblings-Künstler",
"HeaderFavoriteEpisodes": "Lieblingsepisoden",
"HeaderFavoriteShows": "Lieblingsserien",
- "HeaderFavoriteSongs": "Lieblingslieder",
"HeaderLiveTV": "Live-Fernseh",
"HeaderNextUp": "Als Nächstes",
- "HeaderRecordingGroups": "Aufnahme-Gruppen",
"HomeVideos": "Heimvideos",
"Inherit": "Vererben",
- "ItemAddedWithName": "{0} wurde der Bibliothek hinzugefügt",
- "ItemRemovedWithName": "{0} wurde aus der Bibliothek entfernt",
"LabelIpAddressValue": "IP-Adresse: {0}",
"LabelRunningTimeValue": "Laufzeit: {0}",
"Latest": "Neueste",
- "MessageApplicationUpdated": "Jellyfin-Server wurde aktualisiert",
- "MessageApplicationUpdatedTo": "Jellyfin-Server wurde auf Version {0} aktualisiert",
- "MessageNamedServerConfigurationUpdatedWithValue": "Der Server-Einstellungsbereich {0} wurde aktualisiert",
- "MessageServerConfigurationUpdated": "Serveriistöuige send aktualisiert worde",
"MixedContent": "Gmeschti Inhäut",
"Movies": "Film",
"Music": "Musig",
@@ -61,23 +44,14 @@
"NotificationOptionVideoPlayback": "Videowedergab gstartet",
"NotificationOptionVideoPlaybackStopped": "Videowedergab gstoppt",
"Photos": "Fotis",
- "Playlists": "Wedergabeliste",
- "Plugin": "Plugin",
"PluginInstalledWithName": "{0} esch installiert worde",
"PluginUninstalledWithName": "{0} esch deinstalliert worde",
"PluginUpdatedWithName": "{0} esch updated worde",
- "ProviderValue": "Aabieter: {0}",
"ScheduledTaskFailedWithName": "{0} esch fäugschlage",
- "ScheduledTaskStartedWithName": "{0} het gstartet",
- "ServerNameNeedsToBeRestarted": "{0} mues nöi gstartet wärde",
"Shows": "Serie",
- "Songs": "Lieder",
"StartupEmbyServerIsLoading": "Jellyfin Server ladt. Bitte grad noeinisch probiere.",
"SubtitleDownloadFailureFromForItem": "Ondertetle vo {0} för {1} hend ned chönne abeglade wärde",
- "Sync": "Synchronisation",
- "System": "System",
"TvShows": "Färnsehserie",
- "User": "Benotzer",
"UserCreatedWithName": "Benotzer {0} esch erstöut worde",
"UserDeletedWithName": "Benotzer {0} esch glösche worde",
"UserDownloadingItemWithValues": "{0} ladt {1} abe",
@@ -85,11 +59,8 @@
"UserOfflineFromDevice": "{0} esch vo {1} trennt worde",
"UserOnlineFromDevice": "{0} esch online vo {1}",
"UserPasswordChangedWithName": "S'Passwort för Benotzer {0} esch gänderet worde",
- "UserPolicyUpdatedWithName": "Benotzerrechtlinie för {0} esch aktualisiert worde",
"UserStartedPlayingItemWithValues": "{0} hed d'Wedergab vo {1} of {2} gstartet",
"UserStoppedPlayingItemWithValues": "{0} het d'Wedergab vo {1} of {2} gstoppt",
- "ValueHasBeenAddedToLibrary": "{0} esch dinnere Biblithek hinzuegfüegt worde",
- "ValueSpecialEpisodeName": "Extra - {0}",
"VersionNumber": "Version {0}",
"TaskCleanLogs": "Lösche Log Pfad",
"TaskRefreshLibraryDescription": "Scanne alle Bibliotheken für hinzugefügte Datein und erneuere Metadaten.",
diff --git a/Emby.Server.Implementations/Localization/Core/he.json b/Emby.Server.Implementations/Localization/Core/he.json
index ef95a639f6..48056b0bb8 100644
--- a/Emby.Server.Implementations/Localization/Core/he.json
+++ b/Emby.Server.Implementations/Localization/Core/he.json
@@ -1,41 +1,24 @@
{
- "Albums": "אלבומים",
"AppDeviceValues": "יישום: {0}, מכשיר: {1}",
- "Application": "יישום",
"Artists": "אומנים",
"AuthenticationSucceededWithUserName": "{0} אומת בהצלחה",
"Books": "ספרים",
- "CameraImageUploadedFrom": "תמונת מצלמה חדשה הועלתה מתוך {0}",
- "Channels": "ערוצים",
"ChapterNameValue": "פרק {0}",
"Collections": "אוספים",
- "DeviceOfflineWithName": "{0} התנתק",
- "DeviceOnlineWithName": "{0} מחובר",
"FailedLoginAttemptWithUserName": "ניסיון כניסה שגוי דרך {0}",
"Favorites": "מועדפים",
"Folders": "תיקיות",
"Genres": "ז׳אנרים",
- "HeaderAlbumArtists": "אמני האלבום",
"HeaderContinueWatching": "המשך צפייה",
- "HeaderFavoriteAlbums": "אלבומים מועדפים",
- "HeaderFavoriteArtists": "אמנים מועדפים",
"HeaderFavoriteEpisodes": "פרקים מועדפים",
"HeaderFavoriteShows": "תוכניות מועדפות",
- "HeaderFavoriteSongs": "שירים מועדפים",
"HeaderLiveTV": "שידורים חיים",
"HeaderNextUp": "הבא בתור",
- "HeaderRecordingGroups": "קבוצות הקלטה",
"HomeVideos": "סרטונים בייתים",
"Inherit": "הורש",
- "ItemAddedWithName": "{0} נוסף לספרייה",
- "ItemRemovedWithName": "{0} נמחק מהספרייה",
"LabelIpAddressValue": "Ip כתובת: {0}",
"LabelRunningTimeValue": "משך צפייה: {0}",
"Latest": "אחרון",
- "MessageApplicationUpdated": "שרת Jellyfin עודכן",
- "MessageApplicationUpdatedTo": "שרת Jellyfin עודכן לגרסה {0}",
- "MessageNamedServerConfigurationUpdatedWithValue": "סעיף הגדרת השרת {0} עודכן",
- "MessageServerConfigurationUpdated": "תצורת השרת עודכנה",
"MixedContent": "תוכן מעורב",
"Movies": "סרטים",
"Music": "מוזיקה",
@@ -61,23 +44,14 @@
"NotificationOptionVideoPlayback": "ניגון וידאו החל",
"NotificationOptionVideoPlaybackStopped": "ניגון וידאו הופסק",
"Photos": "צילומים",
- "Playlists": "רשימות נגינה",
- "Plugin": "תוסף",
"PluginInstalledWithName": "{0} הותקן",
"PluginUninstalledWithName": "{0} הוסר",
"PluginUpdatedWithName": "{0} עודכן",
- "ProviderValue": "ספק: {0}",
"ScheduledTaskFailedWithName": "{0} נכשל",
- "ScheduledTaskStartedWithName": "{0} החל",
- "ServerNameNeedsToBeRestarted": "{0} דורש הפעלה מחדש",
"Shows": "סדרות",
- "Songs": "שירים",
"StartupEmbyServerIsLoading": "שרת Jellyfin בתהליך טעינה. נא לנסות שוב בקרוב.",
"SubtitleDownloadFailureFromForItem": "הורדת כתוביות מ־{0} עבור {1} נכשלה",
- "Sync": "סנכרון",
- "System": "מערכת",
"TvShows": "סדרות טלוויזיה",
- "User": "משתמש",
"UserCreatedWithName": "המשתמש {0} נוצר",
"UserDeletedWithName": "המשתמש {0} הוסר",
"UserDownloadingItemWithValues": "{0} מוריד את {1}",
@@ -85,11 +59,8 @@
"UserOfflineFromDevice": "{0} התנתק מ־{1}",
"UserOnlineFromDevice": "{0} מחובר מ־{1}",
"UserPasswordChangedWithName": "הסיסמה שונתה עבור המשתמש {0}",
- "UserPolicyUpdatedWithName": "מדיניות המשתמש {0} עודכנה",
"UserStartedPlayingItemWithValues": "{0} מנגן את {1} על {2}",
"UserStoppedPlayingItemWithValues": "{0} סיים לנגן את {1} על {2}",
- "ValueHasBeenAddedToLibrary": "{0} התווסף לספריית המדיה שלך",
- "ValueSpecialEpisodeName": "מיוחד- {0}",
"VersionNumber": "גרסה {0}",
"TaskRefreshLibrary": "סרוק ספריית מדיה",
"TaskRefreshChapterImages": "חלץ תמונות פרקים",
@@ -127,9 +98,7 @@
"TaskRefreshTrickplayImages": "יצירת תמונות Trickplay",
"TaskRefreshTrickplayImagesDescription": "יוצר תמונות Trickplay לסרטונים בספריות הפעילות.",
"TaskAudioNormalization": "נרמול שמע",
- "TaskCleanCollectionsAndPlaylistsDescription": "מנקה פריטים לא קיימים מאוספים ורשימות השמעה.",
"TaskAudioNormalizationDescription": "מחפש קבצי נורמליזציה של שמע.",
- "TaskCleanCollectionsAndPlaylists": "מנקה אוספים ורשימות השמעה",
"TaskDownloadMissingLyrics": "הורדת מילים חסרות",
"TaskDownloadMissingLyricsDescription": "הורדת מילים לשירים",
"TaskMoveTrickplayImages": "העברת מיקום של תמונות Trickplay",
@@ -137,5 +106,7 @@
"TaskExtractMediaSegmentsDescription": "מחלץ חלקי מדיה מתוספים המאפשרים זאת.",
"TaskMoveTrickplayImagesDescription": "הזזת קבצי Trickplay קיימים בהתאם להגדרות הספרייה.",
"CleanupUserDataTaskDescription": "ניקוי כל המידע של המשתמש (מצב צפייה, מועדפים וכו) ממדיה שאינה קיימת מעל 90 יום.",
- "CleanupUserDataTask": "משימת ניקוי מידע משתמש"
+ "CleanupUserDataTask": "משימת ניקוי מידע משתמש",
+ "LyricDownloadFailureFromForItem": "הורדת המילים מ-{0} עבור {1} נכשלה",
+ "Original": "מקור"
}
diff --git a/Emby.Server.Implementations/Localization/Core/he_IL.json b/Emby.Server.Implementations/Localization/Core/he_IL.json
index 0967ef424b..b551608fd0 100644
--- a/Emby.Server.Implementations/Localization/Core/he_IL.json
+++ b/Emby.Server.Implementations/Localization/Core/he_IL.json
@@ -1 +1,112 @@
-{}
+{
+ "Books": "ספרים",
+ "NameSeasonNumber": "עונה {0}",
+ "Movies": "סרטים",
+ "Music": "מוזיקה",
+ "Collections": "אוספים",
+ "Artists": "אמנים",
+ "ChapterNameValue": "פרק {0}",
+ "External": "חיצונית",
+ "Favorites": "מועדפים",
+ "Folders": "תיקיות",
+ "Genres": "ז'אנרים",
+ "HeaderContinueWatching": "להמשיך לצפות",
+ "HeaderFavoriteEpisodes": "פרקים אהובים",
+ "HeaderFavoriteShows": "תוכניות אהובות",
+ "HeaderLiveTV": "טלוויזיה בשידור חי",
+ "HeaderNextUp": "הבא",
+ "HearingImpaired": "ללקויי שמיעה",
+ "HomeVideos": "סרטונים ביתיים",
+ "AppDeviceValues": "אפליקציה: {0}, מכשיר: {1}",
+ "AuthenticationSucceededWithUserName": "{0} אומת בהצלחה",
+ "Default": "בררת מחדל",
+ "FailedLoginAttemptWithUserName": "התחברות נכשלה מ {0}",
+ "Forced": "בכוח",
+ "Inherit": "ירש",
+ "LabelIpAddressValue": "כתובת IP: {0}",
+ "LabelRunningTimeValue": "זמן ריצה: {0}",
+ "Latest": "הכי חדש",
+ "LyricDownloadFailureFromForItem": "מילות שיר נכשלו לרדת מ{0} בשביל {1}",
+ "MixedContent": "תוכן מעורב",
+ "MusicVideos": "סרטוני מוזיקה",
+ "NameInstallFailed": "{0} התכנות כושלות",
+ "NameSeasonUnknown": "עונה לא ידוע",
+ "NewVersionIsAvailable": "גרסה חדשה של שרת Jellyfin זמינה להורדה.",
+ "NotificationOptionApplicationUpdateAvailable": "גרסת אפליקציה חדשה זמינה להורדה",
+ "NotificationOptionApplicationUpdateInstalled": "עדכון אפליקציה הותקן",
+ "NotificationOptionAudioPlayback": "החלה השמעת אודיו",
+ "NotificationOptionAudioPlaybackStopped": "ניגון השמע הופסק",
+ "NotificationOptionCameraImageUploaded": "תמונת מצלמה עודכן",
+ "NotificationOptionInstallationFailed": "התקנה נכשלה",
+ "NotificationOptionNewLibraryContent": "תוכן חדש נוסף",
+ "NotificationOptionPluginError": "תוסף נכשל",
+ "NotificationOptionPluginInstalled": "תוסף הותקן",
+ "NotificationOptionPluginUninstalled": "תוסף נמחק",
+ "NotificationOptionPluginUpdateInstalled": "עידכון לתוסף הותקן",
+ "NotificationOptionServerRestartRequired": "נדרש התחול מחדש לשרת",
+ "NotificationOptionTaskFailed": "כשל במשימה מתוכננת",
+ "NotificationOptionUserLockedOut": "המשתמש ננעל",
+ "NotificationOptionVideoPlayback": "החלה הפעלת וידאו",
+ "NotificationOptionVideoPlaybackStopped": "הפעלת הסרטון הופסקה",
+ "Original": "מקורי",
+ "Photos": "תמונות",
+ "PluginInstalledWithName": "{0} הותקן",
+ "PluginUninstalledWithName": "{0} נמחק",
+ "PluginUpdatedWithName": "{0} עודכן",
+ "ScheduledTaskFailedWithName": "{0} נכשל",
+ "Shows": "סדרות",
+ "StartupEmbyServerIsLoading": "שרת Jellyfin נטען. אנא נסה שוב בקרוב.",
+ "SubtitleDownloadFailureFromForItem": "הורדת הכתוביות מ-{0} עבור {1} נכשלה",
+ "TvShows": "תוכניות טלויזיה",
+ "Undefined": "לא מוגדר",
+ "UserCreatedWithName": "המשתמש {0} נוצר",
+ "UserDeletedWithName": "המשתמש {0} נמחק",
+ "UserDownloadingItemWithValues": "{0} מוריד את {1}",
+ "UserLockedOutWithName": "המשתמש {0} ננעל בחוץ",
+ "UserOfflineFromDevice": "{0} התנתק מ-{1}",
+ "UserOnlineFromDevice": "{0} מחובר מ-{1}",
+ "UserPasswordChangedWithName": "הסיסמה שונתה עבור המשתמש {0}",
+ "UserStartedPlayingItemWithValues": "{0} מנגן ב-{1} ב-{2}",
+ "UserStoppedPlayingItemWithValues": "{0} סיים לנגן את {1} ב-{2}",
+ "VersionNumber": "גרסה {0}",
+ "TasksMaintenanceCategory": "תחזוקה",
+ "TasksLibraryCategory": "ספריה",
+ "TasksApplicationCategory": "אפליקציה",
+ "TasksChannelsCategory": "ערוצי אינטרנט",
+ "TaskCleanActivityLog": "נקה יומן פעילות",
+ "TaskCleanActivityLogDescription": "מוחק רשומות יומן פעילות ישנות יותר מהגיל שהוגדר.",
+ "TaskCleanCache": "נקה ספריית מטמון",
+ "TaskCleanCacheDescription": "מוחק קבצי מטמון שאינם נחוצים עוד על ידי המערכת.",
+ "TaskRefreshChapterImages": "חלץ תמונות פרק",
+ "TaskRefreshChapterImagesDescription": "יוצר תמונות ממוזערות עבור סרטונים שיש להם פרקים.",
+ "TaskAudioNormalization": "נורמליזציה של שמע",
+ "TaskAudioNormalizationDescription": "סורק קבצים לאיתור נתוני נרמול שמע.",
+ "TaskRefreshLibrary": "סרוק ספריית מדיה",
+ "TaskRefreshLibraryDescription": "סורק את ספריית המדיה שלך לאיתור קבצים חדשים ומרענן מטא-דאטה.",
+ "TaskCleanLogs": "נקה ספריית יומן",
+ "TaskCleanLogsDescription": "מוחק קבצי יומן שגילם עולה על {0} ימים.",
+ "TaskRefreshPeople": "רענן אנשים",
+ "TaskRefreshPeopleDescription": "מעדכן מטא-דאטה עבור שחקנים ובמאים בספריית המדיה שלך.",
+ "TaskRefreshTrickplayImages": "צור תמונות Trickplay",
+ "TaskRefreshTrickplayImagesDescription": "יוצר תצוגות מקדימות של trickplay עבור סרטונים בספריות מופעלות.",
+ "TaskUpdatePlugins": "עדכן פלאגינים",
+ "TaskUpdatePluginsDescription": "מוריד ומתקין עדכונים עבור תוספים שתצורתם נקבעה לעדכון אוטומטי.",
+ "TaskCleanTranscode": "נקה ספריית קידוד",
+ "TaskCleanTranscodeDescription": "תמחוק את קבצי הקידוד בני יותר מיום.",
+ "TaskRefreshChannels": "רענן ערוצים",
+ "TaskRefreshChannelsDescription": "מרענן את פרטי ערוץ האינטרנט.",
+ "TaskDownloadMissingLyrics": "הורד מילות שיר חסרות",
+ "TaskDownloadMissingLyricsDescription": "הורדות מילים לשירים",
+ "TaskDownloadMissingSubtitles": "הורד כתוביות חסרות",
+ "TaskDownloadMissingSubtitlesDescription": "מחפש באינטרנט אחר כתוביות חסרות בהתבסס על תצורת מטא-דאטה.",
+ "TaskOptimizeDatabase": "בצע אופטימיזציה של מסד הנתונים",
+ "TaskOptimizeDatabaseDescription": "דוחס את מסד הנתונים וקיצוץ שטח פנוי. הפעלת משימה זו לאחר סריקת הספרייה או ביצוע שינויים אחרים שמשמעותם שינויים בבסיס הנתונים עשויה לשפר את הביצועים.",
+ "TaskKeyframeExtractor": "מחלץ פריים מרכזי",
+ "TaskKeyframeExtractorDescription": "מחלץ פריימים מרכזיים מקבצי וידאו כדי ליצור רשימות השמעה HLS מדויקות יותר. משימה זו עשויה להימשך זמן רב.",
+ "TaskExtractMediaSegments": "סריקת מקטעי מדיה",
+ "TaskExtractMediaSegmentsDescription": "מחלץ או משיג קטעי מדיה מתוספים התומכים ב-MediaSegment.",
+ "TaskMoveTrickplayImages": "העברת מיקום תמונת Trickplay",
+ "TaskMoveTrickplayImagesDescription": "מעביר קבצי trickplay קיימים בהתאם להגדרות הספרייה.",
+ "CleanupUserDataTask": "משימת ניקוי נתוני משתמש",
+ "CleanupUserDataTaskDescription": "מנקה את כל נתוני המשתמש (מצב צפייה, סטטוס מועדף וכו') ממדיה שכבר לא הייתה קיימת במשך 90 יום לפחות."
+}
diff --git a/Emby.Server.Implementations/Localization/Core/hi.json b/Emby.Server.Implementations/Localization/Core/hi.json
index 80db975ccb..e98a5fbac1 100644
--- a/Emby.Server.Implementations/Localization/Core/hi.json
+++ b/Emby.Server.Implementations/Localization/Core/hi.json
@@ -1,31 +1,20 @@
{
- "Albums": "एल्बम",
- "HeaderRecordingGroups": "रिकॉर्डिंग समूह",
"HeaderNextUp": "इसके बाद",
"HeaderLiveTV": "लाइव टीवी",
- "HeaderFavoriteSongs": "पसंदीदा गीत",
"HeaderFavoriteShows": "पसंदीदा शो",
"HeaderFavoriteEpisodes": "पसंदीदा प्रकरण",
- "HeaderFavoriteArtists": "पसंदीदा कलाकार",
- "HeaderFavoriteAlbums": "पसंदीदा एलबम्स",
"HeaderContinueWatching": "देखना जारी रखें",
- "HeaderAlbumArtists": "एल्बम कलाकार",
"Genres": "शैलियां",
"Forced": "बलपूर्वक",
"Folders": "फ़ोल्डर",
"Favorites": "पसंदीदा",
"FailedLoginAttemptWithUserName": "{0} से संप्रवेश असफल हुआ",
- "DeviceOnlineWithName": "{0} कनेक्ट हो गया है",
- "DeviceOfflineWithName": "{0} डिस्कनेक्ट हो गया है",
"Default": "प्राथमिक",
"Collections": "संग्रह",
"ChapterNameValue": "अध्याय {0}",
- "Channels": "चैनल",
- "CameraImageUploadedFrom": "{0} से एक नया कैमरा छवि अपलोड की गई है",
"Books": "पुस्तकें",
"AuthenticationSucceededWithUserName": "{0} सफलतापूर्वक प्रमाणित किया गया",
"Artists": "कलाकार",
- "Application": "एप्लिकेशन",
"AppDeviceValues": "एप: {0}, उपकरण: {1}",
"NotificationOptionPluginUninstalled": "प्लगइन अनइंस्टाल हो गया",
"NotificationOptionPluginInstalled": "प्लगइन इनस्टॉल हो गया",
@@ -44,13 +33,8 @@
"Music": "संगीत",
"Movies": "फ़िल्म",
"MixedContent": "मिला-जुला कंटेंट",
- "MessageServerConfigurationUpdated": "सर्वर कॉन्फ़िगरेशन अपडेट हो गया है",
- "MessageNamedServerConfigurationUpdatedWithValue": "सर्वर कॉन्फ़िगरेशन भाग {0} अपडेट हो गया है",
- "MessageApplicationUpdatedTo": "जैलीफिन सर्वर {0} में अपडेट हो गया है",
- "MessageApplicationUpdated": "जैलीफिन सर्वर अपडेट हो गया है",
"Latest": "सबसे नया",
"LabelIpAddressValue": "आई पी एड्रेस: {0}",
- "ItemRemovedWithName": "{0} लाइब्रेरी में से निकाल दिया है",
"HomeVideos": "होम चलचित्र",
"NotificationOptionVideoPlayback": "वीडियो प्लेबैक शुरू हुआ",
"NotificationOptionUserLockedOut": "उपयोगकर्ता लॉक हो गया",
@@ -59,22 +43,16 @@
"NotificationOptionPluginUpdateInstalled": "प्लगइन अद्यतन स्थापित",
"NotificationOptionNewLibraryContent": "नई सामग्री जोड़ी गई",
"LabelRunningTimeValue": "चलने का समय: {0}",
- "ItemAddedWithName": "{0} को लाइब्रेरी में जोड़ा गया",
"Inherit": "इनहेरिट",
"NotificationOptionVideoPlaybackStopped": "चलचित्र रुका हुआ",
"PluginUninstalledWithName": "{0} अनइंस्टॉल हुए",
"PluginInstalledWithName": "{0} इंस्टॉल हुए",
- "Plugin": "प्लग-इन",
- "Playlists": "प्लेलिस्ट",
"Photos": "तस्वीरें",
"External": "बाहरी",
"PluginUpdatedWithName": "{0} अपडेट हुए",
- "ScheduledTaskStartedWithName": "{0} शुरू हुए",
- "Songs": "गाने",
"UserStartedPlayingItemWithValues": "{0} {2} पर {1} खेल रहे हैं",
"UserStoppedPlayingItemWithValues": "{0} ने {2} पर {1} खेलना खत्म किया",
"StartupEmbyServerIsLoading": "जेलीफ़िन सर्वर लोड हो रहा है। कृपया शीघ्र ही पुन: प्रयास करें।",
- "ServerNameNeedsToBeRestarted": "{0} रीस्टार्ट करने की आवश्यकता है",
"UserCreatedWithName": "उपयोगकर्ता {0} बनाया गया",
"UserDownloadingItemWithValues": "{0} डाउनलोड हो रहा है",
"UserOfflineFromDevice": "{0} {1} से डिस्कनेक्ट हो गया है",
@@ -83,20 +61,13 @@
"Shows": "शो",
"UserPasswordChangedWithName": "उपयोगकर्ता {0} के लिए पासवर्ड बदल दिया गया है",
"UserDeletedWithName": "उपयोगकर्ता {0} हटा दिया गया",
- "UserPolicyUpdatedWithName": "{0} के लिए उपयोगकर्ता नीति अपडेट कर दी गई है",
- "User": "उपयोगकर्ता",
"SubtitleDownloadFailureFromForItem": "{1} के लिए {0} से उपशीर्षक डाउनलोड करने में विफल",
- "ProviderValue": "प्रदाता: {0}",
"ScheduledTaskFailedWithName": "{0}असफल",
"UserLockedOutWithName": "उपयोगकर्ता {0} को लॉक आउट कर दिया गया है",
- "System": "प्रणाली",
"TvShows": "टीवी शो",
"HearingImpaired": "मूक बधिर",
- "ValueSpecialEpisodeName": "विशेष - {0}",
"TasksMaintenanceCategory": "रखरखाव",
- "Sync": "समाकलयति",
"VersionNumber": "{0} पाठान्तर",
- "ValueHasBeenAddedToLibrary": "{0} आपके माध्यम ग्रन्थालय में उपजात हो गया हैं",
"TasksLibraryCategory": "संग्रहालय",
"TaskOptimizeDatabase": "जानकारी प्रवृद्धि",
"TaskDownloadMissingSubtitles": "लापता अनुलेख डाउनलोड करें",
@@ -127,14 +98,12 @@
"TaskRefreshTrickplayImages": "ट्रिकप्लै चित्रों को सृजन करे",
"TaskRefreshTrickplayImagesDescription": "नियत संग्रहों में चलचित्रों का ट्रीकप्लै दर्शनों को सृजन करे.",
"TaskAudioNormalization": "श्रव्य सामान्यीकरण",
- "TaskAudioNormalizationDescription": "श्रव्य सामान्यीकरण के लिए फाइलें अन्वेषण करें",
+ "TaskAudioNormalizationDescription": "श्रव्य सामान्यीकरण के लिए फाइलें अन्वेषण करें।",
"TaskDownloadMissingLyrics": "लापता गानों के बोल डाउनलोड करेँ",
"TaskDownloadMissingLyricsDescription": "गानों के बोल डाउनलोड करता है",
"TaskExtractMediaSegments": "मीडिया सेगमेंट स्कैन",
"TaskExtractMediaSegmentsDescription": "मीडियासेगमेंट सक्षम प्लगइन्स से मीडिया सेगमेंट निकालता है या प्राप्त करता है।",
"TaskMoveTrickplayImages": "ट्रिकप्ले छवि स्थान माइग्रेट करें",
"TaskMoveTrickplayImagesDescription": "लाइब्रेरी सेटिंग्स के अनुसार मौजूदा ट्रिकप्ले फ़ाइलों को स्थानांतरित करता है।",
- "TaskCleanCollectionsAndPlaylistsDescription": "संग्रहों और प्लेलिस्टों से उन आइटमों को हटाता है जो अब मौजूद नहीं हैं।",
- "TaskCleanCollectionsAndPlaylists": "संग्रह और प्लेलिस्ट साफ़ करें",
- "CleanupUserDataTask": "यूज़र डेटा की सफाई करता है।"
+ "CleanupUserDataTask": "यूज़र डेटा सफाई कार्य"
}
diff --git a/Emby.Server.Implementations/Localization/Core/hr.json b/Emby.Server.Implementations/Localization/Core/hr.json
index 94db435715..8794339fb1 100644
--- a/Emby.Server.Implementations/Localization/Core/hr.json
+++ b/Emby.Server.Implementations/Localization/Core/hr.json
@@ -1,41 +1,24 @@
{
- "Albums": "Albumi",
"AppDeviceValues": "Aplikacija: {0}, Uređaj: {1}",
- "Application": "Aplikacija",
"Artists": "Izvođači",
"AuthenticationSucceededWithUserName": "{0} uspješno ovjerena",
"Books": "Knjige",
- "CameraImageUploadedFrom": "Nova fotografija sa kamere je učitana iz {0}",
- "Channels": "Kanali",
"ChapterNameValue": "Poglavlje {0}",
"Collections": "Zbirke",
- "DeviceOfflineWithName": "{0} je prekinuo vezu",
- "DeviceOnlineWithName": "{0} je povezan",
"FailedLoginAttemptWithUserName": "Neuspješan pokušaj prijave od {0}",
"Favorites": "Favoriti",
"Folders": "Mape",
"Genres": "Žanrovi",
- "HeaderAlbumArtists": "Izvođači albuma",
"HeaderContinueWatching": "Nastavi gledati",
- "HeaderFavoriteAlbums": "Omiljeni albumi",
- "HeaderFavoriteArtists": "Omiljeni izvođači",
"HeaderFavoriteEpisodes": "Omiljene epizode",
"HeaderFavoriteShows": "Omiljene serije",
- "HeaderFavoriteSongs": "Omiljene pjesme",
"HeaderLiveTV": "TV uživo",
- "HeaderNextUp": "Slijedi",
- "HeaderRecordingGroups": "Grupa snimka",
+ "HeaderNextUp": "Sljedeće na redu",
"HomeVideos": "Kućni video",
"Inherit": "Naslijedi",
- "ItemAddedWithName": "{0} je dodano u biblioteku",
- "ItemRemovedWithName": "{0} je uklonjeno iz biblioteke",
"LabelIpAddressValue": "IP adresa: {0}",
"LabelRunningTimeValue": "Vrijeme rada: {0}",
"Latest": "Najnovije",
- "MessageApplicationUpdated": "Jellyfin server je ažuriran",
- "MessageApplicationUpdatedTo": "Jellyfin server je ažuriran na {0}",
- "MessageNamedServerConfigurationUpdatedWithValue": "Dio konfiguracije servera {0} je ažuriran",
- "MessageServerConfigurationUpdated": "Konfiguracija servera je ažurirana",
"MixedContent": "Miješani sadržaj",
"Movies": "Filmovi",
"Music": "Glazba",
@@ -61,23 +44,14 @@
"NotificationOptionVideoPlayback": "Reprodukcija videa započela",
"NotificationOptionVideoPlaybackStopped": "Reprodukcija videa zaustavljena",
"Photos": "Fotografije",
- "Playlists": "Popisi za reprodukciju",
- "Plugin": "Dodatak",
"PluginInstalledWithName": "{0} je instalirano",
"PluginUninstalledWithName": "{0} je deinstalirano",
"PluginUpdatedWithName": "{0} je ažurirano",
- "ProviderValue": "Pružatelj: {0}",
"ScheduledTaskFailedWithName": "{0} neuspjelo",
- "ScheduledTaskStartedWithName": "{0} pokrenuto",
- "ServerNameNeedsToBeRestarted": "{0} treba ponovno pokrenuti",
"Shows": "Emisije",
- "Songs": "Pjesme",
"StartupEmbyServerIsLoading": "Jellyfin server se učitava. Pokušajte ponovo uskoro.",
- "SubtitleDownloadFailureFromForItem": "Prijevod nije uspješno preuzet od {0} za {1}",
- "Sync": "Sinkronizacija",
- "System": "Sustav",
- "TvShows": "Serije",
- "User": "Korisnik",
+ "SubtitleDownloadFailureFromForItem": "Titlovi nisu uspješno preuzeti od {0} za {1}",
+ "TvShows": "TV emisije",
"UserCreatedWithName": "Korisnik {0} je kreiran",
"UserDeletedWithName": "Korisnik {0} je obrisan",
"UserDownloadingItemWithValues": "{0} preuzima {1}",
@@ -85,29 +59,26 @@
"UserOfflineFromDevice": "{0} prekinuo vezu od {1}",
"UserOnlineFromDevice": "{0} povezan od {1}",
"UserPasswordChangedWithName": "Lozinka je promijenjena za korisnika {0}",
- "UserPolicyUpdatedWithName": "Pravila za korisnika ažurirana su za {0}",
"UserStartedPlayingItemWithValues": "{0} je pokrenuo reprodukciju {1} na {2}",
"UserStoppedPlayingItemWithValues": "{0} je završio reprodukciju {1} na {2}",
- "ValueHasBeenAddedToLibrary": "{0} je dodano u medijsku biblioteku",
- "ValueSpecialEpisodeName": "Posebno - {0}",
"VersionNumber": "Verzija {0}",
- "TaskRefreshLibraryDescription": "Skenira medijsku biblioteku radi novih datoteka i osvježava metapodatke.",
- "TaskRefreshLibrary": "Skeniraj medijsku biblioteku",
+ "TaskRefreshLibraryDescription": "Skenira biblioteku medija radi novih datoteka i osvježava metapodatke.",
+ "TaskRefreshLibrary": "Skeniraj biblioteku medija",
"TaskRefreshChapterImagesDescription": "Kreira sličice za videozapise koji imaju poglavlja.",
"TaskRefreshChapterImages": "Izdvoji slike poglavlja",
"TaskCleanCacheDescription": "Briše nepotrebne datoteke iz predmemorije.",
"TaskCleanCache": "Očisti mapu predmemorije",
"TasksApplicationCategory": "Aplikacija",
"TasksMaintenanceCategory": "Održavanje",
- "TaskDownloadMissingSubtitlesDescription": "Pretraži Internet za prijevodima koji nedostaju prema konfiguraciji metapodataka.",
- "TaskDownloadMissingSubtitles": "Preuzmi prijevod koji nedostaje",
+ "TaskDownloadMissingSubtitlesDescription": "Pretraži internet za nedsotajućim titlovima ne osnovi konfiguracije metapodataka.",
+ "TaskDownloadMissingSubtitles": "Preuzmi nedostajuće titlove",
"TaskRefreshChannelsDescription": "Osvježava informacije Internet kanala.",
"TaskRefreshChannels": "Osvježi kanale",
"TaskCleanTranscodeDescription": "Briše transkodirane datoteke starije od jednog dana.",
"TaskCleanTranscode": "Očisti mapu transkodiranja",
"TaskUpdatePluginsDescription": "Preuzima i instalira ažuriranja za dodatke koji su konfigurirani da se ažuriraju automatski.",
"TaskUpdatePlugins": "Ažuriraj dodatke",
- "TaskRefreshPeopleDescription": "Ažurira metapodatke za glumce i redatelje u medijskoj biblioteci.",
+ "TaskRefreshPeopleDescription": "Ažurira metapodatke za glumce i redatelje u biblioteci medija.",
"TaskRefreshPeople": "Osvježi osobe",
"TaskCleanLogsDescription": "Briše zapise dnevnika koji su stariji od {0} dana.",
"TaskCleanLogs": "Očisti mapu dnevnika zapisa",
@@ -119,7 +90,7 @@
"Forced": "Forsirani",
"Default": "Zadano",
"TaskOptimizeDatabase": "Optimiziraj bazu podataka",
- "External": "Vanjski",
+ "External": "Eksterni",
"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",
"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.",
@@ -128,14 +99,13 @@
"TaskRefreshTrickplayImagesDescription": "Stvara preglede brzog pregledavanja za videa u aktiviranim bibliotekama.",
"TaskAudioNormalization": "Normalizacija zvuka",
"TaskAudioNormalizationDescription": "Skenira datoteke u potrazi za podacima o normalizaciji zvuka.",
- "TaskCleanCollectionsAndPlaylistsDescription": "Uklanja stavke iz zbirki i popisa za reprodukciju koje više ne postoje.",
- "TaskCleanCollectionsAndPlaylists": "Očisti zbirke i popise za reprodukciju",
"TaskExtractMediaSegments": "Skeniranje dijelova medija",
"TaskDownloadMissingLyrics": "Preuzmi tekstove koji nedostaju",
"TaskDownloadMissingLyricsDescription": "Preuzmi tekstove pjesama",
"TaskExtractMediaSegmentsDescription": "Izvlači ili pribavlja dijelove medija iz omogućenih media pluginova.",
"TaskMoveTrickplayImages": "Premjesti mjesto slika brzog pregledavanja",
- "TaskMoveTrickplayImagesDescription": "Premješta postojeće datoteke brzog pregledavanja prema postavkama biblioteke.",
+ "TaskMoveTrickplayImagesDescription": "Premješta postojeće datoteke brzog pregledavanja u postavke biblioteke.",
"CleanupUserDataTask": "Zadatak čišćenja korisničkih podataka",
- "CleanupUserDataTaskDescription": "Briše sve korisničke podatke (stanje gledanja, status favorita itd.) s medija koji više nisu prisutni najmanje 90 dana."
+ "CleanupUserDataTaskDescription": "Briše sve korisničke podatke (stanje gledanja, status favorita itd.) s medija koji više nisu prisutni najmanje 90 dana.",
+ "Original": "Original"
}
diff --git a/Emby.Server.Implementations/Localization/Core/ht.json b/Emby.Server.Implementations/Localization/Core/ht.json
index f927d3173a..da90a92339 100644
--- a/Emby.Server.Implementations/Localization/Core/ht.json
+++ b/Emby.Server.Implementations/Localization/Core/ht.json
@@ -1,33 +1,22 @@
{
"Books": "Liv",
"TasksLibraryCategory": "Libreri",
- "Albums": "Albòm yo",
"Artists": "Atis yo",
- "Application": "Aplikasyon",
- "Channels": "Kanal yo",
"ChapterNameValue": "Chapit {0}",
"Default": "Defo",
- "DeviceOnlineWithName": "{0} konekte",
- "DeviceOfflineWithName": "{0} dekonekte",
"External": "Extèn",
"Collections": "Koleksyon yo",
"Favorites": "Pi Renmen",
"Folders": "Dosye",
"Genres": "Jan yo",
"Forced": "Fòse",
- "HeaderAlbumArtists": "Albòm Atis",
"HeaderContinueWatching": "Kontinye Kade",
- "HeaderFavoriteAlbums": "Albòm Pi Renmen",
- "HeaderFavoriteArtists": "Atis Pi Renmen",
"HeaderFavoriteEpisodes": "Epizòd Pi Renmen",
"HeaderFavoriteShows": "Emisyon Pi Renmen",
- "HeaderFavoriteSongs": "Mizik Pi Renmen",
"HeaderLiveTV": "Televizyon an Direk",
"HeaderNextUp": "Pwochen an",
"HomeVideos": "Videyo Lakay",
"Latest": "Pi Resan",
- "MessageApplicationUpdated": "Sèvè Jellyfin met a jou",
- "MessageApplicationUpdatedTo": "Sèvè Jellyfin met a jou sou {0}",
"Movies": "Fim",
"MixedContent": "Kontni Melanje",
"Music": "Mizik",
@@ -42,12 +31,8 @@
"PluginUninstalledWithName": "{0} te dezenstale",
"PluginUpdatedWithName": "{0} te mi a jou",
"ScheduledTaskFailedWithName": "{0} echwe",
- "ScheduledTaskStartedWithName": "{0} komanse",
- "Songs": "Mizik yo",
"Shows": "Emisyon yo",
- "System": "Sistèm",
"TvShows": "Emisyon Tele",
- "User": "Itilizatè",
"UserCreatedWithName": "Itilizatè {0} kreye",
"UserDeletedWithName": "Itilizatè {0} a efase",
"UserDownloadingItemWithValues": "{0} ap telechaje {1}",
@@ -55,8 +40,10 @@
"UserStartedPlayingItemWithValues": "{0} ap jwe {1} sou {2}",
"UserStoppedPlayingItemWithValues": "{0} fin jwe {1} sou {2}",
"UserPasswordChangedWithName": "Modpas la chanje pou Itilizatè {0}",
- "ValueSpecialEpisodeName": "Spesyal - {0}",
"VersionNumber": "Vesyon {0}",
"TasksApplicationCategory": "Aplikasyon",
- "TasksMaintenanceCategory": "Antretyen"
+ "TasksMaintenanceCategory": "Antretyen",
+ "AppDeviceValues": "Aplikasyon: {0}, Aparèy: {1}",
+ "AuthenticationSucceededWithUserName": "{0} otantifye avèk siksè",
+ "Original": "Original"
}
diff --git a/Emby.Server.Implementations/Localization/Core/hu.json b/Emby.Server.Implementations/Localization/Core/hu.json
index 7d72c1f304..1995d7a4cf 100644
--- a/Emby.Server.Implementations/Localization/Core/hu.json
+++ b/Emby.Server.Implementations/Localization/Core/hu.json
@@ -1,45 +1,28 @@
{
- "Albums": "Albumok",
- "AppDeviceValues": "Program: {0}, Eszköz: {1}",
- "Application": "Alkalmazás",
+ "AppDeviceValues": "alkalmazás: {0}, eszköz: {1}",
"Artists": "Előadók",
"AuthenticationSucceededWithUserName": "{0} sikeresen hitelesítve",
"Books": "Könyvek",
- "CameraImageUploadedFrom": "Új kamerakép lett feltöltve innen: {0}",
- "Channels": "Csatornák",
"ChapterNameValue": "{0}. jelenet",
"Collections": "Gyűjtemények",
- "DeviceOfflineWithName": "{0} kijelentkezett",
- "DeviceOnlineWithName": "{0} belépett",
"FailedLoginAttemptWithUserName": "Sikertelen bejelentkezési kísérlet innen: {0}",
"Favorites": "Kedvencek",
"Folders": "Mappák",
"Genres": "Műfajok",
- "HeaderAlbumArtists": "Albumelőadók",
"HeaderContinueWatching": "Megtekintés folytatása",
- "HeaderFavoriteAlbums": "Kedvenc albumok",
- "HeaderFavoriteArtists": "Kedvenc előadók",
"HeaderFavoriteEpisodes": "Kedvenc epizódok",
"HeaderFavoriteShows": "Kedvenc sorozatok",
- "HeaderFavoriteSongs": "Kedvenc számok",
"HeaderLiveTV": "Élő TV",
"HeaderNextUp": "Következik",
- "HeaderRecordingGroups": "Felvételi csoportok",
"HomeVideos": "Otthoni videók",
"Inherit": "Öröklés",
- "ItemAddedWithName": "{0} hozzáadva a médiatárhoz",
- "ItemRemovedWithName": "{0} eltávolítva a médiatárból",
"LabelIpAddressValue": "IP-cím: {0}",
"LabelRunningTimeValue": "Lejátszási idő: {0}",
"Latest": "Legújabb",
- "MessageApplicationUpdated": "A Jellyfin kiszolgáló frissítve lett",
- "MessageApplicationUpdatedTo": "A Jellyfin kiszolgáló frissítve lett a következőre: {0}",
- "MessageNamedServerConfigurationUpdatedWithValue": "A kiszolgálókonfigurációs rész frissítve lett: {0}",
- "MessageServerConfigurationUpdated": "A kiszolgálókonfiguráció frissítve lett",
"MixedContent": "Vegyes tartalom",
"Movies": "Filmek",
"Music": "Zenék",
- "MusicVideos": "Zenei videóklipek",
+ "MusicVideos": "Zenei videók",
"NameInstallFailed": "{0} sikertelen telepítés",
"NameSeasonNumber": "{0}. évad",
"NameSeasonUnknown": "Ismeretlen évad",
@@ -61,23 +44,14 @@
"NotificationOptionVideoPlayback": "Videólejátszás elkezdve",
"NotificationOptionVideoPlaybackStopped": "Videólejátszás leállítva",
"Photos": "Fényképek",
- "Playlists": "Lejátszási listák",
- "Plugin": "Bővítmény",
"PluginInstalledWithName": "{0} telepítve",
"PluginUninstalledWithName": "{0} eltávolítva",
"PluginUpdatedWithName": "{0} frissítve",
- "ProviderValue": "Szolgáltató: {0}",
"ScheduledTaskFailedWithName": "{0} sikertelen",
- "ScheduledTaskStartedWithName": "{0} elkezdve",
- "ServerNameNeedsToBeRestarted": "A(z) {0} újraindítása szükséges",
"Shows": "Sorozatok",
- "Songs": "Számok",
"StartupEmbyServerIsLoading": "A Jellyfin kiszolgáló betöltődik. Próbálja újra hamarosan.",
"SubtitleDownloadFailureFromForItem": "Nem sikerült a felirat letöltése innen: {0}, ehhez: {1}",
- "Sync": "Szinkronizálás",
- "System": "Rendszer",
"TvShows": "TV műsorok",
- "User": "Felhasználó",
"UserCreatedWithName": "{0} felhasználó létrehozva",
"UserDeletedWithName": "{0} felhasználó törölve",
"UserDownloadingItemWithValues": "{0} letölti: {1}",
@@ -85,11 +59,8 @@
"UserOfflineFromDevice": "{0} kijelentkezett innen: {1}",
"UserOnlineFromDevice": "{0} online innen: {1}",
"UserPasswordChangedWithName": "{0} jelszava megváltozott",
- "UserPolicyUpdatedWithName": "{0} felhasználói házirendje frissült",
"UserStartedPlayingItemWithValues": "{0} elkezdte lejátszani a következőt: {1}, itt: {2}",
"UserStoppedPlayingItemWithValues": "{0} befejezte a következő lejátszását: {1}, itt: {2}",
- "ValueHasBeenAddedToLibrary": "{0} hozzáadva a médiatárhoz",
- "ValueSpecialEpisodeName": "Különkiadás – {0}",
"VersionNumber": "Verzió: {0}",
"TaskCleanTranscode": "Átkódolási könyvtár ürítése",
"TaskUpdatePluginsDescription": "Letölti és telepíti a frissítéseket azokhoz a bővítményekhez, amelyeknél az automatikus frissítés engedélyezve van.",
@@ -127,9 +98,7 @@
"TaskRefreshTrickplayImages": "Trickplay képek előállítása",
"TaskRefreshTrickplayImagesDescription": "Trickplay előnézetet készít az engedélyezett könyvtárakban lévő videókhoz.",
"TaskAudioNormalization": "Hangerő-normalizálás",
- "TaskCleanCollectionsAndPlaylistsDescription": "Nem létező elemek törlése a gyűjteményekből és lejátszási listákról.",
"TaskAudioNormalizationDescription": "Hangerő-normalizálási adatok keresése.",
- "TaskCleanCollectionsAndPlaylists": "Gyűjtemények és lejátszási listák optimalizálása",
"TaskExtractMediaSegments": "Médiaszegmens felismerése",
"TaskDownloadMissingLyrics": "Hiányzó szöveg letöltése",
"TaskDownloadMissingLyricsDescription": "Zenék szövegének letöltése",
@@ -137,5 +106,7 @@
"TaskMoveTrickplayImagesDescription": "A médiatár-beállításoknak megfelelően áthelyezi a meglévő trickplay fájlokat.",
"TaskExtractMediaSegmentsDescription": "Kinyeri vagy megszerzi a médiaszegmenseket a MediaSegment támogatással rendelkező bővítményekből.",
"CleanupUserDataTaskDescription": "Legalább 90 napja nem elérhető médiákhoz kapcsolódó összes felhasználói adat (pl. megtekintési állapot, kedvencek) törlése.",
- "CleanupUserDataTask": "Felhasználói adatok tisztítása feladat"
+ "CleanupUserDataTask": "Felhasználói adatok tisztítása feladat",
+ "Original": "Eredeti",
+ "LyricDownloadFailureFromForItem": "Dalszöveg letöltése {0}-tól {1}-hez sikertelen"
}
diff --git a/Emby.Server.Implementations/Localization/Core/hy.json b/Emby.Server.Implementations/Localization/Core/hy.json
index 563f842923..b79b540bf4 100644
--- a/Emby.Server.Implementations/Localization/Core/hy.json
+++ b/Emby.Server.Implementations/Localization/Core/hy.json
@@ -2,38 +2,27 @@
"TasksLibraryCategory": "Գրադարան",
"TasksApplicationCategory": "Հավելված",
"TaskCleanActivityLog": "Մաքրել ակտիվության մատյանը",
- "Application": "Հավելված",
"AuthenticationSucceededWithUserName": "{0} հաջողությամբ վավերականացվել են",
"Books": "Գրքեր",
- "CameraImageUploadedFrom": "Նոր լուսանկար է վերբեռնվել {0}-ի կողմից",
- "Channels": "Ալիքներ",
- "DeviceOfflineWithName": "{0}ը անջատվեց",
"External": "Արտաքին",
"FailedLoginAttemptWithUserName": "Ձախողված մուտքի փործ {0}-ի կողմից",
"Folders": "Պանակներ",
"HeaderContinueWatching": "Շարունակել դիտումը",
"Inherit": "Ժառանգել",
- "ItemAddedWithName": "{0}ը ավացված է գրադարանի մեջ",
- "ItemRemovedWithName": "{0}ը հեռացված է գրադարանից",
"LabelIpAddressValue": "IP հասցե` {0}",
"Movies": "Ֆիլմեր",
"Music": "Երաժշտություն",
"NameSeasonNumber": "Սեզոն {0}",
"Photos": "Լուսանկարներ",
"PluginInstalledWithName": "{0}ն տեղադրված է",
- "Songs": "Երգեր",
- "System": "Համակարգ",
"TvShows": "Հեռուստասերիալներ",
- "User": "Օգտատեր",
"VersionNumber": "Տարբերակ {0}",
"TasksMaintenanceCategory": "Սպասարկում",
"TasksChannelsCategory": "Ինտերնետային ալիքներ",
"TaskRefreshPeople": "Թարմացնել մարդկանց",
"TaskRefreshChannels": "Թարմացնել ալիքները",
"TaskDownloadMissingSubtitles": "Ներբեռնել պակասող ենթագրերը",
- "Albums": "Ալբոմներ",
"AppDeviceValues": "Հավելված` {0}, Սարք `{1}",
"ChapterNameValue": "Գլուխ {0}",
- "Collections": "Հավաքածուներ",
- "DeviceOnlineWithName": "{0}-ն միացված է"
+ "Collections": "Հավաքածուներ"
}
diff --git a/Emby.Server.Implementations/Localization/Core/id.json b/Emby.Server.Implementations/Localization/Core/id.json
index 2a42816852..3502ec39ad 100644
--- a/Emby.Server.Implementations/Localization/Core/id.json
+++ b/Emby.Server.Implementations/Localization/Core/id.json
@@ -1,48 +1,31 @@
{
- "Albums": "Album",
"AuthenticationSucceededWithUserName": "{0} berhasil diautentikasi",
"AppDeviceValues": "Aplikasi : {0}, Perangkat : {1}",
"LabelRunningTimeValue": "Waktu berjalan: {0}",
- "MessageApplicationUpdatedTo": "Jellyfin Server sudah diperbarui ke {0}",
- "MessageApplicationUpdated": "Jellyfin Server sudah diperbarui",
"Latest": "Terbaru",
"LabelIpAddressValue": "Alamat IP: {0}",
- "ItemRemovedWithName": "{0} sudah dihapus dari pustaka",
- "ItemAddedWithName": "{0} telah dimasukkan ke dalam pustaka",
"Inherit": "Warisi",
"HomeVideos": "Video Rumahan",
- "HeaderRecordingGroups": "Grup Rekaman",
"HeaderNextUp": "Selanjutnya",
"HeaderLiveTV": "Siaran langsung",
- "HeaderFavoriteSongs": "Lagu Favorit",
"HeaderFavoriteShows": "Tayangan Favorit",
"HeaderFavoriteEpisodes": "Episode Favorit",
- "HeaderFavoriteArtists": "Artis Favorit",
- "HeaderFavoriteAlbums": "Album Favorit",
"HeaderContinueWatching": "Lanjut Menonton",
- "HeaderAlbumArtists": "Album Artis",
"Genres": "Aliran",
"Folders": "Folder",
"Favorites": "Favorit",
"Collections": "Koleksi",
"Books": "Buku",
"Artists": "Artis",
- "Application": "Aplikasi",
"ChapterNameValue": "Bagian {0}",
- "Channels": "Saluran",
"TvShows": "Seri TV",
"SubtitleDownloadFailureFromForItem": "Subtitel gagal diunduh dari {0} untuk {1}",
"StartupEmbyServerIsLoading": "Server Jellyfin sedang dimuat. Silakan coba lagi nanti.",
- "Songs": "Lagu",
- "Playlists": "Daftar putar",
"NotificationOptionPluginUninstalled": "Plugin dihapus",
"MusicVideos": "Video Musik",
"VersionNumber": "Versi {0}",
- "ValueSpecialEpisodeName": "Spesial - {0}",
- "ValueHasBeenAddedToLibrary": "{0} telah ditambahkan ke pustaka media Anda",
"UserStoppedPlayingItemWithValues": "{0} telah selesai memutar {1} pada {2}",
"UserStartedPlayingItemWithValues": "{0} sedang memutar {1} pada {2}",
- "UserPolicyUpdatedWithName": "Kebijakan pengguna telah diperbarui untuk {0}",
"UserPasswordChangedWithName": "Kata sandi telah diubah untuk pengguna {0}",
"UserOnlineFromDevice": "{0} sedang daring dari {1}",
"UserOfflineFromDevice": "{0} telah terputus dari {1}",
@@ -50,17 +33,10 @@
"UserDownloadingItemWithValues": "{0} sedang mengunduh {1}",
"UserDeletedWithName": "Pengguna {0} telah dihapus",
"UserCreatedWithName": "Pengguna {0} telah dibuat",
- "User": "Pengguna",
- "System": "Sistem",
- "Sync": "Sinkron",
"Shows": "Tayangan",
- "ServerNameNeedsToBeRestarted": "{0} perlu dimuat ulang",
- "ScheduledTaskStartedWithName": "{0} dimulai",
"ScheduledTaskFailedWithName": "{0} gagal",
- "ProviderValue": "Penyedia: {0}",
"PluginUpdatedWithName": "{0} telah diperbarui",
"PluginInstalledWithName": "{0} telah dipasang",
- "Plugin": "Plugin",
"Photos": "Foto",
"NotificationOptionUserLockedOut": "Pengguna terkunci",
"NotificationOptionTaskFailed": "Kegagalan tugas terjadwal",
@@ -79,12 +55,7 @@
"NameInstallFailed": "{0} penginstalan gagal",
"Music": "Musik",
"Movies": "Film",
- "MessageServerConfigurationUpdated": "Konfigurasi server telah diperbarui",
- "MessageNamedServerConfigurationUpdatedWithValue": "Bagian konfigurasi server {0} telah diperbarui",
"FailedLoginAttemptWithUserName": "Gagal upaya login dari {0}",
- "CameraImageUploadedFrom": "Sebuah gambar kamera baru telah diunggah dari {0}",
- "DeviceOfflineWithName": "{0} telah terputus",
- "DeviceOnlineWithName": "{0} telah terhubung",
"NotificationOptionVideoPlaybackStopped": "Pemutaran video berhenti",
"NotificationOptionVideoPlayback": "Pemutaran video dimulai",
"NotificationOptionAudioPlaybackStopped": "Pemutaran audio berhenti",
@@ -128,8 +99,6 @@
"TaskRefreshTrickplayImagesDescription": "Buat pratinjau trickplay untuk video di perpustakaan yang diaktifkan.",
"TaskAudioNormalizationDescription": "Pindai file untuk data normalisasi audio.",
"TaskAudioNormalization": "Normalisasi Audio",
- "TaskCleanCollectionsAndPlaylists": "Bersihkan koleksi dan daftar putar",
- "TaskCleanCollectionsAndPlaylistsDescription": "Menghapus item dari koleksi dan daftar putar yang sudah tidak ada.",
"TaskDownloadMissingLyricsDescription": "Unduh lirik untuk lagu",
"TaskExtractMediaSegmentsDescription": "Mengekstrak atau memperoleh segmen media dari plugin yang mendukung MediaSegment.",
"TaskMoveTrickplayImagesDescription": "Memindahkan file trickplay yang sudah ada sesuai dengan pengaturan pustaka.",
@@ -137,5 +106,7 @@
"TaskExtractMediaSegments": "Scan Segmen media",
"TaskMoveTrickplayImages": "Migrasikan Lokasi Gambar Trickplay",
"TaskDownloadMissingLyrics": "Unduh Lirik yang Hilang",
- "CleanupUserDataTask": "Tugas Pembersihan Data Pengguna"
+ "CleanupUserDataTask": "Tugas Pembersihan Data Pengguna",
+ "LyricDownloadFailureFromForItem": "Lirik gagal di download dari {0} untuk {1}",
+ "Original": "Asli"
}
diff --git a/Emby.Server.Implementations/Localization/Core/is.json b/Emby.Server.Implementations/Localization/Core/is.json
index 6f94df9d79..44e057e4de 100644
--- a/Emby.Server.Implementations/Localization/Core/is.json
+++ b/Emby.Server.Implementations/Localization/Core/is.json
@@ -1,36 +1,22 @@
{
"LabelIpAddressValue": "IP tala: {0}",
- "ItemRemovedWithName": "{0} var fjarlægt úr safninu",
- "ItemAddedWithName": "{0} var bætt í safnið",
"Inherit": "Erfa",
"HomeVideos": "Heimamyndbönd",
- "HeaderRecordingGroups": "Upptökuhópar",
"HeaderNextUp": "Næst á dagskrá",
"HeaderLiveTV": "Sjónvarp í beinni útsendingu",
- "HeaderFavoriteSongs": "Uppáhalds Lög",
"HeaderFavoriteShows": "Uppáhalds Sjónvarpsþættir",
"HeaderFavoriteEpisodes": "Uppáhalds Þættir",
- "HeaderFavoriteArtists": "Uppáhalds Listamenn",
- "HeaderFavoriteAlbums": "Uppáhalds Plötur",
"HeaderContinueWatching": "Halda áfram að horfa",
- "HeaderAlbumArtists": "Listamaður á umslagi",
"Genres": "Stefnur",
"Folders": "Möppur",
"Favorites": "Uppáhalds",
"FailedLoginAttemptWithUserName": "{0} mistókst að auðkenna sig",
- "DeviceOnlineWithName": "{0} hefur tengst",
- "DeviceOfflineWithName": "{0} hefur aftengst",
"Collections": "Söfn",
"ChapterNameValue": "Kafli {0}",
- "Channels": "Rásir",
- "CameraImageUploadedFrom": "{0} hefur hlaðið upp nýrri ljósmynd úr myndavél sinni",
"Books": "Bækur",
"AuthenticationSucceededWithUserName": "Auðkenning fyrir {0} tókst",
"Artists": "Listamenn",
- "Application": "Forrit",
"AppDeviceValues": "Snjallforrit: {0}, Tæki: {1}",
- "Albums": "Plötur",
- "Plugin": "Viðbótarvirkni",
"Photos": "Ljósmyndir",
"NotificationOptionVideoPlaybackStopped": "Myndbandsafspilun stöðvuð",
"NotificationOptionVideoPlayback": "Myndbandsafspilun hafin",
@@ -49,13 +35,8 @@
"NameSeasonUnknown": "Þáttaröð óþekkt",
"NameSeasonNumber": "Þáttaröð {0}",
"MixedContent": "Blandað efni",
- "MessageServerConfigurationUpdated": "Stillingar þjóns hafa verið uppfærðar",
- "MessageApplicationUpdatedTo": "Jellyfin þjónn hefur verið uppfærður í {0}",
- "MessageApplicationUpdated": "Jellyfin þjónn hefur verið uppfærður",
"Latest": "Nýjasta",
"LabelRunningTimeValue": "spilunartími: {0}",
- "User": "Notandi",
- "System": "Kerfi",
"NotificationOptionNewLibraryContent": "Nýju efni bætt við",
"NewVersionIsAvailable": "Ný útgáfa af Jellyfin þjón er tilbúin til niðurhals.",
"NameInstallFailed": "{0} uppsetning mistókst",
@@ -65,10 +46,6 @@
"UserDeletedWithName": "Notanda {0} hefur verið eytt",
"UserCreatedWithName": "Notandi {0} hefur verið stofnaður",
"TvShows": "Sjónvarpsþættir",
- "Sync": "Samstilla",
- "Songs": "Lög",
- "ServerNameNeedsToBeRestarted": "{0} þarf að vera endurræstur",
- "ScheduledTaskStartedWithName": "{0} hafin",
"ScheduledTaskFailedWithName": "{0} mistókst",
"PluginUpdatedWithName": "{0} var uppfært",
"PluginUninstalledWithName": "{0} var fjarlægt",
@@ -76,21 +53,15 @@
"NotificationOptionTaskFailed": "Tímasett verkefni mistókst",
"StartupEmbyServerIsLoading": "Jellyfin netþjónnin er að ræsa sig upp. Vinsamlegast reyndu aftur fljótlega.",
"VersionNumber": "Útgáfa {0}",
- "ValueHasBeenAddedToLibrary": "{0} hefur verið bætt við í gagnasafnið þitt",
"UserStoppedPlayingItemWithValues": "{0} hefur lokið spilunar af {1} á {2}",
"UserStartedPlayingItemWithValues": "{0} er að spila {1} á {2}",
- "UserPolicyUpdatedWithName": "Notandaregla hefur verið uppfærð fyrir {0}",
"UserPasswordChangedWithName": "Lykilorði fyrir notandann {0} hefur verið breytt",
"UserOnlineFromDevice": "{0} hefur verið virkur síðan {1}",
"UserOfflineFromDevice": "{0} hefur aftengst frá {1}",
"UserLockedOutWithName": "Notandi {0} hefur verið læstur úti",
"UserDownloadingItemWithValues": "{0} hleður niður {1}",
"SubtitleDownloadFailureFromForItem": "Tókst ekki að hala niður skjátextum frá {0} til {1}",
- "ProviderValue": "Efnisveita: {0}",
- "MessageNamedServerConfigurationUpdatedWithValue": "Stilling {0} hefur verið uppfærð á netþjón",
- "ValueSpecialEpisodeName": "Sérstaktur - {0}",
"Shows": "Þættir",
- "Playlists": "Efnisskrár",
"TaskRefreshChannelsDescription": "Endurhlaða upplýsingum netrása.",
"TaskRefreshChannels": "Endurhlaða Rásir",
"TaskCleanTranscodeDescription": "Eyða umkóðuðum skrám sem eru meira en einum degi eldri.",
@@ -128,11 +99,12 @@
"TaskRefreshTrickplayImages": "Búa til hraðspilunarmyndir",
"TaskAudioNormalization": "Hljóðstöðlun",
"TaskAudioNormalizationDescription": "Leitar að hljóðstöðlunargögnum í skrám.",
- "TaskCleanCollectionsAndPlaylists": "Hreinsa söfn og spilunarlista",
- "TaskCleanCollectionsAndPlaylistsDescription": "Fjarlægir hluti úr söfnum og spilalistum sem eru ekki lengur til.",
"TaskDownloadMissingLyricsDescription": "Sækja söngtexta fyrir lög",
"TaskDownloadMissingLyrics": "Sækja söngtexta sem vantar",
"TaskExtractMediaSegments": "Skönnun efnishluta",
"CleanupUserDataTask": "Hreinsun notendagagna",
- "CleanupUserDataTaskDescription": "Hreinsar öll notendagögn (spilunarstöðu, uppáhöld o.s.frv.) um gögn sem hafa ekki verið til staðar í að lámarki 90 daga."
+ "CleanupUserDataTaskDescription": "Hreinsar öll notendagögn (spilunarstöðu, uppáhöld o.s.frv.) um gögn sem hafa ekki verið til staðar í að lámarki 90 daga.",
+ "LyricDownloadFailureFromForItem": "Ekki tókst að niðurhala texta frá {0} fyrir {1}",
+ "Original": "Upprunaleg",
+ "TaskExtractMediaSegmentsDescription": "Sækir myndbúta úr viðbótum þar sem MediaSegment er virkt."
}
diff --git a/Emby.Server.Implementations/Localization/Core/it.json b/Emby.Server.Implementations/Localization/Core/it.json
index f0c4b50270..f13944e6be 100644
--- a/Emby.Server.Implementations/Localization/Core/it.json
+++ b/Emby.Server.Implementations/Localization/Core/it.json
@@ -1,41 +1,24 @@
{
- "Albums": "Album",
"AppDeviceValues": "App: {0}, Dispositivo: {1}",
- "Application": "Applicazione",
"Artists": "Artisti",
"AuthenticationSucceededWithUserName": "{0} autenticato correttamente",
"Books": "Libri",
- "CameraImageUploadedFrom": "È stata caricata una nuova fotografia da {0}",
- "Channels": "Canali",
"ChapterNameValue": "Capitolo {0}",
"Collections": "Collezioni",
- "DeviceOfflineWithName": "{0} si è disconnesso",
- "DeviceOnlineWithName": "{0} è connesso",
"FailedLoginAttemptWithUserName": "Tentativo di accesso non riuscito da {0}",
"Favorites": "Preferiti",
"Folders": "Cartelle",
"Genres": "Generi",
- "HeaderAlbumArtists": "Artisti dell'album",
"HeaderContinueWatching": "Continua a guardare",
- "HeaderFavoriteAlbums": "Album preferiti",
- "HeaderFavoriteArtists": "Artisti preferiti",
"HeaderFavoriteEpisodes": "Episodi preferiti",
"HeaderFavoriteShows": "Serie TV preferite",
- "HeaderFavoriteSongs": "Brani preferiti",
"HeaderLiveTV": "Diretta TV",
"HeaderNextUp": "Prossimo",
- "HeaderRecordingGroups": "Gruppi di registrazione",
"HomeVideos": "Video personali",
"Inherit": "Eredita",
- "ItemAddedWithName": "{0} è stato aggiunto alla libreria",
- "ItemRemovedWithName": "{0} è stato rimosso dalla libreria",
"LabelIpAddressValue": "Indirizzo IP: {0}",
"LabelRunningTimeValue": "Durata: {0}",
"Latest": "Novità",
- "MessageApplicationUpdated": "Jellyfin Server è stato aggiornato",
- "MessageApplicationUpdatedTo": "Jellyfin Server è stato aggiornato a {0}",
- "MessageNamedServerConfigurationUpdatedWithValue": "La sezione {0} della configurazione server è stata aggiornata",
- "MessageServerConfigurationUpdated": "La configurazione del server è stata aggiornata",
"MixedContent": "Contenuto misto",
"Movies": "Film",
"Music": "Musica",
@@ -61,23 +44,14 @@
"NotificationOptionVideoPlayback": "Riproduzione video iniziata",
"NotificationOptionVideoPlaybackStopped": "Riproduzione video interrotta",
"Photos": "Foto",
- "Playlists": "Scalette",
- "Plugin": "Plugin",
"PluginInstalledWithName": "{0} è stato installato",
"PluginUninstalledWithName": "{0} è stato disinstallato",
"PluginUpdatedWithName": "{0} è stato aggiornato",
- "ProviderValue": "Provider: {0}",
"ScheduledTaskFailedWithName": "{0} non riuscito",
- "ScheduledTaskStartedWithName": "{0} avviato",
- "ServerNameNeedsToBeRestarted": "{0} deve essere riavviato",
"Shows": "Serie TV",
- "Songs": "Brani",
"StartupEmbyServerIsLoading": "Jellyfin Server si sta avviando. Riprova più tardi.",
"SubtitleDownloadFailureFromForItem": "Impossibile scaricare i sottotitoli da {0} per {1}",
- "Sync": "Sincronizza",
- "System": "Sistema",
"TvShows": "Serie TV",
- "User": "Utente",
"UserCreatedWithName": "L'utente {0} è stato creato",
"UserDeletedWithName": "L'utente {0} è stato eliminato",
"UserDownloadingItemWithValues": "{0} sta scaricando {1}",
@@ -85,11 +59,8 @@
"UserOfflineFromDevice": "{0} si è disconnesso da {1}",
"UserOnlineFromDevice": "{0} è online su {1}",
"UserPasswordChangedWithName": "La password è stata cambiata per l'utente {0}",
- "UserPolicyUpdatedWithName": "La policy dell'utente è stata aggiornata per {0}",
"UserStartedPlayingItemWithValues": "{0} ha avviato la riproduzione di {1} su {2}",
"UserStoppedPlayingItemWithValues": "{0} ha interrotto la riproduzione di {1} su {2}",
- "ValueHasBeenAddedToLibrary": "{0} è stato aggiunto alla tua libreria multimediale",
- "ValueSpecialEpisodeName": "Speciale - {0}",
"VersionNumber": "Versione {0}",
"TaskRefreshChannelsDescription": "Aggiorna le informazioni dei canali internet.",
"TaskDownloadMissingSubtitlesDescription": "Cerca su internet i sottotitoli mancanti basandosi sulle configurazioni dei metadati.",
@@ -126,8 +97,6 @@
"HearingImpaired": "Non udenti",
"TaskRefreshTrickplayImages": "Genera immagini Trickplay",
"TaskRefreshTrickplayImagesDescription": "Crea anteprime trickplay per i video nelle librerie abilitate.",
- "TaskCleanCollectionsAndPlaylists": "Ripulisci le collezioni e le scalette",
- "TaskCleanCollectionsAndPlaylistsDescription": "Rimuove gli elementi dalle collezioni e dalle scalette che non esistono più.",
"TaskAudioNormalization": "Normalizzazione dell'audio",
"TaskAudioNormalizationDescription": "Scansiona i file alla ricerca dei dati per la normalizzazione dell'audio.",
"TaskDownloadMissingLyricsDescription": "Scarica testi per le canzoni",
@@ -137,5 +106,7 @@
"TaskExtractMediaSegmentsDescription": "Estrae o ottiene segmenti multimediali dai plugin abilitati MediaSegment.",
"TaskExtractMediaSegments": "Scansiona Segmento Media",
"CleanupUserDataTask": "Task di pulizia dei dati utente",
- "CleanupUserDataTaskDescription": "Pulisce tutti i dati utente (stato di visione, status preferiti, ecc.) dai contenuti non più presenti da almeno 90 giorni."
+ "CleanupUserDataTaskDescription": "Pulisce tutti i dati utente (stato di visione, status preferiti, ecc.) dai contenuti non più presenti da almeno 90 giorni.",
+ "Original": "Originale",
+ "LyricDownloadFailureFromForItem": "Scaricamento dei testi non riuscito da {0} per {1}"
}
diff --git a/Emby.Server.Implementations/Localization/Core/ja.json b/Emby.Server.Implementations/Localization/Core/ja.json
index bdca8ae1cd..78b7ec744b 100644
--- a/Emby.Server.Implementations/Localization/Core/ja.json
+++ b/Emby.Server.Implementations/Localization/Core/ja.json
@@ -1,41 +1,24 @@
{
- "Albums": "アルバム",
"AppDeviceValues": "アプリ: {0}, デバイス: {1}",
- "Application": "アプリケーション",
"Artists": "アーティスト",
"AuthenticationSucceededWithUserName": "{0} 認証に成功しました",
"Books": "ブック",
- "CameraImageUploadedFrom": "新しいカメライメージが {0}からアップロードされました",
- "Channels": "チャンネル",
"ChapterNameValue": "チャプター {0}",
"Collections": "コレクション",
- "DeviceOfflineWithName": "{0} が切断しました",
- "DeviceOnlineWithName": "{0} が接続しました",
"FailedLoginAttemptWithUserName": "{0} からのログインに失敗しました",
"Favorites": "お気に入り",
"Folders": "フォルダー",
"Genres": "ジャンル",
- "HeaderAlbumArtists": "アルバムアーティスト",
"HeaderContinueWatching": "再生を続ける",
- "HeaderFavoriteAlbums": "お気に入りのアルバム",
- "HeaderFavoriteArtists": "お気に入りのアーティスト",
"HeaderFavoriteEpisodes": "お気に入りのエピソード",
"HeaderFavoriteShows": "お気に入りの番組",
- "HeaderFavoriteSongs": "お気に入りの曲",
"HeaderLiveTV": "ライブTV",
"HeaderNextUp": "次",
- "HeaderRecordingGroups": "レコーディンググループ",
"HomeVideos": "ホームビデオ",
"Inherit": "継承",
- "ItemAddedWithName": "{0} をライブラリーに追加しました",
- "ItemRemovedWithName": "{0} をライブラリーから削除しました",
"LabelIpAddressValue": "IPアドレス: {0}",
"LabelRunningTimeValue": "時間: {0}",
"Latest": "最新",
- "MessageApplicationUpdated": "Jellyfin Server を更新しました",
- "MessageApplicationUpdatedTo": "Jellyfin Server を {0}に更新しました",
- "MessageNamedServerConfigurationUpdatedWithValue": "サーバー設定項目の {0} を更新しました",
- "MessageServerConfigurationUpdated": "サーバー設定を更新しました",
"MixedContent": "ミックスコンテンツ",
"Movies": "映画",
"Music": "音楽",
@@ -61,23 +44,14 @@
"NotificationOptionVideoPlayback": "ビデオの再生を開始",
"NotificationOptionVideoPlaybackStopped": "ビデオの再生を停止",
"Photos": "フォト",
- "Playlists": "プレイリスト",
- "Plugin": "プラグイン",
"PluginInstalledWithName": "{0} をインストールしました",
"PluginUninstalledWithName": "{0} をアンインストールしました",
"PluginUpdatedWithName": "{0} を更新しました",
- "ProviderValue": "プロバイダ: {0}",
"ScheduledTaskFailedWithName": "{0} が失敗しました",
- "ScheduledTaskStartedWithName": "{0} を開始",
- "ServerNameNeedsToBeRestarted": "{0} を再起動してください",
"Shows": "番組",
- "Songs": "曲",
"StartupEmbyServerIsLoading": "Jellyfin Server は現在読み込み中です。しばらくしてからもう一度お試しください。",
"SubtitleDownloadFailureFromForItem": "{0} から {1}の字幕のダウンロードに失敗しました",
- "Sync": "同期",
- "System": "システム",
"TvShows": "テレビ番組",
- "User": "ユーザー",
"UserCreatedWithName": "ユーザー {0} が作成されました",
"UserDeletedWithName": "User {0} を削除しました",
"UserDownloadingItemWithValues": "{0} が {1} をダウンロードしています",
@@ -85,11 +59,8 @@
"UserOfflineFromDevice": "{0} は {1} から切断しました",
"UserOnlineFromDevice": "{0} は {1} からオンラインになりました",
"UserPasswordChangedWithName": "ユーザー {0} のパスワードは変更されました",
- "UserPolicyUpdatedWithName": "ユーザーポリシーが{0}に更新されました",
"UserStartedPlayingItemWithValues": "{0} は {2}で{1} を再生しています",
"UserStoppedPlayingItemWithValues": "{0} は{2}で{1} の再生が終わりました",
- "ValueHasBeenAddedToLibrary": "{0} をメディアライブラリーに追加しました",
- "ValueSpecialEpisodeName": "スペシャル - {0}",
"VersionNumber": "バージョン {0}",
"TaskCleanLogsDescription": "{0} 日以上前のログを消去します。",
"TaskCleanLogs": "ログの掃除",
@@ -126,10 +97,8 @@
"HearingImpaired": "聴覚障害の方",
"TaskRefreshTrickplayImages": "トリックプレー画像を生成",
"TaskRefreshTrickplayImagesDescription": "有効なライブラリ内のビデオをもとにトリックプレーのプレビューを生成します。",
- "TaskCleanCollectionsAndPlaylists": "コレクションとプレイリストをクリーンアップ",
"TaskAudioNormalization": "音声の正規化",
"TaskAudioNormalizationDescription": "音声の正規化データのためにファイルをスキャンします。",
- "TaskCleanCollectionsAndPlaylistsDescription": "在しなくなったコレクションやプレイリストからアイテムを削除します。",
"TaskDownloadMissingLyricsDescription": "歌詞をダウンロード",
"TaskExtractMediaSegments": "メディアセグメントを読み取る",
"TaskMoveTrickplayImages": "Trickplayの画像を移動",
@@ -137,5 +106,7 @@
"TaskDownloadMissingLyrics": "失われた歌詞をダウンロード",
"TaskExtractMediaSegmentsDescription": "MediaSegment 対応プラグインからメディア セグメントを抽出または取得します。",
"CleanupUserDataTask": "ユーザーデータのクリーンアップタスク",
- "CleanupUserDataTaskDescription": "90日以上存在しないメディアに対して、視聴状態やお気に入り状態などのユーザーデータをすべて削除します。"
+ "CleanupUserDataTaskDescription": "90日以上存在しないメディアに対して、視聴状態やお気に入り状態などのユーザーデータをすべて削除します。",
+ "LyricDownloadFailureFromForItem": "歌詞",
+ "Original": "オリジナル"
}
diff --git a/Emby.Server.Implementations/Localization/Core/jbo.json b/Emby.Server.Implementations/Localization/Core/jbo.json
index 1b47bb2f23..50d6d49601 100644
--- a/Emby.Server.Implementations/Localization/Core/jbo.json
+++ b/Emby.Server.Implementations/Localization/Core/jbo.json
@@ -1,7 +1,4 @@
{
- "Albums": "lo albuma",
"Artists": "lo larpra",
- "Books": "lo cukta",
- "HeaderAlbumArtists": "lo albuma larpra",
- "Playlists": "lo zgipor"
+ "Books": "lo cukta"
}
diff --git a/Emby.Server.Implementations/Localization/Core/ka.json b/Emby.Server.Implementations/Localization/Core/ka.json
index 2d02522fea..f7ca19d7f0 100644
--- a/Emby.Server.Implementations/Localization/Core/ka.json
+++ b/Emby.Server.Implementations/Localization/Core/ka.json
@@ -1,42 +1,26 @@
{
"Genres": "ჟანრები",
- "HeaderAlbumArtists": "ალბომის შემსრულებლები",
- "HeaderFavoriteAlbums": "რჩეული ალბომები",
"TasksApplicationCategory": "აპლიკაცია",
- "Albums": "ალბომები",
"AppDeviceValues": "აპლიკაცია: {0}, მოწყობილობა: {1}",
- "Application": "აპლიკაცია",
- "Artists": "არტისტი",
+ "Artists": "შემსრულებლები",
"AuthenticationSucceededWithUserName": "{0} -ის ავთენტიკაცია წარმატებულია",
"Books": "წიგნები",
- "Forced": "ძალით",
+ "Forced": "იძულებითი",
"Inherit": "მემკვიდრეობით",
"Latest": "უახლესი",
"Movies": "ფილმები",
"Music": "მუსიკა",
"Photos": "ფოტოები",
- "Playlists": "დასაკრავი სიები",
- "Plugin": "დამატება",
"Shows": "სერიალები",
- "Songs": "სიმღერები",
- "Sync": "სინქრონიზაცია",
- "System": "სისტემა",
- "Undefined": "აღუწერელი",
- "User": "მომხმარებელი",
+ "Undefined": "განუსაზღვრელი",
"TasksMaintenanceCategory": "რემონტი",
"TasksLibraryCategory": "ბიბლიოთეკა",
"ChapterNameValue": "თავი {0}",
"HeaderContinueWatching": "ყურების გაგრძელება",
- "HeaderFavoriteArtists": "რჩეული შემსრულებლები",
- "DeviceOfflineWithName": "{0} გაითიშა",
"External": "გარე",
"HeaderFavoriteEpisodes": "რჩეული ეპიზოდები",
- "HeaderFavoriteSongs": "რჩეული სიმღერები",
- "HeaderRecordingGroups": "ჩამწერი ჯგუფები",
"HearingImpaired": "სმენადაქვეითებული",
- "LabelRunningTimeValue": "გაშვებულობის დრო: {0}",
- "MessageApplicationUpdatedTo": "Jellyfin-ის სერვერი განახლდა {0}-ზე",
- "MessageNamedServerConfigurationUpdatedWithValue": "სერვერის კონფიგურაციის სექცია {0} განახლდა",
+ "LabelRunningTimeValue": "გაშვების დრო: {0}",
"MixedContent": "შერეული შემცველობა",
"MusicVideos": "მუსიკის ვიდეოები",
"NotificationOptionInstallationFailed": "დაყენების შეცდომა",
@@ -45,85 +29,84 @@
"NotificationOptionCameraImageUploaded": "კამერის გამოსახულება ატვირთულია",
"NotificationOptionVideoPlaybackStopped": "ვიდეოს დაკვრა გაჩერებულია",
"PluginUninstalledWithName": "{0} წაიშალა",
- "ScheduledTaskStartedWithName": "{0} გაეშვა",
"VersionNumber": "ვერსია {0}",
"TasksChannelsCategory": "ინტერნეტ-არხები",
- "ValueSpecialEpisodeName": "სპეციალური - {0}",
- "TaskRefreshChannelsDescription": "ინტერნეტ-არხის ინფორმაციის განახლება.",
- "Channels": "არხები",
+ "TaskRefreshChannelsDescription": "განაახლებს ინტერნეტ-არხის ინფორმაციას.",
"Collections": "კოლექციები",
- "Default": "ნაგულისხმები",
+ "Default": "ნაგულისხმევი",
"Favorites": "რჩეულები",
"Folders": "საქაღალდეები",
"HeaderFavoriteShows": "რჩეული სერიალები",
- "HeaderLiveTV": "ცოცხალი TV",
- "HeaderNextUp": "შემდეგი ზემოთ",
+ "HeaderLiveTV": "ცოცხალი ტელევიზია",
+ "HeaderNextUp": "შემდეგი",
"HomeVideos": "სახლის ვიდეოები",
"NameSeasonNumber": "სეზონი {0}",
"NameSeasonUnknown": "სეზონი უცნობია",
"NotificationOptionPluginError": "დამატების შეცდომა",
"NotificationOptionPluginInstalled": "დამატება დაყენებულია",
- "NotificationOptionPluginUninstalled": "დამატება წაიშალა",
- "ProviderValue": "მომწოდებელი: {0}",
- "ScheduledTaskFailedWithName": "{0} ავარიულია",
- "TvShows": "TV სერიალები",
+ "NotificationOptionPluginUninstalled": "მოდული წაიშალა",
+ "ScheduledTaskFailedWithName": "{0} ჩავარდა",
+ "TvShows": "სატელევიზიო სერიალები",
"TaskRefreshPeople": "ხალხის განახლება",
"TaskUpdatePlugins": "დამატებების განახლება",
"TaskRefreshChannels": "არხების განახლება",
- "TaskOptimizeDatabase": "ბაზების ოპტიმიზაცია",
+ "TaskOptimizeDatabase": "მონაცემთა ბაზის ოპტიმიზაცია",
"TaskKeyframeExtractor": "საკვანძო კადრის გამომღები",
- "DeviceOnlineWithName": "{0} შეერთებულია",
"LabelIpAddressValue": "IP მისამართი: {0}",
- "NameInstallFailed": "{0}-ის დაყენების შეცდომა",
+ "NameInstallFailed": "{0}-ის დაყენების ჩავარდა",
"NotificationOptionApplicationUpdateAvailable": "ხელმისაწვდომია აპლიკაციის განახლება",
"NotificationOptionAudioPlaybackStopped": "აუდიოს დაკვრა გაჩერებულია",
"NotificationOptionNewLibraryContent": "ახალი შემცველობა დამატებულია",
"NotificationOptionPluginUpdateInstalled": "დამატების განახლება დაყენებულია",
- "NotificationOptionServerRestartRequired": "სერვერის გადატვირთვა აუცილებელია",
- "NotificationOptionTaskFailed": "დაგეგმილი ამოცანის შეცდომა",
+ "NotificationOptionServerRestartRequired": "საჭიროა სერვერის გადატვირთვა",
+ "NotificationOptionTaskFailed": "დაგეგმილი ამოცანა ჩავარდა",
"NotificationOptionUserLockedOut": "მომხმარებელი დაიბლოკა",
"NotificationOptionVideoPlayback": "ვიდეოს დაკვრა დაწყებულია",
"PluginInstalledWithName": "{0} დაყენებულია",
"PluginUpdatedWithName": "{0} განახლდა",
"TaskCleanActivityLog": "აქტივობების ჟურნალის გასუფთავება",
- "TaskCleanCache": "ქეშის საქაღალდის გასუფთავება",
- "TaskRefreshChapterImages": "თავის სურათების გაშლა",
+ "TaskCleanCache": "კეშის საქაღალდის გასუფთავება",
+ "TaskRefreshChapterImages": "თავის სურათების ამოღება",
"TaskRefreshLibrary": "მედიის ბიბლიოთეკის სკანირება",
"TaskCleanLogs": "ჟურნალის საქაღალდის გასუფთავება",
"TaskCleanTranscode": "ტრანსკოდირების საქაღალდის გასუფთავება",
"TaskDownloadMissingSubtitles": "ნაკლული სუბტიტრების გადმოწერა",
- "UserDownloadingItemWithValues": "{0} -ი {0}-ს იწერს",
- "FailedLoginAttemptWithUserName": "{0}-დან შემოსვლის შეცდომა",
- "MessageApplicationUpdated": "Jellyfin-ის სერვერი განახლდა",
- "MessageServerConfigurationUpdated": "სერვერის კონფიგურაცია განახლდა",
- "ServerNameNeedsToBeRestarted": "საჭიროა {0}-ის გადატვირთვა",
+ "UserDownloadingItemWithValues": "{0} იწერს {1}-ს",
+ "FailedLoginAttemptWithUserName": "შესვლის წარუმატებელი მცდელობა {0}-დან",
"UserCreatedWithName": "მომხმარებელი {0} შეიქმნა",
- "UserDeletedWithName": "მომხმარებელი {0} წაშლილია",
- "UserOnlineFromDevice": "{0}-ი ხაზზეა {1}-დან",
- "UserOfflineFromDevice": "{0}-ი {1}-დან გაითიშა",
- "ItemAddedWithName": "{0} ჩამატებულია ბიბლიოთეკაში",
- "ItemRemovedWithName": "{0} წაშლილია ბიბლიოთეკიდან",
+ "UserDeletedWithName": "მომხმარებელი {0} წაიშალა",
+ "UserOnlineFromDevice": "{0} ხაზზეა {1}-დან",
+ "UserOfflineFromDevice": "{0} გაითიშა {1}-დან",
"UserLockedOutWithName": "მომხმარებელი {0} დაბლოკილია",
- "UserStartedPlayingItemWithValues": "{0} თამაშობს {1}-ს {2}-ზე",
- "UserPasswordChangedWithName": "მომხმარებლისთვის {0} პაროლი შეცვლილია",
- "UserPolicyUpdatedWithName": "{0}-ის მომხმარებლის პოლიტიკა განახლდა",
- "UserStoppedPlayingItemWithValues": "{0}-მა დაამთავრა {1}-ის დაკვრა {2}-ზე",
+ "UserStartedPlayingItemWithValues": "{0} უკრავს {1}-ს {2}-ზე",
+ "UserPasswordChangedWithName": "მომხმარებელი {0}-სთვის პაროლი შეიცვალა",
+ "UserStoppedPlayingItemWithValues": "{0}-მა დაასრულა {1}-ის ყურება {2}-ზე",
"TaskRefreshChapterImagesDescription": "თავების მქონე ვიდეოებისთვის მინიატურების შექმნა.",
"TaskKeyframeExtractorDescription": "უფრო ზუსტი HLS დასაკრავი სიებისითვის ვიდეოდან საკვანძო გადრების ამოღება. შეიძლება საკმაო დრო დასჭირდეს.",
"NewVersionIsAvailable": "გადმოსაწერად ხელმისაწვდომია Jellyfin -ის ახალი ვერსია.",
- "CameraImageUploadedFrom": "ახალი კამერის გამოსახულება ატვირთულია {0}-დან",
"StartupEmbyServerIsLoading": "Jellyfin სერვერი იტვირთება. მოგვიანებით სცადეთ.",
- "SubtitleDownloadFailureFromForItem": "{0}-დან {1}-სთვის სუბტიტრების გადმოწერის შეცდომა",
- "ValueHasBeenAddedToLibrary": "{0} დაემატა თქვენს მედიის ბიბლიოთეკას",
- "TaskCleanActivityLogDescription": "მითითებულ ასაკზე ძველი ჟურნალის ჩანაწერების წაშლა.",
- "TaskCleanCacheDescription": "სისტემისთვის არასაჭირო ქეშის ფაილების წაშლა.",
- "TaskRefreshLibraryDescription": "თქვენი მედია ბიბლიოთეკაში ახალი ფაილების ძებნა და მეტამონაცემების განახლება.",
+ "SubtitleDownloadFailureFromForItem": "{0}-დან {1}-სთვის სუბტიტრების გადმოწერა ვერ შესრულდა",
+ "TaskCleanActivityLogDescription": "შლის მითითებულ ასაკზე ძველ ჟურნალის ჩანაწერებს.",
+ "TaskCleanCacheDescription": "შლის სისტემისთვის არასაჭირო ქეშის ფაილებს.",
+ "TaskRefreshLibraryDescription": "ეძებს ახალ ფაილებს თქვენს მედიის ბიბლიოთეკაში და ანახლებს მეტამონაცემებს.",
"TaskCleanLogsDescription": "{0} დღეზე ძველი ჟურნალის ფაილების წაშლა.",
"TaskRefreshPeopleDescription": "თქვენს მედიის ბიბლიოთეკაში მსახიობების და რეჟისორების მეტამონაცემების განახლება.",
- "TaskUpdatePluginsDescription": "ავტომატურად განახლებადად მონიშნული დამატებების განახლებების გადმოწერა და დაყენება.",
+ "TaskUpdatePluginsDescription": "ავტომატურად განახლებადად მონიშნული მოდულების განახლებების გადმოწერა და დაყენება.",
"TaskCleanTranscodeDescription": "ერთ დღეზე უფრო ძველი ტრანსკოდირების ფაილების წაშლა.",
- "TaskDownloadMissingSubtitlesDescription": "მეტამონაცემებზე დაყრდნობით ინტერნეტში ნაკლული სუბტიტრების ძებნა.",
- "TaskOptimizeDatabaseDescription": "ბაზს შეკუშვა და ადგილის გათავისუფლება. ამ ამოცანის ბიბლიოთეკის სკანირების ან ნებისმიერი ცვლილების, რომელიც ბაზაში რამეს აკეთებს, გაშვებას შეუძლია ბაზის წარმადობა გაზარდოს.",
- "TaskRefreshTrickplayImagesDescription": "ქმნის trickplay წინასწარ ხედებს ვიდეოებისთვის ჩართულ ბიბლიოთეკებში.",
- "TaskRefreshTrickplayImages": "Trickplay სურათების გენერირება"
+ "TaskDownloadMissingSubtitlesDescription": "ეძებს ბიბლიოთეკაში მიუწვდომელ სუბტიტრებს ინტერნეტში მეტამონაცემებზე დაყრდნობით.",
+ "TaskOptimizeDatabaseDescription": "კუმშავს მონაცემთა ბაზას ადგილის გათავისუფლებლად. ამ ამოცანის ბიბლიოთეკის სკანირების ან ნებისმიერი ცვლილების, რომელიც ბაზაში რამეს აკეთებს, გაშვებას შეუძლია ბაზის წარმადობა გაზარდოს.",
+ "TaskRefreshTrickplayImagesDescription": "ქმნის trickplay წინასწარ ხედებს ვიდეოებისთვის დაშვებულ ბიბლიოთეკებში.",
+ "TaskRefreshTrickplayImages": "Trickplay სურათების გენერაცია",
+ "TaskAudioNormalization": "აუდიოს ნორმალიზაცია",
+ "TaskAudioNormalizationDescription": "აანალიზებს ფაილებს აუდიოს ნორმალიზაციისთვის.",
+ "TaskDownloadMissingLyrics": "მიუწვდომელი ლირიკების ჩამოტვირთვა",
+ "TaskDownloadMissingLyricsDescription": "გადმოწერს ლირიკას სიმღერებისთვის",
+ "TaskExtractMediaSegments": "მედიის სეგმენტების სკანირება",
+ "TaskExtractMediaSegmentsDescription": "მედია სეგმენტების სკანირება მხარდაჭერილი მოდულებისთვის.",
+ "TaskMoveTrickplayImages": "Trickplay-ის გამოსახულებების მდებარეობის მიგრაცია",
+ "TaskMoveTrickplayImagesDescription": "გადააქვს trickplay ფაილები ბიბლიოთეკის პარამეტრებზე დაყრდნობით.",
+ "CleanupUserDataTask": "მომხმარებლების მონაცემების გასუფთავების ამოცანა",
+ "CleanupUserDataTaskDescription": "ასუფთავებს მომხმარებლების მონაცემებს (ყურების სტატუსი, ფავორიტები ანდ ა.შ) მედია ელემენტებისთვის რომლების 90 დღეზე მეტია აღარ არსებობენ.",
+ "LyricDownloadFailureFromForItem": "{1}-ისთვის {0}-დან ლირიკის გადმოწერა ჩავარდა",
+ "Original": "ორიგინალი"
}
diff --git a/Emby.Server.Implementations/Localization/Core/kab.json b/Emby.Server.Implementations/Localization/Core/kab.json
index 9551f0e5c1..0d0932b585 100644
--- a/Emby.Server.Implementations/Localization/Core/kab.json
+++ b/Emby.Server.Implementations/Localization/Core/kab.json
@@ -1,14 +1,10 @@
{
"Music": "Aẓawan",
- "Sync": "Amtawi",
"Photos": "Tiwlafin",
"Movies": "Isura",
"External": "Azɣaray",
- "User": "Aseqdac",
"Folders": "Ikaramen",
"Favorites": "Ismenyifen",
"Default": "Lexṣas",
- "Collections": "Tigrummiwin",
- "Channels": "Ibuda",
- "Albums": "Iseɣraz"
+ "Collections": "Tigrummiwin"
}
diff --git a/Emby.Server.Implementations/Localization/Core/kk.json b/Emby.Server.Implementations/Localization/Core/kk.json
index fc5fcf3c4d..ddcf60944d 100644
--- a/Emby.Server.Implementations/Localization/Core/kk.json
+++ b/Emby.Server.Implementations/Localization/Core/kk.json
@@ -1,41 +1,24 @@
{
- "Albums": "Älbomdar",
"AppDeviceValues": "Qoldanba: {0}, Qūrylğy: {1}",
- "Application": "Qoldanba",
"Artists": "Oryndauşylar",
"AuthenticationSucceededWithUserName": "{0} tüpnūsqalyq rastaluy sättı aiaqtaldy",
"Books": "Kıtaptar",
- "CameraImageUploadedFrom": "{0} kamerasynan jaña suret jüktep salyndy",
- "Channels": "Arnalar",
"ChapterNameValue": "{0}-sahna",
"Collections": "Jiyntyqtar",
- "DeviceOfflineWithName": "{0} ajyratylğan",
- "DeviceOnlineWithName": "{0} qosylğan",
"FailedLoginAttemptWithUserName": "{0} tarapynan kıru äreketı sätsız aiaqtaldy",
"Favorites": "Tañdaulylar",
"Folders": "Qaltalar",
"Genres": "Janrlar",
- "HeaderAlbumArtists": "Älbom oryndauşylary",
"HeaderContinueWatching": "Qaraudy jalğastyru",
- "HeaderFavoriteAlbums": "Tañdauly älbomdar",
- "HeaderFavoriteArtists": "Tañdauly oryndauşylar",
"HeaderFavoriteEpisodes": "Tañdauly telebölımder",
"HeaderFavoriteShows": "Tañdauly körsetımder",
- "HeaderFavoriteSongs": "Tañdauly äuender",
"HeaderLiveTV": "Efir",
"HeaderNextUp": "Kezektı",
- "HeaderRecordingGroups": "Jazba toptary",
"HomeVideos": "Üilık beineler",
"Inherit": "İelenu",
- "ItemAddedWithName": "{0} tasyğyşhanağa üstelindı",
- "ItemRemovedWithName": "{0} tasyğyşhanadan alastaldy",
"LabelIpAddressValue": "IP-mekenjaiy: {0}",
"LabelRunningTimeValue": "Oinatu uaqyty: {0}",
"Latest": "Eñ keiıngı",
- "MessageApplicationUpdated": "Jellyfin Serverı jañartyldy",
- "MessageApplicationUpdatedTo": "Jellyfin Serverı {0} nūsqasyna jañartyldy",
- "MessageNamedServerConfigurationUpdatedWithValue": "Server teñşelımderınıñ {0} bölımı jañartyldy",
- "MessageServerConfigurationUpdated": "Server teñşelımderı jañartyldy",
"MixedContent": "Aralas mazmūn",
"Movies": "Filmder",
"Music": "Muzyka",
@@ -61,23 +44,14 @@
"NotificationOptionVideoPlayback": "Beine oinatuy bastaldy",
"NotificationOptionVideoPlaybackStopped": "Beine oinatuy toqtatyldy",
"Photos": "Fotosuretter",
- "Playlists": "Oinatu tızımderı",
- "Plugin": "Plagin",
"PluginInstalledWithName": "{0} ornatyldy",
"PluginUninstalledWithName": "{0} joiyldy",
"PluginUpdatedWithName": "{0} jañartyldy",
- "ProviderValue": "Jetkızuşı: {0}",
"ScheduledTaskFailedWithName": "{0} sätsız",
- "ScheduledTaskStartedWithName": "{0} ıske qosyldy",
- "ServerNameNeedsToBeRestarted": "{0} qaita ıske qosu qajet",
"Shows": "Körsetımder",
- "Songs": "Äuender",
"StartupEmbyServerIsLoading": "Jellyfin Server jüktelude. Ärekettı köp ūzamai qaitalañyz.",
"SubtitleDownloadFailureFromForItem": "{1} üşın subtitrlerdı {0} közınen jüktep alu sätsız",
- "Sync": "Ündestıru",
- "System": "Jüie",
"TvShows": "TD-körsetımder",
- "User": "Paidalanuşy",
"UserCreatedWithName": "Paidalanuşy {0} jasalğan",
"UserDeletedWithName": "Paidalanuşy {0} joiylğan",
"UserDownloadingItemWithValues": "{0} — {1} jüktep aluda",
@@ -85,11 +59,8 @@
"UserOfflineFromDevice": "{0} — {1} tarapynan ajyratyldy",
"UserOnlineFromDevice": "{0} — {1} tarapynan qosyldy",
"UserPasswordChangedWithName": "Paidalanuşy {0} üşın paröl özgertıldı",
- "UserPolicyUpdatedWithName": "Paidalanuşy {0} üşın saiasattary jañartyldy",
"UserStartedPlayingItemWithValues": "{0} — {2} tarapynan {1} oinatuda",
"UserStoppedPlayingItemWithValues": "{0} — {2} tarapynan {1} oinatuyn toqtatty",
- "ValueHasBeenAddedToLibrary": "{0} tasyğyşhanağa üstelındı",
- "ValueSpecialEpisodeName": "Arnaiy - {0}",
"VersionNumber": "Nūsqasy {0}",
"Default": "Ädepkı",
"TaskDownloadMissingSubtitles": "Joq subtitrlerdı jüktep alu",
diff --git a/Emby.Server.Implementations/Localization/Core/km.json b/Emby.Server.Implementations/Localization/Core/km.json
index 5d10975f32..b4057eb8eb 100644
--- a/Emby.Server.Implementations/Localization/Core/km.json
+++ b/Emby.Server.Implementations/Localization/Core/km.json
@@ -1,88 +1,62 @@
{
- "Albums": "អាលប៊ុម",
- "MessageApplicationUpdatedTo": "ម៉ាស៊ីនមេនៃJellyfinត្រូវបានអាប់ដេតទៅកាន់ {0}",
- "MessageNamedServerConfigurationUpdatedWithValue": "ការកំណត់ម៉ាស៊ីនមេ ផ្នែក {0} ត្រូវបានអាប់ដេត",
- "MessageServerConfigurationUpdated": "ការកំណត់ម៉ាស៊ីនមេត្រូវបានអាប់ដេត",
"AppDeviceValues": "កម្មវិធី: {0}, ឧបករណ៍: {1}",
"MixedContent": "មាតិកាចម្រុះ",
"UserLockedOutWithName": "អ្នកប្រើប្រាស់ {0} ត្រូវ​បាន​ផ្អាក",
- "Application": "កម្មវិធី",
"Artists": "សិល្បករ",
"AuthenticationSucceededWithUserName": "{0} បានផ្ទៀងផ្ទាត់ដោយជោគជ័យ",
"Books": "សៀវភៅ",
"NameSeasonNumber": "រដូវកាលទី {0}",
"NotificationOptionPluginInstalled": "Plugin បានដំឡើងរួច",
- "CameraImageUploadedFrom": "រូបភាពកាមេរ៉ាថ្មីត្រូវបានបង្ហោះពី {0}",
- "Channels": "ប៉ុស្ត៍",
"ChapterNameValue": "ជំពូក {0}",
"Collections": "បណ្តុំ",
"External": "ខាងក្រៅ",
"Default": "លំនាំដើម",
"NotificationOptionInstallationFailed": "ការដំឡើងមិនបានសម្រេច",
- "DeviceOfflineWithName": "{0} បានផ្តាច់",
"Folders": "ថតឯកសារ",
- "DeviceOnlineWithName": "{0} បានភ្ចាប់",
"HearingImpaired": "ខ្សោយការស្តាប់",
"HomeVideos": "វីឌីអូថតខ្លួនឯង",
"Favorites": "ចំណូលចិត្ត",
"HeaderFavoriteEpisodes": "ភាគដែលចូលចិត្ត",
"Forced": "បង្ខំ",
"Genres": "ប្រភេទ",
- "HeaderFavoriteArtists": "សិល្បករដែលចូលចិត្ត",
"NotificationOptionApplicationUpdateAvailable": "កម្មវិធី យើងអាចអាប់ដេតបាន",
"NotificationOptionApplicationUpdateInstalled": "កម្មវិធី ដែលបានដំឡើងរួច",
"NotificationOptionAudioPlaybackStopped": "ការ​ចាក់សម្លេងបានផ្អាក",
"HeaderContinueWatching": "បន្តមើល",
- "HeaderFavoriteAlbums": "អាល់ប៊ុមដែលចូលចិត្ត",
"HeaderFavoriteShows": "រឿងភាគដែលចូលចិត្ត",
"NewVersionIsAvailable": "មានជំនាន់ថ្មី ម៉ាស៊ីនមេJellyfin អាចទាញយកបាន.",
- "HeaderAlbumArtists": "សិល្បករអាល់ប៊ុម",
"NotificationOptionCameraImageUploaded": "រូបភាពពីកាំមេរ៉ាបានអាប់ឡូតរួច",
- "HeaderFavoriteSongs": "ចម្រៀងដែលចូលចិត្ត",
"HeaderNextUp": "បន្ទាប់",
"HeaderLiveTV": "ទូរទស្សន៍ផ្សាយផ្ទាល់",
"Movies": "រឿង",
- "HeaderRecordingGroups": "ក្រុមនៃការថត",
"Music": "តន្ត្រី",
"Inherit": "មរតក",
"MusicVideos": "វីដេអូតន្ត្រី",
"NameInstallFailed": "{0} ការដំឡើងបានបរាជ័យ",
"NotificationOptionNewLibraryContent": "មាតិកាថ្មីៗត្រូវបានបន្ថែម",
- "ItemAddedWithName": "{0} ត្រូវបានបន្ថែមទៅបណ្ណាល័យ",
"NameSeasonUnknown": "រដូវកាលមិនច្បាស់លាស់",
- "ItemRemovedWithName": "{0} ត្រូវបានដកចេញពីបណ្ណាល័យ",
"LabelIpAddressValue": "លេខ IP: {0}",
"LabelRunningTimeValue": "ពេលវេលាកំពុងដំណើរការ: {0}",
"Latest": "ចុងក្រោយ",
"NotificationOptionAudioPlayback": "ការ​ចាក់​សំឡេង​បាន​ចាប់ផ្ដើម",
"NotificationOptionPluginError": "Plugin មិនដំណើរការ",
"NotificationOptionPluginUninstalled": "Plugin បានលុបចេញរួច",
- "MessageApplicationUpdated": "ម៉ាស៊ីនមេនៃJellyfinត្រូវបានអាប់ដេត",
"NotificationOptionPluginUpdateInstalled": "Plugin អាប់ដេតបានដំឡើងរួច",
"NotificationOptionUserLockedOut": "អ្នកប្រើប្រាស់ត្រូវបានជាប់គាំង",
"NotificationOptionServerRestartRequired": "តម្រូវឱ្យចាប់ផ្ដើមម៉ាស៊ីនមេឡើងវិញ",
"Photos": "រូបថត",
- "Playlists": "បញ្ជីចាក់",
- "Plugin": "Plugin",
"PluginInstalledWithName": "{0} ត្រូវបានដំឡើង",
"NotificationOptionTaskFailed": "កិច្ចការដែលបានគ្រោងទុកបានបរាជ័យ",
"PluginUpdatedWithName": "{0} ត្រូវបានអាប់ដេត",
"NotificationOptionVideoPlayback": "ការចាក់វីដេអូបានចាប់ផ្តើម",
- "Songs": "ចម្រៀង",
- "ScheduledTaskStartedWithName": "{0} បានចាប់ផ្តើម",
"NotificationOptionVideoPlaybackStopped": "ការ​ចាក់​វីដេអូ​បាន​បញ្ឈប់",
"PluginUninstalledWithName": "{0} ត្រូវបានលុបចេញ",
"Shows": "រឿងភាគ",
- "ProviderValue": "អ្នកផ្តល់សេវា: {0}",
"SubtitleDownloadFailureFromForItem": "សាប់ថាយថលបានបរាជ័យក្នុងការទាញយកពី {0} នៃ {1}",
- "Sync": "ធ្វើអោយដំណាលគ្នា",
- "System": "ប្រព័ន្ធ",
"TvShows": "កម្មវិធីទូរទស្សន៍",
"ScheduledTaskFailedWithName": "{0} បានបរាជ័យ",
"Undefined": "មិនបានកំណត់",
- "User": "អ្នកប្រើប្រាស់",
"UserCreatedWithName": "អ្នកប្រើប្រាស់ {0} ត្រូវបានបង្កើតឡើង",
- "ServerNameNeedsToBeRestarted": "{0} ចាំបាច់ត្រូវចាប់ផ្តើមឡើងវិញ",
"StartupEmbyServerIsLoading": "ម៉ាស៊ីនមេJellyfin កំពុងដំណើរការ. សូមព្យាយាមម្តងទៀតក្នុងពេលឆាប់ៗនេះ.",
"UserDeletedWithName": "អ្នកប្រើប្រាស់ {0} ត្រូវបានលុបចេញ",
"UserOnlineFromDevice": "{0} បានឃើញអនឡានពី {1}",
@@ -98,10 +72,7 @@
"UserPasswordChangedWithName": "ពាក្យសម្ងាត់ត្រូវបានផ្លាស់ប្តូរសម្រាប់អ្នកប្រើប្រាស់ {0}",
"TaskCleanCache": "សម្អាតបញ្ជីឃ្លាំងសម្ងាត់",
"TaskRefreshChapterImages": "ដកស្រង់រូបភាពតាមជំពូក",
- "UserPolicyUpdatedWithName": "គោលការណ៍អ្នកប្រើប្រាស់ត្រូវបានធ្វើបច្ចុប្បន្នភាពសម្រាប់ {0}",
"UserStoppedPlayingItemWithValues": "{0} បានបញ្ចប់ការចាក់ {1} នៅលើ {2}",
- "ValueHasBeenAddedToLibrary": "{0} ត្រូវបានបញ្ចូលទៅក្នុងបណ្ណាល័យរឿងរបស់អ្នក",
- "ValueSpecialEpisodeName": "ពិសេស - {0}",
"TasksChannelsCategory": "ប៉ុស្តតាមអ៊ីនធឺណិត",
"TaskAudioNormalization": "ធ្វើឱ្យមានតន្ត្រីមានសម្លេងស្មើគ្នា",
"TaskCleanActivityLogDescription": "លុបកំណត់ហេតុសកម្មភាពចាស់ជាងអាយុដែលបានកំណត់រចនាសម្ព័ន្ធ.",
@@ -127,7 +98,5 @@
"TaskUpdatePluginsDescription": "ទាញយក និងដំឡើងបច្ចុប្បន្នភាពសម្រាប់Plugins ដែលត្រូវបាន Config ដើម្បីធ្វើបច្ចុប្បន្នភាពដោយស្វ័យប្រវត្តិ.",
"TaskCleanTranscodeDescription": "លុបឯកសារ Transcode ដែលលើសពីមួយថ្ងៃ.",
"TaskDownloadMissingSubtitlesDescription": "ស្វែងរកតាមអ៊ីនធឺណិត សម្រាប់សាប់ថាយថល ដែលបាត់ដោយផ្អែកលើ metadata.",
- "TaskOptimizeDatabase": "ធ្វើឱ្យ Database ប្រសើរឡើង",
- "TaskCleanCollectionsAndPlaylistsDescription": "លុបរបស់របរចេញពីបណ្តុំ និងបញ្ជីចាក់ដែលលែងមាន.",
- "TaskCleanCollectionsAndPlaylists": "សម្អាតបណ្តុំ និងបញ្ជីចាក់"
+ "TaskOptimizeDatabase": "ធ្វើឱ្យ Database ប្រសើរឡើង"
}
diff --git a/Emby.Server.Implementations/Localization/Core/kn.json b/Emby.Server.Implementations/Localization/Core/kn.json
index 9f49be53b4..6009b50fe0 100644
--- a/Emby.Server.Implementations/Localization/Core/kn.json
+++ b/Emby.Server.Implementations/Localization/Core/kn.json
@@ -4,8 +4,6 @@
"TaskOptimizeDatabaseDescription": "ಡೇಟಾಬೇಸ್ ಅನ್ನು ಕಾಂಪ್ಯಾಕ್ಟ್ ಮಾಡುತ್ತದೆ ಮತ್ತು ಮುಕ್ತ ಜಾಗವನ್ನು ಮೊಟಕುಗೊಳಿಸುತ್ತದೆ. ಲೈಬ್ರರಿಯನ್ನು ಸ್ಕ್ಯಾನ್ ಮಾಡಿದ ನಂತರ ಈ ಕಾರ್ಯವನ್ನು ನಡೆಸುವುದು ಅಥವಾ ಡೇಟಾಬೇಸ್ ಮಾರ್ಪಾಡುಗಳನ್ನು ಸೂಚಿಸುವ ಇತರ ಬದಲಾವಣೆಗಳನ್ನು ಮಾಡುವುದರಿಂದ ಕಾರ್ಯಕ್ಷಮತೆಯನ್ನು ಸುಧಾರಿಸಬಹುದು.",
"TaskKeyframeExtractor": "ಕೀಫ್ರೇಮ್ ಎಕ್ಸ್‌ಟ್ರಾಕ್ಟರ್",
"TaskKeyframeExtractorDescription": "ಹೆಚ್ಚು ನಿಖರವಾದ HLS ಪ್ಲೇಪಟ್ಟಿಗಳನ್ನು ರಚಿಸಲು ವೀಡಿಯೊ ಫೈಲ್‌ಗಳಿಂದ ಕೀಫ್ರೇಮ್‌ಗಳನ್ನು ಹೊರತೆಗೆಯುತ್ತದೆ. ಈ ಕಾರ್ಯವು ದೀರ್ಘಕಾಲದವರೆಗೆ ನಡೆಯಬಹುದು.",
- "ValueHasBeenAddedToLibrary": "{0} ಅನ್ನು ನಿಮ್ಮ ಮಾಧ್ಯಮ ಲೈಬ್ರರಿಗೆ ಸೇರಿಸಲಾಗಿದೆ",
- "ValueSpecialEpisodeName": "ವಿಶೇಷ - {0}",
"TasksLibraryCategory": "ಸಮೊಹ",
"TasksApplicationCategory": "ಅಪ್ಲಿಕೇಶನ್",
"TasksChannelsCategory": "ಇಂಟರ್ನೆಟ್ ಚಾನೆಲ್ಗಳು",
@@ -13,8 +11,6 @@
"TaskCleanCacheDescription": "ಸಿಸ್ಟಮ್‌ಗೆ ಇನ್ನು ಮುಂದೆ ಅಗತ್ಯವಿಲ್ಲದ ಸಂಗ್ರಹ ಫೈಲ್‌ಗಳನ್ನು ಅಳಿಸುತ್ತದೆ.",
"TaskRefreshLibrary": "ಸ್ಕ್ಯಾನ್ ಮೀಡಿಯಾ ಲೈಬ್ರರಿ",
"UserOfflineFromDevice": "{1} ನಿಂದ {0} ಸಂಪರ್ಕ ಕಡಿತಗೊಂಡಿದೆ",
- "Albums": "ಸಂಪುಟ",
- "Application": "ಅಪ್ಲಿಕೇಶನ್",
"AppDeviceValues": "ಅಪ್ಲಿಕೇಶನ್: {0}, ಸಾಧನ: {1}",
"Artists": "ಕಲಾವಿದರು",
"AuthenticationSucceededWithUserName": "{0} ಯಶಸ್ವಿಯಾಗಿ ದೃಢೀಕರಿಸಲಾಗಿದೆ",
@@ -22,8 +18,6 @@
"ChapterNameValue": "ಅಧ್ಯಾಯ {0}",
"Collections": "ಸಂಗ್ರಹಣೆಗಳು",
"Default": "ಪೂರ್ವನಿಯೋಜಿತ",
- "DeviceOfflineWithName": "{0} ಸಂಪರ್ಕ ಕಡಿತಗೊಂಡಿದೆ",
- "DeviceOnlineWithName": "{0} ಸಂಪರ್ಕಗೊಂಡಿದೆ",
"External": "ಹೊರಗಿನ",
"FailedLoginAttemptWithUserName": "ವಿಫಲ ಲಾಗಿನ್ ಪ್ರಯತ್ನ ಸಂಖ್ಯೆ {0}",
"Favorites": "ಮೆಚ್ಚಿನವುಗಳು",
@@ -31,22 +25,11 @@
"Forced": "ಬಲವಂತವಾಗಿ",
"Genres": "ಪ್ರಕಾರಗಳು",
"HeaderContinueWatching": "ನೋಡುವುದನ್ನು ಮುಂದುವರಿಸಿ",
- "HeaderFavoriteAlbums": "ಮೆಚ್ಚಿನ ಸಂಪುಟಗಳು",
- "HeaderFavoriteArtists": "ಮೆಚ್ಚಿನ ಕಲಾವಿದರು",
"HeaderFavoriteShows": "ಮೆಚ್ಚಿನ ಪ್ರದರ್ಶನಗಳು",
- "HeaderFavoriteSongs": "ಮೆಚ್ಚಿನ ಹಾಡುಗಳು",
"HeaderLiveTV": "ನೇರ ದೂರದರ್ಶನ",
"HeaderNextUp": "ಮುಂದೆ",
- "HeaderRecordingGroups": "ರೆಕಾರ್ಡಿಂಗ್ ಗುಂಪುಗಳು",
- "MessageApplicationUpdated": "ಜೆಲ್ಲಿಫಿನ್ ಸರ್ವರ್ ಅನ್ನು ನವೀಕರಿಸಲಾಗಿದೆ",
- "CameraImageUploadedFrom": "ಹೊಸ ಕ್ಯಾಮರಾ ಚಿತ್ರವನ್ನು {0} ನಿಂದ ಅಪ್‌ಲೋಡ್ ಮಾಡಲಾಗಿದೆ",
- "Channels": "ಮೂಲಗಳು",
- "HeaderAlbumArtists": "ಸಂಪುಟ ಕಲಾವಿದರು",
"HeaderFavoriteEpisodes": "ಮೆಚ್ಚಿನ ಸಂಚಿಕೆಗಳು",
"HearingImpaired": "ಮೂಗ",
- "ItemAddedWithName": "{0} ಅನ್ನು ಸಂಕಲನಕ್ಕೆ ಸೇರಿಸಲಾಗಿದೆ",
- "MessageApplicationUpdatedTo": "ಜೆಲ್ಲಿಫಿನ್ ಸರ್ವರ್ ಅನ್ನು {0} ಗೆ ನವೀಕರಿಸಲಾಗಿದೆ",
- "MessageNamedServerConfigurationUpdatedWithValue": "ಸರ್ವರ್ ಕಾನ್ಫಿಗರೇಶನ್ ವಿಭಾಗ {0} ಅನ್ನು ನವೀಕರಿಸಲಾಗಿದೆ",
"NewVersionIsAvailable": "ಜೆಲ್ಲಿಫಿನ್ ಸರ್ವರ್‌ನ ಹೊಸ ಆವೃತ್ತಿಯು ಡೌನ್‌ಲೋಡ್‌ಗೆ ಲಭ್ಯವಿದೆ.",
"NotificationOptionAudioPlayback": "ಆಡಿಯೋ ಪ್ಲೇಬ್ಯಾಕ್ ಪ್ರಾರಂಭವಾಗಿದೆ",
"NotificationOptionCameraImageUploaded": "ಕ್ಯಾಮರಾ ಚಿತ್ರವನ್ನು ಅಪ್ಲೋಡ್ ಮಾಡಲಾಗಿದೆ",
@@ -55,13 +38,10 @@
"NotificationOptionVideoPlaybackStopped": "ವೀಡಿಯೊ ಪ್ಲೇಬ್ಯಾಕ್ ನಿಲ್ಲಿಸಲಾಗಿದೆ",
"PluginUninstalledWithName": "{0} ಅನ್ನು ಅನ್‌ಇನ್‌ಸ್ಟಾಲ್ ಮಾಡಲಾಗಿದೆ",
"ScheduledTaskFailedWithName": "{0} ವಿಫಲವಾಗಿದೆ",
- "ScheduledTaskStartedWithName": "{0} ಪ್ರಾರಂಭವಾಯಿತು",
- "ServerNameNeedsToBeRestarted": "{0} ಅನ್ನು ಮರುಪ್ರಾರಂಭಿಸಬೇಕಾಗಿದೆ",
"UserCreatedWithName": "ಬಳಕೆದಾರ {0} ಅನ್ನು ರಚಿಸಲಾಗಿದೆ",
"UserLockedOutWithName": "ಬಳಕೆದಾರ {0} ಅನ್ನು ಲಾಕ್ ಮಾಡಲಾಗಿದೆ",
"UserOnlineFromDevice": "{1} ನಿಂದ {0} ಆನ್‌ಲೈನ್‌ನಲ್ಲಿದೆ",
"UserPasswordChangedWithName": "{0} ಬಳಕೆದಾರರಿಗಾಗಿ ಪಾಸ್‌ವರ್ಡ್ ಅನ್ನು ಬದಲಾಯಿಸಲಾಗಿದೆ",
- "UserPolicyUpdatedWithName": "ಬಳಕೆದಾರರ ನೀತಿಯನ್ನು {0} ಗೆ ನವೀಕರಿಸಲಾಗಿದೆ",
"UserStartedPlayingItemWithValues": "{2} ರಂದು {0} ಆಡುತ್ತಿದೆ {1}",
"UserStoppedPlayingItemWithValues": "{0} ಅವರು {1} ಅನ್ನು {2} ನಲ್ಲಿ ಆಡುವುದನ್ನು ಮುಗಿಸಿದ್ದಾರೆ",
"VersionNumber": "ಆವೃತ್ತಿ {0}",
@@ -76,23 +56,17 @@
"TaskCleanTranscodeDescription": "ಒಂದು ದಿನಕ್ಕಿಂತ ಹಳೆಯದಾದ ಟ್ರಾನ್ಸ್‌ಕೋಡ್ ಫೈಲ್‌ಗಳನ್ನು ಅಳಿಸುತ್ತದೆ.",
"TaskDownloadMissingSubtitles": "ಕಾಣೆಯಾದ ಉಪಶೀರ್ಷಿಕೆಗಳನ್ನು ಡೌನ್‌ಲೋಡ್ ಮಾಡಿ",
"Shows": "ಧಾರವಾಹಿಗಳು",
- "Songs": "ಹಾಡುಗಳು",
"StartupEmbyServerIsLoading": "ಜೆಲ್ಲಿಫಿನ್ ಸರ್ವರ್ ಲೋಡ್ ಆಗುತ್ತಿದೆ. ದಯವಿಟ್ಟು ಸ್ವಲ್ಪ ಸಮಯದ ನಂತರ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ.",
"UserDeletedWithName": "ಬಳಕೆದಾರ {0} ಅನ್ನು ಅಳಿಸಲಾಗಿದೆ",
"UserDownloadingItemWithValues": "{0} ಡೌನ್‌ಲೋಡ್ ಆಗುತ್ತಿದೆ {1}",
"SubtitleDownloadFailureFromForItem": "ಉಪಶೀರ್ಷಿಕೆಗಳು {0} ನಿಂದ {1} ಗಾಗಿ ಡೌನ್‌ಲೋಡ್ ಮಾಡಲು ವಿಫಲವಾಗಿವೆ",
- "Sync": "ಹೊಂದಿಕೆ",
- "System": "ವ್ಯವಸ್ಥೆ",
"TvShows": "ದೂರದರ್ಶನ ಕಾರ್ಯಕ್ರಮಗಳು",
"Undefined": "ವ್ಯಾಖ್ಯಾನಿಸಲಾಗಿಲ್ಲ",
- "User": "ಬಳಕೆದಾರ",
"HomeVideos": "ಮುಖಪುಟ ವೀಡಿಯೊಗಳು",
"Inherit": "ಪಾರಂಪರ್ಯವಾಗಿ",
- "ItemRemovedWithName": "{0} ಅನ್ನು ಸಂಕಲನದಿಂದ ತೆಗೆದುಹಾಕಲಾಗಿದೆ",
"LabelIpAddressValue": "IP ವಿಳಾಸ: {0}",
"LabelRunningTimeValue": "ಅವಧಿ: {0}",
"Latest": "ಹೊಸದಾದ",
- "MessageServerConfigurationUpdated": "ಸರ್ವರ್ ಕಾನ್ಫಿಗರೇಶನ್ ಅನ್ನು ನವೀಕರಿಸಲಾಗಿದೆ",
"MixedContent": "ಮಿಶ್ರ ವಿಷಯ",
"Movies": "ಚಲನಚಿತ್ರಗಳು",
"Music": "ಸಂಗೀತ",
@@ -106,17 +80,14 @@
"NotificationOptionInstallationFailed": "ಸ್ಥಾಪನ ವೈಫಲ್ಯ",
"NotificationOptionNewLibraryContent": "ಹೊಸ ವಿಷಯವನ್ನು ಒಳಗೊಂಡಿದೆ",
"NotificationOptionPluginError": "ಪ್ಲಗಿನ್ ವೈಫಲ್ಯ",
- "NotificationOptionPluginInstalled": "ಪ್ಲಗಿನ್ ವೈಫಲ್ಯ",
+ "NotificationOptionPluginInstalled": "ಪ್ಲಗಿನ್ ಸ್ಥಾಪಿಸಲಾಗಿದೆ",
"NotificationOptionPluginUpdateInstalled": "ಪ್ಲಗಿನ್ ನವೀಕರಣವನ್ನು ಸ್ಥಾಪಿಸಲಾಗಿದೆ",
"NotificationOptionServerRestartRequired": "ಸರ್ವರ್ ಮರುಪ್ರಾರಂಭದ ಅಗತ್ಯವಿದೆ",
"NotificationOptionTaskFailed": "ನಿಗದಿತ ಕಾರ್ಯ ವೈಫಲ್ಯ",
"NotificationOptionVideoPlayback": "ವೀಡಿಯೊ ಪ್ಲೇಬ್ಯಾಕ್ ಪ್ರಾರಂಭವಾಗಿದೆ",
"Photos": "ಚಿತ್ರಗಳು",
- "Playlists": "ಪ್ಲೇಪಟ್ಟಿಗಳು",
- "Plugin": "ಪ್ಲಗಿನ್",
"PluginInstalledWithName": "{0} ಅನ್ನು ಸ್ಥಾಪಿಸಲಾಗಿದೆ",
"PluginUpdatedWithName": "{0} ಅನ್ನು ನವೀಕರಿಸಲಾಗಿದೆ",
- "ProviderValue": "ಒದಗಿಸುವವರು: {0}",
"TaskCleanLogs": "ಕ್ಲೀನ್ ಲಾಗ್ ಡೈರೆಕ್ಟರಿ",
"TaskRefreshPeople": "ಜನರನ್ನು ರಿಫ್ರೆಶ್ ಮಾಡಿ",
"TaskRefreshPeopleDescription": "ನಿಮ್ಮ ಮಾಧ್ಯಮ ಲೈಬ್ರರಿಯಲ್ಲಿ ನಟರು ಮತ್ತು ನಿರ್ದೇಶಕರಿಗಾಗಿ ಮೆಟಾಡೇಟಾವನ್ನು ನವೀಕರಿಸಿ.",
@@ -129,7 +100,5 @@
"TaskExtractMediaSegments": "ಮಾಧ್ಯಮ ವಿಭಾಗದ ಹುಡುಕು",
"TaskDownloadMissingLyrics": "ಇಲ್ಲದ ಸಾಹಿತ್ಯವನ್ನು ಪಡೆಯಿರಿ",
"TaskAudioNormalization": "ಧ್ವನಿ ಸಾಮಾನ್ಯೀಕರಣ",
- "TaskRefreshTrickplayImages": "ಟ್ರಿಕ್‌ಪ್ಲೇ ಚಿತ್ರಗಳನ್ನು ರಚಿಸಿ",
- "TaskCleanCollectionsAndPlaylists": "ಸಂಗ್ರಹಗಳು ಮತ್ತು ಪ್ಲೇಪಟ್ಟಿಗಳನ್ನು ಸ್ವಚ್ಛಗೊಳಿಸಿ",
- "TaskCleanCollectionsAndPlaylistsDescription": "ಇಲ್ಲದ ಸಂಗ್ರಹಗಳು ಮತ್ತು ಪ್ಲೇಪಟ್ಟಿಗಳಿಂದ ವಸ್ತುಗಳನ್ನು ತೆಗೆದುಹಾಕುತ್ತದೆ."
+ "TaskRefreshTrickplayImages": "ಟ್ರಿಕ್‌ಪ್ಲೇ ಚಿತ್ರಗಳನ್ನು ರಚಿಸಿ"
}
diff --git a/Emby.Server.Implementations/Localization/Core/ko.json b/Emby.Server.Implementations/Localization/Core/ko.json
index 2b24ea2c8b..a210125d34 100644
--- a/Emby.Server.Implementations/Localization/Core/ko.json
+++ b/Emby.Server.Implementations/Localization/Core/ko.json
@@ -1,41 +1,24 @@
{
- "Albums": "앨범",
"AppDeviceValues": "앱: {0}, 장치: {1}",
- "Application": "애플리케이션",
"Artists": "아티스트",
"AuthenticationSucceededWithUserName": "{0} 사용자가 성공적으로 인증됨",
"Books": "도서",
- "CameraImageUploadedFrom": "{0}에서 새로운 카메라 이미지가 업로드됨",
- "Channels": "채널",
"ChapterNameValue": "챕터 {0}",
"Collections": "컬렉션",
- "DeviceOfflineWithName": "{0}의 연결 끊김",
- "DeviceOnlineWithName": "{0}이(가) 연결됨",
"FailedLoginAttemptWithUserName": "{0}에서 로그인 실패",
"Favorites": "즐겨찾기",
"Folders": "폴더",
"Genres": "장르",
- "HeaderAlbumArtists": "앨범 음악가",
"HeaderContinueWatching": "계속 시청하기",
- "HeaderFavoriteAlbums": "즐겨찾는 앨범",
- "HeaderFavoriteArtists": "즐겨찾는 아티스트",
"HeaderFavoriteEpisodes": "즐겨찾는 에피소드",
"HeaderFavoriteShows": "즐겨찾는 쇼",
- "HeaderFavoriteSongs": "즐겨찾는 노래",
"HeaderLiveTV": "실시간 TV",
"HeaderNextUp": "다음으로",
- "HeaderRecordingGroups": "녹화 그룹",
"HomeVideos": "홈 비디오",
"Inherit": "상속",
- "ItemAddedWithName": "{0}가 라이브러리에 추가되었습니다",
- "ItemRemovedWithName": "{0}가 라이브러리에서 제거됨",
"LabelIpAddressValue": "IP 주소: {0}",
"LabelRunningTimeValue": "상영 시간: {0}",
"Latest": "최근",
- "MessageApplicationUpdated": "Jellyfin 서버가 업데이트되었습니다",
- "MessageApplicationUpdatedTo": "Jellyfin 서버가 {0}로 업데이트되었습니다",
- "MessageNamedServerConfigurationUpdatedWithValue": "서버 환경 설정 {0} 섹션이 업데이트되었습니다",
- "MessageServerConfigurationUpdated": "서버 환경 설정이 업데이트되었습니다",
"MixedContent": "혼합 콘텐츠",
"Movies": "영화",
"Music": "음악",
@@ -61,23 +44,14 @@
"NotificationOptionVideoPlayback": "비디오 재생 시작됨",
"NotificationOptionVideoPlaybackStopped": "비디오 재생 중지됨",
"Photos": "사진",
- "Playlists": "재생목록",
- "Plugin": "플러그인",
"PluginInstalledWithName": "{0} 설치됨",
"PluginUninstalledWithName": "{0} 제거됨",
"PluginUpdatedWithName": "{0} 업데이트됨",
- "ProviderValue": "제공자: {0}",
"ScheduledTaskFailedWithName": "{0} 실패",
- "ScheduledTaskStartedWithName": "{0} 시작",
- "ServerNameNeedsToBeRestarted": "{0}를 재시작해야합니다",
"Shows": "시리즈",
- "Songs": "노래",
"StartupEmbyServerIsLoading": "Jellyfin 서버를 불러오고 있습니다. 잠시 후에 다시 시도하십시오.",
"SubtitleDownloadFailureFromForItem": "{0}에서 {1} 자막 다운로드에 실패했습니다",
- "Sync": "동기화",
- "System": "시스템",
"TvShows": "TV 쇼",
- "User": "사용자",
"UserCreatedWithName": "사용자 {0} 생성됨",
"UserDeletedWithName": "사용자 {0} 삭제됨",
"UserDownloadingItemWithValues": "{0} 사용자가 {1} 다운로드 중",
@@ -85,11 +59,8 @@
"UserOfflineFromDevice": "{0} 사용자의 {1}에서 연결이 끊김",
"UserOnlineFromDevice": "{0} 사용자가 {1}에서 접속함",
"UserPasswordChangedWithName": "{0} 사용자 비밀번호 변경됨",
- "UserPolicyUpdatedWithName": "{0} 사용자 정책 업데이트됨",
"UserStartedPlayingItemWithValues": "{0} 사용자의 {2}에서 {1} 재생 중",
"UserStoppedPlayingItemWithValues": "{0} 사용자의 {2}에서 {1} 재생을 마침",
- "ValueHasBeenAddedToLibrary": "{0}가 미디어 라이브러리에 추가되었습니다",
- "ValueSpecialEpisodeName": "스페셜 - {0}",
"VersionNumber": "버전 {0}",
"TasksApplicationCategory": "어플리케이션",
"TasksMaintenanceCategory": "유지 보수",
@@ -124,12 +95,10 @@
"TaskKeyframeExtractor": "키프레임 추출",
"External": "외부",
"HearingImpaired": "청각 장애",
- "TaskCleanCollectionsAndPlaylists": "컬렉션과 재생목록 정리",
"TaskAudioNormalization": "오디오의 볼륨 수준을 일정하게 조정",
"TaskAudioNormalizationDescription": "오디오의 볼륨 수준을 일정하게 조정하기 위해 파일을 스캔합니다.",
"TaskRefreshTrickplayImages": "비디오 탐색용 미리보기 썸네일 생성",
"TaskRefreshTrickplayImagesDescription": "활성화된 라이브러리에서 비디오의 트릭플레이 미리보기를 생성합니다.",
- "TaskCleanCollectionsAndPlaylistsDescription": "더 이상 존재하지 않는 컬렉션 및 재생 목록에서 항목을 제거합니다.",
"TaskExtractMediaSegments": "미디어 세그먼트 스캔",
"TaskExtractMediaSegmentsDescription": "MediaSegment를 지원하는 플러그인에서 미디어 세그먼트를 추출하거나 가져옵니다.",
"TaskMoveTrickplayImages": "트릭플레이 이미지 위치 마이그레이션",
@@ -137,5 +106,7 @@
"TaskDownloadMissingLyrics": "누락된 가사 다운로드",
"TaskDownloadMissingLyricsDescription": "가사 다운로드",
"CleanupUserDataTask": "사용자 데이터 정리 작업",
- "CleanupUserDataTaskDescription": "최소 90일 이상 존재하지 않는 미디어에 대한 사용자 데이터(시청 상태, 즐겨찾기 등)를 정리합니다."
+ "CleanupUserDataTaskDescription": "최소 90일 이상 존재하지 않는 미디어에 대한 사용자 데이터(시청 상태, 즐겨찾기 등)를 정리합니다.",
+ "LyricDownloadFailureFromForItem": "{1}에 대한 가사를 {0}에서 다운로드하지 못했습니다",
+ "Original": "원본"
}
diff --git a/Emby.Server.Implementations/Localization/Core/kw.json b/Emby.Server.Implementations/Localization/Core/kw.json
index 336d286fc4..fc2e189e7f 100644
--- a/Emby.Server.Implementations/Localization/Core/kw.json
+++ b/Emby.Server.Implementations/Localization/Core/kw.json
@@ -1,21 +1,13 @@
{
"Collections": "Kuntellow",
- "DeviceOfflineWithName": "{0} re anjunyas",
"External": "A-ves",
"Folders": "Plegellow",
- "HeaderFavoriteAlbums": "Albomow Drudh",
- "HeaderFavoriteArtists": "Artydhyon Drudh",
"HeaderFavoriteEpisodes": "Towlennow Drudh",
- "HeaderFavoriteSongs": "Kanow Drudh",
- "HeaderRecordingGroups": "Bagasow Rekordya",
"HearingImpaired": "Klewans Aperys",
"HomeVideos": "Gwydhyow Tre",
"Inherit": "Herya",
"LabelRunningTimeValue": "Prys ow ponya: {0}",
"Latest": "Diwettha",
- "MessageApplicationUpdated": "Servell Jellyfin re beu nowedhys",
- "MessageApplicationUpdatedTo": "Servell Jellyfin re beu nowedhys dhe {0}",
- "MessageNamedServerConfigurationUpdatedWithValue": "Rann dewisyans servell {0} re beu nowedhys",
"MixedContent": "Dalgh kemmyskys",
"Movies": "Fylmow",
"MusicVideos": "Gwydhyow Ilow",
@@ -25,23 +17,16 @@
"NotificationOptionPluginError": "Defowt ystynnans",
"NotificationOptionPluginUninstalled": "Ystynnans anynstallys",
"NotificationOptionPluginUpdateInstalled": "Nowedheans ystynnans ynstallys",
- "Application": "Gweythres",
"Favorites": "Moyha Kerys",
"Forced": "Konstrynys",
- "Albums": "Albomow",
"Books": "Lyvrow",
- "Channels": "Kanolyow",
"AppDeviceValues": "App: {0}, Devis: {1}",
"Artists": "Artyhdyon",
- "HeaderAlbumArtists": "Albom artydhyon",
"HeaderNextUp": "Nessa",
- "CameraImageUploadedFrom": "Skeusen kamera nowydh re beu ughkargys a-dhyworth {0}",
"ChapterNameValue": "Chaptra {0}",
"FailedLoginAttemptWithUserName": "Assay omgelm fyllys a-dhyworth {0}",
"AuthenticationSucceededWithUserName": "{0} omgelmys yn sewen",
"Default": "Defowt",
- "DeviceOnlineWithName": "{0} yw junys",
- "ItemRemovedWithName": "{0} a veu dileys a-dhyworth an lyverva",
"LabelIpAddressValue": "Trigva PK: {)}",
"Music": "Ilow",
"HeaderContinueWatching": "Pesya Ow Kweles",
@@ -50,8 +35,6 @@
"NotificationOptionCameraImageUploaded": "Skeusen kamera ughkargys",
"HeaderFavoriteShows": "Diskwedhyansow Drudh",
"HeaderLiveTV": "PW Yn Fyw",
- "MessageServerConfigurationUpdated": "Dewisyans servell re beu nowedhys",
- "ItemAddedWithName": "{0} a veu keworrys dhe'n lyverva",
"NameInstallFailed": "{0} ynstallyans fyllys",
"NotificationOptionNewLibraryContent": "Dalgh nowydh keworrys",
"NewVersionIsAvailable": "Yma versyon nowydh a Servell Jellyfin neb yw kavadow rag iskarga.",
@@ -62,8 +45,6 @@
"NotificationOptionServerRestartRequired": "Dastalleth servell yw res",
"StartupEmbyServerIsLoading": "Yma Servell Jellyfin ow kargya. Assay arta yn berr mar pleg.",
"SubtitleDownloadFailureFromForItem": "Istitlow a fyllis iskarga a-dhyworth {0] rag {1}",
- "System": "Kevreyth",
- "User": "Devnydhyer",
"UserDeletedWithName": "Devnydhyer {0} re beu dileys",
"UserLockedOutWithName": "Devnydhyer {0} re beu alhwedhys yn-mes",
"UserStoppedPlayingItemWithValues": "{0} re worfennas gwari {1} war {2}",
@@ -71,21 +52,15 @@
"UserOnlineFromDevice": "{0} yw warlinen a-dhyworth {1}",
"NotificationOptionUserLockedOut": "Devnydhyer yw alhwedhys yn-mes",
"Photos": "Skeusennow",
- "Playlists": "Rolyow-gwari",
- "Plugin": "Ystynnans",
"PluginInstalledWithName": "{0} a veu ynstallys",
- "UserPolicyUpdatedWithName": "Polici devnydhyer re beu nowedhys rag {0}",
"PluginUpdatedWithName": "{0} a veu nowedhys",
"ScheduledTaskFailedWithName": "{0} a fyllis",
- "Songs": "Kanow",
- "Sync": "Kesseni",
"TvShows": "Towlennow PW",
"Undefined": "Anstyrys",
"UserCreatedWithName": "Devnydhyer {0} re beu gwruthys",
"UserDownloadingItemWithValues": "Yma {0} owth iskarga {1}",
"UserPasswordChangedWithName": "Ger-tremena re beu chanjys rag devnydhyer {0}",
"UserStartedPlayingItemWithValues": "Yma {0} ow kwari {1} war {2}",
- "ValueHasBeenAddedToLibrary": "{0} re beu keworrys dhe'th lyverva media",
"VersionNumber": "Versyon {0}",
"TasksLibraryCategory": "Lyverva",
"TaskCleanActivityLog": "Glanhe Kovlyver Gwrians",
@@ -96,10 +71,6 @@
"NotificationOptionVideoPlayback": "Gwareans gwydhyow yw dallethys",
"PluginUninstalledWithName": "{0} a veu anynstallys",
"NotificationOptionTaskFailed": "Defowt oberen towlennys",
- "ProviderValue": "Provier: {0}",
- "ScheduledTaskStartedWithName": "{0} a dhallathas",
- "ServerNameNeedsToBeRestarted": "Yma edhom dhe {0} a vos dastallathys",
- "ValueSpecialEpisodeName": "Arbennik - {0}",
"TasksMaintenanceCategory": "Mentons",
"TasksApplicationCategory": "Gweythres",
"TasksChannelsCategory": "Kanolyow Kesrosweyth",
@@ -128,9 +99,7 @@
"TaskOptimizeDatabaseDescription": "Y hwra kesstrotha ha berrhe efander rydh. Martesen y hwra gwellhe gwryth mar kwre'ta an oberen ma wosa ty dhe arhwilas an lyverva, po neb chanj aral neb a brof chanjyansow selvanylyon.",
"TaskAudioNormalizationDescription": "Y hwra arhwilas restrennow rag manylyon normalheans klewans.",
"TaskRefreshLibraryDescription": "Y hwra arhwilas dha lyverva media rag restrennow nowydh ha disegha metamanylyon.",
- "TaskCleanCollectionsAndPlaylists": "Glanhe kuntellow ha rolyow-gwari",
"TaskKeyframeExtractor": "Estennell Framalhwedh",
- "TaskCleanCollectionsAndPlaylistsDescription": "Y hwra dilea taklow a-dhyworth kuntellow ha rolyow-gwari na vos na moy.",
"TaskKeyframeExtractorDescription": "Y hwra kuntel framyowalhwedh a-dhyworth restrennow gwydhyowyow rag gul rolyow-gwari HLS moy poran. Martesen y hwra an oberen ma ow ponya rag termyn hir.",
"TaskExtractMediaSegments": "Arhwilas Rann Media",
"TaskExtractMediaSegmentsDescription": "Kavos rannow media a-dhyworth ystynansow gallosegys MediaSegment.",
diff --git a/Emby.Server.Implementations/Localization/Core/lb.json b/Emby.Server.Implementations/Localization/Core/lb.json
index 176f2ba2b7..e94709b083 100644
--- a/Emby.Server.Implementations/Localization/Core/lb.json
+++ b/Emby.Server.Implementations/Localization/Core/lb.json
@@ -1,38 +1,24 @@
{
- "Albums": "Alben",
- "Application": "Applikatioun",
"Artists": "Kënschtler",
"Books": "Bicher",
- "Channels": "Kanäl",
"Collections": "Kollektiounen",
"Default": "Standard",
"ChapterNameValue": "Kapitel {0}",
- "DeviceOnlineWithName": "{0} ass Online",
- "DeviceOfflineWithName": "{0} ass Offline",
"External": "Extern",
"Favorites": "Favoritten",
"Folders": "Dossieren",
"Forced": "Forcéiert",
- "HeaderAlbumArtists": "Album Kënschtler",
- "HeaderFavoriteAlbums": "Léifsten Alben",
- "HeaderFavoriteArtists": "Léifsten Kënschtler",
"HeaderFavoriteEpisodes": "Léifsten Episoden",
"HeaderFavoriteShows": "Léifsten Shows",
- "HeaderFavoriteSongs": "Léifsten Lidder",
"Genres": "Generen",
"HeaderContinueWatching": "Weider kucken",
"Inherit": "Iwwerhuelen",
"HeaderNextUp": "Als Nächst",
- "HeaderRecordingGroups": "Opname Gruppen",
"HearingImpaired": "Daaf",
"HomeVideos": "Amateur Videoen",
- "ItemRemovedWithName": "Element ewech geholl: {0}",
"LabelIpAddressValue": "IP Adress: {0}",
"LabelRunningTimeValue": "Lafzäit: {0}",
"Latest": "Dat Aktuellst",
- "MessageApplicationUpdatedTo": "Jellyfin Server aktualiséiert op {0}",
- "MessageNamedServerConfigurationUpdatedWithValue": "Server Konfiguratiounssektioun {0} aktualiséiert",
- "MessageServerConfigurationUpdated": "Server Konfiguratioun aktualiséiert",
"Movies": "Filmer",
"Music": "Musek",
"NameInstallFailed": "{0} Installatioun net gelongen",
@@ -55,19 +41,11 @@
"NotificationOptionUserLockedOut": "Benotzer Gesperrt",
"NotificationOptionVideoPlaybackStopped": "Video ofspillen gestoppt",
"NotificationOptionVideoPlayback": "Video ofspillen gestartet",
- "Plugin": "Plugin",
"PluginUninstalledWithName": "{0} desinstalléiert",
"PluginUpdatedWithName": "{0} aktualiséiert",
- "ProviderValue": "Provider: {0}",
"ScheduledTaskFailedWithName": "Aufgab: {0} net gelongen",
- "Playlists": "Playlëschten",
"Shows": "Shows",
- "Songs": "Lidder",
- "ServerNameNeedsToBeRestarted": "{0} muss nei gestart ginn",
"StartupEmbyServerIsLoading": "Jellyfin Server luedt. Probéier méi spéit nach eng Kéier.",
- "Sync": "Synchroniséieren",
- "System": "System",
- "User": "Benotzer",
"TvShows": "TV Shows",
"Undefined": "Net definéiert",
"UserCreatedWithName": "Benotzer {0} erstellt",
@@ -76,13 +54,10 @@
"UserLockedOutWithName": "Benotzer {0} gesperrt",
"UserOnlineFromDevice": "{0} Benotzer Online um Gerät {1}",
"UserPasswordChangedWithName": "Benotzer Passwuert geännert fir {0}",
- "UserPolicyUpdatedWithName": "Benotzer Politik aktualiséiert fir: {0}",
"UserStartedPlayingItemWithValues": "{0} spillt {1} op {2} oof",
- "ValueHasBeenAddedToLibrary": "{0} der Bibliothéik bäigefüügt",
"VersionNumber": "Versioun {0}",
"TasksMaintenanceCategory": "Ënnerhalt",
"TasksLibraryCategory": "Bibliothéik",
- "ValueSpecialEpisodeName": "Spezial-Episodenumm",
"TasksChannelsCategory": "Internet Kanäl",
"TaskCleanActivityLog": "Aktivitéits Log botzen",
"TaskCleanActivityLogDescription": "Läscht Aktivitéitslogs méi al wéi konfiguréiert.",
@@ -104,22 +79,16 @@
"TaskDownloadMissingSubtitles": "Fehlend Ënnertitelen eroflueden",
"TaskOptimizeDatabase": "Datebank optiméieren",
"TaskKeyframeExtractor": "Schlësselbild Extrakter",
- "TaskCleanCollectionsAndPlaylists": "Sammlungen a Playlisten botzen",
- "TaskCleanCollectionsAndPlaylistsDescription": "Ewechhuele vun Elementer aus Sammlungen a Playlisten, déi net méi existéieren.",
"TaskExtractMediaSegments": "Mediesegment-Scan",
"NewVersionIsAvailable": "Nei Versioun fir Jellyfin Server ass verfügbar.",
- "CameraImageUploadedFrom": "En neit Kamera Bild gouf vu {0} eropgelueden",
"PluginInstalledWithName": "{0} installéiert",
"TaskMoveTrickplayImagesDescription": "Verschëfft existent Trickplay-Dateien no de Bibliothéik-Astellungen.",
"AppDeviceValues": "App: {0}, Geräter: {1}",
"FailedLoginAttemptWithUserName": "Net Gelongen Umeldung {0}",
"HeaderLiveTV": "LiveTV",
- "ItemAddedWithName": "Element derbäi gesat: {0}",
"NotificationOptionServerRestartRequired": "Server Restart Erfuerderlech",
- "ScheduledTaskStartedWithName": "Aufgab: {0} gestart",
"AuthenticationSucceededWithUserName": "{0} Authentifikatioun gelongen",
"MixedContent": "Gemëschten Inhalt",
- "MessageApplicationUpdated": "Jellyfin Server Aktualiséiert",
"SubtitleDownloadFailureFromForItem": "Ënnertitel Download Feeler vun {0} fir {1}",
"TaskCleanLogsDescription": "Läscht Log-Dateien, déi méi al wéi {0} Deeg sinn.",
"TaskUpdatePlugins": "Plugins aktualiséieren",
diff --git a/Emby.Server.Implementations/Localization/Core/lt-LT.json b/Emby.Server.Implementations/Localization/Core/lt-LT.json
index bdf63b4ca0..ed26004a43 100644
--- a/Emby.Server.Implementations/Localization/Core/lt-LT.json
+++ b/Emby.Server.Implementations/Localization/Core/lt-LT.json
@@ -1,41 +1,24 @@
{
- "Albums": "Albumai",
"AppDeviceValues": "Programa: {0}, Įrenginys: {1}",
- "Application": "Programėlė",
"Artists": "Atlikėjai",
"AuthenticationSucceededWithUserName": "{0} sėkmingai autentifikuota",
"Books": "Knygos",
- "CameraImageUploadedFrom": "Nauja nuotrauka įkelta iš kameros {0}",
- "Channels": "Kanalai",
"ChapterNameValue": "Scena{0}",
"Collections": "Rinkiniai",
- "DeviceOfflineWithName": "{0} buvo atjungtas",
- "DeviceOnlineWithName": "{0} prisijungęs",
"FailedLoginAttemptWithUserName": "Nesėkmingas {0} bandymas prisijungti",
"Favorites": "Mėgstami",
"Folders": "Katalogai",
"Genres": "Žanrai",
- "HeaderAlbumArtists": "Albumo atlikėjai",
"HeaderContinueWatching": "Žiūrėti toliau",
- "HeaderFavoriteAlbums": "Mėgstami albumai",
- "HeaderFavoriteArtists": "Mėgstami atlikėjai",
"HeaderFavoriteEpisodes": "Mėgstamiausios serijos",
"HeaderFavoriteShows": "Mėgstamiausios TV Laidos",
- "HeaderFavoriteSongs": "Mėgstamos Dainos",
"HeaderLiveTV": "Tiesioginė TV",
"HeaderNextUp": "Toliau",
- "HeaderRecordingGroups": "Įrašų grupės",
"HomeVideos": "Namų vaizdo įrašai",
"Inherit": "Paveldėti",
- "ItemAddedWithName": "{0} - buvo įkeltas į biblioteką",
- "ItemRemovedWithName": "{0} - buvo pašalinta iš bibliotekos",
"LabelIpAddressValue": "IP adresas: {0}",
"LabelRunningTimeValue": "Trukmė: {0}",
"Latest": "Naujausi",
- "MessageApplicationUpdated": "\"Jellyfin Server\" atnaujintas",
- "MessageApplicationUpdatedTo": "\"Jellyfin Server\" buvo atnaujinta iki {0}",
- "MessageNamedServerConfigurationUpdatedWithValue": "Serverio nustatymai (skyrius {0}) buvo atnaujinti",
- "MessageServerConfigurationUpdated": "Serverio nustatymai buvo atnaujinti",
"MixedContent": "Mišrus turinys",
"Movies": "Filmai",
"Music": "Muzika",
@@ -61,23 +44,14 @@
"NotificationOptionVideoPlayback": "Vaizdo įrašo atkūrimas pradėtas",
"NotificationOptionVideoPlaybackStopped": "Vaizdo įrašo atkūrimas sustabdytas",
"Photos": "Nuotraukos",
- "Playlists": "Grojaraščiai",
- "Plugin": "Įskiepis",
"PluginInstalledWithName": "{0} buvo įdiegtas",
"PluginUninstalledWithName": "{0} buvo pašalintas",
"PluginUpdatedWithName": "{0} buvo atnaujintas",
- "ProviderValue": "Paslaugos tiekėjas: {0}",
"ScheduledTaskFailedWithName": "{0} nepavyko",
- "ScheduledTaskStartedWithName": "{0} paleista",
- "ServerNameNeedsToBeRestarted": "{0} reikia iš naujo paleisti",
"Shows": "Laidos",
- "Songs": "Kūriniai",
"StartupEmbyServerIsLoading": "Jellyfin Server kraunasi. Netrukus pabandykite dar kartą.",
"SubtitleDownloadFailureFromForItem": "{1} subtitrai buvo nesėkmingai parsiųsti iš {0}",
- "Sync": "Sinchronizuoti",
- "System": "Sistema",
"TvShows": "TV laidos",
- "User": "Naudotojas",
"UserCreatedWithName": "Buvo sukurtas {0} naudotojas",
"UserDeletedWithName": "Naudotojas {0} ištrintas",
"UserDownloadingItemWithValues": "{0} siunčiasi {1}",
@@ -85,11 +59,8 @@
"UserOfflineFromDevice": "{0} buvo atjungtas nuo {1}",
"UserOnlineFromDevice": "{0} prisijungęs iš {1}",
"UserPasswordChangedWithName": "Slaptažodis pakeistas naudotojui {0}",
- "UserPolicyUpdatedWithName": "Naudotojo {0} teisės buvo pakeistos",
"UserStartedPlayingItemWithValues": "{0} leidžia {1} į {2}",
"UserStoppedPlayingItemWithValues": "{0} baigė leisti {1} į {2}",
- "ValueHasBeenAddedToLibrary": "{0} pridėtas į mediateką",
- "ValueSpecialEpisodeName": "Ypatingų - {0}",
"VersionNumber": "Versija {0}",
"TaskUpdatePluginsDescription": "Atsisiunčia ir įdiegia įskiepių, kurie sukonfigūruoti atnaujinti automatiškai, naujinius.",
"TaskUpdatePlugins": "Atnaujinti įskieius",
@@ -126,8 +97,6 @@
"HearingImpaired": "Su klausos sutrikimais",
"TaskRefreshTrickplayImages": "Generuoti Trickplay atvaizdus",
"TaskRefreshTrickplayImagesDescription": "Sukuria trickplay peržiūras vaizdo įrašams įgalintose bibliotekose.",
- "TaskCleanCollectionsAndPlaylists": "Išvalo duomenis rinkiniuose ir grojaraščiuose",
- "TaskCleanCollectionsAndPlaylistsDescription": "Pašalina neegzistuojančius elementus iš rinkinių ir grojaraščių.",
"TaskAudioNormalization": "Garso normalizavimas",
"TaskAudioNormalizationDescription": "Skenuoja failus, ieškant garso normalizavimo duomenų.",
"TaskExtractMediaSegments": "Medijos segmentų nuskaitymas",
diff --git a/Emby.Server.Implementations/Localization/Core/lv.json b/Emby.Server.Implementations/Localization/Core/lv.json
index 55549c66d0..4a1b248e76 100644
--- a/Emby.Server.Implementations/Localization/Core/lv.json
+++ b/Emby.Server.Implementations/Localization/Core/lv.json
@@ -1,15 +1,10 @@
{
- "ServerNameNeedsToBeRestarted": "{0} ir vajadzīgs restarts",
"NotificationOptionTaskFailed": "Plānota uzdevuma kļūme",
- "HeaderRecordingGroups": "Ierakstu grupas",
- "UserPolicyUpdatedWithName": "Lietotāju politika atjaunota priekš {0}",
"SubtitleDownloadFailureFromForItem": "Subtitru lejupielāde no {0} priekš {1} neizdevās",
"NotificationOptionVideoPlaybackStopped": "Video atskaņošana apturēta",
"NotificationOptionVideoPlayback": "Video atskaņošana sākta",
"NotificationOptionInstallationFailed": "Instalācija neizdevās",
"AuthenticationSucceededWithUserName": "{0} veiksmīgi autentificējies",
- "ValueSpecialEpisodeName": "Speciālais - {0}",
- "ScheduledTaskStartedWithName": "{0} iesākts",
"ScheduledTaskFailedWithName": "{0} neizdevās",
"Photos": "Attēli",
"NotificationOptionUserLockedOut": "Lietotājs bloķēts",
@@ -17,7 +12,6 @@
"Inherit": "Pārmantot",
"AppDeviceValues": "Lietotne: {0}, Ierīce: {1}",
"VersionNumber": "Versija {0}",
- "ValueHasBeenAddedToLibrary": "{0} tika pievienots jūsu multvides bibliotēkai",
"UserStoppedPlayingItemWithValues": "{0} ir beidzis atskaņot {1} uz {2}",
"UserStartedPlayingItemWithValues": "{0} atskaņo {1} uz {2}",
"UserPasswordChangedWithName": "Lietotāja {0} parole tika nomainīta",
@@ -27,23 +21,16 @@
"UserDownloadingItemWithValues": "{0} lejupielādē {1}",
"UserDeletedWithName": "Lietotājs {0} ir izdzēsts",
"UserCreatedWithName": "Lietotājs {0} ir ticis izveidots",
- "User": "Lietotājs",
"TvShows": "TV raidījumi",
- "Sync": "Sinhronizācija",
- "System": "Sistēma",
"StartupEmbyServerIsLoading": "Jellyfin Serveris lādējas. Lūdzu mēģiniet vēlreiz pēc brīža.",
- "Songs": "Dziesmas",
"Shows": "Šovi",
"PluginUpdatedWithName": "{0} tika atjaunots",
"PluginUninstalledWithName": "{0} tika noņemts",
"PluginInstalledWithName": "{0} tika uzstādīts",
- "Plugin": "Paplašinājums",
- "Playlists": "Atskaņošanas saraksti",
"MixedContent": "Jaukts saturs",
"HomeVideos": "Mājas video",
"HeaderNextUp": "Nākamais",
"ChapterNameValue": "{0}. nodaļa",
- "Application": "Lietotne",
"NotificationOptionServerRestartRequired": "Nepieciešams servera restarts",
"NotificationOptionPluginUpdateInstalled": "Paplašinājuma atjauninājums uzstādīts",
"NotificationOptionPluginUninstalled": "Paplašinājums noņemts",
@@ -62,35 +49,19 @@
"MusicVideos": "Mūzikas video",
"Music": "Mūzika",
"Movies": "Filmas",
- "MessageServerConfigurationUpdated": "Servera konfigurācija ir tikusi atjaunota",
- "MessageNamedServerConfigurationUpdatedWithValue": "Servera konfigurācijas sadaļa {0} tika atjaunota",
- "MessageApplicationUpdatedTo": "Jellyfin Server ir ticis atjaunots uz {0}",
- "MessageApplicationUpdated": "Jellyfin Server ir ticis atjaunots",
"Latest": "Jaunākais",
"LabelIpAddressValue": "IP adrese: {0}",
- "ItemRemovedWithName": "{0} tika noņemts no bibliotēkas",
- "ItemAddedWithName": "{0} tika pievienots bibliotēkai",
"HeaderLiveTV": "Tiešraides TV",
"HeaderContinueWatching": "Turpini skatīties",
- "HeaderAlbumArtists": "Albumu izpildītāji",
"Genres": "Žanri",
"Folders": "Mapes",
"Favorites": "Izlase",
"FailedLoginAttemptWithUserName": "Neizdevies ieiešanas mēģinājums no {0}",
- "DeviceOnlineWithName": "Savienojums ar {0} ir izveidots",
- "DeviceOfflineWithName": "Savienojums ar {0} ir pārtraukts",
"Collections": "Kolekcijas",
- "Channels": "Kanāli",
- "CameraImageUploadedFrom": "Jauns kameras attēls tika augšupielādēts no {0}",
"Books": "Grāmatas",
"Artists": "Izpildītāji",
- "Albums": "Albumi",
- "ProviderValue": "Provider: {0}",
- "HeaderFavoriteSongs": "Dziesmu izlase",
"HeaderFavoriteShows": "Raidījumu izlase",
"HeaderFavoriteEpisodes": "Sēriju izlase",
- "HeaderFavoriteArtists": "Izpildītāju izlase",
- "HeaderFavoriteAlbums": "Albumu izlase",
"TaskCleanCacheDescription": "Nodzēš kešatmiņas datnes, kas vairs nav sistēmai vajadzīgas.",
"TaskRefreshChapterImages": "Izvilkt nodaļu attēlus",
"TasksApplicationCategory": "Lietotne",
@@ -127,9 +98,7 @@
"TaskRefreshTrickplayImages": "Ģenerēt partīšanas attēlus",
"TaskRefreshTrickplayImagesDescription": "Izveido priekšskatījumus videoklipu pārtīšanai iespējotajās bibliotēkās.",
"TaskAudioNormalization": "Audio normalizācija",
- "TaskCleanCollectionsAndPlaylistsDescription": "Noņem vairs neeksistējošus vienumus no kolekcijām un atskaņošanas sarakstiem.",
"TaskAudioNormalizationDescription": "Skanē failus priekš audio normālizācijas informācijas.",
- "TaskCleanCollectionsAndPlaylists": "Notīrīt kolekcijas un atskaņošanas sarakstus",
"TaskExtractMediaSegments": "Multivides segmenta skenēšana",
"TaskExtractMediaSegmentsDescription": "Izvelk vai iegūst multivides segmentus no MediaSegment iespējotiem spraudņiem.",
"TaskMoveTrickplayImages": "Trickplay attēlu pārvietošana",
@@ -137,5 +106,6 @@
"TaskDownloadMissingLyrics": "Lejupielādēt trūkstošos vārdus",
"TaskDownloadMissingLyricsDescription": "Lejupielādēt vārdus dziesmām",
"CleanupUserDataTask": "Lietotāju datu tīrīšanas uzdevums",
- "CleanupUserDataTaskDescription": "Notīra visus lietotāja datus (skatīšanās stāvokļus, favorītu statusi utt.) no medijiem, kas vairs nav pieejami vismaz 90 dienas."
+ "CleanupUserDataTaskDescription": "Notīra visus lietotāja datus (skatīšanās stāvokļus, favorītu statusi utt.) no medijiem, kas vairs nav pieejami vismaz 90 dienas.",
+ "Original": "Oriģināls"
}
diff --git a/Emby.Server.Implementations/Localization/Core/lzh.json b/Emby.Server.Implementations/Localization/Core/lzh.json
index 9fb53e41d5..6b61755d12 100644
--- a/Emby.Server.Implementations/Localization/Core/lzh.json
+++ b/Emby.Server.Implementations/Localization/Core/lzh.json
@@ -1,10 +1,8 @@
{
- "Albums": "辑册",
"Artists": "艺人",
"AuthenticationSucceededWithUserName": "{0} 授之权矣",
"Books": "册",
"Genres": "类",
- "HeaderAlbumArtists": "辑者",
"Favorites": "至爱",
"Folders": "箧",
"HeaderContinueWatching": "接目未竟"
diff --git a/Emby.Server.Implementations/Localization/Core/mi.json b/Emby.Server.Implementations/Localization/Core/mi.json
index 3b20abb369..74fae52dca 100644
--- a/Emby.Server.Implementations/Localization/Core/mi.json
+++ b/Emby.Server.Implementations/Localization/Core/mi.json
@@ -1,9 +1,15 @@
{
- "Albums": "Pukaemi",
"AppDeviceValues": "Taupānga: {0}, Pūrere: {1}",
- "Application": "Taupānga",
"Artists": "Kaiwaiata",
"AuthenticationSucceededWithUserName": "{0} has been successfully authenticated",
"Books": "Ngā pukapuka",
- "CameraImageUploadedFrom": "Kua tuku ake he whakaahua kāmera hou mai i {0}"
+ "Default": "Taunoa",
+ "Collections": "Kohinga",
+ "External": "Waho",
+ "Folders": "Kōpaki",
+ "Forced": "Kaha",
+ "Music": "Waiata",
+ "Movies": "Kiriata",
+ "Latest": "Hou",
+ "Inherit": "Riro"
}
diff --git a/Emby.Server.Implementations/Localization/Core/mk.json b/Emby.Server.Implementations/Localization/Core/mk.json
index 6da31227d7..daf8112eef 100644
--- a/Emby.Server.Implementations/Localization/Core/mk.json
+++ b/Emby.Server.Implementations/Localization/Core/mk.json
@@ -1,11 +1,8 @@
{
"ScheduledTaskFailedWithName": "{0} неуспешно",
- "ProviderValue": "Провајдер: {0}",
"PluginUpdatedWithName": "{0} беше надоградено",
"PluginUninstalledWithName": "{0} беше успешно деинсталирано",
"PluginInstalledWithName": "{0} беше успешно инсталирано",
- "Plugin": "Додатоци",
- "Playlists": "Плејлисти",
"Photos": "Слики",
"NotificationOptionVideoPlaybackStopped": "Видео стопирано",
"NotificationOptionVideoPlayback": "Видео пуштено",
@@ -31,49 +28,29 @@
"Music": "Музика",
"Movies": "Филмови",
"MixedContent": "Мешана содржина",
- "MessageServerConfigurationUpdated": "Серверската конфигурација беше надградена",
- "MessageNamedServerConfigurationUpdatedWithValue": "Секцијата на конфигурација на сервер {0} беше надоградена",
- "MessageApplicationUpdatedTo": "Jellyfin беше надограден до {0}",
- "MessageApplicationUpdated": "Jellyfin Серверот беше надограден",
"Latest": "Последно",
"LabelRunningTimeValue": "Време на работа: {0}",
"LabelIpAddressValue": "ИП Адреса: {0}",
- "ItemRemovedWithName": "{0} е избришано до Библиотеката",
- "ItemAddedWithName": "{0} беше додадено во Библиотеката",
"Inherit": "Следно",
"HomeVideos": "Домашни Видеа",
- "HeaderRecordingGroups": "Групи на снимање",
"HeaderNextUp": "Следно",
"HeaderLiveTV": "ТВ",
- "HeaderFavoriteSongs": "Омилени Песни",
"HeaderFavoriteShows": "Омилени Серии",
"HeaderFavoriteEpisodes": "Омилени Епизоди",
- "HeaderFavoriteArtists": "Омилени Изведувачи",
- "HeaderFavoriteAlbums": "Омилени Албуми",
"HeaderContinueWatching": "Продолжи со Гледање",
- "HeaderAlbumArtists": "Изведувачи од Албуми",
"Genres": "Жанрови",
"Folders": "Папки",
"Favorites": "Омилени",
"FailedLoginAttemptWithUserName": "Неуспешен обид за најавување од {0}",
- "DeviceOnlineWithName": "{0} е приклучен",
- "DeviceOfflineWithName": "{0} се исклучи",
"Collections": "Колекции",
"ChapterNameValue": "Дел {0}",
- "Channels": "Канали",
- "CameraImageUploadedFrom": "Нова слика од камера беше поставена од {0}",
"Books": "Книги",
"AuthenticationSucceededWithUserName": "{0} успешно поврзан",
"Artists": "Изведувачи",
- "Application": "Апликација",
"AppDeviceValues": "Апликација: {0}, Уред: {1}",
- "Albums": "Албуми",
"VersionNumber": "Верзија {0}",
- "ValueSpecialEpisodeName": "Специјално - {0}",
- "ValueHasBeenAddedToLibrary": "{0} е додадено во твојата библиотека",
"UserStoppedPlayingItemWithValues": "{0} заврши со репродукција {1} во {2}",
"UserStartedPlayingItemWithValues": "{0} пушти {1} на {2}",
- "UserPolicyUpdatedWithName": "Полисата на користење беше надоградена за {0}",
"UserPasswordChangedWithName": "Лозинката е сменета за корисникот {0}",
"UserOnlineFromDevice": "{0} е приклучен од {1}",
"UserOfflineFromDevice": "{0} е дисконектиран од {1}",
@@ -81,16 +58,10 @@
"UserDownloadingItemWithValues": "{0} се спушта {1}",
"UserDeletedWithName": "Корисникот {0} е избришан",
"UserCreatedWithName": "Корисникот {0} е креиран",
- "User": "Корисник",
"TvShows": "ТВ Серии",
- "System": "Систем",
- "Sync": "Синхронизација",
"SubtitleDownloadFailureFromForItem": "Преводот неуспешно се спушти од {0} за {1}",
"StartupEmbyServerIsLoading": "Jellyfin Server се пушта. Ве молиме причекајте.",
- "Songs": "Песни",
"Shows": "Серии",
- "ServerNameNeedsToBeRestarted": "{0} треба да се рестартира",
- "ScheduledTaskStartedWithName": "{0} започна",
"TaskRefreshChapterImages": "Извези Слики од Поглавје",
"TaskCleanCacheDescription": "Ги брише кешираните фајлови што не се повеќе потребни од системот.",
"TaskCleanCache": "Исчисти Го Кешот",
@@ -124,13 +95,15 @@
"TaskCleanActivityLog": "Избриши Лог на Активности",
"External": "Надворешен",
"HearingImpaired": "Оштетен слух",
- "TaskCleanCollectionsAndPlaylists": "Исчисти ги колекциите и плејлистите",
"TaskAudioNormalizationDescription": "Скенирање датотеки за податоци за нормализација на звукот.",
"TaskDownloadMissingLyrics": "Преземи стихови кои недостасуваат",
"TaskDownloadMissingLyricsDescription": "Преземи стихови/текстови за песни",
"TaskRefreshTrickplayImages": "Генерирај слики за прегледување (Trickplay)",
"TaskAudioNormalization": "Нормализација на звукот",
"TaskRefreshTrickplayImagesDescription": "Креира трикплеј прегледи за видеа во овозможените библиотеки.",
- "TaskCleanCollectionsAndPlaylistsDescription": "Отстранува ставки од колекциите и плејлистите што веќе не постојат.",
- "TaskExtractMediaSegments": "Скенирање на сегменти на содржина"
+ "TaskExtractMediaSegments": "Скенирање на сегменти на содржина",
+ "TaskMoveTrickplayImages": "Мигрирај ја локацијата на сликата од Trickplay",
+ "TaskMoveTrickplayImagesDescription": "Ги преместува постоечките датотеки за трикплеј според поставките на библиотеката.",
+ "CleanupUserDataTask": "Задача за чистење на кориснички податоци",
+ "CleanupUserDataTaskDescription": "Ги чисти сите кориснички податоци (состојба на гледање, статус на омилени итн.) од медиуми што повеќе не се присутни најмалку 90 дена."
}
diff --git a/Emby.Server.Implementations/Localization/Core/ml.json b/Emby.Server.Implementations/Localization/Core/ml.json
index 8c20ded3ac..dbf2ed4648 100644
--- a/Emby.Server.Implementations/Localization/Core/ml.json
+++ b/Emby.Server.Implementations/Localization/Core/ml.json
@@ -1,32 +1,18 @@
{
"AppDeviceValues": "അപ്ലിക്കേഷൻ: {0}, ഉപകരണം: {1}",
- "Application": "അപ്ലിക്കേഷൻ",
"AuthenticationSucceededWithUserName": "{0} വിജയകരമായി പ്രാമാണീകരിച്ചു",
- "CameraImageUploadedFrom": "{0} എന്നതിൽ നിന്ന് ഒരു പുതിയ ക്യാമറ ചിത്രം അപ്‌ലോഡുചെയ്‌തു",
"ChapterNameValue": "അധ്യായം {0}",
- "DeviceOfflineWithName": "{0} വിച്ഛേദിച്ചു",
- "DeviceOnlineWithName": "{0} ബന്ധിപ്പിച്ചു",
"FailedLoginAttemptWithUserName": "{0}ൽ നിന്നുള്ള പ്രവേശന ശ്രമം പരാജയപ്പെട്ടു",
"Forced": "നിർബന്ധിതമായി",
- "HeaderFavoriteAlbums": "പ്രിയപ്പെട്ട ആൽബങ്ങൾ",
- "HeaderFavoriteArtists": "പ്രിയപ്പെട്ട കലാകാരന്മാർ",
"HeaderFavoriteEpisodes": "പ്രിയപ്പെട്ട എപ്പിസോഡുകൾ",
"HeaderFavoriteShows": "പ്രിയപ്പെട്ട ഷോകൾ",
- "HeaderFavoriteSongs": "പ്രിയപ്പെട്ട ഗാനങ്ങൾ",
"HeaderLiveTV": "തത്സമയ ടിവി",
"HeaderNextUp": "അടുത്തത്",
- "HeaderRecordingGroups": "ഗ്രൂപ്പുകൾ റെക്കോർഡുചെയ്യുന്നു",
"HomeVideos": "ഹോം വീഡിയോകൾ",
"Inherit": "അനന്തരാവകാശം",
- "ItemAddedWithName": "{0} ലൈബ്രറിയിൽ ചേർത്തു",
- "ItemRemovedWithName": "{0} ലൈബ്രറിയിൽ നിന്ന് നീക്കംചെയ്തു",
"LabelIpAddressValue": "IP വിലാസം: {0}",
"LabelRunningTimeValue": "പ്രവർത്തന സമയം: {0}",
"Latest": "ഏറ്റവും പുതിയ",
- "MessageApplicationUpdated": "ജെല്ലിഫിൻ സെർവർ അപ്‌ഡേറ്റുചെയ്‌തു",
- "MessageApplicationUpdatedTo": "ജെല്ലിഫിൻ സെർവർ {0 to ലേക്ക് അപ്‌ഡേറ്റുചെയ്‌തു",
- "MessageNamedServerConfigurationUpdatedWithValue": "സെർവർ കോൺഫിഗറേഷൻ വിഭാഗം {0 അപ്‌ഡേറ്റുചെയ്‌തു",
- "MessageServerConfigurationUpdated": "സെർവർ കോൺഫിഗറേഷൻ അപ്‌ഡേറ്റുചെയ്‌തു",
"MixedContent": "മിശ്രിത ഉള്ളടക്കം",
"Music": "സംഗീതം",
"MusicVideos": "സംഗീത വീഡിയോകൾ",
@@ -50,20 +36,14 @@
"NotificationOptionUserLockedOut": "ഉപയോക്താവ് ലോക്ക് out ട്ട് ചെയ്‌തു",
"NotificationOptionVideoPlayback": "വീഡിയോ പ്ലേബാക്ക് ആരംഭിച്ചു",
"NotificationOptionVideoPlaybackStopped": "വീഡിയോ പ്ലേബാക്ക് നിർത്തി",
- "Plugin": "പ്ലഗിൻ",
"PluginInstalledWithName": "{0} ഇൻസ്റ്റാളുചെയ്‌തു",
"PluginUninstalledWithName": "{0 un അൺഇൻസ്റ്റാൾ ചെയ്തു",
"PluginUpdatedWithName": "{0} അപ്‌ഡേറ്റുചെയ്‌തു",
- "ProviderValue": "ദാതാവ്: {0}",
"ScheduledTaskFailedWithName": "{0} പരാജയപ്പെട്ടു",
- "ScheduledTaskStartedWithName": "{0} ആരംഭിച്ചു",
- "ServerNameNeedsToBeRestarted": "{0} പുനരാരംഭിക്കേണ്ടതുണ്ട്",
"StartupEmbyServerIsLoading": "ജെല്ലിഫിൻ സെർവർ ലോഡുചെയ്യുന്നു. ഉടൻ തന്നെ വീണ്ടും ശ്രമിക്കുക.",
"SubtitleDownloadFailureFromForItem": "സബ്ടൈറ്റിലുകൾ {1} ന് {0 from ൽ നിന്ന് ഡ download ൺലോഡ് ചെയ്യുന്നതിൽ പരാജയപ്പെട്ടു",
- "System": "സിസ്റ്റം",
"TvShows": "ടിവി ഷോകൾ",
"Undefined": "നിർവചിച്ചിട്ടില്ല",
- "User": "ഉപയോക്താവ്",
"UserCreatedWithName": "ഉപയോക്താവ് {0 created സൃഷ്ടിച്ചു",
"UserDeletedWithName": "ഉപയോക്താവ് {0 deleted ഇല്ലാതാക്കി",
"UserDownloadingItemWithValues": "{0} ഡൗൺലോഡുചെയ്യുന്നു {1}",
@@ -71,10 +51,8 @@
"UserOfflineFromDevice": "{0} {1} ൽ നിന്ന് വിച്ഛേദിച്ചു",
"UserOnlineFromDevice": "{0} {1} മുതൽ ഓൺ‌ലൈനിലാണ്",
"UserPasswordChangedWithName": "{0} ഉപയോക്താവിനായി പാസ്‌വേഡ് മാറ്റി",
- "UserPolicyUpdatedWithName": "{0} എന്നതിനായി ഉപയോക്തൃ നയം അപ്‌ഡേറ്റുചെയ്‌തു",
"UserStartedPlayingItemWithValues": "{0} {2} ൽ {1} പ്ലേ ചെയ്യുന്നു",
"UserStoppedPlayingItemWithValues": "{0} {2} ൽ {1 play കളിക്കുന്നത് പൂർത്തിയാക്കി",
- "ValueHasBeenAddedToLibrary": "Media 0 your നിങ്ങളുടെ മീഡിയ ലൈബ്രറിയിലേക്ക് ചേർത്തു",
"VersionNumber": "പതിപ്പ് {0}",
"TasksMaintenanceCategory": "പരിപാലനം",
"TasksLibraryCategory": "പുസ്തകശാല",
@@ -100,16 +78,10 @@
"TaskRefreshChannelsDescription": "ഇന്റർനെറ്റ് ചാനൽ വിവരങ്ങൾ പുതുക്കുന്നു.",
"TaskDownloadMissingSubtitles": "നഷ്‌ടമായ സബ്‌ടൈറ്റിലുകൾ ഡൗൺലോഡുചെയ്യുക",
"TaskDownloadMissingSubtitlesDescription": "മെറ്റാഡാറ്റ കോൺഫിഗറേഷനെ അടിസ്ഥാനമാക്കി നഷ്‌ടമായ സബ്‌ടൈറ്റിലുകൾക്കായി ഇന്റർനെറ്റ് തിരയുന്നു.",
- "ValueSpecialEpisodeName": "പ്രത്യേക - {0}",
"Collections": "ശേഖരങ്ങൾ",
"Folders": "ഫോൾഡറുകൾ",
- "HeaderAlbumArtists": "കലാകാരന്റെ ആൽബം",
- "Sync": "സമന്വയിപ്പിക്കുക",
"Movies": "സിനിമകൾ",
"Photos": "ഫോട്ടോകൾ",
- "Albums": "ആൽബങ്ങൾ",
- "Playlists": "പ്ലേലിസ്റ്റുകൾ",
- "Songs": "ഗാനങ്ങൾ",
"HeaderContinueWatching": "കാണുന്നത് തുടരുക",
"Artists": "കലാകാരന്മാർ",
"Shows": "ഷോകൾ",
@@ -117,17 +89,19 @@
"Favorites": "പ്രിയപ്പെട്ടവ",
"Books": "പുസ്തകങ്ങൾ",
"Genres": "വിഭാഗങ്ങൾ",
- "Channels": "ചാനലുകൾ",
"TaskOptimizeDatabaseDescription": "ഡാറ്റാബേസ് ചുരുക്കുകയും സ്വതന്ത്ര ഇടം വെട്ടിച്ചുരുക്കുകയും ചെയ്യുന്നു. ലൈബ്രറി സ്‌കാൻ ചെയ്‌തതിനുശേഷം അല്ലെങ്കിൽ ഡാറ്റാബേസ് പരിഷ്‌ക്കരണങ്ങളെ സൂചിപ്പിക്കുന്ന മറ്റ് മാറ്റങ്ങൾ ചെയ്‌തതിന് ശേഷം ഈ ടാസ്‌ക് പ്രവർത്തിപ്പിക്കുന്നത് പ്രകടനം മെച്ചപ്പെടുത്തും.",
"TaskOptimizeDatabase": "ഡാറ്റാബേസ് ഒപ്റ്റിമൈസ് ചെയ്യുക",
"HearingImpaired": "കേൾവി തകരാറുകൾ",
"External": "പുറമേയുള്ള",
"TaskKeyframeExtractorDescription": "കൂടുതൽ കൃത്യമായ HLS പ്ലേലിസ്റ്റുകൾ സൃഷ്‌ടിക്കുന്നതിന് വീഡിയോ ഫയലുകളിൽ നിന്ന് കീഫ്രെയിമുകൾ എക്‌സ്‌ട്രാക്‌റ്റ് ചെയ്യുന്നു. ഈ പ്രവർത്തനം പൂർത്തിയാവാൻ കുറച്ചധികം സമയം എടുത്തേക്കാം.",
"TaskKeyframeExtractor": "കീഫ്രെയിം എക്സ്ട്രാക്റ്റർ",
- "TaskCleanCollectionsAndPlaylistsDescription": "നിലവിലില്ലാത്ത ശേഖരങ്ങളിൽ നിന്നും പ്ലേലിസ്റ്റുകളിൽ നിന്നും ഇനങ്ങൾ നീക്കംചെയ്യുന്നു.",
- "TaskCleanCollectionsAndPlaylists": "ശേഖരങ്ങളും പ്ലേലിസ്റ്റുകളും വൃത്തിയാക്കുക",
"TaskAudioNormalization": "സാധാരണ ശബ്ദ നിലയിലെത്തിലെത്തിക്കുക",
"TaskAudioNormalizationDescription": "സാധാരണ ശബ്ദ നിലയിലെത്തിലെത്തിക്കുന്ന ഡാറ്റയ്ക്കായി ഫയലുകൾ സ്കാൻ ചെയ്യുക.",
"TaskRefreshTrickplayImages": "ട്രിക്ക് പ്ലേ ചിത്രങ്ങൾ സൃഷ്ടിക്കുക",
- "TaskRefreshTrickplayImagesDescription": "പ്രവർത്തനക്ഷമമാക്കിയ ലൈബ്രറികളിൽ വീഡിയോകൾക്കായി ട്രിക്ക്പ്ലേ പ്രിവ്യൂകൾ സൃഷ്ടിക്കുന്നു."
+ "TaskRefreshTrickplayImagesDescription": "പ്രവർത്തനക്ഷമമാക്കിയ ലൈബ്രറികളിൽ വീഡിയോകൾക്കായി ട്രിക്ക്പ്ലേ പ്രിവ്യൂകൾ സൃഷ്ടിക്കുന്നു.",
+ "Original": "ഓറിജിനൽ",
+ "TaskDownloadMissingLyrics": "ഇല്ലാത്ത വരികൾ ഡൗൺലോഡ് ചെയ്യുക",
+ "TaskDownloadMissingLyricsDescription": "പാട്ടുകളുടെ വരികൾ ഡൗൺലോഡ് ചെയ്യുന്നു",
+ "TaskExtractMediaSegments": "മീഡിയ സെഗ്‌മെന്റ് സ്‌കാൻ",
+ "TaskExtractMediaSegmentsDescription": "മീഡിയസെഗ്മെന്റ് പ്രാപ്തമാക്കിയ പ്ലഗിനുകളിൽ നിന്ന് മീഡിയ സെഗ്‌മെന്റുകൾ എക്‌സ്‌ട്രാക്റ്റുചെയ്യുന്നു അല്ലെങ്കിൽ നേടുന്നു."
}
diff --git a/Emby.Server.Implementations/Localization/Core/mn.json b/Emby.Server.Implementations/Localization/Core/mn.json
index a684ff2041..83caaf346f 100644
--- a/Emby.Server.Implementations/Localization/Core/mn.json
+++ b/Emby.Server.Implementations/Localization/Core/mn.json
@@ -2,15 +2,12 @@
"Books": "Номнууд",
"HeaderNextUp": "Дараа нь",
"HeaderContinueWatching": "Үргэлжлүүлэн үзэх",
- "Songs": "Дуунууд",
- "Playlists": "Тоглуулах жагсаалтууд",
"Movies": "Кинонууд",
"Latest": "Сүүлийн үеийн",
"Genres": "Төрлүүд",
"Favorites": "Дуртай",
"Collections": "Цуглуулгууд",
"Artists": "Уран бүтээлчид",
- "Albums": "Дуут цомгууд",
"TaskExtractMediaSegments": "Медиа сегмент шалга",
"TaskExtractMediaSegmentsDescription": "MediaSegment идэвхжүүлсэн залгаасуудаас медиа сегментүүдийг задлах эсвэл олж авах.",
"TaskMoveTrickplayImages": "Трикплэй зургуудын байршлыг шилжүүлэх",
@@ -21,20 +18,17 @@
"TaskKeyframeExtractor": "Түлхүүр кадр гаргагч",
"TaskCleanCache": "Кэш санг цэвэрлэх",
"NewVersionIsAvailable": "Jellyfin Server-н шинэ хувилбар татаж авахад нээлттэй боллоо.",
- "MessageNamedServerConfigurationUpdatedWithValue": "Server-н {0}-р хэсгийн тохиргоо шинэчлэгдлээ",
"NotificationOptionAudioPlaybackStopped": "Дууг зогсоов",
"NotificationOptionNewLibraryContent": "Шинэ агуулга орлоо",
"NotificationOptionServerRestartRequired": "Server-г дахин асаана уу",
"NotificationOptionVideoPlaybackStopped": "Бичлэгийг зогсоов",
"UserPasswordChangedWithName": "Хэрэглэгч {0}-н нууц үгийг өөрчиллөө",
- "TaskCleanCollectionsAndPlaylists": "Цуглуулга ба тоглуулах жагсаалтыг цэвэрлэх",
"ScheduledTaskFailedWithName": "{0} амжилтгүй",
"StartupEmbyServerIsLoading": "Jellyfin Server ачааллаж байна. Хэсэг хугацааны дараа дахин оролдоно уу.",
"TaskCleanActivityLog": "Үйл ажиллагааны бүртгэлийг цэвэрлэх",
"SubtitleDownloadFailureFromForItem": "{0}-г {1}-д зориулсан хадмал орчуулгыг татаж авч чадсангүй",
"TaskRefreshLibraryDescription": "Таны медиа санг шинэ файлуудын хувьд шалгаж, мета мэдээллийг шинэчилнэ.",
"UserOfflineFromDevice": "{0}-г {1}-с салгалаа",
- "ValueHasBeenAddedToLibrary": "{0}-г медиа сан руу нэмэгдлээ",
"TaskRefreshPeopleDescription": "Таны медиа санд байгаа жүжигчид болон найруулагчдын мета мэдээллийг шинэчилнэ.",
"TaskCleanTranscodeDescription": "Нэг өдрөөс илүү настай транскодлох файлуудыг устгана.",
"TaskRefreshChannelsDescription": "Интернет сувгуудын мэдээллийг шинэчлэх.",
@@ -44,7 +38,6 @@
"NotificationOptionAudioPlayback": "Дууг тоглууллаа",
"TaskRefreshTrickplayImages": "Трикплэй зургуудыг үүсгэх",
"TaskUpdatePlugins": "Plugin-уудыг шинэчлэх",
- "TaskCleanCollectionsAndPlaylistsDescription": "Одоо байхгүй болсон зүйлсийг цуглуулга ба тоглуулах жагсаалтаас устгана.",
"TaskAudioNormalization": "Аудиог хэвшүүлэх",
"TaskAudioNormalizationDescription": "Файлуудаас дууны хэвийн хэмжээсийн мэдээллийг шалгана.",
"TaskRefreshTrickplayImagesDescription": "Идэвхжсэн сангуудад байгаа видеонуудын трикплэй урьдчилсан харагдацыг үүсгэнэ.",
@@ -53,36 +46,21 @@
"TaskRefreshChannels": "Сувгуудыг шинэчлэх",
"TaskDownloadMissingSubtitles": "Алга болсон хадмал орчуулгыг татах",
"External": "Гадны",
- "HeaderFavoriteArtists": "Дуртай уран бүтээлчид",
"HeaderFavoriteEpisodes": "Дуртай ангиуд",
"HeaderFavoriteShows": "Дуртай нэвтрүүлэг",
- "HeaderFavoriteSongs": "Дуртай дуу",
"AppDeviceValues": "Aпп: {0}, Төхөөрөмж: {1}",
- "Application": "Aпп",
"AuthenticationSucceededWithUserName": "{0} амжилттай нэвтэрлээ",
- "CameraImageUploadedFrom": "{0}-с шинэ зураг байршуулагдлаа",
- "Channels": "Сувгууд",
"ChapterNameValue": "{0}-р бүлэг",
"Default": "Анхдагч",
- "DeviceOfflineWithName": "{0}-н холболт саллаа",
- "DeviceOnlineWithName": "{0} холбогдлоо",
"FailedLoginAttemptWithUserName": "{0}-н нэвтрэх оролдлого амжилтгүй",
"Folders": "Хавтасууд",
"Forced": "Хүчээр",
- "HeaderAlbumArtists": "Цомгийн уран бүтээлчид",
- "HeaderFavoriteAlbums": "Дуртай цомгууд",
"HeaderLiveTV": "Шууд ТВ",
- "HeaderRecordingGroups": "Бичлэгийн бүлгүүд",
"HearingImpaired": "Сонсголын бэрхшээлтэй",
"HomeVideos": "Үндсэн дүрсүүд",
"Inherit": "Уламжлах",
- "ItemAddedWithName": "{0}-г санд нэмлээ",
- "ItemRemovedWithName": "{0}-с сангаас хаслаа",
"LabelIpAddressValue": "IP хаяг: {0}",
"LabelRunningTimeValue": "Үргэлжлэх хугацаа: {0}",
- "MessageApplicationUpdated": "Jellyfin Server шинэчлэгдлээ",
- "MessageApplicationUpdatedTo": "Jellyfin Server {0} болж шинэчлэгдлээ",
- "MessageServerConfigurationUpdated": "Server-н тохиргоо шинэчлэгдлээ",
"MixedContent": "Холимог агуулга",
"Music": "Хөгжим",
"MusicVideos": "Дууны клипүүд",
@@ -101,28 +79,19 @@
"NotificationOptionUserLockedOut": "Хэрэглэгчийг түгжив",
"NotificationOptionVideoPlayback": "Бичлэгийг тоглуулж эхлэв",
"Photos": "Зургууд",
- "Plugin": "Плагин",
"PluginInstalledWithName": "{0}-г суулгалаа",
"PluginUninstalledWithName": "{0}-г устгалаа",
"PluginUpdatedWithName": "{0}-г шинэчиллээ",
- "ProviderValue": "Нийлүүлэгч: {0}",
- "ScheduledTaskStartedWithName": "{0}-г эхлүүлэв",
- "ServerNameNeedsToBeRestarted": "{0}-г дахин асаана уу",
"Shows": "Шоу",
- "Sync": "Синхрончлох",
- "System": "Систем",
"TvShows": "ТВ нэвтрүүлгүүд",
"Undefined": "Танисангүй",
- "User": "Хэрэглэгч",
"UserCreatedWithName": "Хэрэглэгч {0}-г үүсгэлээ",
"UserDeletedWithName": "Хэрэглэгч {0}-г устгалаа",
"UserDownloadingItemWithValues": "{0} нь {1}-г татаж байна",
"UserLockedOutWithName": "Хэрэглэгч {0}-г түгжлээ",
"UserOnlineFromDevice": "{0} нь {1}-тэй холбоотой байна",
- "UserPolicyUpdatedWithName": "Хэрэглэгчийн журмыг {0}-д зориулан шинэчиллээ",
"UserStartedPlayingItemWithValues": "{0}-г {2} дээр {1}-г тоглуулж байна",
"UserStoppedPlayingItemWithValues": "{0}-г {2} дээр {1}-г тоглуулж дуусгалаа",
- "ValueSpecialEpisodeName": "Онцгой - {0}",
"VersionNumber": "Хувилбар {0}",
"TasksMaintenanceCategory": "Засвар",
"TasksLibraryCategory": "Сан",
diff --git a/Emby.Server.Implementations/Localization/Core/mr.json b/Emby.Server.Implementations/Localization/Core/mr.json
index 727bbee168..f169d085d5 100644
--- a/Emby.Server.Implementations/Localization/Core/mr.json
+++ b/Emby.Server.Implementations/Localization/Core/mr.json
@@ -1,22 +1,13 @@
{
"Books": "पुस्तकं",
"Artists": "संगीतकार",
- "Albums": "अल्बम",
- "Playlists": "प्लेलिस्ट",
- "HeaderAlbumArtists": "अल्बम संगीतकार",
"Folders": "फोल्डर",
"HeaderFavoriteEpisodes": "आवडते भाग",
- "HeaderFavoriteSongs": "आवडती गाणी",
"Movies": "चित्रपट",
- "HeaderFavoriteArtists": "आवडते संगीतकार",
"Shows": "कार्यक्रम",
- "HeaderFavoriteAlbums": "आवडते अल्बम",
- "Channels": "वाहिन्या",
- "ValueSpecialEpisodeName": "विशेष - {0}",
"HeaderFavoriteShows": "आवडते कार्यक्रम",
"Favorites": "आवडीचे",
"HeaderNextUp": "यानंतर",
- "Songs": "गाणी",
"HeaderLiveTV": "लाइव्ह टीव्ही",
"Genres": "जाँनरे",
"Photos": "चित्र",
@@ -34,10 +25,8 @@
"UserOnlineFromDevice": "{0} हे {1} येथून ऑनलाइन आहेत",
"UserDeletedWithName": "प्रयोक्ता {0} काढून टाकण्यात आले आहे",
"UserCreatedWithName": "प्रयोक्ता {0} बनवण्यात आले आहे",
- "User": "प्रयोक्ता",
"TvShows": "टीव्ही कार्यक्रम",
"StartupEmbyServerIsLoading": "जेलिफिन सर्व्हर लोड होत आहे. कृपया थोड्या वेळात पुन्हा प्रयत्न करा.",
- "Plugin": "प्लगइन",
"NotificationOptionCameraImageUploaded": "कॅमेरा चित्र अपलोड केले आहे",
"NotificationOptionApplicationUpdateInstalled": "अ‍ॅप्लिकेशन अपडेट इन्स्टॉल केले आहे",
"NotificationOptionApplicationUpdateAvailable": "अ‍ॅप्लिकेशन अपडेट उपलब्ध आहे",
@@ -46,16 +35,9 @@
"NameSeasonNumber": "सीझन {0}",
"MusicVideos": "संगीत व्हिडीयो",
"Music": "संगीत",
- "MessageApplicationUpdatedTo": "जेलिफिन सर्व्हर अपडेट होऊन {0} आवृत्तीवर पोहोचला आहे",
- "MessageApplicationUpdated": "जेलिफिन सर्व्हर अपडेट केला गेला आहे",
"Latest": "नवीनतम",
"LabelIpAddressValue": "आयपी पत्ता: {0}",
- "ItemRemovedWithName": "{0} हे संग्रहालयातून काढून टाकण्यात आले",
- "ItemAddedWithName": "{0} हे संग्रहालयात जोडले गेले",
"HomeVideos": "घरचे व्हिडीयो",
- "HeaderRecordingGroups": "रेकॉर्डिंग गट",
- "CameraImageUploadedFrom": "एक नवीन कॅमेरा चित्र {0} येथून अपलोड केले आहे",
- "Application": "अ‍ॅप्लिकेशन",
"AppDeviceValues": "अ‍ॅप: {0}, यंत्र: {1}",
"Collections": "संग्रह",
"ChapterNameValue": "धडा {0}",
@@ -70,18 +52,12 @@
"TaskRefreshChapterImagesDescription": "अध्याय असलेल्या व्हिडियोंसाठी थंबनेल चित्र बनवतो.",
"TaskRefreshChapterImages": "अध्याय चित्र काढून घ्या",
"TasksMaintenanceCategory": "देखरेख",
- "ValueHasBeenAddedToLibrary": "{0} हे तुमच्या माध्यम संग्रहात जोडण्यात आले आहे",
"UserStoppedPlayingItemWithValues": "{0} यांचं {2} वर {1} पूर्णपणे प्ले करून झालं आहे",
"UserStartedPlayingItemWithValues": "{0} हे {2} वर {1} प्ले करत आहे",
"UserDownloadingItemWithValues": "{0} हे {1} डाउनलोड करत आहे",
- "System": "प्रणाली",
"Undefined": "अव्याख्यात",
- "Sync": "सिंक",
- "ServerNameNeedsToBeRestarted": "{0} याला बंद करून पुन्हा सुरू करायची गरज आहे",
"SubtitleDownloadFailureFromForItem": "{0} येथून {1} यासाठी उपशिर्षक डाउनलोड करण्यात अपयश",
- "ScheduledTaskStartedWithName": "{0} सुरू झाले",
"ScheduledTaskFailedWithName": "{0} अपयशी झाले",
- "ProviderValue": "पुरवणारा: {0}",
"PluginUpdatedWithName": "{0} अपडेट केले",
"PluginUninstalledWithName": "{0} अनिन्स्टॉल केले",
"PluginInstalledWithName": "{0} इन्स्टॉल केले",
@@ -109,24 +85,18 @@
"TaskCleanCacheDescription": "सिस्टमला यापुढे आवश्यक नसलेल्या कॅशे फाइल्स हटवा.",
"TaskCleanActivityLogDescription": "कॉन्फिगर केलेल्या वयापेक्षा जुन्या क्रियाकलाप लॉग एंट्री हटवा.",
"TaskCleanActivityLog": "क्रियाकलाप लॉग साफ करा",
- "UserPolicyUpdatedWithName": "{0} साठी वापरकर्ता धोरण अपडेट केले गेले आहे",
"UserOfflineFromDevice": "{0} {1} वरून डिस्कनेक्ट झाला आहे",
"UserLockedOutWithName": "वापरकर्ता {0} लॉक केले गेले आहे",
"NotificationOptionUserLockedOut": "वापरकर्ता लॉक आउट",
"NameInstallFailed": "{0} स्थापना अयशस्वी",
- "MessageServerConfigurationUpdated": "सर्व्हर कॉन्फिगरेशन अद्यतनित केले आहे",
- "MessageNamedServerConfigurationUpdatedWithValue": "सर्व्हर कॉन्फिगरेशन विभाग {0} अद्यतनित केला गेला आहे",
"Inherit": "वारसा",
"Forced": "सक्ती केली आहे",
"FailedLoginAttemptWithUserName": "{0} कडून लॉगिन करण्याचा प्रयत्न अयशस्वी झाला",
"External": "बाहेरचा",
- "DeviceOnlineWithName": "{0} कनेक्ट झाले",
- "DeviceOfflineWithName": "{0} डिस्कनेक्ट झाला आहे",
"AuthenticationSucceededWithUserName": "{0} यशस्वीरित्या प्रमाणीकृत",
"HearingImpaired": "कर्णबधीर",
"TaskRefreshTrickplayImages": "ट्रिकप्ले प्रतिमा तयार करा",
"TaskRefreshTrickplayImagesDescription": "सक्षम लायब्ररीमधील व्हिडिओंसाठी ट्रिकप्ले पूर्वावलोकन तयार करते.",
- "TaskCleanCollectionsAndPlaylists": "संग्रह आणि प्लेलिस्ट व्यवस्थित करा",
"TaskExtractMediaSegments": "मिडिया विभाग तपासणी",
"TaskMoveTrickplayImages": "ट्रिकप्ले प्रतिमेचे स्थान स्थलांतर करा",
"TaskDownloadMissingLyrics": "उपलब्ध नसलेली गीतपट्टी (Lyrics) डाउनलोड करा",
@@ -135,7 +105,6 @@
"TaskDownloadMissingLyricsDescription": "गाण्यांची गीतपट्टी (Lyrics) डाउनलोड करतो",
"TaskExtractMediaSegmentsDescription": "सक्रिय असलेल्या प्लगिनमधून मीडिया विभाग प्राप्त करते.",
"TaskMoveTrickplayImagesDescription": "लायब्ररीच्या सेटिंग्जप्रमाणे आधीपासून अस्तित्वात असलेल्या ट्रिकप्ले फाइल्सचे स्थान बदलते.",
- "TaskCleanCollectionsAndPlaylistsDescription": "जे संग्रह आणि प्लेलिस्ट आता अस्तित्वात नाहीत, त्यांमधील घटक हटवते.",
"CleanupUserDataTask": "वापरकर्ता डेटाची स्वच्छता प्रक्रिया",
"CleanupUserDataTaskDescription": "९० दिवसांहून अधिक काळ अनुपस्थित असलेल्या माध्यमांवरील सर्व वापरकर्ता माहिती (जसे पाहण्याची स्थिती, आवडी इ.) हटवते."
}
diff --git a/Emby.Server.Implementations/Localization/Core/ms.json b/Emby.Server.Implementations/Localization/Core/ms.json
index 2be04be80a..9fc61fadd5 100644
--- a/Emby.Server.Implementations/Localization/Core/ms.json
+++ b/Emby.Server.Implementations/Localization/Core/ms.json
@@ -1,41 +1,24 @@
{
- "Albums": "Album",
"AppDeviceValues": "Aplikasi: {0}, Peranti: {1}",
- "Application": "Aplikasi",
"Artists": "Artis",
"AuthenticationSucceededWithUserName": "{0} berjaya disahkan",
"Books": "Buku",
- "CameraImageUploadedFrom": "Gambar baharu telah dimuat naik melalui {0}",
- "Channels": "Saluran",
"ChapterNameValue": "Bab {0}",
"Collections": "Koleksi",
- "DeviceOfflineWithName": "{0} telah dinyahsambung",
- "DeviceOnlineWithName": "{0} telah disambung",
"FailedLoginAttemptWithUserName": "Percubaan gagal log masuk daripada {0}",
"Favorites": "Kegemaran",
"Folders": "Folder-folder",
"Genres": "Genre-genre",
- "HeaderAlbumArtists": "Album artis-artis",
"HeaderContinueWatching": "Teruskan Menonton",
- "HeaderFavoriteAlbums": "Album-album Kegemaran",
- "HeaderFavoriteArtists": "Artis-artis Kegemaran",
"HeaderFavoriteEpisodes": "Episod-episod Kegemaran",
"HeaderFavoriteShows": "Rancangan-rancangan Kegemaran",
- "HeaderFavoriteSongs": "Lagu-lagu Kegemaran",
"HeaderLiveTV": "TV Siaran Langsung",
"HeaderNextUp": "Seterusnya",
- "HeaderRecordingGroups": "Kumpulan-kumpulan Rakaman",
"HomeVideos": "Video Peribadi",
"Inherit": "Warisi",
- "ItemAddedWithName": "{0} telah ditambah ke dalam pustaka",
- "ItemRemovedWithName": "{0} telah dibuang daripada pustaka",
"LabelIpAddressValue": "Alamat IP: {0}",
"LabelRunningTimeValue": "Masa berjalan: {0}",
"Latest": "Terbaharu",
- "MessageApplicationUpdated": "Pelayan Jellyfin telah dikemas kini",
- "MessageApplicationUpdatedTo": "Pelayan Jellyfin telah dikemas kini ke {0}",
- "MessageNamedServerConfigurationUpdatedWithValue": "Konfigurasi pelayan bahagian {0} telah dikemas kini",
- "MessageServerConfigurationUpdated": "Konfigurasi pelayan telah dikemas kini",
"MixedContent": "Kandungan campuran",
"Movies": "Filem-filem",
"Music": "Muzik",
@@ -61,23 +44,14 @@
"NotificationOptionVideoPlayback": "Ulangmain video bermula",
"NotificationOptionVideoPlaybackStopped": "Ulangmain video dihentikan",
"Photos": "Gambar-gambar",
- "Playlists": "Senarai ulangmain",
- "Plugin": "Plugin",
"PluginInstalledWithName": "{0} telah dipasang",
"PluginUninstalledWithName": "{0} telah dinyahpasang",
"PluginUpdatedWithName": "{0} telah dikemaskini",
- "ProviderValue": "Pembekal: {0}",
"ScheduledTaskFailedWithName": "{0} gagal",
- "ScheduledTaskStartedWithName": "{0} bermula",
- "ServerNameNeedsToBeRestarted": "{0} perlu di ulangmula",
"Shows": "Tayangan",
- "Songs": "Lagu-lagu",
"StartupEmbyServerIsLoading": "Pelayan Jellyfin sedang dimuatkan. Sila cuba sebentar lagi.",
"SubtitleDownloadFailureFromForItem": "Muat turun sarikata gagal dari {0} untuk {1}",
- "Sync": "Segerak",
- "System": "Sistem",
"TvShows": "Tayangan TV",
- "User": "Pengguna",
"UserCreatedWithName": "Pengguna {0} telah diwujudkan",
"UserDeletedWithName": "Pengguna {0} telah dipadamkan",
"UserDownloadingItemWithValues": "{0} sedang memuat turun {1}",
@@ -85,11 +59,8 @@
"UserOfflineFromDevice": "{0} telah terputus dari {1}",
"UserOnlineFromDevice": "{0} berada dalam talian dari {1}",
"UserPasswordChangedWithName": "Kata laluan telah ditukar bagi pengguna {0}",
- "UserPolicyUpdatedWithName": "Dasar pengguna telah dikemas kini untuk {0}",
"UserStartedPlayingItemWithValues": "{0} sedang dimainkan {1} pada {2}",
"UserStoppedPlayingItemWithValues": "{0} telah tamat dimainkan {1} pada {2}",
- "ValueHasBeenAddedToLibrary": "{0} telah ditambah ke media library anda",
- "ValueSpecialEpisodeName": "Khas - {0}",
"VersionNumber": "Versi {0}",
"TaskCleanActivityLog": "Log Aktiviti Bersih",
"TasksChannelsCategory": "Saluran Internet",
@@ -132,10 +103,8 @@
"TaskDownloadMissingLyrics": "Muat turun lirik yang hilang",
"TaskDownloadMissingLyricsDescription": "Memuat turun lirik-lirik untuk lagu-lagu",
"TaskMoveTrickplayImages": "Alih Lokasi Imej Trickplay",
- "TaskCleanCollectionsAndPlaylists": "Bersihkan koleksi dan senarai audio video",
"TaskAudioNormalization": "Normalisasi Audio",
"TaskAudioNormalizationDescription": "Mengimbas fail-fail untuk data normalisasi audio.",
- "TaskCleanCollectionsAndPlaylistsDescription": "Mengalih keluar item daripada koleksi dan senarai audio video yang tidak wujud lagi.",
"CleanupUserDataTaskDescription": "Membersihkan semua data pengguna (keadaan tontonan, status kegemaran, dan sebagainya) daripada media yang tidak lagi wujud sekurang-kurangnya selama 90 hari.",
"CleanupUserDataTask": "Tugas pembersihan data pengguna"
}
diff --git a/Emby.Server.Implementations/Localization/Core/mt.json b/Emby.Server.Implementations/Localization/Core/mt.json
index f7501ab404..e237a80b70 100644
--- a/Emby.Server.Implementations/Localization/Core/mt.json
+++ b/Emby.Server.Implementations/Localization/Core/mt.json
@@ -1,28 +1,18 @@
{
- "Albums": "Albums",
"AppDeviceValues": "Applikazzjoni: {0}, Device: {1}",
- "Application": "Applikazzjoni",
"Artists": "Artisti",
"AuthenticationSucceededWithUserName": "{1} awtentikat b'suċċess",
"Books": "Kotba",
- "CameraImageUploadedFrom": "Ttella' ritratt ġdid tal-kamera minn {1}",
- "Channels": "Stazzjonijiet",
"ChapterNameValue": "Kapitlu {0}",
"Collections": "Kollezzjonijiet",
- "DeviceOfflineWithName": "{0} tneħħa",
- "DeviceOnlineWithName": "{0} tqabbad",
"External": "Estern",
"FailedLoginAttemptWithUserName": "Attentat fallut ta' login minn {0}",
"Favorites": "Favoriti",
"Forced": "Sfurzat",
"Genres": "Ġeneri",
- "HeaderAlbumArtists": "Artisti tal-album",
"HeaderContinueWatching": "Kompli Ara",
- "HeaderFavoriteAlbums": "Albums Favoriti",
- "HeaderFavoriteArtists": "Artisti Favoriti",
"HeaderFavoriteEpisodes": "Episodji Favoriti",
"HeaderFavoriteShows": "Programmi Favoriti",
- "HeaderFavoriteSongs": "Kanzunetti Favoriti",
"HeaderNextUp": "Li Jmiss",
"SubtitleDownloadFailureFromForItem": "Is-sottotitli ma setgħux jitniżżlu minn {0} għal {1}",
"UserPasswordChangedWithName": "Il-password għall-utent {0} inbidlet",
@@ -32,18 +22,11 @@
"Default": "Standard",
"Folders": "Folders",
"HeaderLiveTV": "TV Dirett",
- "HeaderRecordingGroups": "Gruppi ta' Rikordjar",
"HearingImpaired": "Nuqqas ta' Smigħ",
"HomeVideos": "Filmati Personali",
"Inherit": "Jiret",
- "ItemAddedWithName": "{0} żdied fil-librerija",
- "ItemRemovedWithName": "{0} tneħħa mil-librerija",
"LabelIpAddressValue": "Indirizz tal-IP: {0}",
"Latest": "Tal-Aħħar",
- "MessageApplicationUpdated": "Il-Jellyfin Server ġie aġġornat",
- "MessageApplicationUpdatedTo": "Il-JellyFin Server ġie aġġornat għal {0}",
- "MessageNamedServerConfigurationUpdatedWithValue": "Is-sezzjoni {0} tal-konfigurazzjoni tas-server ġiet aġġornata",
- "MessageServerConfigurationUpdated": "Il-konfigurazzjoni tas-server ġiet aġġornata",
"MixedContent": "Kontenut imħallat",
"Movies": "Films",
"Music": "Mużika",
@@ -67,21 +50,12 @@
"NotificationOptionTaskFailed": "Falliment tat-task skedat",
"NotificationOptionUserLockedOut": "Utent imsakkar",
"Photos": "Ritratti",
- "Playlists": "Playlists",
- "Plugin": "Plugin",
"PluginInstalledWithName": "{0} ġie installat",
"PluginUninstalledWithName": "{0} tneħħa",
"PluginUpdatedWithName": "{0} ġie aġġornat",
- "ProviderValue": "Fornitur: {0}",
"ScheduledTaskFailedWithName": "{0} falla",
- "ScheduledTaskStartedWithName": "{0} beda",
- "ServerNameNeedsToBeRestarted": "{0} jeħtieġ restart",
- "Songs": "Kanzunetti",
"StartupEmbyServerIsLoading": "Jellyfin Server qed jillowdja. Jekk jogħġbok erġa' pprova ftit tal-ħin oħra.",
- "Sync": "Sinkronizza",
- "System": "Sistema",
"Undefined": "Bla Definizzjoni",
- "User": "Utent",
"UserCreatedWithName": "L-utent {0} inħoloq",
"UserDeletedWithName": "L-utent {0} tħassar",
"UserDownloadingItemWithValues": "{0} qed iniżżel {1}",
@@ -93,11 +67,8 @@
"NotificationOptionVideoPlaybackStopped": "Il-playback tal-filmat twaqqaf",
"Shows": "Serje",
"TvShows": "Serje Televiżivi",
- "UserPolicyUpdatedWithName": "Il-politka tal-utent ġiet aġġornata għal {0}",
"UserStartedPlayingItemWithValues": "{0} qed jara {1} fuq {2}",
"UserStoppedPlayingItemWithValues": "{0} waqaf jara {1} fuq {2}",
- "ValueHasBeenAddedToLibrary": "{0} ġie miżjud mal-librerija tal-midja tiegħek",
- "ValueSpecialEpisodeName": "Speċjali - {0}",
"VersionNumber": "Verżjoni {0}",
"TasksMaintenanceCategory": "Manutenzjoni",
"TasksLibraryCategory": "Librerija",
@@ -128,8 +99,6 @@
"TaskOptimizeDatabase": "Ottimiżża d-database",
"TaskKeyframeExtractor": "Estrattur ta' Keyframes",
"TaskKeyframeExtractorDescription": "Jiġbed il-keyframes mill-fajls tal-videos biex jagħmel playlists HLS aktar preċiżi. Dan it-task jista' jdum żmien twil biex ilesti.",
- "TaskCleanCollectionsAndPlaylists": "Naddaf il-kollezzjonijiet u l-playlists",
- "TaskCleanCollectionsAndPlaylistsDescription": "Ineħħi oġġetti minn kollezzjonijiet u playlists li m'għadhomx jeżistu.",
"TaskDownloadMissingLyrics": "Niżżel il-lirika nieqsa",
"TaskDownloadMissingLyricsDescription": "Iniżżel il-lirika għal-kanzunetti",
"TaskExtractMediaSegments": "Scan tas-Sezzjoni tal-Midja",
diff --git a/Emby.Server.Implementations/Localization/Core/my.json b/Emby.Server.Implementations/Localization/Core/my.json
index 097d0d2fba..47728adfc5 100644
--- a/Emby.Server.Implementations/Localization/Core/my.json
+++ b/Emby.Server.Implementations/Localization/Core/my.json
@@ -1,10 +1,8 @@
{
"Default": "ပုံသေ",
"Collections": "စုစည်းမှုများ",
- "Channels": "တီဗွီလိုင်းများ",
"Books": "စာအုပ်များ",
"Artists": "အနုပညာရှင်များ",
- "Albums": "သီချင်းအခွေများ",
"TaskOptimizeDatabaseDescription": "ဒေတာဘေ့စ်ကို ကျစ်လစ်စေပြီး နေရာလွတ်များကို ဖြတ်တောက်ပေးသည်။ စာကြည့်တိုက်ကို စကင်န်ဖတ်ပြီးနောက် ဤလုပ်ငန်းကို လုပ်ဆောင်ခြင်း သို့မဟုတ် ဒေတာဘေ့စ်မွမ်းမံမှုများ စွမ်းဆောင်ရည်ကို မြှင့်တင်ပေးနိုင်သည်ဟု ရည်ညွှန်းသော အခြားပြောင်းလဲမှုများကို လုပ်ဆောင်ခြင်း။.",
"TaskOptimizeDatabase": "ဒေတာဘေ့စ်ကို အကောင်းဆုံးဖြစ်အောင်လုပ်ပါ",
"TaskDownloadMissingSubtitlesDescription": "မက်တာဒေတာ ဖွဲ့စည်းမှုပုံစံအပေါ် အခြေခံ၍ ပျောက်ဆုံးနေသော စာတန်းထိုးများအတွက် အင်တာနက်ကို ရှာဖွေသည်။",
@@ -32,11 +30,8 @@
"TasksLibraryCategory": "မီဒီယာတိုက်",
"TasksMaintenanceCategory": "ပြုပြင် ထိန်းသိမ်းခြင်း",
"VersionNumber": "ဗားရှင်း {0}",
- "ValueSpecialEpisodeName": "အထူး- {0}",
- "ValueHasBeenAddedToLibrary": "{0} ကို သင့်မီဒီယာဒစ်ဂျစ်တိုက်သို့ ပေါင်းထည့်လိုက်ပါပြီ",
"UserStoppedPlayingItemWithValues": "{0} သည် {1} ကို {2} တွင် ဖွင့်ပြီးပါပြီ",
"UserStartedPlayingItemWithValues": "{0} သည် {1} ကို {2} တွင် ပြသနေသည်",
- "UserPolicyUpdatedWithName": "{0} အတွက် အသုံးပြုသူမူဝါဒကို အပ်ဒိတ်လုပ်ပြီးပါပြီ",
"UserPasswordChangedWithName": "အသုံးပြုသူ {0} အတွက် စကားဝှက်ကို ပြောင်းထားသည်",
"UserOnlineFromDevice": "{0} သည် {1} မှ အွန်လိုင်းဖြစ်သည်",
"UserOfflineFromDevice": "{0} သည် {1} မှ ချိတ်ဆက်မှုပြတ်တောက်သွားသည်",
@@ -44,24 +39,15 @@
"UserDownloadingItemWithValues": "{0} သည် {1} ကို ဒေါင်းလုဒ်လုပ်နေသည်",
"UserDeletedWithName": "အသုံးပြုသူ {0} ကို ဖျက်လိုက်ပါပြီ",
"UserCreatedWithName": "အသုံးပြုသူ {0} ကို ဖန်တီးပြီးပါပြီ",
- "User": "အသုံးပြုသူ",
"Undefined": "သတ်မှတ်မထားသော",
"TvShows": "တီဗီ ဇာတ်လမ်းတွဲများ",
- "System": "စနစ်",
- "Sync": "ချိန်ကိုက်မည်",
"SubtitleDownloadFailureFromForItem": "{1} အတွက် {0} မှ စာတန်းထိုးများ ဒေါင်းလုဒ်လုပ်ခြင်း မအောင်မြင်ပါ",
"StartupEmbyServerIsLoading": "Jellyfin ဆာဗာကို အသင့်ပြင်နေပါသည်။ ခဏနေ ထပ်စမ်းကြည့်ပါ။",
- "Songs": "သီချင်းများ",
"Shows": "ဇာတ်လမ်းတွဲများ",
- "ServerNameNeedsToBeRestarted": "{0} ကို ပြန်လည်စတင်ရန် လိုအပ်သည်",
- "ScheduledTaskStartedWithName": "{0} စတင်ခဲ့သည်",
"ScheduledTaskFailedWithName": "{0} မအောင်မြင်ပါ",
- "ProviderValue": "ဝန်ဆောင်မှုပေးသူ- {0}",
"PluginUpdatedWithName": "ပလပ်ခ်အင် {0} ကို အပ်ဒိတ်လုပ်ထားသည်",
"PluginUninstalledWithName": "ပလပ်ခ်အင် {0} ကို ဖြုတ်လိုက်ပါပြီ",
"PluginInstalledWithName": "ပလပ်ခ်အင် {0} ကို ထည့်သွင်းခဲ့သည်",
- "Plugin": "ပလပ်အင်",
- "Playlists": "အစီအစဉ်များ",
"Photos": "ဓာတ်ပုံများ",
"NotificationOptionVideoPlaybackStopped": "ဗီဒီယိုဖွင့်ခြင်း ရပ်သွားသည်",
"NotificationOptionVideoPlayback": "ဗီဒီယိုဖွင့်ခြင်း စတင်ပါပြီ",
@@ -87,45 +73,28 @@
"Music": "တေးဂီတ",
"Movies": "ရုပ်ရှင်များ",
"MixedContent": "ရောနှောပါဝင်မှု",
- "MessageServerConfigurationUpdated": "ဆာဗာဖွဲ့စည်းပုံကို အပ်ဒိတ်လုပ်ပြီးပါပြီ",
- "MessageNamedServerConfigurationUpdatedWithValue": "ဆာဗာဖွဲ့စည်းပုံကဏ္ဍ {0} ကို အပ်ဒိတ်လုပ်ပြီးပါပြီ",
- "MessageApplicationUpdatedTo": "Jellyfin ဆာဗာကို {0} သို့ အပ်ဒိတ်လုပ်ထားသည်",
- "MessageApplicationUpdated": "Jellyfin ဆာဗာကို အပ်ဒိတ်လုပ်ပြီးပါပြီ",
"Latest": "နောက်ဆုံး",
"LabelRunningTimeValue": "ကြာချိန် - {0}",
"LabelIpAddressValue": "IP လိပ်စာ- {0}",
- "ItemRemovedWithName": "{0} ကို ဒစ်ဂျစ်တိုက်မှ ဖယ်ရှားခဲ့သည်",
- "ItemAddedWithName": "{0} ကို စာကြည့်တိုက်သို့ ထည့်ထားသည်",
"Inherit": "ဆက်ခံ၍ လုပ်ဆောင်သည်",
"HomeVideos": "ကိုယ်တိုင်ရိုက် ဗီဒီယိုများ",
- "HeaderRecordingGroups": "အသံဖမ်းအဖွဲ့များ",
"HeaderNextUp": "နောက်ထပ်",
"HeaderLiveTV": "တီဗွီတိုက်ရိုက်",
- "HeaderFavoriteSongs": "အကြိုက်ဆုံးသီချင်းများ",
"HeaderFavoriteShows": "အကြိုက်ဆုံး ဇာတ်လမ်းတွဲများ",
"HeaderFavoriteEpisodes": "အကြိုက်ဆုံး ဇာတ်လမ်းအပိုင်းများ",
- "HeaderFavoriteArtists": "အကြိုက်ဆုံး အနုပညာရှင်များ",
- "HeaderFavoriteAlbums": "အကြိုက်ဆုံး အယ်လ်ဘမ်များ",
"HeaderContinueWatching": "ဆက်လက်ကြည့်ရှုပါ",
- "HeaderAlbumArtists": "အယ်လ်ဘမ်အနုပညာရှင်များ",
"Genres": "အမျိုးအစားများ",
"Forced": "အတင်းအကြပ်",
"Folders": "ဖိုလ်ဒါများ",
"Favorites": "အကြိုက်ဆုံးများ",
"FailedLoginAttemptWithUserName": "{0} မှ အကောင့်ဝင်ရန် မအောင်မြင်ပါ",
- "DeviceOnlineWithName": "{0} ကို ချိတ်ဆက်ထားသည်",
- "DeviceOfflineWithName": "{0} နှင့် အဆက်ပြတ်သွားပါပြီ",
"ChapterNameValue": "အခန်း {0}",
- "CameraImageUploadedFrom": "ကင်မရာပုံအသစ်ကို {0} မှ ထည့်သွင်းလိုက်သည်",
"AuthenticationSucceededWithUserName": "{0} အောင်မြင်စွာ စစ်မှန်ကြောင်း အတည်ပြုပြီးပါပြီ",
- "Application": "အပလီကေးရှင်း",
"AppDeviceValues": "အက်ပ်- {0}၊ စက်- {1}",
"External": "ပြင်ပ",
"TaskKeyframeExtractorDescription": "ပိုမိုတိကျသည့် အိတ်ချ်အယ်လ်အက်စ် အစဉ်လိုက်ပြသမှုများ ဖန်တီးနိုင်ရန်အတွက် ဗီဒီယိုဖိုင်များမှ ကီးဖရိန်များကို ထုတ်နှုတ်ယူမည် ဖြစ်သည်။ ဤလုပ်ဆောင်မှုသည် အချိန်ကြာရှည်နိုင်သည်။",
- "TaskCleanCollectionsAndPlaylistsDescription": "စုစည်းမှုများနှင့် အစဉ်လိုက်ပြသမှုများမှ မရှိတော့သည်များကို ဖယ်ရှားမည်။",
"TaskRefreshTrickplayImages": "ထရစ်ခ်ပလေး ပုံများကို ထုတ်မည်",
"TaskKeyframeExtractor": "ကီးဖရိန်များကို ထုတ်နုတ်ခြင်း",
- "TaskCleanCollectionsAndPlaylists": "စုစည်းမှုများနှင့် အစဉ်လိုက်ပြသမှုများကို ရှင်းလင်းမည်",
"HearingImpaired": "အကြားအာရုံ ချို့တဲ့သူ",
"TaskDownloadMissingLyrics": "ကျန်နေသောသီချင်းစာသားများအား ဒေါင်းလုတ်ဆွဲပါ",
"TaskDownloadMissingLyricsDescription": "သီချင်းများအတွက် သီချင်းစာသား ဒေါင်းလုတ်ဆွဲပါ"
diff --git a/Emby.Server.Implementations/Localization/Core/nb.json b/Emby.Server.Implementations/Localization/Core/nb.json
index cd03157200..752b74ec1c 100644
--- a/Emby.Server.Implementations/Localization/Core/nb.json
+++ b/Emby.Server.Implementations/Localization/Core/nb.json
@@ -1,41 +1,24 @@
{
- "Albums": "Album",
"AppDeviceValues": "App: {0}, Enhet: {1}",
- "Application": "Program",
"Artists": "Artister",
"AuthenticationSucceededWithUserName": "{0} har logget inn",
"Books": "Bøker",
- "CameraImageUploadedFrom": "Et nytt kamerabilde har blitt lastet opp fra {0}",
- "Channels": "Kanaler",
"ChapterNameValue": "Kapittel {0}",
"Collections": "Samlinger",
- "DeviceOfflineWithName": "{0} har koblet fra",
- "DeviceOnlineWithName": "{0} er tilkoblet",
"FailedLoginAttemptWithUserName": "Mislykket påloggingsforsøk fra {0}",
"Favorites": "Favoritter",
"Folders": "Mapper",
"Genres": "Sjangre",
- "HeaderAlbumArtists": "Albumartister",
"HeaderContinueWatching": "Fortsett å se",
- "HeaderFavoriteAlbums": "Favorittalbum",
- "HeaderFavoriteArtists": "Favorittartister",
"HeaderFavoriteEpisodes": "Favorittepisoder",
"HeaderFavoriteShows": "Favorittserier",
- "HeaderFavoriteSongs": "Favorittsanger",
"HeaderLiveTV": "Direkte-TV",
"HeaderNextUp": "Neste",
- "HeaderRecordingGroups": "Opptaksgrupper",
"HomeVideos": "Hjemmelagde filmer",
"Inherit": "Arve",
- "ItemAddedWithName": "{0} ble lagt til i biblioteket",
- "ItemRemovedWithName": "{0} ble fjernet fra biblioteket",
"LabelIpAddressValue": "IP-adresse: {0}",
"LabelRunningTimeValue": "Spilletid: {0}",
"Latest": "Siste",
- "MessageApplicationUpdated": "Jellyfin-serveren har blitt oppdatert",
- "MessageApplicationUpdatedTo": "Jellyfin-serveren ble oppdatert til {0}",
- "MessageNamedServerConfigurationUpdatedWithValue": "Serverkonfigurasjonsseksjon {0} har blitt oppdatert",
- "MessageServerConfigurationUpdated": "Serverkonfigurasjon har blitt oppdatert",
"MixedContent": "Blandet innhold",
"Movies": "Filmer",
"Music": "Musikk",
@@ -61,23 +44,14 @@
"NotificationOptionVideoPlayback": "Videoavspilling startet",
"NotificationOptionVideoPlaybackStopped": "Videoavspilling stoppet",
"Photos": "Bilder",
- "Playlists": "Spillelister",
- "Plugin": "Programvareutvidelse",
"PluginInstalledWithName": "{0} ble installert",
"PluginUninstalledWithName": "{0} ble avinstallert",
"PluginUpdatedWithName": "{0} ble oppdatert",
- "ProviderValue": "Leverandør: {0}",
"ScheduledTaskFailedWithName": "{0} mislykkes",
- "ScheduledTaskStartedWithName": "{0} startet",
- "ServerNameNeedsToBeRestarted": "{0} må startes på nytt",
"Shows": "Serier",
- "Songs": "Sanger",
"StartupEmbyServerIsLoading": "Jellyfin Server laster. Prøv igjen snart.",
"SubtitleDownloadFailureFromForItem": "Kunne ikke laste ned undertekster fra {0} for {1}",
- "Sync": "Synkroniser",
- "System": "System",
"TvShows": "TV-serier",
- "User": "Bruker",
"UserCreatedWithName": "Bruker {0} er opprettet",
"UserDeletedWithName": "Bruker {0} har blitt slettet",
"UserDownloadingItemWithValues": "{0} laster ned {1}",
@@ -85,11 +59,8 @@
"UserOfflineFromDevice": "{0} har koblet fra {1}",
"UserOnlineFromDevice": "{0} er tilkoblet fra {1}",
"UserPasswordChangedWithName": "Passordet for {0} er oppdatert",
- "UserPolicyUpdatedWithName": "Brukerretningslinjene har blitt oppdatert for {0}",
"UserStartedPlayingItemWithValues": "{0} har startet avspilling {1} på {2}",
"UserStoppedPlayingItemWithValues": "{0} har stoppet avspilling {1}",
- "ValueHasBeenAddedToLibrary": "{0} har blitt lagt til i mediebiblioteket ditt",
- "ValueSpecialEpisodeName": "Spesialepisode - {0}",
"VersionNumber": "Versjon {0}",
"TasksChannelsCategory": "Internettkanaler",
"TasksApplicationCategory": "Applikasjon",
@@ -126,10 +97,8 @@
"HearingImpaired": "Hørselshemmet",
"TaskRefreshTrickplayImages": "Generer Trickplay bilder",
"TaskRefreshTrickplayImagesDescription": "Oppretter trickplay-forhåndsvisninger for videoer i aktiverte biblioteker.",
- "TaskCleanCollectionsAndPlaylists": "Rydd kolleksjoner og spillelister",
"TaskAudioNormalization": "Lydnormalisering",
"TaskAudioNormalizationDescription": "Skan filer for lydnormaliserende data.",
- "TaskCleanCollectionsAndPlaylistsDescription": "Fjerner elementer fra kolleksjoner og spillelister som ikke lengere finnes.",
"TaskDownloadMissingLyrics": "Last ned manglende tekster",
"TaskDownloadMissingLyricsDescription": "Last ned sangtekster",
"TaskExtractMediaSegments": "Skann mediasegment",
diff --git a/Emby.Server.Implementations/Localization/Core/ne.json b/Emby.Server.Implementations/Localization/Core/ne.json
index 7c6b08fb36..ad6070c4c3 100644
--- a/Emby.Server.Implementations/Localization/Core/ne.json
+++ b/Emby.Server.Implementations/Localization/Core/ne.json
@@ -21,64 +21,39 @@
"Music": "संगीत",
"Movies": "चलचित्रहरू",
"MixedContent": "मिश्रित सामग्री",
- "MessageServerConfigurationUpdated": "सर्भर कन्फिगरेसन अद्यावधिक गरिएको छ",
- "MessageNamedServerConfigurationUpdatedWithValue": "सर्भर कन्फिगरेसन विभाग {0} अद्यावधिक गरिएको छ",
- "MessageApplicationUpdatedTo": "जेलीफिन सर्भर {0} मा अद्यावधिक गरिएको छ",
- "MessageApplicationUpdated": "जेलीफिन सर्भर अपडेट गरिएको छ",
"Latest": "नविनतम",
"LabelRunningTimeValue": "कुल समय: {0}",
"LabelIpAddressValue": "आईपी ठेगाना: {0}",
- "ItemRemovedWithName": "{0}लाई पुस्तकालयबाट हटाईयो",
- "ItemAddedWithName": "{0} लाईब्रेरीमा थपियो",
"Inherit": "उत्तराधिकार",
"HomeVideos": "घरेलु भिडियोहरू",
- "HeaderRecordingGroups": "रेकर्ड समूहहरू",
"HeaderNextUp": "आगामी",
"HeaderLiveTV": "प्रत्यक्ष टिभी",
- "HeaderFavoriteSongs": "मनपर्ने गीतहरू",
"HeaderFavoriteShows": "मनपर्ने कार्यक्रमहरू",
"HeaderFavoriteEpisodes": "मनपर्ने एपिसोडहरू",
- "HeaderFavoriteArtists": "मनपर्ने कलाकारहरू",
- "HeaderFavoriteAlbums": "मनपर्ने एल्बमहरू",
"HeaderContinueWatching": "हेर्न जारी राख्नुहोस्",
- "HeaderAlbumArtists": "एल्बमका कलाकारहरू",
"Genres": "विधाहरू",
"Folders": "फोल्डरहरू",
"Favorites": "मनपर्ने",
- "FailedLoginAttemptWithUserName": "{0}को लग इन प्रयास असफल",
- "DeviceOnlineWithName": "{0}को साथ जडित",
- "DeviceOfflineWithName": "{0}बाट विच्छेदन भयो",
+ "FailedLoginAttemptWithUserName": "असफल लग इन प्रयास {0} देखि",
"Collections": "संग्रह",
"ChapterNameValue": "अध्याय {0}",
- "Channels": "च्यानलहरू",
"AppDeviceValues": "अनुप्रयोग: {0}, उपकरण: {1}",
"AuthenticationSucceededWithUserName": "{0} सफलतापूर्वक प्रमाणीकरण गरियो",
- "CameraImageUploadedFrom": "{0}बाट नयाँ क्यामेरा छवि अपलोड गरिएको छ",
"Books": "पुस्तकहरु",
"Artists": "कलाकारहरू",
- "Application": "अनुप्रयोगहरू",
- "Albums": "एल्बमहरू",
"TasksLibraryCategory": "पुस्तकालय",
"TasksApplicationCategory": "अनुप्रयोग",
"TasksMaintenanceCategory": "मर्मत",
- "UserPolicyUpdatedWithName": "प्रयोगकर्ता नीति को लागी अद्यावधिक गरिएको छ {0}",
"UserPasswordChangedWithName": "पासवर्ड प्रयोगकर्ताका लागि परिवर्तन गरिएको छ {0}",
"UserOnlineFromDevice": "{0} बाट अनलाइन छ {1}",
"UserOfflineFromDevice": "{0} बाट विच्छेदन भएको छ {1}",
"UserLockedOutWithName": "प्रयोगकर्ता {0} लक गरिएको छ",
"UserDeletedWithName": "प्रयोगकर्ता {0} हटाइएको छ",
"UserCreatedWithName": "प्रयोगकर्ता {0} सिर्जना गरिएको छ",
- "User": "प्रयोगकर्ता",
"PluginInstalledWithName": "{0} सभएको थियो",
"StartupEmbyServerIsLoading": "Jellyfin सर्भर लोड हुँदैछ। कृपया छिट्टै फेरि प्रयास गर्नुहोस्।",
- "Songs": "गीतहरू",
"Shows": "शोहरू",
- "ServerNameNeedsToBeRestarted": "{0} लाई पुन: सुरु गर्नु पर्छ",
- "ScheduledTaskStartedWithName": "{0} सुरु भयो",
"ScheduledTaskFailedWithName": "{0} असफल",
- "ProviderValue": "प्रदायक: {0}",
- "Plugin": "प्लगइनहरू",
- "Playlists": "प्लेलिस्टहरू",
"Photos": "तस्बिरहरु",
"NotificationOptionVideoPlaybackStopped": "भिडियो प्लेब्याक रोकियो",
"NotificationOptionVideoPlayback": "भिडियो प्लेब्याक सुरु भयो",
@@ -98,15 +73,11 @@
"TaskCleanActivityLog": "गतिविधि लग सफा गर्नुहोस्",
"TasksChannelsCategory": "इन्टरनेट च्यानलहरू",
"VersionNumber": "संस्करण {0}",
- "ValueSpecialEpisodeName": "विशेष - {0}",
- "ValueHasBeenAddedToLibrary": "{0} तपाईंको मिडिया लाइब्रेरीमा थपिएको छ",
"UserStoppedPlayingItemWithValues": "{2} मा {0} हेरिसकेको छ{1}",
"UserStartedPlayingItemWithValues": "{0} हेर्दै {1} मा {2}",
"UserDownloadingItemWithValues": "{0} डाउनलोड गर्दै छ {1}",
"Undefined": "अपरिभाषित",
"TvShows": "टेलिभिजन कार्यक्रमहरू",
- "System": "प्रणाली",
- "Sync": "समकालीन",
"SubtitleDownloadFailureFromForItem": "उपशीर्षकहरू {0} बाट {1} को लागि डाउनलोड गर्न असफल",
"PluginUpdatedWithName": "{0} अद्यावधिक गरिएको थियो",
"PluginUninstalledWithName": "{0} को स्थापना रद्द गरिएको थियो",
diff --git a/Emby.Server.Implementations/Localization/Core/nl.json b/Emby.Server.Implementations/Localization/Core/nl.json
index 534c64e93c..9aea3adc22 100644
--- a/Emby.Server.Implementations/Localization/Core/nl.json
+++ b/Emby.Server.Implementations/Localization/Core/nl.json
@@ -1,41 +1,23 @@
{
- "Albums": "Albums",
"AppDeviceValues": "App: {0}, Apparaat: {1}",
- "Application": "Applicatie",
"Artists": "Artiesten",
"AuthenticationSucceededWithUserName": "{0} is succesvol geauthenticeerd",
"Books": "Boeken",
- "CameraImageUploadedFrom": "Nieuwe camera-afbeelding toegevoegd vanaf {0}",
- "Channels": "Kanalen",
"ChapterNameValue": "Hoofdstuk {0}",
"Collections": "Collecties",
- "DeviceOfflineWithName": "Verbinding met {0} is verbroken",
- "DeviceOnlineWithName": "{0} is verbonden",
"FailedLoginAttemptWithUserName": "Mislukte aanmeldpoging van {0}",
"Favorites": "Favorieten",
"Folders": "Mappen",
- "Genres": "Genres",
- "HeaderAlbumArtists": "Albumartiesten",
"HeaderContinueWatching": "Verderkijken",
- "HeaderFavoriteAlbums": "Favoriete albums",
- "HeaderFavoriteArtists": "Favoriete artiesten",
"HeaderFavoriteEpisodes": "Favoriete afleveringen",
"HeaderFavoriteShows": "Favoriete series",
- "HeaderFavoriteSongs": "Favoriete nummers",
"HeaderLiveTV": "Live-tv",
- "HeaderNextUp": "Als volgende",
- "HeaderRecordingGroups": "Opnamegroepen",
+ "HeaderNextUp": "Volgende",
"HomeVideos": "Homevideo's",
- "Inherit": "Erven",
- "ItemAddedWithName": "{0} is toegevoegd aan de bibliotheek",
- "ItemRemovedWithName": "{0} is verwijderd uit de bibliotheek",
+ "Inherit": "Overnemen",
"LabelIpAddressValue": "IP-adres: {0}",
"LabelRunningTimeValue": "Looptijd: {0}",
"Latest": "Nieuwste",
- "MessageApplicationUpdated": "Jellyfin Server is bijgewerkt",
- "MessageApplicationUpdatedTo": "Jellyfin Server is bijgewerkt naar {0}",
- "MessageNamedServerConfigurationUpdatedWithValue": "Sectie {0} van de serverconfiguratie is bijgewerkt",
- "MessageServerConfigurationUpdated": "Serverconfiguratie is bijgewerkt",
"MixedContent": "Gemengde inhoud",
"Movies": "Films",
"Music": "Muziek",
@@ -61,23 +43,14 @@
"NotificationOptionVideoPlayback": "Afspelen van video gestart",
"NotificationOptionVideoPlaybackStopped": "Afspelen van video gestopt",
"Photos": "Foto's",
- "Playlists": "Afspeellijsten",
- "Plugin": "Plug-in",
"PluginInstalledWithName": "{0} is geïnstalleerd",
"PluginUninstalledWithName": "{0} is verwijderd",
"PluginUpdatedWithName": "{0} is bijgewerkt",
- "ProviderValue": "Aanbieder: {0}",
"ScheduledTaskFailedWithName": "{0} is mislukt",
- "ScheduledTaskStartedWithName": "{0} is gestart",
- "ServerNameNeedsToBeRestarted": "{0} moet herstart worden",
"Shows": "Series",
- "Songs": "Nummers",
"StartupEmbyServerIsLoading": "Jellyfin Server is aan het laden. Probeer het later opnieuw.",
"SubtitleDownloadFailureFromForItem": "Ondertiteling kon niet gedownload worden van {0} voor {1}",
- "Sync": "Synchronisatie",
- "System": "Systeem",
"TvShows": "Tv-series",
- "User": "Gebruiker",
"UserCreatedWithName": "Gebruiker {0} is aangemaakt",
"UserDeletedWithName": "Gebruiker {0} is verwijderd",
"UserDownloadingItemWithValues": "{0} downloadt {1}",
@@ -85,11 +58,8 @@
"UserOfflineFromDevice": "Verbinding van {0} via {1} is verbroken",
"UserOnlineFromDevice": "{0} is verbonden via {1}",
"UserPasswordChangedWithName": "Wachtwoord voor {0} is gewijzigd",
- "UserPolicyUpdatedWithName": "Gebruikersbeleid gewijzigd voor {0}",
"UserStartedPlayingItemWithValues": "{0} speelt {1} af op {2}",
"UserStoppedPlayingItemWithValues": "{0} heeft afspelen van {1} gestopt op {2}",
- "ValueHasBeenAddedToLibrary": "{0} is toegevoegd aan je mediabibliotheek",
- "ValueSpecialEpisodeName": "Special - {0}",
"VersionNumber": "Versie {0}",
"TaskDownloadMissingSubtitlesDescription": "Zoekt op het internet naar ontbrekende ondertiteling gebaseerd op metadataconfiguratie.",
"TaskDownloadMissingSubtitles": "Ontbrekende ondertiteling downloaden",
@@ -116,7 +86,7 @@
"TaskCleanActivityLogDescription": "Verwijdert activiteitenlogs ouder dan de ingestelde leeftijd.",
"TaskCleanActivityLog": "Activiteitenlogboek legen",
"Undefined": "Niet gedefinieerd",
- "Forced": "Gedwongen",
+ "Forced": "Geforceerd",
"Default": "Standaard",
"TaskOptimizeDatabaseDescription": "Comprimeert de database en trimt vrije ruimte. Het uitvoeren van deze taak kan de prestaties verbeteren, na het scannen van de bibliotheek of andere aanpassingen die invloed hebben op de database.",
"TaskOptimizeDatabase": "Database optimaliseren",
@@ -126,8 +96,6 @@
"HearingImpaired": "Slechthorenden",
"TaskRefreshTrickplayImages": "Trickplay-afbeeldingen genereren",
"TaskRefreshTrickplayImagesDescription": "Creëert trickplay-voorvertoningen voor video's in bibliotheken waarvoor dit is ingeschakeld.",
- "TaskCleanCollectionsAndPlaylists": "Collecties en afspeellijsten opruimen",
- "TaskCleanCollectionsAndPlaylistsDescription": "Verwijdert niet langer bestaande items uit collecties en afspeellijsten.",
"TaskAudioNormalization": "Geluidsnormalisatie",
"TaskAudioNormalizationDescription": "Scant bestanden op gegevens voor geluidsnormalisatie.",
"TaskDownloadMissingLyrics": "Ontbrekende liedteksten downloaden",
@@ -137,5 +105,8 @@
"TaskMoveTrickplayImagesDescription": "Verplaatst bestaande trickplay-bestanden op basis van de bibliotheekinstellingen.",
"TaskExtractMediaSegments": "Scannen op mediasegmenten",
"CleanupUserDataTaskDescription": "Wist alle gebruikersgegevens (kijkstatus, favorieten, etc.) van media die al minstens 90 dagen niet meer aanwezig zijn.",
- "CleanupUserDataTask": "Opruimtaak gebruikersdata"
+ "CleanupUserDataTask": "Opruimtaak gebruikersdata",
+ "Genres": "Genres",
+ "Original": "Oorspronkelijk",
+ "LyricDownloadFailureFromForItem": "Downloaden van liedteksten voor {1} van {0} mislukt"
}
diff --git a/Emby.Server.Implementations/Localization/Core/nn.json b/Emby.Server.Implementations/Localization/Core/nn.json
index feb5fe2154..8c5da8c1de 100644
--- a/Emby.Server.Implementations/Localization/Core/nn.json
+++ b/Emby.Server.Implementations/Localization/Core/nn.json
@@ -1,41 +1,24 @@
{
- "MessageServerConfigurationUpdated": "Tenarkonfigurasjonen har blitt oppdatert",
- "MessageNamedServerConfigurationUpdatedWithValue": "Tenar konfigurasjon seksjon {0} har blitt oppdatert",
- "MessageApplicationUpdatedTo": "Jellyfin-tenaren har blitt oppdatert til {0}",
- "MessageApplicationUpdated": "Jellyfin-tenaren har blitt oppdatert",
"Latest": "Nyaste",
"LabelRunningTimeValue": "Speletid: {0}",
"LabelIpAddressValue": "IP-adresse: {0}",
- "ItemRemovedWithName": "{0} vart fjerna frå biblioteket",
- "ItemAddedWithName": "{0} vart lagt til i biblioteket",
"Inherit": "Arve",
"HomeVideos": "Heimevideoar",
- "HeaderRecordingGroups": "Innspelingsgrupper",
"HeaderNextUp": "Neste",
"HeaderLiveTV": "Direkte TV",
- "HeaderFavoriteSongs": "Favorittsongar",
"HeaderFavoriteShows": "Favorittseriar",
"HeaderFavoriteEpisodes": "Favorittepisodar",
- "HeaderFavoriteArtists": "Favorittartistar",
- "HeaderFavoriteAlbums": "Favorittalbum",
"HeaderContinueWatching": "Fortsett å sjå",
- "HeaderAlbumArtists": "Albumartist",
"Genres": "Sjangrar",
"Folders": "Mapper",
"Favorites": "Favorittar",
"FailedLoginAttemptWithUserName": "Mislukka påloggingsforsøk frå {0}",
- "DeviceOnlineWithName": "{0} er tilkopla",
- "DeviceOfflineWithName": "{0} har kopla frå",
"Collections": "Samlingar",
"ChapterNameValue": "Kapittel {0}",
- "Channels": "Kanalar",
- "CameraImageUploadedFrom": "Eit nytt kamerabilete har blitt lasta opp frå {0}",
"Books": "Bøker",
"AuthenticationSucceededWithUserName": "{0} har logga inn",
"Artists": "Artistar",
- "Application": "Program",
"AppDeviceValues": "App: {0}, Eining: {1}",
- "Albums": "Album",
"NotificationOptionServerRestartRequired": "Tenaren krev omstart",
"NotificationOptionPluginUpdateInstalled": "Programvaretilleggoppdatering vart installert",
"NotificationOptionPluginUninstalled": "Programvaretillegg avinstallert",
@@ -56,7 +39,6 @@
"Music": "Musikk",
"Movies": "Filmar",
"MixedContent": "Blanda innhald",
- "Sync": "Synkroniser",
"TaskDownloadMissingSubtitlesDescription": "Søk Internettet for manglande undertekstar basert på metadatainnstillingar.",
"TaskDownloadMissingSubtitles": "Last ned manglande undertekstar",
"TaskRefreshChannelsDescription": "Oppdater internettkanalinformasjon.",
@@ -80,11 +62,8 @@
"TasksLibraryCategory": "Bibliotek",
"TasksMaintenanceCategory": "Vedlikehald",
"VersionNumber": "Versjon {0}",
- "ValueSpecialEpisodeName": "Spesialepisode - {0}",
- "ValueHasBeenAddedToLibrary": "{0} har blitt lagt til i mediebiblioteket ditt",
"UserStoppedPlayingItemWithValues": "{0} har fullført avspeling {1} på {2}",
"UserStartedPlayingItemWithValues": "{0} spelar {1} på {2}",
- "UserPolicyUpdatedWithName": "Brukarreglar har blitt oppdatert for {0}",
"UserPasswordChangedWithName": "Passordet for {0} er oppdatert",
"UserOnlineFromDevice": "{0} er direktekopla frå {1}",
"UserOfflineFromDevice": "{0} har kopla frå {1}",
@@ -92,22 +71,14 @@
"UserDownloadingItemWithValues": "{0} lastar ned {1}",
"UserDeletedWithName": "Brukar {0} er sletta",
"UserCreatedWithName": "Brukar {0} er oppretta",
- "User": "Brukar",
"TvShows": "TV-seriar",
- "System": "System",
"SubtitleDownloadFailureFromForItem": "Feila å laste ned undertekstar frå {0} for {1}",
"StartupEmbyServerIsLoading": "Jellyfin-tenaren laster. Prøv igjen seinare.",
- "Songs": "Sangar",
"Shows": "Seriar",
- "ServerNameNeedsToBeRestarted": "{0} må omstartast",
- "ScheduledTaskStartedWithName": "{0} starta",
"ScheduledTaskFailedWithName": "{0} feila",
- "ProviderValue": "Leverandør: {0}",
"PluginUpdatedWithName": "{0} blei oppdatert",
"PluginUninstalledWithName": "{0} blei avinstallert",
"PluginInstalledWithName": "{0} blei installert",
- "Plugin": "Programvaretillegg",
- "Playlists": "Spelelister",
"Photos": "Bilete",
"NotificationOptionVideoPlaybackStopped": "Videoavspeling stoppa",
"NotificationOptionVideoPlayback": "Videoavspeling starta",
diff --git a/Emby.Server.Implementations/Localization/Core/oc.json b/Emby.Server.Implementations/Localization/Core/oc.json
index 0967ef424b..cad5640763 100644
--- a/Emby.Server.Implementations/Localization/Core/oc.json
+++ b/Emby.Server.Implementations/Localization/Core/oc.json
@@ -1 +1,3 @@
-{}
+{
+ "AppDeviceValues": "Aplicacion: {0}, Periferic: {1}"
+}
diff --git a/Emby.Server.Implementations/Localization/Core/or.json b/Emby.Server.Implementations/Localization/Core/or.json
index 8251c12907..febc98d1a4 100644
--- a/Emby.Server.Implementations/Localization/Core/or.json
+++ b/Emby.Server.Implementations/Localization/Core/or.json
@@ -1,11 +1,8 @@
{
"External": "ବହିଃସ୍ଥ",
"Genres": "ଧରଣ",
- "Albums": "ଆଲବମଗୁଡ଼ିକ",
"Artists": "କଳାକାରଗୁଡ଼ିକ",
- "Application": "ଆପ୍ଲିକେସନ",
"Books": "ବହିଗୁଡ଼ିକ",
- "Channels": "ଚ୍ୟାନେଲଗୁଡ଼ିକ",
"ChapterNameValue": "ବିଭାଗ {0}",
"Collections": "ସଂଗ୍ରହଗୁଡ଼ିକ",
"Folders": "ଫୋଲ୍ଡରଗୁଡ଼ିକ"
diff --git a/Emby.Server.Implementations/Localization/Core/pa.json b/Emby.Server.Implementations/Localization/Core/pa.json
index ced9204b46..e609ad52c8 100644
--- a/Emby.Server.Implementations/Localization/Core/pa.json
+++ b/Emby.Server.Implementations/Localization/Core/pa.json
@@ -24,11 +24,8 @@
"TasksLibraryCategory": "ਲਾਇਬ੍ਰੇਰੀ",
"TasksMaintenanceCategory": "ਰੱਖ-ਰਖਾਅ",
"VersionNumber": "ਵਰਜਨ {0}",
- "ValueSpecialEpisodeName": "ਖਾਸ - {0}",
- "ValueHasBeenAddedToLibrary": "{0} ਤੁਹਾਡੀ ਮੀਡੀਆ ਲਾਇਬ੍ਰੇਰੀ ਵਿੱਚ ਸ਼ਾਮਲ ਕੀਤਾ ਗਿਆ ਹੈ",
"UserStoppedPlayingItemWithValues": "{0} ਨੇ {2} 'ਤੇ {1} ਖੇਡਣਾ ਪੂਰਾ ਕਰ ਲਿਆ ਹੈ",
"UserStartedPlayingItemWithValues": "{0} {2} 'ਤੇ {1} ਖੇਡ ਰਿਹਾ ਹੈ",
- "UserPolicyUpdatedWithName": "ਵਰਤੋਂਕਾਰ ਨੀਤੀ ਨੂੰ {0} ਲਈ ਅਪਡੇਟ ਕੀਤਾ ਗਿਆ ਹੈ",
"UserPasswordChangedWithName": "{0} ਵਰਤੋਂਕਾਰ ਲਈ ਪਾਸਵਰਡ ਬਦਲਿਆ ਗਿਆ ਸੀ",
"UserOnlineFromDevice": "{0} ਨੂੰ {1} ਤੋਂ ਆਨਲਾਈਨ ਹੈ",
"UserOfflineFromDevice": "{0} ਤੋਂ ਡਿਸਕਨੈਕਟ ਹੋ ਗਿਆ ਹੈ {1}",
@@ -36,24 +33,15 @@
"UserDownloadingItemWithValues": "{0} {1} ਨੂੰ ਡਾਊਨਲੋਡ ਕਰ ਰਿਹਾ ਹੈ",
"UserDeletedWithName": "ਵਰਤੋਂਕਾਰ {0} ਨੂੰ ਹਟਾਇਆ ਗਿਆ",
"UserCreatedWithName": "ਵਰਤੋਂਕਾਰ {0} ਬਣਾਇਆ ਗਿਆ ਹੈ",
- "User": "ਵਰਤੋਂਕਾਰ",
"Undefined": "ਪਰਿਭਾਸ਼ਤ",
"TvShows": "ਟੀਵੀ ਸ਼ੋਅ",
- "System": "ਸਿਸਟਮ",
- "Sync": "ਸਿੰਕ",
"SubtitleDownloadFailureFromForItem": "ਉਪਸਿਰਲੇਖ {1} ਲਈ {0} ਤੋਂ ਡਾਊਨਲੋਡ ਕਰਨ ਵਿੱਚ ਅਸਫਲ ਰਹੇ",
"StartupEmbyServerIsLoading": "Jellyfin ਸਰਵਰ ਲੋਡ ਹੋ ਰਿਹਾ ਹੈ। ਛੇਤੀ ਹੀ ਫ਼ੇਰ ਕੋਸ਼ਿਸ਼ ਕਰੋ।",
- "Songs": "ਗਾਣੇ",
"Shows": "ਸ਼ੋਅ",
- "ServerNameNeedsToBeRestarted": "{0} ਮੁੜ ਚਾਲੂ ਕਰਨ ਦੀ ਲੋੜ ਹੈ",
- "ScheduledTaskStartedWithName": "{0} ਸ਼ੁਰੂ ਹੋਇਆ",
"ScheduledTaskFailedWithName": "{0} ਅਸਫਲ",
- "ProviderValue": "ਦੇਣ ਵਾਲੇ: {0}",
"PluginUpdatedWithName": "{0} ਅਪਡੇਟ ਕੀਤਾ ਗਿਆ ਸੀ",
"PluginUninstalledWithName": "{0} ਅਣਇੰਸਟੌਲ ਕੀਤਾ ਗਿਆ ਸੀ",
"PluginInstalledWithName": "{0} ਲਗਾਇਆ ਗਿਆ ਸੀ",
- "Plugin": "ਪਲੱਗਇਨ",
- "Playlists": "ਪਲੇਸੂਚੀਆਂ",
"Photos": "ਫੋਟੋਆਂ",
"NotificationOptionVideoPlaybackStopped": "ਵੀਡੀਓ ਪਲੇਬੈਕ ਰੋਕਿਆ ਗਿਆ",
"NotificationOptionVideoPlayback": "ਵੀਡੀਓ ਪਲੇਬੈਕ ਸ਼ੁਰੂ ਹੋਇਆ",
@@ -79,45 +67,28 @@
"Music": "ਸੰਗੀਤ",
"Movies": "ਫਿਲਮਾਂ",
"MixedContent": "ਮਿਸ਼ਰਤ ਸਮੱਗਰੀ",
- "MessageServerConfigurationUpdated": "ਸਰਵਰ ਕੌਂਫਿਗਰੇਸ਼ਨ ਨੂੰ ਅਪਡੇਟ ਕੀਤਾ ਗਿਆ ਹੈ",
- "MessageNamedServerConfigurationUpdatedWithValue": "ਸਰਵਰ ਕੌਂਫਿਗਰੇਸ਼ਨ ਸੈਕਸ਼ਨ {0} ਅਪਡੇਟ ਕੀਤਾ ਗਿਆ ਹੈ",
- "MessageApplicationUpdatedTo": "ਜੈਲੀਫਿਨ ਸਰਵਰ ਨੂੰ ਅੱਪਡੇਟ ਕੀਤਾ ਗਿਆ ਹੈ {0}",
- "MessageApplicationUpdated": "ਜੈਲੀਫਿਨ ਸਰਵਰ ਅਪਡੇਟ ਕੀਤਾ ਗਿਆ ਹੈ",
"Latest": "ਤਾਜ਼ਾ",
"LabelRunningTimeValue": "ਚੱਲਦਾ ਸਮਾਂ: {0}",
"LabelIpAddressValue": "IP ਪਤਾ: {0}",
- "ItemRemovedWithName": "{0} ਲਾਇਬ੍ਰੇਰੀ ਵਿੱਚੋਂ ਹਟਾ ਦਿੱਤਾ ਗਿਆ ਸੀ",
- "ItemAddedWithName": "{0} ਲਾਇਬ੍ਰੇਰੀ ਵਿੱਚ ਸ਼ਾਮਲ ਕੀਤਾ ਗਿਆ ਸੀ",
"Inherit": "ਵਿਰਾਸਤ",
"HomeVideos": "ਘਰੇਲੂ ਵੀਡੀਓ",
- "HeaderRecordingGroups": "ਰਿਕਾਰਡਿੰਗ ਸਮੂਹ",
"HeaderNextUp": "ਅੱਗੇ",
"HeaderLiveTV": "ਲਾਈਵ ਟੀਵੀ",
- "HeaderFavoriteSongs": "ਮਨਪਸੰਦ ਗਾਣੇ",
"HeaderFavoriteShows": "ਮਨਪਸੰਦ ਸ਼ੋਅ",
"HeaderFavoriteEpisodes": "ਮਨਪਸੰਦ ਐਪੀਸੋਡ",
- "HeaderFavoriteArtists": "ਮਨਪਸੰਦ ਕਲਾਕਾਰ",
- "HeaderFavoriteAlbums": "ਮਨਪਸੰਦ ਐਲਬਮ",
"HeaderContinueWatching": "ਵੇਖਣਾ ਜਾਰੀ ਰੱਖੋ",
- "HeaderAlbumArtists": "ਐਲਬਮ ਕਲਾਕਾਰ",
"Genres": "ਸ਼ੈਲੀਆਂ",
"Forced": "ਮਜਬੂਰ",
"Folders": "ਫੋਲਡਰ",
"Favorites": "ਮਨਪਸੰਦ",
"FailedLoginAttemptWithUserName": "{0} ਤੋਂ ਲਾਗਇਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਫੇਲ ਹੋਈ",
- "DeviceOnlineWithName": "{0} ਜੁੜਿਆ ਹੋਇਆ ਹੈ",
- "DeviceOfflineWithName": "{0} ਡਿਸਕਨੈਕਟ ਹੋ ਗਿਆ ਹੈ",
"Default": "ਡਿਫੌਲਟ",
"Collections": "ਸੰਗ੍ਰਹਿਣ",
"ChapterNameValue": "ਚੈਪਟਰ {0}",
- "Channels": "ਚੈਨਲ",
- "CameraImageUploadedFrom": "{0} ਤੋਂ ਇੱਕ ਨਵਾਂ ਕੈਮਰਾ ਚਿੱਤਰ ਅਪਲੋਡ ਕੀਤਾ ਗਿਆ ਹੈ",
"Books": "ਕਿਤਾਬਾਂ",
"AuthenticationSucceededWithUserName": "{0} ਸਫਲਤਾਪੂਰਕ ਪ੍ਰਮਾਣਿਤ",
"Artists": "ਕਲਾਕਾਰ",
- "Application": "ਐਪਲੀਕੇਸ਼ਨ",
"AppDeviceValues": "ਐਪ: {0}, ਜੰਤਰ: {1}",
- "Albums": "ਐਲਬਮਾਂ",
"TaskOptimizeDatabase": "ਡਾਟਾਬੇਸ ਅਨੁਕੂਲ ਬਣਾਓ",
"External": "ਬਾਹਰੀ",
"HearingImpaired": "ਸੁਨਣ ਵਿਚ ਕਮਜ਼ੋਰ",
@@ -131,8 +102,6 @@
"TaskDownloadMissingLyrics": "ਅਧੂਰੇ ਬੋਲ ਡਾਊਨਲੋਡ ਕਰੋ",
"TaskDownloadMissingLyricsDescription": "ਗੀਤਾਂ ਲਈ ਡਾਊਨਲੋਡ ਕਿਤੇ ਬੋਲ",
"TaskKeyframeExtractor": "ਕੀ-ਫ੍ਰੇਮ ਐਕਸਟ੍ਰੈਕਟਰ",
- "TaskCleanCollectionsAndPlaylistsDescription": "ਕਲੈਕਸ਼ਨਾਂ ਅਤੇ ਪਲੇਲਿਸਟਾਂ ਵਿੱਚੋਂ ਉਹ ਆਈਟਮ ਹਟਾਉਂਦਾ ਹੈ ਜੋ ਹੁਣ ਮੌਜੂਦ ਨਹੀਂ ਹਨ।",
- "TaskCleanCollectionsAndPlaylists": "ਕਲੈਕਸ਼ਨਾਂ ਅਤੇ ਪਲੇਲਿਸਟਾਂ ਨੂੰ ਸਾਫ ਕਰੋ",
"TaskAudioNormalization": "ਆਵਾਜ਼ ਸਧਾਰਣੀਕਰਨ",
"TaskRefreshTrickplayImagesDescription": "ਵੀਡੀਓ ਲਈ ਟ੍ਰਿਕਪਲੇ ਪ੍ਰੀਵਿਊ ਬਣਾਉਂਦਾ ਹੈ (ਜੇ ਲਾਇਬ੍ਰੇਰੀ ਵਿੱਚ ਚੁਣਿਆ ਗਿਆ ਹੈ)।",
"TaskKeyframeExtractorDescription": "ਕੀ-ਫ੍ਰੇਮਜ਼ ਨੂੰ ਵੀਡੀਓ ਫਾਈਲਾਂ ਵਿੱਚੋਂ ਨਿਕਾਲਦਾ ਹੈ ਤਾਂ ਜੋ ਹੋਰ ਜ਼ਿਆਦਾ ਸਟਿਕ ਹੋਣ ਵਾਲੀਆਂ HLS ਪਲੇਲਿਸਟਾਂ ਬਣਾਈਆਂ ਜਾ ਸਕਣ। ਇਹ ਕੰਮ ਲੰਬੇ ਸਮੇਂ ਤੱਕ ਚੱਲ ਸਕਦਾ ਹੈ।",
diff --git a/Emby.Server.Implementations/Localization/Core/pl.json b/Emby.Server.Implementations/Localization/Core/pl.json
index f1c19ac1d9..c4657bdd6e 100644
--- a/Emby.Server.Implementations/Localization/Core/pl.json
+++ b/Emby.Server.Implementations/Localization/Core/pl.json
@@ -1,41 +1,24 @@
{
- "Albums": "Albumy",
"AppDeviceValues": "Aplikacja: {0}, Urządzenie: {1}",
- "Application": "Aplikacja",
"Artists": "Wykonawcy",
"AuthenticationSucceededWithUserName": "{0} został pomyślnie uwierzytelniony",
"Books": "Książki",
- "CameraImageUploadedFrom": "Nowy obraz został przekazany z {0}",
- "Channels": "Kanały",
"ChapterNameValue": "Rozdział {0}",
"Collections": "Kolekcje",
- "DeviceOfflineWithName": "{0} został rozłączony",
- "DeviceOnlineWithName": "{0} połączył się",
"FailedLoginAttemptWithUserName": "Nieudana próba logowania przez {0}",
"Favorites": "Ulubione",
"Folders": "Foldery",
"Genres": "Gatunki",
- "HeaderAlbumArtists": "Wykonawcy albumów",
"HeaderContinueWatching": "Kontynuuj odtwarzanie",
- "HeaderFavoriteAlbums": "Ulubione albumy",
- "HeaderFavoriteArtists": "Ulubieni wykonawcy",
"HeaderFavoriteEpisodes": "Ulubione odcinki",
"HeaderFavoriteShows": "Ulubione seriale",
- "HeaderFavoriteSongs": "Ulubione utwory",
"HeaderLiveTV": "Telewizja",
"HeaderNextUp": "Do obejrzenia",
- "HeaderRecordingGroups": "Grupy nagrań",
"HomeVideos": "Nagrania domowe",
"Inherit": "Dziedzicz",
- "ItemAddedWithName": "{0} zostało dodane do biblioteki",
- "ItemRemovedWithName": "{0} zostało usunięte z biblioteki",
"LabelIpAddressValue": "Adres IP: {0}",
"LabelRunningTimeValue": "Czas trwania: {0}",
"Latest": "Ostatnio dodane",
- "MessageApplicationUpdated": "Serwer Jellyfin został zaktualizowany",
- "MessageApplicationUpdatedTo": "Serwer Jellyfin został zaktualizowany do wersji {0}",
- "MessageNamedServerConfigurationUpdatedWithValue": "Sekcja {0} konfiguracji serwera została zaktualizowana",
- "MessageServerConfigurationUpdated": "Konfiguracja serwera została zaktualizowana",
"MixedContent": "Zawartość mieszana",
"Movies": "Filmy",
"Music": "Muzyka",
@@ -61,23 +44,14 @@
"NotificationOptionVideoPlayback": "Rozpoczęto odtwarzanie wideo",
"NotificationOptionVideoPlaybackStopped": "Zatrzymano odtwarzanie wideo",
"Photos": "Zdjęcia",
- "Playlists": "Listy odtwarzania",
- "Plugin": "Wtyczka",
"PluginInstalledWithName": "{0} zostało zainstalowane",
"PluginUninstalledWithName": "{0} odinstalowane",
"PluginUpdatedWithName": "{0} zaktualizowane",
- "ProviderValue": "Dostawca: {0}",
"ScheduledTaskFailedWithName": "Nieudane {0}",
- "ScheduledTaskStartedWithName": "Rozpoczęto {0}",
- "ServerNameNeedsToBeRestarted": "{0} wymaga ponownego uruchomienia",
"Shows": "Seriale",
- "Songs": "Utwory",
"StartupEmbyServerIsLoading": "Trwa wczytywanie serwera Jellyfin. Spróbuj ponownie za chwilę.",
"SubtitleDownloadFailureFromForItem": "Nieudane pobieranie napisów z {0} dla {1}",
- "Sync": "Synchronizacja",
- "System": "System",
"TvShows": "Seriale",
- "User": "Użytkownik",
"UserCreatedWithName": "Użytkownik {0} został utworzony",
"UserDeletedWithName": "Użytkownik {0} został usunięty",
"UserDownloadingItemWithValues": "{0} pobiera {1}",
@@ -85,11 +59,8 @@
"UserOfflineFromDevice": "{0} z {1} został rozłączony",
"UserOnlineFromDevice": "{0} połączył się z {1}",
"UserPasswordChangedWithName": "Hasło użytkownika {0} zostało zmienione",
- "UserPolicyUpdatedWithName": "Zmieniono zasady użytkowania dla {0}",
"UserStartedPlayingItemWithValues": "{0} odtwarza {1} na {2}",
"UserStoppedPlayingItemWithValues": "{0} zakończył odtwarzanie {1} na {2}",
- "ValueHasBeenAddedToLibrary": "{0} został dodany do biblioteki mediów",
- "ValueSpecialEpisodeName": "Specjalne - {0}",
"VersionNumber": "Wersja {0}",
"TaskDownloadMissingSubtitlesDescription": "Przeszukuje internet w poszukiwaniu brakujących napisów w oparciu o konfigurację metadanych.",
"TaskDownloadMissingSubtitles": "Pobierz brakujące napisy",
@@ -126,8 +97,6 @@
"HearingImpaired": "Niedosłyszący",
"TaskRefreshTrickplayImages": "Generuj obrazy Trickplay",
"TaskRefreshTrickplayImagesDescription": "Tworzy podglądy Trickplay dla filmów we włączonych bibliotekach.",
- "TaskCleanCollectionsAndPlaylistsDescription": "Usuwa elementy z kolekcji i list odtwarzania, które już nie istnieją.",
- "TaskCleanCollectionsAndPlaylists": "Oczyść kolekcje i listy odtwarzania",
"TaskAudioNormalization": "Normalizacja dźwięku",
"TaskAudioNormalizationDescription": "Skanuje pliki w poszukiwaniu danych normalizacji dźwięku.",
"TaskDownloadMissingLyrics": "Pobierz brakujące słowa",
@@ -137,5 +106,7 @@
"TaskExtractMediaSegmentsDescription": "Wyodrębnia lub pobiera segmenty mediów z wtyczek obsługujących MediaSegment.",
"TaskMoveTrickplayImagesDescription": "Przenosi istniejące pliki Trickplay zgodnie z ustawieniami biblioteki.",
"CleanupUserDataTaskDescription": "Usuwa wszystkie dane użytkownika (stan oglądanych, status ulubionych itp.) z mediów, które nie są dostępne od co najmniej 90 dni.",
- "CleanupUserDataTask": "Zadanie czyszczenia danych użytkownika"
+ "CleanupUserDataTask": "Zadanie czyszczenia danych użytkownika",
+ "Original": "Oryginalny",
+ "LyricDownloadFailureFromForItem": "Błąd podczas pobierania tekstu piosenki z {0} dla {1}"
}
diff --git a/Emby.Server.Implementations/Localization/Core/pr.json b/Emby.Server.Implementations/Localization/Core/pr.json
index fee7e65f1d..912f5c876a 100644
--- a/Emby.Server.Implementations/Localization/Core/pr.json
+++ b/Emby.Server.Implementations/Localization/Core/pr.json
@@ -2,48 +2,31 @@
"Books": "Scrolls",
"AuthenticationSucceededWithUserName": "{0} passed yer trial",
"Artists": "Artistas",
- "Songs": "Shantees",
- "Albums": "Tomes",
"Photos": "Paintings",
"NotificationOptionUserLockedOut": "Crewmate sent to the brig",
"HeaderContinueWatching": "Continue Yer Journey",
"Folders": "Chests",
- "Application": "Captain",
- "DeviceOnlineWithName": "{0} joined yer crew",
- "DeviceOfflineWithName": "{0} abandoned ship",
"AppDeviceValues": "Captain: {0}, Ship: {1}",
- "CameraImageUploadedFrom": "Yer looking glass has glimpsed another painting from {0}",
"Collections": "Barrels",
- "ItemAddedWithName": "{0} is now with yer treasure",
"Default": "Normal-like",
"FailedLoginAttemptWithUserName": "Ye failed to enter from {0}",
"Favorites": "Finest Loot",
- "ItemRemovedWithName": "{0} was taken from yer treasure",
"LabelIpAddressValue": "Ship's coordinates: {0}",
"Genres": "types o' booty",
"TaskDownloadMissingSubtitlesDescription": "Scours the seven seas o' the internet for subtitles that be missin' based on the captain's map o' metadata.",
- "HeaderAlbumArtists": "Buccaneers o' the musical arts",
- "HeaderFavoriteAlbums": "Beloved booty o' musical adventures",
- "HeaderFavoriteArtists": "Treasured scallywags o' the creative seas",
- "Channels": "Channels",
"Forced": "Pressed",
"External": "Outboard",
"HeaderFavoriteEpisodes": "Treasured Tales",
"HeaderFavoriteShows": "Treasured Tales",
"ChapterNameValue": "Piece {0}",
- "HeaderFavoriteSongs": "Treasured Chimes",
"HeaderNextUp": "Incoming",
"HeaderLiveTV": "Scrying Glass",
"HearingImpaired": "Hard o' Hearing",
"LabelRunningTimeValue": "Journey duration: {0}",
- "MessageApplicationUpdated": "Yer Map of the Seas has been scribbled",
"HomeVideos": "Yer Onboard Booty",
"MixedContent": "Jumbled loot",
"Music": "Tunes",
"NameInstallFailed": "Ye couldn't bring {0} aboard yer ship",
- "MessageApplicationUpdatedTo": "Yer Map of the Seas has been scribbled with {0}",
- "MessageNamedServerConfigurationUpdatedWithValue": "Yer Map Drawer has been rescribbled to {0}",
- "MessageServerConfigurationUpdated": "Yer Map drawer has been rescribbled",
"Inherit": "Carry on what be passed along",
"Latest": "Newfangled",
"Movies": "Moving pictures",
@@ -55,17 +38,13 @@
"UserOfflineFromDevice": "{0} severed ties with {1}",
"UserDownloadingItemWithValues": "{0} be haulin’ in {1}",
"UserStartedPlayingItemWithValues": "{0} be playin’ {1} aboard {2}",
- "ValueHasBeenAddedToLibrary": "{0} be stashed in yer treasure trove",
"TaskCleanCacheDescription": "Wipes away cache cargo no longer called fer.",
"TaskCleanLogsDescription": "Clears the logbook o’ entries older than {0} days.",
"TaskRefreshPeopleDescription": "Refreshes the charts fer actors an’ directors in yer Treasure Trove.",
"UserLockedOutWithName": "Matey {0} be denied boarding",
"TaskAudioNormalization": "Steadyin’ the shanties",
"TaskAudioNormalizationDescription": "Scans files fer shanty steadiyin’ data.",
- "HeaderRecordingGroups": "Loggin' Groups",
"MusicVideos": "Shanty films",
- "Playlists": "Lists o’ plunder",
- "Plugin": "Extra sail",
"NotificationOptionVideoPlaybackStopped": "Video playback dropped anchor",
"NameSeasonNumber": "Saga {0}",
"NameSeasonUnknown": "Saga be Lost",
@@ -87,23 +66,15 @@
"TaskRefreshPeople": "Freshen the Mateys",
"PluginUninstalledWithName": "{0} sent t’ Davy Jones",
"PluginUpdatedWithName": "{0} patched ‘n ready",
- "ProviderValue": "Supplier o’ goods: {0}",
- "ScheduledTaskStartedWithName": "{0} set sail",
- "ServerNameNeedsToBeRestarted": "{0} be cravin’ a restart",
"Shows": "Sagas",
"SubtitleDownloadFailureFromForItem": "Subtitles be sunk fetchin’ from {0} fer {1}",
- "Sync": "Match the tides",
- "System": "The ship’s works",
"TvShows": "TV Sagas",
"Undefined": "Uncharted",
- "User": "Matey",
"UserCreatedWithName": "Matey {0} joined the crew",
"UserDeletedWithName": "Matey {0} cast overboard",
"UserOnlineFromDevice": "{0} be aboard ship from {1}",
"UserPasswordChangedWithName": "New passphrase set fer Matey {0}",
- "UserPolicyUpdatedWithName": "Ship rules be changed fer {0}",
"UserStoppedPlayingItemWithValues": "{0} be done playin’ {1} on {2",
- "ValueSpecialEpisodeName": "Special Tale – {0}",
"VersionNumber": "Edition {0}",
"TasksMaintenanceCategory": "Hull patchin’",
"TasksLibraryCategory": "Treasure Trove",
diff --git a/Emby.Server.Implementations/Localization/Core/pt-BR.json b/Emby.Server.Implementations/Localization/Core/pt-BR.json
index 8e76c6c63c..1db500adf3 100644
--- a/Emby.Server.Implementations/Localization/Core/pt-BR.json
+++ b/Emby.Server.Implementations/Localization/Core/pt-BR.json
@@ -1,41 +1,24 @@
{
- "Albums": "Álbuns",
"AppDeviceValues": "App: {0}, Dispositivo: {1}",
- "Application": "Aplicativo",
"Artists": "Artistas",
"AuthenticationSucceededWithUserName": "{0} autenticado com sucesso",
"Books": "Livros",
- "CameraImageUploadedFrom": "Uma nova imagem da câmera foi enviada de {0}",
- "Channels": "Canais",
"ChapterNameValue": "Capítulo {0}",
"Collections": "Coleções",
- "DeviceOfflineWithName": "{0} se desconectou",
- "DeviceOnlineWithName": "{0} se conectou",
"FailedLoginAttemptWithUserName": "Falha na tentativa de login de {0}",
"Favorites": "Favoritos",
"Folders": "Pastas",
"Genres": "Gêneros",
- "HeaderAlbumArtists": "Artistas do Álbum",
"HeaderContinueWatching": "Continuar assistindo",
- "HeaderFavoriteAlbums": "Álbuns Favoritos",
- "HeaderFavoriteArtists": "Artistas favoritos",
"HeaderFavoriteEpisodes": "Episódios favoritos",
"HeaderFavoriteShows": "Séries favoritas",
- "HeaderFavoriteSongs": "Músicas favoritas",
"HeaderLiveTV": "TV ao Vivo",
"HeaderNextUp": "A Seguir",
- "HeaderRecordingGroups": "Grupos de Gravação",
"HomeVideos": "Vídeos caseiros",
"Inherit": "Herdar",
- "ItemAddedWithName": "{0} foi adicionado à biblioteca",
- "ItemRemovedWithName": "{0} foi removido da biblioteca",
"LabelIpAddressValue": "Endereço IP: {0}",
"LabelRunningTimeValue": "Tempo de execução: {0}",
"Latest": "Recentes",
- "MessageApplicationUpdated": "Servidor Jellyfin atualizado",
- "MessageApplicationUpdatedTo": "Servidor Jellyfin atualizado para {0}",
- "MessageNamedServerConfigurationUpdatedWithValue": "A seção {0} da configuração do servidor foi atualizada",
- "MessageServerConfigurationUpdated": "A configuração do servidor foi atualizada",
"MixedContent": "Conteúdo misto",
"Movies": "Filmes",
"Music": "Música",
@@ -61,23 +44,14 @@
"NotificationOptionVideoPlayback": "Reprodução de vídeo iniciada",
"NotificationOptionVideoPlaybackStopped": "Reprodução de vídeo parada",
"Photos": "Fotos",
- "Playlists": "Listas de Reprodução",
- "Plugin": "Plugin",
"PluginInstalledWithName": "{0} foi instalado",
"PluginUninstalledWithName": "{0} foi desinstalado",
"PluginUpdatedWithName": "{0} foi atualizado",
- "ProviderValue": "Provedor: {0}",
"ScheduledTaskFailedWithName": "{0} falhou",
- "ScheduledTaskStartedWithName": "{0} iniciada",
- "ServerNameNeedsToBeRestarted": "O servidor {0} precisa ser reiniciado",
"Shows": "Séries",
- "Songs": "Músicas",
"StartupEmbyServerIsLoading": "O Servidor Jellyfin está carregando. Por favor, tente novamente mais tarde.",
"SubtitleDownloadFailureFromForItem": "Houve um problema ao baixar as legendas de {0} para {1}",
- "Sync": "Sincronizar",
- "System": "Sistema",
"TvShows": "Séries",
- "User": "Usuário",
"UserCreatedWithName": "O usuário {0} foi criado",
"UserDeletedWithName": "O usuário {0} foi excluído",
"UserDownloadingItemWithValues": "{0} está baixando {1}",
@@ -85,11 +59,8 @@
"UserOfflineFromDevice": "{0} se desconectou de {1}",
"UserOnlineFromDevice": "{0} está online em {1}",
"UserPasswordChangedWithName": "A senha foi alterada para o usuário {0}",
- "UserPolicyUpdatedWithName": "A política de usuário foi atualizada para {0}",
"UserStartedPlayingItemWithValues": "{0} está reproduzindo {1} em {2}",
"UserStoppedPlayingItemWithValues": "{0} parou de reproduzir {1} em {2}",
- "ValueHasBeenAddedToLibrary": "{0} foi adicionado à sua biblioteca de mídia",
- "ValueSpecialEpisodeName": "Especial - {0}",
"VersionNumber": "Versão {0}",
"TaskDownloadMissingSubtitlesDescription": "Procurar na internet por legendas faltando baseado na configuração de metadados.",
"TaskDownloadMissingSubtitles": "Baixar legendas que estão faltando",
@@ -126,8 +97,6 @@
"HearingImpaired": "Deficiência Auditiva",
"TaskRefreshTrickplayImages": "Gerar imagens Trickplay",
"TaskRefreshTrickplayImagesDescription": "Cria prévias Trickplay para vídeos em bibliotecas em que o recurso está habilitado.",
- "TaskCleanCollectionsAndPlaylists": "Limpe coleções e playlists",
- "TaskCleanCollectionsAndPlaylistsDescription": "Remove itens de coleções e playlists que não existem mais.",
"TaskAudioNormalization": "Normalização de áudio",
"TaskAudioNormalizationDescription": "Examina os ficheiros em busca de dados de normalização de áudio.",
"TaskDownloadMissingLyricsDescription": "Baixar letras para músicas",
@@ -137,5 +106,7 @@
"TaskExtractMediaSegmentsDescription": "Extrai ou obtém segmentos de mídia de plug-ins habilitados para MediaSegment.",
"TaskMoveTrickplayImages": "Migrar o local da imagem do Trickplay",
"CleanupUserDataTask": "Tarefa de limpeza de dados do usuário",
- "CleanupUserDataTaskDescription": "Limpa todos os dados do usuário (estado de visualização, status de favorito, etc.) de mídias que não estão presentes por pelo menos 90 dias."
+ "CleanupUserDataTaskDescription": "Limpa todos os dados do usuário (estado de visualização, status de favorito, etc.) de mídias que não estão presentes por pelo menos 90 dias.",
+ "LyricDownloadFailureFromForItem": "Download das Letras falharam em {0} para o item {1}",
+ "Original": "Original"
}
diff --git a/Emby.Server.Implementations/Localization/Core/pt-PT.json b/Emby.Server.Implementations/Localization/Core/pt-PT.json
index c2ce2ba40f..ce7f6d120e 100644
--- a/Emby.Server.Implementations/Localization/Core/pt-PT.json
+++ b/Emby.Server.Implementations/Localization/Core/pt-PT.json
@@ -1,41 +1,24 @@
{
- "Albums": "Álbuns",
"AppDeviceValues": "Aplicação: {0}, Dispositivo: {1}",
- "Application": "Aplicação",
"Artists": "Artistas",
"AuthenticationSucceededWithUserName": "{0} autenticado com sucesso",
"Books": "Livros",
- "CameraImageUploadedFrom": "Uma nova imagem da câmara foi enviada a partir de {0}",
- "Channels": "Canais",
"ChapterNameValue": "Capítulo {0}",
"Collections": "Coleçõ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",
- "HeaderFavoriteAlbums": "Álbuns Favoritos",
- "HeaderFavoriteArtists": "Artistas Favoritos",
"HeaderFavoriteEpisodes": "Episódios Favoritos",
"HeaderFavoriteShows": "Séries Favoritas",
- "HeaderFavoriteSongs": "Músicas Favoritas",
"HeaderLiveTV": "TV em Direto",
"HeaderNextUp": "A Seguir",
- "HeaderRecordingGroups": "Grupos de Gravação",
"HomeVideos": "Vídeos Caseiros",
"Inherit": "Herdar",
- "ItemAddedWithName": "{0} foi adicionado à mediateca",
- "ItemRemovedWithName": "{0} foi removido da mediateca",
"LabelIpAddressValue": "Endereço IP: {0}",
"LabelRunningTimeValue": "Duração: {0}",
"Latest": "Mais Recente",
- "MessageApplicationUpdated": "O servidor Jellyfin foi atualizado",
- "MessageApplicationUpdatedTo": "O servidor Jellyfin foi atualizado para a versão {0}",
- "MessageNamedServerConfigurationUpdatedWithValue": "Configurações de servidor na secção {0} foram atualizadas",
- "MessageServerConfigurationUpdated": "A configuração do servidor foi atualizada",
"MixedContent": "Conteúdo Misto",
"Movies": "Filmes",
"Music": "Música",
@@ -61,23 +44,14 @@
"NotificationOptionVideoPlayback": "Reprodução do vídeo iniciada",
"NotificationOptionVideoPlaybackStopped": "Reprodução do vídeo parada",
"Photos": "Fotografias",
- "Playlists": "Playlists",
- "Plugin": "Extensão",
"PluginInstalledWithName": "{0} foi instalado",
"PluginUninstalledWithName": "{0} foi desinstalado",
"PluginUpdatedWithName": "{0} foi atualizado",
- "ProviderValue": "Provider: {0}",
"ScheduledTaskFailedWithName": "{0} falhou",
- "ScheduledTaskStartedWithName": "{0} iniciou",
- "ServerNameNeedsToBeRestarted": "{0} necessita de ser reiniciado",
"Shows": "Séries",
- "Songs": "Músicas",
"StartupEmbyServerIsLoading": "O servidor Jellyfin está a iniciar. Tente novamente mais tarde.",
"SubtitleDownloadFailureFromForItem": "Falha na transferência de legendas a partir de {0} para {1}",
- "Sync": "Sincronização",
- "System": "Sistema",
"TvShows": "Séries",
- "User": "Utilizador",
"UserCreatedWithName": "Utilizador {0} criado",
"UserDeletedWithName": "Utilizador {0} apagado",
"UserDownloadingItemWithValues": "{0} está a transferir {1}",
@@ -85,11 +59,8 @@
"UserOfflineFromDevice": "{0} desligou-se a partir de {1}",
"UserOnlineFromDevice": "{0} ligou-se a partir de {1}",
"UserPasswordChangedWithName": "Palavra-passe alterada para o utilizador {0}",
- "UserPolicyUpdatedWithName": "Política de utilizador alterada para {0}",
"UserStartedPlayingItemWithValues": "{0} está a reproduzir {1} em {2}",
"UserStoppedPlayingItemWithValues": "{0} terminou a reprodução de {1} em {2}",
- "ValueHasBeenAddedToLibrary": "{0} foi adicionado à sua mediateca",
- "ValueSpecialEpisodeName": "Especial - {0}",
"VersionNumber": "Versão {0}",
"TaskDownloadMissingSubtitlesDescription": "Procurar na internet por legendas em falta baseado na configuração de metadados.",
"TaskDownloadMissingSubtitles": "Transferir legendas em falta",
@@ -126,8 +97,6 @@
"HearingImpaired": "Surdo",
"TaskRefreshTrickplayImages": "Gerar imagens de trickplay",
"TaskRefreshTrickplayImagesDescription": "Cria pré-visualizações de trickplay para vídeos nas bibliotecas ativadas.",
- "TaskCleanCollectionsAndPlaylistsDescription": "Remove itens de coleções e listas de reprodução que já não existem.",
- "TaskCleanCollectionsAndPlaylists": "Limpar coleções e listas de reprodução",
"TaskAudioNormalizationDescription": "Analisa os ficheiros para obter dados de normalização de áudio.",
"TaskAudioNormalization": "Normalização de áudio",
"TaskExtractMediaSegments": "Analisar segmentos de multimédia",
@@ -137,5 +106,7 @@
"TaskExtractMediaSegmentsDescription": "Extrai ou obtém segmentos de multimédia a partir de plugins com suporte para MediaSegment.",
"TaskMoveTrickplayImagesDescription": "Move os ficheiros trickplay existentes de acordo com as definições da mediateca.",
"CleanupUserDataTaskDescription": "Apaga todos os dados de utilizador (estados de reprodução, favoritos, etc) de arquivos média não presentes há 90 dias ou mais.",
- "CleanupUserDataTask": "Limpeza de dados de utilizador"
+ "CleanupUserDataTask": "Limpeza de dados de utilizador",
+ "Original": "Original",
+ "LyricDownloadFailureFromForItem": "Erro ao descarregar letras de {0} para {1}"
}
diff --git a/Emby.Server.Implementations/Localization/Core/pt.json b/Emby.Server.Implementations/Localization/Core/pt.json
index 9ae346e253..ce338acf34 100644
--- a/Emby.Server.Implementations/Localization/Core/pt.json
+++ b/Emby.Server.Implementations/Localization/Core/pt.json
@@ -3,44 +3,30 @@
"Collections": "Coleções",
"Books": "Livros",
"Artists": "Artistas",
- "Albums": "Álbuns",
"HeaderNextUp": "A Seguir",
- "HeaderFavoriteSongs": "Músicas Favoritas",
- "HeaderFavoriteArtists": "Artistas Favoritos",
- "HeaderFavoriteAlbums": "Álbuns Favoritos",
"HeaderFavoriteEpisodes": "Episódios Favoritos",
"HeaderFavoriteShows": "Séries Favoritas",
"HeaderContinueWatching": "Continuar a ver",
- "HeaderAlbumArtists": "Artistas do Álbum",
"Genres": "Géneros",
"Folders": "Pastas",
"Favorites": "Favoritos",
- "Channels": "Canais",
"UserDownloadingItemWithValues": "{0} está sendo baixado {1}",
"VersionNumber": "Versão {0}",
- "ValueHasBeenAddedToLibrary": "{0} foi adicionado à sua mediateca",
"UserStoppedPlayingItemWithValues": "{0} terminou a reprodução de {1} em {2}",
"UserStartedPlayingItemWithValues": "{0} está reproduzindo {1} em {2}",
- "UserPolicyUpdatedWithName": "A política do usuário {0} foi alterada",
"UserPasswordChangedWithName": "A senha do usuário {0} foi alterada",
"UserOnlineFromDevice": "{0} está online a partir de {1}",
"UserOfflineFromDevice": "{0} desconectou-se a partir de {1}",
"UserLockedOutWithName": "O usuário {0} foi bloqueado",
"UserDeletedWithName": "O usuário {0} foi removido",
"UserCreatedWithName": "O usuário {0} foi criado",
- "User": "Usuário",
"TvShows": "Séries",
- "System": "Sistema",
"SubtitleDownloadFailureFromForItem": "Falha na transferência de legendas de {0} para {1}",
"StartupEmbyServerIsLoading": "O servidor Jellyfin está iniciando. Tente novamente dentro de momentos.",
- "ServerNameNeedsToBeRestarted": "{0} necessita ser reiniciado",
- "ScheduledTaskStartedWithName": "{0} iniciou",
"ScheduledTaskFailedWithName": "{0} falhou",
- "ProviderValue": "Fornecedor: {0}",
"PluginUpdatedWithName": "{0} foi atualizado",
"PluginUninstalledWithName": "{0} foi desinstalado",
"PluginInstalledWithName": "{0} foi instalado",
- "Plugin": "Plugin",
"NotificationOptionVideoPlaybackStopped": "Reprodução de vídeo parada",
"NotificationOptionVideoPlayback": "Reprodução de vídeo iniciada",
"NotificationOptionUserLockedOut": "Usuário bloqueado",
@@ -64,32 +50,17 @@
"MusicVideos": "Videoclipes",
"Music": "Música",
"MixedContent": "Conteúdo diverso",
- "MessageServerConfigurationUpdated": "A configuração do servidor foi atualizada",
- "MessageNamedServerConfigurationUpdatedWithValue": "As configurações do servidor na seção {0} foram atualizadas",
- "MessageApplicationUpdatedTo": "O servidor Jellyfin foi atualizado para a versão {0}",
- "MessageApplicationUpdated": "O servidor Jellyfin foi atualizado",
"Latest": "Mais Recente",
"LabelRunningTimeValue": "Duração: {0}",
"LabelIpAddressValue": "Endereço de IP: {0}",
- "ItemRemovedWithName": "{0} foi removido da mediateca",
- "ItemAddedWithName": "{0} foi adicionado à mediateca",
"Inherit": "Herdar",
"HomeVideos": "Vídeos Caseiros",
- "HeaderRecordingGroups": "Grupos de Gravação",
- "ValueSpecialEpisodeName": "Especial - {0}",
- "Sync": "Sincronização",
- "Songs": "Músicas",
"Shows": "Séries",
- "Playlists": "Playlists",
"Photos": "Fotografias",
"Movies": "Filmes",
"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": "Aplicação",
"AppDeviceValues": "Aplicação: {0}, Dispositivo: {1}",
"TaskCleanCache": "Limpar Diretório de Cache",
"TasksApplicationCategory": "Aplicação",
@@ -126,8 +97,6 @@
"TaskKeyframeExtractorDescription": "Retira frames chave do video para criar listas HLS precisas. Esta tarefa pode correr durante algum tempo.",
"TaskRefreshTrickplayImages": "Gerar imagens de trickplay",
"TaskRefreshTrickplayImagesDescription": "Cria pré-visualizações de trickplay para vídeos nas bibliotecas ativadas.",
- "TaskCleanCollectionsAndPlaylistsDescription": "Remove itens de coleções e listas de reprodução que já não existem.",
- "TaskCleanCollectionsAndPlaylists": "Limpar coleções e listas de reprodução",
"TaskAudioNormalizationDescription": "Analisa os ficheiros para obter dados de normalização de áudio.",
"TaskAudioNormalization": "Normalização de áudio",
"TaskDownloadMissingLyrics": "Transferir letra em falta",
@@ -137,5 +106,7 @@
"TaskExtractMediaSegmentsDescription": "Extrai ou obtém segmentos de multimédia a partir de plugins com suporte para MediaSegment.",
"TaskMoveTrickplayImages": "Migrar a localização da imagem do Trickplay",
"CleanupUserDataTask": "Task de limpeza de dados do usuário",
- "CleanupUserDataTaskDescription": "Remove todos os dados do usuário (progresso, favoritos etc) de mídias que não estão presentes há pelo menos 90 dias."
+ "CleanupUserDataTaskDescription": "Remove todos os dados do usuário (progresso, favoritos etc) de mídias que não estão presentes há pelo menos 90 dias.",
+ "Original": "Original",
+ "LyricDownloadFailureFromForItem": "Erro ao descarregar letras de {0} para {1}"
}
diff --git a/Emby.Server.Implementations/Localization/Core/ro.json b/Emby.Server.Implementations/Localization/Core/ro.json
index bf71c5afa1..ea83b88951 100644
--- a/Emby.Server.Implementations/Localization/Core/ro.json
+++ b/Emby.Server.Implementations/Localization/Core/ro.json
@@ -1,11 +1,8 @@
{
"HeaderNextUp": "Urmează",
"VersionNumber": "Versiunea {0}",
- "ValueSpecialEpisodeName": "Special - {0}",
- "ValueHasBeenAddedToLibrary": "{0} a fost adăugat la biblioteca multimedia",
"UserStoppedPlayingItemWithValues": "{0} a terminat rularea {1} pe {2}",
"UserStartedPlayingItemWithValues": "{0} ruleaza {1} pe {2}",
- "UserPolicyUpdatedWithName": "Politica utilizatorului {0} a fost actualizată",
"UserPasswordChangedWithName": "Parola utilizatorului {0} a fost schimbată",
"UserOnlineFromDevice": "{0} este conectat de la {1}",
"UserOfflineFromDevice": "{0} s-a deconectat de la {1}",
@@ -13,23 +10,14 @@
"UserDownloadingItemWithValues": "{0} descarcă {1}",
"UserDeletedWithName": "Utilizatorul {0} a fost șters",
"UserCreatedWithName": "Utilizatorul {0} a fost creat",
- "User": "Utilizator",
"TvShows": "Seriale TV",
- "System": "Sistem",
- "Sync": "Sincronizare",
"SubtitleDownloadFailureFromForItem": "Subtitrările nu au putut fi descărcate de la {0} pentru {1}",
"StartupEmbyServerIsLoading": "Se încarcă serverul Jellyfin. Încercați din nou în scurt timp.",
- "Songs": "Melodii",
"Shows": "Seriale",
- "ServerNameNeedsToBeRestarted": "{0} trebuie să fie repornit",
- "ScheduledTaskStartedWithName": "{0} pornit/ă",
"ScheduledTaskFailedWithName": "{0} eșuat/ă",
- "ProviderValue": "Furnizor: {0}",
"PluginUpdatedWithName": "{0} a fost actualizat/ă",
"PluginUninstalledWithName": "{0} a fost dezinstalat",
"PluginInstalledWithName": "{0} a fost instalat",
- "Plugin": "Extensie",
- "Playlists": "Liste de redare",
"Photos": "Fotografii",
"NotificationOptionVideoPlaybackStopped": "Redarea video oprită",
"NotificationOptionVideoPlayback": "Redare video începută",
@@ -55,42 +43,25 @@
"Music": "Muzică",
"Movies": "Filme",
"MixedContent": "Conținut amestecat",
- "MessageServerConfigurationUpdated": "Configurarea serverului a fost actualizată",
- "MessageNamedServerConfigurationUpdatedWithValue": "Secțiunea de configurare a serverului {0} a fost acualizata",
- "MessageApplicationUpdatedTo": "Jellyfin Server a fost actualizat la {0}",
- "MessageApplicationUpdated": "Jellyfin Server a fost actualizat",
"Latest": "Cele mai recente",
"LabelRunningTimeValue": "Durată: {0}",
"LabelIpAddressValue": "Adresa IP: {0}",
- "ItemRemovedWithName": "{0} a fost eliminat din bibliotecă",
- "ItemAddedWithName": "{0} a fost adăugat în bibliotecă",
"Inherit": "Moștenit",
"HomeVideos": "Filme personale",
- "HeaderRecordingGroups": "Grupuri de înregistrare",
"HeaderLiveTV": "TV în Direct",
- "HeaderFavoriteSongs": "Melodii Favorite",
"HeaderFavoriteShows": "Seriale TV Favorite",
"HeaderFavoriteEpisodes": "Episoade Favorite",
- "HeaderFavoriteArtists": "Artiști Favoriți",
- "HeaderFavoriteAlbums": "Albume Favorite",
"HeaderContinueWatching": "Vizionează în continuare",
- "HeaderAlbumArtists": "Artiști album",
"Genres": "Genuri",
"Folders": "Dosare",
"Favorites": "Preferate",
"FailedLoginAttemptWithUserName": "Încercare de conectare eșuată pentru {0}",
- "DeviceOnlineWithName": "{0} este conectat",
- "DeviceOfflineWithName": "{0} s-a deconectat",
"Collections": "Colecții",
"ChapterNameValue": "Capitolul {0}",
- "Channels": "Canale",
- "CameraImageUploadedFrom": "O nouă fotografie a fost încărcată din {0}",
"Books": "Cărți",
"AuthenticationSucceededWithUserName": "{0} autentificare reușită",
"Artists": "Artiști",
- "Application": "Aplicație",
"AppDeviceValues": "Aplicație: {0}, Dispozitiv: {1}",
- "Albums": "Albume",
"TaskDownloadMissingSubtitlesDescription": "Caută pe internet subtitrările lipsă pe baza configurației metadatelor.",
"TaskDownloadMissingSubtitles": "Descarcă subtitrările lipsă",
"TaskRefreshChannelsDescription": "Actualizează informațiile despre canalul de internet.",
@@ -128,8 +99,6 @@
"TaskRefreshTrickplayImagesDescription": "Generează previzualizările trickplay pentru videourile din librăriile selectate.",
"TaskAudioNormalizationDescription": "Scanează fișiere pentru date necesare normalizării sunetului.",
"TaskAudioNormalization": "Normalizare sunet",
- "TaskCleanCollectionsAndPlaylists": "Curăță colecțiile și listele de redare",
- "TaskCleanCollectionsAndPlaylistsDescription": "Elimină elementele care nu mai există din colecții și liste de redare.",
"TaskExtractMediaSegments": "Scanează segmentele media",
"TaskMoveTrickplayImagesDescription": "Mută fișierele trickplay existente conform setărilor librăriei.",
"TaskExtractMediaSegmentsDescription": "Extrage sau obține segmentele media de la pluginurile MediaSegment activate.",
@@ -137,5 +106,7 @@
"TaskDownloadMissingLyrics": "Descarcă versurile lipsă",
"TaskDownloadMissingLyricsDescription": "Descarcă versuri pentru melodii",
"CleanupUserDataTask": "Sarcina de curatare a datelor utilizatorului",
- "CleanupUserDataTaskDescription": "Sterge toate datele utilizatorului (starea vizionarii, starea favoritelor etc.) de pe suporturile media care nu mai sunt prezente timp de cel puțin 90 de zile."
+ "CleanupUserDataTaskDescription": "Sterge toate datele utilizatorului (starea vizionarii, starea favoritelor etc.) de pe suporturile media care nu mai sunt prezente timp de cel puțin 90 de zile.",
+ "LyricDownloadFailureFromForItem": "Versurile nu au putut fi descărcate din {0} pentru {1}",
+ "Original": "Original"
}
diff --git a/Emby.Server.Implementations/Localization/Core/ru.json b/Emby.Server.Implementations/Localization/Core/ru.json
index 03bce0ebdf..40d5e3985d 100644
--- a/Emby.Server.Implementations/Localization/Core/ru.json
+++ b/Emby.Server.Implementations/Localization/Core/ru.json
@@ -1,41 +1,24 @@
{
- "Albums": "Альбомы",
"AppDeviceValues": "Приложение: {0}, Устройство: {1}",
- "Application": "Приложение",
"Artists": "Исполнители",
"AuthenticationSucceededWithUserName": "{0} - авторизация успешна",
"Books": "Книги",
- "CameraImageUploadedFrom": "Новое фото загружено с камеры {0}",
- "Channels": "Каналы",
"ChapterNameValue": "Сцена {0}",
"Collections": "Коллекции",
- "DeviceOfflineWithName": "{0} - отключено",
- "DeviceOnlineWithName": "{0} - подключено",
"FailedLoginAttemptWithUserName": "Неудачная попытка входа с {0}",
"Favorites": "Избранное",
"Folders": "Папки",
"Genres": "Жанры",
- "HeaderAlbumArtists": "Исполнители альбома",
"HeaderContinueWatching": "Продолжить просмотр",
- "HeaderFavoriteAlbums": "Избранные альбомы",
- "HeaderFavoriteArtists": "Избранные исполнители",
"HeaderFavoriteEpisodes": "Избранные эпизоды",
"HeaderFavoriteShows": "Избранные сериалы",
- "HeaderFavoriteSongs": "Избранные композиции",
"HeaderLiveTV": "Эфир",
"HeaderNextUp": "Следующий",
- "HeaderRecordingGroups": "Группы записей",
"HomeVideos": "Домашние видео",
"Inherit": "Наследуемое",
- "ItemAddedWithName": "{0} - добавлено в медиатеку",
- "ItemRemovedWithName": "{0} - изъято из медиатеки",
"LabelIpAddressValue": "IP-адрес: {0}",
"LabelRunningTimeValue": "Длительность: {0}",
"Latest": "Последние",
- "MessageApplicationUpdated": "Jellyfin Server был обновлён",
- "MessageApplicationUpdatedTo": "Jellyfin Server был обновлён до {0}",
- "MessageNamedServerConfigurationUpdatedWithValue": "Конфигурация сервера (раздел {0}) была обновлена",
- "MessageServerConfigurationUpdated": "Конфигурация сервера была обновлена",
"MixedContent": "Смешанное содержание",
"Movies": "Фильмы",
"Music": "Музыка",
@@ -61,23 +44,14 @@
"NotificationOptionVideoPlayback": "Воспроизведение видео запущено",
"NotificationOptionVideoPlaybackStopped": "Воспроизведение видео остановлено",
"Photos": "Фото",
- "Playlists": "Плей-листы",
- "Plugin": "Плагин",
"PluginInstalledWithName": "{0} - было установлено",
"PluginUninstalledWithName": "{0} - было удалено",
"PluginUpdatedWithName": "{0} - было обновлено",
- "ProviderValue": "Поставщик: {0}",
"ScheduledTaskFailedWithName": "{0} - неудачна",
- "ScheduledTaskStartedWithName": "{0} - запущена",
- "ServerNameNeedsToBeRestarted": "Необходим перезапуск {0}",
"Shows": "Сериалы",
- "Songs": "Композиции",
"StartupEmbyServerIsLoading": "Jellyfin Server загружается. Повторите попытку в ближайшее время.",
"SubtitleDownloadFailureFromForItem": "Субтитры к {1} не удалось загрузить с {0}",
- "Sync": "Синхронизация",
- "System": "Система",
"TvShows": "Телесериалы",
- "User": "Пользователь",
"UserCreatedWithName": "Пользователь {0} был создан",
"UserDeletedWithName": "Пользователь {0} был удалён",
"UserDownloadingItemWithValues": "{0} загружает {1}",
@@ -85,11 +59,8 @@
"UserOfflineFromDevice": "{0} отключился с {1}",
"UserOnlineFromDevice": "{0} подключился с {1}",
"UserPasswordChangedWithName": "Пароль пользователя {0} был изменён",
- "UserPolicyUpdatedWithName": "Политики пользователя {0} были обновлены",
"UserStartedPlayingItemWithValues": "{0} - воспроизведение «{1}» на {2}",
"UserStoppedPlayingItemWithValues": "{0} - воспроизведение остановлено «{1}» на {2}",
- "ValueHasBeenAddedToLibrary": "{0} добавлено в медиатеку",
- "ValueSpecialEpisodeName": "Спецэпизод - {0}",
"VersionNumber": "Версия {0}",
"TaskDownloadMissingSubtitles": "Загрузка отсутствующих субтитров",
"TaskRefreshChannels": "Обновление каналов",
@@ -126,8 +97,6 @@
"HearingImpaired": "Для слабослышащих",
"TaskRefreshTrickplayImages": "Сгенерировать изображения для Trickplay",
"TaskRefreshTrickplayImagesDescription": "Создает предпросмотры для Trickplay для видео в библиотеках, где эта функция включена.",
- "TaskCleanCollectionsAndPlaylists": "Очистка коллекций и списков воспроизведения",
- "TaskCleanCollectionsAndPlaylistsDescription": "Удаляет элементы из коллекций и списков воспроизведения, которые больше не существуют.",
"TaskAudioNormalization": "Нормализация звука",
"TaskAudioNormalizationDescription": "Сканирует файлы на наличие данных о нормализации звука.",
"TaskDownloadMissingLyrics": "Загрузить недостающий текст",
@@ -137,5 +106,7 @@
"TaskExtractMediaSegmentsDescription": "Извлекает или получает медиасегменты из плагинов MediaSegment.",
"TaskMoveTrickplayImagesDescription": "Перемещает существующие файлы trickplay в соответствии с настройками медиатеки.",
"CleanupUserDataTask": "Задача очистки пользовательских данных",
- "CleanupUserDataTaskDescription": "Очищает все пользовательские данные (состояние просмотра, статус избранного и т.д.) с медиа, отсутствующих по меньшей мере в течение 90 дней."
+ "CleanupUserDataTaskDescription": "Очищает все пользовательские данные (состояние просмотра, статус избранного и т.д.) с медиа, отсутствующих по меньшей мере в течение 90 дней.",
+ "Original": "Оригинальный",
+ "LyricDownloadFailureFromForItem": "Не получилось скачать текст песни с {0} для {1}"
}
diff --git a/Emby.Server.Implementations/Localization/Core/si.json b/Emby.Server.Implementations/Localization/Core/si.json
index 0967ef424b..8efc0d1f2e 100644
--- a/Emby.Server.Implementations/Localization/Core/si.json
+++ b/Emby.Server.Implementations/Localization/Core/si.json
@@ -1 +1,15 @@
-{}
+{
+ "AppDeviceValues": "ඇප්: {0}, උපාංග: {1}",
+ "Artists": "කලාකරුවන්",
+ "AuthenticationSucceededWithUserName": "{0} සාර්ථකව තහවුරු කරන ලදී",
+ "Books": "පොත්",
+ "ChapterNameValue": "{0} වෙනි පරිච්ඡේදය",
+ "Collections": "සංහිතා",
+ "Default": "පෙරනිමි",
+ "External": "බාහිර",
+ "FailedLoginAttemptWithUserName": "{0} වෙතින් සිදුකළ පිවිසීමේ උත්සාහය අසාර්ථක විය",
+ "Favorites": "ප්‍රියතමයන්",
+ "Folders": "ෆෝල්ඩර",
+ "Forced": "නියමිත",
+ "Genres": "ප්‍රභේද"
+}
diff --git a/Emby.Server.Implementations/Localization/Core/sk.json b/Emby.Server.Implementations/Localization/Core/sk.json
index 7c8d860476..7ae8857e5d 100644
--- a/Emby.Server.Implementations/Localization/Core/sk.json
+++ b/Emby.Server.Implementations/Localization/Core/sk.json
@@ -1,41 +1,24 @@
{
- "Albums": "Albumy",
"AppDeviceValues": "Aplikácia: {0}, Zariadenie: {1}",
- "Application": "Aplikácia",
"Artists": "Interpreti",
"AuthenticationSucceededWithUserName": "{0} úspešne overený",
"Books": "Knihy",
- "CameraImageUploadedFrom": "Z {0} bola nahraná nová fotografia",
- "Channels": "Kanály",
"ChapterNameValue": "Kapitola {0}",
"Collections": "Kolekcie",
- "DeviceOfflineWithName": "{0} sa odpojil",
- "DeviceOnlineWithName": "{0} je pripojený",
"FailedLoginAttemptWithUserName": "Neúspešný pokus o prihlásenie z {0}",
"Favorites": "Obľúbené",
"Folders": "Priečinky",
"Genres": "Žánre",
- "HeaderAlbumArtists": "Interpreti albumu",
"HeaderContinueWatching": "Pokračovať v pozeraní",
- "HeaderFavoriteAlbums": "Obľúbené albumy",
- "HeaderFavoriteArtists": "Obľúbení interpreti",
"HeaderFavoriteEpisodes": "Obľúbené epizódy",
"HeaderFavoriteShows": "Obľúbené seriály",
- "HeaderFavoriteSongs": "Obľúbené skladby",
"HeaderLiveTV": "Živá TV",
"HeaderNextUp": "Nasleduje",
- "HeaderRecordingGroups": "Skupiny nahrávok",
"HomeVideos": "Domáce videá",
"Inherit": "Zdediť",
- "ItemAddedWithName": "{0} bol pridaný do knižnice",
- "ItemRemovedWithName": "{0} bol odstránený z knižnice",
"LabelIpAddressValue": "IP adresa: {0}",
"LabelRunningTimeValue": "Dĺžka: {0}",
"Latest": "Najnovšie",
- "MessageApplicationUpdated": "Jellyfin Server bol aktualizovaný",
- "MessageApplicationUpdatedTo": "Jellyfin Server bol aktualizovaný na verziu {0}",
- "MessageNamedServerConfigurationUpdatedWithValue": "Sekcia {0} konfigurácie servera bola aktualizovaná",
- "MessageServerConfigurationUpdated": "Konfigurácia servera bola aktualizovaná",
"MixedContent": "Zmiešaný obsah",
"Movies": "Filmy",
"Music": "Hudba",
@@ -61,23 +44,14 @@
"NotificationOptionVideoPlayback": "Spustené prehrávanie videa",
"NotificationOptionVideoPlaybackStopped": "Zastavené prehrávanie videa",
"Photos": "Fotky",
- "Playlists": "Playlisty",
- "Plugin": "Zásuvný modul",
"PluginInstalledWithName": "{0} bol nainštalovaný",
"PluginUninstalledWithName": "{0} bol odinštalovaný",
"PluginUpdatedWithName": "{0} bol aktualizovaný",
- "ProviderValue": "Poskytovateľ: {0}",
"ScheduledTaskFailedWithName": "{0} zlyhalo",
- "ScheduledTaskStartedWithName": "{0} zahájených",
- "ServerNameNeedsToBeRestarted": "{0} vyžaduje reštart",
"Shows": "Seriály",
- "Songs": "Skladby",
"StartupEmbyServerIsLoading": "Jellyfin Server sa spúšťa. Prosím, skúste to o chvíľu znova.",
"SubtitleDownloadFailureFromForItem": "Sťahovanie titulkov z {0} pre {1} zlyhalo",
- "Sync": "Synchronizácia",
- "System": "Systém",
"TvShows": "TV seriály",
- "User": "Používateľ",
"UserCreatedWithName": "Používateľ {0} bol vytvorený",
"UserDeletedWithName": "Používateľ {0} bol vymazaný",
"UserDownloadingItemWithValues": "{0} sťahuje {1}",
@@ -85,11 +59,8 @@
"UserOfflineFromDevice": "{0} sa odpojil od {1}",
"UserOnlineFromDevice": "{0} je online z {1}",
"UserPasswordChangedWithName": "Heslo používateľa {0} bolo zmenené",
- "UserPolicyUpdatedWithName": "Používateľské zásady pre {0} boli aktualizované",
"UserStartedPlayingItemWithValues": "{0} spustil prehrávanie {1} na {2}",
"UserStoppedPlayingItemWithValues": "{0} ukončil prehrávanie {1} na {2}",
- "ValueHasBeenAddedToLibrary": "{0} bol pridaný do vašej knižnice médií",
- "ValueSpecialEpisodeName": "Špeciál - {0}",
"VersionNumber": "Verzia {0}",
"TaskDownloadMissingSubtitlesDescription": "Vyhľadá na internete chýbajúce titulky podľa toho, ako sú nakonfigurované metadáta.",
"TaskDownloadMissingSubtitles": "Stiahnuť chýbajúce titulky",
@@ -126,8 +97,6 @@
"HearingImpaired": "Sluchovo postihnutí",
"TaskRefreshTrickplayImages": "Generovanie obrázkov Trickplay",
"TaskRefreshTrickplayImagesDescription": "Vytvára trickplay náhľady pre videá v povolených knižniciach.",
- "TaskCleanCollectionsAndPlaylists": "Vyčistiť kolekcie a playlisty",
- "TaskCleanCollectionsAndPlaylistsDescription": "Odstráni položky z kolekcií a playlistov, ktoré už neexistujú.",
"TaskAudioNormalization": "Normalizácia zvuku",
"TaskAudioNormalizationDescription": "Skenovať súbory za účelom normalizácie zvuku.",
"TaskExtractMediaSegments": "Skenovanie segmentov médií",
@@ -137,5 +106,7 @@
"TaskDownloadMissingLyrics": "Stiahnuť chýbajúce texty piesní",
"TaskDownloadMissingLyricsDescription": "Stiahne texty pre piesne",
"CleanupUserDataTask": "Prečistiť používateľské dáta",
- "CleanupUserDataTaskDescription": "Vyčistí všetky dáta používateľa (stav sledovania, stav obľúbených atď.) z médií, ktoré už neexistujú aspoň 90 dní."
+ "CleanupUserDataTaskDescription": "Vyčistí všetky dáta používateľa (stav sledovania, stav obľúbených atď.) z médií, ktoré už neexistujú aspoň 90 dní.",
+ "LyricDownloadFailureFromForItem": "Text piesne sa nepodarilo stiahnuť z {0} pre {1}",
+ "Original": "Originál"
}
diff --git a/Emby.Server.Implementations/Localization/Core/sl-SI.json b/Emby.Server.Implementations/Localization/Core/sl-SI.json
index 7c7c88e28a..a1b5b714af 100644
--- a/Emby.Server.Implementations/Localization/Core/sl-SI.json
+++ b/Emby.Server.Implementations/Localization/Core/sl-SI.json
@@ -1,41 +1,24 @@
{
- "Albums": "Albumi",
"AppDeviceValues": "Aplikacija: {0}, Naprava: {1}",
- "Application": "Aplikacija",
"Artists": "Izvajalci",
"AuthenticationSucceededWithUserName": "{0} se je uspešno prijavil/a",
"Books": "Knjige",
- "CameraImageUploadedFrom": "Nova fotografija je bila naložena iz {0}",
- "Channels": "Kanali",
"ChapterNameValue": "Poglavje {0}",
"Collections": "Zbirke",
- "DeviceOfflineWithName": "{0} je prekinil povezavo",
- "DeviceOnlineWithName": "{0} je povezan",
"FailedLoginAttemptWithUserName": "Neuspešen poskus prijave z {0}",
"Favorites": "Priljubljeno",
"Folders": "Mape",
"Genres": "Zvrsti",
- "HeaderAlbumArtists": "Izvajalci albuma",
"HeaderContinueWatching": "Nadaljuj ogled",
- "HeaderFavoriteAlbums": "Priljubljeni albumi",
- "HeaderFavoriteArtists": "Priljubljeni izvajalci",
"HeaderFavoriteEpisodes": "Priljubljene epizode",
"HeaderFavoriteShows": "Priljubljene serije",
- "HeaderFavoriteSongs": "Priljubljene pesmi",
"HeaderLiveTV": "TV v živo",
"HeaderNextUp": "Sledi",
- "HeaderRecordingGroups": "Zbirke posnetkov",
"HomeVideos": "Domači posnetki",
"Inherit": "Podeduj",
- "ItemAddedWithName": "{0} je dodan v knjižnico",
- "ItemRemovedWithName": "{0} je bil odstranjen iz knjižnice",
"LabelIpAddressValue": "IP naslov: {0}",
"LabelRunningTimeValue": "Čas trajanja: {0}",
"Latest": "Najnovejše",
- "MessageApplicationUpdated": "Jellyfin strežnik je bil posodobljen",
- "MessageApplicationUpdatedTo": "Jellyfin strežnik je bil posodobljen na {0}",
- "MessageNamedServerConfigurationUpdatedWithValue": "Oddelek nastavitev {0} je bil posodobljen",
- "MessageServerConfigurationUpdated": "Nastavitve strežnika so bile posodobljene",
"MixedContent": "Mešane vsebine",
"Movies": "Filmi",
"Music": "Glasba",
@@ -61,23 +44,14 @@
"NotificationOptionVideoPlayback": "Predvajanje videa se je začelo",
"NotificationOptionVideoPlaybackStopped": "Predvajanje videa se je ustavilo",
"Photos": "Fotografije",
- "Playlists": "Seznami predvajanja",
- "Plugin": "Dodatek",
"PluginInstalledWithName": "{0} je bil nameščen",
"PluginUninstalledWithName": "{0} je bil odstranjen",
"PluginUpdatedWithName": "{0} je bil posodobljen",
- "ProviderValue": "Ponudnik: {0}",
"ScheduledTaskFailedWithName": "{0} ni uspelo",
- "ScheduledTaskStartedWithName": "{0} začeto",
- "ServerNameNeedsToBeRestarted": "{0} mora biti ponovno zagnan",
"Shows": "Serije",
- "Songs": "Pesmi",
"StartupEmbyServerIsLoading": "Jellyfin strežnik se zaganja. Poskusite ponovno kasneje.",
"SubtitleDownloadFailureFromForItem": "Neuspešen prenos podnapisov iz {0} za {1}",
- "Sync": "Sinhroniziraj",
- "System": "Sistem",
"TvShows": "TV serije",
- "User": "Uporabnik",
"UserCreatedWithName": "Uporabnik {0} je bil ustvarjen",
"UserDeletedWithName": "Uporabnik {0} je bil izbrisan",
"UserDownloadingItemWithValues": "{0} prenaša {1}",
@@ -85,11 +59,8 @@
"UserOfflineFromDevice": "{0} je prekinil povezavo z {1}",
"UserOnlineFromDevice": "{0} je aktiven na {1}",
"UserPasswordChangedWithName": "Geslo za uporabnika {0} je bilo spremenjeno",
- "UserPolicyUpdatedWithName": "Pravilnik uporabe je bil posodobljen za uporabnika {0}",
"UserStartedPlayingItemWithValues": "{0} predvaja {1} na {2}",
"UserStoppedPlayingItemWithValues": "{0} je nehal predvajati {1} na {2}",
- "ValueHasBeenAddedToLibrary": "{0} je bil dodan vaši knjižnici",
- "ValueSpecialEpisodeName": "Posebna epizoda - {0}",
"VersionNumber": "Različica {0}",
"TaskDownloadMissingSubtitles": "Prenesi manjkajoče podnapise",
"TaskRefreshChannelsDescription": "Osveži podatke spletnih kanalov.",
@@ -132,10 +103,10 @@
"TaskMoveTrickplayImages": "Preseli lokacijo Trickplay slik",
"TaskDownloadMissingLyrics": "Prenesi manjkajoča besedila pesmi",
"TaskDownloadMissingLyricsDescription": "Prenesi besedila za pesmi",
- "TaskCleanCollectionsAndPlaylists": "Počisti zbirke in sezname predvajanja",
"TaskAudioNormalization": "Normalizacija zvoka",
"TaskAudioNormalizationDescription": "Pregled datotek za podatke o normalizaciji zvoka.",
- "TaskCleanCollectionsAndPlaylistsDescription": "Odstrani elemente iz zbirk in seznamov predvajanja, ki ne obstajajo več.",
"CleanupUserDataTask": "Čiščenje uporabniških podatkov",
- "CleanupUserDataTaskDescription": "Izbriše vse uporabniške podatke (stanje ogleda, priljubljene itd.) za vsebine, ki že več kot 90 dni niso na voljo."
+ "CleanupUserDataTaskDescription": "Izbriše vse uporabniške podatke (stanje ogleda, priljubljene itd.) za vsebine, ki že več kot 90 dni niso na voljo.",
+ "LyricDownloadFailureFromForItem": "Besedila ni bilo mogoče prenesti iz {0} za {1}",
+ "Original": "Original"
}
diff --git a/Emby.Server.Implementations/Localization/Core/sn.json b/Emby.Server.Implementations/Localization/Core/sn.json
index 74720e7646..45a459cbe1 100644
--- a/Emby.Server.Implementations/Localization/Core/sn.json
+++ b/Emby.Server.Implementations/Localization/Core/sn.json
@@ -1,28 +1,18 @@
{
- "HeaderAlbumArtists": "Vaimbi vemadambarefu",
"HeaderContinueWatching": "Simudzira kuona",
- "HeaderFavoriteSongs": "Nziyo dzaunofarira",
- "Albums": "Dambarefu",
"AppDeviceValues": "Apu: {0}, Dhivhaisi: {1}",
- "Application": "Purogiramu",
"Artists": "Vaimbi",
"AuthenticationSucceededWithUserName": "apinda",
"Books": "Mabhuku",
- "CameraImageUploadedFrom": "Mufananidzo mutsva vabva pakamera {0}",
- "Channels": "Machanewo",
"ChapterNameValue": "Chikamu {0}",
"Collections": "Akafanana",
"Default": "Zvakasarudzwa Kare",
- "DeviceOfflineWithName": "{0} haasisipo",
- "DeviceOnlineWithName": "{0} aripo",
"External": "Zvekunze",
"FailedLoginAttemptWithUserName": "Vatadza kuloga chimboedza kushandisa {0}",
"Favorites": "Zvaunofarira",
"Folders": "Mafoodha",
"Forced": "Zvekumanikidzira",
"Genres": "Mhando",
- "HeaderFavoriteAlbums": "Madambarefu aunofarira",
- "HeaderFavoriteArtists": "Vaimbi vaunofarira",
"HeaderFavoriteEpisodes": "Maepisodhi aunofarira",
"HeaderFavoriteShows": "Masirisi aunofarira"
}
diff --git a/Emby.Server.Implementations/Localization/Core/sq.json b/Emby.Server.Implementations/Localization/Core/sq.json
index 8f0079c94f..e13f7b09e1 100644
--- a/Emby.Server.Implementations/Localization/Core/sq.json
+++ b/Emby.Server.Implementations/Localization/Core/sq.json
@@ -1,5 +1,4 @@
{
- "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ë",
@@ -24,11 +23,8 @@
"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}",
@@ -36,23 +32,14 @@
"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ët",
"Shows": "Serialet",
- "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",
@@ -78,41 +65,25 @@
"Music": "Muzikë",
"Movies": "Filmat",
"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",
- "HeaderAlbumArtists": "Artistët e albumeve",
"Genres": "Zhanret",
"Folders": "Skedarët",
"Favorites": "Të preferuarat",
"FailedLoginAttemptWithUserName": "Përpjekja për hyrje dështoi nga {0}",
- "DeviceOnlineWithName": "{0} u lidh",
- "DeviceOfflineWithName": "{0} u shkëput",
"Collections": "Koleksionet",
"ChapterNameValue": "Kapituj",
- "Channels": "Kanalet",
- "CameraImageUploadedFrom": "Një foto e re nga kamera u ngarkua nga {0}",
"Books": "Librat",
"AuthenticationSucceededWithUserName": "{0} u identifikua me sukses",
"Artists": "Artistët",
- "Application": "Aplikacioni",
"AppDeviceValues": "Aplikacioni: {0}, Pajisja: {1}",
- "Albums": "Albumet",
"TaskCleanActivityLogDescription": "Pastro të dhënat mbi aktivitetin më të vjetra sesa koha e përcaktuar.",
"TaskCleanActivityLog": "Pastro të dhënat mbi aktivitetin",
"Undefined": "I papërcaktuar",
@@ -132,10 +103,10 @@
"TaskMoveTrickplayImagesDescription": "Zhvendos skedarët ekzistues të trickplay sipas cilësimeve të bibliotekës.",
"TaskDownloadMissingLyrics": "Shkarko tekstet e këngëve që mungojnë",
"TaskDownloadMissingLyricsDescription": "Shkarkon tekstet e këngëve",
- "TaskCleanCollectionsAndPlaylists": "Pastron koleksionet dhe listat e këngëve",
- "TaskCleanCollectionsAndPlaylistsDescription": "Heq elementet nga koleksionet dhe listat e këngëve që nuk ekzistojnë më.",
"TaskAudioNormalization": "Normalizimi i audios",
"TaskAudioNormalizationDescription": "Skannon skedarët për të dhëna të normalizimit të audios.",
"CleanupUserDataTaskDescription": "Pastron të gjitha të dhënat e përdorueseve (gjendja e shikimit, statusi i të preferuarave etj.) nga mediat që nuk janë më të pranishme për të paktën 90 ditë.",
- "CleanupUserDataTask": "Veprim për pastrimin të dhënave të përdorueseve"
+ "CleanupUserDataTask": "Veprim për pastrimin të dhënave të përdorueseve",
+ "LyricDownloadFailureFromForItem": "Teksti i këngës nuk arriti të shkarkohej nga {0} për {1}",
+ "Original": "Origjinal"
}
diff --git a/Emby.Server.Implementations/Localization/Core/sr.json b/Emby.Server.Implementations/Localization/Core/sr.json
index 76a136cf56..92f309c80c 100644
--- a/Emby.Server.Implementations/Localization/Core/sr.json
+++ b/Emby.Server.Implementations/Localization/Core/sr.json
@@ -1,9 +1,6 @@
{
- "UserPolicyUpdatedWithName": "Корисничке смернице ажуриране за {0}",
"NotificationOptionUserLockedOut": "Корисник закључан",
"VersionNumber": "Верзија {0}",
- "ValueSpecialEpisodeName": "Специјал - {0}",
- "ValueHasBeenAddedToLibrary": "{0} је додато у вашу медијску библиотеку",
"UserStoppedPlayingItemWithValues": "{0} завршио пуштање {1} на {2}",
"UserStartedPlayingItemWithValues": "{0} пушта {1} на {2}",
"UserPasswordChangedWithName": "Лозинка је промењена за корисника {0}",
@@ -13,23 +10,14 @@
"UserDownloadingItemWithValues": "{0} преузима {1}",
"UserDeletedWithName": "Корисник {0} је обрисан",
"UserCreatedWithName": "Корисник {0} је направљен",
- "User": "Корисник",
"TvShows": "ТВ серије",
- "System": "Систем",
- "Sync": "Усклади",
"SubtitleDownloadFailureFromForItem": "Неуспело преузимање титлова за {1} са {0}",
"StartupEmbyServerIsLoading": "Џелифин сервер се подиже. Покушајте поново убрзо.",
- "Songs": "Песме",
"Shows": "Серије",
- "ServerNameNeedsToBeRestarted": "{0} треба поново покренути",
- "ScheduledTaskStartedWithName": "{0} покренуто",
"ScheduledTaskFailedWithName": "{0} неуспело",
- "ProviderValue": "Пружалац: {0}",
"PluginUpdatedWithName": "{0} ажуриран",
"PluginUninstalledWithName": "{0} деинсталиран",
"PluginInstalledWithName": "{0} инсталиран",
- "Plugin": "Прикључак",
- "Playlists": "Листе",
"Photos": "Фотографије",
"NotificationOptionVideoPlaybackStopped": "Заустављено пуштање видеа",
"NotificationOptionVideoPlayback": "Покренуто пуштање видеа",
@@ -54,43 +42,26 @@
"Music": "Музика",
"Movies": "Филмови",
"MixedContent": "Мешовит садржај",
- "MessageServerConfigurationUpdated": "Серверска поставка је ажурирана",
- "MessageNamedServerConfigurationUpdatedWithValue": "Одељак серверске поставке {0} је ажуриран",
- "MessageApplicationUpdatedTo": "Џелифин сервер је ажуриран на {0}",
- "MessageApplicationUpdated": "Џелифин сервер је ажуриран",
"Latest": "Последње",
"LabelRunningTimeValue": "Време рада: {0}",
"LabelIpAddressValue": "ИП адреса: {0}",
- "ItemRemovedWithName": "{0} уклоњено из библиотеке",
- "ItemAddedWithName": "{0} додато у библиотеку",
"Inherit": "Наследи",
"HomeVideos": "Кућни Видео",
- "HeaderRecordingGroups": "Групе снимања",
"HeaderNextUp": "Следи",
"HeaderLiveTV": "ТВ уживо",
- "HeaderFavoriteSongs": "Омиљене песме",
"HeaderFavoriteShows": "Омиљене серије",
"HeaderFavoriteEpisodes": "Омиљене епизоде",
- "HeaderFavoriteArtists": "Омиљени извођачи",
- "HeaderFavoriteAlbums": "Омиљени албуми",
"HeaderContinueWatching": "Настави гледање",
- "HeaderAlbumArtists": "Извођачи албума",
"Genres": "Жанрови",
"Folders": "Фасцикле",
"Favorites": "Омиљено",
"FailedLoginAttemptWithUserName": "Неуспели покушај пријавe са {0}",
- "DeviceOnlineWithName": "{0} је повезан",
- "DeviceOfflineWithName": "{0} је прекинуо везу",
"Collections": "Колекције",
"ChapterNameValue": "Поглавље {0}",
- "Channels": "Канали",
- "CameraImageUploadedFrom": "Нова фотографија је учитана са {0}",
"Books": "Књиге",
"AuthenticationSucceededWithUserName": "{0} Успешна аутентификација",
"Artists": "Извођачи",
- "Application": "Апликација",
"AppDeviceValues": "Апликација: {0}, Уређај: {1}",
- "Albums": "Албуми",
"TaskDownloadMissingSubtitlesDescription": "Претражује интернет за недостајуће титлове на основу конфигурације метаподатака.",
"TaskDownloadMissingSubtitles": "Преузмите недостајуће титлове",
"TaskRefreshChannelsDescription": "Освежава информације о интернет каналу.",
@@ -125,17 +96,17 @@
"TaskKeyframeExtractor": "Екстрактор кључних сличица",
"HearingImpaired": "ослабљен слух",
"TaskAudioNormalization": "Нормализација звука",
- "TaskCleanCollectionsAndPlaylists": "Очистите колекције и плејлисте",
"TaskAudioNormalizationDescription": "Скенира датотеке за податке о нормализацији звука.",
"TaskRefreshTrickplayImages": "Направи сличице за визуелно премотавање",
"TaskRefreshTrickplayImagesDescription": "Прављење сличица које помажу код визуелног премотавања видео-снимака.",
"TaskDownloadMissingLyrics": "Преузми стихове који недостају",
- "TaskCleanCollectionsAndPlaylistsDescription": "Уклања ставке које више не постоје из колекција и плејлиста.",
"TaskExtractMediaSegments": "Скенирај сегменте медија",
"TaskExtractMediaSegmentsDescription": "Извлачи или добавља сегменте медија у додацима који раде са MediaSegment-ом.",
"TaskMoveTrickplayImagesDescription": "Премешта постојеће сличице за визуелно премотавање сходно подешавањима библиотеке.",
"CleanupUserDataTask": "Задатак чишћења корисничких података",
"CleanupUserDataTaskDescription": "Чисти све корисничке податке (напредак гледања, ознаке за омиљено...) медија који нису доступни 90 дана или дуже.",
"TaskMoveTrickplayImages": "Промени локацију сличица за визуелно премотавање",
- "TaskDownloadMissingLyricsDescription": "Преузми стихове песама"
+ "TaskDownloadMissingLyricsDescription": "Преузми стихове песама",
+ "LyricDownloadFailureFromForItem": "Није успело преузимање стихова са {0} за {1}",
+ "Original": "Изворно"
}
diff --git a/Emby.Server.Implementations/Localization/Core/sv.json b/Emby.Server.Implementations/Localization/Core/sv.json
index 23acd3c532..7384967122 100644
--- a/Emby.Server.Implementations/Localization/Core/sv.json
+++ b/Emby.Server.Implementations/Localization/Core/sv.json
@@ -1,41 +1,24 @@
{
- "Albums": "Album",
"AppDeviceValues": "Applikation: {0}, Enhet: {1}",
- "Application": "Applikation",
"Artists": "Artister",
"AuthenticationSucceededWithUserName": "{0} har autentiserats",
"Books": "Böcker",
- "CameraImageUploadedFrom": "En ny kamerabild har laddats upp från {0}",
- "Channels": "Kanaler",
"ChapterNameValue": "Kapitel {0}",
"Collections": "Samlingar",
- "DeviceOfflineWithName": "{0} har kopplat ned",
- "DeviceOnlineWithName": "{0} är ansluten",
"FailedLoginAttemptWithUserName": "Misslyckat inloggningsförsök från {0}",
"Favorites": "Favoriter",
"Folders": "Mappar",
"Genres": "Genrer",
- "HeaderAlbumArtists": "Albumartister",
"HeaderContinueWatching": "Fortsätt titta",
- "HeaderFavoriteAlbums": "Favoritalbum",
- "HeaderFavoriteArtists": "Favoritartister",
"HeaderFavoriteEpisodes": "Favoritavsnitt",
"HeaderFavoriteShows": "Favoritserier",
- "HeaderFavoriteSongs": "Favoritlåtar",
"HeaderLiveTV": "Direktsänd TV",
"HeaderNextUp": "Nästa",
- "HeaderRecordingGroups": "Inspelningsgrupper",
"HomeVideos": "Hemmavideor",
"Inherit": "Ärv",
- "ItemAddedWithName": "{0} lades till i biblioteket",
- "ItemRemovedWithName": "{0} togs bort från biblioteket",
"LabelIpAddressValue": "IP-adress: {0}",
"LabelRunningTimeValue": "Speltid: {0}",
"Latest": "Senaste",
- "MessageApplicationUpdated": "Jellyfin Server har uppdaterats",
- "MessageApplicationUpdatedTo": "Jellyfin Server har uppdaterats till {0}",
- "MessageNamedServerConfigurationUpdatedWithValue": "Serverinställningarna {0} har uppdaterats",
- "MessageServerConfigurationUpdated": "Serverkonfigurationen har uppdaterats",
"MixedContent": "Blandat innehåll",
"Movies": "Filmer",
"Music": "Musik",
@@ -61,23 +44,14 @@
"NotificationOptionVideoPlayback": "Videouppspelning har påbörjats",
"NotificationOptionVideoPlaybackStopped": "Videouppspelning stoppades",
"Photos": "Bilder",
- "Playlists": "Spellistor",
- "Plugin": "Tillägg",
"PluginInstalledWithName": "{0} installerades",
"PluginUninstalledWithName": "{0} avinstallerades",
"PluginUpdatedWithName": "{0} uppdaterades",
- "ProviderValue": "Leverantör: {0}",
"ScheduledTaskFailedWithName": "{0} misslyckades",
- "ScheduledTaskStartedWithName": "{0} startades",
- "ServerNameNeedsToBeRestarted": "{0} behöver startas om",
"Shows": "Serier",
- "Songs": "Låtar",
"StartupEmbyServerIsLoading": "Jellyfin Server arbetar. Pröva igen snart.",
"SubtitleDownloadFailureFromForItem": "Undertexter kunde inte laddas ner från {0} till {1}",
- "Sync": "Synk",
- "System": "System",
- "TvShows": "TV-serier",
- "User": "Användare",
+ "TvShows": "Tv-serier",
"UserCreatedWithName": "Användaren {0} har skapats",
"UserDeletedWithName": "Användaren {0} har tagits bort",
"UserDownloadingItemWithValues": "{0} laddar ner {1}",
@@ -85,11 +59,8 @@
"UserOfflineFromDevice": "{0} har kopplat ned från {1}",
"UserOnlineFromDevice": "{0} är uppkopplad från {1}",
"UserPasswordChangedWithName": "Lösenordet för {0} har ändrats",
- "UserPolicyUpdatedWithName": "Användarpolicyn har uppdaterats för {0}",
"UserStartedPlayingItemWithValues": "{0} spelar {1} på {2}",
"UserStoppedPlayingItemWithValues": "{0} har stoppat uppspelningen av {1} på {2}",
- "ValueHasBeenAddedToLibrary": "{0} har lagts till i ditt mediebibliotek",
- "ValueSpecialEpisodeName": "Specialavsnitt - {0}",
"VersionNumber": "Version {0}",
"TaskDownloadMissingSubtitlesDescription": "Söker på internet efter saknade undertexter baserat på metadata-konfiguration.",
"TaskDownloadMissingSubtitles": "Ladda ner saknade undertexter",
@@ -126,9 +97,7 @@
"HearingImpaired": "Hörselskadad",
"TaskRefreshTrickplayImages": "Generera Trickplay-bilder",
"TaskRefreshTrickplayImagesDescription": "Skapar trickplay-förhandsvisningar för videor i aktiverade bibliotek.",
- "TaskCleanCollectionsAndPlaylists": "Rensa upp samlingar och spellistor",
"TaskAudioNormalization": "Ljudnormalisering",
- "TaskCleanCollectionsAndPlaylistsDescription": "Tar bort objekt från samlingar och spellistor som inte längre finns.",
"TaskAudioNormalizationDescription": "Skannar filer för ljudnormaliseringsdata.",
"TaskDownloadMissingLyrics": "Ladda ner saknad låttext",
"TaskDownloadMissingLyricsDescription": "Laddar ner låttexter",
@@ -137,5 +106,7 @@
"TaskMoveTrickplayImages": "Migrera platsen för Trickplay-bilder",
"TaskMoveTrickplayImagesDescription": "Flyttar befintliga trickplay-filer enligt bibliotekets inställningar.",
"CleanupUserDataTaskDescription": "Tar bort all användardata (såsom vad du sett, favoriter med mera) för media som inte funnits på enheten på minst 90 dagar.",
- "CleanupUserDataTask": "Uppgift för rensning av användardata"
+ "CleanupUserDataTask": "Uppgift för rensning av användardata",
+ "Original": "Original",
+ "LyricDownloadFailureFromForItem": "Misslyckades att ladda ner låttexter från {0} för {1}"
}
diff --git a/Emby.Server.Implementations/Localization/Core/sw.json b/Emby.Server.Implementations/Localization/Core/sw.json
index 0967ef424b..c117a0eae2 100644
--- a/Emby.Server.Implementations/Localization/Core/sw.json
+++ b/Emby.Server.Implementations/Localization/Core/sw.json
@@ -1 +1,5 @@
-{}
+{
+ "Artists": "Wasanii",
+ "Books": "Vitabu",
+ "Collections": "Mikusanyiko"
+}
diff --git a/Emby.Server.Implementations/Localization/Core/ta.json b/Emby.Server.Implementations/Localization/Core/ta.json
index defdc5925a..f613b973db 100644
--- a/Emby.Server.Implementations/Localization/Core/ta.json
+++ b/Emby.Server.Implementations/Localization/Core/ta.json
@@ -1,13 +1,11 @@
{
"VersionNumber": "பதிப்பு {0}",
- "ValueSpecialEpisodeName": "சிறப்பு - {0}",
"TasksMaintenanceCategory": "பராமரிப்பு",
"TaskCleanCache": "தற்காலிக சேமிப்பு கோப்பகத்தை சுத்தம் செய்யவும்",
"TaskRefreshChapterImages": "அத்தியாயப் படங்களை பிரித்தெடுக்கவும்",
"TaskRefreshPeople": "மக்களைப் புதுப்பிக்கவும்",
"TaskCleanTranscode": "டிரான்ஸ்கோட் கோப்பகத்தை சுத்தம் செய்யவும்",
"TaskRefreshChannelsDescription": "இணையச் சேனல் தகவல்களைப் புதுப்பிக்கிறது.",
- "System": "ஒருங்கியம்",
"NotificationOptionTaskFailed": "திட்டமிடப்பட்ட பணி தோல்வியடைந்தது",
"NotificationOptionPluginUpdateInstalled": "உட்செருகி புதுப்பிக்கப்பட்டது",
"NotificationOptionPluginUninstalled": "உட்செருகி நீக்கப்பட்டது",
@@ -15,17 +13,10 @@
"NotificationOptionPluginError": "உட்செருகி செயலிழந்தது",
"NotificationOptionCameraImageUploaded": "புகைப்படம் பதிவேற்றப்பட்டது",
"MixedContent": "கலப்பு உள்ளடக்கங்கள்",
- "MessageServerConfigurationUpdated": "சேவையக அமைப்புகள் புதுப்பிக்கப்பட்டன",
- "MessageApplicationUpdatedTo": "ஜெல்லிஃபின் சேவையகம் {0} இற்கு புதுப்பிக்கப்பட்டது",
- "MessageApplicationUpdated": "ஜெல்லிஃபின் சேவையகம் புதுப்பிக்கப்பட்டது",
"Inherit": "மரபுரிமையாகப் பெறு",
- "HeaderRecordingGroups": "பதிவு குழுக்கள்",
"Folders": "கோப்புறைகள்",
"FailedLoginAttemptWithUserName": "{0} இலிருந்து உள்நுழைவு முயற்சி தோல்வியடைந்தது",
- "DeviceOnlineWithName": "{0} இணைக்கப்பட்டது",
- "DeviceOfflineWithName": "{0} துண்டிக்கப்பட்டது",
"Collections": "தொகுப்புகள்",
- "CameraImageUploadedFrom": "{0} இல் இருந்து புதிய புகைப்படம் பதிவேற்றப்பட்டது",
"AppDeviceValues": "செயலி: {0}, சாதனம்: {1}",
"TaskDownloadMissingSubtitles": "விடுபட்டுபோன வசன வரிகளைப் பதிவிறக்கு",
"TaskRefreshChannels": "சேனல்களை புதுப்பி",
@@ -34,27 +25,18 @@
"TasksChannelsCategory": "இணைய சேனல்கள்",
"TasksApplicationCategory": "செயலி",
"TasksLibraryCategory": "நூலகம்",
- "UserPolicyUpdatedWithName": "பயனர் கொள்கை {0} இற்கு புதுப்பிக்கப்பட்டுள்ளது",
"UserPasswordChangedWithName": "{0} பயனருக்கு கடவுச்சொல் மாற்றப்பட்டுள்ளது",
"UserLockedOutWithName": "பயனர் {0} முடக்கப்பட்டார்",
"UserDownloadingItemWithValues": "{0} ஆல் {1} பதிவிறக்கப்படுகிறது",
"UserDeletedWithName": "பயனர் {0} நீக்கப்பட்டார்",
"UserCreatedWithName": "பயனர் {0} உருவாக்கப்பட்டார்",
- "User": "பயனர்",
"TvShows": "தொலைக்காட்சித் தொடர்கள்",
- "Sync": "ஒத்திசைவு",
"StartupEmbyServerIsLoading": "ஜெல்லிஃபின் சேவையகம் துவங்குகிறது. சிறிது நேரம் கழித்து முயற்சிக்கவும்.",
- "Songs": "பாடல்கள்",
"Shows": "நிகழ்ச்சிகள்",
- "ServerNameNeedsToBeRestarted": "{0} மறுதொடக்கம் செய்யப்பட வேண்டும்",
- "ScheduledTaskStartedWithName": "{0} துவங்கியது",
"ScheduledTaskFailedWithName": "{0} தோல்வியடைந்தது",
- "ProviderValue": "வழங்குநர்: {0}",
"PluginUpdatedWithName": "{0} புதுப்பிக்கப்பட்டது",
"PluginUninstalledWithName": "{0} நீக்கப்பட்டது",
"PluginInstalledWithName": "{0} நிறுவப்பட்டது",
- "Plugin": "உட்செருகி",
- "Playlists": "தொடர் பட்டியல்கள்",
"Photos": "புகைப்படங்கள்",
"NotificationOptionVideoPlaybackStopped": "நிகழ்பட ஒளிபரப்பு நிறுத்தப்பட்டது",
"NotificationOptionVideoPlayback": "நிகழ்பட ஒளிபரப்பு துவங்கியது",
@@ -75,28 +57,18 @@
"Latest": "புதியவை",
"LabelRunningTimeValue": "ஓடும் நேரம்: {0}",
"LabelIpAddressValue": "ஐபி முகவரி: {0}",
- "ItemRemovedWithName": "{0} நூலகத்திலிருந்து அகற்றப்பட்டது",
- "ItemAddedWithName": "{0} நூலகத்தில் சேர்க்கப்பட்டது",
"HeaderNextUp": "அடுத்தது",
"HeaderLiveTV": "நேரடித் தொலைக்காட்சி",
- "HeaderFavoriteSongs": "பிடித்த பாடல்கள்",
"HeaderFavoriteShows": "பிடித்த தொடர்கள்",
"HeaderFavoriteEpisodes": "பிடித்த அத்தியாயங்கள்",
- "HeaderFavoriteArtists": "பிடித்த கலைஞர்கள்",
- "HeaderFavoriteAlbums": "பிடித்த ஆல்பங்கள்",
"HeaderContinueWatching": "தொடர்ந்து பார்",
- "HeaderAlbumArtists": "கலைஞரின் ஆல்பம்",
"Genres": "வகைகள்",
"Favorites": "பிடித்தவை",
"ChapterNameValue": "அத்தியாயம் {0}",
- "Channels": "சேனல்கள்",
"Books": "புத்தகங்கள்",
"AuthenticationSucceededWithUserName": "{0} வெற்றிகரமாக அங்கீகரிக்கப்பட்டது",
"Artists": "கலைஞர்கள்",
- "Application": "செயலி",
- "Albums": "ஆல்பங்கள்",
"NewVersionIsAvailable": "ஜெல்லிஃபின் சேவையகத்தின் புதிய பதிப்பு பதிவிறக்கத்திற்கு கிடைக்கிறது.",
- "MessageNamedServerConfigurationUpdatedWithValue": "சேவையக உள்ளமைவு பிரிவு {0} புதுப்பிக்கப்பட்டது",
"TaskCleanCacheDescription": "கணினிக்கு இனி தேவைப்படாத தற்காலிக கோப்புகளை நீக்கு.",
"UserOfflineFromDevice": "{0} இலிருந்து {1} துண்டிக்கப்பட்டுள்ளது",
"SubtitleDownloadFailureFromForItem": "வசன வரிகள் {0} இல் இருந்து {1} க்கு பதிவிறக்கத் தவறிவிட்டன",
@@ -108,7 +80,6 @@
"TaskCleanLogs": "பதிவு அடைவை சுத்தம் செய்யுங்கள்",
"TaskRefreshLibraryDescription": "புதிய கோப்புகளுக்காக உங்கள் ஊடக நூலகத்தை ஆராய்ந்து மீத்தரவை புதுப்பிக்கும்.",
"TaskRefreshChapterImagesDescription": "அத்தியாயங்களைக் கொண்ட வீடியோக்களுக்கான சிறு உருவங்களை உருவாக்குகிறது.",
- "ValueHasBeenAddedToLibrary": "உங்கள் மீடியா நூலகத்தில் {0} சேர்க்கப்பட்டது",
"UserOnlineFromDevice": "{1} இருந்து {0} ஆன்லைன்",
"HomeVideos": "முகப்பு வீடியோக்கள்",
"UserStoppedPlayingItemWithValues": "{0} {2} இல் {1} முடித்துவிட்டது",
@@ -126,8 +97,6 @@
"HearingImpaired": "செவித்திறன் குறைபாடுடையவர்",
"TaskRefreshTrickplayImages": "முன்னோட்ட படங்களை உருவாக்கு",
"TaskRefreshTrickplayImagesDescription": "செயல்பாட்டில் உள்ள தொகுப்புகளுக்கு முன்னோட்ட படங்களை உருவாக்கும்.",
- "TaskCleanCollectionsAndPlaylists": "சேகரிப்புகள் மற்றும் பிளேலிஸ்ட்களை சுத்தம் செய்யவும்",
- "TaskCleanCollectionsAndPlaylistsDescription": "சேகரிப்புகள் மற்றும் பிளேலிஸ்ட்களில் இருந்து உருப்படிகளை நீக்குகிறது.",
"TaskAudioNormalization": "ஆடியோ இயல்பாக்கம்",
"TaskAudioNormalizationDescription": "ஆடியோ இயல்பாக்குதல் தரவுக்காக கோப்புகளை ஸ்கேன் செய்கிறது.",
"TaskDownloadMissingLyrics": "விடுபட்ட பாடல் வரிகளைப் பதிவிறக்கவும்",
diff --git a/Emby.Server.Implementations/Localization/Core/te.json b/Emby.Server.Implementations/Localization/Core/te.json
index ca9e345214..7ac770752e 100644
--- a/Emby.Server.Implementations/Localization/Core/te.json
+++ b/Emby.Server.Implementations/Localization/Core/te.json
@@ -1,51 +1,31 @@
{
- "ValueSpecialEpisodeName": "ప్రత్యేక - {0}",
- "Sync": "సమకాలీకరించు",
- "Songs": "పాటలు",
"Shows": "ప్రదర్శనలు",
- "Playlists": "ప్లేజాబితాలు",
"Photos": "ఫోటోలు",
"MusicVideos": "మ్యూజిక్ వీడియోలు",
"Music": "సంగీతం",
"Movies": "సినిమాలు",
"HeaderContinueWatching": "చూడటం కొనసాగించండి",
- "HeaderAlbumArtists": "ఆల్బమ్ కళాకారులు",
"Genres": "శైలులు",
"Forced": "బలవంతంగా",
"Folders": "ఫోల్డర్లు",
"Favorites": "ఇష్టమైనవి",
"Default": "డిఫాల్ట్",
"Collections": "సేకరణలు",
- "Channels": "ఛానెల్‌లు",
"Books": "పుస్తకాలు",
"Artists": "కళాకారులు",
- "Albums": "ఆల్బమ్‌లు",
"HearingImpaired": "వినికిడి లోపం",
"HomeVideos": "హోమ్ వీడియోలు",
"AppDeviceValues": "అప్లికేషన్ : {0}, పరికరం: {1}",
- "Application": "అప్లికేషన్",
"AuthenticationSucceededWithUserName": "విజయవంతంగా ఆమోదించబడింది",
- "CameraImageUploadedFrom": "{0} నుండి కొత్త కెమెరా చిత్రం అప్‌లోడ్ చేయబడింది",
"ChapterNameValue": "అధ్యాయం",
- "DeviceOfflineWithName": "{0} డిస్‌కనెక్ట్ చేయబడింది",
- "DeviceOnlineWithName": "{0} కనెక్ట్ చేయబడింది",
"External": "బాహ్య",
"FailedLoginAttemptWithUserName": "{0} నుండి విఫలమైన లాగిన్ ప్రయత్నం",
- "HeaderFavoriteAlbums": "ఇష్టమైన ఆల్బమ్‌లు",
- "HeaderFavoriteArtists": "ఇష్టమైన కళాకారులు",
"HeaderFavoriteEpisodes": "ఇష్టమైన ఎపిసోడ్‌లు",
"HeaderFavoriteShows": "ఇష్టమైన ప్రదర్శనలు",
- "HeaderFavoriteSongs": "ఇష్టమైన పాటలు",
"HeaderLiveTV": "ప్రత్యక్ష TV",
"HeaderNextUp": "తదుపరి",
- "HeaderRecordingGroups": "రికార్డింగ్ గుంపులు",
- "MessageApplicationUpdated": "జెల్లీఫిన్ సర్వర్ అప్‌డేట్ చేయడం పూర్తి అయ్యింది",
- "MessageApplicationUpdatedTo": "జెల్లీఫిన్ సర్వర్ {0} వెర్షన్ కి అప్‌డేట్ చెయ్యబడింది",
- "MessageServerConfigurationUpdated": "సర్వర్ కన్ఫిగరేషన్ అప్డేట్ చేయబడింది",
"NewVersionIsAvailable": "జెల్లీఫిన్ సర్వర్ యొక్క కొత్త వెర్షన్ డౌన్‌లోడ్ చేసుకోవడానికి అందుబాటులో ఉంది.",
"NotificationOptionApplicationUpdateInstalled": "అప్లికేషన్ అప్‌డేట్ ఇన్‌స్టాల్ చేయబడింది",
- "ItemAddedWithName": "{0} లైబ్రరీకి జోడించబడింది",
- "ItemRemovedWithName": "లైబ్రరీ నుండి {0} తీసివేయబడింది",
"LabelIpAddressValue": "ఐపీ చిరునామా: {0}",
"LabelRunningTimeValue": "నడుస్తున్న సమయం: {0}",
"Latest": "తాజా",
diff --git a/Emby.Server.Implementations/Localization/Core/th.json b/Emby.Server.Implementations/Localization/Core/th.json
index 65ddb55e94..89c2c26748 100644
--- a/Emby.Server.Implementations/Localization/Core/th.json
+++ b/Emby.Server.Implementations/Localization/Core/th.json
@@ -1,10 +1,7 @@
{
- "ProviderValue": "ผู้ให้บริการ: {0}",
"PluginUpdatedWithName": "อัปเดต {0} แล้ว",
"PluginUninstalledWithName": "ถอนการติดตั้ง {0} แล้ว",
"PluginInstalledWithName": "ติดตั้ง {0} แล้ว",
- "Plugin": "ปลั๊กอิน",
- "Playlists": "เพลย์ลิสต์",
"Photos": "รูปภาพ",
"NotificationOptionVideoPlaybackStopped": "หยุดเล่นวิดีโอ",
"NotificationOptionVideoPlayback": "เริ่มเล่นวิดีโอ",
@@ -30,48 +27,28 @@
"Music": "ดนตรี",
"Movies": "ภาพยนตร์",
"MixedContent": "เนื้อหาผสม",
- "MessageServerConfigurationUpdated": "อัปเดตการกำหนดค่าเซิร์ฟเวอร์แล้ว",
- "MessageNamedServerConfigurationUpdatedWithValue": "อัปเดตการกำหนดค่าเซิร์ฟเวอร์ในส่วน {0} แล้ว",
- "MessageApplicationUpdatedTo": "เซิร์ฟเวอร์ Jellyfin ได้รับการอัปเดตเป็น {0}",
- "MessageApplicationUpdated": "อัพเดตเซิร์ฟเวอร์ Jellyfin แล้ว",
"Latest": "ล่าสุด",
"LabelRunningTimeValue": "ผ่านไปแล้ว: {0}",
"LabelIpAddressValue": "ที่อยู่ IP: {0}",
- "ItemRemovedWithName": "{0} ถูกลบออกจากไลบรารี",
- "ItemAddedWithName": "{0} ถูกเพิ่มลงในไลบรารีแล้ว",
"Inherit": "สืบทอด",
"HomeVideos": "โฮมวิดีโอ",
- "HeaderRecordingGroups": "กลุ่มการบันทึก",
"HeaderNextUp": "ถัดไป",
"HeaderLiveTV": "ทีวีสด",
- "HeaderFavoriteSongs": "เพลงที่ชื่นชอบ",
"HeaderFavoriteShows": "รายการที่ชื่นชอบ",
"HeaderFavoriteEpisodes": "ตอนที่ชื่นชอบ",
- "HeaderFavoriteArtists": "ศิลปินที่ชื่นชอบ",
- "HeaderFavoriteAlbums": "อัมบั้มที่ชื่นชอบ",
"HeaderContinueWatching": "ดูต่อ",
- "HeaderAlbumArtists": "ศิลปินอัลบั้ม",
"Genres": "ประเภท",
"Folders": "โฟลเดอร์",
"Favorites": "รายการโปรด",
"FailedLoginAttemptWithUserName": "ความพยายามในการเข้าสู่ระบบล้มเหลวจาก {0}",
- "DeviceOnlineWithName": "{0} เชื่อมต่อสำเร็จแล้ว",
- "DeviceOfflineWithName": "{0} ยกเลิกการเชื่อมต่อแล้ว",
"Collections": "คอลเลกชัน",
"ChapterNameValue": "บทที่ {0}",
- "Channels": "ช่อง",
- "CameraImageUploadedFrom": "ภาพถ่ายใหม่ได้ถูกอัปโหลดมาจาก {0}",
"Books": "หนังสือ",
"AuthenticationSucceededWithUserName": "{0} ยืนยันตัวตนสำเร็จแล้ว",
"Artists": "ศิลปิน",
- "Application": "แอปพลิเคชัน",
"AppDeviceValues": "แอป: {0}, อุปกรณ์: {1}",
- "Albums": "อัลบั้ม",
- "ScheduledTaskStartedWithName": "{0} เริ่มต้น",
"ScheduledTaskFailedWithName": "{0} ล้มเหลว",
- "Songs": "เพลง",
"Shows": "รายการ",
- "ServerNameNeedsToBeRestarted": "{0} ต้องการการรีสตาร์ท",
"TaskDownloadMissingSubtitlesDescription": "ค้นหาคำบรรยายที่หายไปในอินเทอร์เน็ตตามค่ากำหนดในข้อมูลเมตา",
"TaskDownloadMissingSubtitles": "ดาวน์โหลดคำบรรยายที่ขาดหายไป",
"TaskRefreshChannelsDescription": "รีเฟรชข้อมูลช่องอินเทอร์เน็ต",
@@ -95,11 +72,8 @@
"TasksLibraryCategory": "ไลบรารี",
"TasksMaintenanceCategory": "ปิดซ่อมบำรุง",
"VersionNumber": "เวอร์ชัน {0}",
- "ValueSpecialEpisodeName": "พิเศษ - {0}",
- "ValueHasBeenAddedToLibrary": "เพิ่ม {0} ลงในไลบรารีสื่อของคุณแล้ว",
"UserStoppedPlayingItemWithValues": "{0} เล่นเสร็จแล้ว {1} บน {2}",
"UserStartedPlayingItemWithValues": "{0} กำลังเล่น {1} บน {2}",
- "UserPolicyUpdatedWithName": "มีการอัปเดตนโยบายผู้ใช้ของ {0}",
"UserPasswordChangedWithName": "มีการเปลี่ยนรหัสผ่านของผู้ใช้ {0}",
"UserOnlineFromDevice": "{0} ออนไลน์จาก {1}",
"UserOfflineFromDevice": "{0} ได้ยกเลิกการเชื่อมต่อจาก {1}",
@@ -107,10 +81,7 @@
"UserDownloadingItemWithValues": "{0} กำลังดาวน์โหลด {1}",
"UserDeletedWithName": "ลบผู้ใช้ {0} แล้ว",
"UserCreatedWithName": "สร้างผู้ใช้ {0} แล้ว",
- "User": "ผู้ใช้งาน",
"TvShows": "รายการทีวี",
- "System": "ระบบ",
- "Sync": "ซิงค์",
"SubtitleDownloadFailureFromForItem": "ไม่สามารถดาวน์โหลดคำบรรยายจาก {0} สำหรับ {1} ได้",
"StartupEmbyServerIsLoading": "กำลังโหลดเซิร์ฟเวอร์ Jellyfin โปรดลองอีกครั้งในอีกสักครู่",
"Default": "ค่าเริ่มต้น",
@@ -130,8 +101,6 @@
"TaskDownloadMissingLyricsDescription": "ดาวน์โหลดเนื้อเพลงสำหรับเพลง",
"TaskAudioNormalization": "ปรับระดับเสียงให้สม่ำเสมอ",
"TaskAudioNormalizationDescription": "สแกนไฟล์เพื่อค้นหาข้อมูลการปรับระดับเสียงให้สม่ำเสมอ",
- "TaskCleanCollectionsAndPlaylists": "จัดระเบียบคอลเลกชันและเพลย์ลิสต์",
- "TaskCleanCollectionsAndPlaylistsDescription": "ลบรายการออกจากคอลเลกชันและเพลย์ลิสต์ที่ไม่มีแล้ว",
"TaskExtractMediaSegments": "การสแกนส่วนของสื่อมีเดีย",
"TaskMoveTrickplayImagesDescription": "ย้ายไฟล์ Trickplay ตามการตั้งค่าของไลบรารี",
"TaskExtractMediaSegmentsDescription": "แยกหรือดึงส่วนของสื่อจากปลั๊กอินที่เปิดใช้งาน MediaSegment",
diff --git a/Emby.Server.Implementations/Localization/Core/tr.json b/Emby.Server.Implementations/Localization/Core/tr.json
index d13f662e4c..0c42d4a55f 100644
--- a/Emby.Server.Implementations/Localization/Core/tr.json
+++ b/Emby.Server.Implementations/Localization/Core/tr.json
@@ -1,41 +1,24 @@
{
- "Albums": "Albümler",
"AppDeviceValues": "Uygulama: {0}, Aygıt: {1}",
- "Application": "Uygulama",
"Artists": "Sanatçılar",
"AuthenticationSucceededWithUserName": "{0} kimliği başarıyla doğrulandı",
"Books": "Kitaplar",
- "CameraImageUploadedFrom": "{0} 'den yeni bir kamera resmi yüklendi",
- "Channels": "Kanallar",
"ChapterNameValue": "{0}. Bölüm",
"Collections": "Koleksiyonlar",
- "DeviceOfflineWithName": "{0} bağlantısı kesildi",
- "DeviceOnlineWithName": "{0} bağlı",
"FailedLoginAttemptWithUserName": "{0} kullanıcısının başarısız oturum açma girişimi",
"Favorites": "Favoriler",
"Folders": "Klasörler",
"Genres": "Türler",
- "HeaderAlbumArtists": "Albüm sanatçıları",
"HeaderContinueWatching": "İzlemeye Devam Et",
- "HeaderFavoriteAlbums": "Favori Albümler",
- "HeaderFavoriteArtists": "Favori Sanatçılar",
"HeaderFavoriteEpisodes": "Favori Bölümler",
"HeaderFavoriteShows": "Favori Diziler",
- "HeaderFavoriteSongs": "Favori Şarkılar",
"HeaderLiveTV": "Canlı TV",
"HeaderNextUp": "Sıradaki Bölümler",
- "HeaderRecordingGroups": "Kayıt Grupları",
"HomeVideos": "Ana Ekran Videoları",
"Inherit": "Devral",
- "ItemAddedWithName": "{0} kütüphaneye eklendi",
- "ItemRemovedWithName": "{0} kütüphaneden silindi",
"LabelIpAddressValue": "IP adresi: {0}",
"LabelRunningTimeValue": "Oynatma süresi: {0}",
"Latest": "En son",
- "MessageApplicationUpdated": "Jellyfin Sunucusu güncellendi",
- "MessageApplicationUpdatedTo": "Jellyfin Sunucusu {0} sürümüne güncellendi",
- "MessageNamedServerConfigurationUpdatedWithValue": "Sunucu yapılandırma bölümü {0} güncellendi",
- "MessageServerConfigurationUpdated": "Sunucu yapılandırması güncellendi",
"MixedContent": "Karışık içerik",
"Movies": "Filmler",
"Music": "Müzik",
@@ -61,23 +44,14 @@
"NotificationOptionVideoPlayback": "Video oynatma başladı",
"NotificationOptionVideoPlaybackStopped": "Video oynatma durduruldu",
"Photos": "Fotoğraflar",
- "Playlists": "Çalma listeleri",
- "Plugin": "Eklenti",
"PluginInstalledWithName": "{0} yüklendi",
"PluginUninstalledWithName": "{0} kaldırıldı",
"PluginUpdatedWithName": "{0} güncellendi",
- "ProviderValue": "Sağlayıcı: {0}",
"ScheduledTaskFailedWithName": "{0} başarısız oldu",
- "ScheduledTaskStartedWithName": "{0} başladı",
- "ServerNameNeedsToBeRestarted": "{0} yeniden başlatılması gerekiyor",
"Shows": "Diziler",
- "Songs": "Şarkılar",
"StartupEmbyServerIsLoading": "Jellyfin Sunucusu yükleniyor. Lütfen kısa süre sonra tekrar deneyin.",
"SubtitleDownloadFailureFromForItem": "{1} için altyazılar {0} sağlayıcısından indirilemedi",
- "Sync": "Eşzamanlama",
- "System": "Sistem",
"TvShows": "Diziler",
- "User": "Kullanıcı",
"UserCreatedWithName": "{0} kullanıcısı oluşturuldu",
"UserDeletedWithName": "{0} kullanıcısı silindi",
"UserDownloadingItemWithValues": "{0} kullanıcısı {1} medyasını indiriyor",
@@ -85,11 +59,8 @@
"UserOfflineFromDevice": "{0} kullanıcısının {1} ile bağlantısı kesildi",
"UserOnlineFromDevice": "{0} kullanıcısı {1} ile çevrimiçi",
"UserPasswordChangedWithName": "{0} kullanıcısının parolası değiştirildi",
- "UserPolicyUpdatedWithName": "{0} için kullanıcı politikası güncellendi",
"UserStartedPlayingItemWithValues": "{0}, {2} cihazında {1} izliyor",
"UserStoppedPlayingItemWithValues": "{0}, {2} cihazında {1} izlemeyi bitirdi",
- "ValueHasBeenAddedToLibrary": "{0} medya kütüphanenize eklendi",
- "ValueSpecialEpisodeName": "Özel - {0}",
"VersionNumber": "Sürüm {0}",
"TaskCleanCache": "Önbellek Dizinini Temizle",
"TasksChannelsCategory": "İnternet Kanalları",
@@ -126,8 +97,6 @@
"HearingImpaired": "Duyma Engelli",
"TaskRefreshTrickplayImages": "Hızlı Önizleme Görsellerini Oluştur",
"TaskRefreshTrickplayImagesDescription": "Etkin kütüphanelerdeki videolar için hızlı önizleme görselleri oluşturur.",
- "TaskCleanCollectionsAndPlaylistsDescription": "Artık var olmayan koleksiyon ve çalma listelerindeki ögeleri kaldırır.",
- "TaskCleanCollectionsAndPlaylists": "Koleksiyonları ve çalma listelerini temizleyin",
"TaskAudioNormalizationDescription": "Ses normalleştirme verileri için dosyaları tarar.",
"TaskAudioNormalization": "Ses Normalleştirme",
"TaskExtractMediaSegments": "Medya Segmenti Tarama",
@@ -137,5 +106,7 @@
"TaskDownloadMissingLyricsDescription": "Şarkı sözlerini indirir",
"TaskExtractMediaSegmentsDescription": "MediaSegment özelliği etkin olan eklentilerden medya segmentlerini çıkarır veya alır.",
"CleanupUserDataTask": "Kullanıcı verisi temizleme görevi",
- "CleanupUserDataTaskDescription": "En az 90 gün boyunca artık mevcut olmayan medyadaki tüm kullanıcı verilerini (İzleme durumu, favori durumu vb.) temizler."
+ "CleanupUserDataTaskDescription": "En az 90 gün boyunca artık mevcut olmayan medyadaki tüm kullanıcı verilerini (İzleme durumu, favori durumu vb.) temizler.",
+ "LyricDownloadFailureFromForItem": "{1} şarkı sözleri {0} adresinden indirilemedi",
+ "Original": "Orijinal"
}
diff --git a/Emby.Server.Implementations/Localization/Core/ug.json b/Emby.Server.Implementations/Localization/Core/ug.json
index 0bcbffb41a..1d5adecb26 100644
--- a/Emby.Server.Implementations/Localization/Core/ug.json
+++ b/Emby.Server.Implementations/Localization/Core/ug.json
@@ -1,22 +1,15 @@
{
"ChapterNameValue": "باب {0}",
- "Channels": "قانال",
- "CameraImageUploadedFrom": "{0} ئورۇندىن يېڭى سۈرەت چىقىرىلدى",
"Books": "كىتاب",
"AuthenticationSucceededWithUserName": "{0} تەستىقلاش مۇۋاپىقىيەتلىك بولدى",
"Artists": "سەنئەتكار",
- "Albums": "پىلاستىنكا",
- "DeviceOnlineWithName": "{0} ئۇلاندى",
- "DeviceOfflineWithName": "{0} ئۈزۈلدى",
"Collections": "توپلام",
- "Application": "ئەپ",
"AppDeviceValues": "ئەپ: {0}، ئۈسكۈنە: {1}",
"HeaderLiveTV": "تور تېلېۋىزىيەسى",
"Default": "سۈكۈتتىكى",
"Folders": "ھۆججەت خالتىسى",
"Favorites": "ساقلىغۇچ",
"LabelRunningTimeValue": "ئىجرا بولغان ۋاقتى:{0}",
- "HeaderRecordingGroups": "خاتىرلەش گۇرۇپىسى",
"Forced": "ئەڭ",
"TaskKeyframeExtractor": "ھالقىلىق رامكا ئاجراتقۇچ",
"TaskKeyframeExtractorDescription": "سىن ھۆججەتلىرىدىن رامكا ئاجرىتىپ، تېخىمۇ ئېنىق بولغان HLS قويۇلۇش تىزىملىكىنى قۇرۇلىدۇ. بۇ ۋەزىپە ئۇزۇن داۋام قىلىشى مۇمكىن.",
@@ -46,35 +39,23 @@
"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": "سىن قويۇلدى",
@@ -100,24 +81,14 @@
"Music": "مۇزىكا",
"Movies": "فىلىملەر",
"MixedContent": "ئارىلاشما مەزمۇن",
- "MessageNamedServerConfigurationUpdatedWithValue": "مۇلازىمىتېر تەڭشىكىنىڭ {0} قىسمى يېڭىلىنىپ بولدى",
- "MessageServerConfigurationUpdated": "مۇلازىمىتېر يېڭىلىنىپ بولدى",
- "MessageApplicationUpdated": "Jellyfin مۇلازىمىتېرى يېڭىلاندى",
- "MessageApplicationUpdatedTo": "Jellyfin مۇلازىمىتېر نەشرى {0} گە يېڭىلاندى",
"Latest": "ئەڭ يېڭى",
"LabelIpAddressValue": "{0}: IP ئادرىسى",
- "ItemRemovedWithName": "{0} ئامباردىن چىقىرىلدى",
- "ItemAddedWithName": "{0} ئامبارغا قوشۇلدى",
"Inherit": "داۋاملاشتۇرۇش",
"HomeVideos": "ئائىلە سىنلىرى",
"HeaderNextUp": "كېيىنكىسى",
- "HeaderFavoriteSongs": "ئەڭ ياقتۇرىدىغان ناخشىلار",
"HeaderFavoriteShows": "ئەڭ ياقتۇرىدىغان پروگراممىلار",
"HeaderFavoriteEpisodes": "ئەڭ ياقتۇرىدىغان تىياتېرلار",
- "HeaderFavoriteArtists": "ئەڭ ياقتۇرىدىغان سەنئەتكارلار",
- "HeaderFavoriteAlbums": "ياقتۇرىدىغان پىلاستىنكىلار",
"HeaderContinueWatching": "داۋاملىق كۆرۈش",
- "HeaderAlbumArtists": "پىلاستىنكا سەنئەتكارلىرى",
"Genres": "ئۇسلۇبلار",
"FailedLoginAttemptWithUserName": "{0} كىرىش ئوڭۇشلۇق بولمىدى",
"External": "سىرتقى"
diff --git a/Emby.Server.Implementations/Localization/Core/uk.json b/Emby.Server.Implementations/Localization/Core/uk.json
index 3ad772aa9c..ccb9d915d1 100644
--- a/Emby.Server.Implementations/Localization/Core/uk.json
+++ b/Emby.Server.Implementations/Localization/Core/uk.json
@@ -2,36 +2,22 @@
"MusicVideos": "Відеокліпи",
"Music": "Музика",
"Movies": "Фільми",
- "MessageApplicationUpdatedTo": "Jellyfin Server оновлено до версії {0}",
- "MessageApplicationUpdated": "Jellyfin Server оновлено",
"Latest": "Останні",
"LabelIpAddressValue": "IP-адреса: {0}",
- "ItemRemovedWithName": "{0} видалено з медіатеки",
- "ItemAddedWithName": "{0} додано до медіатеки",
"HeaderNextUp": "Наступний",
"HeaderLiveTV": "Ефірне ТБ",
- "HeaderFavoriteSongs": "Обрані пісні",
"HeaderFavoriteShows": "Обрані шоу",
"HeaderFavoriteEpisodes": "Обрані епізоди",
- "HeaderFavoriteArtists": "Обрані виконавці",
- "HeaderFavoriteAlbums": "Обрані альбоми",
"HeaderContinueWatching": "Продовжити перегляд",
- "HeaderAlbumArtists": "Виконавці альбому",
"Genres": "Жанри",
"Folders": "Теки",
"Favorites": "Обрані",
- "DeviceOnlineWithName": "Пристрій {0} підключився",
- "DeviceOfflineWithName": "Пристрій {0} відключився",
"Collections": "Колекції",
"ChapterNameValue": "Сцена {0}",
- "Channels": "Канали",
- "CameraImageUploadedFrom": "Нову фотографію завантажено з {0}",
"Books": "Книги",
"AuthenticationSucceededWithUserName": "{0} успішно авторизовано",
"Artists": "Виконавці",
- "Application": "Додаток",
"AppDeviceValues": "Додаток: {0}, Пристрій: {1}",
- "Albums": "Альбоми",
"NotificationOptionServerRestartRequired": "Необхідно перезапустити сервер",
"NotificationOptionPluginUpdateInstalled": "Встановлено оновлення плагіна",
"NotificationOptionPluginUninstalled": "Плагін видалено",
@@ -64,11 +50,8 @@
"TasksLibraryCategory": "Медіатека",
"TasksMaintenanceCategory": "Обслуговування",
"VersionNumber": "Версія {0}",
- "ValueSpecialEpisodeName": "Спецепізод - {0}",
- "ValueHasBeenAddedToLibrary": "{0} додано до медіатеки",
"UserStoppedPlayingItemWithValues": "{0} закінчив відтворення {1} на {2}",
"UserStartedPlayingItemWithValues": "{0} відтворює {1} на {2}",
- "UserPolicyUpdatedWithName": "Політика користувача оновлена для {0}",
"UserPasswordChangedWithName": "Пароль змінено для користувача {0}",
"UserOnlineFromDevice": "{0} підключився з {1}",
"UserOfflineFromDevice": "{0} відключився від {1}",
@@ -76,23 +59,14 @@
"UserDownloadingItemWithValues": "{0} завантажує {1}",
"UserDeletedWithName": "Користувача {0} видалено",
"UserCreatedWithName": "Користувача {0} створено",
- "User": "Користувач",
"TvShows": "ТВ-шоу",
- "System": "Система",
- "Sync": "Синхронізація",
"SubtitleDownloadFailureFromForItem": "Не вдалося завантажити субтитри з {0} для {1}",
"StartupEmbyServerIsLoading": "Jellyfin Server завантажується. Будь ласка, спробуйте трішки пізніше.",
- "Songs": "Пісні",
"Shows": "Серіали",
- "ServerNameNeedsToBeRestarted": "{0} потрібно перезапустити",
- "ScheduledTaskStartedWithName": "{0} розпочато",
"ScheduledTaskFailedWithName": "{0} незавершено, збій",
- "ProviderValue": "Постачальник: {0}",
"PluginUpdatedWithName": "{0} оновлено",
"PluginUninstalledWithName": "{0} видалено",
"PluginInstalledWithName": "{0} встановлено",
- "Plugin": "Плагін",
- "Playlists": "Плейлисти",
"Photos": "Фотографії",
"NotificationOptionVideoPlaybackStopped": "Відтворення відео зупинено",
"NotificationOptionVideoPlayback": "Розпочато відтворення відео",
@@ -109,10 +83,7 @@
"NameSeasonNumber": "Сезон {0}",
"NameInstallFailed": "Не вдалося встановити {0}",
"MixedContent": "Змішаний контент",
- "MessageServerConfigurationUpdated": "Конфігурація сервера оновлена",
- "MessageNamedServerConfigurationUpdatedWithValue": "Розділ конфігурації сервера {0} оновлено",
"Inherit": "Успадкувати",
- "HeaderRecordingGroups": "Групи запису",
"Forced": "Форсовані",
"TaskCleanActivityLogDescription": "Видаляє старші за встановлений термін записи з журналу активності.",
"TaskCleanActivityLog": "Очистити журнал активності",
@@ -124,18 +95,18 @@
"TaskKeyframeExtractor": "Екстрактор ключових кадрів",
"External": "Зовнішній",
"HearingImpaired": "З порушеннями слуху",
- "TaskRefreshTrickplayImagesDescription": "Створює trickplay-зображення для відео у ввімкнених медіатеках.",
- "TaskRefreshTrickplayImages": "Створити Trickplay-зображення",
- "TaskCleanCollectionsAndPlaylists": "Очистити колекції і списки відтворення",
- "TaskCleanCollectionsAndPlaylistsDescription": "Видаляє елементи з колекцій і списків відтворення, які більше не існують.",
+ "TaskRefreshTrickplayImagesDescription": "Створює прев'ю-зображення для відео у ввімкнених медіатеках.",
+ "TaskRefreshTrickplayImages": "Створити Прев'ю-зображення",
"TaskAudioNormalizationDescription": "Сканує файли на наявність даних для нормалізації звуку.",
"TaskAudioNormalization": "Нормалізація аудіо",
"TaskDownloadMissingLyrics": "Завантажити відсутні тексти пісень",
"TaskDownloadMissingLyricsDescription": "Завантаження текстів пісень",
- "TaskMoveTrickplayImagesDescription": "Переміщує наявні Trickplay-зображення відповідно до налаштувань медіатеки.",
+ "TaskMoveTrickplayImagesDescription": "Переміщує наявні прев'ю-зображення відповідно до налаштувань медіатеки.",
"TaskExtractMediaSegments": "Сканування медіа-сегментів",
- "TaskMoveTrickplayImages": "Змінити місце розташування Trickplay-зображень",
+ "TaskMoveTrickplayImages": "Змінити місце розташування прев'ю-зображень",
"TaskExtractMediaSegmentsDescription": "Витягує або отримує медіа-сегменти з плагінів з підтримкою MediaSegment.",
"CleanupUserDataTask": "Завдання очищення даних користувача",
- "CleanupUserDataTaskDescription": "Очищає всі дані користувача (стан перегляду, статус обраного тощо) з медіа, які перестали бути доступними щонайменше 90 днів тому."
+ "CleanupUserDataTaskDescription": "Очищає всі дані користувача (стан перегляду, статус обраного тощо) з медіа, які перестали бути доступними щонайменше 90 днів тому.",
+ "Original": "Оригінал",
+ "LyricDownloadFailureFromForItem": "Не вдалося завантажити текст пісні з {0} для {1}"
}
diff --git a/Emby.Server.Implementations/Localization/Core/ur.json b/Emby.Server.Implementations/Localization/Core/ur.json
index 94d9c8541e..07d309c270 100644
--- a/Emby.Server.Implementations/Localization/Core/ur.json
+++ b/Emby.Server.Implementations/Localization/Core/ur.json
@@ -1,16 +1,10 @@
{
"Books": "کتابیں",
"AppDeviceValues": "ایپ: {0}، ڈیوائس: {1}",
- "Albums": "البمز",
- "Application": "ایپلی کیشن",
"Artists": "فنکار",
"AuthenticationSucceededWithUserName": "{0} کی کامیابی سے تصدیق ہو چکی ہے",
- "CameraImageUploadedFrom": "ایک نئی کیمرے کی تصویر {0} سے اپ لوڈ کی گئی ہے",
- "Channels": "چینلز",
"ChapterNameValue": "باب {0}",
"Collections": "مجموعے",
"Default": "ڈیفالٹ",
- "DeviceOfflineWithName": "{0} نے رابطہ منقطع کر دیا ہے",
- "DeviceOnlineWithName": "{0} منسلک ہے",
"External": "بیرونی"
}
diff --git a/Emby.Server.Implementations/Localization/Core/ur_PK.json b/Emby.Server.Implementations/Localization/Core/ur_PK.json
index f6539adff3..b3f24a31e3 100644
--- a/Emby.Server.Implementations/Localization/Core/ur_PK.json
+++ b/Emby.Server.Implementations/Localization/Core/ur_PK.json
@@ -1,27 +1,17 @@
{
- "HeaderFavoriteAlbums": "پسندیدہ البمز",
"HeaderNextUp": "اگلا",
- "HeaderFavoriteArtists": "پسندیدہ فنکار",
- "HeaderAlbumArtists": "البم کے فنکار",
"Movies": "فلمیں",
"HeaderFavoriteEpisodes": "پسندیدہ اقساط",
"Collections": "مجموعے",
"Folders": "فولڈرز",
"HeaderLiveTV": "براہ راست ٹی وی",
- "Channels": "چینلز",
"HeaderContinueWatching": "دیکھنا جاری رکھیں",
- "Playlists": "پلے لسٹس",
- "ValueSpecialEpisodeName": "خصوصی - {0}",
"Shows": "دکھاتا ہے",
"Genres": "انواع",
"Artists": "فنکار",
- "Sync": "مطابقت پذیری",
"Photos": "تصاویر",
- "Albums": "البمز",
"Favorites": "پسندیدہ",
- "Songs": "گانے",
"Books": "کتابیں",
- "HeaderFavoriteSongs": "پسندیدہ گانے",
"HeaderFavoriteShows": "پسندیدہ شوز",
"TaskDownloadMissingSubtitlesDescription": "میٹا ڈیٹا کی تشکیل پر مبنی ذیلی عنوانات کے غائب عنوانات انٹرنیٹ پے تلاش کرتا ہے۔",
"TaskDownloadMissingSubtitles": "غائب سب ٹائٹلز ڈاؤن لوڈ کریں",
@@ -46,10 +36,8 @@
"TasksLibraryCategory": "لآیبریری",
"TasksMaintenanceCategory": "مرمت",
"VersionNumber": "ورژن {0}",
- "ValueHasBeenAddedToLibrary": "{0} آپ کی میڈیا لائبریری میں شامل کر دیا گیا ہے",
"UserStoppedPlayingItemWithValues": "{0} نے {1} چلانا ختم کر دیا ھے {2} پے",
"UserStartedPlayingItemWithValues": "{0} چلا رہا ہے {1} {2} پے",
- "UserPolicyUpdatedWithName": "صارف {0} کی پالیسی کیلئے تازہ کاری کی گئی ہے",
"UserPasswordChangedWithName": "صارف {0} کے لئے پاس ورڈ تبدیل کر دیا گیا ہے",
"UserOnlineFromDevice": "{0} آن لائن ہے {1} سے",
"UserOfflineFromDevice": "{0} سے منقطع ہوگیا ہے {1}",
@@ -57,19 +45,13 @@
"UserDownloadingItemWithValues": "{0} ڈاؤن لوڈ کر رھا ھے {1}",
"UserDeletedWithName": "صارف {0} کو ہٹا دیا گیا ہے",
"UserCreatedWithName": "صارف {0} تشکیل دیا گیا ہے",
- "User": "صارف",
"TvShows": "ٹی وی کے پروگرام",
- "System": "نظام",
"SubtitleDownloadFailureFromForItem": "ذیلی عنوانات {0} سے ڈاؤن لوڈ کرنے میں ناکام {1} کے لیے",
"StartupEmbyServerIsLoading": "جیلیفن سرور لوڈ ہورہا ہے۔ براہ کرم جلد ہی دوبارہ کوشش کریں۔",
- "ServerNameNeedsToBeRestarted": "{0} دوبارہ چلانے کرنے کی ضرورت ہے",
- "ScheduledTaskStartedWithName": "{0} شروع",
"ScheduledTaskFailedWithName": "{0} ناکام",
- "ProviderValue": "فراہم کرنے والا: {0}",
"PluginUpdatedWithName": "{0} تازہ کاری کی گئی تھی",
"PluginUninstalledWithName": "[0} ہٹا دیا گیا تھا",
"PluginInstalledWithName": "{0} انسٹال کیا گیا تھا",
- "Plugin": "پلگن",
"NotificationOptionVideoPlaybackStopped": "ویڈیو پلے بیک رک گیا",
"NotificationOptionVideoPlayback": "ویڈیو پلے بیک شروع ہوا",
"NotificationOptionUserLockedOut": "صارف کو لاک آؤٹ کیا گیا",
@@ -93,25 +75,14 @@
"MusicVideos": "میوزک ویڈیوز",
"Music": "موسیقی",
"MixedContent": "مخلوط مواد",
- "MessageServerConfigurationUpdated": "سرور کو اپ ڈیٹ کر دیا گیا ہے",
- "MessageNamedServerConfigurationUpdatedWithValue": "سرور ضمن {0} کو ترتیب دے دیا گیا ھے",
- "MessageApplicationUpdatedTo": "جیلیفن سرور کو اپ ڈیٹ کیا ہے {0}",
- "MessageApplicationUpdated": "جیلیفن سرور کو اپ ڈیٹ کر دیا گیا ہے",
"Latest": "تازہ ترین",
"LabelRunningTimeValue": "چلانے کی مدت",
"LabelIpAddressValue": "آئ پی ایڈریس {0}",
- "ItemRemovedWithName": "لائبریری سے ہٹا دیا گیا ھے",
- "ItemAddedWithName": "[0} لائبریری میں شامل کیا گیا ھے",
"Inherit": "وراثت",
"HomeVideos": "ہوم ویڈیوز",
- "HeaderRecordingGroups": "ریکارڈنگ گروپس",
"FailedLoginAttemptWithUserName": "{0} سے لاگ ان کی ناکام کوشش",
- "DeviceOnlineWithName": "{0} متصل ھو چکا ھے",
- "DeviceOfflineWithName": "{0} منقطع ھو چکا ھے",
"ChapterNameValue": "باب",
"AuthenticationSucceededWithUserName": "{0} کامیابی کے ساتھ تصدیق ھوچکی ھے",
- "CameraImageUploadedFrom": "ایک نئی کیمرہ تصویر اپ لوڈ کی گئی ہے {0}",
- "Application": "پروگرام",
"AppDeviceValues": "پروگرام:{0}, ڈیوائس:{1}",
"Forced": "جَبری",
"Undefined": "غير وضاحتى",
diff --git a/Emby.Server.Implementations/Localization/Core/uz.json b/Emby.Server.Implementations/Localization/Core/uz.json
index e44b3f5167..3215733c1a 100644
--- a/Emby.Server.Implementations/Localization/Core/uz.json
+++ b/Emby.Server.Implementations/Localization/Core/uz.json
@@ -1,47 +1,32 @@
{
"HeaderContinueWatching": "Ko‘rishda davom etish",
- "HeaderAlbumArtists": "Albom ijrochilari",
"Genres": "Janrlar",
"Folders": "Jildlar",
"Favorites": "Sevimlilar",
"Collections": "To'plamlar",
- "Channels": "Kanallar",
"Books": "Kitoblar",
"Artists": "Ijrochilar",
- "Albums": "Albomlar",
"AuthenticationSucceededWithUserName": "{0} muvaffaqiyatli tasdiqlandi",
"AppDeviceValues": "Ilova: {0}, Qurilma: {1}",
- "Application": "Ilova",
- "CameraImageUploadedFrom": "{0}dan yangi kamera rasmi yuklandi",
- "DeviceOnlineWithName": "{0} ulangan",
- "ItemRemovedWithName": "{0} kutbxonadan o'chirildi",
"External": "Tashqi",
"FailedLoginAttemptWithUserName": "Muvafaqiyatsiz kirishlar soni {0}",
"Forced": "Majburiy",
"ChapterNameValue": "{0}chi bo'lim",
- "DeviceOfflineWithName": "{0} aloqa uzildi",
"HeaderLiveTV": "Jonli TV",
"HeaderNextUp": "Keyingisi",
- "ItemAddedWithName": "{0} kutbxonaga qo'shildi",
"LabelIpAddressValue": "IP manzil: {0}",
"SubtitleDownloadFailureFromForItem": "{0} dan {1} uchun taglavhalarni yuklab boʻlmadi",
"UserPasswordChangedWithName": "Foydalanuvchi {0} paroli oʻzgartirildi",
- "ValueHasBeenAddedToLibrary": "{0} kutubxonaga qoʻshildi",
"TaskCleanActivityLogDescription": "Belgilangan yoshdan kattaroq faoliyat jurnali yozuvlarini oʻchiradi.",
"TaskAudioNormalization": "Ovozni normallashtirish",
"TaskRefreshLibraryDescription": "Media kutubxonasi yangi fayllar uchun skanerlanmoqda va metama'lumotlar yangilanmoqda.",
"Default": "Joriy",
- "HeaderFavoriteAlbums": "Tanlangan albomlar",
- "HeaderFavoriteArtists": "Tanlangan artistlar",
"HeaderFavoriteEpisodes": "Tanlangan epizodlar",
"HeaderFavoriteShows": "Tanlangan shoular",
- "HeaderFavoriteSongs": "Tanlangan qo'shiqlar",
- "HeaderRecordingGroups": "Yozuvlar guruhi",
"HomeVideos": "Uy videolari",
"NotificationOptionVideoPlaybackStopped": "Video ijrosi toʻxtatildi",
"TvShows": "TV seriallar",
"Undefined": "Belgilanmagan",
- "User": "Foydalanuvchi",
"UserCreatedWithName": "{0} foydalanuvchi yaratildi",
"TaskCleanCacheDescription": "Tizimga kerak bo'lmagan kesh fayllari o'chiriladi.",
"TaskAudioNormalizationDescription": "Ovozni normallashtirish ma'lumotlari uchun fayllarni skanerlaydi.",
@@ -67,10 +52,6 @@
"NotificationOptionVideoPlayback": "Video ijrosi boshlandi",
"Photos": "Surat",
"Latest": "So'ngi",
- "MessageApplicationUpdated": "Jellyfin Server yangilandi",
- "MessageApplicationUpdatedTo": "Jellyfin Server {0} gacha yangilandi",
- "MessageNamedServerConfigurationUpdatedWithValue": "Server konfiguratsiyasi ({0}-boʻlim) yangilandi",
- "MessageServerConfigurationUpdated": "Server konfiguratsiyasi yangilandi",
"MixedContent": "Aralashgan tarkib",
"Movies": "Kinolar",
"Music": "Qo'shiqlar",
@@ -78,29 +59,19 @@
"NameInstallFailed": "Omadsiz ornatish {0}",
"NameSeasonNumber": "{0} Fasl",
"NameSeasonUnknown": "Fasl aniqlanmagan",
- "Playlists": "Pleylistlar",
"NewVersionIsAvailable": "Yuklab olish uchun Jellyfin Server ning yangi versiyasi mavjud",
- "Plugin": "Plagin",
"TaskCleanLogs": "Jurnallar katalogini tozalash",
"PluginUpdatedWithName": "{0} - yangilandi",
- "ProviderValue": "Yetkazib beruvchi: {0}",
"ScheduledTaskFailedWithName": "{0} - omadsiz",
- "ScheduledTaskStartedWithName": "{0} - ishga tushirildi",
- "ServerNameNeedsToBeRestarted": "Qayta yuklash kerak {0}",
"Shows": "Teleko'rsatuv",
- "Songs": "Kompozitsiyalar",
"StartupEmbyServerIsLoading": "Jellyfin Server yuklanmoqda. Tez orada qayta urinib koʻring.",
- "Sync": "Sinxronizatsiya",
- "System": "Tizim",
"UserDeletedWithName": "{0} foydalanuvchisi oʻchirib tashlandi",
"UserDownloadingItemWithValues": "{0} yuklanmoqda {1}",
"UserLockedOutWithName": "{0} foydalanuvchisi bloklandi",
"UserOfflineFromDevice": "{0} {1}dan uzildi",
"UserOnlineFromDevice": "{0} {1} dan ulandi",
- "UserPolicyUpdatedWithName": "{0} foydalanuvchisining siyosatlari yangilandi",
"UserStartedPlayingItemWithValues": "{0} - {2} da \"{1}\" ijrosi",
"UserStoppedPlayingItemWithValues": "{0} - ijro etish to‘xtatildi {1} {2}",
- "ValueSpecialEpisodeName": "Maxsus qism – {0}",
"VersionNumber": "Versiya {0}",
"TasksMaintenanceCategory": "Xizmat ko'rsatish",
"TasksLibraryCategory": "Media kutubxona",
@@ -111,5 +82,7 @@
"TaskRefreshChapterImages": "Sahnadan tasvirini chiqarish",
"TaskRefreshChapterImagesDescription": "Sahnalarni o'z ichiga olgan videolar uchun eskizlarni yaratadi.",
"TaskRefreshLibrary": "Media kutubxonangizni skanerlash",
- "TaskCleanLogsDescription": "{0} kundan eski log fayllarni o'chiradi."
+ "TaskCleanLogsDescription": "{0} kundan eski log fayllarni o'chiradi.",
+ "Original": "Original",
+ "LyricDownloadFailureFromForItem": "{0} dan {1} gacha qo'shiq matninin yuklab olishda xatolik ketdi"
}
diff --git a/Emby.Server.Implementations/Localization/Core/vi.json b/Emby.Server.Implementations/Localization/Core/vi.json
index 3f4bf1f7ff..2ba665e2ff 100644
--- a/Emby.Server.Implementations/Localization/Core/vi.json
+++ b/Emby.Server.Implementations/Localization/Core/vi.json
@@ -3,17 +3,11 @@
"Favorites": "Yêu Thích",
"Folders": "Thư Mục",
"Genres": "Thể Loại",
- "HeaderAlbumArtists": "Album nghệ sĩ",
"HeaderContinueWatching": "Xem Tiếp",
"HeaderLiveTV": "TV Trực Tiếp",
"Movies": "Phim",
"Photos": "Ảnh",
- "Playlists": "Danh sách phát",
"Shows": "Chương Trình TV",
- "Songs": "Bài Hát",
- "Sync": "Đồng Bộ",
- "ValueSpecialEpisodeName": "Đặc Biệt - {0}",
- "Albums": "Album",
"Artists": "Ca Sĩ",
"TaskDownloadMissingSubtitlesDescription": "Tìm kiếm phụ đề bị thiếu trên Internet dựa trên cấu hình dữ liệu mô tả.",
"TaskDownloadMissingSubtitles": "Tải Xuống Phụ Đề Bị Thiếu",
@@ -38,10 +32,8 @@
"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} đã kết thúc phát {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}",
@@ -49,19 +41,13 @@
"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": "Đã dừng phát lại video",
"NotificationOptionVideoPlayback": "Đã bắt đầu phát lại video",
"NotificationOptionUserLockedOut": "Người dùng bị khóa",
@@ -85,33 +71,18 @@
"MusicVideos": "Videos 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",
"FailedLoginAttemptWithUserName": "Nỗ lực đăng nhập không thành công từ {0}",
- "DeviceOnlineWithName": "{0} đã kết nối",
- "DeviceOfflineWithName": "{0} đã ngắt kết nối",
"ChapterNameValue": "Phân Cảnh {0}",
- "Channels": "Kênh",
- "CameraImageUploadedFrom": "Một hình ảnh máy ảnh mới đã được tải lên từ {0}",
"Books": "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}",
"TaskCleanActivityLogDescription": "Xóa các mục nhật ký hoạt động cũ hơn độ tuổi đã cài đặt.",
"TaskCleanActivityLog": "Xóa Nhật Ký Hoạt Động",
@@ -126,8 +97,6 @@
"HearingImpaired": "Khiếm Thính",
"TaskRefreshTrickplayImages": "Tạo Ảnh Xem Trước Trickplay",
"TaskRefreshTrickplayImagesDescription": "Tạo bản xem trước trịckplay cho video trong thư viện đã bật.",
- "TaskCleanCollectionsAndPlaylists": "Dọn dẹp bộ sưu tập và danh sách phát",
- "TaskCleanCollectionsAndPlaylistsDescription": "Xóa các mục khỏi bộ sưu tập và danh sách phát không còn tồn tại.",
"TaskAudioNormalization": "Chuẩn Hóa Âm Thanh",
"TaskAudioNormalizationDescription": "Quét tập tin để tìm dữ liệu chuẩn hóa âm thanh.",
"TaskDownloadMissingLyricsDescription": "Tải xuống lời cho bài hát",
@@ -137,5 +106,7 @@
"TaskMoveTrickplayImagesDescription": "Di chuyển các tập tin trickplay hiện có theo cài đặt thư viện.",
"TaskExtractMediaSegments": "Quét Phân Đoạn Phương Tiện",
"CleanupUserDataTask": "Tác vụ dọn dẹp dữ liệu người dùng",
- "CleanupUserDataTaskDescription": "Làm sạch tất cả dữ liệu người dùng (trạng thái xem, trạng thái yêu thích, v.v.) từ phương tiện không còn có mặt trong ít nhất 90 ngày."
+ "CleanupUserDataTaskDescription": "Làm sạch tất cả dữ liệu người dùng (trạng thái xem, trạng thái yêu thích, v.v.) từ phương tiện không còn có mặt trong ít nhất 90 ngày.",
+ "Original": "Gốc",
+ "LyricDownloadFailureFromForItem": "Lời bài hát không tải xuống được từ {0} cho {1}"
}
diff --git a/Emby.Server.Implementations/Localization/Core/zh-CN.json b/Emby.Server.Implementations/Localization/Core/zh-CN.json
index 0a0795d41b..18418ae0bc 100644
--- a/Emby.Server.Implementations/Localization/Core/zh-CN.json
+++ b/Emby.Server.Implementations/Localization/Core/zh-CN.json
@@ -1,41 +1,24 @@
{
- "Albums": "专辑",
"AppDeviceValues": "应用:{0},设备:{1}",
- "Application": "应用程序",
"Artists": "艺术家",
"AuthenticationSucceededWithUserName": "{0} 认证成功",
"Books": "书籍",
- "CameraImageUploadedFrom": "已从 {0} 上传新的相机照片",
- "Channels": "频道",
"ChapterNameValue": "章节 {0}",
"Collections": "合集",
- "DeviceOfflineWithName": "{0} 已断开连接",
- "DeviceOnlineWithName": "{0} 已连接",
"FailedLoginAttemptWithUserName": "来自 {0} 的登录失败",
"Favorites": "收藏夹",
"Folders": "文件夹",
"Genres": "类型",
- "HeaderAlbumArtists": "专辑艺术家",
"HeaderContinueWatching": "继续观看",
- "HeaderFavoriteAlbums": "收藏的专辑",
- "HeaderFavoriteArtists": "收藏的艺术家",
"HeaderFavoriteEpisodes": "收藏的剧集",
"HeaderFavoriteShows": "收藏的节目",
- "HeaderFavoriteSongs": "收藏的歌曲",
"HeaderLiveTV": "电视直播",
"HeaderNextUp": "接下来播放",
- "HeaderRecordingGroups": "录制组",
"HomeVideos": "家庭视频",
"Inherit": "继承",
- "ItemAddedWithName": "{0} 已添加到媒体库",
- "ItemRemovedWithName": "{0} 已从媒体库移除",
"LabelIpAddressValue": "IP 地址:{0}",
"LabelRunningTimeValue": "运行时间:{0}",
"Latest": "最新",
- "MessageApplicationUpdated": "Jellyfin 服务器已更新",
- "MessageApplicationUpdatedTo": "Jellyfin 服务器版本已更新到 {0}",
- "MessageNamedServerConfigurationUpdatedWithValue": "服务器配置 {0} 部分已更新",
- "MessageServerConfigurationUpdated": "服务器配置已更新",
"MixedContent": "混合内容",
"Movies": "电影",
"Music": "音乐",
@@ -61,23 +44,14 @@
"NotificationOptionVideoPlayback": "视频已开始播放",
"NotificationOptionVideoPlaybackStopped": "视频播放已停止",
"Photos": "照片",
- "Playlists": "播放列表",
- "Plugin": "插件",
"PluginInstalledWithName": "{0} 已安装",
"PluginUninstalledWithName": "{0} 已卸载",
"PluginUpdatedWithName": "{0} 已更新",
- "ProviderValue": "提供商:{0}",
"ScheduledTaskFailedWithName": "{0} 已失败",
- "ScheduledTaskStartedWithName": "{0} 已开始",
- "ServerNameNeedsToBeRestarted": "{0} 需要重新启动",
"Shows": "节目",
- "Songs": "歌曲",
"StartupEmbyServerIsLoading": "Jellyfin 服务器正在启动,请稍后再试。",
"SubtitleDownloadFailureFromForItem": "无法从 {0} 下载 {1} 的字幕",
- "Sync": "同步",
- "System": "系统",
"TvShows": "电视剧",
- "User": "用户",
"UserCreatedWithName": "已创建用户 {0}",
"UserDeletedWithName": "已删除用户 {0}",
"UserDownloadingItemWithValues": "{0} 正在下载 {1}",
@@ -85,11 +59,8 @@
"UserOfflineFromDevice": "{0} 已从 {1} 断开",
"UserOnlineFromDevice": "{0} 已在 {1} 上线",
"UserPasswordChangedWithName": "用户 {0} 的密码已更改",
- "UserPolicyUpdatedWithName": "用户协议已更新为 {0}",
"UserStartedPlayingItemWithValues": "{0} 在 {2} 上开始播放 {1}",
"UserStoppedPlayingItemWithValues": "{0} 在 {2} 上停止播放 {1}",
- "ValueHasBeenAddedToLibrary": "{0} 已添加至您的媒体库中",
- "ValueSpecialEpisodeName": "特典 - {0}",
"VersionNumber": "版本 {0}",
"TaskUpdatePluginsDescription": "为已设置为自动更新的插件下载和安装更新。",
"TaskRefreshPeople": "刷新演职人员",
@@ -126,8 +97,6 @@
"HearingImpaired": "听力障碍",
"TaskRefreshTrickplayImages": "生成进度条预览图",
"TaskRefreshTrickplayImagesDescription": "为启用的媒体库中的视频生成进度条预览图。",
- "TaskCleanCollectionsAndPlaylists": "清理合集和播放列表",
- "TaskCleanCollectionsAndPlaylistsDescription": "清理合集和播放列表中已不存在的项目。",
"TaskAudioNormalization": "音频标准化",
"TaskAudioNormalizationDescription": "扫描文件以寻找音频标准化数据。",
"TaskDownloadMissingLyrics": "下载缺失的歌词",
@@ -137,5 +106,7 @@
"TaskExtractMediaSegmentsDescription": "从支持 MediaSegment 的插件中提取或获取媒体分段。",
"TaskMoveTrickplayImagesDescription": "根据媒体库设置移动现有的进度条预览图文件。",
"CleanupUserDataTask": "用户数据清理任务",
- "CleanupUserDataTaskDescription": "清理已被删除超过90天的媒体中的所有用户数据(观看状态、收藏夹状态等)。"
+ "CleanupUserDataTaskDescription": "清理已被删除超过90天的媒体中的所有用户数据(观看状态、收藏夹状态等)。",
+ "LyricDownloadFailureFromForItem": "无法从 {0} 下载 {1} 的歌词",
+ "Original": "原始"
}
diff --git a/Emby.Server.Implementations/Localization/Core/zh-HK.json b/Emby.Server.Implementations/Localization/Core/zh-HK.json
index e57a0c5b09..b2fcbc93a2 100644
--- a/Emby.Server.Implementations/Localization/Core/zh-HK.json
+++ b/Emby.Server.Implementations/Localization/Core/zh-HK.json
@@ -1,141 +1,112 @@
{
- "Albums": "專輯",
- "AppDeviceValues": "程式:{0},設備:{1}",
- "Application": "應用程式",
+ "AppDeviceValues": "程式:{0},裝置:{1}",
"Artists": "藝人",
- "AuthenticationSucceededWithUserName": "成功授權 {0}",
+ "AuthenticationSucceededWithUserName": "{0} 成功通過驗證",
"Books": "書籍",
- "CameraImageUploadedFrom": "{0} 成功上傳一張新照片",
- "Channels": "頻道",
"ChapterNameValue": "第 {0} 章",
"Collections": "系列",
- "DeviceOfflineWithName": "{0} 已中斷連接",
- "DeviceOnlineWithName": "{0} 已連接",
- "FailedLoginAttemptWithUserName": "{0} 登入失敗",
- "Favorites": "我的最愛",
+ "FailedLoginAttemptWithUserName": "來自 {0} 嘅登入嘗試失敗咗",
+ "Favorites": "心水",
"Folders": "資料夾",
"Genres": "風格",
- "HeaderAlbumArtists": "專輯歌手",
- "HeaderContinueWatching": "繼續觀看",
- "HeaderFavoriteAlbums": "最愛的專輯",
- "HeaderFavoriteArtists": "最愛的藝人",
- "HeaderFavoriteEpisodes": "最愛的劇集",
- "HeaderFavoriteShows": "最愛的節目",
- "HeaderFavoriteSongs": "最愛的歌曲",
+ "HeaderContinueWatching": "繼續睇返",
+ "HeaderFavoriteEpisodes": "心水嘅劇集",
+ "HeaderFavoriteShows": "心水嘅節目",
"HeaderLiveTV": "電視直播",
- "HeaderNextUp": "繼續觀看",
- "HeaderRecordingGroups": "錄製組",
+ "HeaderNextUp": "跟住落嚟",
"HomeVideos": "家庭影片",
"Inherit": "繼承",
- "ItemAddedWithName": "{0} 已被加入至媒體庫",
- "ItemRemovedWithName": "{0} 已從媒體庫移除",
- "LabelIpAddressValue": "IP 地址:{0}",
- "LabelRunningTimeValue": "運作時間:{0}",
+ "LabelIpAddressValue": "IP 位址:{0}",
+ "LabelRunningTimeValue": "運行時間:{0}",
"Latest": "最新",
- "MessageApplicationUpdated": "Jellyfin 已被更新",
- "MessageApplicationUpdatedTo": "Jellyfin 已被更新至 {0}",
- "MessageNamedServerConfigurationUpdatedWithValue": "伺服器設定 {0} 已被更新",
- "MessageServerConfigurationUpdated": "已更新伺服器設定",
"MixedContent": "混合內容",
"Movies": "電影",
"Music": "音樂",
"MusicVideos": "MV",
"NameInstallFailed": "{0} 安裝失敗",
"NameSeasonNumber": "第 {0} 季",
- "NameSeasonUnknown": "未知的季度",
- "NewVersionIsAvailable": "有新版本的 Jellyfin 可供下載。",
- "NotificationOptionApplicationUpdateAvailable": "有可用的更新",
- "NotificationOptionApplicationUpdateInstalled": "完成更新應用程式",
- "NotificationOptionAudioPlayback": "播放音訊",
- "NotificationOptionAudioPlaybackStopped": "停止播放音訊",
- "NotificationOptionCameraImageUploaded": "相片上傳",
+ "NameSeasonUnknown": "未知嘅季度",
+ "NewVersionIsAvailable": "有新版本嘅 Jellyfin 可以下載。",
+ "NotificationOptionApplicationUpdateAvailable": "有得更新應用程式",
+ "NotificationOptionApplicationUpdateInstalled": "應用程式更新好咗",
+ "NotificationOptionAudioPlayback": "開始播放音訊",
+ "NotificationOptionAudioPlaybackStopped": "停咗播放音訊",
+ "NotificationOptionCameraImageUploaded": "相機相片上載咗",
"NotificationOptionInstallationFailed": "安裝失敗",
- "NotificationOptionNewLibraryContent": "新增媒體",
- "NotificationOptionPluginError": "插件錯誤",
- "NotificationOptionPluginInstalled": "安裝插件",
- "NotificationOptionPluginUninstalled": "解除安裝插件",
- "NotificationOptionPluginUpdateInstalled": "完成更新插件",
- "NotificationOptionServerRestartRequired": "伺服器需要重啟",
- "NotificationOptionTaskFailed": "排程工作執行失敗",
- "NotificationOptionUserLockedOut": "封鎖用戶",
- "NotificationOptionVideoPlayback": "播放影片",
- "NotificationOptionVideoPlaybackStopped": "停止播放影片",
+ "NotificationOptionNewLibraryContent": "加咗新內容",
+ "NotificationOptionPluginError": "外掛程式錯誤",
+ "NotificationOptionPluginInstalled": "安裝外掛程式",
+ "NotificationOptionPluginUninstalled": "解除安裝外掛程式",
+ "NotificationOptionPluginUpdateInstalled": "外掛程式更新好咗",
+ "NotificationOptionServerRestartRequired": "伺服器需要重新啟動",
+ "NotificationOptionTaskFailed": "排程工作失敗",
+ "NotificationOptionUserLockedOut": "用家被鎖定咗",
+ "NotificationOptionVideoPlayback": "開始播放影片",
+ "NotificationOptionVideoPlaybackStopped": "停咗播放影片",
"Photos": "相片",
- "Playlists": "播放清單",
- "Plugin": "插件",
- "PluginInstalledWithName": "已安裝 {0}",
- "PluginUninstalledWithName": "已移除 {0}",
- "PluginUpdatedWithName": "已更新 {0}",
- "ProviderValue": "提供者:{0}",
+ "PluginInstalledWithName": "裝好咗 {0}",
+ "PluginUninstalledWithName": "剷走咗 {0}",
+ "PluginUpdatedWithName": "更新好咗 {0}",
"ScheduledTaskFailedWithName": "{0} 執行失敗",
- "ScheduledTaskStartedWithName": "開始執行 {0}",
- "ServerNameNeedsToBeRestarted": "{0} 需要重啟",
"Shows": "節目",
- "Songs": "歌曲",
- "StartupEmbyServerIsLoading": "正在載入 Jellyfin,請稍後再試。",
- "SubtitleDownloadFailureFromForItem": "無法從 {0} 下載 {1} 的字幕",
- "Sync": "同步",
- "System": "系統",
+ "StartupEmbyServerIsLoading": "Jellyfin 伺服器載入緊,唔該稍後再試。",
+ "SubtitleDownloadFailureFromForItem": "經 {0} 下載 {1} 嘅字幕失敗咗",
"TvShows": "電視節目",
- "User": "用戶",
- "UserCreatedWithName": "建立新用戶 {0}",
- "UserDeletedWithName": "用戶 {0} 已被移除",
- "UserDownloadingItemWithValues": "{0} 正在下載 {1}",
- "UserLockedOutWithName": "用戶 {0} 已被封鎖",
- "UserOfflineFromDevice": "{0} 終止了 {1} 的連接",
- "UserOnlineFromDevice": "{0} 從 {1} 連線",
- "UserPasswordChangedWithName": "{0} 的密碼已被更改",
- "UserPolicyUpdatedWithName": "使用條款已更新為 {0}",
- "UserStartedPlayingItemWithValues": "{0} 在 {2} 上播放 {1}",
- "UserStoppedPlayingItemWithValues": "{0} 停止在 {2} 上播放 {1}",
- "ValueHasBeenAddedToLibrary": "{0} 已被加入至你的媒體庫",
- "ValueSpecialEpisodeName": "特典 - {0}",
+ "UserCreatedWithName": "經已建立咗新使用者 {0}",
+ "UserDeletedWithName": "使用者 {0} 經已被刪走",
+ "UserDownloadingItemWithValues": "{0} 下載緊 {1}",
+ "UserLockedOutWithName": "使用者 {0} 經已被鎖定",
+ "UserOfflineFromDevice": "{0} 經已由 {1} 斷開咗連線",
+ "UserOnlineFromDevice": "{0} 正喺 {1} 連線",
+ "UserPasswordChangedWithName": "使用者 {0} 嘅密碼經已更改咗",
+ "UserStartedPlayingItemWithValues": "{0} 正喺 {2} 播緊 {1}",
+ "UserStoppedPlayingItemWithValues": "{0} 已經喺 {2} 停止播放 {1}",
"VersionNumber": "版本 {0}",
- "TaskDownloadMissingSubtitles": "下載欠缺字幕",
- "TaskUpdatePlugins": "更新插件",
+ "TaskDownloadMissingSubtitles": "下載漏咗嘅字幕",
+ "TaskUpdatePlugins": "更新外掛程式",
"TasksApplicationCategory": "應用程式",
- "TaskRefreshLibraryDescription": "掃描媒體庫以加入新增的檔案及重新載入元數據。",
+ "TaskRefreshLibraryDescription": "掃描媒體櫃嚟搵新檔案,同時重新載入媒體詳細資料。",
"TasksMaintenanceCategory": "維護",
- "TaskDownloadMissingSubtitlesDescription": "根據元數據中的設定,在網上搜尋欠缺的字幕。",
- "TaskRefreshChannelsDescription": "重新載入網絡頻道的資訊。",
+ "TaskDownloadMissingSubtitlesDescription": "根據媒體詳細資料設定,喺網上幫你搵返啲欠缺嘅字幕。",
+ "TaskRefreshChannelsDescription": "重新整理網上頻道嘅資訊。",
"TaskRefreshChannels": "重新載入頻道",
- "TaskCleanTranscodeDescription": "刪除超過一天的轉碼檔案。",
- "TaskCleanTranscode": "清理轉碼檔資料夾",
- "TaskUpdatePluginsDescription": "下載並更新能夠被自動更新的插件。",
- "TaskRefreshPeopleDescription": "更新你的媒體中有關的演員和導演的元數據。",
- "TaskCleanLogsDescription": "刪除超過{0}天的紀錄檔。",
- "TaskCleanLogs": "清理紀錄檔資料夾",
- "TaskRefreshLibrary": "掃描媒體庫",
- "TaskRefreshChapterImagesDescription": "為帶有章節的影片建立縮圖。",
- "TaskRefreshChapterImages": "提取章節圖像",
- "TaskCleanCacheDescription": "刪除系統不再需要的緩存文件。",
- "TaskCleanCache": "清理緩存資料夾",
- "TasksChannelsCategory": "網絡頻道",
- "TasksLibraryCategory": "媒體庫",
+ "TaskCleanTranscodeDescription": "自動刪走超過一日嘅轉碼檔案。",
+ "TaskCleanTranscode": "清理轉碼資料夾",
+ "TaskUpdatePluginsDescription": "自動幫嗰啲設咗要自動更新嘅外掛程式進行下載同安裝。",
+ "TaskRefreshPeopleDescription": "更新媒體櫃入面演員同導演嘅媒體詳細資料。",
+ "TaskCleanLogsDescription": "自動刪走超過 {0} 日嘅紀錄檔。",
+ "TaskCleanLogs": "清理日誌資料夾",
+ "TaskRefreshLibrary": "掃描媒體櫃",
+ "TaskRefreshChapterImagesDescription": "幫有章節嘅影片整返啲章節預覽圖。",
+ "TaskRefreshChapterImages": "擷取章節圖像",
+ "TaskCleanCacheDescription": "刪走系統已經唔再需要嘅快取檔案。",
+ "TaskCleanCache": "清理快取(Cache)資料夾",
+ "TasksChannelsCategory": "網路頻道",
+ "TasksLibraryCategory": "媒體櫃",
"TaskRefreshPeople": "重新載入人物",
- "TaskCleanActivityLog": "清理活動記錄",
+ "TaskCleanActivityLog": "清理活動紀錄",
"Undefined": "未定義",
"Forced": "強制",
- "Default": "預設",
- "TaskOptimizeDatabaseDescription": "壓縮數據庫及釋放可用空間。完成任何會修改數據庫的工作(例如掃描媒體庫)後,執行此工作或可提升伺服器速度。",
- "TaskOptimizeDatabase": "最佳化數據庫",
- "TaskCleanActivityLogDescription": "刪除早於設定時間的活動記錄。",
- "TaskKeyframeExtractorDescription": "提取關鍵影格(Keyframe)以建立更準確的 HLS playlist。此工作可能需要使用較長時間來完成。",
+ "Default": "初始",
+ "TaskOptimizeDatabaseDescription": "壓縮數據櫃並釋放剩餘空間。喺掃描媒體櫃或者做咗一啲會修改數據櫃嘅操作之後行呢個任務,或者可以提升效能。",
+ "TaskOptimizeDatabase": "最佳化數據櫃",
+ "TaskCleanActivityLogDescription": "刪走超過設定日期嘅活動記錄。",
+ "TaskKeyframeExtractorDescription": "提取關鍵影格(Keyframe)嚟建立更準確嘅 HLS 播放列表。呢個任務可能要行好耐。",
"TaskKeyframeExtractor": "關鍵影格提取器",
"External": "外部",
"HearingImpaired": "聽力障礙",
- "TaskRefreshTrickplayImages": "建立 Trickplay 圖像",
- "TaskRefreshTrickplayImagesDescription": "為已啟用 Trickplay 的媒體庫內的影片建立 Trickplay 預覽圖。",
+ "TaskRefreshTrickplayImages": "產生搜畫預覽圖",
+ "TaskRefreshTrickplayImagesDescription": "為已啟用功能嘅媒體櫃影片製作快轉預覽圖。",
"TaskExtractMediaSegments": "掃描媒體分段資訊",
- "TaskExtractMediaSegmentsDescription": "從允許MediaSegment 功能的插件中獲取媒體片段。",
- "TaskDownloadMissingLyrics": "下載欠缺歌詞",
- "TaskDownloadMissingLyricsDescription": "下載歌詞",
- "TaskCleanCollectionsAndPlaylists": "整理媒體與播放清單",
+ "TaskExtractMediaSegmentsDescription": "從支援 MediaSegment 功能嘅外掛程式入面提取媒體片段。",
+ "TaskDownloadMissingLyrics": "下載缺失嘅歌詞",
+ "TaskDownloadMissingLyricsDescription": "幫啲歌下載歌詞",
"TaskAudioNormalization": "音訊同等化",
- "TaskAudioNormalizationDescription": "掃描檔案裏的音訊同等化資料。",
- "TaskCleanCollectionsAndPlaylistsDescription": "從資料庫及播放清單中移除已不存在的項目。",
- "TaskMoveTrickplayImagesDescription": "根據媒體庫設定移動現有的 Trickplay 檔案。",
- "TaskMoveTrickplayImages": "轉移 Trickplay 影像位置",
- "CleanupUserDataTask": "用戶資料清理工作",
- "CleanupUserDataTaskDescription": "從用戶數據中清除已經被刪除超過 90 日的媒體相關資料。"
+ "TaskAudioNormalizationDescription": "掃描檔案入面嘅音訊標准化(Audio Normalization)數據。",
+ "TaskMoveTrickplayImagesDescription": "根據媒體櫃設定,將現有嘅 Trickplay(快轉預覽)檔案搬去對應位置。",
+ "TaskMoveTrickplayImages": "搬移快轉預覽圖嘅位置",
+ "CleanupUserDataTask": "清理使用者資料嘅任務",
+ "CleanupUserDataTaskDescription": "清理已消失至少 90 日嘅媒體用家數據(包括觀看狀態、心水狀態等)。",
+ "LyricDownloadFailureFromForItem": "冇辦法從 {0} 下載 {1} 嘅歌詞",
+ "Original": "原始"
}
diff --git a/Emby.Server.Implementations/Localization/Core/zh-TW.json b/Emby.Server.Implementations/Localization/Core/zh-TW.json
index b3bb9106b8..5dace3b0b7 100644
--- a/Emby.Server.Implementations/Localization/Core/zh-TW.json
+++ b/Emby.Server.Implementations/Localization/Core/zh-TW.json
@@ -1,39 +1,23 @@
{
- "Albums": "專輯",
"AppDeviceValues": "應用程式:{0},裝置:{1}",
- "Application": "應用程式",
"Artists": "藝人",
"AuthenticationSucceededWithUserName": "成功授權 {0}",
"Books": "書籍",
- "CameraImageUploadedFrom": "已從 {0} 成功上傳一張照片",
- "Channels": "頻道",
"ChapterNameValue": "章節 {0}",
"Collections": "系列作",
- "DeviceOfflineWithName": "{0} 已中斷連接",
- "DeviceOnlineWithName": "{0} 已連接",
"FailedLoginAttemptWithUserName": "來自 {0} 的登入失敗嘗試",
"Favorites": "我的最愛",
"Folders": "資料夾",
"Genres": "風格",
- "HeaderAlbumArtists": "專輯演出者",
"HeaderContinueWatching": "繼續觀看",
- "HeaderFavoriteAlbums": "最愛專輯",
- "HeaderFavoriteArtists": "最愛的藝人",
"HeaderFavoriteEpisodes": "最愛的劇集",
"HeaderFavoriteShows": "最愛的節目",
- "HeaderFavoriteSongs": "最愛的歌曲",
"HeaderLiveTV": "電視直播",
"HeaderNextUp": "接下來",
"HomeVideos": "家庭影片",
- "ItemAddedWithName": "{0} 已新增至媒體庫",
- "ItemRemovedWithName": "{0} 已從媒體庫移除",
"LabelIpAddressValue": "IP 位址:{0}",
"LabelRunningTimeValue": "運行時間:{0}",
"Latest": "最新",
- "MessageApplicationUpdated": "Jellyfin 伺服器已經更新",
- "MessageApplicationUpdatedTo": "Jellyfin 伺服器已經更新至 {0}",
- "MessageNamedServerConfigurationUpdatedWithValue": "伺服器設定 {0} 部分已經更新",
- "MessageServerConfigurationUpdated": "伺服器設定已經更新",
"MixedContent": "混合內容",
"Movies": "電影",
"Music": "音樂",
@@ -59,22 +43,13 @@
"NotificationOptionVideoPlayback": "影片播放已開始",
"NotificationOptionVideoPlaybackStopped": "影片播放已停止",
"Photos": "相片",
- "Playlists": "播放清單",
- "Plugin": "擴充功能",
"PluginInstalledWithName": "已安裝 {0}",
"PluginUninstalledWithName": "已移除 {0}",
"PluginUpdatedWithName": "已更新 {0}",
- "ProviderValue": "提供者:{0}",
"ScheduledTaskFailedWithName": "排程任務 {0} 執行失敗",
- "ScheduledTaskStartedWithName": "排程任務 {0} 已開始",
- "ServerNameNeedsToBeRestarted": "伺服器 {0} 需要重新啟動",
"Shows": "節目",
- "Songs": "歌曲",
"StartupEmbyServerIsLoading": "Jellyfin 伺服器載入中,請稍後再試。",
- "Sync": "同步",
- "System": "系統",
"TvShows": "電視節目",
- "User": "使用者",
"UserCreatedWithName": "已建立使用者 {0}",
"UserDeletedWithName": "已刪除使用者 {0}",
"UserDownloadingItemWithValues": "使用者 {0} 正在下載 {1}",
@@ -82,13 +57,9 @@
"UserOfflineFromDevice": "使用者 {0} 已從 {1} 斷線",
"UserOnlineFromDevice": "使用者 {0} 已從 {1} 連線",
"UserPasswordChangedWithName": "使用者 {0} 的密碼已變更",
- "UserPolicyUpdatedWithName": "使用者權限已更新為 {0}",
"UserStartedPlayingItemWithValues": "{0} 正在 {2} 上播放 {1}",
"UserStoppedPlayingItemWithValues": "{0} 已在 {2} 上停止播放 {1}",
- "ValueHasBeenAddedToLibrary": "{0} 已新增至您的媒體庫",
- "ValueSpecialEpisodeName": "特輯 - {0}",
"VersionNumber": "版本 {0}",
- "HeaderRecordingGroups": "錄製組",
"Inherit": "繼承",
"SubtitleDownloadFailureFromForItem": "無法從 {0} 下載 {1} 的字幕",
"TaskDownloadMissingSubtitlesDescription": "透過媒體資訊從網路上搜尋遺失的字幕。",
@@ -126,8 +97,6 @@
"HearingImpaired": "聽力障礙",
"TaskRefreshTrickplayImages": "生成快轉縮圖",
"TaskRefreshTrickplayImagesDescription": "為啟用快轉縮圖的媒體庫生成快轉縮圖。",
- "TaskCleanCollectionsAndPlaylists": "清理系列作和播放清單",
- "TaskCleanCollectionsAndPlaylistsDescription": "清理系列作品與播放清單中已不存在的項目。",
"TaskAudioNormalization": "音量標準化",
"TaskAudioNormalizationDescription": "掃描文件以找出音量標準化資料。",
"TaskDownloadMissingLyrics": "下載缺少的歌詞",
diff --git a/Emby.Server.Implementations/Localization/Core/zu.json b/Emby.Server.Implementations/Localization/Core/zu.json
index aa056d4498..669ea0372d 100644
--- a/Emby.Server.Implementations/Localization/Core/zu.json
+++ b/Emby.Server.Implementations/Localization/Core/zu.json
@@ -2,37 +2,24 @@
"TasksApplicationCategory": "Ukusetshenziswa",
"TasksLibraryCategory": "Umtapo",
"TasksMaintenanceCategory": "Ukunakekela",
- "User": "Umsebenzisi",
"Undefined": "Akuchaziwe",
- "System": "Isistimu",
- "Sync": "Vumelanisa",
- "Songs": "Amaculo",
"Shows": "Izinhlelo",
- "Plugin": "Isijobelelo",
- "Playlists": "Izinhla Zokudlalayo",
"Photos": "Izithombe",
"Music": "Umculo",
"Movies": "Amamuvi",
"Latest": "lwakamuva",
"Inherit": "Ngefa",
"Forced": "Kuphoqiwe",
- "Application": "Ukusetshenziswa",
"Genres": "Izinhlobo",
"Folders": "Izikhwama",
"Favorites": "Izintandokazi",
"Default": "Okumisiwe",
"Collections": "Amaqoqo",
- "Channels": "Amashaneli",
"Books": "Izincwadi",
"Artists": "Abadlali",
- "Albums": "Ama-albhamu",
- "CameraImageUploadedFrom": "Kulandelayo lwesithonjana sekhamera selithunyelwe kusuka ku {0}",
- "HeaderFavoriteArtists": "Abasethi Abathandekayo",
"HeaderFavoriteEpisodes": "Izilimi Ezithandekayo",
"HeaderFavoriteShows": "Izisho Ezithandekayo",
"External": "Kwezifungo",
"FailedLoginAttemptWithUserName": "Ukushayiswa kwesithombe sokungena okungekho {0}",
- "HeaderContinueWatching": "Buyela Ukubona",
- "HeaderFavoriteAlbums": "Izimpahla Ezithandwayo",
- "HeaderAlbumArtists": "Abasethi wenkulumo"
+ "HeaderContinueWatching": "Buyela Ukubona"
}
diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs
index bc80c2b405..e7dd984ec4 100644
--- a/Emby.Server.Implementations/Localization/LocalizationManager.cs
+++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs
@@ -3,6 +3,7 @@ using System.Collections.Concurrent;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
@@ -26,6 +27,7 @@ namespace Emby.Server.Implementations.Localization
private const string RatingsPath = "Emby.Server.Implementations.Localization.Ratings.";
private const string CulturesPath = "Emby.Server.Implementations.Localization.iso6392.txt";
private const string CountriesPath = "Emby.Server.Implementations.Localization.countries.json";
+ private const string CoreResourcePrefix = "Emby.Server.Implementations.Localization.Core.";
private static readonly Assembly _assembly = typeof(LocalizationManager).Assembly;
private static readonly string[] _unratedValues = ["n/a", "unrated", "not rated", "nr"];
@@ -34,13 +36,21 @@ namespace Emby.Server.Implementations.Localization
private readonly Dictionary<string, Dictionary<string, ParentalRatingScore?>> _allParentalRatings = new(StringComparer.OrdinalIgnoreCase);
- private readonly ConcurrentDictionary<string, Dictionary<string, string>> _dictionaries = new(StringComparer.OrdinalIgnoreCase);
+ private readonly ConcurrentDictionary<string, Dictionary<string, string>> _cultureOnlyDictionaries = new(StringComparer.OrdinalIgnoreCase);
private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options;
private readonly ConcurrentDictionary<string, CultureDto?> _cultureCache = new(StringComparer.OrdinalIgnoreCase);
private List<CultureDto> _cultures = [];
+ private static readonly (IReadOnlyList<LocalizationOption> Options, FrozenDictionary<string, string> Bcp47ToJellyfinMap) _localizationData = BuildLocalizationData();
+ private static readonly IReadOnlyList<LocalizationOption> _localizationOptions = _localizationData.Options;
+
+ // Maps BCP-47 hyphenated culture codes (set by ASP.NET Core's RequestLocalizationMiddleware
+ // and used as CurrentUICulture.Name) to Jellyfin's underscore-based resource file codes.
+ // Built reflexively from the resource file scan so both directions stay in sync.
+ private static readonly FrozenDictionary<string, string> _bcp47ToJellyfinMap = _localizationData.Bcp47ToJellyfinMap;
+
private FrozenDictionary<string, string> _iso6392BtoT = null!;
/// <summary>
@@ -54,6 +64,59 @@ namespace Emby.Server.Implementations.Localization
{
_configurationManager = configurationManager;
_logger = logger;
+
+ _configurationManager.ConfigurationUpdated += OnConfigurationUpdated;
+ }
+
+ /// <summary>
+ /// Gets the supported UI cultures.
+ /// </summary>
+ /// <returns>A list of <see cref="CultureInfo"/> objects covering every embedded translation.</returns>
+ public static IList<CultureInfo> GetSupportedUICultures()
+ {
+ var cultures = new List<CultureInfo>();
+ foreach (var option in _localizationOptions)
+ {
+ // Skip novelty codes (e.g. "pr" Pirate, "jbo" Lojban) that .NET cannot resolve.
+ if (TryGetCultureInfo(option.Value, out var cultureInfo))
+ {
+ cultures.Add(cultureInfo);
+ }
+ }
+
+ return cultures;
+ }
+
+ /// <summary>
+ /// Resolves a Jellyfin resource culture code (which may use underscores, e.g. <c>es_419</c>)
+ /// to a <see cref="CultureInfo"/>. Returns <see langword="false"/> for codes .NET cannot resolve.
+ /// </summary>
+ private static bool TryGetCultureInfo(string cultureCode, [NotNullWhen(true)] out CultureInfo? cultureInfo)
+ {
+ try
+ {
+ // Resource files use underscores for some variants (e.g. es_419);
+ // CultureInfo only accepts hyphenated BCP-47 codes.
+ cultureInfo = CultureInfo.GetCultureInfo(cultureCode.Replace('_', '-'));
+ return true;
+ }
+ catch (CultureNotFoundException)
+ {
+ cultureInfo = null;
+ return false;
+ }
+ }
+
+ private static void OnConfigurationUpdated(object? sender, EventArgs e)
+ {
+ if (sender is IServerConfigurationManager configManager)
+ {
+ var uiCulture = configManager.Configuration.UICulture;
+ if (!string.IsNullOrEmpty(uiCulture))
+ {
+ CultureInfo.DefaultThreadCurrentUICulture = new CultureInfo(uiCulture);
+ }
+ }
}
/// <summary>
@@ -138,7 +201,7 @@ namespace Emby.Server.Implementations.Localization
string twoCharName = parts[2];
if (string.IsNullOrWhiteSpace(twoCharName))
{
- continue;
+ twoCharName = string.Empty;
}
else if (twoCharName.Contains('-', StringComparison.OrdinalIgnoreCase))
{
@@ -255,13 +318,13 @@ namespace Emby.Server.Implementations.Localization
// A lot of countries don't explicitly have a separate rating for adult content
if (ratings.All(x => x.RatingScore?.Score != 1000))
{
- ratings.Add(new ParentalRating("XXX", new(1000, null)));
+ ratings.Add(new ParentalRating("XXX", new(1000, null)));
}
// A lot of countries don't explicitly have a separate rating for banned content
if (ratings.All(x => x.RatingScore?.Score != 1001))
{
- ratings.Add(new ParentalRating("Banned", new(1001, null)));
+ ratings.Add(new ParentalRating("Banned", new(1001, null)));
}
return [.. ratings.OrderBy(r => r.RatingScore?.Score).ThenBy(r => r.RatingScore?.SubScore)];
@@ -293,6 +356,27 @@ namespace Emby.Server.Implementations.Localization
{
ArgumentException.ThrowIfNullOrEmpty(rating);
+ // Some providers may list multiple ratings separated by '/' (e.g. "SE:15 / SE:15+ / SE:Från 15 år").
+ // Try each one in order and use the first that resolves.
+ var ratingValues = rating.Split('/', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
+
+ foreach (var ratingValue in ratingValues)
+ {
+ var score = GetSingleRatingScore(ratingValue, countryCode);
+ if (score is not null)
+ {
+ return score;
+ }
+ }
+
+ return null;
+ }
+
+ /// <summary>
+ /// Resolves a single rating value to a score.
+ /// </summary>
+ private ParentalRatingScore? GetSingleRatingScore(string rating, string? countryCode)
+ {
// Handle unrated content
if (_unratedValues.Contains(rating.AsSpan(), StringComparison.OrdinalIgnoreCase))
{
@@ -301,7 +385,7 @@ namespace Emby.Server.Implementations.Localization
// Convert ints directly
// This may override some of the locale specific age ratings (but those always map to the same age)
- if (int.TryParse(rating, out var ratingAge))
+ if (TryParseRatingAsScore(rating, out var ratingAge))
{
return new(ratingAge, null);
}
@@ -320,6 +404,14 @@ namespace Emby.Server.Implementations.Localization
{
return value;
}
+
+ if (ratingsDictionary is not null && rating.Length > countryCode.Length
+ && rating.StartsWith(countryCode, StringComparison.OrdinalIgnoreCase)
+ && (rating[countryCode.Length] == '-' || rating[countryCode.Length] == ':')
+ && ratingsDictionary.TryGetValue(rating[(countryCode.Length + 1)..].Trim(), out var normalizedValue))
+ {
+ return normalizedValue;
+ }
}
else
{
@@ -345,38 +437,98 @@ namespace Emby.Server.Implementations.Localization
}
}
- // Try splitting by : to handle "Germany: FSK-18"
- if (rating.Contains(':', StringComparison.OrdinalIgnoreCase))
+ // Try splitting by country prefix separator to handle "US:PG-13", "Germany: FSK-18", "DE-FSK-18"
+ if (TryGetRatingScoreBySeparator(rating, ':', out var result)
+ || TryGetRatingScoreBySeparator(rating, '-', out result))
+ {
+ return result;
+ }
+
+ return null;
+ }
+
+ private bool TryGetRatingScoreBySeparator(string rating, char separator, out ParentalRatingScore? result)
+ {
+ result = null;
+
+ if (rating.IndexOf(separator, StringComparison.Ordinal) < 0)
+ {
+ return false;
+ }
+
+ var ratingSpan = rating.AsSpan();
+ var countryPart = ratingSpan.LeftPart(separator).Trim().ToString();
+ var ratingPart = ratingSpan.RightPart(separator).Trim().ToString();
+ if (ratingPart.Length == 0)
+ {
+ return false;
+ }
+
+ string? resolvedCountryCode = null;
+
+ if (_allParentalRatings.ContainsKey(countryPart))
{
- var ratingLevelRightPart = rating.AsSpan().RightPart(':');
- if (ratingLevelRightPart.Length != 0)
+ resolvedCountryCode = countryPart;
+ }
+ else
+ {
+ var culture = FindLanguageInfo(countryPart);
+ if (culture is not null)
{
- return GetRatingScore(ratingLevelRightPart.ToString());
+ resolvedCountryCode = culture.TwoLetterISOLanguageName;
}
}
- // Handle prefix country code to handle "DE-18"
- if (rating.Contains('-', StringComparison.OrdinalIgnoreCase))
+ if (resolvedCountryCode is not null
+ && _allParentalRatings.TryGetValue(resolvedCountryCode, out var countryRatings))
{
- var ratingSpan = rating.AsSpan();
-
- // Extract culture from country prefix
- var culture = FindLanguageInfo(ratingSpan.LeftPart('-').ToString());
+ if (countryRatings.TryGetValue(ratingPart, out result))
+ {
+ return true;
+ }
- var ratingLevelRightPart = ratingSpan.RightPart('-');
- if (ratingLevelRightPart.Length != 0)
+ // If it's not a recognized rating string, fall back to using the number as the score
+ if (TryParseRatingAsScore(ratingPart, out var numericScore))
{
- // Check rating system of culture
- return GetRatingScore(ratingLevelRightPart.ToString(), culture?.TwoLetterISOLanguageName);
+ result = new ParentalRatingScore(numericScore, null);
+ return true;
}
+
+ _logger.LogWarning(
+ "Rating '{Rating}' not found in the '{CountryCode}' rating system, treating as unrated",
+ rating,
+ resolvedCountryCode);
+
+ return true;
}
- return null;
+ // Country not identified or no rating data available, try recursive lookup
+ result = GetRatingScore(ratingPart, resolvedCountryCode);
+
+ return true;
+ }
+
+ /// <summary>
+ /// Tries to parse a rating as a number, allowing an optional trailing '+' (e.g. "16" or "18+").
+ /// </summary>
+ /// <param name="ratingValue">Rating value to parse.</param>
+ /// <param name="score">Parsed score.</param>
+ /// <returns>Returns true if parsing was successful.</returns>
+ private static bool TryParseRatingAsScore(ReadOnlySpan<char> ratingValue, out int score)
+ {
+ var trimmed = ratingValue.TrimEnd('+');
+ return int.TryParse(trimmed, out score);
}
/// <inheritdoc />
public string GetLocalizedString(string phrase)
{
+ return GetLocalizedString(phrase, CultureInfo.CurrentUICulture.Name);
+ }
+
+ /// <inheritdoc />
+ public string GetServerLocalizedString(string phrase)
+ {
return GetLocalizedString(phrase, _configurationManager.Configuration.UICulture);
}
@@ -393,6 +545,12 @@ namespace Emby.Server.Implementations.Localization
culture = DefaultCulture;
}
+ // Normalize BCP-47 hyphenated codes to Jellyfin's underscore-based codes
+ if (_bcp47ToJellyfinMap.TryGetValue(culture, out var mapped))
+ {
+ culture = mapped;
+ }
+
var dictionary = GetLocalizationDictionary(culture);
if (dictionary.TryGetValue(phrase, out var value))
@@ -400,6 +558,15 @@ namespace Emby.Server.Implementations.Localization
return value;
}
+ if (!string.Equals(culture, DefaultCulture, StringComparison.OrdinalIgnoreCase))
+ {
+ var fallback = GetLocalizationDictionary(DefaultCulture);
+ if (fallback.TryGetValue(phrase, out var fallbackValue))
+ {
+ return fallbackValue;
+ }
+ }
+
return phrase;
}
@@ -407,26 +574,17 @@ namespace Emby.Server.Implementations.Localization
{
ArgumentException.ThrowIfNullOrEmpty(culture);
- const string Prefix = "Core";
-
- return _dictionaries.GetOrAdd(
+ return _cultureOnlyDictionaries.GetOrAdd(
culture,
- static (key, localizationManager) => localizationManager.GetDictionary(Prefix, key, DefaultCulture + ".json").GetAwaiter().GetResult(),
- this);
- }
-
- private async Task<Dictionary<string, string>> GetDictionary(string prefix, string culture, string baseFilename)
- {
- ArgumentException.ThrowIfNullOrEmpty(culture);
-
- var dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
-
- var namespaceName = GetType().Namespace + "." + prefix;
-
- await CopyInto(dictionary, namespaceName + "." + baseFilename).ConfigureAwait(false);
- await CopyInto(dictionary, namespaceName + "." + GetResourceFilename(culture)).ConfigureAwait(false);
+ static (key, localizationManager) =>
+ {
+ var dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+ var namespaceName = localizationManager.GetType().Namespace + ".Core";
+ localizationManager.CopyInto(dictionary, namespaceName + "." + GetResourceFilename(key)).GetAwaiter().GetResult();
- return dictionary;
+ return dictionary;
+ },
+ this);
}
private async Task CopyInto(IDictionary<string, string> dictionary, string resourcePath)
@@ -448,11 +606,15 @@ namespace Emby.Server.Implementations.Localization
private static string GetResourceFilename(string culture)
{
- var parts = culture.Split('-');
+ // Region codes may use a '-' (BCP-47, e.g. "pt-BR") or '_' (e.g. "es_419", "ar_SA") separator.
+ // Normalize the casing (lower-case language, upper-case region) while preserving the separator
+ // so the result matches the embedded resource file name, which is case-sensitive.
+ var separatorIndex = culture.IndexOfAny(['-', '_']);
- if (parts.Length == 2)
+ if (separatorIndex > 0)
{
- culture = parts[0].ToLowerInvariant() + "-" + parts[1].ToUpperInvariant();
+ var separator = culture[separatorIndex];
+ culture = culture[..separatorIndex].ToLowerInvariant() + separator + culture[(separatorIndex + 1)..].ToUpperInvariant();
}
else
{
@@ -465,77 +627,55 @@ namespace Emby.Server.Implementations.Localization
/// <inheritdoc />
public IEnumerable<LocalizationOption> GetLocalizationOptions()
{
- yield return new LocalizationOption("Afrikaans", "af");
- yield return new LocalizationOption("العربية", "ar");
- yield return new LocalizationOption("Беларуская", "be");
- yield return new LocalizationOption("Български", "bg-BG");
- yield return new LocalizationOption("বাংলা (বাংলাদেশ)", "bn");
- yield return new LocalizationOption("Català", "ca");
- yield return new LocalizationOption("Čeština", "cs");
- yield return new LocalizationOption("Cymraeg", "cy");
- yield return new LocalizationOption("Dansk", "da");
- yield return new LocalizationOption("Deutsch", "de");
- yield return new LocalizationOption("English (United Kingdom)", "en-GB");
- yield return new LocalizationOption("English", "en-US");
- yield return new LocalizationOption("Ελληνικά", "el");
- yield return new LocalizationOption("Esperanto", "eo");
- yield return new LocalizationOption("Español", "es");
- yield return new LocalizationOption("Español americano", "es_419");
- yield return new LocalizationOption("Español (Argentina)", "es-AR");
- yield return new LocalizationOption("Español (Dominicana)", "es_DO");
- yield return new LocalizationOption("Español (México)", "es-MX");
- yield return new LocalizationOption("Eesti", "et");
- yield return new LocalizationOption("Basque", "eu");
- yield return new LocalizationOption("فارسی", "fa");
- yield return new LocalizationOption("Suomi", "fi");
- yield return new LocalizationOption("Filipino", "fil");
- yield return new LocalizationOption("Français", "fr");
- yield return new LocalizationOption("Français (Canada)", "fr-CA");
- yield return new LocalizationOption("Galego", "gl");
- yield return new LocalizationOption("Schwiizerdütsch", "gsw");
- yield return new LocalizationOption("עִבְרִית", "he");
- yield return new LocalizationOption("हिन्दी", "hi");
- yield return new LocalizationOption("Hrvatski", "hr");
- yield return new LocalizationOption("Magyar", "hu");
- yield return new LocalizationOption("Bahasa Indonesia", "id");
- yield return new LocalizationOption("Íslenska", "is");
- yield return new LocalizationOption("Italiano", "it");
- yield return new LocalizationOption("日本語", "ja");
- yield return new LocalizationOption("Qazaqşa", "kk");
- yield return new LocalizationOption("한국어", "ko");
- yield return new LocalizationOption("Lietuvių", "lt");
- yield return new LocalizationOption("Latviešu", "lv");
- yield return new LocalizationOption("Македонски", "mk");
- yield return new LocalizationOption("മലയാളം", "ml");
- yield return new LocalizationOption("मराठी", "mr");
- yield return new LocalizationOption("Bahasa Melayu", "ms");
- yield return new LocalizationOption("Norsk bokmål", "nb");
- yield return new LocalizationOption("नेपाली", "ne");
- yield return new LocalizationOption("Nederlands", "nl");
- yield return new LocalizationOption("Norsk nynorsk", "nn");
- yield return new LocalizationOption("ਪੰਜਾਬੀ", "pa");
- yield return new LocalizationOption("Polski", "pl");
- yield return new LocalizationOption("Pirate", "pr");
- yield return new LocalizationOption("Português", "pt");
- yield return new LocalizationOption("Português (Brasil)", "pt-BR");
- yield return new LocalizationOption("Português (Portugal)", "pt-PT");
- yield return new LocalizationOption("Românește", "ro");
- yield return new LocalizationOption("Русский", "ru");
- yield return new LocalizationOption("Slovenčina", "sk");
- yield return new LocalizationOption("Slovenščina", "sl-SI");
- yield return new LocalizationOption("Shqip", "sq");
- yield return new LocalizationOption("Српски", "sr");
- yield return new LocalizationOption("Svenska", "sv");
- yield return new LocalizationOption("தமிழ்", "ta");
- yield return new LocalizationOption("తెలుగు", "te");
- yield return new LocalizationOption("ภาษาไทย", "th");
- yield return new LocalizationOption("Türkçe", "tr");
- yield return new LocalizationOption("Українська", "uk");
- yield return new LocalizationOption("اُردُو", "ur_PK");
- yield return new LocalizationOption("Tiếng Việt", "vi");
- yield return new LocalizationOption("汉语 (简体字)", "zh-CN");
- yield return new LocalizationOption("漢語 (繁體字)", "zh-TW");
- yield return new LocalizationOption("廣東話 (香港)", "zh-HK");
+ return _localizationOptions;
+ }
+
+ private static (IReadOnlyList<LocalizationOption> Options, FrozenDictionary<string, string> Bcp47ToJellyfinMap) BuildLocalizationData()
+ {
+ var options = new List<LocalizationOption>();
+ var bcp47Map = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+ var prefix = CoreResourcePrefix;
+
+ foreach (var resource in _assembly.GetManifestResourceNames())
+ {
+ if (!resource.StartsWith(prefix, StringComparison.Ordinal)
+ || !resource.EndsWith(".json", StringComparison.Ordinal))
+ {
+ continue;
+ }
+
+ // Extract culture code from resource name: "...Core.de.json" -> "de", "...Core.pt-BR.json" -> "pt-BR"
+ var code = resource[prefix.Length..^5];
+
+ // Record the BCP-47 → Jellyfin mapping for any resource file using underscores.
+ if (code.Contains('_', StringComparison.Ordinal))
+ {
+ bcp47Map[code.Replace('_', '-')] = code;
+ }
+
+ // Skip the base language file — en-US is added explicitly below
+ if (code.Equals(DefaultCulture, StringComparison.OrdinalIgnoreCase))
+ {
+ continue;
+ }
+
+ var displayName = GetDisplayName(code);
+ options.Add(new LocalizationOption(displayName, code));
+ }
+
+ // Ensure en-US is always present
+ options.Add(new LocalizationOption("English", DefaultCulture));
+
+ options.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase));
+ return (options, bcp47Map.ToFrozenDictionary(StringComparer.OrdinalIgnoreCase));
+ }
+
+ private static string GetDisplayName(string cultureCode)
+ {
+ // Custom/novelty codes like "pr" (Pirate) — fall back to code itself
+ return TryGetCultureInfo(cultureCode, out var cultureInfo)
+ ? cultureInfo.NativeName
+ : cultureCode;
}
/// <inheritdoc />
diff --git a/Emby.Server.Implementations/Localization/Ratings/ca.json b/Emby.Server.Implementations/Localization/Ratings/ca.json
index fa43a8f2b7..76550b64c3 100644
--- a/Emby.Server.Implementations/Localization/Ratings/ca.json
+++ b/Emby.Server.Implementations/Localization/Ratings/ca.json
@@ -3,7 +3,7 @@
"supportsSubScores": true,
"ratings": [
{
- "ratingStrings": ["E", "G", "TV-Y", "TV-G"],
+ "ratingStrings": ["C", "E", "G", "TV-Y", "TV-G"],
"ratingScore": {
"score": 0,
"subScore": 0
@@ -24,13 +24,20 @@
}
},
{
- "ratingStrings": ["PG", "TV-PG"],
+ "ratingStrings": ["C8"],
"ratingScore": {
- "score": 9,
+ "score": 8,
"subScore": 0
}
},
{
+ "ratingStrings": ["PG", "TV-PG"],
+ "ratingScore": {
+ "score": 8,
+ "subScore": 1
+ }
+ },
+ {
"ratingStrings": ["14A"],
"ratingScore": {
"score": 14,
@@ -38,7 +45,7 @@
}
},
{
- "ratingStrings": ["TV-14"],
+ "ratingStrings": ["14+", "TV-14"],
"ratingScore": {
"score": 14,
"subScore": 1
diff --git a/Emby.Server.Implementations/Localization/Ratings/gr.json b/Emby.Server.Implementations/Localization/Ratings/gr.json
index 794bf0b313..73abab72d3 100644
--- a/Emby.Server.Implementations/Localization/Ratings/gr.json
+++ b/Emby.Server.Implementations/Localization/Ratings/gr.json
@@ -10,21 +10,42 @@
}
},
{
- "ratingStrings": ["K12"],
+ "ratingStrings": ["K12", "K-12", "12"],
+ "ratingScore": {
+ "score": 12,
+ "subScore": null
+ }
+ },
+ {
+ "ratingStrings": ["K13", "K-13", "13"],
"ratingScore": {
"score": 13,
"subScore": null
}
},
{
- "ratingStrings": ["K15"],
+ "ratingStrings": ["K15", "K-15", "15"],
"ratingScore": {
"score": 15,
"subScore": null
}
},
{
- "ratingStrings": ["K18"],
+ "ratingStrings": ["K16", "K-16", "16"],
+ "ratingScore": {
+ "score": 16,
+ "subScore": null
+ }
+ },
+ {
+ "ratingStrings": ["K17", "K-17", "17"],
+ "ratingScore": {
+ "score": 17,
+ "subScore": null
+ }
+ },
+ {
+ "ratingStrings": ["K18", "K-18", "18", "18+"],
"ratingScore": {
"score": 18,
"subScore": null
diff --git a/Emby.Server.Implementations/Localization/Ratings/se.json b/Emby.Server.Implementations/Localization/Ratings/se.json
index 70084995d1..818565e16b 100644
--- a/Emby.Server.Implementations/Localization/Ratings/se.json
+++ b/Emby.Server.Implementations/Localization/Ratings/se.json
@@ -10,7 +10,7 @@
}
},
{
- "ratingStrings": ["7"],
+ "ratingStrings": ["7", "7+", "7 År", "Från 7 år"],
"ratingScore": {
"score": 7,
"subScore": null
@@ -31,7 +31,7 @@
}
},
{
- "ratingStrings": ["11"],
+ "ratingStrings": ["11", "11+", "11 År", "Från 11 år"],
"ratingScore": {
"score": 11,
"subScore": null
@@ -45,11 +45,18 @@
}
},
{
- "ratingStrings": ["15"],
+ "ratingStrings": ["15", "15+", "15 År", "Från 15 år"],
"ratingScore": {
"score": 15,
"subScore": null
}
+ },
+ {
+ "ratingStrings": ["18", "18+", "Barnförbjuden", "Bfj"],
+ "ratingScore": {
+ "score": 18,
+ "subScore": null
+ }
}
]
}
diff --git a/Emby.Server.Implementations/Localization/countries.json b/Emby.Server.Implementations/Localization/countries.json
index d92dc880b1..811a7d4094 100644
--- a/Emby.Server.Implementations/Localization/countries.json
+++ b/Emby.Server.Implementations/Localization/countries.json
@@ -750,6 +750,12 @@
"TwoLetterISORegionName": "TJ"
},
{
+ "DisplayName": "Tanzania",
+ "Name": "TZ",
+ "ThreeLetterISORegionName": "TZA",
+ "TwoLetterISORegionName": "TZ"
+ },
+ {
"DisplayName": "Thailand",
"Name": "TH",
"ThreeLetterISORegionName": "THA",
diff --git a/Emby.Server.Implementations/Playlists/PlaylistsFolder.cs b/Emby.Server.Implementations/Playlists/PlaylistsFolder.cs
index a5be2b616e..3bbbdd43a0 100644
--- a/Emby.Server.Implementations/Playlists/PlaylistsFolder.cs
+++ b/Emby.Server.Implementations/Playlists/PlaylistsFolder.cs
@@ -43,8 +43,9 @@ namespace Emby.Server.Implementations.Playlists
}
query.Recursive = true;
- query.IncludeItemTypes = new[] { BaseItemKind.Playlist };
- return QueryWithPostFiltering2(query);
+ query.IncludeItemTypes = [BaseItemKind.Playlist];
+
+ return QueryWithPostFiltering(query);
}
public override string GetClientTypeName()
diff --git a/Emby.Server.Implementations/Plugins/PluginManager.cs b/Emby.Server.Implementations/Plugins/PluginManager.cs
index 91ccb16ef9..f699c99d85 100644
--- a/Emby.Server.Implementations/Plugins/PluginManager.cs
+++ b/Emby.Server.Implementations/Plugins/PluginManager.cs
@@ -564,7 +564,8 @@ namespace Emby.Server.Implementations.Plugins
Id = instance.Id,
Status = PluginStatus.Active,
Name = instance.Name,
- Version = instance.Version.ToString()
+ Version = instance.Version.ToString(),
+ ImageResourceName = (instance as IHasEmbeddedImage)?.ImageResourceName
})
{
Instance = instance
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/AudioNormalizationTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/AudioNormalizationTask.cs
index 36708e2582..e4939205c9 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/AudioNormalizationTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/AudioNormalizationTask.cs
@@ -4,6 +4,7 @@ using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
+using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
@@ -26,7 +27,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks;
/// </summary>
public partial class AudioNormalizationTask : IScheduledTask
{
- private readonly IItemRepository _itemRepository;
+ private readonly IItemPersistenceService _persistenceService;
private readonly ILibraryManager _libraryManager;
private readonly IMediaEncoder _mediaEncoder;
private readonly IApplicationPaths _applicationPaths;
@@ -38,21 +39,21 @@ public partial class AudioNormalizationTask : IScheduledTask
/// <summary>
/// Initializes a new instance of the <see cref="AudioNormalizationTask"/> class.
/// </summary>
- /// <param name="itemRepository">Instance of the <see cref="IItemRepository"/> interface.</param>
+ /// <param name="persistenceService">Instance of the <see cref="IItemPersistenceService"/> interface.</param>
/// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
/// <param name="mediaEncoder">Instance of the <see cref="IMediaEncoder"/> interface.</param>
/// <param name="applicationPaths">Instance of the <see cref="IApplicationPaths"/> interface.</param>
/// <param name="localizationManager">Instance of the <see cref="ILocalizationManager"/> interface.</param>
/// <param name="logger">Instance of the <see cref="ILogger{AudioNormalizationTask}"/> interface.</param>
public AudioNormalizationTask(
- IItemRepository itemRepository,
+ IItemPersistenceService persistenceService,
ILibraryManager libraryManager,
IMediaEncoder mediaEncoder,
IApplicationPaths applicationPaths,
ILocalizationManager localizationManager,
ILogger<AudioNormalizationTask> logger)
{
- _itemRepository = itemRepository;
+ _persistenceService = persistenceService;
_libraryManager = libraryManager;
_mediaEncoder = mediaEncoder;
_applicationPaths = applicationPaths;
@@ -138,7 +139,7 @@ public partial class AudioNormalizationTask : IScheduledTask
{
if (toSaveDbItems.Count > 1)
{
- _itemRepository.SaveItems(toSaveDbItems, cancellationToken);
+ _persistenceService.SaveItems(toSaveDbItems, cancellationToken);
toSaveDbItems.Clear();
}
@@ -158,7 +159,7 @@ public partial class AudioNormalizationTask : IScheduledTask
if (toSaveDbItems.Count > 1)
{
- _itemRepository.SaveItems(toSaveDbItems, cancellationToken);
+ _persistenceService.SaveItems(toSaveDbItems, cancellationToken);
toSaveDbItems.Clear();
}
@@ -183,7 +184,7 @@ public partial class AudioNormalizationTask : IScheduledTask
{
if (toSaveDbItems.Count > 1)
{
- _itemRepository.SaveItems(toSaveDbItems, cancellationToken);
+ _persistenceService.SaveItems(toSaveDbItems, cancellationToken);
toSaveDbItems.Clear();
}
@@ -200,7 +201,7 @@ public partial class AudioNormalizationTask : IScheduledTask
if (toSaveDbItems.Count > 1)
{
- _itemRepository.SaveItems(toSaveDbItems, cancellationToken);
+ _persistenceService.SaveItems(toSaveDbItems, cancellationToken);
}
// Update progress
@@ -234,7 +235,7 @@ public partial class AudioNormalizationTask : IScheduledTask
{
FileName = _mediaEncoder.EncoderPath,
Arguments = args,
- RedirectStandardOutput = false,
+ StandardErrorEncoding = Encoding.UTF8,
RedirectStandardError = true
},
})
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs
index f81309560e..f1e1579a1d 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs
@@ -92,7 +92,8 @@ public class ChapterImagesTask : IScheduledTask
EnableImages = false
},
SourceTypes = [SourceType.Library],
- IsVirtualItem = false
+ IsVirtualItem = false,
+ IncludeOwnedItems = true
})
.OfType<Video>()
.ToList();
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanupCollectionAndPlaylistPathsTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanupCollectionAndPlaylistPathsTask.cs
deleted file mode 100644
index 7f68f7701e..0000000000
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanupCollectionAndPlaylistPathsTask.cs
+++ /dev/null
@@ -1,139 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Controller.Collections;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Movies;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Playlists;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Globalization;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Tasks;
-using Microsoft.Extensions.Logging;
-
-namespace Emby.Server.Implementations.ScheduledTasks.Tasks;
-
-/// <summary>
-/// Deletes path references from collections and playlists that no longer exists.
-/// </summary>
-public class CleanupCollectionAndPlaylistPathsTask : IScheduledTask
-{
- private readonly ILocalizationManager _localization;
- private readonly ICollectionManager _collectionManager;
- private readonly IPlaylistManager _playlistManager;
- private readonly ILogger<CleanupCollectionAndPlaylistPathsTask> _logger;
- private readonly IProviderManager _providerManager;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="CleanupCollectionAndPlaylistPathsTask"/> class.
- /// </summary>
- /// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
- /// <param name="collectionManager">Instance of the <see cref="ICollectionManager"/> interface.</param>
- /// <param name="playlistManager">Instance of the <see cref="IPlaylistManager"/> interface.</param>
- /// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param>
- /// <param name="providerManager">Instance of the <see cref="IProviderManager"/> interface.</param>
- public CleanupCollectionAndPlaylistPathsTask(
- ILocalizationManager localization,
- ICollectionManager collectionManager,
- IPlaylistManager playlistManager,
- ILogger<CleanupCollectionAndPlaylistPathsTask> logger,
- IProviderManager providerManager)
- {
- _localization = localization;
- _collectionManager = collectionManager;
- _playlistManager = playlistManager;
- _logger = logger;
- _providerManager = providerManager;
- }
-
- /// <inheritdoc />
- public string Name => _localization.GetLocalizedString("TaskCleanCollectionsAndPlaylists");
-
- /// <inheritdoc />
- public string Key => "CleanCollectionsAndPlaylists";
-
- /// <inheritdoc />
- public string Description => _localization.GetLocalizedString("TaskCleanCollectionsAndPlaylistsDescription");
-
- /// <inheritdoc />
- public string Category => _localization.GetLocalizedString("TasksMaintenanceCategory");
-
- /// <inheritdoc />
- public async Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
- {
- var collectionsFolder = await _collectionManager.GetCollectionsFolder(false).ConfigureAwait(false);
- if (collectionsFolder is null)
- {
- _logger.LogDebug("There is no collections folder to be found");
- }
- else
- {
- var collections = collectionsFolder.Children.OfType<BoxSet>().ToArray();
- _logger.LogDebug("Found {CollectionLength} boxsets", collections.Length);
-
- for (var index = 0; index < collections.Length; index++)
- {
- var collection = collections[index];
- _logger.LogDebug("Checking boxset {CollectionName}", collection.Name);
-
- await CleanupLinkedChildrenAsync(collection, cancellationToken).ConfigureAwait(false);
- progress.Report(50D / collections.Length * (index + 1));
- }
- }
-
- var playlistsFolder = _playlistManager.GetPlaylistsFolder();
- if (playlistsFolder is null)
- {
- _logger.LogDebug("There is no playlists folder to be found");
- return;
- }
-
- var playlists = playlistsFolder.Children.OfType<Playlist>().ToArray();
- _logger.LogDebug("Found {PlaylistLength} playlists", playlists.Length);
-
- for (var index = 0; index < playlists.Length; index++)
- {
- var playlist = playlists[index];
- _logger.LogDebug("Checking playlist {PlaylistName}", playlist.Name);
-
- await CleanupLinkedChildrenAsync(playlist, cancellationToken).ConfigureAwait(false);
- progress.Report(50D / playlists.Length * (index + 1));
- }
- }
-
- private async Task CleanupLinkedChildrenAsync<T>(T folder, CancellationToken cancellationToken)
- where T : Folder
- {
- List<LinkedChild>? itemsToRemove = null;
- foreach (var linkedChild in folder.LinkedChildren)
- {
- var path = linkedChild.Path;
- if (!File.Exists(path) && !Directory.Exists(path))
- {
- _logger.LogInformation("Item in {FolderName} cannot be found at {ItemPath}", folder.Name, path);
- (itemsToRemove ??= new List<LinkedChild>()).Add(linkedChild);
- }
- }
-
- if (itemsToRemove is not null)
- {
- _logger.LogDebug("Updating {FolderName}", folder.Name);
- folder.LinkedChildren = folder.LinkedChildren.Except(itemsToRemove).ToArray();
- await _providerManager.SaveMetadataAsync(folder, ItemUpdateType.MetadataEdit).ConfigureAwait(false);
- await folder.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, cancellationToken).ConfigureAwait(false);
- }
- }
-
- /// <inheritdoc />
- public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
- {
- yield return new TaskTriggerInfo
- {
- Type = TaskTriggerInfoType.StartupTrigger,
- };
- }
-}
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/MediaSegmentExtractionTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/MediaSegmentExtractionTask.cs
index 51920c5b14..9cc6eb265a 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/MediaSegmentExtractionTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/MediaSegmentExtractionTask.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
@@ -68,6 +68,7 @@ public class MediaSegmentExtractionTask : IScheduledTask
DtoOptions = new DtoOptions(true),
SourceTypes = [SourceType.Library],
Recursive = true,
+ IncludeOwnedItems = true,
Limit = pagesize
};
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/OptimizeDatabaseTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/OptimizeDatabaseTask.cs
index 92d7a3907a..8d133dc074 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/OptimizeDatabaseTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/OptimizeDatabaseTask.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Database.Implementations;
+using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Tasks;
using Microsoft.Extensions.Logging;
@@ -17,6 +18,7 @@ public class OptimizeDatabaseTask : IScheduledTask, IConfigurableScheduledTask
private readonly ILogger<OptimizeDatabaseTask> _logger;
private readonly ILocalizationManager _localization;
private readonly IJellyfinDatabaseProvider _jellyfinDatabaseProvider;
+ private readonly ILibraryManager _libraryManager;
/// <summary>
/// Initializes a new instance of the <see cref="OptimizeDatabaseTask" /> class.
@@ -24,14 +26,17 @@ public class OptimizeDatabaseTask : IScheduledTask, IConfigurableScheduledTask
/// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param>
/// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
/// <param name="jellyfinDatabaseProvider">Instance of the JellyfinDatabaseProvider that can be used for provider specific operations.</param>
+ /// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
public OptimizeDatabaseTask(
ILogger<OptimizeDatabaseTask> logger,
ILocalizationManager localization,
- IJellyfinDatabaseProvider jellyfinDatabaseProvider)
+ IJellyfinDatabaseProvider jellyfinDatabaseProvider,
+ ILibraryManager libraryManager)
{
_logger = logger;
_localization = localization;
_jellyfinDatabaseProvider = jellyfinDatabaseProvider;
+ _libraryManager = libraryManager;
}
/// <inheritdoc />
@@ -68,6 +73,15 @@ public class OptimizeDatabaseTask : IScheduledTask, IConfigurableScheduledTask
/// <inheritdoc />
public async Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
{
+ // Vacuuming/checkpointing requires an exclusive lock on the database. Running it while a library scan is in
+ // progress causes both operations to contend for the database and can stall the scan, so defer optimization
+ // until no scan is running. The task will run again on its next trigger.
+ if (_libraryManager.IsScanRunning)
+ {
+ _logger.LogInformation("Skipping database optimization because a library scan is currently running.");
+ return;
+ }
+
_logger.LogInformation("Optimizing and vacuuming jellyfin.db...");
try
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs
index 6e4e5c7808..dff9a473af 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/PeopleValidationTask.cs
@@ -4,11 +4,18 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Data.Enums;
using Jellyfin.Database.Implementations;
+using Jellyfin.Database.Implementations.Entities;
+using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Persistence;
+using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Globalization;
+using MediaBrowser.Model.IO;
using MediaBrowser.Model.Tasks;
using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.ScheduledTasks.Tasks;
@@ -20,6 +27,9 @@ public class PeopleValidationTask : IScheduledTask, IConfigurableScheduledTask
private readonly ILibraryManager _libraryManager;
private readonly ILocalizationManager _localization;
private readonly IDbContextFactory<JellyfinDbContext> _dbContextFactory;
+ private readonly IFileSystem _fileSystem;
+ private readonly ILogger<PeopleValidationTask> _logger;
+ private readonly IItemTypeLookup _itemTypeLookup;
/// <summary>
/// Initializes a new instance of the <see cref="PeopleValidationTask" /> class.
@@ -27,11 +37,23 @@ public class PeopleValidationTask : IScheduledTask, IConfigurableScheduledTask
/// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
/// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
/// <param name="dbContextFactory">Instance of the <see cref="IDbContextFactory{TContext}"/> interface.</param>
- public PeopleValidationTask(ILibraryManager libraryManager, ILocalizationManager localization, IDbContextFactory<JellyfinDbContext> dbContextFactory)
+ /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
+ /// <param name="logger">Instance of the <see cref="ILogger{PeopleValidationTask}"/> interface.</param>
+ /// <param name="itemTypeLookup">Instance of the <see cref="IItemTypeLookup"/> interface.</param>
+ public PeopleValidationTask(
+ ILibraryManager libraryManager,
+ ILocalizationManager localization,
+ IDbContextFactory<JellyfinDbContext> dbContextFactory,
+ IFileSystem fileSystem,
+ ILogger<PeopleValidationTask> logger,
+ IItemTypeLookup itemTypeLookup)
{
_libraryManager = libraryManager;
_localization = localization;
_dbContextFactory = dbContextFactory;
+ _fileSystem = fileSystem;
+ _logger = logger;
+ _itemTypeLookup = itemTypeLookup;
}
/// <inheritdoc />
@@ -71,13 +93,19 @@ public class PeopleValidationTask : IScheduledTask, IConfigurableScheduledTask
/// <inheritdoc />
public async Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
{
- IProgress<double> subProgress = new Progress<double>((val) => progress.Report(val / 2));
- await _libraryManager.ValidatePeopleAsync(subProgress, cancellationToken).ConfigureAwait(false);
+ // People validation performs heavy database writes that contend with an active library scan.
+ // Defer it until the scan has finished; the task will run again on its next trigger.
+ if (_libraryManager.IsScanRunning)
+ {
+ _logger.LogInformation("Skipping people validation because a library scan is currently running.");
+ return;
+ }
- subProgress = new Progress<double>((val) => progress.Report((val / 2) + 50));
+ // Phase 1: Deduplicate and remove orphaned people (0-33%)
var context = await _dbContextFactory.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
await using (context.ConfigureAwait(false))
{
+ IProgress<double> subProgress = new Progress<double>((val) => progress.Report(val / 3));
var dupQuery = context.Peoples
.GroupBy(e => new { e.Name, e.PersonType })
.Where(e => e.Count() > 1)
@@ -123,7 +151,113 @@ public class PeopleValidationTask : IScheduledTask, IConfigurableScheduledTask
ArrayPool<Guid[]>.Shared.Return(buffer);
}
+ var peopleToDelete = await context.Peoples
+ .Where(p => !context.PeopleBaseItemMap.Any(m => m.PeopleId.Equals(p.Id)))
+ .ExecuteDeleteAsync(cancellationToken)
+ .ConfigureAwait(false);
+ _logger.LogInformation("Removed {Count} orphaned people.", peopleToDelete);
+
subProgress.Report(100);
}
+
+ // Phase 2: Validate people (33-66%). Runs after orphaned PeopleBaseItemMap entries are
+ // cleaned up above, so dead people are removed in a single pass instead of requiring a second run.
+ IProgress<double> validateProgress = new Progress<double>((val) => progress.Report((val / 3) + 33));
+ await _libraryManager.ValidatePeopleAsync(validateProgress, cancellationToken).ConfigureAwait(false);
+
+ // Phase 3: Refresh images for people missing them (66-100%)
+ IProgress<double> refreshProgress = new Progress<double>((val) => progress.Report((val / 3) + 66));
+ await RefreshPeopleImagesAsync(refreshProgress, cancellationToken).ConfigureAwait(false);
+
+ progress.Report(100);
+ }
+
+ private async Task RefreshPeopleImagesAsync(IProgress<double> progress, CancellationToken cancellationToken)
+ {
+ var thirtyDaysAgo = DateTime.UtcNow.AddDays(-30);
+ var personTypeName = _itemTypeLookup.BaseItemKindNames[BaseItemKind.Person];
+
+ var context = await _dbContextFactory.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
+ await using (context.ConfigureAwait(false))
+ {
+ const int PartitionSize = 100;
+
+ var numPeople = await context.BaseItems
+ .AsNoTracking()
+ .Where(b => b.Type == personTypeName)
+ .Where(b => b.DateLastRefreshed == null || b.DateLastRefreshed < thirtyDaysAgo)
+ .Where(b =>
+ !b.Images!.Any(i => i.ImageType == ImageInfoImageType.Primary) ||
+ string.IsNullOrEmpty(b.Overview))
+ .CountAsync(cancellationToken)
+ .ConfigureAwait(false);
+
+ _logger.LogDebug("Found {Count} people needing image/overview refresh", numPeople);
+
+ if (numPeople == 0)
+ {
+ progress.Report(100);
+ return;
+ }
+
+ var numComplete = 0;
+ var numRefreshed = 0;
+
+ await foreach (var entry in context.BaseItems
+ .AsNoTracking()
+ .Where(b => b.Type == personTypeName)
+ .Where(b => b.DateLastRefreshed == null || b.DateLastRefreshed < thirtyDaysAgo)
+ .Where(b =>
+ !b.Images!.Any(i => i.ImageType == ImageInfoImageType.Primary) ||
+ string.IsNullOrEmpty(b.Overview))
+ .OrderBy(b => b.Id)
+ .WithPartitionProgress(partition => _logger.LogDebug("Processing people partition {Partition}", partition))
+ .PartitionEagerAsync(PartitionSize, cancellationToken)
+ .WithCancellation(cancellationToken)
+ .ConfigureAwait(false))
+ {
+ if (await RefreshPersonAsync(entry.Id, cancellationToken).ConfigureAwait(false))
+ {
+ numRefreshed++;
+ }
+
+ numComplete++;
+ progress.Report(100.0 * numComplete / numPeople);
+ }
+
+ _logger.LogInformation("Refreshed metadata for {Count} people missing images or overview", numRefreshed);
+ }
+ }
+
+ private async Task<bool> RefreshPersonAsync(Guid personId, CancellationToken cancellationToken)
+ {
+ try
+ {
+ if (_libraryManager.GetItemById(personId) is not Person item)
+ {
+ return false;
+ }
+
+ var hasImage = item.HasImage(MediaBrowser.Model.Entities.ImageType.Primary);
+ var hasOverview = !string.IsNullOrEmpty(item.Overview);
+
+ var options = new MetadataRefreshOptions(new DirectoryService(_fileSystem))
+ {
+ ImageRefreshMode = hasImage ? MetadataRefreshMode.ValidationOnly : MetadataRefreshMode.Default,
+ MetadataRefreshMode = hasOverview ? MetadataRefreshMode.ValidationOnly : MetadataRefreshMode.Default
+ };
+
+ await item.RefreshMetadata(options, cancellationToken).ConfigureAwait(false);
+ return true;
+ }
+ catch (OperationCanceledException)
+ {
+ throw;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error refreshing images for person {PersonId}", personId);
+ return false;
+ }
}
}
diff --git a/Emby.Server.Implementations/Serialization/MyXmlSerializer.cs b/Emby.Server.Implementations/Serialization/MyXmlSerializer.cs
index aa5fbbdf73..5c9a94cd36 100644
--- a/Emby.Server.Implementations/Serialization/MyXmlSerializer.cs
+++ b/Emby.Server.Implementations/Serialization/MyXmlSerializer.cs
@@ -85,9 +85,17 @@ namespace Emby.Server.Implementations.Serialization
/// <returns>System.Object.</returns>
public object? DeserializeFromFile(Type type, string file)
{
- using (var stream = File.OpenRead(file))
+ try
{
- return DeserializeFromStream(type, stream);
+ using (var stream = File.OpenRead(file))
+ {
+ return DeserializeFromStream(type, stream);
+ }
+ }
+ catch (Exception ex)
+ {
+ ex.Data.Add("Filename", file);
+ throw;
}
}
diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs
index 8e14f5bdf4..828bdd6859 100644
--- a/Emby.Server.Implementations/Session/SessionManager.cs
+++ b/Emby.Server.Implementations/Session/SessionManager.cs
@@ -271,9 +271,9 @@ namespace Emby.Server.Implementations.Session
user.LastActivityDate = activityDate;
await _userManager.UpdateUserAsync(user).ConfigureAwait(false);
}
- catch (DbUpdateConcurrencyException e)
+ catch (DbUpdateConcurrencyException)
{
- _logger.LogDebug(e, "Error updating user's last activity date.");
+ _logger.LogDebug("Error updating user's last activity date due to concurrency conflict. This is an expected event.");
}
}
}
@@ -343,6 +343,10 @@ namespace Emby.Server.Implementations.Session
_activeLiveStreamSessions.TryRemove(liveStreamId, out _);
}
}
+ else
+ {
+ liveStreamNeedsToBeClosed = true;
+ }
if (liveStreamNeedsToBeClosed)
{
@@ -386,7 +390,7 @@ namespace Emby.Server.Implementations.Session
{
if (session is null)
{
- return;
+ return;
}
if (string.IsNullOrEmpty(info.MediaSourceId))
@@ -453,18 +457,6 @@ namespace Emby.Server.Implementations.Session
session.PlayState.RepeatMode = info.RepeatMode;
session.PlayState.PlaybackOrder = info.PlaybackOrder;
session.PlaylistItemId = info.PlaylistItemId;
-
- var nowPlayingQueue = info.NowPlayingQueue;
-
- if (nowPlayingQueue?.Length > 0 && !nowPlayingQueue.SequenceEqual(session.NowPlayingQueue))
- {
- session.NowPlayingQueue = nowPlayingQueue;
-
- var itemIds = Array.ConvertAll(nowPlayingQueue, queue => queue.Id);
- session.NowPlayingQueueFullItems = _dtoService.GetBaseItemDtos(
- _libraryManager.GetItemList(new InternalItemsQuery { ItemIds = itemIds }),
- new DtoOptions(true));
- }
}
/// <summary>
@@ -738,6 +730,31 @@ namespace Emby.Server.Implementations.Session
}
/// <summary>
+ /// Resolves the item whose user data (playback position, played status) should be updated
+ /// for a playback report. When an alternate version is played the client reports the displayed
+ /// item as <c>ItemId</c> and the played version as <c>MediaSourceId</c>.
+ /// </summary>
+ /// <param name="libraryItem">The now playing (displayed) item.</param>
+ /// <param name="mediaSourceId">The reported media source id.</param>
+ /// <returns>The item to track progress against.</returns>
+ private BaseItem GetProgressItem(BaseItem libraryItem, string mediaSourceId)
+ {
+ if (libraryItem is Video libraryVideo
+ && !string.IsNullOrEmpty(mediaSourceId)
+ && Guid.TryParse(mediaSourceId, out var mediaSourceItemId)
+ && !mediaSourceItemId.Equals(libraryVideo.Id))
+ {
+ var versionItem = libraryVideo.GetAlternateVersion(mediaSourceItemId);
+ if (versionItem is not null)
+ {
+ return versionItem;
+ }
+ }
+
+ return libraryItem;
+ }
+
+ /// <summary>
/// Used to report that playback has started for an item.
/// </summary>
/// <param name="info">The info.</param>
@@ -768,9 +785,10 @@ namespace Emby.Server.Implementations.Session
if (libraryItem is not null)
{
+ var progressItem = GetProgressItem(libraryItem, info.MediaSourceId);
foreach (var user in users)
{
- OnPlaybackStart(user, libraryItem);
+ OnPlaybackStart(user, progressItem);
}
}
@@ -832,10 +850,6 @@ namespace Emby.Server.Implementations.Session
{
data.Played = true;
}
- else
- {
- data.Played = false;
- }
_userDataManager.SaveUserData(user, item, data, UserDataSaveReason.PlaybackStart, CancellationToken.None);
}
@@ -906,9 +920,10 @@ namespace Emby.Server.Implementations.Session
// only update saved user data on actual check-ins, not automated ones
if (libraryItem is not null && !isAutomated)
{
+ var progressItem = GetProgressItem(libraryItem, info.MediaSourceId);
foreach (var user in users)
{
- OnPlaybackProgress(user, libraryItem, info);
+ OnPlaybackProgress(user, progressItem, info);
}
}
@@ -960,7 +975,7 @@ namespace Emby.Server.Implementations.Session
}
var tracksChanged = UpdatePlaybackSettings(user, info, data);
- if (!tracksChanged)
+ if (tracksChanged)
{
changed = true;
}
@@ -968,6 +983,20 @@ namespace Emby.Server.Implementations.Session
if (changed)
{
_userDataManager.SaveUserData(user, item, data, UserDataSaveReason.PlaybackProgress, CancellationToken.None);
+
+ // A completed version marks every alternate version played and clears their resume points, so the
+ // whole movie leaves Continue Watching and reads as watched everywhere. (Per-version resume positions
+ // only persist while nothing has been completed yet.)
+ if (data.Played == true && item is Video playedVideo)
+ {
+ playedVideo.PropagatePlayedState(user, true);
+ }
+ }
+
+ if ((!user.RememberAudioSelections && info.AudioStreamIndex.HasValue)
+ || (!user.RememberSubtitleSelections && info.SubtitleStreamIndex.HasValue))
+ {
+ _userDataManager.ResetPlaybackStreamSelections(user, item);
}
}
@@ -977,7 +1006,7 @@ namespace Emby.Server.Implementations.Session
if (user.RememberAudioSelections)
{
- if (data.AudioStreamIndex != info.AudioStreamIndex)
+ if (info.AudioStreamIndex.HasValue && data.AudioStreamIndex != info.AudioStreamIndex)
{
data.AudioStreamIndex = info.AudioStreamIndex;
changed = true;
@@ -994,7 +1023,7 @@ namespace Emby.Server.Implementations.Session
if (user.RememberSubtitleSelections)
{
- if (data.SubtitleStreamIndex != info.SubtitleStreamIndex)
+ if (info.SubtitleStreamIndex.HasValue && data.SubtitleStreamIndex != info.SubtitleStreamIndex)
{
data.SubtitleStreamIndex = info.SubtitleStreamIndex;
changed = true;
@@ -1025,15 +1054,22 @@ namespace Emby.Server.Implementations.Session
ArgumentNullException.ThrowIfNull(info);
+ var session = GetSession(info.SessionId);
+
+ session.StopAutomaticProgress();
+
if (info.PositionTicks.HasValue && info.PositionTicks.Value < 0)
{
+ // Ensure live stream is cleaned up before throwing, to prevent tuner
+ // resource leaks when stalled clients report a negative PositionTicks.
+ if (!string.IsNullOrEmpty(info.LiveStreamId))
+ {
+ await CloseLiveStreamIfNeededAsync(info.LiveStreamId, session.Id).ConfigureAwait(false);
+ }
+
throw new ArgumentOutOfRangeException(nameof(info), "The PlaybackStopInfo's PositionTicks was negative.");
}
- var session = GetSession(info.SessionId);
-
- session.StopAutomaticProgress();
-
var libraryItem = info.ItemId.IsEmpty()
? null
: GetNowPlayingItem(session, info.ItemId);
@@ -1092,9 +1128,10 @@ namespace Emby.Server.Implementations.Session
if (libraryItem is not null)
{
+ var progressItem = GetProgressItem(libraryItem, info.MediaSourceId);
foreach (var user in users)
{
- playedToCompletion = OnPlaybackStopped(user, libraryItem, info.PositionTicks, info.Failed);
+ playedToCompletion = OnPlaybackStopped(user, progressItem, info.PositionTicks, info.Failed);
}
}
@@ -1147,6 +1184,14 @@ namespace Emby.Server.Implementations.Session
_userDataManager.SaveUserData(user, item, data, UserDataSaveReason.PlaybackFinished, CancellationToken.None);
+ // A completed version marks every alternate version played and clears their resume points, so the
+ // whole movie leaves Continue Watching and reads as watched everywhere. (Per-version resume positions
+ // only persist while nothing has been completed yet.)
+ if (data.Played == true && item is Video playedVideo)
+ {
+ playedVideo.PropagatePlayedState(user, true);
+ }
+
return playedToCompletion;
}
@@ -1214,7 +1259,6 @@ namespace Emby.Server.Implementations.Session
SupportsMediaControl = sessionInfo.SupportsMediaControl,
SupportsRemoteControl = sessionInfo.SupportsRemoteControl,
NowPlayingQueue = sessionInfo.NowPlayingQueue,
- NowPlayingQueueFullItems = sessionInfo.NowPlayingQueueFullItems,
HasCustomDeviceName = sessionInfo.HasCustomDeviceName,
PlaylistItemId = sessionInfo.PlaylistItemId,
ServerId = sessionInfo.ServerId,
@@ -1832,7 +1876,6 @@ namespace Emby.Server.Implementations.Session
fields.Remove(ItemFields.Settings);
fields.Remove(ItemFields.SortName);
fields.Remove(ItemFields.Tags);
- fields.Remove(ItemFields.ExtraIds);
dtoOptions.Fields = fields.ToArray();
@@ -2054,7 +2097,7 @@ namespace Emby.Server.Implementations.Session
{
CheckDisposed();
- var adminUserIds = _userManager.Users
+ var adminUserIds = _userManager.GetUsers()
.Where(i => i.HasPermission(PermissionKind.IsAdministrator))
.Select(i => i.Id)
.ToList();
diff --git a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs
index 6a26e92e14..2582ed9df0 100644
--- a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs
+++ b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs
@@ -246,8 +246,21 @@ namespace Emby.Server.Implementations.Session
_logger.LogInformation("Lost {0} WebSockets.", lost.Count);
foreach (var webSocket in lost)
{
- // TODO: handle session relative to the lost webSocket
RemoveWebSocket(webSocket);
+
+ // The connection stopped answering keep-alives, so a close frame will
+ // never arrive and the pending receive loop would hang forever, keeping
+ // the session (and e.g. its SyncPlay group membership) alive. Disposing
+ // the connection aborts the receive loop, which raises Closed and lets
+ // the session end normally.
+ try
+ {
+ webSocket.Dispose();
+ }
+ catch (Exception exception)
+ {
+ _logger.LogWarning(exception, "Error disposing lost WebSocket from {RemoteEndPoint}.", webSocket.RemoteEndPoint);
+ }
}
}
}
diff --git a/Emby.Server.Implementations/Sorting/OfficialRatingComparer.cs b/Emby.Server.Implementations/Sorting/OfficialRatingComparer.cs
index 789af01cc3..c0e453d63d 100644
--- a/Emby.Server.Implementations/Sorting/OfficialRatingComparer.cs
+++ b/Emby.Server.Implementations/Sorting/OfficialRatingComparer.cs
@@ -41,8 +41,8 @@ public class OfficialRatingComparer : IBaseItemComparer
ArgumentNullException.ThrowIfNull(y);
var zeroRating = new ParentalRatingScore(0, 0);
- var ratingX = string.IsNullOrEmpty(x.OfficialRating) ? zeroRating : _localizationManager.GetRatingScore(x.OfficialRating) ?? zeroRating;
- var ratingY = string.IsNullOrEmpty(y.OfficialRating) ? zeroRating : _localizationManager.GetRatingScore(y.OfficialRating) ?? zeroRating;
+ var ratingX = string.IsNullOrEmpty(x.OfficialRating) ? zeroRating : _localizationManager.GetRatingScore(x.OfficialRating, x.GetPreferredMetadataCountryCode()) ?? zeroRating;
+ var ratingY = string.IsNullOrEmpty(y.OfficialRating) ? zeroRating : _localizationManager.GetRatingScore(y.OfficialRating, y.GetPreferredMetadataCountryCode()) ?? zeroRating;
var scoreCompare = ratingX.Score.CompareTo(ratingY.Score);
if (scoreCompare is 0)
{
diff --git a/Emby.Server.Implementations/SyncPlay/Group.cs b/Emby.Server.Implementations/SyncPlay/Group.cs
index c2e834ad58..38a0018a70 100644
--- a/Emby.Server.Implementations/SyncPlay/Group.cs
+++ b/Emby.Server.Implementations/SyncPlay/Group.cs
@@ -206,7 +206,8 @@ namespace Emby.Server.Implementations.SyncPlay
foreach (var itemId in queue)
{
var item = _libraryManager.GetItemById(itemId);
- if (!item.IsVisibleStandalone(user))
+
+ if (item is null || !item.IsVisibleStandalone(user))
{
return false;
}
diff --git a/Emby.Server.Implementations/SystemManager.cs b/Emby.Server.Implementations/SystemManager.cs
index d140426ddf..11a94648f8 100644
--- a/Emby.Server.Implementations/SystemManager.cs
+++ b/Emby.Server.Implementations/SystemManager.cs
@@ -89,11 +89,11 @@ public class SystemManager : ISystemManager
.GetVirtualFolders()
.Where(e => !string.IsNullOrWhiteSpace(e.ItemId)) // this should not be null but for some users it is.
.Select(e => new LibraryStorageInfo()
- {
- Id = Guid.Parse(e.ItemId),
- Name = e.Name,
- Folders = e.Locations.Select(f => StorageHelper.GetFreeSpaceOf(f)).ToArray()
- });
+ {
+ Id = Guid.Parse(e.ItemId),
+ Name = e.Name,
+ Folders = e.Locations.Select(f => StorageHelper.GetFreeSpaceOf(f)).ToArray()
+ });
return new SystemStorageInfo()
{
diff --git a/Emby.Server.Implementations/TV/TVSeriesManager.cs b/Emby.Server.Implementations/TV/TVSeriesManager.cs
index cd98dbe86e..459ad1a17e 100644
--- a/Emby.Server.Implementations/TV/TVSeriesManager.cs
+++ b/Emby.Server.Implementations/TV/TVSeriesManager.cs
@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
+using System.Globalization;
using System.Linq;
using Jellyfin.Data;
using Jellyfin.Data.Enums;
@@ -47,7 +48,7 @@ namespace Emby.Server.Implementations.TV
if (!string.IsNullOrEmpty(presentationUniqueKey))
{
- return GetResult(GetNextUpEpisodes(query, user, new[] { presentationUniqueKey }, options), query);
+ return GetNextUpBatched(query, user, [presentationUniqueKey], options);
}
BaseItem[] parents;
@@ -58,11 +59,11 @@ namespace Emby.Server.Implementations.TV
if (parent is not null)
{
- parents = new[] { parent };
+ parents = [parent];
}
else
{
- parents = Array.Empty<BaseItem>();
+ parents = [];
}
}
else
@@ -93,7 +94,7 @@ namespace Emby.Server.Implementations.TV
if (!string.IsNullOrEmpty(presentationUniqueKey))
{
- return GetResult(GetNextUpEpisodes(request, user, [presentationUniqueKey], options), request);
+ return GetNextUpBatched(request, user, [presentationUniqueKey], options);
}
if (limit.HasValue)
@@ -103,151 +104,220 @@ namespace Emby.Server.Implementations.TV
var nextUpSeriesKeys = _libraryManager.GetNextUpSeriesKeys(new InternalItemsQuery(user) { Limit = limit }, parentsFolders, request.NextUpDateCutoff);
- var episodes = GetNextUpEpisodes(request, user, nextUpSeriesKeys, options);
-
- return GetResult(episodes, request);
+ return GetNextUpBatched(request, user, nextUpSeriesKeys, options);
}
- private IEnumerable<Episode> GetNextUpEpisodes(NextUpQuery request, User user, IReadOnlyList<string> seriesKeys, DtoOptions dtoOptions)
+ private QueryResult<BaseItem> GetNextUpBatched(NextUpQuery request, User user, IReadOnlyList<string> seriesKeys, DtoOptions dtoOptions)
{
- var allNextUp = seriesKeys.Select(i => GetNextUp(i, user, dtoOptions, request.EnableResumable, false));
-
- if (request.EnableRewatching)
+ if (seriesKeys.Count == 0)
{
- allNextUp = allNextUp
- .Concat(seriesKeys.Select(i => GetNextUp(i, user, dtoOptions, false, true)))
- .OrderByDescending(i => i.LastWatchedDate);
+ return new QueryResult<BaseItem>();
}
- return allNextUp
- .Select(i => i.GetEpisodeFunction())
- .Where(i => i is not null)!;
- }
+ var includeSpecials = _configurationManager.Configuration.DisplaySpecialsWithinSeasons;
+ var includeRewatching = request.EnableRewatching;
- private static string GetUniqueSeriesKey(Series series)
- {
- return series.GetPresentationUniqueKey();
- }
-
- /// <summary>
- /// Gets the next up.
- /// </summary>
- /// <returns>Task{Episode}.</returns>
- private (DateTime LastWatchedDate, Func<Episode?> GetEpisodeFunction) GetNextUp(string seriesKey, User user, DtoOptions dtoOptions, bool includeResumable, bool includePlayed)
- {
- var lastQuery = new InternalItemsQuery(user)
+ var query = new InternalItemsQuery(user)
{
- AncestorWithPresentationUniqueKey = null,
- SeriesPresentationUniqueKey = seriesKey,
- IncludeItemTypes = [BaseItemKind.Episode],
- IsPlayed = true,
- Limit = 1,
- ParentIndexNumberNotEquals = 0,
- DtoOptions = new DtoOptions
- {
- Fields = [ItemFields.SortName],
- EnableImages = false
- }
+ DtoOptions = dtoOptions
};
- // If including played results, sort first by date played and then by season and episode numbers
- lastQuery.OrderBy = includePlayed
- ? new[] { (ItemSortBy.DatePlayed, SortOrder.Descending), (ItemSortBy.ParentIndexNumber, SortOrder.Descending), (ItemSortBy.IndexNumber, SortOrder.Descending) }
- : new[] { (ItemSortBy.ParentIndexNumber, SortOrder.Descending), (ItemSortBy.IndexNumber, SortOrder.Descending) };
+ var batchResult = _libraryManager.GetNextUpEpisodesBatch(query, seriesKeys, includeSpecials, includeRewatching);
- var lastWatchedEpisode = _libraryManager.GetItemList(lastQuery).Cast<Episode>().FirstOrDefault();
+ var nextUpList = new List<(DateTime LastWatchedDate, Episode Episode)>();
- Episode? GetEpisode()
+ foreach (var seriesKey in seriesKeys)
{
- var nextQuery = new InternalItemsQuery(user)
- {
- AncestorWithPresentationUniqueKey = null,
- SeriesPresentationUniqueKey = seriesKey,
- IncludeItemTypes = [BaseItemKind.Episode],
- OrderBy = [(ItemSortBy.ParentIndexNumber, SortOrder.Ascending), (ItemSortBy.IndexNumber, SortOrder.Ascending)],
- Limit = 1,
- IsPlayed = includePlayed,
- IsVirtualItem = false,
- ParentIndexNumberNotEquals = 0,
- DtoOptions = dtoOptions
- };
-
- // Locate the next up episode based on the last watched episode's season and episode number
- var lastWatchedParentIndexNumber = lastWatchedEpisode?.ParentIndexNumber;
- var lastWatchedIndexNumber = lastWatchedEpisode?.IndexNumberEnd ?? lastWatchedEpisode?.IndexNumber;
- if (lastWatchedParentIndexNumber.HasValue && lastWatchedIndexNumber.HasValue)
+ if (!batchResult.TryGetValue(seriesKey, out var result))
{
- nextQuery.MinParentAndIndexNumber = (lastWatchedParentIndexNumber.Value, lastWatchedIndexNumber.Value + 1);
+ continue;
}
- var nextEpisode = _libraryManager.GetItemList(nextQuery).Cast<Episode>().FirstOrDefault();
+ var nextEpisode = DetermineNextEpisode(result, user, includeSpecials, request.EnableResumable, false);
- if (_configurationManager.Configuration.DisplaySpecialsWithinSeasons)
+ if (nextEpisode is not null)
{
- var consideredEpisodes = _libraryManager.GetItemList(new InternalItemsQuery(user)
- {
- AncestorWithPresentationUniqueKey = null,
- SeriesPresentationUniqueKey = seriesKey,
- ParentIndexNumber = 0,
- IncludeItemTypes = [BaseItemKind.Episode],
- IsPlayed = includePlayed,
- IsVirtualItem = false,
- DtoOptions = dtoOptions
- })
- .Cast<Episode>()
- .Where(episode => episode.AirsBeforeSeasonNumber is not null || episode.AirsAfterSeasonNumber is not null)
- .ToList();
+ // The last played date and the version that was actually played live on the version item's user data
+ // The played state propagated to the sibling versions carries no date
+ var (playedVersion, lastPlayedDate) = GetMostRecentlyPlayedVersion(result.LastWatched, user);
+ nextEpisode = GetPreferredVersion(nextEpisode, result.LastWatched, playedVersion);
- if (lastWatchedEpisode is not null)
+ DateTime lastWatchedDate = DateTime.MinValue;
+ if (result.LastWatched is not null)
{
- // Last watched episode is added, because there could be specials that aired before the last watched episode
- consideredEpisodes.Add(lastWatchedEpisode);
+ lastWatchedDate = lastPlayedDate ?? DateTime.MinValue.AddDays(1);
}
- if (nextEpisode is not null)
+ nextUpList.Add((lastWatchedDate, nextEpisode));
+ }
+
+ if (includeRewatching)
+ {
+ var nextPlayedEpisode = DetermineNextEpisodeForRewatching(result, user, includeSpecials);
+
+ if (nextPlayedEpisode is not null)
{
- consideredEpisodes.Add(nextEpisode);
+ var (playedVersion, lastPlayedDate) = GetMostRecentlyPlayedVersion(result.LastWatchedForRewatching, user);
+ nextPlayedEpisode = GetPreferredVersion(nextPlayedEpisode, result.LastWatchedForRewatching, playedVersion);
+
+ DateTime rewatchLastWatchedDate = DateTime.MinValue;
+ if (result.LastWatchedForRewatching is not null)
+ {
+ rewatchLastWatchedDate = lastPlayedDate ?? DateTime.MinValue.AddDays(1);
+ }
+
+ nextUpList.Add((rewatchLastWatchedDate, nextPlayedEpisode));
}
+ }
+ }
+
+ var sortedEpisodes = nextUpList
+ .OrderByDescending(x => x.LastWatchedDate)
+ .Select(x => (BaseItem)x.Episode);
+
+ return GetResult(sortedEpisodes, request);
+ }
+
+ private Episode? DetermineNextEpisode(
+ MediaBrowser.Controller.Persistence.NextUpEpisodeBatchResult result,
+ User user,
+ bool includeSpecials,
+ bool includeResumable,
+ bool includePlayed)
+ {
+ var nextEpisode = (includePlayed ? result.NextPlayedForRewatching : result.NextUp) as Episode;
+ var lastWatchedEpisode = (includePlayed ? result.LastWatchedForRewatching : result.LastWatched) as Episode;
- var sortedConsideredEpisodes = _libraryManager.Sort(consideredEpisodes, user, [(ItemSortBy.AiredEpisodeOrder, SortOrder.Ascending)])
+ if (includeSpecials && result.Specials?.Count > 0)
+ {
+ var consideredEpisodes = result.Specials
+ .Cast<Episode>()
+ .Where(episode => episode.AirsBeforeSeasonNumber is not null || episode.AirsAfterSeasonNumber is not null)
+ .ToList();
+
+ if (lastWatchedEpisode is not null)
+ {
+ consideredEpisodes.Add(lastWatchedEpisode);
+ }
+
+ if (nextEpisode is not null)
+ {
+ consideredEpisodes.Add(nextEpisode);
+ }
+
+ if (consideredEpisodes.Count > 0)
+ {
+ var sortedEpisodes = _libraryManager.Sort(consideredEpisodes, user, [(ItemSortBy.AiredEpisodeOrder, SortOrder.Ascending)])
.Cast<Episode>();
+
if (lastWatchedEpisode is not null)
{
- sortedConsideredEpisodes = sortedConsideredEpisodes.SkipWhile(episode => !episode.Id.Equals(lastWatchedEpisode.Id)).Skip(1);
+ sortedEpisodes = sortedEpisodes.SkipWhile(episode => !episode.Id.Equals(lastWatchedEpisode.Id)).Skip(1);
+ }
+
+ if (!includePlayed)
+ {
+ sortedEpisodes = sortedEpisodes.Where(episode => _userDataManager.GetUserData(user, episode) is not { Played: true });
}
- nextEpisode = sortedConsideredEpisodes.FirstOrDefault();
+ nextEpisode = sortedEpisodes.FirstOrDefault();
}
+ }
- if (nextEpisode is not null && !includeResumable)
+ if (nextEpisode is not null && !includeResumable)
+ {
+ // The resume progress may live on an alternate version
+ foreach (var version in nextEpisode.GetAllVersions())
{
- var userData = _userDataManager.GetUserData(user, nextEpisode);
-
- if (userData?.PlaybackPositionTicks > 0)
+ if (_userDataManager.GetUserData(user, version)?.PlaybackPositionTicks > 0)
{
return null;
}
}
+ }
- return nextEpisode;
+ return nextEpisode;
+ }
+
+ private Episode? DetermineNextEpisodeForRewatching(
+ MediaBrowser.Controller.Persistence.NextUpEpisodeBatchResult result,
+ User user,
+ bool includeSpecials)
+ {
+ return DetermineNextEpisode(result, user, includeSpecials, includeResumable: false, includePlayed: true);
+ }
+
+ /// <summary>
+ /// Gets the version of the last watched episode that was actually played, together with its last played date.
+ /// The version that was played carries the most recent LastPlayedDate.
+ /// dates.
+ /// </summary>
+ /// <param name="lastWatched">The last watched episode (any version).</param>
+ /// <param name="user">The user.</param>
+ /// <returns>The played version and its last played date.</returns>
+ private (Video? PlayedVersion, DateTime? LastPlayedDate) GetMostRecentlyPlayedVersion(BaseItem? lastWatched, User user)
+ {
+ if (lastWatched is not Video lastWatchedVideo)
+ {
+ return (null, null);
}
- if (lastWatchedEpisode is not null)
+ var versions = lastWatchedVideo.GetAllVersions();
+ var userDataByVersion = _userDataManager.GetUserDataBatch(versions, user);
+
+ var playedVersion = VersionPlaybackSelector.SelectMostRecentlyPlayed(
+ versions,
+ version => userDataByVersion.GetValueOrDefault(version.Id),
+ data => data.LastPlayedDate.HasValue);
+
+ return (playedVersion, playedVersion is null ? null : userDataByVersion[playedVersion.Id].LastPlayedDate);
+ }
+
+ /// <summary>
+ /// When the last watched episode was played as an alternate version, prefer the next episode's version with the matching name,
+ /// so Next Up continues in the version the user has been watching instead of falling back to the primary.
+ /// </summary>
+ /// <param name="nextEpisode">The determined next episode (a primary).</param>
+ /// <param name="lastWatched">The last watched episode.</param>
+ /// <param name="playedVersion">The version of the last watched episode that was played.</param>
+ /// <returns>The matching version of the next episode, or the episode itself.</returns>
+ private Episode GetPreferredVersion(Episode nextEpisode, BaseItem? lastWatched, Video? playedVersion)
+ {
+ // No version preference, or the primary was played
+ if (lastWatched is not Video lastWatchedVideo
+ || playedVersion is null
+ || !playedVersion.PrimaryVersionId.HasValue)
{
- var userData = _userDataManager.GetUserData(user, lastWatchedEpisode);
+ return nextEpisode;
+ }
- if (userData is null)
- {
- return (DateTime.MinValue, GetEpisode);
- }
+ // Match by version name
+ var playedVersionId = playedVersion.Id.ToString("N", CultureInfo.InvariantCulture);
+ var playedVersionName = lastWatchedVideo.GetMediaSources(false)
+ .FirstOrDefault(source => string.Equals(source.Id, playedVersionId, StringComparison.OrdinalIgnoreCase))?.Name;
- var lastWatchedDate = userData.LastPlayedDate ?? DateTime.MinValue.AddDays(1);
+ if (string.IsNullOrEmpty(playedVersionName))
+ {
+ return nextEpisode;
+ }
- return (lastWatchedDate, GetEpisode);
+ var matchingSource = nextEpisode.GetMediaSources(false)
+ .FirstOrDefault(source => string.Equals(source.Name, playedVersionName, StringComparison.OrdinalIgnoreCase));
+
+ if (matchingSource is not null
+ && Guid.TryParse(matchingSource.Id, out var matchingId)
+ && !matchingId.Equals(nextEpisode.Id)
+ && _libraryManager.GetItemById<Episode>(matchingId) is { } matchingVersion)
+ {
+ return matchingVersion;
}
- // Return the first episode
- return (DateTime.MinValue, GetEpisode);
+ return nextEpisode;
+ }
+
+ private static string GetUniqueSeriesKey(Series series)
+ {
+ return series.GetPresentationUniqueKey();
}
private static QueryResult<BaseItem> GetResult(IEnumerable<BaseItem> items, NextUpQuery query)
diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs
index 67b77a112d..6a60f7f5f6 100644
--- a/Emby.Server.Implementations/Updates/InstallationManager.cs
+++ b/Emby.Server.Implementations/Updates/InstallationManager.cs
@@ -1,4 +1,5 @@
using System;
+using System.Buffers;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
@@ -32,6 +33,8 @@ namespace Emby.Server.Implementations.Updates
/// </summary>
public class InstallationManager : IInstallationManager
{
+ private static readonly SearchValues<char> InvalidPackageNameChars = SearchValues.Create([.. Path.GetInvalidFileNameChars(), '/', '\\']);
+
/// <summary>
/// The logger.
/// </summary>
@@ -521,48 +524,68 @@ namespace Emby.Server.Implementations.Updates
return;
}
+ if (!IsValidPackageDirectoryName(package.Name))
+ {
+ _logger.LogError("Refusing to install package with invalid name {PackageName}.", package.Name);
+ throw new InvalidDataException($"Plugin package name '{package.Name}' is not a valid directory name.");
+ }
+
// Always override the passed-in target (which is a file) and figure it out again
string targetDir = Path.Combine(_appPaths.PluginsPath, package.Name);
- using var response = await _httpClientFactory.CreateClient(NamedClient.Default)
- .GetAsync(new Uri(package.SourceUrl), cancellationToken).ConfigureAwait(false);
- response.EnsureSuccessStatusCode();
- await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
-
- // CA5351: Do Not Use Broken Cryptographic Algorithms
-#pragma warning disable CA5351
- cancellationToken.ThrowIfCancellationRequested();
-
- var hash = Convert.ToHexString(await MD5.HashDataAsync(stream, cancellationToken).ConfigureAwait(false));
- if (!string.Equals(package.Checksum, hash, StringComparison.OrdinalIgnoreCase))
+ var pluginsRoot = Path.TrimEndingDirectorySeparator(Path.GetFullPath(_appPaths.PluginsPath));
+ var resolvedTarget = Path.GetFullPath(targetDir);
+ if (!resolvedTarget.StartsWith(pluginsRoot + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase))
{
_logger.LogError(
- "The checksums didn't match while installing {Package}, expected: {Expected}, got: {Received}",
+ "Refusing to install package {PackageName}: resolved target {Resolved} is outside plugins directory {Root}.",
package.Name,
- package.Checksum,
- hash);
- throw new InvalidDataException("The checksum of the received data doesn't match.");
+ resolvedTarget,
+ pluginsRoot);
+ throw new InvalidDataException($"Plugin package name '{package.Name}' resolves outside the plugins directory.");
}
- // Version folder as they cannot be overwritten in Windows.
- targetDir += "_" + package.Version;
-
- if (Directory.Exists(targetDir))
+ using var response = await _httpClientFactory.CreateClient(NamedClient.Default)
+ .GetAsync(new Uri(package.SourceUrl), cancellationToken).ConfigureAwait(false);
+ response.EnsureSuccessStatusCode();
+ Stream stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
+ await using (stream.ConfigureAwait(false))
{
- try
+ // CA5351: Do Not Use Broken Cryptographic Algorithms
+#pragma warning disable CA5351
+ cancellationToken.ThrowIfCancellationRequested();
+
+ var hash = Convert.ToHexString(await MD5.HashDataAsync(stream, cancellationToken).ConfigureAwait(false));
+ if (!string.Equals(package.Checksum, hash, StringComparison.OrdinalIgnoreCase))
{
- Directory.Delete(targetDir, true);
+ _logger.LogError(
+ "The checksums didn't match while installing {Package}, expected: {Expected}, got: {Received}",
+ package.Name,
+ package.Checksum,
+ hash);
+ throw new InvalidDataException("The checksum of the received data doesn't match.");
}
+
+ // Version folder as they cannot be overwritten in Windows.
+ targetDir += "_" + package.Version;
+
+ if (Directory.Exists(targetDir))
+ {
+ try
+ {
+ Directory.Delete(targetDir, true);
+ }
#pragma warning disable CA1031 // Do not catch general exception types
- catch
+ catch
#pragma warning restore CA1031 // Do not catch general exception types
- {
- // Ignore any exceptions.
+ {
+ // Ignore any exceptions.
+ }
}
- }
- stream.Position = 0;
- await ZipFile.ExtractToDirectoryAsync(stream, targetDir, true, cancellationToken);
+ stream.Position = 0;
+ await ZipFile.ExtractToDirectoryAsync(stream, targetDir, true, cancellationToken).ConfigureAwait(false);
+ }
// Ensure we create one or populate existing ones with missing data.
await _pluginManager.PopulateManifest(package.PackageInfo, package.Version, targetDir, status).ConfigureAwait(false);
@@ -570,6 +593,26 @@ namespace Emby.Server.Implementations.Updates
_pluginManager.ImportPluginFrom(targetDir);
}
+ private static bool IsValidPackageDirectoryName(string? name)
+ {
+ if (string.IsNullOrWhiteSpace(name))
+ {
+ return false;
+ }
+
+ if (name.Equals(".", StringComparison.Ordinal) || name.Equals("..", StringComparison.Ordinal))
+ {
+ return false;
+ }
+
+ if (name.IndexOfAny(InvalidPackageNameChars) >= 0)
+ {
+ return false;
+ }
+
+ return true;
+ }
+
private async Task<bool> InstallPackageInternal(InstallationInfo package, CancellationToken cancellationToken)
{
LocalPlugin? plugin = _pluginManager.Plugins.FirstOrDefault(p => p.Id.Equals(package.Id) && p.Version.Equals(package.Version))