mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-05-01 14:26:40 +01:00
Merge remote-tracking branch 'upstream/master' into network-rewrite
This commit is contained in:
@@ -18,7 +18,6 @@ using System.Threading.Tasks;
|
||||
using Emby.Dlna;
|
||||
using Emby.Dlna.Main;
|
||||
using Emby.Dlna.Ssdp;
|
||||
using Emby.Drawing;
|
||||
using Emby.Naming.Common;
|
||||
using Emby.Notifications;
|
||||
using Emby.Photos;
|
||||
@@ -45,6 +44,7 @@ using Emby.Server.Implementations.SyncPlay;
|
||||
using Emby.Server.Implementations.TV;
|
||||
using Emby.Server.Implementations.Updates;
|
||||
using Jellyfin.Api.Helpers;
|
||||
using Jellyfin.Drawing;
|
||||
using Jellyfin.MediaEncoding.Hls.Playlist;
|
||||
using Jellyfin.Networking.Configuration;
|
||||
using Jellyfin.Networking.Manager;
|
||||
@@ -193,11 +193,6 @@ namespace Emby.Server.Implementations
|
||||
/// </summary>
|
||||
private string PublishedServerUrl => _startupConfig[AddressOverrideKey];
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance can self restart.
|
||||
/// </summary>
|
||||
public bool CanSelfRestart => _startupOptions.RestartPath is not null;
|
||||
|
||||
public bool CoreStartupHasCompleted { get; private set; }
|
||||
|
||||
public virtual bool CanLaunchWebBrowser
|
||||
@@ -654,7 +649,7 @@ namespace Emby.Server.Implementations
|
||||
/// <returns>A task representing the service initialization operation.</returns>
|
||||
public async Task InitializeServices()
|
||||
{
|
||||
var jellyfinDb = await Resolve<IDbContextFactory<JellyfinDb>>().CreateDbContextAsync().ConfigureAwait(false);
|
||||
var jellyfinDb = await Resolve<IDbContextFactory<JellyfinDbContext>>().CreateDbContextAsync().ConfigureAwait(false);
|
||||
await using (jellyfinDb.ConfigureAwait(false))
|
||||
{
|
||||
if ((await jellyfinDb.Database.GetPendingMigrationsAsync().ConfigureAwait(false)).Any())
|
||||
@@ -935,17 +930,13 @@ namespace Emby.Server.Implementations
|
||||
/// </summary>
|
||||
public void Restart()
|
||||
{
|
||||
if (!CanSelfRestart)
|
||||
{
|
||||
throw new PlatformNotSupportedException("The server is unable to self-restart. Please restart manually.");
|
||||
}
|
||||
|
||||
if (IsShuttingDown)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
IsShuttingDown = true;
|
||||
_pluginManager.UnloadAssemblies();
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
@@ -1047,7 +1038,6 @@ namespace Emby.Server.Implementations
|
||||
CachePath = ApplicationPaths.CachePath,
|
||||
OperatingSystem = MediaBrowser.Common.System.OperatingSystem.Id.ToString(),
|
||||
OperatingSystemDisplayName = MediaBrowser.Common.System.OperatingSystem.Name,
|
||||
CanSelfRestart = CanSelfRestart,
|
||||
CanLaunchWebBrowser = CanLaunchWebBrowser,
|
||||
TranscodingTempPath = ConfigurationManager.GetTranscodePath(),
|
||||
ServerName = FriendlyName,
|
||||
|
||||
@@ -60,11 +60,22 @@ namespace Emby.Server.Implementations.Data
|
||||
/// <value>The cache size or null.</value>
|
||||
protected virtual int? CacheSize => null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the locking mode. <see href="https://www.sqlite.org/pragma.html#pragma_locking_mode" />.
|
||||
/// </summary>
|
||||
protected virtual string LockingMode => "EXCLUSIVE";
|
||||
|
||||
/// <summary>
|
||||
/// Gets the journal mode. <see href="https://www.sqlite.org/pragma.html#pragma_journal_mode" />.
|
||||
/// </summary>
|
||||
/// <value>The journal mode.</value>
|
||||
protected virtual string JournalMode => "TRUNCATE";
|
||||
protected virtual string JournalMode => "WAL";
|
||||
|
||||
/// <summary>
|
||||
/// Gets the journal size limit. <see href="https://www.sqlite.org/pragma.html#pragma_journal_size_limit" />.
|
||||
/// </summary>
|
||||
/// <value>The journal size limit.</value>
|
||||
protected virtual int? JournalSizeLimit => 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the page size.
|
||||
@@ -84,7 +95,7 @@ namespace Emby.Server.Implementations.Data
|
||||
/// </summary>
|
||||
/// <value>The synchronous mode or null.</value>
|
||||
/// <see cref="SynchronousMode"/>
|
||||
protected virtual SynchronousMode? Synchronous => null;
|
||||
protected virtual SynchronousMode? Synchronous => SynchronousMode.Normal;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the write lock.
|
||||
@@ -116,11 +127,21 @@ namespace Emby.Server.Implementations.Data
|
||||
WriteConnection.Execute("PRAGMA cache_size=" + CacheSize.Value);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(LockingMode))
|
||||
{
|
||||
WriteConnection.Execute("PRAGMA locking_mode=" + LockingMode);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(JournalMode))
|
||||
{
|
||||
WriteConnection.Execute("PRAGMA journal_mode=" + JournalMode);
|
||||
}
|
||||
|
||||
if (JournalSizeLimit.HasValue)
|
||||
{
|
||||
WriteConnection.Execute("PRAGMA journal_size_limit=" + (int)JournalSizeLimit.Value);
|
||||
}
|
||||
|
||||
if (Synchronous.HasValue)
|
||||
{
|
||||
WriteConnection.Execute("PRAGMA synchronous=" + (int)Synchronous.Value);
|
||||
|
||||
@@ -359,8 +359,6 @@ namespace Emby.Server.Implementations.Data
|
||||
|
||||
string[] queries =
|
||||
{
|
||||
"PRAGMA locking_mode=EXCLUSIVE",
|
||||
|
||||
"create table if not exists TypedBaseItems (guid GUID primary key NOT NULL, type TEXT NOT NULL, data BLOB NULL, ParentId GUID NULL, Path TEXT NULL)",
|
||||
|
||||
"create table if not exists AncestorIds (ItemId GUID NOT NULL, AncestorId GUID NOT NULL, AncestorIdText TEXT NOT NULL, PRIMARY KEY (ItemId, AncestorId))",
|
||||
@@ -385,39 +383,6 @@ namespace Emby.Server.Implementations.Data
|
||||
|
||||
string[] postQueries =
|
||||
{
|
||||
// obsolete
|
||||
"drop index if exists idx_TypedBaseItems",
|
||||
"drop index if exists idx_mediastreams",
|
||||
"drop index if exists idx_mediastreams1",
|
||||
"drop index if exists idx_" + ChaptersTableName,
|
||||
"drop index if exists idx_UserDataKeys1",
|
||||
"drop index if exists idx_UserDataKeys2",
|
||||
"drop index if exists idx_TypeTopParentId3",
|
||||
"drop index if exists idx_TypeTopParentId2",
|
||||
"drop index if exists idx_TypeTopParentId4",
|
||||
"drop index if exists idx_Type",
|
||||
"drop index if exists idx_TypeTopParentId",
|
||||
"drop index if exists idx_GuidType",
|
||||
"drop index if exists idx_TopParentId",
|
||||
"drop index if exists idx_TypeTopParentId6",
|
||||
"drop index if exists idx_ItemValues2",
|
||||
"drop index if exists Idx_ProviderIds",
|
||||
"drop index if exists idx_ItemValues3",
|
||||
"drop index if exists idx_ItemValues4",
|
||||
"drop index if exists idx_ItemValues5",
|
||||
"drop index if exists idx_UserDataKeys3",
|
||||
"drop table if exists UserDataKeys",
|
||||
"drop table if exists ProviderIds",
|
||||
"drop index if exists Idx_ProviderIds1",
|
||||
"drop table if exists Images",
|
||||
"drop index if exists idx_Images",
|
||||
"drop index if exists idx_TypeSeriesPresentationUniqueKey",
|
||||
"drop index if exists idx_SeriesPresentationUniqueKey",
|
||||
"drop index if exists idx_TypeSeriesPresentationUniqueKey2",
|
||||
"drop index if exists idx_AncestorIds3",
|
||||
"drop index if exists idx_AncestorIds4",
|
||||
"drop index if exists idx_AncestorIds2",
|
||||
|
||||
"create index if not exists idx_PathTypedBaseItems on TypedBaseItems(Path)",
|
||||
"create index if not exists idx_ParentIdTypedBaseItems on TypedBaseItems(ParentId)",
|
||||
|
||||
@@ -458,6 +423,9 @@ namespace Emby.Server.Implementations.Data
|
||||
|
||||
// Used to update inherited tags
|
||||
"create index if not exists idx_ItemValues8 on ItemValues(Type, ItemId, Value)",
|
||||
|
||||
"CREATE INDEX IF NOT EXISTS idx_TypedBaseItemsUserDataKeyType ON TypedBaseItems(UserDataKey, Type)",
|
||||
"CREATE INDEX IF NOT EXISTS idx_PeopleNameListOrder ON People(Name, ListOrder)"
|
||||
};
|
||||
|
||||
using (var connection = GetConnection())
|
||||
@@ -2401,13 +2369,17 @@ namespace Emby.Server.Implementations.Data
|
||||
var builder = new StringBuilder();
|
||||
builder.Append('(');
|
||||
|
||||
if (string.IsNullOrEmpty(item.OfficialRating))
|
||||
if (item.InheritedParentalRatingValue == 0)
|
||||
{
|
||||
builder.Append("(OfficialRating is null * 10)");
|
||||
builder.Append("((InheritedParentalRatingValue=0) * 10)");
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.Append("(OfficialRating=@ItemOfficialRating * 10)");
|
||||
builder.Append(
|
||||
@"(SELECT CASE WHEN InheritedParentalRatingValue=0
|
||||
THEN 0
|
||||
ELSE 10.0 / (1.0 + ABS(InheritedParentalRatingValue - @InheritedParentalRatingValue))
|
||||
END)");
|
||||
}
|
||||
|
||||
if (item.ProductionYear.HasValue)
|
||||
@@ -2521,6 +2493,11 @@ namespace Emby.Server.Implementations.Data
|
||||
{
|
||||
statement.TryBind("@SimilarItemId", item.Id);
|
||||
}
|
||||
|
||||
if (commandText.Contains("@InheritedParentalRatingValue", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
statement.TryBind("@InheritedParentalRatingValue", item.InheritedParentalRatingValue);
|
||||
}
|
||||
}
|
||||
|
||||
private string GetJoinUserDataText(InternalItemsQuery query)
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<ProjectReference Include="..\Emby.Dlna\Emby.Dlna.csproj" />
|
||||
<ProjectReference Include="..\MediaBrowser.LocalMetadata\MediaBrowser.LocalMetadata.csproj" />
|
||||
<ProjectReference Include="..\Emby.Photos\Emby.Photos.csproj" />
|
||||
<ProjectReference Include="..\Emby.Drawing\Emby.Drawing.csproj" />
|
||||
<ProjectReference Include="..\src\Jellyfin.Drawing\Jellyfin.Drawing.csproj" />
|
||||
<ProjectReference Include="..\MediaBrowser.MediaEncoding\MediaBrowser.MediaEncoding.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="7.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="7.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="7.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="7.0.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="7.0.2" />
|
||||
<PackageReference Include="Mono.Nat" Version="3.0.4" />
|
||||
<PackageReference Include="prometheus-net.DotNetRuntime" Version="4.4.0" />
|
||||
<PackageReference Include="SQLitePCL.pretty.netstandard" Version="3.1.0" />
|
||||
@@ -54,7 +54,7 @@
|
||||
|
||||
<!-- Code Analyzers-->
|
||||
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.3">
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
@@ -20,16 +20,6 @@ namespace Emby.Server.Implementations
|
||||
/// </summary>
|
||||
string? PackageName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the --restartpath command line option.
|
||||
/// </summary>
|
||||
string? RestartPath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the --restartargs command line option.
|
||||
/// </summary>
|
||||
string? RestartArgs { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the --published-server-url command line option.
|
||||
/// </summary>
|
||||
|
||||
@@ -89,17 +89,7 @@ namespace Emby.Server.Implementations.Library
|
||||
// Give some preference to external text subs for better performance
|
||||
return streams
|
||||
.Where(i => i.Type == type)
|
||||
.OrderBy(i =>
|
||||
{
|
||||
var index = languagePreferences.FindIndex(x => string.Equals(x, i.Language, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
return index == -1 ? 100 : index;
|
||||
})
|
||||
.ThenBy(i => GetBooleanOrderBy(i.IsDefault))
|
||||
.ThenBy(i => GetBooleanOrderBy(i.SupportsExternalStream))
|
||||
.ThenBy(i => GetBooleanOrderBy(i.IsTextSubtitleStream))
|
||||
.ThenBy(i => GetBooleanOrderBy(i.IsExternal))
|
||||
.ThenBy(i => i.Index);
|
||||
.OrderByDescending(i => GetStreamScore(i, languagePreferences));
|
||||
}
|
||||
|
||||
public static void SetSubtitleStreamScores(
|
||||
@@ -113,9 +103,9 @@ namespace Emby.Server.Implementations.Library
|
||||
return;
|
||||
}
|
||||
|
||||
var sortedStreams = GetSortedStreams(streams, MediaStreamType.Subtitle, preferredLanguages);
|
||||
var sortedStreams = GetSortedStreams(streams, MediaStreamType.Subtitle, preferredLanguages).ToList();
|
||||
|
||||
var filteredStreams = new List<MediaStream>();
|
||||
List<MediaStream>? filteredStreams = null;
|
||||
|
||||
if (mode == SubtitlePlaybackMode.Default)
|
||||
{
|
||||
@@ -144,46 +134,26 @@ namespace Emby.Server.Implementations.Library
|
||||
}
|
||||
|
||||
// load forced subs if we have found no suitable full subtitles
|
||||
var iterStreams = filteredStreams.Count == 0
|
||||
var iterStreams = filteredStreams is null || filteredStreams.Count == 0
|
||||
? sortedStreams.Where(s => s.IsForced && string.Equals(s.Language, audioTrackLanguage, StringComparison.OrdinalIgnoreCase))
|
||||
: filteredStreams;
|
||||
|
||||
foreach (var stream in iterStreams)
|
||||
{
|
||||
stream.Score = GetSubtitleScore(stream, preferredLanguages);
|
||||
stream.Score = GetStreamScore(stream, preferredLanguages);
|
||||
}
|
||||
}
|
||||
|
||||
private static int GetSubtitleScore(MediaStream stream, IReadOnlyList<string> languagePreferences)
|
||||
internal static int GetStreamScore(MediaStream stream, IReadOnlyList<string> languagePreferences)
|
||||
{
|
||||
var values = new List<int>();
|
||||
|
||||
var index = languagePreferences.FindIndex(x => string.Equals(x, stream.Language, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
values.Add(index == -1 ? 0 : 100 - index);
|
||||
|
||||
values.Add(stream.IsForced ? 1 : 0);
|
||||
values.Add(stream.IsDefault ? 1 : 0);
|
||||
values.Add(stream.SupportsExternalStream ? 1 : 0);
|
||||
values.Add(stream.IsTextSubtitleStream ? 1 : 0);
|
||||
values.Add(stream.IsExternal ? 1 : 0);
|
||||
|
||||
values.Reverse();
|
||||
var scale = 1;
|
||||
var score = 0;
|
||||
|
||||
foreach (var value in values)
|
||||
{
|
||||
score += scale * (value + 1);
|
||||
scale *= 10;
|
||||
}
|
||||
|
||||
var score = index == -1 ? 1 : 101 - index;
|
||||
score = (score * 10) + (stream.IsForced ? 2 : 1);
|
||||
score = (score * 10) + (stream.IsDefault ? 2 : 1);
|
||||
score = (score * 10) + (stream.SupportsExternalStream ? 2 : 1);
|
||||
score = (score * 10) + (stream.IsTextSubtitleStream ? 2 : 1);
|
||||
score = (score * 10) + (stream.IsExternal ? 2 : 1);
|
||||
return score;
|
||||
}
|
||||
|
||||
private static int GetBooleanOrderBy(bool value)
|
||||
{
|
||||
return value ? 0 : 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1814,21 +1814,29 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||
program.AddGenre("News");
|
||||
}
|
||||
|
||||
if (timer.IsProgramSeries)
|
||||
var config = GetConfiguration();
|
||||
|
||||
if (config.SaveRecordingNFO)
|
||||
{
|
||||
await SaveSeriesNfoAsync(timer, seriesPath).ConfigureAwait(false);
|
||||
await SaveVideoNfoAsync(timer, recordingPath, program, false).ConfigureAwait(false);
|
||||
}
|
||||
else if (!timer.IsMovie || timer.IsSports || timer.IsNews)
|
||||
{
|
||||
await SaveVideoNfoAsync(timer, recordingPath, program, true).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await SaveVideoNfoAsync(timer, recordingPath, program, false).ConfigureAwait(false);
|
||||
if (timer.IsProgramSeries)
|
||||
{
|
||||
await SaveSeriesNfoAsync(timer, seriesPath).ConfigureAwait(false);
|
||||
await SaveVideoNfoAsync(timer, recordingPath, program, false).ConfigureAwait(false);
|
||||
}
|
||||
else if (!timer.IsMovie || timer.IsSports || timer.IsNews)
|
||||
{
|
||||
await SaveVideoNfoAsync(timer, recordingPath, program, true).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await SaveVideoNfoAsync(timer, recordingPath, program, false).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
await SaveRecordingImages(recordingPath, program).ConfigureAwait(false);
|
||||
if (config.SaveRecordingImages)
|
||||
{
|
||||
await SaveRecordingImages(recordingPath, program).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -122,5 +122,6 @@
|
||||
"TaskOptimizeDatabase": "Optimaliseer databasis",
|
||||
"TaskKeyframeExtractorDescription": "Haal keyframes vanuit video lêers om meer presiese HLS afspeellyste te maak. Dit kan lank duur.",
|
||||
"TaskKeyframeExtractor": "Keyframe Ekstraktor",
|
||||
"External": "Ekstern"
|
||||
"External": "Ekstern",
|
||||
"HearingImpaired": "gehoorgestremd"
|
||||
}
|
||||
|
||||
@@ -3,20 +3,20 @@
|
||||
"AppDeviceValues": "تطبيق: {0}, جهاز: {1}",
|
||||
"Application": "تطبيق",
|
||||
"Artists": "الفنانين",
|
||||
"AuthenticationSucceededWithUserName": "تمت مصادقة {0} بنجاح",
|
||||
"AuthenticationSucceededWithUserName": "نجحت عملية التوثيق بـ {0}",
|
||||
"Books": "الكتب",
|
||||
"CameraImageUploadedFrom": "صورة كاميرا جديدة تم رفعها من {0}",
|
||||
"CameraImageUploadedFrom": "رُفعت صورة الكاميرا الجديدة من {0}",
|
||||
"Channels": "القنوات",
|
||||
"ChapterNameValue": "الفصل {0}",
|
||||
"Collections": "التجميعات",
|
||||
"DeviceOfflineWithName": "قُطِع الاتصال ب{0}",
|
||||
"DeviceOnlineWithName": "{0} متصل",
|
||||
"FailedLoginAttemptWithUserName": "محاولة تسجيل الدخول فشلت من {0}",
|
||||
"Favorites": "مفضلات",
|
||||
"Favorites": "المفضلة",
|
||||
"Folders": "المجلدات",
|
||||
"Genres": "التصنيفات",
|
||||
"HeaderAlbumArtists": "فناني الألبوم",
|
||||
"HeaderContinueWatching": "استمر بالمشاهدة",
|
||||
"HeaderContinueWatching": "استئناف المشاهدة",
|
||||
"HeaderFavoriteAlbums": "الألبومات المفضلة",
|
||||
"HeaderFavoriteArtists": "الفنانون المفضلون",
|
||||
"HeaderFavoriteEpisodes": "الحلقات المفضلة",
|
||||
@@ -27,15 +27,15 @@
|
||||
"HeaderRecordingGroups": "مجموعات التسجيل",
|
||||
"HomeVideos": "الفيديوهات الشخصية",
|
||||
"Inherit": "توريث",
|
||||
"ItemAddedWithName": "تم إضافة {0} للمكتبة",
|
||||
"ItemRemovedWithName": "تم إزالة {0} من المكتبة",
|
||||
"ItemAddedWithName": "أُضيف {0} للمكتبة",
|
||||
"ItemRemovedWithName": "أُزيل {0} من المكتبة",
|
||||
"LabelIpAddressValue": "عنوان الآي بي: {0}",
|
||||
"LabelRunningTimeValue": "مدة التشغيل: {0}",
|
||||
"Latest": "أحدث",
|
||||
"MessageApplicationUpdated": "لقد تم تحديث خادم Jellyfin",
|
||||
"MessageApplicationUpdatedTo": "تم تحديث خادم Jellyfin الى {0}",
|
||||
"MessageNamedServerConfigurationUpdatedWithValue": "تم تحديث إعدادات الخادم في قسم {0}",
|
||||
"MessageServerConfigurationUpdated": "تم تحديث إعدادات الخادم",
|
||||
"MessageApplicationUpdated": "حُدث خادم Jellyfin",
|
||||
"MessageApplicationUpdatedTo": "حُدث خادم Jellyfin إلى {0}",
|
||||
"MessageNamedServerConfigurationUpdatedWithValue": "حُدثت إعدادات الخادم في قسم {0}",
|
||||
"MessageServerConfigurationUpdated": "حُدثت إعدادات الخادم",
|
||||
"MixedContent": "محتوى مختلط",
|
||||
"Movies": "الأفلام",
|
||||
"Music": "الموسيقى",
|
||||
@@ -45,14 +45,14 @@
|
||||
"NameSeasonUnknown": "الموسم غير معروف",
|
||||
"NewVersionIsAvailable": "نسخة جديدة من خادم Jellyfin متوفرة للتحميل.",
|
||||
"NotificationOptionApplicationUpdateAvailable": "يوجد تحديث للتطبيق",
|
||||
"NotificationOptionApplicationUpdateInstalled": "تم تحديث التطبيق",
|
||||
"NotificationOptionApplicationUpdateInstalled": "نُصب تحديث التطبيق",
|
||||
"NotificationOptionAudioPlayback": "بدأ تشغيل المقطع الصوتي",
|
||||
"NotificationOptionAudioPlaybackStopped": "تم إيقاف تشغيل المقطع الصوتي",
|
||||
"NotificationOptionCameraImageUploaded": "تم رفع صورة الكاميرا",
|
||||
"NotificationOptionAudioPlaybackStopped": "أُوقف تشغيل المقطع الصوتي",
|
||||
"NotificationOptionCameraImageUploaded": "رُفعت صورة الكاميرا",
|
||||
"NotificationOptionInstallationFailed": "فشل في التثبيت",
|
||||
"NotificationOptionNewLibraryContent": "تم إضافة محتوى جديد",
|
||||
"NotificationOptionNewLibraryContent": "أُضيف محتوى جديدا",
|
||||
"NotificationOptionPluginError": "فشل في الملحق",
|
||||
"NotificationOptionPluginInstalled": "تم تثبيت الملحق",
|
||||
"NotificationOptionPluginInstalled": "ثُبتت المكونات الإضافية",
|
||||
"NotificationOptionPluginUninstalled": "تمت إزالة الملحق",
|
||||
"NotificationOptionPluginUpdateInstalled": "تم تثبيت تحديثات الملحق",
|
||||
"NotificationOptionServerRestartRequired": "يجب إعادة تشغيل الخادم",
|
||||
@@ -91,13 +91,13 @@
|
||||
"UserStoppedPlayingItemWithValues": "قام {0} بإيقاف تشغيل {1} على {2}",
|
||||
"ValueHasBeenAddedToLibrary": "تمت اضافت {0} إلى مكتبة الوسائط",
|
||||
"ValueSpecialEpisodeName": "حلقه خاصه - {0}",
|
||||
"VersionNumber": "النسخة {0}",
|
||||
"VersionNumber": "الإصدار {0}",
|
||||
"TaskCleanCacheDescription": "يحذف الملفات المؤقتة التي لم يعد النظام بحاجة إليها.",
|
||||
"TaskCleanCache": "احذف ما بمجلد الملفات المؤقتة",
|
||||
"TasksChannelsCategory": "قنوات الإنترنت",
|
||||
"TasksLibraryCategory": "مكتبة",
|
||||
"TasksMaintenanceCategory": "صيانة",
|
||||
"TaskRefreshLibraryDescription": "يفحص مكتبة الوسائط الخاصة بك باحثا عن ملفات جديدة، ومن ثم يتحدث البيانات الوصفية.",
|
||||
"TaskRefreshLibraryDescription": "يفحص مكتبة الوسائط الخاصة بك باحثا عن ملفات جديدة، ومن ثم يُحدث البيانات الوصفية.",
|
||||
"TaskRefreshLibrary": "افحص مكتبة الوسائط",
|
||||
"TaskRefreshChapterImagesDescription": "يُنشئ صور مصغرة لمقاطع الفيديو التي تحتوي على فصول.",
|
||||
"TaskRefreshChapterImages": "استخراج صور الفصل",
|
||||
@@ -123,5 +123,6 @@
|
||||
"TaskOptimizeDatabase": "تحسين قاعدة البيانات",
|
||||
"TaskKeyframeExtractorDescription": "يستخرج الإطارات الرئيسية من ملفات الفيديو لكي ينشئ قوائم تشغيل بث HTTP المباشر. قد تستمر هذه العملية لوقت طويل.",
|
||||
"TaskKeyframeExtractor": "مستخرج الإطار الرئيسي",
|
||||
"External": "خارجي"
|
||||
"External": "خارجي",
|
||||
"HearingImpaired": "ضعاف السمع"
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
"Movies": "Pel·lícules",
|
||||
"Music": "Música",
|
||||
"MusicVideos": "Vídeos Musicals",
|
||||
"NameInstallFailed": "Instal·lació de {0} fallida",
|
||||
"NameInstallFailed": "{0} instal·lació fallida",
|
||||
"NameSeasonNumber": "Temporada {0}",
|
||||
"NameSeasonUnknown": "Temporada Desconeguda",
|
||||
"NewVersionIsAvailable": "Una nova versió del Servidor Jellyfin està disponible per descarregar.",
|
||||
@@ -118,7 +118,7 @@
|
||||
"TaskCleanActivityLog": "Buidar Registre d'Activitat",
|
||||
"Undefined": "Indefinit",
|
||||
"Forced": "Forçat",
|
||||
"Default": "Defecte",
|
||||
"Default": "Per defecte",
|
||||
"TaskOptimizeDatabaseDescription": "Compacta la base de dades i trunca l'espai lliure. Executar aquesta tasca després d’escanejar la biblioteca o fer altres canvis que impliquin modificacions a la base de dades pot millorar el rendiment.",
|
||||
"TaskOptimizeDatabase": "Optimitzar la base de dades",
|
||||
"TaskKeyframeExtractorDescription": "Extreu fotogrames clau dels fitxers de vídeo per crear llistes de reproducció HLS més precises. Aquesta tasca pot durar molt de temps.",
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
"Favorites": "Αγαπημένα",
|
||||
"Folders": "Φάκελοι",
|
||||
"Genres": "Είδη",
|
||||
"HeaderAlbumArtists": "Δισκογραφικοί καλλιτέχνες",
|
||||
"HeaderAlbumArtists": "Καλλιτέχνες άλμπουμ",
|
||||
"HeaderContinueWatching": "Συνεχίστε την παρακολούθηση",
|
||||
"HeaderFavoriteAlbums": "Αγαπημένα Άλμπουμ",
|
||||
"HeaderFavoriteArtists": "Αγαπημένοι Καλλιτέχνες",
|
||||
@@ -24,8 +24,8 @@
|
||||
"HeaderFavoriteSongs": "Αγαπημένα Τραγούδια",
|
||||
"HeaderLiveTV": "Ζωντανή Τηλεόραση",
|
||||
"HeaderNextUp": "Επόμενο",
|
||||
"HeaderRecordingGroups": "Μουσικά Συγκροτήματα",
|
||||
"HomeVideos": "Προσωπικά βίντεο",
|
||||
"HeaderRecordingGroups": "Ομάδες Ηχογράφησης",
|
||||
"HomeVideos": "Προσωπικά Βίντεο",
|
||||
"Inherit": "Κληρονόμηση",
|
||||
"ItemAddedWithName": "{0} προστέθηκε στη βιβλιοθήκη",
|
||||
"ItemRemovedWithName": "{0} διαγράφηκε από τη βιβλιοθήκη",
|
||||
@@ -51,10 +51,10 @@
|
||||
"NotificationOptionCameraImageUploaded": "Μεταφορτώθηκε φωτογραφία απο κάμερα",
|
||||
"NotificationOptionInstallationFailed": "Αποτυχία εγκατάστασης",
|
||||
"NotificationOptionNewLibraryContent": "Προστέθηκε νέο περιεχόμενο",
|
||||
"NotificationOptionPluginError": "Αποτυχία του plugin",
|
||||
"NotificationOptionPluginInstalled": "Το plugin εγκαταστάθηκε",
|
||||
"NotificationOptionPluginUninstalled": "Το plugin απεγκαταστάθηκε",
|
||||
"NotificationOptionPluginUpdateInstalled": "Η αναβάθμιση του plugin εγκαταστάθηκε",
|
||||
"NotificationOptionPluginError": "Αποτυχία του πρόσθετου",
|
||||
"NotificationOptionPluginInstalled": "Το πρόσθετο εγκαταστάθηκε",
|
||||
"NotificationOptionPluginUninstalled": "Το πρόσθετο απεγκαταστάθηκε",
|
||||
"NotificationOptionPluginUpdateInstalled": "Η αναβάθμιση του πρόσθετου εγκαταστάθηκε",
|
||||
"NotificationOptionServerRestartRequired": "Ο διακομιστής χρειάζεται επανεκκίνηση",
|
||||
"NotificationOptionTaskFailed": "Αποτυχία προγραμματισμένης εργασίας",
|
||||
"NotificationOptionUserLockedOut": "Ο χρήστης αποκλείστηκε",
|
||||
@@ -66,7 +66,7 @@
|
||||
"PluginInstalledWithName": "{0} εγκαταστήθηκε",
|
||||
"PluginUninstalledWithName": "{0} έχει απεγκατασταθεί",
|
||||
"PluginUpdatedWithName": "{0} έχει αναβαθμιστεί",
|
||||
"ProviderValue": "Provider: {0}",
|
||||
"ProviderValue": "Πάροχος: {0}",
|
||||
"ScheduledTaskFailedWithName": "{0} αποτυχία",
|
||||
"ScheduledTaskStartedWithName": "{0} ξεκίνησε",
|
||||
"ServerNameNeedsToBeRestarted": "{0} χρειάζεται επανεκκίνηση",
|
||||
@@ -79,7 +79,7 @@
|
||||
"System": "Σύστημα",
|
||||
"TvShows": "Τηλεοπτικές Σειρές",
|
||||
"User": "Χρήστης",
|
||||
"UserCreatedWithName": "Δημιουργήθηκε ο χρήστης {0}",
|
||||
"UserCreatedWithName": "Ο χρήστης {0} δημιουργήθηκε",
|
||||
"UserDeletedWithName": "Ο χρήστης {0} έχει διαγραφεί",
|
||||
"UserDownloadingItemWithValues": "{0} κατεβάζει {1}",
|
||||
"UserLockedOutWithName": "Ο χρήστης {0} αποκλείστηκε",
|
||||
@@ -93,29 +93,29 @@
|
||||
"ValueSpecialEpisodeName": "Σπέσιαλ - {0}",
|
||||
"VersionNumber": "Έκδοση {0}",
|
||||
"TaskRefreshPeople": "Ανανέωση Ατόμων",
|
||||
"TaskCleanLogsDescription": "Διαγράφει τα αρχεία καταγραφής που είναι άνω των {0} ημερών.",
|
||||
"TaskCleanLogs": "Καθαρισμός Καταλόγου Καταγραφής",
|
||||
"TaskRefreshLibraryDescription": "Σαρώνει την βιβλιοθήκη πολυμέσων σας για νέα αρχεία και αναζωογονεί τα μεταδεδομένα.",
|
||||
"TaskCleanLogsDescription": "Διαγράφει αρχεία καταγραφής που είναι πάνω από {0} ημέρες.",
|
||||
"TaskCleanLogs": "Εκκαθάριση Καταλόγου Καταγραφής",
|
||||
"TaskRefreshLibraryDescription": "Σαρώνει την βιβλιοθήκη πολυμέσων σας για νέα αρχεία και ανανεώνει τα μεταδεδομένα.",
|
||||
"TaskRefreshLibrary": "Βιβλιοθήκη Σάρωσης Πολυμέσων",
|
||||
"TaskRefreshChapterImagesDescription": "Δημιουργεί μικρογραφίες για βίντεο με κεφάλαια.",
|
||||
"TaskRefreshChapterImagesDescription": "Δημιουργεί μικρογραφίες για βίντεο που έχουν κεφάλαια.",
|
||||
"TaskRefreshChapterImages": "Εξαγωγή Εικόνων Κεφαλαίου",
|
||||
"TaskCleanCacheDescription": "Τα διαγραμμένα αρχεία προσωρινής μνήμης που δεν χρειάζονται πλέον από το σύστημα.",
|
||||
"TaskCleanCacheDescription": "Διαγράφει αρχεία προσωρινής μνήμης που δεν χρειάζονται πλέον το σύστημα.",
|
||||
"TaskCleanCache": "Καθαρισμός Καταλόγου Προσωρινής Μνήμης",
|
||||
"TasksChannelsCategory": "Κανάλια Διαδικτύου",
|
||||
"TasksApplicationCategory": "Εφαρμογή",
|
||||
"TasksLibraryCategory": "Βιβλιοθήκη",
|
||||
"TasksMaintenanceCategory": "Συντήρηση",
|
||||
"TaskDownloadMissingSubtitlesDescription": "Αναζητήσεις στο διαδίκτυο όπου λείπουν υπότιτλους με βάση τη διαμόρφωση μεταδεδομένων.",
|
||||
"TaskDownloadMissingSubtitlesDescription": "Ψάχνει στο διαδίκτυο για υπότιτλους που λείπουν με βάση τη διαμόρφωση μεταδεδομένων.",
|
||||
"TaskDownloadMissingSubtitles": "Λήψη υπότιτλων που λείπουν",
|
||||
"TaskRefreshChannelsDescription": "Ανανεώνει τις πληροφορίες καναλιού στο διαδικτύου.",
|
||||
"TaskRefreshChannels": "Ανανέωση Καναλιών",
|
||||
"TaskCleanTranscodeDescription": "Διαγράφει αρχείου διακωδικοποιητή περισσότερο από μία ημέρα.",
|
||||
"TaskCleanTranscode": "Καθαρισμός Kαταλόγου Διακωδικοποιητή",
|
||||
"TaskUpdatePluginsDescription": "Κατεβάζει και εγκαθιστά ενημερώσεις για τις προσθήκες που έχουν ρυθμιστεί για αυτόματη ενημέρωση.",
|
||||
"TaskUpdatePlugins": "Ενημέρωση Προσθηκών",
|
||||
"TaskRefreshPeopleDescription": "Ενημερώνει μεταδεδομένα για ηθοποιούς και σκηνοθέτες στην βιβλιοθήκη των πολυμέσων σας.",
|
||||
"TaskCleanTranscodeDescription": "Διαγράφει αρχεία διακωδικοποίησης άνω της μίας ημέρας.",
|
||||
"TaskCleanTranscode": "Εκκαθάριση Kαταλόγου Διακωδικοποίησης",
|
||||
"TaskUpdatePluginsDescription": "Κατεβάζει και εγκαθιστά ενημερώσεις για τα πρόσθετα που έχουν ρυθμιστεί για αυτόματη ενημέρωση.",
|
||||
"TaskUpdatePlugins": "Ενημέρωση Πρόσθετων",
|
||||
"TaskRefreshPeopleDescription": "Ενημερώνει τα μεταδεδομένα για ηθοποιούς και σκηνοθέτες στη βιβλιοθήκη πολυμέσων σας.",
|
||||
"TaskCleanActivityLogDescription": "Διαγράφει καταχωρήσεις απο το αρχείο καταγραφής παλαιότερες από την επιλεγμένη ηλικία.",
|
||||
"TaskCleanActivityLog": "Καθαρό Αρχείο Καταγραφής Δραστηριοτήτων",
|
||||
"TaskCleanActivityLog": "Εκκαθάριση Αρχείου Καταγραφής Δραστηριοτήτων",
|
||||
"Undefined": "Απροσδιόριστο",
|
||||
"Forced": "Εξαναγκασμένο",
|
||||
"Default": "Προεπιλογή",
|
||||
|
||||
@@ -100,7 +100,7 @@
|
||||
"ItemRemovedWithName": "{0} liburutegitik ezabatu da",
|
||||
"ItemAddedWithName": "{0} liburutegira gehitu da",
|
||||
"HomeVideos": "Etxeko bideoak",
|
||||
"HeaderNextUp": "Hurrengoa",
|
||||
"HeaderNextUp": "Nobedadeak",
|
||||
"HeaderLiveTV": "Zuzeneko TB",
|
||||
"HeaderFavoriteSongs": "Gogoko abestiak",
|
||||
"HeaderFavoriteShows": "Gogoko showak",
|
||||
|
||||
@@ -122,5 +122,6 @@
|
||||
"TaskOptimizeDatabase": "データベースの最適化",
|
||||
"TaskKeyframeExtractorDescription": "より正確なHLSプレイリストを作成するため、動画ファイルからキーフレームを抽出する。この処理には時間がかかる場合があります。",
|
||||
"TaskKeyframeExtractor": "キーフレーム抽出",
|
||||
"External": "外部"
|
||||
"External": "外部",
|
||||
"HearingImpaired": "聴覚障害の方"
|
||||
}
|
||||
|
||||
@@ -108,5 +108,20 @@
|
||||
"UserPasswordChangedWithName": "მომხმარებლისთვის {0} პაროლი შეცვლილია",
|
||||
"UserPolicyUpdatedWithName": "{0}-ის მომხმარებლის პოლიტიკა განახლდა",
|
||||
"UserStoppedPlayingItemWithValues": "{0}-მა დაამთავრა {1}-ის დაკვრა {2}-ზე",
|
||||
"TaskRefreshChapterImagesDescription": "თავების მქონე ვიდეოებისთვის მინიატურების შექმნა."
|
||||
"TaskRefreshChapterImagesDescription": "თავების მქონე ვიდეოებისთვის მინიატურების შექმნა.",
|
||||
"TaskKeyframeExtractorDescription": "უფრო ზუსტი HLS დასაკრავი სიებისითვის ვიდეოდან საკვანძო გადრების ამოღება. შეიძლება საკმაო დრო დასჭირდეს.",
|
||||
"NewVersionIsAvailable": "გადმოსაწერად ხელმისაწვდომია Jellyfin -ის ახალი ვერსია.",
|
||||
"CameraImageUploadedFrom": "ახალი კამერის გამოსახულება ატვირთულია {0}-დან",
|
||||
"StartupEmbyServerIsLoading": "Jellyfin სერვერი იტვირთება. მოგვიანებით სცადეთ.",
|
||||
"SubtitleDownloadFailureFromForItem": "{0}-დან {1}-სთვის სუბტიტრების გადმოწერის შეცდომა",
|
||||
"ValueHasBeenAddedToLibrary": "{0} დაემატა თქვენს მედიის ბიბლიოთეკას",
|
||||
"TaskCleanActivityLogDescription": "მითითებულ ასაკზე ძველი ჟურნალის ჩანაწერების წაშლა.",
|
||||
"TaskCleanCacheDescription": "სისტემისთვის არასაჭირო ქეშის ფაილების წაშლა.",
|
||||
"TaskRefreshLibraryDescription": "თქვენი მედია ბიბლიოთეკაში ახალი ფაილების ძებნა და მეტამონაცემების განახლება.",
|
||||
"TaskCleanLogsDescription": "{0} დღეზე ძველი ჟურნალის ფაილების წაშლა.",
|
||||
"TaskRefreshPeopleDescription": "თქვენს მედიის ბიბლიოთეკაში მსახიობების და რეჟისორების მეტამონაცემების განახლება.",
|
||||
"TaskUpdatePluginsDescription": "ავტომატურად განახლებადად მონიშნული დამატებების განახლებების გადმოწერა და დაყენება.",
|
||||
"TaskCleanTranscodeDescription": "ერთ დღეზე უფრო ძველი ტრანსკოდირების ფაილების წაშლა.",
|
||||
"TaskDownloadMissingSubtitlesDescription": "მეტამონაცემებზე დაყრდნობით ინტერნეტში ნაკლული სუბტიტრების ძებნა.",
|
||||
"TaskOptimizeDatabaseDescription": "ბაზს შეკუშვა და ადგილის გათავისუფლება. ამ ამოცანის ბიბლიოთეკის სკანირების ან ნებისმიერი ცვლილების, რომელიც ბაზაში რამეს აკეთებს, გაშვებას შეუძლია ბაზის წარმადობა გაზარდოს."
|
||||
}
|
||||
|
||||
@@ -123,5 +123,6 @@
|
||||
"TaskOptimizeDatabase": "데이터베이스 최적화",
|
||||
"TaskKeyframeExtractorDescription": "비디오 파일에서 키프레임을 추출하여 더 정확한 HLS 재생 목록을 만듭니다. 이 작업은 오랫동안 진행될 수 있습니다.",
|
||||
"TaskKeyframeExtractor": "키프레임 추출",
|
||||
"External": "외부"
|
||||
"External": "외부",
|
||||
"HearingImpaired": "청각 장애"
|
||||
}
|
||||
|
||||
@@ -123,5 +123,6 @@
|
||||
"TaskOptimizeDatabaseDescription": "Kompaktuje bazę danych i obcina wolne miejsce. Uruchomienie tego zadania po przeskanowaniu biblioteki lub dokonaniu innych zmian, które pociągają za sobą modyfikacje bazy danych, może poprawić wydajność.",
|
||||
"External": "Zewnętrzny",
|
||||
"TaskKeyframeExtractorDescription": "Wyodrębnia klatki kluczowe z plików wideo w celu utworzenia bardziej precyzyjnych list odtwarzania HLS. To zadanie może trwać przez długi czas.",
|
||||
"TaskKeyframeExtractor": "Ekstraktor klatek kluczowych"
|
||||
"TaskKeyframeExtractor": "Ekstraktor klatek kluczowych",
|
||||
"HearingImpaired": "Niedosłyszący"
|
||||
}
|
||||
|
||||
@@ -13,5 +13,11 @@
|
||||
"DeviceOfflineWithName": "{0} abandoned ship",
|
||||
"AppDeviceValues": "Captain: {0}, Ship: {1}",
|
||||
"CameraImageUploadedFrom": "Yer looking glass has glimpsed another painting from {0}",
|
||||
"Collections": "Barrels"
|
||||
"Collections": "Barrels",
|
||||
"ItemAddedWithName": "{0} is now with yer treasure",
|
||||
"Default": "Normal-like",
|
||||
"FailedLoginAttemptWithUserName": "Ye failed to get in, try from {0}",
|
||||
"Favorites": "Finest Loot",
|
||||
"ItemRemovedWithName": "{0} was taken from yer treasure",
|
||||
"LabelIpAddressValue": "Ship's coordinates: {0}"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"Albums": "Альбомы",
|
||||
"AppDeviceValues": "Приложение.: {0}, Устройство.: {1}",
|
||||
"AppDeviceValues": "Приложение: {0}, Устройство: {1}",
|
||||
"Application": "Приложение",
|
||||
"Artists": "Исполнители",
|
||||
"AuthenticationSucceededWithUserName": "{0} - авторизация успешна",
|
||||
@@ -50,7 +50,7 @@
|
||||
"NotificationOptionAudioPlaybackStopped": "Воспроизведение аудио остановлено",
|
||||
"NotificationOptionCameraImageUploaded": "Изображения с камеры загружены",
|
||||
"NotificationOptionInstallationFailed": "Сбой установки",
|
||||
"NotificationOptionNewLibraryContent": "Новое содержание добавлено",
|
||||
"NotificationOptionNewLibraryContent": "Новое содержимое добавлено",
|
||||
"NotificationOptionPluginError": "Сбой плагина",
|
||||
"NotificationOptionPluginInstalled": "Плагин установлен",
|
||||
"NotificationOptionPluginUninstalled": "Плагин удалён",
|
||||
|
||||
@@ -122,5 +122,6 @@
|
||||
"TaskOptimizeDatabaseDescription": "Сажима базу података и скраћује слободан простор. Покретање овог задатка након скенирања библиотеке или других промена које подразумевају измене базе података које могу побољшати перформансе.",
|
||||
"External": "Спољно",
|
||||
"TaskKeyframeExtractorDescription": "Екстрактује кљулне сличице из видео датотека да би креирао више преицзну HLS плеј-листу. Овај задатак може да потраје дуже време.",
|
||||
"TaskKeyframeExtractor": "Екстрактор кључних сличица"
|
||||
"TaskKeyframeExtractor": "Екстрактор кључних сличица",
|
||||
"HearingImpaired": "ослабљен слух"
|
||||
}
|
||||
|
||||
@@ -120,5 +120,6 @@
|
||||
"Forced": "บังคับใช้",
|
||||
"TaskOptimizeDatabase": "ปรับปรุงประสิทธิภาพฐานข้อมูล",
|
||||
"TaskOptimizeDatabaseDescription": "ลดขนาดการจัดเก็บฐานข้อมูล ใช้งานคำสั่งนี้หลังจากสแกนไลบรารีหรือหลังจากการเปลี่ยนแปลงฐานข้อมูล อาจจะทำให้ระบบทำงานเร็วขึ้น",
|
||||
"External": "ภายนอก"
|
||||
"External": "ภายนอก",
|
||||
"HearingImpaired": "บกพร่องทางการได้ยิน"
|
||||
}
|
||||
|
||||
@@ -9,15 +9,15 @@
|
||||
"Channels": "頻道",
|
||||
"ChapterNameValue": "章節 {0}",
|
||||
"Collections": "合輯",
|
||||
"DeviceOfflineWithName": "{0} 已經斷開連結",
|
||||
"DeviceOfflineWithName": "{0} 已經斷開連接",
|
||||
"DeviceOnlineWithName": "{0} 已經連接",
|
||||
"FailedLoginAttemptWithUserName": "來自 {0} 的登入失敗",
|
||||
"FailedLoginAttemptWithUserName": "{0} 登入失敗",
|
||||
"Favorites": "我的最愛",
|
||||
"Folders": "資料夾",
|
||||
"Genres": "風格",
|
||||
"HeaderAlbumArtists": "專輯藝人",
|
||||
"HeaderContinueWatching": "繼續觀看",
|
||||
"HeaderFavoriteAlbums": "最愛專輯",
|
||||
"HeaderFavoriteAlbums": "最愛的專輯",
|
||||
"HeaderFavoriteArtists": "最愛的藝人",
|
||||
"HeaderFavoriteEpisodes": "最愛的劇集",
|
||||
"HeaderFavoriteShows": "最愛的節目",
|
||||
@@ -44,10 +44,10 @@
|
||||
"NameSeasonNumber": "第 {0} 季",
|
||||
"NameSeasonUnknown": "未知季數",
|
||||
"NewVersionIsAvailable": "新版本的 Jellyfin 伺服器可供下載。",
|
||||
"NotificationOptionApplicationUpdateAvailable": "有可用的應用程式更新",
|
||||
"NotificationOptionApplicationUpdateAvailable": "有可用的更新",
|
||||
"NotificationOptionApplicationUpdateInstalled": "應用程式已更新",
|
||||
"NotificationOptionAudioPlayback": "開始播放音頻",
|
||||
"NotificationOptionAudioPlaybackStopped": "已停止播放音頻",
|
||||
"NotificationOptionAudioPlayback": "開始播放音訊",
|
||||
"NotificationOptionAudioPlaybackStopped": "已停止播放音訊",
|
||||
"NotificationOptionCameraImageUploaded": "相片已上傳",
|
||||
"NotificationOptionInstallationFailed": "安裝失敗",
|
||||
"NotificationOptionNewLibraryContent": "已添加新内容",
|
||||
|
||||
@@ -77,6 +77,7 @@ chb|||Chibcha|chibcha
|
||||
che||ce|Chechen|tchétchène
|
||||
chg|||Chagatai|djaghataï
|
||||
chi|zho|zh|Chinese|chinois
|
||||
chi|zho|ze|Chinese; Bilingual|chinois
|
||||
chi|zho|zh-tw|Chinese; Traditional|chinois
|
||||
chi|zho|zh-hk|Chinese; Hong Kong|chinois
|
||||
chk|||Chuukese|chuuk
|
||||
|
||||
33
Emby.Server.Implementations/Plugins/PluginLoadContext.cs
Normal file
33
Emby.Server.Implementations/Plugins/PluginLoadContext.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
|
||||
namespace Emby.Server.Implementations.Plugins;
|
||||
|
||||
/// <summary>
|
||||
/// A custom <see cref="AssemblyLoadContext"/> for loading Jellyfin plugins.
|
||||
/// </summary>
|
||||
public class PluginLoadContext : AssemblyLoadContext
|
||||
{
|
||||
private readonly AssemblyDependencyResolver _resolver;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PluginLoadContext"/> class.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the plugin assembly.</param>
|
||||
public PluginLoadContext(string path) : base(true)
|
||||
{
|
||||
_resolver = new AssemblyDependencyResolver(path);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Assembly? Load(AssemblyName assemblyName)
|
||||
{
|
||||
var assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);
|
||||
if (assemblyPath is not null)
|
||||
{
|
||||
return LoadFromAssemblyPath(assemblyPath);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
@@ -30,6 +31,7 @@ namespace Emby.Server.Implementations.Plugins
|
||||
{
|
||||
private readonly string _pluginsPath;
|
||||
private readonly Version _appVersion;
|
||||
private readonly List<AssemblyLoadContext> _assemblyLoadContexts;
|
||||
private readonly JsonSerializerOptions _jsonOptions;
|
||||
private readonly ILogger<PluginManager> _logger;
|
||||
private readonly IApplicationHost _appHost;
|
||||
@@ -76,6 +78,8 @@ namespace Emby.Server.Implementations.Plugins
|
||||
_appHost = appHost;
|
||||
_minimumVersion = new Version(0, 0, 0, 1);
|
||||
_plugins = Directory.Exists(_pluginsPath) ? DiscoverPlugins().ToList() : new List<LocalPlugin>();
|
||||
|
||||
_assemblyLoadContexts = new List<AssemblyLoadContext>();
|
||||
}
|
||||
|
||||
private IHttpClientFactory HttpClientFactory
|
||||
@@ -124,7 +128,10 @@ namespace Emby.Server.Implementations.Plugins
|
||||
Assembly assembly;
|
||||
try
|
||||
{
|
||||
assembly = Assembly.LoadFrom(file);
|
||||
var assemblyLoadContext = new PluginLoadContext(file);
|
||||
_assemblyLoadContexts.Add(assemblyLoadContext);
|
||||
|
||||
assembly = assemblyLoadContext.LoadFromAssemblyPath(file);
|
||||
|
||||
// Load all required types to verify that the plugin will load
|
||||
assembly.GetTypes();
|
||||
@@ -156,6 +163,15 @@ namespace Emby.Server.Implementations.Plugins
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void UnloadAssemblies()
|
||||
{
|
||||
foreach (var assemblyLoadContext in _assemblyLoadContexts)
|
||||
{
|
||||
assemblyLoadContext.Unload();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates all the plugin instances.
|
||||
/// </summary>
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
|
||||
{
|
||||
private readonly ILogger<OptimizeDatabaseTask> _logger;
|
||||
private readonly ILocalizationManager _localization;
|
||||
private readonly IDbContextFactory<JellyfinDb> _provider;
|
||||
private readonly IDbContextFactory<JellyfinDbContext> _provider;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="OptimizeDatabaseTask" /> class.
|
||||
@@ -28,7 +28,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
|
||||
public OptimizeDatabaseTask(
|
||||
ILogger<OptimizeDatabaseTask> logger,
|
||||
ILocalizationManager localization,
|
||||
IDbContextFactory<JellyfinDb> provider)
|
||||
IDbContextFactory<JellyfinDbContext> provider)
|
||||
{
|
||||
_logger = logger;
|
||||
_localization = localization;
|
||||
|
||||
@@ -95,12 +95,6 @@ namespace Emby.Server.Implementations.Session
|
||||
_deviceManager.DeviceOptionsUpdated += OnDeviceManagerDeviceOptionsUpdated;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<GenericEventArgs<AuthenticationRequest>> AuthenticationFailed;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<GenericEventArgs<AuthenticationResult>> AuthenticationSucceeded;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when playback has started.
|
||||
/// </summary>
|
||||
@@ -1468,7 +1462,7 @@ namespace Emby.Server.Implementations.Session
|
||||
|
||||
if (user is null)
|
||||
{
|
||||
AuthenticationFailed?.Invoke(this, new GenericEventArgs<AuthenticationRequest>(request));
|
||||
await _eventManager.PublishAsync(new GenericEventArgs<AuthenticationRequest>(request)).ConfigureAwait(false);
|
||||
throw new AuthenticationException("Invalid username or password entered.");
|
||||
}
|
||||
|
||||
@@ -1504,8 +1498,7 @@ namespace Emby.Server.Implementations.Session
|
||||
ServerId = _appHost.SystemId
|
||||
};
|
||||
|
||||
AuthenticationSucceeded?.Invoke(this, new GenericEventArgs<AuthenticationResult>(returnResult));
|
||||
|
||||
await _eventManager.PublishAsync(new GenericEventArgs<AuthenticationResult>(returnResult)).ConfigureAwait(false);
|
||||
return returnResult;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user