diff options
21 files changed, 247 insertions, 179 deletions
diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml index c829da98a..13cc67528 100644 --- a/.ci/azure-pipelines.yml +++ b/.ci/azure-pipelines.yml @@ -200,8 +200,8 @@ jobs: persistCredentials: true - task: CmdLine@2 - displayName: "Check out web" - condition: and(succeeded(), or(contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI', 'BuildCompletion')) + displayName: "Check out web (master, release or tag)" + condition: and(succeeded(), or(contains(variables['Build.SourceBranch'], 'release'), contains(variables['Build.SourceBranch'], 'master'), contains(variables['Build.SourceBranch'], 'tag')) ,eq(variables['BuildConfiguration'], 'Release'), in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI', 'BuildCompletion')) inputs: script: 'git clone --single-branch --branch $(Build.SourceBranchName) --depth=1 https://github.com/jellyfin/jellyfin-web.git $(Agent.TempDirectory)/jellyfin-web' diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index eb9069c44..fde4d7059 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -37,6 +37,7 @@ <PackageReference Include="ServiceStack.Text.Core" Version="5.7.0" /> <PackageReference Include="sharpcompress" Version="0.24.0" /> <PackageReference Include="SQLitePCL.pretty.netstandard" Version="2.0.1" /> + <PackageReference Include="System.Interactive.Async" Version="4.0.0" /> </ItemGroup> <ItemGroup> diff --git a/Emby.Server.Implementations/Localization/Core/af.json b/Emby.Server.Implementations/Localization/Core/af.json new file mode 100644 index 000000000..dcec26801 --- /dev/null +++ b/Emby.Server.Implementations/Localization/Core/af.json @@ -0,0 +1,96 @@ +{ + "Artists": "Kunstenare", + "Channels": "Kanale", + "Folders": "Fouers", + "Favorites": "Gunstelinge", + "HeaderFavoriteShows": "Gunsteling Vertonings", + "ValueSpecialEpisodeName": "Spesiaal - {0}", + "HeaderAlbumArtists": "Album Kunstenaars", + "Books": "Boeke", + "HeaderNextUp": "Volgende", + "Movies": "Rolprente", + "Shows": "Program", + "HeaderContinueWatching": "Hou Aan Kyk", + "HeaderFavoriteEpisodes": "Gunsteling Episodes", + "Photos": "Fotos", + "Playlists": "Speellysse", + "HeaderFavoriteArtists": "Gunsteling Kunstenaars", + "HeaderFavoriteAlbums": "Gunsteling Albums", + "Sync": "Sinkroniseer", + "HeaderFavoriteSongs": "Gunsteling Liedjies", + "Songs": "Liedjies", + "DeviceOnlineWithName": "{0} is verbind", + "DeviceOfflineWithName": "{0} het afgesluit", + "Collections": "Versamelings", + "Inherit": "Ontvang", + "HeaderLiveTV": "Live 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}", + "UserLockedOutWithName": "Gebruiker {0} is uitgesluit", + "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", + "SubtitlesDownloadedForItem": "Ondertitels afgelaai vir {0}", + "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", + "NotificationOptionTaskFailed": "Geskeduleerde taak het misluk", + "NotificationOptionServerRestartRequired": "Bediener herbegin nodig", + "NotificationOptionPluginUpdateInstalled": "Nuwe inprop module geïnstalleer", + "NotificationOptionPluginUninstalled": "Inprop module verwyder", + "NotificationOptionPluginInstalled": "Inprop module geïnstalleer", + "NotificationOptionPluginError": "Inprop module het misluk", + "NotificationOptionNewLibraryContent": "Nuwe inhoud bygevoeg", + "NotificationOptionInstallationFailed": "Installering het misluk", + "NotificationOptionCameraImageUploaded": "Kamera foto is opgelaai", + "NotificationOptionAudioPlaybackStopped": "Oudio terugspeel het gestop", + "NotificationOptionAudioPlayback": "Oudio terugspeel het begin", + "NotificationOptionApplicationUpdateInstalled": "Nuwe program weergawe geïnstalleer", + "NotificationOptionApplicationUpdateAvailable": "Nuwe program weergawe beskikbaar", + "NewVersionIsAvailable": "'n Nuwe Jellyfin Bedienaar weergawe kan afgelaai word.", + "NameSeasonUnknown": "Seisoen Onbekend", + "NameSeasonNumber": "Seisoen {0}", + "NameInstallFailed": "{0} installering het misluk", + "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": "Lopende tyd: {0}", + "LabelIpAddressValue": "IP adres: {0}", + "ItemRemovedWithName": "{0} is uit versameling verwyder", + "ItemAddedWithName": "{0} is in die versameling", + "HomeVideos": "Tuis opnames", + "HeaderRecordingGroups": "Groep Opnames", + "HeaderCameraUploads": "Kamera Oplaai", + "Genres": "Genres", + "FailedLoginAttemptWithUserName": "Mislukte aansluiting van {0}", + "ChapterNameValue": "Hoofstuk", + "CameraImageUploadedFrom": "'n Nuwe kamera photo opgelaai van {0}", + "AuthenticationSucceededWithUserName": "{0} suksesvol geverifieer", + "Albums": "Albums" +} diff --git a/Emby.Server.Implementations/Localization/Core/de.json b/Emby.Server.Implementations/Localization/Core/de.json index 12bc16d74..019736c47 100644 --- a/Emby.Server.Implementations/Localization/Core/de.json +++ b/Emby.Server.Implementations/Localization/Core/de.json @@ -3,14 +3,14 @@ "AppDeviceValues": "App: {0}, Gerät: {1}", "Application": "Anwendung", "Artists": "Interpreten", - "AuthenticationSucceededWithUserName": "{0} hat sich angemeldet", + "AuthenticationSucceededWithUserName": "{0} hat sich erfolgreich angemeldet", "Books": "Bücher", "CameraImageUploadedFrom": "Ein neues Foto wurde hochgeladen von {0}", "Channels": "Kanäle", "ChapterNameValue": "Kapitel {0}", "Collections": "Sammlungen", "DeviceOfflineWithName": "{0} wurde getrennt", - "DeviceOnlineWithName": "{0} hat sich verbunden", + "DeviceOnlineWithName": "{0} ist verbunden", "FailedLoginAttemptWithUserName": "Fehlgeschlagener Anmeldeversuch von {0}", "Favorites": "Favoriten", "Folders": "Verzeichnisse", diff --git a/Emby.Server.Implementations/Localization/Core/he.json b/Emby.Server.Implementations/Localization/Core/he.json index 0ed998c4b..b08c8966e 100644 --- a/Emby.Server.Implementations/Localization/Core/he.json +++ b/Emby.Server.Implementations/Localization/Core/he.json @@ -1,41 +1,41 @@ { "Albums": "אלבומים", - "AppDeviceValues": "App: {0}, Device: {1}", + "AppDeviceValues": "יישום: {0}, מכשיר: {1}", "Application": "אפליקציה", "Artists": "אמנים", - "AuthenticationSucceededWithUserName": "{0} successfully authenticated", + "AuthenticationSucceededWithUserName": "{0} זוהה בהצלחה", "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": "Favorites", - "Folders": "Folders", + "CameraImageUploadedFrom": "תמונה חדשה הועלתה מ{0}", + "Channels": "ערוצים", + "ChapterNameValue": "פרק {0}", + "Collections": "קולקציות", + "DeviceOfflineWithName": "{0} התנתק", + "DeviceOnlineWithName": "{0} מחובר", + "FailedLoginAttemptWithUserName": "ניסיון כניסה שגוי מ{0}", + "Favorites": "אהובים", + "Folders": "תיקיות", "Genres": "ז'אנרים", - "HeaderAlbumArtists": "Album Artists", - "HeaderCameraUploads": "Camera Uploads", - "HeaderContinueWatching": "המשך בצפייה", - "HeaderFavoriteAlbums": "Favorite Albums", - "HeaderFavoriteArtists": "Favorite Artists", - "HeaderFavoriteEpisodes": "Favorite Episodes", - "HeaderFavoriteShows": "Favorite Shows", - "HeaderFavoriteSongs": "Favorite Songs", - "HeaderLiveTV": "Live TV", - "HeaderNextUp": "Next Up", + "HeaderAlbumArtists": "אמני האלבום", + "HeaderCameraUploads": "העלאות ממצלמה", + "HeaderContinueWatching": "המשך לצפות", + "HeaderFavoriteAlbums": "אלבומים שאהבתי", + "HeaderFavoriteArtists": "אמנים שאהבתי", + "HeaderFavoriteEpisodes": "פרקים אהובים", + "HeaderFavoriteShows": "תוכניות אהובות", + "HeaderFavoriteSongs": "שירים שאהבתי", + "HeaderLiveTV": "טלוויזיה בשידור חי", + "HeaderNextUp": "הבא", "HeaderRecordingGroups": "קבוצות הקלטה", - "HomeVideos": "Home videos", - "Inherit": "Inherit", + "HomeVideos": "סרטונים בייתים", + "Inherit": "הורש", "ItemAddedWithName": "{0} was added to the library", - "ItemRemovedWithName": "{0} was removed from the library", - "LabelIpAddressValue": "Ip address: {0}", - "LabelRunningTimeValue": "Running time: {0}", + "ItemRemovedWithName": "{0} נמחק מהספרייה", + "LabelIpAddressValue": "Ip כתובת: {0}", + "LabelRunningTimeValue": "משך צפייה: {0}", "Latest": "אחרון", - "MessageApplicationUpdated": "Jellyfin Server has been updated", - "MessageApplicationUpdatedTo": "Jellyfin Server has been updated to {0}", - "MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated", + "MessageApplicationUpdated": "שרת הJellyfin עודכן", + "MessageApplicationUpdatedTo": "שרת הJellyfin עודכן לגרסא {0}", + "MessageNamedServerConfigurationUpdatedWithValue": "הגדרת השרת {0} שונתה", "MessageServerConfigurationUpdated": "Server configuration has been updated", "MixedContent": "תוכן מעורב", "Movies": "סרטים", @@ -50,7 +50,7 @@ "NotificationOptionAudioPlayback": "Audio playback started", "NotificationOptionAudioPlaybackStopped": "Audio playback stopped", "NotificationOptionCameraImageUploaded": "Camera image uploaded", - "NotificationOptionInstallationFailed": "Installation failure", + "NotificationOptionInstallationFailed": "התקנה נכשלה", "NotificationOptionNewLibraryContent": "New content added", "NotificationOptionPluginError": "Plugin failure", "NotificationOptionPluginInstalled": "Plugin installed", diff --git a/Emby.Server.Implementations/Localization/Core/sk.json b/Emby.Server.Implementations/Localization/Core/sk.json index 6eade7942..75eecbe3e 100644 --- a/Emby.Server.Implementations/Localization/Core/sk.json +++ b/Emby.Server.Implementations/Localization/Core/sk.json @@ -5,7 +5,7 @@ "Artists": "Umelci", "AuthenticationSucceededWithUserName": "{0} úspešne overený", "Books": "Knihy", - "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}", + "CameraImageUploadedFrom": "Z {0} bola nahraná nová fotografia", "Channels": "Kanály", "ChapterNameValue": "Kapitola {0}", "Collections": "Zbierky", @@ -15,9 +15,9 @@ "Favorites": "Obľúbené", "Folders": "Priečinky", "Genres": "Žánre", - "HeaderAlbumArtists": "Album Artists", + "HeaderAlbumArtists": "Albumy umelcov", "HeaderCameraUploads": "Nahrané fotografie", - "HeaderContinueWatching": "Pokračujte v pozeraní", + "HeaderContinueWatching": "Pokračovať v pozeraní", "HeaderFavoriteAlbums": "Obľúbené albumy", "HeaderFavoriteArtists": "Obľúbení umelci", "HeaderFavoriteEpisodes": "Obľúbené epizódy", diff --git a/Emby.Server.Implementations/Localization/Core/tr.json b/Emby.Server.Implementations/Localization/Core/tr.json index 24366b070..eb1c2623f 100644 --- a/Emby.Server.Implementations/Localization/Core/tr.json +++ b/Emby.Server.Implementations/Localization/Core/tr.json @@ -25,33 +25,33 @@ "HeaderFavoriteSongs": "Favori Şarkılar", "HeaderLiveTV": "Canlı TV", "HeaderNextUp": "Sonraki hafta", - "HeaderRecordingGroups": "Recording Groups", - "HomeVideos": "Home videos", - "Inherit": "Inherit", - "ItemAddedWithName": "{0} was added to the library", - "ItemRemovedWithName": "{0} was removed from the library", - "LabelIpAddressValue": "Ip adresi: {0}", + "HeaderRecordingGroups": "Kayıt Grupları", + "HomeVideos": "Ev videoları", + "Inherit": "Devral", + "ItemAddedWithName": "{0} kütüphaneye eklendi", + "ItemRemovedWithName": "{0} kütüphaneden silindi", + "LabelIpAddressValue": "IP adresi: {0}", "LabelRunningTimeValue": "Çalışma süresi: {0}", - "Latest": "Latest", + "Latest": "En son", "MessageApplicationUpdated": "Jellyfin Sunucusu güncellendi", - "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", + "MessageApplicationUpdatedTo": "Jellyfin Sunucusu {0} olarak güncellendi", + "MessageNamedServerConfigurationUpdatedWithValue": "Sunucu ayarları kısım {0} güncellendi", + "MessageServerConfigurationUpdated": "Sunucu ayarları güncellendi", + "MixedContent": "Karışık içerik", "Movies": "Filmler", "Music": "Müzik", "MusicVideos": "Müzik videoları", - "NameInstallFailed": "{0} kurulum başarısız", + "NameInstallFailed": "{0} kurulumu başarısız", "NameSeasonNumber": "Sezon {0}", "NameSeasonUnknown": "Bilinmeyen Sezon", "NewVersionIsAvailable": "Jellyfin Sunucusunun yeni bir versiyonu indirmek için hazır.", "NotificationOptionApplicationUpdateAvailable": "Uygulama güncellemesi mevcut", "NotificationOptionApplicationUpdateInstalled": "Uygulama güncellemesi yüklendi", - "NotificationOptionAudioPlayback": "Audio playback started", - "NotificationOptionAudioPlaybackStopped": "Audio playback stopped", - "NotificationOptionCameraImageUploaded": "Camera image uploaded", + "NotificationOptionAudioPlayback": "Ses çalma başladı", + "NotificationOptionAudioPlaybackStopped": "Ses çalma durduruldu", + "NotificationOptionCameraImageUploaded": "Kamera fotoğrafı yüklendi", "NotificationOptionInstallationFailed": "Yükleme başarısız oldu", - "NotificationOptionNewLibraryContent": "New content added", + "NotificationOptionNewLibraryContent": "Yeni içerik eklendi", "NotificationOptionPluginError": "Eklenti hatası", "NotificationOptionPluginInstalled": "Eklenti yüklendi", "NotificationOptionPluginUninstalled": "Eklenti kaldırıldı", @@ -59,8 +59,8 @@ "NotificationOptionServerRestartRequired": "Sunucu yeniden başlatma gerekli", "NotificationOptionTaskFailed": "Zamanlanmış görev hatası", "NotificationOptionUserLockedOut": "Kullanıcı kitlendi", - "NotificationOptionVideoPlayback": "Video playback started", - "NotificationOptionVideoPlaybackStopped": "Video playback stopped", + "NotificationOptionVideoPlayback": "Video oynatma başladı", + "NotificationOptionVideoPlaybackStopped": "Video oynatma durduruldu", "Photos": "Fotoğraflar", "Playlists": "Çalma listeleri", "Plugin": "Eklenti", @@ -75,23 +75,23 @@ "Songs": "Şarkılar", "StartupEmbyServerIsLoading": "Jellyfin Sunucusu yükleniyor. Lütfen kısa süre sonra tekrar deneyin.", "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", - "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}", - "SubtitlesDownloadedForItem": "Subtitles downloaded for {0}", + "SubtitleDownloadFailureFromForItem": "{1} için alt yazılar {0} 'dan indirilemedi", + "SubtitlesDownloadedForItem": "{0} için altyazılar indirildi", "Sync": "Eşitle", - "System": "System", - "TvShows": "TV Shows", + "System": "Sistem", + "TvShows": "Diziler", "User": "Kullanıcı", - "UserCreatedWithName": "Kullanıcı {0} yaratıldı", + "UserCreatedWithName": "{0} kullanıcısı oluşturuldu", "UserDeletedWithName": "Kullanıcı {0} silindi", "UserDownloadingItemWithValues": "{0} indiriliyor {1}", "UserLockedOutWithName": "Kullanıcı {0} kitlendi", - "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": "Özel -{0}", - "VersionNumber": "Version {0}" + "UserOfflineFromDevice": "{0}, {1} ile bağlantısı kesildi", + "UserOnlineFromDevice": "{0}, {1} çevrimiçi", + "UserPasswordChangedWithName": "{0} kullanıcısı için şifre değiştirildi", + "UserPolicyUpdatedWithName": "Kullanıcı politikası {0} için güncellendi", + "UserStartedPlayingItemWithValues": "{0}, {2} cihazında {1} izliyor", + "UserStoppedPlayingItemWithValues": "{0}, {2} cihazında {1} izlemeyi bitirdi", + "ValueHasBeenAddedToLibrary": "Medya kitaplığınıza {0} eklendi", + "ValueSpecialEpisodeName": "Özel - {0}", + "VersionNumber": "Versiyon {0}" } diff --git a/Emby.Server.Implementations/Localization/Core/zh-HK.json b/Emby.Server.Implementations/Localization/Core/zh-HK.json index 387fc2b92..33fcb2d37 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-HK.json +++ b/Emby.Server.Implementations/Localization/Core/zh-HK.json @@ -2,7 +2,7 @@ "Albums": "Albums", "AppDeviceValues": "App: {0}, Device: {1}", "Application": "Application", - "Artists": "Artists", + "Artists": "藝人", "AuthenticationSucceededWithUserName": "{0} successfully authenticated", "Books": "Books", "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}", diff --git a/Emby.Server.Implementations/Localization/Core/zh-TW.json b/Emby.Server.Implementations/Localization/Core/zh-TW.json index 293fc28a8..33bdbfb98 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-TW.json +++ b/Emby.Server.Implementations/Localization/Core/zh-TW.json @@ -1,6 +1,6 @@ { "Albums": "專輯", - "AppDeviceValues": "應用: {0}, 裝置: {1}", + "AppDeviceValues": "軟體: {0}, 裝置: {1}", "Application": "應用程式", "Artists": "演出者", "AuthenticationSucceededWithUserName": "{0} 成功授權", diff --git a/Emby.Server.Implementations/Networking/NetworkManager.cs b/Emby.Server.Implementations/Networking/NetworkManager.cs index 0b3567986..1d8d3cf39 100644 --- a/Emby.Server.Implementations/Networking/NetworkManager.cs +++ b/Emby.Server.Implementations/Networking/NetworkManager.cs @@ -7,8 +7,6 @@ using System.Net.NetworkInformation; using System.Net.Sockets; using System.Threading.Tasks; using MediaBrowser.Common.Net; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Net; using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.Networking @@ -55,10 +53,7 @@ namespace Emby.Server.Implementations.Networking _macAddresses = null; } - if (NetworkChanged != null) - { - NetworkChanged(this, EventArgs.Empty); - } + NetworkChanged?.Invoke(this, EventArgs.Empty); } public IPAddress[] GetLocalIpAddresses(bool ignoreVirtualInterface = true) @@ -261,10 +256,10 @@ namespace Emby.Server.Implementations.Networking return true; } - if (normalizedSubnet.IndexOf('/') != -1) + if (normalizedSubnet.Contains('/', StringComparison.Ordinal)) { - var ipnetwork = IPNetwork.Parse(normalizedSubnet); - if (ipnetwork.Contains(address)) + var ipNetwork = IPNetwork.Parse(normalizedSubnet); + if (ipNetwork.Contains(address)) { return true; } @@ -455,9 +450,9 @@ namespace Emby.Server.Implementations.Networking public bool IsInSameSubnet(IPAddress address1, IPAddress address2, IPAddress subnetMask) { - IPAddress network1 = GetNetworkAddress(address1, subnetMask); - IPAddress network2 = GetNetworkAddress(address2, subnetMask); - return network1.Equals(network2); + IPAddress network1 = GetNetworkAddress(address1, subnetMask); + IPAddress network2 = GetNetworkAddress(address2, subnetMask); + return network1.Equals(network2); } private IPAddress GetNetworkAddress(IPAddress address, IPAddress subnetMask) @@ -473,7 +468,7 @@ namespace Emby.Server.Implementations.Networking byte[] broadcastAddress = new byte[ipAdressBytes.Length]; for (int i = 0; i < broadcastAddress.Length; i++) { - broadcastAddress[i] = (byte)(ipAdressBytes[i] & (subnetMaskBytes[i])); + broadcastAddress[i] = (byte)(ipAdressBytes[i] & subnetMaskBytes[i]); } return new IPAddress(broadcastAddress); @@ -513,24 +508,5 @@ namespace Emby.Server.Implementations.Networking return null; } - - /// <summary> - /// Gets the network shares. - /// </summary> - /// <param name="path">The path.</param> - /// <returns>IEnumerable{NetworkShare}.</returns> - public virtual IEnumerable<NetworkShare> GetNetworkShares(string path) - { - return new List<NetworkShare>(); - } - - /// <summary> - /// Gets available devices within the domain - /// </summary> - /// <returns>PC's in the Domain</returns> - public virtual IEnumerable<FileSystemEntryInfo> GetNetworkDevices() - { - return new List<FileSystemEntryInfo>(); - } } } diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs index fe8deae59..909fffb1f 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/PluginUpdateTask.cs @@ -52,7 +52,9 @@ namespace Emby.Server.Implementations.ScheduledTasks { progress.Report(0); - var packagesToInstall = (await _installationManager.GetAvailablePluginUpdates(cancellationToken).ConfigureAwait(false)).ToList(); + var packagesToInstall = await _installationManager.GetAvailablePluginUpdates(cancellationToken) + .ToListAsync(cancellationToken) + .ConfigureAwait(false); progress.Report(10); diff --git a/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs b/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs index 690ba0be4..1781df8b5 100644 --- a/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs +++ b/Emby.Server.Implementations/SocketSharp/WebSocketSharpRequest.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Net; +using System.Net.Mime; using MediaBrowser.Common.Net; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Extensions; @@ -14,9 +15,9 @@ namespace Emby.Server.Implementations.SocketSharp { public class WebSocketSharpRequest : IHttpRequest { - public const string FormUrlEncoded = "application/x-www-form-urlencoded"; - public const string MultiPartFormData = "multipart/form-data"; - public const string Soap11 = "text/xml; charset=utf-8"; + private const string FormUrlEncoded = "application/x-www-form-urlencoded"; + private const string MultiPartFormData = "multipart/form-data"; + private const string Soap11 = "text/xml; charset=utf-8"; private string _remoteIp; private Dictionary<string, object> _items; @@ -77,7 +78,7 @@ namespace Emby.Server.Implementations.SocketSharp get => _responseContentType ?? (_responseContentType = GetResponseContentType(Request)); - set => this._responseContentType = value; + set => _responseContentType = value; } public string PathInfo => Request.Path.Value; @@ -90,7 +91,6 @@ namespace Emby.Server.Implementations.SocketSharp public bool IsLocal => Request.HttpContext.Connection.LocalIpAddress.Equals(Request.HttpContext.Connection.RemoteIpAddress); - public string HttpMethod => Request.Method; public string Verb => HttpMethod; @@ -123,24 +123,29 @@ namespace Emby.Server.Implementations.SocketSharp return specifiedContentType; } - const string serverDefaultContentType = "application/json"; + const string ServerDefaultContentType = MediaTypeNames.Application.Json; var acceptContentTypes = httpReq.Headers.GetCommaSeparatedValues(HeaderNames.Accept); string defaultContentType = null; if (HasAnyOfContentTypes(httpReq, FormUrlEncoded, MultiPartFormData)) { - defaultContentType = serverDefaultContentType; + defaultContentType = ServerDefaultContentType; } var acceptsAnything = false; var hasDefaultContentType = defaultContentType != null; if (acceptContentTypes != null) { - foreach (var acceptsType in acceptContentTypes) + foreach (ReadOnlySpan<char> acceptsType in acceptContentTypes) { - // TODO: @bond move to Span when Span.Split lands - // https://github.com/dotnet/corefx/issues/26528 - var contentType = acceptsType?.Split(';')[0].Trim(); + ReadOnlySpan<char> contentType = acceptsType; + var index = contentType.IndexOf(';'); + if (index != -1) + { + contentType = contentType.Slice(0, index); + } + + contentType = contentType.Trim(); acceptsAnything = contentType.Equals("*/*", StringComparison.OrdinalIgnoreCase); if (acceptsAnything) @@ -157,7 +162,7 @@ namespace Emby.Server.Implementations.SocketSharp } else { - return serverDefaultContentType; + return ServerDefaultContentType; } } } @@ -168,7 +173,7 @@ namespace Emby.Server.Implementations.SocketSharp } // We could also send a '406 Not Acceptable', but this is allowed also - return serverDefaultContentType; + return ServerDefaultContentType; } public static bool HasAnyOfContentTypes(HttpRequest request, params string[] contentTypes) @@ -196,12 +201,12 @@ namespace Emby.Server.Implementations.SocketSharp private static string GetQueryStringContentType(HttpRequest httpReq) { - ReadOnlySpan<char> format = httpReq.Query["format"].ToString().AsSpan(); + ReadOnlySpan<char> format = httpReq.Query["format"].ToString(); if (format == null) { - const int formatMaxLength = 4; - ReadOnlySpan<char> pi = httpReq.Path.ToString().AsSpan(); - if (pi == null || pi.Length <= formatMaxLength) + const int FormatMaxLength = 4; + ReadOnlySpan<char> pi = httpReq.Path.ToString(); + if (pi == null || pi.Length <= FormatMaxLength) { return null; } @@ -212,18 +217,18 @@ namespace Emby.Server.Implementations.SocketSharp } format = LeftPart(pi, '/'); - if (format.Length > formatMaxLength) + if (format.Length > FormatMaxLength) { return null; } } format = LeftPart(format, '.'); - if (format.Contains("json".AsSpan(), StringComparison.OrdinalIgnoreCase)) + if (format.Contains("json", StringComparison.OrdinalIgnoreCase)) { return "application/json"; } - else if (format.Contains("xml".AsSpan(), StringComparison.OrdinalIgnoreCase)) + else if (format.Contains("xml", StringComparison.OrdinalIgnoreCase)) { return "application/xml"; } diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs index 1c5402268..09a5a0dca 100644 --- a/Emby.Server.Implementations/Updates/InstallationManager.cs +++ b/Emby.Server.Implementations/Updates/InstallationManager.cs @@ -180,7 +180,7 @@ namespace Emby.Server.Implementations.Updates // Package not found. if (package == null) { - return null; + return Enumerable.Empty<PackageVersionInfo>(); } return GetCompatibleVersions( @@ -190,19 +190,23 @@ namespace Emby.Server.Implementations.Updates } /// <inheritdoc /> - public async Task<IEnumerable<PackageVersionInfo>> GetAvailablePluginUpdates(CancellationToken cancellationToken = default) + public async IAsyncEnumerable<PackageVersionInfo> GetAvailablePluginUpdates(CancellationToken cancellationToken = default) { var catalog = await GetAvailablePackages(cancellationToken).ConfigureAwait(false); var systemUpdateLevel = _applicationHost.SystemUpdateLevel; // Figure out what needs to be installed - return _applicationHost.Plugins.Select(x => + foreach (var plugin in _applicationHost.Plugins) { - var compatibleversions = GetCompatibleVersions(catalog, x.Name, x.Id, x.Version, systemUpdateLevel); - return compatibleversions.FirstOrDefault(y => y.Version > x.Version); - }).Where(x => x != null) - .Where(x => !CompletedInstallations.Any(y => string.Equals(y.AssemblyGuid, x.guid, StringComparison.OrdinalIgnoreCase))); + var compatibleversions = GetCompatibleVersions(catalog, plugin.Name, plugin.Id, plugin.Version, systemUpdateLevel); + var version = compatibleversions.FirstOrDefault(y => y.Version > plugin.Version); + if (version != null + && !CompletedInstallations.Any(x => string.Equals(x.AssemblyGuid, version.guid, StringComparison.OrdinalIgnoreCase))) + { + yield return version; + } + } } /// <inheritdoc /> diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index 7d97a1f20..110028176 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -46,6 +46,7 @@ <PackageReference Include="Serilog.Sinks.Async" Version="1.4.0" /> <PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" /> <PackageReference Include="Serilog.Sinks.File" Version="4.1.0" /> + <PackageReference Include="Serilog.Sinks.Graylog" Version="2.1.1" /> <PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.0.2" /> <PackageReference Include="SQLitePCLRaw.provider.sqlite3.netstandard11" Version="1.1.14" /> </ItemGroup> diff --git a/Jellyfin.Server/Resources/Configuration/logging.json b/Jellyfin.Server/Resources/Configuration/logging.json index d16991277..e85ef05af 100644 --- a/Jellyfin.Server/Resources/Configuration/logging.json +++ b/Jellyfin.Server/Resources/Configuration/logging.json @@ -17,6 +17,9 @@ "Args": { "path": "%JELLYFIN_LOG_DIR%//log_.log", "rollingInterval": "Day", + "retainedFileCountLimit": 3, + "rollOnFileSizeLimit": true, + "fileSizeLimitBytes": 100000000, "outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] {Message}{NewLine}{Exception}" } } diff --git a/MediaBrowser.Api/EnvironmentService.cs b/MediaBrowser.Api/EnvironmentService.cs index f4813e713..a63ef9f05 100644 --- a/MediaBrowser.Api/EnvironmentService.cs +++ b/MediaBrowser.Api/EnvironmentService.cs @@ -52,6 +52,7 @@ namespace MediaBrowser.Api public bool? IsFile { get; set; } } + [Obsolete] [Route("/Environment/NetworkShares", "GET", Summary = "Gets shares from a network device")] public class GetNetworkShares : IReturn<List<FileSystemEntryInfo>> { @@ -192,22 +193,18 @@ namespace MediaBrowser.Api var networkPrefix = UncSeparatorString + UncSeparatorString; - if (path.StartsWith(networkPrefix, StringComparison.OrdinalIgnoreCase) && path.LastIndexOf(UncSeparator) == 1) + if (path.StartsWith(networkPrefix, StringComparison.OrdinalIgnoreCase) + && path.LastIndexOf(UncSeparator) == 1) { - return ToOptimizedResult(GetNetworkShares(path).OrderBy(i => i.Path).ToList()); + return ToOptimizedResult(Array.Empty<FileSystemEntryInfo>()); } return ToOptimizedResult(GetFileSystemEntries(request).ToList()); } + [Obsolete] public object Get(GetNetworkShares request) - { - var path = request.Path; - - var shares = GetNetworkShares(path).OrderBy(i => i.Path).ToList(); - - return ToOptimizedResult(shares); - } + => ToOptimizedResult(Array.Empty<FileSystemEntryInfo>()); /// <summary> /// Gets the specified request. @@ -241,26 +238,7 @@ namespace MediaBrowser.Api /// <param name="request">The request.</param> /// <returns>System.Object.</returns> public object Get(GetNetworkDevices request) - { - var result = _networkManager.GetNetworkDevices().ToList(); - - return ToOptimizedResult(result); - } - - /// <summary> - /// Gets the network shares. - /// </summary> - /// <param name="path">The path.</param> - /// <returns>IEnumerable{FileSystemEntryInfo}.</returns> - private IEnumerable<FileSystemEntryInfo> GetNetworkShares(string path) - { - return _networkManager.GetNetworkShares(path).Where(s => s.ShareType == NetworkShareType.Disk).Select(c => new FileSystemEntryInfo - { - Name = c.Name, - Path = Path.Combine(path, c.Name), - Type = FileSystemEntryType.NetworkShare - }); - } + => ToOptimizedResult(Array.Empty<FileSystemEntryInfo>()); /// <summary> /// Gets the file system entries. diff --git a/MediaBrowser.Common/Net/INetworkManager.cs b/MediaBrowser.Common/Net/INetworkManager.cs index 97504a471..0b99dc910 100644 --- a/MediaBrowser.Common/Net/INetworkManager.cs +++ b/MediaBrowser.Common/Net/INetworkManager.cs @@ -4,8 +4,6 @@ using System; using System.Collections.Generic; using System.Net; using System.Net.NetworkInformation; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Net; namespace MediaBrowser.Common.Net { @@ -37,19 +35,6 @@ namespace MediaBrowser.Common.Net bool IsInPrivateAddressSpace(string endpoint); /// <summary> - /// Gets the network shares. - /// </summary> - /// <param name="path">The path.</param> - /// <returns>IEnumerable{NetworkShare}.</returns> - IEnumerable<NetworkShare> GetNetworkShares(string path); - - /// <summary> - /// Gets available devices within the domain - /// </summary> - /// <returns>PC's in the Domain</returns> - IEnumerable<FileSystemEntryInfo> GetNetworkDevices(); - - /// <summary> /// Determines whether [is in local network] [the specified endpoint]. /// </summary> /// <param name="endpoint">The endpoint.</param> diff --git a/MediaBrowser.Common/Updates/IInstallationManager.cs b/MediaBrowser.Common/Updates/IInstallationManager.cs index 524d8f3c6..e49812f15 100644 --- a/MediaBrowser.Common/Updates/IInstallationManager.cs +++ b/MediaBrowser.Common/Updates/IInstallationManager.cs @@ -92,7 +92,7 @@ namespace MediaBrowser.Common.Updates /// </summary> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>The available plugin updates.</returns> - Task<IEnumerable<PackageVersionInfo>> GetAvailablePluginUpdates(CancellationToken cancellationToken = default); + IAsyncEnumerable<PackageVersionInfo> GetAvailablePluginUpdates(CancellationToken cancellationToken = default); /// <summary> /// Installs the package. diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 349e371a7..0664bdd98 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -2529,13 +2529,25 @@ namespace MediaBrowser.Controller.MediaEncoding case "h264": if (_mediaEncoder.SupportsDecoder("h264_mmal") && encodingOptions.HardwareDecodingCodecs.Contains("h264", StringComparer.OrdinalIgnoreCase)) { - return "-c:v h264_mmal"; + return "-c:v h264_mmal "; } break; case "mpeg2video": if (_mediaEncoder.SupportsDecoder("mpeg2_mmal") && encodingOptions.HardwareDecodingCodecs.Contains("mpeg2video", StringComparer.OrdinalIgnoreCase)) { - return "-c:v mpeg2_mmal"; + return "-c:v mpeg2_mmal "; + } + break; + case "mpeg4": + if (_mediaEncoder.SupportsDecoder("mpeg4_mmal") && encodingOptions.HardwareDecodingCodecs.Contains("mpeg4", StringComparer.OrdinalIgnoreCase)) + { + return "-c:v mpeg4_mmal "; + } + break; + case "vc1": + if (_mediaEncoder.SupportsDecoder("vc1_mmal") && encodingOptions.HardwareDecodingCodecs.Contains("vc1", StringComparer.OrdinalIgnoreCase)) + { + return "-c:v vc1_mmal "; } break; } diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index 3620abfee..1feca0ec9 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -18,7 +18,10 @@ namespace MediaBrowser.MediaEncoding.Encoder "h264_qsv", "hevc_qsv", "mpeg2_qsv", + "mpeg2_mmal", + "mpeg4_mmal", "vc1_qsv", + "vc1_mmal", "h264_cuvid", "hevc_cuvid", "dts", @@ -26,6 +29,7 @@ namespace MediaBrowser.MediaEncoding.Encoder "aac", "mp3", "h264", + "h264_mmal", "hevc" }; diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs index eaebc13e3..fc7f12b1a 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs @@ -57,7 +57,8 @@ namespace MediaBrowser.Providers.TV.TheTVDB { IndexNumber = episode.IndexNumber.Value, ParentIndexNumber = episode.ParentIndexNumber.Value, - SeriesProviderIds = series.ProviderIds + SeriesProviderIds = series.ProviderIds, + SeriesDisplayOrder = series.DisplayOrder }; string episodeTvdbId = await _tvDbClientManager .GetEpisodeTvdbId(episodeInfo, language, cancellationToken).ConfigureAwait(false); |
