mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-03-31 23:42:09 +01:00
Updated to master
This commit is contained in:
@@ -4,6 +4,7 @@ using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
@@ -37,6 +38,7 @@ using Emby.Server.Implementations.LiveTv;
|
||||
using Emby.Server.Implementations.Localization;
|
||||
using Emby.Server.Implementations.Net;
|
||||
using Emby.Server.Implementations.Playlists;
|
||||
using Emby.Server.Implementations.Plugins;
|
||||
using Emby.Server.Implementations.QuickConnect;
|
||||
using Emby.Server.Implementations.ScheduledTasks;
|
||||
using Emby.Server.Implementations.Security;
|
||||
@@ -120,6 +122,7 @@ namespace Emby.Server.Implementations
|
||||
|
||||
private readonly IFileSystem _fileSystemManager;
|
||||
private readonly IXmlSerializer _xmlSerializer;
|
||||
private readonly IJsonSerializer _jsonSerializer;
|
||||
private readonly IStartupOptions _startupOptions;
|
||||
|
||||
private IMediaEncoder _mediaEncoder;
|
||||
@@ -259,6 +262,8 @@ namespace Emby.Server.Implementations
|
||||
IServiceCollection serviceCollection)
|
||||
{
|
||||
_xmlSerializer = new MyXmlSerializer();
|
||||
_jsonSerializer = new JsonSerializer();
|
||||
|
||||
ServiceCollection = serviceCollection;
|
||||
|
||||
ApplicationPaths = applicationPaths;
|
||||
@@ -1012,6 +1017,119 @@ namespace Emby.Server.Implementations
|
||||
|
||||
protected abstract void RestartInternal();
|
||||
|
||||
/// <summary>
|
||||
/// Comparison function used in <see cref="GetPlugins" />.
|
||||
/// </summary>
|
||||
/// <param name="a">Item to compare.</param>
|
||||
/// <param name="b">Item to compare with.</param>
|
||||
/// <returns>Boolean result of the operation.</returns>
|
||||
private static int VersionCompare(
|
||||
(Version PluginVersion, string Name, string Path) a,
|
||||
(Version PluginVersion, string Name, string Path) b)
|
||||
{
|
||||
int compare = string.Compare(a.Name, b.Name, true, CultureInfo.InvariantCulture);
|
||||
|
||||
if (compare == 0)
|
||||
{
|
||||
return a.PluginVersion.CompareTo(b.PluginVersion);
|
||||
}
|
||||
|
||||
return compare;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of plugins to install.
|
||||
/// </summary>
|
||||
/// <param name="path">Path to check.</param>
|
||||
/// <param name="cleanup">True if an attempt should be made to delete old plugs.</param>
|
||||
/// <returns>Enumerable list of dlls to load.</returns>
|
||||
private IEnumerable<string> GetPlugins(string path, bool cleanup = true)
|
||||
{
|
||||
var dllList = new List<string>();
|
||||
var versions = new List<(Version PluginVersion, string Name, string Path)>();
|
||||
var directories = Directory.EnumerateDirectories(path, "*.*", SearchOption.TopDirectoryOnly);
|
||||
string metafile;
|
||||
|
||||
foreach (var dir in directories)
|
||||
{
|
||||
try
|
||||
{
|
||||
metafile = Path.Combine(dir, "meta.json");
|
||||
if (File.Exists(metafile))
|
||||
{
|
||||
var manifest = _jsonSerializer.DeserializeFromFile<PluginManifest>(metafile);
|
||||
|
||||
if (!Version.TryParse(manifest.TargetAbi, out var targetAbi))
|
||||
{
|
||||
targetAbi = new Version(0, 0, 0, 1);
|
||||
}
|
||||
|
||||
if (!Version.TryParse(manifest.Version, out var version))
|
||||
{
|
||||
version = new Version(0, 0, 0, 1);
|
||||
}
|
||||
|
||||
if (ApplicationVersion >= targetAbi)
|
||||
{
|
||||
// Only load Plugins if the plugin is built for this version or below.
|
||||
versions.Add((version, manifest.Name, dir));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// No metafile, so lets see if the folder is versioned.
|
||||
metafile = dir.Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries)[^1];
|
||||
|
||||
int versionIndex = dir.LastIndexOf('_');
|
||||
if (versionIndex != -1 && Version.TryParse(dir.Substring(versionIndex + 1), out Version ver))
|
||||
{
|
||||
// Versioned folder.
|
||||
versions.Add((ver, metafile, dir));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Un-versioned folder - Add it under the path name and version 0.0.0.1.
|
||||
versions.Add((new Version(0, 0, 0, 1), metafile, dir));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
string lastName = string.Empty;
|
||||
versions.Sort(VersionCompare);
|
||||
// Traverse backwards through the list.
|
||||
// The first item will be the latest version.
|
||||
for (int x = versions.Count - 1; x >= 0; x--)
|
||||
{
|
||||
if (!string.Equals(lastName, versions[x].Name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
dllList.AddRange(Directory.EnumerateFiles(versions[x].Path, "*.dll", SearchOption.AllDirectories));
|
||||
lastName = versions[x].Name;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(lastName) && cleanup)
|
||||
{
|
||||
// Attempt a cleanup of old folders.
|
||||
try
|
||||
{
|
||||
Logger.LogDebug("Deleting {Path}", versions[x].Path);
|
||||
Directory.Delete(versions[x].Path, true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.LogWarning(e, "Unable to delete {Path}", versions[x].Path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dllList;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the composable part assemblies.
|
||||
/// </summary>
|
||||
@@ -1020,7 +1138,7 @@ namespace Emby.Server.Implementations
|
||||
{
|
||||
if (Directory.Exists(ApplicationPaths.PluginsPath))
|
||||
{
|
||||
foreach (var file in Directory.EnumerateFiles(ApplicationPaths.PluginsPath, "*.dll", SearchOption.AllDirectories))
|
||||
foreach (var file in GetPlugins(ApplicationPaths.PluginsPath))
|
||||
{
|
||||
Assembly plugAss;
|
||||
try
|
||||
|
||||
@@ -234,7 +234,9 @@ namespace Emby.Server.Implementations.Data
|
||||
{
|
||||
if (statement.BindParameters.TryGetValue(name, out IBindParameter bindParam))
|
||||
{
|
||||
bindParam.Bind(value.ToByteArray());
|
||||
Span<byte> byteValue = stackalloc byte[16];
|
||||
value.TryWriteBytes(byteValue);
|
||||
bindParam.Bind(byteValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -32,10 +32,10 @@
|
||||
<PackageReference Include="Microsoft.AspNetCore.ResponseCompression" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.2.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.7" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.1.7" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.7" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.7" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.8" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.1.8" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.8" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.8" />
|
||||
<PackageReference Include="Mono.Nat" Version="3.0.0" />
|
||||
<PackageReference Include="prometheus-net.DotNetRuntime" Version="3.4.0" />
|
||||
<PackageReference Include="ServiceStack.Text.Core" Version="5.9.2" />
|
||||
|
||||
@@ -107,7 +107,7 @@
|
||||
"TaskCleanLogsDescription": "Supprime les journaux de plus de {0} jours.",
|
||||
"TaskCleanLogs": "Nettoyer le répertoire des journaux",
|
||||
"TaskRefreshLibraryDescription": "Scanne toute les bibliothèques pour trouver les nouveaux fichiers et rafraîchit les métadonnées.",
|
||||
"TaskRefreshLibrary": "Scanner toute les Bibliothèques",
|
||||
"TaskRefreshLibrary": "Scanner toutes les Bibliothèques",
|
||||
"TaskRefreshChapterImagesDescription": "Crée des images de miniature pour les vidéos ayant des chapitres.",
|
||||
"TaskRefreshChapterImages": "Extraire les images de chapitre",
|
||||
"TaskCleanCacheDescription": "Supprime les fichiers de cache dont le système n'a plus besoin.",
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
{
|
||||
"Albums": "Álbumes"
|
||||
"Albums": "Álbumes",
|
||||
"Collections": "Colecións",
|
||||
"ChapterNameValue": "Capítulos {0}",
|
||||
"Channels": "Canles",
|
||||
"CameraImageUploadedFrom": "Cargouse unha nova imaxe da cámara desde {0}",
|
||||
"Books": "Libros",
|
||||
"AuthenticationSucceededWithUserName": "{0} autenticouse correctamente",
|
||||
"Artists": "Artistas",
|
||||
"Application": "Aplicativo"
|
||||
}
|
||||
|
||||
@@ -84,8 +84,8 @@
|
||||
"UserDeletedWithName": "사용자 {0} 삭제됨",
|
||||
"UserDownloadingItemWithValues": "{0}이(가) {1}을 다운로드 중입니다",
|
||||
"UserLockedOutWithName": "유저 {0} 은(는) 잠금처리 되었습니다",
|
||||
"UserOfflineFromDevice": "{1}로부터 {0}의 연결이 끊겼습니다",
|
||||
"UserOnlineFromDevice": "{0}은 {1}에서 온라인 상태입니다",
|
||||
"UserOfflineFromDevice": "{1}에서 {0}의 연결이 끊킴",
|
||||
"UserOnlineFromDevice": "{0}이 {1}으로 접속",
|
||||
"UserPasswordChangedWithName": "사용자 {0}의 비밀번호가 변경되었습니다",
|
||||
"UserPolicyUpdatedWithName": "{0}의 사용자 정책이 업데이트되었습니다",
|
||||
"UserStartedPlayingItemWithValues": "{2}에서 {0}이 {1} 재생 중",
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
"NotificationOptionAudioPlayback": "Lydavspilling startet",
|
||||
"NotificationOptionAudioPlaybackStopped": "Lydavspilling stoppet",
|
||||
"NotificationOptionCameraImageUploaded": "Kamerabilde lastet opp",
|
||||
"NotificationOptionInstallationFailed": "Installasjonsfeil",
|
||||
"NotificationOptionInstallationFailed": "Installasjonen feilet",
|
||||
"NotificationOptionNewLibraryContent": "Nytt innhold lagt til",
|
||||
"NotificationOptionPluginError": "Pluginfeil",
|
||||
"NotificationOptionPluginInstalled": "Plugin installert",
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"DeviceOnlineWithName": "{0} இணைக்கப்பட்டது",
|
||||
"DeviceOfflineWithName": "{0} துண்டிக்கப்பட்டது",
|
||||
"Collections": "தொகுப்புகள்",
|
||||
"CameraImageUploadedFrom": "{0} இலிருந்து புதிய புகைப்படம் பதிவேற்றப்பட்டது",
|
||||
"CameraImageUploadedFrom": "{0} இல் இருந்து புதிய புகைப்படம் பதிவேற்றப்பட்டது",
|
||||
"AppDeviceValues": "செயலி: {0}, சாதனம்: {1}",
|
||||
"TaskDownloadMissingSubtitles": "விடுபட்டுபோன வசன வரிகளைப் பதிவிறக்கு",
|
||||
"TaskRefreshChannels": "சேனல்களை புதுப்பி",
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
{
|
||||
"Collections": "Bộ Sưu Tập",
|
||||
"Favorites": "Sở Thích",
|
||||
"Favorites": "Yêu Thích",
|
||||
"Folders": "Thư Mục",
|
||||
"Genres": "Thể Loại",
|
||||
"HeaderAlbumArtists": "Bộ Sưu Tập Nghệ sĩ",
|
||||
"HeaderContinueWatching": "Tiếp Tục Xem",
|
||||
"HeaderContinueWatching": "Xem Tiếp",
|
||||
"HeaderLiveTV": "TV Trực Tiếp",
|
||||
"Movies": "Phim",
|
||||
"Photos": "Ảnh",
|
||||
"Playlists": "Danh Sách Chơi",
|
||||
"Shows": "Các Chương Trình",
|
||||
"Playlists": "Danh sách phát",
|
||||
"Shows": "Chương Trình TV",
|
||||
"Songs": "Các Bài Hát",
|
||||
"Sync": "Đồng Bộ",
|
||||
"ValueSpecialEpisodeName": "Đặc Biệt - {0}",
|
||||
"Albums": "Bộ Sưu Tập",
|
||||
"Artists": "Nghệ Sĩ",
|
||||
"Albums": "Albums",
|
||||
"Artists": "Các Nghệ Sĩ",
|
||||
"TaskDownloadMissingSubtitlesDescription": "Tìm kiếm phụ đề bị thiếu trên Internet dựa trên cấu hình thông tin chi tiết.",
|
||||
"TaskDownloadMissingSubtitles": "Tải xuống phụ đề bị thiếu",
|
||||
"TaskRefreshChannelsDescription": "Làm mới thông tin kênh internet.",
|
||||
@@ -29,8 +29,8 @@
|
||||
"TaskCleanLogs": "Làm sạch nhật ký",
|
||||
"TaskRefreshLibraryDescription": "Quét thư viện phương tiện của bạn để tìm các tệp mới và làm mới thông tin chi tiết.",
|
||||
"TaskRefreshLibrary": "Quét Thư viện Phương tiện",
|
||||
"TaskRefreshChapterImagesDescription": "Tạo hình thu nhỏ cho các video có chương.",
|
||||
"TaskRefreshChapterImages": "Trích xuất hình ảnh chương",
|
||||
"TaskRefreshChapterImagesDescription": "Tạo hình thu nhỏ cho video có các phân cảnh.",
|
||||
"TaskRefreshChapterImages": "Trích Xuất Ảnh Phân Cảnh",
|
||||
"TaskCleanCacheDescription": "Xóa các tệp cache không còn cần thiết của hệ thống.",
|
||||
"TaskCleanCache": "Làm Sạch Thư Mục Cache",
|
||||
"TasksChannelsCategory": "Kênh Internet",
|
||||
@@ -107,8 +107,8 @@
|
||||
"FailedLoginAttemptWithUserName": "Nỗ lực đăng nhập thất bại từ {0}",
|
||||
"DeviceOnlineWithName": "{0} đã kết nối",
|
||||
"DeviceOfflineWithName": "{0} đã ngắt kết nối",
|
||||
"ChapterNameValue": "Chương {0}",
|
||||
"Channels": "Kênh",
|
||||
"ChapterNameValue": "Phân Cảnh {0}",
|
||||
"Channels": "Các Kênh",
|
||||
"CameraImageUploadedFrom": "Một hình ảnh máy ảnh mới đã được tải lên từ {0}",
|
||||
"Books": "Sách",
|
||||
"AuthenticationSucceededWithUserName": "{0} xác thực thành công",
|
||||
|
||||
@@ -96,7 +96,7 @@
|
||||
"TaskDownloadMissingSubtitles": "下載遺失的字幕",
|
||||
"TaskRefreshChannels": "重新整理頻道",
|
||||
"TaskUpdatePlugins": "更新外掛",
|
||||
"TaskRefreshPeople": "重新整理人員",
|
||||
"TaskRefreshPeople": "刷新用戶",
|
||||
"TaskCleanLogsDescription": "刪除超過 {0} 天的舊紀錄檔。",
|
||||
"TaskCleanLogs": "清空紀錄資料夾",
|
||||
"TaskRefreshLibraryDescription": "重新掃描媒體庫的新檔案並更新描述資料。",
|
||||
|
||||
@@ -413,6 +413,7 @@ namespace Emby.Server.Implementations.Localization
|
||||
yield return new LocalizationOption("Swedish", "sv");
|
||||
yield return new LocalizationOption("Swiss German", "gsw");
|
||||
yield return new LocalizationOption("Turkish", "tr");
|
||||
yield return new LocalizationOption("Tiếng Việt", "vi");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
60
Emby.Server.Implementations/Plugins/PluginManifest.cs
Normal file
60
Emby.Server.Implementations/Plugins/PluginManifest.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
|
||||
namespace Emby.Server.Implementations.Plugins
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a Plugin manifest file.
|
||||
/// </summary>
|
||||
public class PluginManifest
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the category of the plugin.
|
||||
/// </summary>
|
||||
public string Category { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the changelog information.
|
||||
/// </summary>
|
||||
public string Changelog { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the description of the plugin.
|
||||
/// </summary>
|
||||
public string Description { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Global Unique Identifier for the plugin.
|
||||
/// </summary>
|
||||
public Guid Guid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Name of the plugin.
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an overview of the plugin.
|
||||
/// </summary>
|
||||
public string Overview { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the owner of the plugin.
|
||||
/// </summary>
|
||||
public string Owner { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the compatibility version for the plugin.
|
||||
/// </summary>
|
||||
public string TargetAbi { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the timestamp of the plugin.
|
||||
/// </summary>
|
||||
public DateTime Timestamp { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Version number of the plugin.
|
||||
/// </summary>
|
||||
public string Version { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1037,7 +1037,7 @@ namespace Emby.Server.Implementations.Session
|
||||
|
||||
var generalCommand = new GeneralCommand
|
||||
{
|
||||
Name = GeneralCommandType.DisplayMessage.ToString()
|
||||
Name = GeneralCommandType.DisplayMessage
|
||||
};
|
||||
|
||||
generalCommand.Arguments["Header"] = command.Header;
|
||||
@@ -1268,7 +1268,7 @@ namespace Emby.Server.Implementations.Session
|
||||
{
|
||||
var generalCommand = new GeneralCommand
|
||||
{
|
||||
Name = GeneralCommandType.DisplayContent.ToString(),
|
||||
Name = GeneralCommandType.DisplayContent,
|
||||
Arguments =
|
||||
{
|
||||
["ItemId"] = command.ItemId,
|
||||
|
||||
@@ -15,12 +15,14 @@ using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Common.Plugins;
|
||||
using MediaBrowser.Common.Updates;
|
||||
using MediaBrowser.Common.System;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Model.IO;
|
||||
using MediaBrowser.Model.Net;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
using MediaBrowser.Model.Updates;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MediaBrowser.Model.System;
|
||||
|
||||
namespace Emby.Server.Implementations.Updates
|
||||
{
|
||||
@@ -377,11 +379,20 @@ namespace Emby.Server.Implementations.Updates
|
||||
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))
|
||||
{
|
||||
Directory.Delete(targetDir, true);
|
||||
try
|
||||
{
|
||||
Directory.Delete(targetDir, true);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore any exceptions.
|
||||
}
|
||||
}
|
||||
|
||||
stream.Position = 0;
|
||||
_zipClient.ExtractAllFromZip(stream, targetDir, true);
|
||||
|
||||
@@ -423,15 +434,22 @@ namespace Emby.Server.Implementations.Updates
|
||||
path = file;
|
||||
}
|
||||
|
||||
if (isDirectory)
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("Deleting plugin directory {0}", path);
|
||||
Directory.Delete(path, true);
|
||||
if (isDirectory)
|
||||
{
|
||||
_logger.LogInformation("Deleting plugin directory {0}", path);
|
||||
Directory.Delete(path, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation("Deleting plugin file {0}", path);
|
||||
_fileSystem.DeleteFile(path);
|
||||
}
|
||||
}
|
||||
else
|
||||
catch
|
||||
{
|
||||
_logger.LogInformation("Deleting plugin file {0}", path);
|
||||
_fileSystem.DeleteFile(path);
|
||||
// Ignore file errors.
|
||||
}
|
||||
|
||||
var list = _config.Configuration.UninstalledPlugins.ToList();
|
||||
|
||||
Reference in New Issue
Block a user