From d7f0596d5dcc6c6eddee05dbddd1d5e493c3580d Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Fri, 11 Nov 2022 08:32:29 -0700 Subject: Don't auto-update if plugin is pending restart --- Emby.Server.Implementations/Plugins/PluginManager.cs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Plugins/PluginManager.cs b/Emby.Server.Implementations/Plugins/PluginManager.cs index ec4e0dbeb9..3f7d46822e 100644 --- a/Emby.Server.Implementations/Plugins/PluginManager.cs +++ b/Emby.Server.Implementations/Plugins/PluginManager.cs @@ -715,6 +715,7 @@ namespace Emby.Server.Implementations.Plugins { // This value is memory only - so that the web will show restart required. plugin.Manifest.Status = PluginStatus.Restart; + plugin.Manifest.AutoUpdate = false; return; } @@ -729,6 +730,7 @@ namespace Emby.Server.Implementations.Plugins // This value is memory only - so that the web will show restart required. plugin.Manifest.Status = PluginStatus.Restart; + plugin.Manifest.AutoUpdate = false; } } } -- cgit v1.2.3 From cf060ee6643279473af93012e5be056cc852ba9a Mon Sep 17 00:00:00 2001 From: TheBlueKingLP <12997043+TheBlueKingLP@users.noreply.github.com> Date: Sun, 13 Nov 2022 00:53:38 +0900 Subject: Correcting LocalizationOption MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changing from 体(the simplified variant) to 體(the traditional variant) in the LocalizationOption for the "Traditional Chinese" language. --- Emby.Server.Implementations/Localization/LocalizationManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs index 22b283b8a0..4eab040a4a 100644 --- a/Emby.Server.Implementations/Localization/LocalizationManager.cs +++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs @@ -435,7 +435,7 @@ namespace Emby.Server.Implementations.Localization yield return new LocalizationOption("اُردُو", "ur_PK"); yield return new LocalizationOption("Tiếng Việt", "vi"); yield return new LocalizationOption("汉语 (简化字)", "zh-CN"); - yield return new LocalizationOption("漢語 (繁体字)", "zh-TW"); + yield return new LocalizationOption("漢語 (繁體字)", "zh-TW"); yield return new LocalizationOption("廣東話 (香港)", "zh-HK"); } } -- cgit v1.2.3 From 9c06001aee93c289fbf6871f74f4d72266ad6ddb Mon Sep 17 00:00:00 2001 From: TheBlueKingLP <12997043+TheBlueKingLP@users.noreply.github.com> Date: Tue, 15 Nov 2022 06:39:21 +0300 Subject: Change the Translation of "Simplified Chinese" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change the translation of "Simplified Chinese" from "汉语 (简化字)" to "汉语 (简体字)" --- Emby.Server.Implementations/Localization/LocalizationManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs index 4eab040a4a..b771681266 100644 --- a/Emby.Server.Implementations/Localization/LocalizationManager.cs +++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs @@ -434,7 +434,7 @@ namespace Emby.Server.Implementations.Localization yield return new LocalizationOption("Українська", "uk"); yield return new LocalizationOption("اُردُو", "ur_PK"); yield return new LocalizationOption("Tiếng Việt", "vi"); - yield return new LocalizationOption("汉语 (简化字)", "zh-CN"); + yield return new LocalizationOption("汉语 (简体字)", "zh-CN"); yield return new LocalizationOption("漢語 (繁體字)", "zh-TW"); yield return new LocalizationOption("廣東話 (香港)", "zh-HK"); } -- cgit v1.2.3 From 712a3b006397bcf1789c21bd9c9dc2e744ffac92 Mon Sep 17 00:00:00 2001 From: Tom Date: Mon, 14 Nov 2022 11:34:38 +0000 Subject: Translated using Weblate (Lithuanian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/lt/ --- Emby.Server.Implementations/Localization/Core/lt-LT.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/lt-LT.json b/Emby.Server.Implementations/Localization/Core/lt-LT.json index 232b3ec934..e1c937b6cd 100644 --- a/Emby.Server.Implementations/Localization/Core/lt-LT.json +++ b/Emby.Server.Implementations/Localization/Core/lt-LT.json @@ -123,5 +123,6 @@ "TaskKeyframeExtractorDescription": "Iš vaizdo įrašo paruošia reikšminius kadrus, kad būtų sukuriamas tikslenis HLS grojaraštis. Šios užduoties vykdymas gali ilgai užtrukti.", "TaskKeyframeExtractor": "Pagrindinių kadrų ištraukėjas", "TaskOptimizeDatabaseDescription": "Suspaudžia duomenų bazę ir atlaisvina vietą. Paleidžiant šią užduotį, po bibliotekos skenavimo arba kitų veiksmų kurie galimai modifikuoja duomenų bazė, gali pagerinti greitaveiką.", - "External": "Išorinis" + "External": "Išorinis", + "HearingImpaired": "Su klausos sutrikimais" } -- cgit v1.2.3 From 74e54825ed0397da4a2110894ed520b46de4fa84 Mon Sep 17 00:00:00 2001 From: 5h4d Date: Mon, 14 Nov 2022 15:24:55 +0000 Subject: Translated using Weblate (Slovak) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/sk/ --- Emby.Server.Implementations/Localization/Core/sk.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/sk.json b/Emby.Server.Implementations/Localization/Core/sk.json index 7502969a64..858cc40dd8 100644 --- a/Emby.Server.Implementations/Localization/Core/sk.json +++ b/Emby.Server.Implementations/Localization/Core/sk.json @@ -123,5 +123,6 @@ "TaskOptimizeDatabase": "Optimalizovať databázu", "TaskKeyframeExtractorDescription": "Extrahuje kľúčové snímky z video súborov na vytvorenie presnejších HLS playlistov. Táto úloha môže trvať dlhšiu dobu.", "TaskKeyframeExtractor": "Extraktor kľúčových snímkov", - "External": "Externé" + "External": "Externé", + "HearingImpaired": "Sluchovo Postihnutý" } -- cgit v1.2.3 From ce7a542c1f30b056cb72028a40b7c2924790784a Mon Sep 17 00:00:00 2001 From: Pavel Petrescu Date: Sun, 13 Nov 2022 11:12:17 +0000 Subject: Translated using Weblate (Romanian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ro/ --- .../Localization/Core/ro.json | 47 +++++++++++----------- 1 file changed, 24 insertions(+), 23 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ro.json b/Emby.Server.Implementations/Localization/Core/ro.json index 53456269af..2c10bb4779 100644 --- a/Emby.Server.Implementations/Localization/Core/ro.json +++ b/Emby.Server.Implementations/Localization/Core/ro.json @@ -11,7 +11,7 @@ "UserOfflineFromDevice": "{0} s-a deconectat de la {1}", "UserLockedOutWithName": "Utilizatorul {0} a fost blocat", "UserDownloadingItemWithValues": "{0} descarcă {1}", - "UserDeletedWithName": "Utilizatorul {0} a fost eliminat", + "UserDeletedWithName": "Utilizatorul {0} a fost șters", "UserCreatedWithName": "Utilizatorul {0} a fost creat", "User": "Utilizator", "TvShows": "Seriale TV", @@ -20,33 +20,33 @@ "SubtitleDownloadFailureFromForItem": "Subtitrările nu au putut fi descărcate de la {0} pentru {1}", "StartupEmbyServerIsLoading": "Se încarcă serverul Jellyfin. Încercați din nou în scurt timp.", "Songs": "Melodii", - "Shows": "Spectacole", - "ServerNameNeedsToBeRestarted": "{0} trebuie repornit", + "Shows": "Seriale", + "ServerNameNeedsToBeRestarted": "{0} trebuie să fie repornit", "ScheduledTaskStartedWithName": "{0} pornit/ă", "ScheduledTaskFailedWithName": "{0} eșuat/ă", "ProviderValue": "Furnizor: {0}", "PluginUpdatedWithName": "{0} a fost actualizat/ă", "PluginUninstalledWithName": "{0} a fost dezinstalat", "PluginInstalledWithName": "{0} a fost instalat", - "Plugin": "Plugin", - "Playlists": "Liste redare", + "Plugin": "Extensie", + "Playlists": "Liste de redare", "Photos": "Fotografii", "NotificationOptionVideoPlaybackStopped": "Redarea video oprită", "NotificationOptionVideoPlayback": "Redare video începută", "NotificationOptionUserLockedOut": "Utilizatorul a fost blocat", - "NotificationOptionTaskFailed": "Activitate programata eșuată", + "NotificationOptionTaskFailed": "Activitate programată eșuată", "NotificationOptionServerRestartRequired": "Este necesară repornirea serverului", - "NotificationOptionPluginUpdateInstalled": "Actualizare plugin instalată", - "NotificationOptionPluginUninstalled": "Plugin dezinstalat", - "NotificationOptionPluginInstalled": "Plugin instalat", - "NotificationOptionPluginError": "Plugin-ul a eșuat", - "NotificationOptionNewLibraryContent": "Adăugat conținut nou", - "NotificationOptionInstallationFailed": "Eșec la instalare", - "NotificationOptionCameraImageUploaded": "Încarcată imagine cameră", + "NotificationOptionPluginUpdateInstalled": "Actualizarea extensiei este instalată", + "NotificationOptionPluginUninstalled": "Extensie dezinstalată", + "NotificationOptionPluginInstalled": "Extensie instalată", + "NotificationOptionPluginError": "Eroare de extensie", + "NotificationOptionNewLibraryContent": "A fost adăugat conținut nou", + "NotificationOptionInstallationFailed": "Instalare eșuată", + "NotificationOptionCameraImageUploaded": "Imagine încarcată", "NotificationOptionAudioPlaybackStopped": "Redare audio oprită", "NotificationOptionAudioPlayback": "A început redarea audio", "NotificationOptionApplicationUpdateInstalled": "Actualizarea aplicației a fost instalată", - "NotificationOptionApplicationUpdateAvailable": "Disponibilă o actualizare a aplicației", + "NotificationOptionApplicationUpdateAvailable": "Este disponibilă o actualizare a aplicației", "NewVersionIsAvailable": "O nouă versiune a Jellyfin Server este disponibilă pentru descărcare.", "NameSeasonUnknown": "Sezon Necunoscut", "NameSeasonNumber": "Sezonul {0}", @@ -54,8 +54,8 @@ "MusicVideos": "Videoclipuri muzicale", "Music": "Muzică", "Movies": "Filme", - "MixedContent": "Conținut mixt", - "MessageServerConfigurationUpdated": "Configurația serverului a fost actualizată", + "MixedContent": "Conținut amestecat", + "MessageServerConfigurationUpdated": "Configurarea serverului a fost actualizată", "MessageNamedServerConfigurationUpdatedWithValue": "Secțiunea de configurare a serverului {0} a fost acualizata", "MessageApplicationUpdatedTo": "Jellyfin Server a fost actualizat la {0}", "MessageApplicationUpdated": "Jellyfin Server a fost actualizat", @@ -69,7 +69,7 @@ "HeaderRecordingGroups": "Grupuri de înregistrare", "HeaderLiveTV": "TV în Direct", "HeaderFavoriteSongs": "Melodii Favorite", - "HeaderFavoriteShows": "Spectacole Favorite", + "HeaderFavoriteShows": "Seriale TV Favorite", "HeaderFavoriteEpisodes": "Episoade Favorite", "HeaderFavoriteArtists": "Artiști Favoriți", "HeaderFavoriteAlbums": "Albume Favorite", @@ -97,10 +97,10 @@ "TaskRefreshChannels": "Actualizează canale", "TaskCleanTranscodeDescription": "Șterge fișierele de transcodare mai vechi de o zi.", "TaskCleanTranscode": "Curățați directorul de transcodare", - "TaskUpdatePluginsDescription": "Descarcă și instalează actualizări pentru pluginuri care sunt configurate să se actualizeze automat.", - "TaskUpdatePlugins": "Actualizați plugin-uri", + "TaskUpdatePluginsDescription": "Descarcă și instalează actualizări pentru extensiile care sunt configurate să se actualizeze automat.", + "TaskUpdatePlugins": "Actualizați Extensile", "TaskRefreshPeopleDescription": "Actualizează metadatele pentru actori și regizori din biblioteca media.", - "TaskRefreshPeople": "Actualizează oamenii", + "TaskRefreshPeople": "Actualizează Persoanele", "TaskCleanLogsDescription": "Șterge fișierele jurnal care au mai mult de {0} zile.", "TaskCleanLogs": "Curățare director jurnal", "TaskRefreshLibraryDescription": "Scanează biblioteca media pentru fișiere noi și reîmprospătează metadatele.", @@ -114,13 +114,14 @@ "TasksLibraryCategory": "Librărie", "TasksMaintenanceCategory": "Mentenanță", "TaskCleanActivityLogDescription": "Șterge intrările din jurnalul de activitate mai vechi de data configurată.", - "TaskCleanActivityLog": "Curăță Jurnalul de Activitate", + "TaskCleanActivityLog": "Curăță Jurnalul de Activități", "Undefined": "Nedefinit", "Forced": "Forțat", "Default": "Implicit", - "TaskOptimizeDatabaseDescription": "Compactează baza de date și trunchiază spațiul liber. Rularea acestei sarcini după scanarea bibliotecii sau după efectuarea altor modificări care implică modificări ale bazei de date poate îmbunătăți performanța.", + "TaskOptimizeDatabaseDescription": "Comprimă baza de date și trunchiază spațiul liber. Rularea acestei sarcini după scanarea bibliotecii sau după efectuarea altor modificări care implică modificări ale bazei de date poate îmbunătăți performanța.", "TaskOptimizeDatabase": "Optimizează baza de date", "TaskKeyframeExtractorDescription": "Extrage cadrele cheie din fișierele video pentru a crea liste de redare HLS mai precise. Această sarcină poate rula o perioadă lungă de timp.", "External": "Extern", - "TaskKeyframeExtractor": "Extractor de cadre cheie" + "TaskKeyframeExtractor": "Extractor de cadre cheie", + "HearingImpaired": "Ascultare Impară" } -- cgit v1.2.3 From 469b01e18e0a1aed92de9032d20e0afb76714dcb Mon Sep 17 00:00:00 2001 From: hoanghuy309 Date: Sun, 13 Nov 2022 10:40:22 +0000 Subject: Translated using Weblate (Vietnamese) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/vi/ --- Emby.Server.Implementations/Localization/Core/vi.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/vi.json b/Emby.Server.Implementations/Localization/Core/vi.json index b9e2f1e6c2..44ce4ac5b2 100644 --- a/Emby.Server.Implementations/Localization/Core/vi.json +++ b/Emby.Server.Implementations/Localization/Core/vi.json @@ -122,5 +122,6 @@ "TaskOptimizeDatabase": "Tối ưu hóa cơ sở dữ liệu", "TaskKeyframeExtractor": "Trích Xuất Khung Hình", "TaskKeyframeExtractorDescription": "Trích xuất khung hình chính từ các tệp video để tạo danh sách phát HLS chính xác hơn. Tác vụ này có thể chạy trong một thời gian dài.", - "External": "Bên ngoài" + "External": "Bên ngoài", + "HearingImpaired": "Khiếm Thính" } -- cgit v1.2.3 From 072651c4be3914f0ffb5e0be8f57e714d4303fe1 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Fri, 15 Apr 2022 19:27:38 +0200 Subject: Add xmldocs for TMDb provider, correct provider spelling --- .../Library/LibraryManager.cs | 4 +- .../Library/Resolvers/Movies/MovieResolver.cs | 4 +- Jellyfin.Api/Controllers/ItemsController.cs | 16 +++---- Jellyfin.Api/Controllers/MoviesController.cs | 20 ++++----- Jellyfin.Api/Controllers/TrailersController.cs | 8 ++-- MediaBrowser.Common/Providers/ProviderIdParsers.cs | 4 +- MediaBrowser.Controller/Entities/Movies/Movie.cs | 4 +- MediaBrowser.Controller/Providers/IHasOrder.cs | 9 +++- .../Providers/IRemoteMetadataProvider.cs | 23 +++++++++- MediaBrowser.Model/Entities/MetadataProvider.cs | 52 +++++++++++++++++++--- MediaBrowser.Model/Querying/ItemFields.cs | 2 +- MediaBrowser.Providers/Manager/ProviderManager.cs | 2 +- .../Plugins/Tmdb/Api/TmdbController.cs | 2 +- .../Plugins/Tmdb/BoxSets/TmdbBoxSetExternalId.cs | 2 +- .../Tmdb/BoxSets/TmdbBoxSetImageProvider.cs | 16 ++++++- .../Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs | 15 ++++++- .../Plugins/Tmdb/Movies/TmdbMovieExternalId.cs | 2 +- .../Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs | 16 ++++++- .../Plugins/Tmdb/Movies/TmdbMovieProvider.cs | 19 +++++--- .../Plugins/Tmdb/People/TmdbPersonExternalId.cs | 2 +- .../Plugins/Tmdb/People/TmdbPersonImageProvider.cs | 14 +++++- .../Plugins/Tmdb/People/TmdbPersonProvider.cs | 14 +++++- .../Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs | 27 +++++++---- .../Plugins/Tmdb/TV/TmdbEpisodeProvider.cs | 16 +++++-- .../Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs | 45 +++++++++++++------ .../Plugins/Tmdb/TV/TmdbSeasonProvider.cs | 14 +++++- .../Plugins/Tmdb/TV/TmdbSeriesExternalId.cs | 2 +- .../Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs | 17 +++++-- .../Plugins/Tmdb/TV/TmdbSeriesProvider.cs | 17 +++++-- MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs | 23 +++++----- MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs | 22 ++++----- 31 files changed, 314 insertions(+), 119 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index cef82ebbcc..b688af5286 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -2590,9 +2590,9 @@ namespace Emby.Server.Implementations.Library { /* Anime series don't generally have a season in their file name, however, - tvdb needs a season to correctly get the metadata. + TVDb needs a season to correctly get the metadata. Hence, a null season needs to be filled with something. */ - // FIXME perhaps this would be better for tvdb parser to ask for season 1 if no season is specified + // FIXME perhaps this would be better for TVDb parser to ask for season 1 if no season is specified episode.ParentIndexNumber = 1; } diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs index 8f9e5f01b1..d6ae8aba85 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs @@ -376,7 +376,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies if (!justName.IsEmpty) { - // check for tmdb id + // Check for TMDb id var tmdbid = justName.GetAttributeValue("tmdbid"); if (!string.IsNullOrWhiteSpace(tmdbid)) @@ -387,7 +387,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies if (!string.IsNullOrEmpty(item.Path)) { - // check for imdb id - we use full media path, as we can assume, that this will match in any use case (either id in parent dir or in file name) + // Check for IMDb id - we use full media path, as we can assume that this will match in any use case (wither id in parent dir or in file name) var imdbid = item.Path.AsSpan().GetAttributeValue("imdbid"); if (!string.IsNullOrWhiteSpace(imdbid)) diff --git a/Jellyfin.Api/Controllers/ItemsController.cs b/Jellyfin.Api/Controllers/ItemsController.cs index 33b67b3898..3ee5b8d737 100644 --- a/Jellyfin.Api/Controllers/ItemsController.cs +++ b/Jellyfin.Api/Controllers/ItemsController.cs @@ -87,9 +87,9 @@ namespace Jellyfin.Api.Controllers /// Optional. The minimum last saved date for the current user. Format = ISO. /// Optional. The maximum premiere date. Format = ISO. /// Optional filter by items that have an overview or not. - /// Optional filter by items that have an imdb id or not. - /// Optional filter by items that have a tmdb id or not. - /// Optional filter by items that have a tvdb id or not. + /// Optional filter by items that have an IMDb id or not. + /// Optional filter by items that have a TMDb id or not. + /// Optional filter by items that have a TVDb id or not. /// Optional filter for live tv movies. /// Optional filter for live tv series. /// Optional filter for live tv news. @@ -100,7 +100,7 @@ namespace Jellyfin.Api.Controllers /// Optional. The maximum number of records to return. /// When searching within folders, this determines whether or not the search will be recursive. true/false. /// Optional. Filter based on a search term. - /// Sort Order - Ascending,Descending. + /// Sort Order - Ascending, Descending. /// Specify this to localize the search to a specific item or folder. Omit to use the root. /// Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimited. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines. /// Optional. If specified, results will be filtered based on item type. This allows multiple, comma delimited. @@ -536,9 +536,9 @@ namespace Jellyfin.Api.Controllers /// Optional. The minimum last saved date for the current user. Format = ISO. /// Optional. The maximum premiere date. Format = ISO. /// Optional filter by items that have an overview or not. - /// Optional filter by items that have an imdb id or not. - /// Optional filter by items that have a tmdb id or not. - /// Optional filter by items that have a tvdb id or not. + /// Optional filter by items that have an IMDb id or not. + /// Optional filter by items that have a TMDb id or not. + /// Optional filter by items that have a TVDb id or not. /// Optional filter for live tv movies. /// Optional filter for live tv series. /// Optional filter for live tv news. @@ -549,7 +549,7 @@ namespace Jellyfin.Api.Controllers /// Optional. The maximum number of records to return. /// When searching within folders, this determines whether or not the search will be recursive. true/false. /// Optional. Filter based on a search term. - /// Sort Order - Ascending,Descending. + /// Sort Order - Ascending, Descending. /// Specify this to localize the search to a specific item or folder. Omit to use the root. /// Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimited. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines. /// Optional. If specified, results will be filtered based on item type. This allows multiple, comma delimited. diff --git a/Jellyfin.Api/Controllers/MoviesController.cs b/Jellyfin.Api/Controllers/MoviesController.cs index 8195fc7609..03f864b4a7 100644 --- a/Jellyfin.Api/Controllers/MoviesController.cs +++ b/Jellyfin.Api/Controllers/MoviesController.cs @@ -193,7 +193,7 @@ namespace Jellyfin.Api.Controllers new InternalItemsQuery(user) { Person = name, - // Account for duplicates by imdb id, since the database doesn't support this yet + // Account for duplicates by IMDb id, since the database doesn't support this yet Limit = itemLimit + 2, PersonTypes = new[] { PersonType.Director }, IncludeItemTypes = itemTypes.ToArray(), @@ -232,15 +232,15 @@ namespace Jellyfin.Api.Controllers foreach (var name in names) { var items = _libraryManager.GetItemList(new InternalItemsQuery(user) - { - Person = name, - // Account for duplicates by imdb id, since the database doesn't support this yet - Limit = itemLimit + 2, - IncludeItemTypes = itemTypes.ToArray(), - IsMovie = true, - EnableGroupByMetadataKey = true, - DtoOptions = dtoOptions - }).GroupBy(i => i.GetProviderId(MediaBrowser.Model.Entities.MetadataProvider.Imdb) ?? Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture)) + { + Person = name, + // Account for duplicates by IMDb id, since the database doesn't support this yet + Limit = itemLimit + 2, + IncludeItemTypes = itemTypes.ToArray(), + IsMovie = true, + EnableGroupByMetadataKey = true, + DtoOptions = dtoOptions + }).GroupBy(i => i.GetProviderId(MediaBrowser.Model.Entities.MetadataProvider.Imdb) ?? Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture)) .Select(x => x.First()) .Take(itemLimit) .ToList(); diff --git a/Jellyfin.Api/Controllers/TrailersController.cs b/Jellyfin.Api/Controllers/TrailersController.cs index b296d1c960..53a839e431 100644 --- a/Jellyfin.Api/Controllers/TrailersController.cs +++ b/Jellyfin.Api/Controllers/TrailersController.cs @@ -55,9 +55,9 @@ namespace Jellyfin.Api.Controllers /// Optional. The minimum last saved date for the current user. Format = ISO. /// Optional. The maximum premiere date. Format = ISO. /// Optional filter by items that have an overview or not. - /// Optional filter by items that have an imdb id or not. - /// Optional filter by items that have a tmdb id or not. - /// Optional filter by items that have a tvdb id or not. + /// Optional filter by items that have an IMDb id or not. + /// Optional filter by items that have a TMDb id or not. + /// Optional filter by items that have a TVDb id or not. /// Optional filter for live tv movies. /// Optional filter for live tv series. /// Optional filter for live tv news. @@ -68,7 +68,7 @@ namespace Jellyfin.Api.Controllers /// Optional. The maximum number of records to return. /// When searching within folders, this determines whether or not the search will be recursive. true/false. /// Optional. Filter based on a search term. - /// Sort Order - Ascending,Descending. + /// Sort Order - Ascending, Descending. /// Specify this to localize the search to a specific item or folder. Omit to use the root. /// Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimited. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines. /// Optional. If specified, results will be filtered based on item type. This allows multiple, comma delimited. diff --git a/MediaBrowser.Common/Providers/ProviderIdParsers.cs b/MediaBrowser.Common/Providers/ProviderIdParsers.cs index 487b5a6d29..d569167b1d 100644 --- a/MediaBrowser.Common/Providers/ProviderIdParsers.cs +++ b/MediaBrowser.Common/Providers/ProviderIdParsers.cs @@ -20,7 +20,7 @@ namespace MediaBrowser.Common.Providers /// True if parsing was successful, false otherwise. public static bool TryFindImdbId(ReadOnlySpan text, out ReadOnlySpan imdbId) { - // imdb id is at least 9 chars (tt + 7 numbers) + // IMDb id is at least 9 chars (tt + 7 numbers) while (text.Length >= 2 + ImdbMinNumbers) { var ttPos = text.IndexOf(ImdbPrefix); @@ -42,7 +42,7 @@ namespace MediaBrowser.Common.Providers } } - // skip if more than 8 digits + 2 chars for tt + // Skip if more than 8 digits + 2 chars for tt if (i <= ImdbMaxNumbers + 2 && i >= ImdbMinNumbers + 2) { imdbId = text.Slice(0, i); diff --git a/MediaBrowser.Controller/Entities/Movies/Movie.cs b/MediaBrowser.Controller/Entities/Movies/Movie.cs index 77e70f8fbd..3c12acd90d 100644 --- a/MediaBrowser.Controller/Entities/Movies/Movie.cs +++ b/MediaBrowser.Controller/Entities/Movies/Movie.cs @@ -33,9 +33,9 @@ namespace MediaBrowser.Controller.Entities.Movies .ToArray(); /// - /// Gets or sets the name of the TMDB collection. + /// Gets or sets the name of the TMDb collection. /// - /// The name of the TMDB collection. + /// The name of the TMDb collection. public string TmdbCollectionName { get; set; } [JsonIgnore] diff --git a/MediaBrowser.Controller/Providers/IHasOrder.cs b/MediaBrowser.Controller/Providers/IHasOrder.cs index 9fde0e6958..77b0407a20 100644 --- a/MediaBrowser.Controller/Providers/IHasOrder.cs +++ b/MediaBrowser.Controller/Providers/IHasOrder.cs @@ -1,9 +1,14 @@ -#pragma warning disable CS1591 - namespace MediaBrowser.Controller.Providers { + /// + /// Interface IHasOrder. + /// public interface IHasOrder { + /// + /// Gets the order. + /// + /// The order. int Order { get; } } } diff --git a/MediaBrowser.Controller/Providers/IRemoteMetadataProvider.cs b/MediaBrowser.Controller/Providers/IRemoteMetadataProvider.cs index f146decb60..2c943d9e77 100644 --- a/MediaBrowser.Controller/Providers/IRemoteMetadataProvider.cs +++ b/MediaBrowser.Controller/Providers/IRemoteMetadataProvider.cs @@ -1,5 +1,3 @@ -#pragma warning disable CS1591 - using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; @@ -8,20 +6,41 @@ using MediaBrowser.Model.Providers; namespace MediaBrowser.Controller.Providers { + /// + /// Interface IRemoteMetadataProvider. + /// public interface IRemoteMetadataProvider : IMetadataProvider { } + /// + /// Interface IRemoteMetadataProvider. + /// public interface IRemoteMetadataProvider : IMetadataProvider, IRemoteMetadataProvider, IRemoteSearchProvider where TItemType : BaseItem, IHasLookupInfo where TLookupInfoType : ItemLookupInfo, new() { + /// + /// Gets the metadata for a specific LookupInfoType. + /// + /// The LookupInfoType to get metadata for. + /// The . + /// Task{MetadataResult{TItemType}}. Task> GetMetadata(TLookupInfoType info, CancellationToken cancellationToken); } + /// + /// Interface IRemoteMetadataProvider. + /// public interface IRemoteSearchProvider : IRemoteSearchProvider where TLookupInfoType : ItemLookupInfo { + /// + /// Gets the list of for a specific LookupInfoType. + /// + /// The LookupInfoType to search for. + /// The . + /// Task{IEnumerable{RemoteSearchResult}}. Task> GetSearchResults(TLookupInfoType searchInfo, CancellationToken cancellationToken); } } diff --git a/MediaBrowser.Model/Entities/MetadataProvider.cs b/MediaBrowser.Model/Entities/MetadataProvider.cs index 37e3d88645..a34bbd3c8f 100644 --- a/MediaBrowser.Model/Entities/MetadataProvider.cs +++ b/MediaBrowser.Model/Entities/MetadataProvider.cs @@ -1,5 +1,3 @@ -#pragma warning disable CS1591 - namespace MediaBrowser.Model.Entities { /// @@ -14,38 +12,78 @@ namespace MediaBrowser.Model.Entities Custom = 0, /// - /// The imdb. + /// The IMDb id. /// Imdb = 2, /// - /// The TMDB. + /// The TMDb id. /// Tmdb = 3, /// - /// The TVDB. + /// The TVDb id. /// Tvdb = 4, /// - /// The tvcom. + /// The tvcom id. /// Tvcom = 5, /// - /// Tmdb Collection Id. + /// TMDb collection id. /// TmdbCollection = 7, + + /// + /// The MusicBrainz album id. + /// MusicBrainzAlbum = 8, + + /// + /// The MusicBrainz album artist id. + /// MusicBrainzAlbumArtist = 9, + + /// + /// The MusicBrainz artist id. + /// MusicBrainzArtist = 10, + + /// + /// The MusicBrainz release group id. + /// MusicBrainzReleaseGroup = 11, + + /// + /// The Zap2It id. + /// Zap2It = 12, + + /// + /// The TvRage id. + /// TvRage = 15, + + /// + /// The AudioDb artist id. + /// AudioDbArtist = 16, + + /// + /// The AudioDb collection id. + /// AudioDbAlbum = 17, + + /// + /// The MusicBrainz track id. + /// MusicBrainzTrack = 18, + + /// + /// The TvMaze id. + /// TvMaze = 19 } } diff --git a/MediaBrowser.Model/Querying/ItemFields.cs b/MediaBrowser.Model/Querying/ItemFields.cs index e6c3a6c260..6fa1d778ad 100644 --- a/MediaBrowser.Model/Querying/ItemFields.cs +++ b/MediaBrowser.Model/Querying/ItemFields.cs @@ -126,7 +126,7 @@ namespace MediaBrowser.Model.Querying ProductionLocations, /// - /// Imdb, tmdb, etc. + /// The ids from IMDb, TMDb, etc. /// ProviderIds, diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index bbb33ddf0e..552ded0c45 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -183,7 +183,7 @@ namespace MediaBrowser.Providers.Manager } } - // thetvdb will sometimes serve a rubbish 404 html page with a 200 OK code, because reasons... + // TVDb will sometimes serve a rubbish 404 html page with a 200 OK code, because reasons... if (contentType.Equals(MediaTypeNames.Text.Html, StringComparison.OrdinalIgnoreCase)) { throw new HttpRequestException("Invalid image received.", null, HttpStatusCode.NotFound); diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Api/TmdbController.cs b/MediaBrowser.Providers/Plugins/Tmdb/Api/TmdbController.cs index 0bab7c3cad..ac3df1d5d6 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/Api/TmdbController.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Api/TmdbController.cs @@ -8,7 +8,7 @@ using TMDbLib.Objects.General; namespace MediaBrowser.Providers.Plugins.Tmdb.Api { /// - /// The TMDb api controller. + /// The TMDb API controller. /// [ApiController] [Authorize(Policy = "DefaultAuthorization")] diff --git a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetExternalId.cs b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetExternalId.cs index 3217ac2f13..0e768bb832 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetExternalId.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetExternalId.cs @@ -7,7 +7,7 @@ using MediaBrowser.Model.Providers; namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets { /// - /// External ID for a TMDB box set. + /// External id for a TMDb box set. /// public class TmdbBoxSetExternalId : IExternalId { diff --git a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs index 29a557c315..ef878e6707 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs @@ -1,7 +1,5 @@ #nullable disable -#pragma warning disable CS1591 - using System; using System.Collections.Generic; using System.Globalization; @@ -18,26 +16,38 @@ using MediaBrowser.Model.Providers; namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets { + /// + /// BoxSet image provider powered by TMDb. + /// public class TmdbBoxSetImageProvider : IRemoteImageProvider, IHasOrder { private readonly IHttpClientFactory _httpClientFactory; private readonly TmdbClientManager _tmdbClientManager; + /// + /// Initializes a new instance of the class. + /// + /// The . + /// The . public TmdbBoxSetImageProvider(IHttpClientFactory httpClientFactory, TmdbClientManager tmdbClientManager) { _httpClientFactory = httpClientFactory; _tmdbClientManager = tmdbClientManager; } + /// public string Name => TmdbUtils.ProviderName; + /// public int Order => 0; + /// public bool Supports(BaseItem item) { return item is BoxSet; } + /// public IEnumerable GetSupportedImages(BaseItem item) { return new List @@ -47,6 +57,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets }; } + /// public async Task> GetImages(BaseItem item, CancellationToken cancellationToken) { var tmdbId = Convert.ToInt32(item.GetProviderId(MetadataProvider.Tmdb), CultureInfo.InvariantCulture); @@ -76,6 +87,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets return remoteImages; } + /// public Task GetImageResponse(string url, CancellationToken cancellationToken) { return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken); diff --git a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs index 62bc9c65f9..90f2aa88f7 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs @@ -1,7 +1,5 @@ #nullable disable -#pragma warning disable CS1591 - using System; using System.Collections.Generic; using System.Globalization; @@ -18,12 +16,21 @@ using MediaBrowser.Model.Providers; namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets { + /// + /// BoxSet provider powered by TMDb. + /// public class TmdbBoxSetProvider : IRemoteMetadataProvider { private readonly IHttpClientFactory _httpClientFactory; private readonly TmdbClientManager _tmdbClientManager; private readonly ILibraryManager _libraryManager; + /// + /// Initializes a new instance of the class. + /// + /// The . + /// The . + /// The . public TmdbBoxSetProvider(IHttpClientFactory httpClientFactory, TmdbClientManager tmdbClientManager, ILibraryManager libraryManager) { _httpClientFactory = httpClientFactory; @@ -31,8 +38,10 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets _libraryManager = libraryManager; } + /// public string Name => TmdbUtils.ProviderName; + /// public async Task> GetSearchResults(BoxSetInfo searchInfo, CancellationToken cancellationToken) { var tmdbId = Convert.ToInt32(searchInfo.GetProviderId(MetadataProvider.Tmdb), CultureInfo.InvariantCulture); @@ -81,6 +90,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets return collections; } + /// public async Task> GetMetadata(BoxSetInfo info, CancellationToken cancellationToken) { var tmdbId = Convert.ToInt32(info.GetProviderId(MetadataProvider.Tmdb), CultureInfo.InvariantCulture); @@ -124,6 +134,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets return result; } + /// public Task GetImageResponse(string url, CancellationToken cancellationToken) { return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken); diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieExternalId.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieExternalId.cs index 31310a8d41..38d2c5c69a 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieExternalId.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieExternalId.cs @@ -7,7 +7,7 @@ using MediaBrowser.Model.Providers; namespace MediaBrowser.Providers.Plugins.Tmdb.Movies { /// - /// External ID for a TMBD movie. + /// External id for a TMDb movie. /// public class TmdbMovieExternalId : IExternalId { diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs index 16f0089f8f..1646a93d22 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs @@ -1,7 +1,5 @@ #nullable disable -#pragma warning disable CS1591 - using System; using System.Collections.Generic; using System.Globalization; @@ -19,26 +17,38 @@ using TMDbLib.Objects.Find; namespace MediaBrowser.Providers.Plugins.Tmdb.Movies { + /// + /// Movie image provider powered by TMDb. + /// public class TmdbMovieImageProvider : IRemoteImageProvider, IHasOrder { private readonly IHttpClientFactory _httpClientFactory; private readonly TmdbClientManager _tmdbClientManager; + /// + /// Initializes a new instance of the class. + /// + /// The . + /// The . public TmdbMovieImageProvider(IHttpClientFactory httpClientFactory, TmdbClientManager tmdbClientManager) { _httpClientFactory = httpClientFactory; _tmdbClientManager = tmdbClientManager; } + /// public int Order => 0; + /// public string Name => TmdbUtils.ProviderName; + /// public bool Supports(BaseItem item) { return item is Movie || item is Trailer; } + /// public IEnumerable GetSupportedImages(BaseItem item) { return new List @@ -49,6 +59,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies }; } + /// public async Task> GetImages(BaseItem item, CancellationToken cancellationToken) { var language = item.GetPreferredMetadataLanguage(); @@ -96,6 +107,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies return remoteImages; } + /// public Task GetImageResponse(string url, CancellationToken cancellationToken) { return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken); diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs index f14f31858c..dd2d5d97d9 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs @@ -1,7 +1,5 @@ #nullable disable -#pragma warning disable CS1591 - using System; using System.Collections.Generic; using System.Globalization; @@ -23,7 +21,7 @@ using TMDbLib.Objects.Search; namespace MediaBrowser.Providers.Plugins.Tmdb.Movies { /// - /// Class MovieDbProvider. + /// Movie provider powered by TMDb. /// public class TmdbMovieProvider : IRemoteMetadataProvider, IHasOrder { @@ -31,6 +29,12 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies private readonly ILibraryManager _libraryManager; private readonly TmdbClientManager _tmdbClientManager; + /// + /// Initializes a new instance of the class. + /// + /// The . + /// The . + /// The . public TmdbMovieProvider( ILibraryManager libraryManager, TmdbClientManager tmdbClientManager, @@ -41,11 +45,13 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies _httpClientFactory = httpClientFactory; } - public string Name => TmdbUtils.ProviderName; - /// public int Order => 1; + /// + public string Name => TmdbUtils.ProviderName; + + /// public async Task> GetSearchResults(MovieInfo searchInfo, CancellationToken cancellationToken) { if (searchInfo.TryGetProviderId(MetadataProvider.Tmdb, out var id)) @@ -133,6 +139,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies return remoteSearchResults; } + /// public async Task> GetMetadata(MovieInfo info, CancellationToken cancellationToken) { var tmdbId = info.GetProviderId(MetadataProvider.Tmdb); @@ -144,7 +151,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies // Caller provides the filename with extension stripped and NOT the parsed filename var parsedName = _libraryManager.ParseName(info.Name); var cleanedName = TmdbUtils.CleanName(parsedName.Name); - var searchResults = await _tmdbClientManager.SearchMovieAsync(cleanedName, info.Year ?? parsedName.Year ?? 0, info.MetadataLanguage, cancellationToken).ConfigureAwait(false); + var searchResults = await _tmdbClientManager.SearchMovieAsync(cleanedName, info.Year ?? parsedName.Year ?? 0, info.MetadataLanguage, cancellationToken).ConfigureAwait(false); if (searchResults.Count > 0) { diff --git a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonExternalId.cs b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonExternalId.cs index 9804d60bdd..027399aec2 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonExternalId.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonExternalId.cs @@ -6,7 +6,7 @@ using MediaBrowser.Model.Providers; namespace MediaBrowser.Providers.Plugins.Tmdb.People { /// - /// External ID for a TMDB person. + /// External id for a TMDb person. /// public class TmdbPersonExternalId : IExternalId { diff --git a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs index 7ce4cfe676..d7f5c99dd2 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs @@ -1,5 +1,3 @@ -#pragma warning disable CS1591 - using System.Collections.Generic; using System.Globalization; using System.Linq; @@ -14,11 +12,19 @@ using MediaBrowser.Model.Providers; namespace MediaBrowser.Providers.Plugins.Tmdb.People { + /// + /// Person image provider powered by TMDb. + /// public class TmdbPersonImageProvider : IRemoteImageProvider, IHasOrder { private readonly IHttpClientFactory _httpClientFactory; private readonly TmdbClientManager _tmdbClientManager; + /// + /// Initializes a new instance of the class. + /// + /// The . + /// The . public TmdbPersonImageProvider(IHttpClientFactory httpClientFactory, TmdbClientManager tmdbClientManager) { _httpClientFactory = httpClientFactory; @@ -31,11 +37,13 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People /// public int Order => 0; + /// public bool Supports(BaseItem item) { return item is Person; } + /// public IEnumerable GetSupportedImages(BaseItem item) { return new List @@ -44,6 +52,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People }; } + /// public async Task> GetImages(BaseItem item, CancellationToken cancellationToken) { var person = (Person)item; @@ -68,6 +77,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People return remoteImages; } + /// public Task GetImageResponse(string url, CancellationToken cancellationToken) { return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken); diff --git a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs index 8790e37592..d760ad1426 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs @@ -1,7 +1,5 @@ #nullable disable -#pragma warning disable CS1591 - using System; using System.Collections.Generic; using System.Globalization; @@ -16,19 +14,29 @@ using MediaBrowser.Model.Providers; namespace MediaBrowser.Providers.Plugins.Tmdb.People { + /// + /// Person image provider powered by TMDb. + /// public class TmdbPersonProvider : IRemoteMetadataProvider { private readonly IHttpClientFactory _httpClientFactory; private readonly TmdbClientManager _tmdbClientManager; + /// + /// Initializes a new instance of the class. + /// + /// The . + /// The . public TmdbPersonProvider(IHttpClientFactory httpClientFactory, TmdbClientManager tmdbClientManager) { _httpClientFactory = httpClientFactory; _tmdbClientManager = tmdbClientManager; } + /// public string Name => TmdbUtils.ProviderName; + /// public async Task> GetSearchResults(PersonLookupInfo searchInfo, CancellationToken cancellationToken) { if (searchInfo.TryGetProviderId(MetadataProvider.Tmdb, out var personTmdbId)) @@ -79,6 +87,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People return remoteSearchResults; } + /// public async Task> GetMetadata(PersonLookupInfo info, CancellationToken cancellationToken) { var personTmdbId = Convert.ToInt32(info.GetProviderId(MetadataProvider.Tmdb), CultureInfo.InvariantCulture); @@ -131,6 +140,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People return result; } + /// public Task GetImageResponse(string url, CancellationToken cancellationToken) { return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken); diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs index 5eec776b5b..e568bc4d3d 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs @@ -1,7 +1,5 @@ #nullable disable -#pragma warning disable CS1591 - using System; using System.Collections.Generic; using System.Globalization; @@ -17,22 +15,38 @@ using MediaBrowser.Model.Providers; namespace MediaBrowser.Providers.Plugins.Tmdb.TV { + /// + /// TV episode iage provider powered by TheMovieDb. + /// public class TmdbEpisodeImageProvider : IRemoteImageProvider, IHasOrder { private readonly IHttpClientFactory _httpClientFactory; private readonly TmdbClientManager _tmdbClientManager; + /// + /// Initializes a new instance of the class. + /// + /// The . + /// The . public TmdbEpisodeImageProvider(IHttpClientFactory httpClientFactory, TmdbClientManager tmdbClientManager) { _httpClientFactory = httpClientFactory; _tmdbClientManager = tmdbClientManager; } - // After TheTvDb + /// public int Order => 1; + /// public string Name => TmdbUtils.ProviderName; + /// + public bool Supports(BaseItem item) + { + return item is Controller.Entities.TV.Episode; + } + + /// public IEnumerable GetSupportedImages(BaseItem item) { return new List @@ -41,6 +55,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV }; } + /// public async Task> GetImages(BaseItem item, CancellationToken cancellationToken) { var episode = (Controller.Entities.TV.Episode)item; @@ -81,14 +96,10 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV return remoteImages; } + /// public Task GetImageResponse(string url, CancellationToken cancellationToken) { return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken); } - - public bool Supports(BaseItem item) - { - return item is Controller.Entities.TV.Episode; - } } } diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs index f50f158772..e20284e6f8 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs @@ -1,7 +1,5 @@ #nullable disable -#pragma warning disable CS1591 - using System; using System.Collections.Generic; using System.Globalization; @@ -19,22 +17,32 @@ using MediaBrowser.Model.Providers; namespace MediaBrowser.Providers.Plugins.Tmdb.TV { + /// + /// TV episode provider powered by TheMovieDb. + /// public class TmdbEpisodeProvider : IRemoteMetadataProvider, IHasOrder { private readonly IHttpClientFactory _httpClientFactory; private readonly TmdbClientManager _tmdbClientManager; + /// + /// Initializes a new instance of the class. + /// + /// The . + /// The . public TmdbEpisodeProvider(IHttpClientFactory httpClientFactory, TmdbClientManager tmdbClientManager) { _httpClientFactory = httpClientFactory; _tmdbClientManager = tmdbClientManager; } - // After TheTvDb + /// public int Order => 1; + /// public string Name => TmdbUtils.ProviderName; + /// public async Task> GetSearchResults(EpisodeInfo searchInfo, CancellationToken cancellationToken) { // The search query must either provide an episode number or date @@ -68,6 +76,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV }; } + /// public async Task> GetMetadata(EpisodeInfo info, CancellationToken cancellationToken) { var metadataResult = new MetadataResult(); @@ -209,6 +218,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV return metadataResult; } + /// public Task GetImageResponse(string url, CancellationToken cancellationToken) { return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken); diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs index 4446fa9665..dea89f1d2c 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs @@ -1,5 +1,3 @@ -#pragma warning disable CS1591 - using System; using System.Collections.Generic; using System.Globalization; @@ -16,26 +14,52 @@ using MediaBrowser.Model.Providers; namespace MediaBrowser.Providers.Plugins.Tmdb.TV { + /// + /// TV season image provider powered by TheMovieDb. + /// public class TmdbSeasonImageProvider : IRemoteImageProvider, IHasOrder { private readonly IHttpClientFactory _httpClientFactory; private readonly TmdbClientManager _tmdbClientManager; + /// + /// Initializes a new instance of the class. + /// + /// The . + /// The . + public TmdbSeasonImageProvider(IHttpClientFactory httpClientFactory, TmdbClientManager tmdbClientManager) { _httpClientFactory = httpClientFactory; _tmdbClientManager = tmdbClientManager; } + /// + /// The order. + /// public int Order => 1; + /// + /// The name. + /// public string Name => TmdbUtils.ProviderName; - public Task GetImageResponse(string url, CancellationToken cancellationToken) + /// + public bool Supports(BaseItem item) { - return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken); + return item is Season; + } + + /// + public IEnumerable GetSupportedImages(BaseItem item) + { + return new List + { + ImageType.Primary + }; } + /// public async Task> GetImages(BaseItem item, CancellationToken cancellationToken) { var season = (Season)item; @@ -68,17 +92,10 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV return remoteImages; } - public IEnumerable GetSupportedImages(BaseItem item) - { - return new List - { - ImageType.Primary - }; - } - - public bool Supports(BaseItem item) + /// + public Task GetImageResponse(string url, CancellationToken cancellationToken) { - return item is Season; + return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken); } } } diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs index 64ed3f408d..2cf0f399e0 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs @@ -1,5 +1,3 @@ -#pragma warning disable CS1591 - using System; using System.Collections.Generic; using System.Globalization; @@ -17,19 +15,29 @@ using MediaBrowser.Model.Providers; namespace MediaBrowser.Providers.Plugins.Tmdb.TV { + /// + /// TV season provider powered by TheMovieDb. + /// public class TmdbSeasonProvider : IRemoteMetadataProvider { private readonly IHttpClientFactory _httpClientFactory; private readonly TmdbClientManager _tmdbClientManager; + /// + /// Initializes a new instance of the class. + /// + /// The . + /// The . public TmdbSeasonProvider(IHttpClientFactory httpClientFactory, TmdbClientManager tmdbClientManager) { _httpClientFactory = httpClientFactory; _tmdbClientManager = tmdbClientManager; } + /// public string Name => TmdbUtils.ProviderName; + /// public async Task> GetMetadata(SeasonInfo info, CancellationToken cancellationToken) { var result = new MetadataResult(); @@ -114,11 +122,13 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV return result; } + /// public Task> GetSearchResults(SeasonInfo searchInfo, CancellationToken cancellationToken) { return Task.FromResult(Enumerable.Empty()); } + /// public Task GetImageResponse(string url, CancellationToken cancellationToken) { return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken); diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesExternalId.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesExternalId.cs index 8a2be80cde..df04cb2e73 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesExternalId.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesExternalId.cs @@ -6,7 +6,7 @@ using MediaBrowser.Model.Providers; namespace MediaBrowser.Providers.Plugins.Tmdb.TV { /// - /// External ID for a TMDB series. + /// External id for a TMDb series. /// public class TmdbSeriesExternalId : IExternalId { diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs index 130d6ce448..e96b680b48 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs @@ -1,5 +1,3 @@ -#pragma warning disable CS1591 - using System; using System.Collections.Generic; using System.Globalization; @@ -16,27 +14,38 @@ using MediaBrowser.Model.Providers; namespace MediaBrowser.Providers.Plugins.Tmdb.TV { + /// + /// TV series image provider powered by TheMovieDb. + /// public class TmdbSeriesImageProvider : IRemoteImageProvider, IHasOrder { private readonly IHttpClientFactory _httpClientFactory; private readonly TmdbClientManager _tmdbClientManager; + /// + /// Initializes a new instance of the class. + /// + /// The . + /// The . public TmdbSeriesImageProvider(IHttpClientFactory httpClientFactory, TmdbClientManager tmdbClientManager) { _httpClientFactory = httpClientFactory; _tmdbClientManager = tmdbClientManager; } + /// public string Name => TmdbUtils.ProviderName; - // After tvdb and fanart + /// public int Order => 2; + /// public bool Supports(BaseItem item) { return item is Series; } + /// public IEnumerable GetSupportedImages(BaseItem item) { return new List @@ -47,6 +56,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV }; } + /// public async Task> GetImages(BaseItem item, CancellationToken cancellationToken) { var tmdbId = item.GetProviderId(MetadataProvider.Tmdb); @@ -80,6 +90,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV return remoteImages; } + /// public Task GetImageResponse(string url, CancellationToken cancellationToken) { return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken); diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs index 4d26052faf..4e8fdf0ee8 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs @@ -1,7 +1,5 @@ #nullable disable -#pragma warning disable CS1591 - using System; using System.Collections.Generic; using System.Globalization; @@ -23,12 +21,21 @@ using TMDbLib.Objects.TvShows; namespace MediaBrowser.Providers.Plugins.Tmdb.TV { + /// + /// TV series provider powered by TheMovieDb. + /// public class TmdbSeriesProvider : IRemoteMetadataProvider, IHasOrder { private readonly IHttpClientFactory _httpClientFactory; private readonly ILibraryManager _libraryManager; private readonly TmdbClientManager _tmdbClientManager; + /// + /// Initializes a new instance of the class. + /// + /// The . + /// The . + /// The . public TmdbSeriesProvider( ILibraryManager libraryManager, IHttpClientFactory httpClientFactory, @@ -39,11 +46,13 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV _tmdbClientManager = tmdbClientManager; } + /// public string Name => TmdbUtils.ProviderName; - // After TheTVDB + /// public int Order => 1; + /// public async Task> GetSearchResults(SeriesInfo searchInfo, CancellationToken cancellationToken) { if (searchInfo.TryGetProviderId(MetadataProvider.Tmdb, out var tmdbId)) @@ -159,6 +168,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV return remoteResult; } + /// public async Task> GetMetadata(SeriesInfo info, CancellationToken cancellationToken) { var result = new MetadataResult @@ -383,6 +393,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV } } + /// public Task GetImageResponse(string url, CancellationToken cancellationToken) { return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken); diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs b/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs index 685eb222f7..44c2c81f44 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs @@ -14,7 +14,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb private static readonly Regex _nonWords = new(@"[\W_]+", RegexOptions.Compiled); /// - /// URL of the TMDB instance to use. + /// URL of the TMDb instance to use. /// public const string BaseTmdbUrl = "https://www.themoviedb.org/"; @@ -50,7 +50,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb } /// - /// Maps the TMDB provided roles for crew members to Jellyfin roles. + /// Maps the TMDb provided roles for crew members to Jellyfin roles. /// /// Crew member to map against the Jellyfin person types. /// The Jellyfin person type. @@ -103,9 +103,9 @@ namespace MediaBrowser.Providers.Plugins.Tmdb languages.Add(preferredLanguage); - if (preferredLanguage.Length == 5) // like en-US + if (preferredLanguage.Length == 5) // Like en-US { - // Currently, TMDB supports 2-letter language codes only + // Currently, TMDb supports 2-letter language codes only. // They are planning to change this in the future, thus we're // supplying both codes if we're having a 5-letter code. languages.Add(preferredLanguage.Substring(0, 2)); @@ -114,6 +114,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb languages.Add("null"); + // Always add English as fallback language if (!string.Equals(preferredLanguage, "en", StringComparison.OrdinalIgnoreCase)) { languages.Add("en"); @@ -134,14 +135,14 @@ namespace MediaBrowser.Providers.Plugins.Tmdb return language; } - // They require this to be uppercase - // Everything after the hyphen must be written in uppercase due to a way TMDB wrote their api. + // TMDb requires this to be uppercase + // Everything after the hyphen must be written in uppercase due to a way TMDb wrote their API. // See here: https://www.themoviedb.org/talk/5119221d760ee36c642af4ad?page=3#56e372a0c3a3685a9e0019ab var parts = language.Split('-'); if (parts.Length == 2) { - // TMDB doesn't support Switzerland (de-CH, it-CH or fr-CH) so use the language (de, it or fr) without country code + // TMDb doesn't support Switzerland (de-CH, it-CH or fr-CH) so use the language (de, it or fr) without country code if (string.Equals(parts[1], "CH", StringComparison.OrdinalIgnoreCase)) { return parts[0]; @@ -174,14 +175,14 @@ namespace MediaBrowser.Providers.Plugins.Tmdb } /// - /// Combines the metadata country code and the parental rating from the Api into the value we store in our database. + /// Combines the metadata country code and the parental rating from the API into the value we store in our database. /// - /// The Iso 3166-1 country code of the rating country. - /// The rating value returned by the Tmdb Api. + /// The ISO 3166-1 country code of the rating country. + /// The rating value returned by the TMDb API. /// The combined parental rating of country code+rating value. public static string BuildParentalRating(string countryCode, string ratingValue) { - // exclude US because we store us values as TV-14 without the country code. + // Exclude US because we store US values as TV-14 without the country code. var ratingPrefix = string.Equals(countryCode, "US", StringComparison.OrdinalIgnoreCase) ? string.Empty : countryCode + "-"; var newRating = ratingPrefix + ratingValue; diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs index da348239a1..0d03876f24 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs @@ -170,7 +170,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers ParseProviderLinks(item.Item, endingXml); - // If the file is just an imdb url, don't go any further + // If the file is just an IMDb url, don't go any further if (index == 0) { return; @@ -1136,20 +1136,20 @@ namespace MediaBrowser.XbmcMetadata.Parsers switch (reader.Name) { case "rating": - { - if (reader.IsEmptyElement) { - reader.Read(); - continue; - } + if (reader.IsEmptyElement) + { + reader.Read(); + continue; + } - var ratingName = reader.GetAttribute("name"); + var ratingName = reader.GetAttribute("name"); - using var subtree = reader.ReadSubtree(); - FetchFromRatingNode(subtree, item, ratingName); + using var subtree = reader.ReadSubtree(); + FetchFromRatingNode(subtree, item, ratingName); - break; - } + break; + } default: reader.Skip(); -- cgit v1.2.3 From 2e639c77c73439901abf64fa3439191f181b0b60 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Wed, 27 Apr 2022 13:08:54 +0200 Subject: Apply review suggestions --- .../Library/Resolvers/Movies/MovieResolver.cs | 2 +- .../Providers/IRemoteMetadataProvider.cs | 4 +- MediaBrowser.Model/Entities/MetadataProvider.cs | 30 ++++++------ .../Plugins/StudioImages/Plugin.cs | 5 +- .../Plugins/StudioImages/StudiosImageProvider.cs | 10 ++-- .../Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs | 2 +- .../Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs | 9 +--- MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs | 55 ++++++++++++++-------- 8 files changed, 62 insertions(+), 55 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs index d6ae8aba85..84d4688aff 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs @@ -387,7 +387,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies if (!string.IsNullOrEmpty(item.Path)) { - // Check for IMDb id - we use full media path, as we can assume that this will match in any use case (wither id in parent dir or in file name) + // Check for IMDb id - we use full media path, as we can assume that this will match in any use case (whether id in parent dir or in file name) var imdbid = item.Path.AsSpan().GetAttributeValue("imdbid"); if (!string.IsNullOrWhiteSpace(imdbid)) diff --git a/MediaBrowser.Controller/Providers/IRemoteMetadataProvider.cs b/MediaBrowser.Controller/Providers/IRemoteMetadataProvider.cs index 2c943d9e77..888ca6c72c 100644 --- a/MediaBrowser.Controller/Providers/IRemoteMetadataProvider.cs +++ b/MediaBrowser.Controller/Providers/IRemoteMetadataProvider.cs @@ -25,7 +25,7 @@ namespace MediaBrowser.Controller.Providers /// /// The LookupInfoType to get metadata for. /// The . - /// Task{MetadataResult{TItemType}}. + /// A task returning a MetadataResult for the specific LookupInfoType. Task> GetMetadata(TLookupInfoType info, CancellationToken cancellationToken); } @@ -40,7 +40,7 @@ namespace MediaBrowser.Controller.Providers /// /// The LookupInfoType to search for. /// The . - /// Task{IEnumerable{RemoteSearchResult}}. + /// A task returning RemoteSearchResults for the searchInfo. Task> GetSearchResults(TLookupInfoType searchInfo, CancellationToken cancellationToken); } } diff --git a/MediaBrowser.Model/Entities/MetadataProvider.cs b/MediaBrowser.Model/Entities/MetadataProvider.cs index a34bbd3c8f..bd8db99416 100644 --- a/MediaBrowser.Model/Entities/MetadataProvider.cs +++ b/MediaBrowser.Model/Entities/MetadataProvider.cs @@ -12,77 +12,77 @@ namespace MediaBrowser.Model.Entities Custom = 0, /// - /// The IMDb id. + /// The IMDb provider. /// Imdb = 2, /// - /// The TMDb id. + /// The TMDb provider. /// Tmdb = 3, /// - /// The TVDb id. + /// The TVDb provider. /// Tvdb = 4, /// - /// The tvcom id. + /// The tvcom providerd. /// Tvcom = 5, /// - /// TMDb collection id. + /// TMDb collection provider. /// TmdbCollection = 7, /// - /// The MusicBrainz album id. + /// The MusicBrainz album provider. /// MusicBrainzAlbum = 8, /// - /// The MusicBrainz album artist id. + /// The MusicBrainz album artist provider. /// MusicBrainzAlbumArtist = 9, /// - /// The MusicBrainz artist id. + /// The MusicBrainz artist provider. /// MusicBrainzArtist = 10, /// - /// The MusicBrainz release group id. + /// The MusicBrainz release group provider. /// MusicBrainzReleaseGroup = 11, /// - /// The Zap2It id. + /// The Zap2It provider. /// Zap2It = 12, /// - /// The TvRage id. + /// The TvRage provider. /// TvRage = 15, /// - /// The AudioDb artist id. + /// The AudioDb artist provider. /// AudioDbArtist = 16, /// - /// The AudioDb collection id. + /// The AudioDb collection provider. /// AudioDbAlbum = 17, /// - /// The MusicBrainz track id. + /// The MusicBrainz track provider. /// MusicBrainzTrack = 18, /// - /// The TvMaze id. + /// The TvMaze provider. /// TvMaze = 19 } diff --git a/MediaBrowser.Providers/Plugins/StudioImages/Plugin.cs b/MediaBrowser.Providers/Plugins/StudioImages/Plugin.cs index f5ea6d103d..78150153ad 100644 --- a/MediaBrowser.Providers/Plugins/StudioImages/Plugin.cs +++ b/MediaBrowser.Providers/Plugins/StudioImages/Plugin.cs @@ -50,10 +50,7 @@ namespace MediaBrowser.Providers.Plugins.StudioImages /// public override string ConfigurationFileName => "Jellyfin.Plugin.StudioImages.xml"; - /// - /// Return the plugin configuration page. - /// - /// PluginPageInfo. + /// public IEnumerable GetPages() { yield return new PluginPageInfo diff --git a/MediaBrowser.Providers/Plugins/StudioImages/StudiosImageProvider.cs b/MediaBrowser.Providers/Plugins/StudioImages/StudiosImageProvider.cs index 88bbdadb46..ffbb338e84 100644 --- a/MediaBrowser.Providers/Plugins/StudioImages/StudiosImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/StudioImages/StudiosImageProvider.cs @@ -122,13 +122,13 @@ namespace MediaBrowser.Providers.Plugins.StudioImages } /// - /// Ensures the list. + /// Ensures the existence of a file listing. /// /// The URL. /// The file. /// The file system. /// The cancellation token. - /// Task. + /// A Task to ensure existence of a file listing. public async Task EnsureList(string url, string file, IFileSystem fileSystem, CancellationToken cancellationToken) { var fileInfo = fileSystem.GetFileInfo(file); @@ -151,7 +151,7 @@ namespace MediaBrowser.Providers.Plugins.StudioImages /// /// The . /// The enumerable of image strings. - /// String. + /// The matching image string. public string FindMatch(BaseItem item, IEnumerable images) { var name = GetComparableName(item.Name); @@ -170,10 +170,10 @@ namespace MediaBrowser.Providers.Plugins.StudioImages } /// - /// Get available images for a file. + /// Get available image strings for a file. /// /// The file. - /// IEnumerable{string}. + /// All images strings of a file. public IEnumerable GetAvailableImages(string file) { using var fileStream = File.OpenRead(file); diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs index e568bc4d3d..943a3a75b2 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs @@ -16,7 +16,7 @@ using MediaBrowser.Model.Providers; namespace MediaBrowser.Providers.Plugins.Tmdb.TV { /// - /// TV episode iage provider powered by TheMovieDb. + /// TV episode image provider powered by TheMovieDb. /// public class TmdbEpisodeImageProvider : IRemoteImageProvider, IHasOrder { diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs index dea89f1d2c..da32ea4081 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs @@ -27,21 +27,16 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV /// /// The . /// The . - public TmdbSeasonImageProvider(IHttpClientFactory httpClientFactory, TmdbClientManager tmdbClientManager) { _httpClientFactory = httpClientFactory; _tmdbClientManager = tmdbClientManager; } - /// - /// The order. - /// + /// public int Order => 1; - /// - /// The name. - /// + /// public string Name => TmdbUtils.ProviderName; /// diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs index 0d03876f24..9e197e737b 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs @@ -1,5 +1,3 @@ -#pragma warning disable CS1591 - using System; using System.Collections.Generic; using System.Globalization; @@ -23,6 +21,10 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.XbmcMetadata.Parsers { + /// + /// The BaseNfoParser class. + /// + /// The type. public class BaseNfoParser where T : BaseItem { @@ -63,16 +65,22 @@ namespace MediaBrowser.XbmcMetadata.Parsers /// protected ILogger Logger { get; } + /// + /// Gets the provider manager. + /// protected IProviderManager ProviderManager { get; } + /// + /// Gets a value indicating whether URLs after a closing XML tag are supporrted. + /// protected virtual bool SupportsUrlAfterClosingXmlTag => false; /// /// Fetches metadata for an item from one xml file. /// - /// The item. + /// The . /// The metadata file. - /// The cancellation token. + /// The . /// item is null. /// metadataFile is null or empty. public void Fetch(MetadataResult item, string metadataFile, CancellationToken cancellationToken) @@ -111,10 +119,10 @@ namespace MediaBrowser.XbmcMetadata.Parsers /// /// Fetches the specified item. /// - /// The item. + /// The . /// The metadata file. - /// The settings. - /// The cancellation token. + /// The . + /// The . protected virtual void Fetch(MetadataResult item, string metadataFile, XmlReaderSettings settings, CancellationToken cancellationToken) { if (!SupportsUrlAfterClosingXmlTag) @@ -216,6 +224,11 @@ namespace MediaBrowser.XbmcMetadata.Parsers } } + /// + /// Parses a XML tag to a provider id. + /// + /// The item. + /// The xml tag. protected void ParseProviderLinks(T item, ReadOnlySpan xml) { if (ProviderIdParsers.TryFindImdbId(xml, out var imdbId)) @@ -245,6 +258,11 @@ namespace MediaBrowser.XbmcMetadata.Parsers } } + /// + /// Fetches metadata from an XML node. + /// + /// The . + /// The . protected virtual void FetchDataFromXmlNode(XmlReader reader, MetadataResult itemResult) { var item = itemResult.Item; @@ -1100,17 +1118,14 @@ namespace MediaBrowser.XbmcMetadata.Parsers switch (reader.Name) { case "language": + _ = reader.ReadElementContentAsString(); + if (item is Video video) { - _ = reader.ReadElementContentAsString(); - - if (item is Video video) - { - video.HasSubtitles = true; - } - - break; + video.HasSubtitles = true; } + break; + default: reader.Skip(); break; @@ -1210,9 +1225,9 @@ namespace MediaBrowser.XbmcMetadata.Parsers } /// - /// Gets the persons from XML node. + /// Gets the persons from a XML node. /// - /// The reader. + /// The . /// IEnumerable{PersonInfo}. private PersonInfo GetPersonFromXmlNode(XmlReader reader) { @@ -1348,10 +1363,10 @@ namespace MediaBrowser.XbmcMetadata.Parsers } /// - /// Parses the ImageType from the nfo aspect property. + /// Parses the from the NFO aspect property. /// - /// The nfo aspect property. - /// The image type. + /// The NFO aspect property. + /// The . private static ImageType GetImageType(string aspect) { return aspect switch -- cgit v1.2.3 From b5f9a093ddf2460d42f5910fd741dd2c57a47b61 Mon Sep 17 00:00:00 2001 From: SenorSmartyPants Date: Sat, 19 Nov 2022 08:12:24 -0600 Subject: Don't cancel DVR recordings when adjusting settings (#8752) Fixes https://github.com/jellyfin/jellyfin/issues/3523 --- .../LiveTv/EmbyTV/EmbyTV.cs | 42 +++++----------------- 1 file changed, 8 insertions(+), 34 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 74321a256e..a0ae328a47 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -2201,7 +2201,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV } } - private void SearchForDuplicateShowIds(List timers) + private void SearchForDuplicateShowIds(IEnumerable timers) { var groups = timers.ToLookup(i => i.ShowId ?? string.Empty).ToList(); @@ -2282,39 +2282,13 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV if (updateTimerSettings) { - // Only update if not currently active - test both new timer and existing in case Id's are different - // Id's could be different if the timer was created manually prior to series timer creation - if (!_activeRecordings.TryGetValue(timer.Id, out _) && !_activeRecordings.TryGetValue(existingTimer.Id, out _)) - { - UpdateExistingTimerWithNewMetadata(existingTimer, timer); - - // Needed by ShouldCancelTimerForSeriesTimer - timer.IsManual = existingTimer.IsManual; - - if (ShouldCancelTimerForSeriesTimer(seriesTimer, timer)) - { - existingTimer.Status = RecordingStatus.Cancelled; - } - else if (!existingTimer.IsManual) - { - existingTimer.Status = RecordingStatus.New; - } - - if (existingTimer.Status != RecordingStatus.Cancelled) - { - enabledTimersForSeries.Add(existingTimer); - } - - existingTimer.KeepUntil = seriesTimer.KeepUntil; - existingTimer.IsPostPaddingRequired = seriesTimer.IsPostPaddingRequired; - existingTimer.IsPrePaddingRequired = seriesTimer.IsPrePaddingRequired; - existingTimer.PostPaddingSeconds = seriesTimer.PostPaddingSeconds; - existingTimer.PrePaddingSeconds = seriesTimer.PrePaddingSeconds; - existingTimer.Priority = seriesTimer.Priority; - existingTimer.SeriesTimerId = seriesTimer.Id; - - _timerProvider.Update(existingTimer); - } + existingTimer.KeepUntil = seriesTimer.KeepUntil; + existingTimer.IsPostPaddingRequired = seriesTimer.IsPostPaddingRequired; + existingTimer.IsPrePaddingRequired = seriesTimer.IsPrePaddingRequired; + existingTimer.PostPaddingSeconds = seriesTimer.PostPaddingSeconds; + existingTimer.PrePaddingSeconds = seriesTimer.PrePaddingSeconds; + existingTimer.Priority = seriesTimer.Priority; + existingTimer.SeriesTimerId = seriesTimer.Id; } existingTimer.SeriesTimerId = seriesTimer.Id; -- cgit v1.2.3 From 159a244654aa533a149fe00636c9c8e8fa5d2c01 Mon Sep 17 00:00:00 2001 From: SenorSmartyPants Date: Sat, 19 Nov 2022 14:14:41 -0600 Subject: Add Options to disable DVR NFO and image saving - SaveRecordingNFO and SaveRecordingImages default to true. Maintains current behavior. - Episode.FillMissingEpisodeNumbersFromPath for live tv so external metadata can be pulled when recording starts. --- .../LiveTv/EmbyTV/EmbyTV.cs | 28 +++++++++++++--------- MediaBrowser.Controller/Entities/TV/Episode.cs | 2 +- MediaBrowser.Model/LiveTv/LiveTvOptions.cs | 4 ++++ 3 files changed, 22 insertions(+), 12 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index a0ae328a47..6739c10c1e 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -1814,21 +1814,27 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV program.AddGenre("News"); } - if (timer.IsProgramSeries) + if (GetConfiguration().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); + 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); + } } - else + + if (GetConfiguration().SaveRecordingImages) { - await SaveVideoNfoAsync(timer, recordingPath, program, false).ConfigureAwait(false); + await SaveRecordingImages(recordingPath, program).ConfigureAwait(false); } - - await SaveRecordingImages(recordingPath, program).ConfigureAwait(false); } catch (Exception ex) { diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs index 15b721fe63..285d3ef085 100644 --- a/MediaBrowser.Controller/Entities/TV/Episode.cs +++ b/MediaBrowser.Controller/Entities/TV/Episode.cs @@ -320,7 +320,7 @@ namespace MediaBrowser.Controller.Entities.TV if (!IsLocked) { - if (SourceType == SourceType.Library) + if (SourceType == SourceType.Library || SourceType == SourceType.LiveTV) { try { diff --git a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs index 4cece941cf..25e5c77969 100644 --- a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs +++ b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs @@ -40,5 +40,9 @@ namespace MediaBrowser.Model.LiveTv public string RecordingPostProcessor { get; set; } public string RecordingPostProcessorArguments { get; set; } + + public bool SaveRecordingNFO { get; set; } = true; + + public bool SaveRecordingImages { get; set; } = true; } } -- cgit v1.2.3 From b77922668b64991b5e043c277c911ea0ee39475b Mon Sep 17 00:00:00 2001 From: Akira Li Date: Sun, 20 Nov 2022 04:52:58 +0000 Subject: Translated using Weblate (Chinese (Traditional, Hong Kong)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/zh_Hant_HK/ --- Emby.Server.Implementations/Localization/Core/zh-HK.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/zh-HK.json b/Emby.Server.Implementations/Localization/Core/zh-HK.json index 6c8bf76274..baa9ecc1c1 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-HK.json +++ b/Emby.Server.Implementations/Localization/Core/zh-HK.json @@ -123,5 +123,6 @@ "TaskCleanActivityLogDescription": "刪除早於設定時間的日誌記錄。", "TaskKeyframeExtractorDescription": "提取關鍵格以創建更準確的HLS播放列表。次指示可能用時很長。", "TaskKeyframeExtractor": "關鍵幀提取器", - "External": "外部" + "External": "外部", + "HearingImpaired": "聽力障礙" } -- cgit v1.2.3 From bebc003e5ae19b582ca42aaf5abb2ebc009b9786 Mon Sep 17 00:00:00 2001 From: Akira Li Date: Sun, 20 Nov 2022 04:52:07 +0000 Subject: Translated using Weblate (Chinese (Traditional)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/zh_Hant/ --- Emby.Server.Implementations/Localization/Core/zh-TW.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/zh-TW.json b/Emby.Server.Implementations/Localization/Core/zh-TW.json index 102a266f8e..a0d1e0b045 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-TW.json +++ b/Emby.Server.Implementations/Localization/Core/zh-TW.json @@ -122,5 +122,6 @@ "TaskOptimizeDatabase": "最佳化資料庫", "TaskKeyframeExtractorDescription": "將關鍵幀從影片檔案提取出來並建立更精準的HLS播放清單。這可能需要很長時間。", "TaskKeyframeExtractor": "關鍵幀提取器", - "External": "外部" + "External": "外部", + "HearingImpaired": "聽力障礙" } -- cgit v1.2.3 From 5443708c4238d77f38346d63d2e0701c7dfb65b2 Mon Sep 17 00:00:00 2001 From: jhih_yu Date: Mon, 21 Nov 2022 15:14:17 +0000 Subject: Translated using Weblate (Chinese (Traditional)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/zh_Hant/ --- Emby.Server.Implementations/Localization/Core/zh-TW.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/zh-TW.json b/Emby.Server.Implementations/Localization/Core/zh-TW.json index a0d1e0b045..4949c5ab6d 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-TW.json +++ b/Emby.Server.Implementations/Localization/Core/zh-TW.json @@ -37,7 +37,7 @@ "MixedContent": "混合內容", "Movies": "電影", "Music": "音樂", - "MusicVideos": "音樂錄影帶", + "MusicVideos": "MV", "NameInstallFailed": "{0} 安裝失敗", "NameSeasonNumber": "第 {0} 季", "NameSeasonUnknown": "未知季數", -- cgit v1.2.3 From 75c96e6e76d7ba3e68f12108856c291623c819e7 Mon Sep 17 00:00:00 2001 From: SenorSmartyPants Date: Tue, 22 Nov 2022 15:02:00 -0600 Subject: DVR: Prefer HD channels then earliest showing when handling duplicate showings. (#8768) Co-authored-by: Bond-009 --- .../LiveTv/EmbyTV/EmbyTV.cs | 5 ++--- .../LiveTv/EmbyTV/TimerManager.cs | 23 +++++++++++++++++++--- 2 files changed, 22 insertions(+), 6 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index a0ae328a47..cf9be5a54a 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -2192,10 +2192,9 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV private void HandleDuplicateShowIds(List timers) { - foreach (var timer in timers.Skip(1)) + // sort showings by HD channels first, then by startDate, record earliest showing possible + foreach (var timer in timers.OrderByDescending(t => _liveTvManager.GetLiveTvChannel(t, this).IsHD).ThenBy(t => t.StartDate).Skip(1)) { - // TODO: Get smarter, prefer HD, etc - timer.Status = RecordingStatus.Cancelled; _timerProvider.Update(timer); } diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs index a861e6ae44..f612565d1b 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs @@ -122,11 +122,28 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV if (_timers.TryAdd(item.Id, timer)) { - Logger.LogInformation( - "Creating recording timer for {Id}, {Name}. Timer will fire in {Minutes} minutes", + if (item.IsSeries) + { + Logger.LogInformation( + "Creating recording timer for {Id}, {Name} {SeasonNumber}x{EpisodeNumber:D2} on channel {ChannelId}. Timer will fire in {Minutes} minutes at {StartDate}", item.Id, item.Name, - dueTime.TotalMinutes.ToString(CultureInfo.InvariantCulture)); + item.SeasonNumber, + item.EpisodeNumber, + item.ChannelId, + dueTime.TotalMinutes.ToString(CultureInfo.InvariantCulture), + item.StartDate); + } + else + { + Logger.LogInformation( + "Creating recording timer for {Id}, {Name} on channel {ChannelId}. Timer will fire in {Minutes} minutes at {StartDate}", + item.Id, + item.Name, + item.ChannelId, + dueTime.TotalMinutes.ToString(CultureInfo.InvariantCulture), + item.StartDate); + } } else { -- cgit v1.2.3 From a84ab072caa3f84db075cf5a8c3186039348a9a1 Mon Sep 17 00:00:00 2001 From: andr8009 Date: Wed, 23 Nov 2022 18:20:02 +0000 Subject: Translated using Weblate (Danish) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/da/ --- Emby.Server.Implementations/Localization/Core/da.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/da.json b/Emby.Server.Implementations/Localization/Core/da.json index 57455587d2..34655ace69 100644 --- a/Emby.Server.Implementations/Localization/Core/da.json +++ b/Emby.Server.Implementations/Localization/Core/da.json @@ -123,5 +123,6 @@ "TaskOptimizeDatabase": "Optimér database", "TaskKeyframeExtractorDescription": "Udtrækker billeder fra videofiler for at lave mere præcise HLS playlister. Denne opgave kan godt tage lang tid.", "TaskKeyframeExtractor": "Billedramme udtrækker", - "External": "Ekstern" + "External": "Ekstern", + "HearingImpaired": "Hørehæmmet" } -- cgit v1.2.3 From 5cef9799c365f3179ef4e4192bb861a0ca83a1e3 Mon Sep 17 00:00:00 2001 From: drlovesan Date: Wed, 23 Nov 2022 19:20:16 +0000 Subject: Translated using Weblate (Urdu (Pakistan)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ur_PK/ --- Emby.Server.Implementations/Localization/Core/ur_PK.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ur_PK.json b/Emby.Server.Implementations/Localization/Core/ur_PK.json index e7f3e492c6..5413346d41 100644 --- a/Emby.Server.Implementations/Localization/Core/ur_PK.json +++ b/Emby.Server.Implementations/Localization/Core/ur_PK.json @@ -5,18 +5,18 @@ "HeaderAlbumArtists": "البم کے فنکار", "Movies": "فلمیں", "HeaderFavoriteEpisodes": "پسندیدہ اقساط", - "Collections": "مجموعہ", + "Collections": "مجموعے", "Folders": "فولڈرز", "HeaderLiveTV": "براہ راست ٹی وی", "Channels": "چینلز", "HeaderContinueWatching": "دیکھنا جاری رکھیں", "Playlists": "پلے لسٹس", - "ValueSpecialEpisodeName": "خاص - {0}", - "Shows": "شوز", + "ValueSpecialEpisodeName": "خصوصی - {0}", + "Shows": "دکھاتا ہے۔", "Genres": "انواع", "Artists": "فنکار", - "Sync": "مطابقت", - "Photos": "تصوریں", + "Sync": "مطابقت پذیری", + "Photos": "تصاویر", "Albums": "البمز", "Favorites": "پسندیدہ", "Songs": "گانے", -- cgit v1.2.3 From 18d7ac1a2aa795cd649e315bad37cbde3b4eed0d Mon Sep 17 00:00:00 2001 From: Pedro Barreiro Date: Fri, 25 Nov 2022 13:25:50 +0000 Subject: Translated using Weblate (Portuguese (Portugal)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/pt_PT/ --- Emby.Server.Implementations/Localization/Core/pt-PT.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/pt-PT.json b/Emby.Server.Implementations/Localization/Core/pt-PT.json index 7047f1c282..a75182f220 100644 --- a/Emby.Server.Implementations/Localization/Core/pt-PT.json +++ b/Emby.Server.Implementations/Localization/Core/pt-PT.json @@ -123,5 +123,6 @@ "TaskOptimizeDatabase": "Otimizar base de dados", "TaskKeyframeExtractorDescription": "Extrai quadros-chave de ficheiros de video para criar listas de reprodução HLS mais precisas. Esta tarefa pode demorar algum tempo.", "TaskKeyframeExtractor": "Extrator de Quadros-chave", - "External": "Externo" + "External": "Externo", + "HearingImpaired": "Surdo" } -- cgit v1.2.3 From f9d3ce0e452fe5ee9fb94b4a5685a5529e096e28 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 27 Nov 2022 14:09:07 +0100 Subject: chore(deps): update dependency prometheus-net.dotnetruntime to v4.4.0 (#8793) --- Emby.Server.Implementations/Emby.Server.Implementations.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index a0bbd0c496..eeaa3346d9 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -31,7 +31,7 @@ - + -- cgit v1.2.3 From 556cc8062debd5370ef907b0c78e8636356a8068 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 23 Nov 2022 15:58:11 +0100 Subject: Investigate some TODO comments --- .gitignore | 2 -- Emby.Dlna/PlayTo/PlayToController.cs | 1 - .../Library/Resolvers/PlaylistResolver.cs | 20 +++++++++++--------- .../ScheduledTasks/Tasks/ChapterImagesTask.cs | 14 +++++++------- .../MediaEncoding/EncodingHelper.cs | 19 ------------------- .../Net/BasePeriodicWebSocketListener.cs | 3 ++- src/Jellyfin.Extensions/AlphanumericComparator.cs | 2 -- .../Jellyfin.Model.Tests/Dlna/StreamBuilderTests.cs | 12 ++++++------ 8 files changed, 26 insertions(+), 47 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/.gitignore b/.gitignore index c2ae76c1e3..9e9fae7bfc 100644 --- a/.gitignore +++ b/.gitignore @@ -150,8 +150,6 @@ publish/ *.pubxml # NuGet Packages Directory -## TODO: If you have NuGet Package Restore enabled, uncomment the next line -# packages/ dlls/ dllssigned/ diff --git a/Emby.Dlna/PlayTo/PlayToController.cs b/Emby.Dlna/PlayTo/PlayToController.cs index b73ce00b6f..65367e24f2 100644 --- a/Emby.Dlna/PlayTo/PlayToController.cs +++ b/Emby.Dlna/PlayTo/PlayToController.cs @@ -338,7 +338,6 @@ namespace Emby.Dlna.PlayTo SubtitleStreamIndex = info.SubtitleStreamIndex, VolumeLevel = _device.Volume, - // TODO CanSeek = true, PlayMethod = info.IsDirectStream ? PlayMethod.DirectStream : PlayMethod.Transcode diff --git a/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs b/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs index 6b0dfe9864..7a2b3da3a9 100644 --- a/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs @@ -31,16 +31,18 @@ namespace Emby.Server.Implementations.Library.Resolvers if (args.IsDirectory) { // It's a boxset if the path is a directory with [playlist] in it's the name - // TODO: Should this use Path.GetDirectoryName() instead? - bool isBoxSet = Path.GetFileName(args.Path) - ?.Contains("[playlist]", StringComparison.OrdinalIgnoreCase) - ?? false; - if (isBoxSet) + var filename = Path.GetFileName(Path.TrimEndingDirectorySeparator(args.Path)); + if (string.IsNullOrEmpty(filename)) + { + return null; + } + + if (filename.Contains("[playlist]", StringComparison.OrdinalIgnoreCase)) { return new Playlist { Path = args.Path, - Name = Path.GetFileName(args.Path).Replace("[playlist]", string.Empty, StringComparison.OrdinalIgnoreCase).Trim() + Name = filename.Replace("[playlist]", string.Empty, StringComparison.OrdinalIgnoreCase).Trim() }; } @@ -51,7 +53,7 @@ namespace Emby.Server.Implementations.Library.Resolvers return new Playlist { Path = args.Path, - Name = Path.GetFileName(args.Path) + Name = filename }; } } @@ -60,8 +62,8 @@ namespace Emby.Server.Implementations.Library.Resolvers // It should have the correct collection type and a supported file extension else if (_musicPlaylistCollectionTypes.Contains(args.CollectionType ?? string.Empty, StringComparison.OrdinalIgnoreCase)) { - var extension = Path.GetExtension(args.Path); - if (Playlist.SupportedExtensions.Contains(extension ?? string.Empty, StringComparison.OrdinalIgnoreCase)) + var extension = Path.GetExtension(args.Path.AsSpan()); + if (Playlist.SupportedExtensions.Contains(extension, StringComparison.OrdinalIgnoreCase)) { return new Playlist { diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs index 0bf0838fac..6106ae6c40 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs @@ -16,6 +16,7 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; using MediaBrowser.Model.Tasks; +using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.ScheduledTasks.Tasks { @@ -24,15 +25,10 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks /// public class ChapterImagesTask : IScheduledTask { - /// - /// The _library manager. - /// + private readonly ILogger _logger; private readonly ILibraryManager _libraryManager; - private readonly IItemRepository _itemRepo; - private readonly IApplicationPaths _appPaths; - private readonly IEncodingManager _encodingManager; private readonly IFileSystem _fileSystem; private readonly ILocalizationManager _localization; @@ -40,6 +36,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks /// /// Initializes a new instance of the class. /// + /// The logger.. /// The library manager.. /// The item repository. /// The application paths. @@ -47,6 +44,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks /// The filesystem. /// The localization manager. public ChapterImagesTask( + ILogger logger, ILibraryManager libraryManager, IItemRepository itemRepo, IApplicationPaths appPaths, @@ -54,6 +52,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks IFileSystem fileSystem, ILocalizationManager localization) { + _logger = logger; _libraryManager = libraryManager; _itemRepo = itemRepo; _appPaths = appPaths; @@ -167,9 +166,10 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks progress.Report(100 * percent); } - catch (ObjectDisposedException) + catch (ObjectDisposedException ex) { // TODO Investigate and properly fix. + _logger.LogError(ex, "Object Disposed"); break; } } diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index cee08eedac..74abb91b28 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -1176,24 +1176,6 @@ namespace MediaBrowser.Controller.MediaEncoding ":fontsdir='{0}'", _mediaEncoder.EscapeSubtitleFilterPath(fontPath)); - // TODO - // var fallbackFontPath = Path.Combine(_appPaths.ProgramDataPath, "fonts", "DroidSansFallback.ttf"); - // string fallbackFontParam = string.Empty; - - // if (!File.Exists(fallbackFontPath)) - // { - // _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(fallbackFontPath)); - // using (var stream = _assemblyInfo.GetManifestResourceStream(GetType(), GetType().Namespace + ".DroidSansFallback.ttf")) - // { - // using (var fileStream = new FileStream(fallbackFontPath, FileMode.Create, FileAccess.Write, FileShare.Read)) - // { - // stream.CopyTo(fileStream); - // } - // } - // } - - // fallbackFontParam = string.Format(CultureInfo.InvariantCulture, ":force_style='FontName=Droid Sans Fallback':fontsdir='{0}'", _mediaEncoder.EscapeSubtitleFilterPath(_fileSystem.GetDirectoryName(fallbackFontPath))); - if (state.SubtitleStream.IsExternal) { var charsetParam = string.Empty; @@ -1221,7 +1203,6 @@ namespace MediaBrowser.Controller.MediaEncoding alphaParam, sub2videoParam, fontParam, - // fallbackFontParam, setPtsParam); } diff --git a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs index 647de50030..2fe3a54723 100644 --- a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs +++ b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs @@ -227,9 +227,10 @@ namespace MediaBrowser.Controller.Net connection.Item2.Cancel(); connection.Item2.Dispose(); } - catch (ObjectDisposedException) + catch (ObjectDisposedException ex) { // TODO Investigate and properly fix. + Logger.LogError(ex, "Object Disposed"); } lock (_activeConnections) diff --git a/src/Jellyfin.Extensions/AlphanumericComparator.cs b/src/Jellyfin.Extensions/AlphanumericComparator.cs index e3c81eba8c..98a32d5b20 100644 --- a/src/Jellyfin.Extensions/AlphanumericComparator.cs +++ b/src/Jellyfin.Extensions/AlphanumericComparator.cs @@ -128,9 +128,7 @@ namespace Jellyfin.Extensions return result; } } -#pragma warning disable SA1500 // TODO remove with StyleCop.Analyzers v1.2.0 https://github.com/DotNetAnalyzers/StyleCopAnalyzers/pull/3196 } while (pos1 < len1 && pos2 < len2); -#pragma warning restore SA1500 return len1 - len2; } diff --git a/tests/Jellyfin.Model.Tests/Dlna/StreamBuilderTests.cs b/tests/Jellyfin.Model.Tests/Dlna/StreamBuilderTests.cs index c279b6b4bb..e1bd2fe0f3 100644 --- a/tests/Jellyfin.Model.Tests/Dlna/StreamBuilderTests.cs +++ b/tests/Jellyfin.Model.Tests/Dlna/StreamBuilderTests.cs @@ -359,7 +359,7 @@ namespace Jellyfin.Model.Tests Assert.Single(val.TargetAudioCodec); // Assert.Single(val.AudioCodecs); - if (transcodeMode == "DirectStream") + if (transcodeMode.Equals("DirectStream", StringComparison.Ordinal)) { Assert.Equal(val.Container, uri.Extension); } @@ -371,14 +371,14 @@ namespace Jellyfin.Model.Tests Assert.NotEmpty(val.AudioCodecs); // Check expected container (todo: this could be a test param) - if (transcodeProtocol == "http") + if (transcodeProtocol.Equals("http", StringComparison.Ordinal)) { // Assert.Equal("webm", val.Container); Assert.Equal(val.Container, uri.Extension); Assert.Equal("stream", uri.Filename); Assert.Equal("http", val.SubProtocol); } - else if (transcodeProtocol == "HLS.mp4") + else if (transcodeProtocol.Equals("HLS.mp4", StringComparison.Ordinal)) { Assert.Equal("mp4", val.Container); Assert.Equal("m3u8", uri.Extension); @@ -394,7 +394,7 @@ namespace Jellyfin.Model.Tests } // Full transcode - if (transcodeMode == "Transcode") + if (transcodeMode.Equals("Transcode", StringComparison.Ordinal)) { if ((val.TranscodeReasons & (StreamBuilder.ContainerReasons | TranscodeReason.DirectPlayError)) == 0) { @@ -413,7 +413,7 @@ namespace Jellyfin.Model.Tests Assert.Contains(targetVideoStream.Codec, val.TargetVideoCodec); Assert.Single(val.TargetVideoCodec); - if (transcodeMode == "DirectStream") + if (transcodeMode.Equals("DirectStream", StringComparison.Ordinal)) { // Check expected audio codecs (1) if (!targetAudioStream.IsExternal) @@ -428,7 +428,7 @@ namespace Jellyfin.Model.Tests } } } - else if (transcodeMode == "Remux") + else if (transcodeMode.Equals("Remux", StringComparison.Ordinal)) { // Check expected audio codecs (1) Assert.Contains(targetAudioStream.Codec, val.AudioCodecs); -- cgit v1.2.3 From fb3e97d7acf74adc1dda7dceb5c70271d3552770 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 27 Nov 2022 14:35:07 +0100 Subject: Use typed logger --- Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs | 2 +- MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs | 4 ++-- MediaBrowser.Providers/MediaInfo/ProbeProvider.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs index 6106ae6c40..da7c8732a8 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs @@ -25,7 +25,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks /// public class ChapterImagesTask : IScheduledTask { - private readonly ILogger _logger; + private readonly ILogger _logger; private readonly ILibraryManager _libraryManager; private readonly IItemRepository _itemRepo; private readonly IApplicationPaths _appPaths; diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index 8c08ab30ef..f00023947d 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -34,7 +34,7 @@ namespace MediaBrowser.Providers.MediaInfo { public class FFProbeVideoInfo { - private readonly ILogger _logger; + private readonly ILogger _logger; private readonly IMediaEncoder _mediaEncoder; private readonly IItemRepository _itemRepo; private readonly IBlurayExaminer _blurayExaminer; @@ -51,7 +51,7 @@ namespace MediaBrowser.Providers.MediaInfo private readonly long _dummyChapterDuration = TimeSpan.FromMinutes(5).Ticks; public FFProbeVideoInfo( - ILogger logger, + ILogger logger, IMediaSourceManager mediaSourceManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, diff --git a/MediaBrowser.Providers/MediaInfo/ProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/ProbeProvider.cs index 6591366076..75f997a281 100644 --- a/MediaBrowser.Providers/MediaInfo/ProbeProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/ProbeProvider.cs @@ -83,7 +83,7 @@ namespace MediaBrowser.Providers.MediaInfo _audioResolver = new AudioResolver(loggerFactory.CreateLogger(), localization, mediaEncoder, fileSystem, namingOptions); _subtitleResolver = new SubtitleResolver(loggerFactory.CreateLogger(), localization, mediaEncoder, fileSystem, namingOptions); _videoProber = new FFProbeVideoInfo( - _logger, + loggerFactory.CreateLogger(), mediaSourceManager, mediaEncoder, itemRepo, -- cgit v1.2.3 From 87d4ef7403f6601151397da977f4e591524c1709 Mon Sep 17 00:00:00 2001 From: SteveTheGrey <118358130+SteveTheGrey@users.noreply.github.com> Date: Sun, 27 Nov 2022 16:03:28 +0000 Subject: Minor search update - full word titles matches first (#8757) --- Emby.Server.Implementations/Data/SqliteItemRepository.cs | 7 +++++++ MediaBrowser.Model/Querying/ItemSortBy.cs | 5 +++++ 2 files changed, 12 insertions(+) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 371111dffe..4f0a15df18 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -2456,6 +2456,8 @@ namespace Emby.Server.Implementations.Data builder.Append('('); builder.Append("((CleanName like @SearchTermStartsWith or (OriginalTitle not null and OriginalTitle like @SearchTermStartsWith)) * 10)"); + builder.Append("+ ((CleanName = @SearchTermStartsWith COLLATE NOCASE or (OriginalTitle not null and OriginalTitle = @SearchTermStartsWith COLLATE NOCASE)) * 10)"); + if (query.SearchTerm.Length > 1) { @@ -3154,6 +3156,11 @@ namespace Emby.Server.Implementations.Data return ItemSortBy.SimilarityScore; } + if (string.Equals(name, ItemSortBy.SearchScore, StringComparison.OrdinalIgnoreCase)) + { + return ItemSortBy.SearchScore; + } + // Unknown SortBy, just sort by the SortName. return ItemSortBy.SortName; } diff --git a/MediaBrowser.Model/Querying/ItemSortBy.cs b/MediaBrowser.Model/Querying/ItemSortBy.cs index 470507c530..1a7c9a63b0 100644 --- a/MediaBrowser.Model/Querying/ItemSortBy.cs +++ b/MediaBrowser.Model/Querying/ItemSortBy.cs @@ -154,5 +154,10 @@ namespace MediaBrowser.Model.Querying /// The similarity score. /// public const string SimilarityScore = "SimilarityScore"; + + /// + /// The search score. + /// + public const string SearchScore = "SearchScore"; } } -- cgit v1.2.3 From 8f4ac1cb811ac4d62512149a5feb01703004b058 Mon Sep 17 00:00:00 2001 From: SenorSmartyPants Date: Sun, 27 Nov 2022 13:13:11 -0600 Subject: Call GetConfiguration just once in function --- Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 6739c10c1e..f27978a449 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -1814,7 +1814,9 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV program.AddGenre("News"); } - if (GetConfiguration().SaveRecordingNFO) + var config = GetConfiguration(); + + if (config.SaveRecordingNFO) { if (timer.IsProgramSeries) { @@ -1831,7 +1833,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV } } - if (GetConfiguration().SaveRecordingImages) + if (config.SaveRecordingImages) { await SaveRecordingImages(recordingPath, program).ConfigureAwait(false); } -- cgit v1.2.3 From 0a56a45a4ac31e6a735c295bfc0d1d09e29b648f Mon Sep 17 00:00:00 2001 From: Pierre Penninckx Date: Mon, 28 Nov 2022 04:34:34 +0000 Subject: Translated using Weblate (French) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/fr/ --- Emby.Server.Implementations/Localization/Core/fr.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/fr.json b/Emby.Server.Implementations/Localization/Core/fr.json index 768245a09b..4877bcd7a7 100644 --- a/Emby.Server.Implementations/Localization/Core/fr.json +++ b/Emby.Server.Implementations/Localization/Core/fr.json @@ -15,7 +15,7 @@ "Favorites": "Favoris", "Folders": "Dossiers", "Genres": "Genres", - "HeaderAlbumArtists": "Artistes d'album", + "HeaderAlbumArtists": "Artistes de l'album", "HeaderContinueWatching": "Reprendre le visionnage", "HeaderFavoriteAlbums": "Albums favoris", "HeaderFavoriteArtists": "Artistes préférés", -- cgit v1.2.3 From 11707f8f082f3bce81a06bb024be720b43d503c0 Mon Sep 17 00:00:00 2001 From: Alejandro Martín Pérez Date: Tue, 29 Nov 2022 14:14:25 +0000 Subject: Translated using Weblate (Catalan) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ca/ --- .../Localization/Core/ca.json | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ca.json b/Emby.Server.Implementations/Localization/Core/ca.json index ab04693ccf..c0ed01fdff 100644 --- a/Emby.Server.Implementations/Localization/Core/ca.json +++ b/Emby.Server.Implementations/Localization/Core/ca.json @@ -40,16 +40,16 @@ "Movies": "Pel·lícules", "Music": "Música", "MusicVideos": "Vídeos Musicals", - "NameInstallFailed": "Instalació de {0} fallida", + "NameInstallFailed": "Instal·lació de {0} fallida", "NameSeasonNumber": "Temporada {0}", "NameSeasonUnknown": "Temporada Desconeguda", "NewVersionIsAvailable": "Una nova versió del Servidor Jellyfin està disponible per descarregar.", "NotificationOptionApplicationUpdateAvailable": "Actualització d'aplicació disponible", "NotificationOptionApplicationUpdateInstalled": "Actualització d'aplicació instal·lada", - "NotificationOptionAudioPlayback": "Reproducció d'audio iniciada", - "NotificationOptionAudioPlaybackStopped": "Reproducció d'audio aturada", + "NotificationOptionAudioPlayback": "Reproducció d'àudio iniciada", + "NotificationOptionAudioPlaybackStopped": "Reproducció d'àudio aturada", "NotificationOptionCameraImageUploaded": "Imatge de càmera pujada", - "NotificationOptionInstallationFailed": "Instalació fallida", + "NotificationOptionInstallationFailed": "Instal·lació fallida", "NotificationOptionNewLibraryContent": "Nou contingut afegit", "NotificationOptionPluginError": "Un connector ha fallat", "NotificationOptionPluginInstalled": "Connector instal·lat", @@ -72,13 +72,13 @@ "ServerNameNeedsToBeRestarted": "{0} necessita ser reiniciat", "Shows": "Sèries", "Songs": "Cançons", - "StartupEmbyServerIsLoading": "El Servidor d'Jellyfin està carregant. Si et plau, prova de nou en breus.", + "StartupEmbyServerIsLoading": "El Servidor de Jellyfin està carregant. Si et plau, prova de nou ben aviat.", "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", "SubtitleDownloadFailureFromForItem": "Els subtítols no s'han pogut baixar de {0} per {1}", "Sync": "Sincronitzar", "System": "Sistema", - "TvShows": "Espectacles de TV", - "User": "User", + "TvShows": "Sèries de TV", + "User": "Usuari", "UserCreatedWithName": "S'ha creat l'usuari {0}", "UserDeletedWithName": "L'usuari {0} ha estat eliminat", "UserDownloadingItemWithValues": "{0} està descarregant {1}", @@ -89,12 +89,12 @@ "UserPolicyUpdatedWithName": "La política d'usuari s'ha actualitzat per {0}", "UserStartedPlayingItemWithValues": "{0} ha començat a reproduir {1}", "UserStoppedPlayingItemWithValues": "{0} ha parat de reproduir {1}", - "ValueHasBeenAddedToLibrary": "{0} ha sigut afegit a la teva llibreria", + "ValueHasBeenAddedToLibrary": "{0} ha sigut afegit a la teva biblioteca", "ValueSpecialEpisodeName": "Especial - {0}", "VersionNumber": "Versió {0}", "TaskDownloadMissingSubtitlesDescription": "Cerca a internet els subtítols que faltin a partir de la configuració de metadades.", "TaskDownloadMissingSubtitles": "Descarrega els subtítols que faltin", - "TaskRefreshChannelsDescription": "Actualitza la informació dels canals d'internet.", + "TaskRefreshChannelsDescription": "Actualitza la informació dels canals d'Internet.", "TaskRefreshChannels": "Actualitza Canals", "TaskCleanTranscodeDescription": "Elimina els arxius temporals de transcodificacions que tinguin més d'un dia.", "TaskCleanTranscode": "Neteja les transcodificacions", @@ -110,7 +110,7 @@ "TaskRefreshChapterImages": "Extreure les imatges dels capítols", "TaskCleanCacheDescription": "Elimina els arxius temporals que ja no són necessaris per al servidor.", "TaskCleanCache": "Elimina arxius temporals", - "TasksChannelsCategory": "Canals d'internet", + "TasksChannelsCategory": "Canals d'Internet", "TasksApplicationCategory": "Aplicació", "TasksLibraryCategory": "Biblioteca", "TasksMaintenanceCategory": "Manteniment", @@ -118,7 +118,7 @@ "TaskCleanActivityLog": "Buidar Registre d'Activitat", "Undefined": "Indefinit", "Forced": "Forçat", - "Default": "Defecto", + "Default": "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.", -- cgit v1.2.3 From 51c21143d477bece52f66d9d7c480546a9a26cf5 Mon Sep 17 00:00:00 2001 From: NorwayFun Date: Fri, 2 Dec 2022 07:15:20 -0500 Subject: Added translation using Weblate (Georgian) --- Emby.Server.Implementations/Localization/Core/ka.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 Emby.Server.Implementations/Localization/Core/ka.json (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ka.json b/Emby.Server.Implementations/Localization/Core/ka.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/Emby.Server.Implementations/Localization/Core/ka.json @@ -0,0 +1 @@ +{} -- cgit v1.2.3 From db2c0d4c91a952407ab7709d9ff5c86017e5753e Mon Sep 17 00:00:00 2001 From: NorwayFun Date: Fri, 2 Dec 2022 12:19:40 +0000 Subject: Translated using Weblate (Georgian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ka/ --- .../Localization/Core/ka.json | 32 +++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ka.json b/Emby.Server.Implementations/Localization/Core/ka.json index 0967ef424b..78cfda3bdc 100644 --- a/Emby.Server.Implementations/Localization/Core/ka.json +++ b/Emby.Server.Implementations/Localization/Core/ka.json @@ -1 +1,31 @@ -{} +{ + "Genres": "ჟანრები", + "HeaderAlbumArtists": "ალბომის შემსრულებლები", + "HeaderFavoriteAlbums": "რჩეული ალბომები", + "TasksApplicationCategory": "აპლიკაცია", + "Albums": "ალბომები", + "AppDeviceValues": "აპი: {0}, მოწყობილობა: {1}", + "Application": "აპლიკაცია", + "Artists": "შემსრულებლები", + "AuthenticationSucceededWithUserName": "{0} -ის ავთენტიკაცია წარმატებულია", + "Books": "წიგნები", + "Forced": "ძალით", + "Inherit": "მემკვიდრეობით", + "Latest": "უახლესი", + "Movies": "ფილმები", + "Music": "მუსიკა", + "Photos": "ფოტოები", + "Playlists": "დასაკრავი სიები", + "Plugin": "დამატება", + "Shows": "სერიალები", + "Songs": "სიმღერები", + "Sync": "სინქრონიზაცია", + "System": "სისტემა", + "Undefined": "აღუწერელი", + "User": "მომხმარებელი", + "TasksMaintenanceCategory": "რემონტი", + "TasksLibraryCategory": "ბიბლიოთეკა", + "ChapterNameValue": "თავი {0}", + "HeaderContinueWatching": "ყურების გაგრძელება", + "HeaderFavoriteArtists": "რჩეული შემსრულებლები" +} -- cgit v1.2.3 From 210a4921f2bc96b8f87a9ac7c1a348fcee792329 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Mon, 5 Dec 2022 13:54:28 +0100 Subject: Fix some warnings and only disable TreatWarningsAsErrors for CodeAnalysis (#8709) --- Emby.Dlna/Emby.Dlna.csproj | 2 +- Emby.Drawing/Emby.Drawing.csproj | 4 ---- Emby.Naming/Emby.Naming.csproj | 2 +- .../Emby.Server.Implementations.csproj | 2 +- .../LiveTv/Listings/XmlTvListingsProvider.cs | 2 +- Jellyfin.Api/Jellyfin.Api.csproj | 2 +- .../Jellyfin.Server.Implementations.csproj | 2 +- MediaBrowser.Common/MediaBrowser.Common.csproj | 2 +- .../MediaBrowser.Controller.csproj | 2 +- .../MediaEncoding/EncodingHelper.cs | 1 - .../MediaEncoding/IMediaEncoder.cs | 2 +- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 3 ++- .../MediaBrowser.MediaEncoding.csproj | 2 +- MediaBrowser.Model/MediaBrowser.Model.csproj | 2 +- .../MediaBrowser.Providers.csproj | 2 +- .../Music/AlbumMetadataService.cs | 1 + jellyfin.ruleset | 2 ++ .../Entities/BaseItemTests.cs | 25 +++++++++++----------- 18 files changed, 29 insertions(+), 31 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Dlna/Emby.Dlna.csproj b/Emby.Dlna/Emby.Dlna.csproj index d59b43ef03..66aded3b42 100644 --- a/Emby.Dlna/Emby.Dlna.csproj +++ b/Emby.Dlna/Emby.Dlna.csproj @@ -23,7 +23,7 @@ - false + false diff --git a/Emby.Drawing/Emby.Drawing.csproj b/Emby.Drawing/Emby.Drawing.csproj index 8f64b0b214..177172e8a5 100644 --- a/Emby.Drawing/Emby.Drawing.csproj +++ b/Emby.Drawing/Emby.Drawing.csproj @@ -11,10 +11,6 @@ true - - false - - diff --git a/Emby.Naming/Emby.Naming.csproj b/Emby.Naming/Emby.Naming.csproj index ca002b9816..fbf0e1cec2 100644 --- a/Emby.Naming/Emby.Naming.csproj +++ b/Emby.Naming/Emby.Naming.csproj @@ -16,7 +16,7 @@ - false + false diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index eeaa3346d9..de756e1e31 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -49,7 +49,7 @@ - false + false diff --git a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs index 82f0baf32e..264eec947a 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs @@ -243,7 +243,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings { Id = c.Id, Name = c.DisplayName, - ImageUrl = c.Icon != null && !string.IsNullOrEmpty(c.Icon.Source) ? c.Icon.Source : null, + ImageUrl = string.IsNullOrEmpty(c.Icon.Source) ? null : c.Icon.Source, Number = string.IsNullOrWhiteSpace(c.Number) ? c.Id : c.Number }).ToList(); } diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj index a4502b6129..c44fdae788 100644 --- a/Jellyfin.Api/Jellyfin.Api.csproj +++ b/Jellyfin.Api/Jellyfin.Api.csproj @@ -13,7 +13,7 @@ - false + false diff --git a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj index 73e9a5882a..ba5d6a3126 100644 --- a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj +++ b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj @@ -7,7 +7,7 @@ - false + false diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj index 7a55f398a1..f058fba60a 100644 --- a/MediaBrowser.Common/MediaBrowser.Common.csproj +++ b/MediaBrowser.Common/MediaBrowser.Common.csproj @@ -39,7 +39,7 @@ - false + false diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index d4e025a43e..62c17b48c8 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -14,7 +14,7 @@ - false + false diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 5e924e4bfb..fae53acbfb 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -2814,7 +2814,6 @@ namespace MediaBrowser.Controller.MediaEncoding { algorithm = "bt.2390"; } - else if (string.Equals(options.TonemappingAlgorithm, "none", StringComparison.OrdinalIgnoreCase)) { algorithm = "clip"; diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs index 52c57b906e..fe8e9063ee 100644 --- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs +++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs @@ -38,7 +38,7 @@ namespace MediaBrowser.Controller.MediaEncoding Version EncoderVersion { get; } /// - /// Whether p key pausing is supported. + /// Gets a value indicating whether p key pausing is supported. /// /// true if p key pausing is supported, false otherwise. bool IsPkeyPauseSupported { get; } diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index b7c49ed99b..93c035c5cf 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -75,7 +75,8 @@ namespace MediaBrowser.MediaEncoding.Encoder private bool _isVaapiDeviceInteli965 = false; private bool _isVaapiDeviceSupportVulkanFmtModifier = false; - private static string[] _vulkanFmtModifierExts = { + private static string[] _vulkanFmtModifierExts = + { "VK_KHR_sampler_ycbcr_conversion", "VK_EXT_image_drm_format_modifier", "VK_KHR_external_memory_fd", diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj index afe4ff4e71..2ed654e1c6 100644 --- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj +++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj @@ -12,7 +12,7 @@ - false + false diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index 4172e9825e..7c5399b4de 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -24,7 +24,7 @@ - false + false diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index eb921e6f52..30f4a449e4 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -36,7 +36,7 @@ - false + false diff --git a/MediaBrowser.Providers/Music/AlbumMetadataService.cs b/MediaBrowser.Providers/Music/AlbumMetadataService.cs index ac40f0b3a4..58cd23aa34 100644 --- a/MediaBrowser.Providers/Music/AlbumMetadataService.cs +++ b/MediaBrowser.Providers/Music/AlbumMetadataService.cs @@ -152,6 +152,7 @@ namespace MediaBrowser.Providers.Music return ItemUpdateType.MetadataEdit; } } + return ItemUpdateType.None; } diff --git a/jellyfin.ruleset b/jellyfin.ruleset index 8144db93d5..71385cee2a 100644 --- a/jellyfin.ruleset +++ b/jellyfin.ruleset @@ -23,6 +23,8 @@ + + diff --git a/tests/Jellyfin.Controller.Tests/Entities/BaseItemTests.cs b/tests/Jellyfin.Controller.Tests/Entities/BaseItemTests.cs index 985bbcde1f..f3ada59dbc 100644 --- a/tests/Jellyfin.Controller.Tests/Entities/BaseItemTests.cs +++ b/tests/Jellyfin.Controller.Tests/Entities/BaseItemTests.cs @@ -1,18 +1,17 @@ using MediaBrowser.Controller.Entities; using Xunit; -namespace Jellyfin.Controller.Tests.Entities +namespace Jellyfin.Controller.Tests.Entities; + +public class BaseItemTests { - public class BaseItemTests - { - [Theory] - [InlineData("", "")] - [InlineData("1", "0000000001")] - [InlineData("t", "t")] - [InlineData("test", "test")] - [InlineData("test1", "test0000000001")] - [InlineData("1test 2", "0000000001test 0000000002")] - public void BaseItem_ModifySortChunks_Valid(string input, string expected) - => Assert.Equal(expected, BaseItem.ModifySortChunks(input)); - } + [Theory] + [InlineData("", "")] + [InlineData("1", "0000000001")] + [InlineData("t", "t")] + [InlineData("test", "test")] + [InlineData("test1", "test0000000001")] + [InlineData("1test 2", "0000000001test 0000000002")] + public void BaseItem_ModifySortChunks_Valid(string input, string expected) + => Assert.Equal(expected, BaseItem.ModifySortChunks(input)); } -- cgit v1.2.3 From 8eb688dc988bb287adf67af576dc5dcc4cf96979 Mon Sep 17 00:00:00 2001 From: shoddysheep Date: Sun, 4 Dec 2022 12:57:16 +0000 Subject: Translated using Weblate (Danish) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/da/ --- Emby.Server.Implementations/Localization/Core/da.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/da.json b/Emby.Server.Implementations/Localization/Core/da.json index 34655ace69..0d0d0c8136 100644 --- a/Emby.Server.Implementations/Localization/Core/da.json +++ b/Emby.Server.Implementations/Localization/Core/da.json @@ -15,8 +15,8 @@ "Favorites": "Favoritter", "Folders": "Mapper", "Genres": "Genrer", - "HeaderAlbumArtists": "Kunstnerens album", - "HeaderContinueWatching": "Fortsæt Afspilning", + "HeaderAlbumArtists": "Albumkunstner", + "HeaderContinueWatching": "Fortsæt afspilning", "HeaderFavoriteAlbums": "Favoritalbummer", "HeaderFavoriteArtists": "Favoritkunstnere", "HeaderFavoriteEpisodes": "Favoritepisoder", @@ -42,7 +42,7 @@ "MusicVideos": "Musik videoer", "NameInstallFailed": "{0} installationen mislykkedes", "NameSeasonNumber": "Sæson {0}", - "NameSeasonUnknown": "Ukendt Sæson", + "NameSeasonUnknown": "Ukendt sæson", "NewVersionIsAvailable": "En ny version af Jellyfin Server er tilgængelig til download.", "NotificationOptionApplicationUpdateAvailable": "Opdatering til applikation tilgængelig", "NotificationOptionApplicationUpdateInstalled": "Opdatering til applikation installeret", @@ -77,7 +77,7 @@ "SubtitleDownloadFailureFromForItem": "Undertekster kunne ikke downloades fra {0} til {1}", "Sync": "Synk", "System": "System", - "TvShows": "TV serier", + "TvShows": "Tv-serier", "User": "Bruger", "UserCreatedWithName": "Bruger {0} er blevet oprettet", "UserDeletedWithName": "Brugeren {0} er blevet slettet", -- cgit v1.2.3 From 3943ff03de54defc77e1621573506bcb09c19ac8 Mon Sep 17 00:00:00 2001 From: Weevild Date: Sun, 4 Dec 2022 12:47:05 +0000 Subject: Translated using Weblate (Swedish) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/sv/ --- Emby.Server.Implementations/Localization/Core/sv.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/sv.json b/Emby.Server.Implementations/Localization/Core/sv.json index af5db1976c..318a0f3cf1 100644 --- a/Emby.Server.Implementations/Localization/Core/sv.json +++ b/Emby.Server.Implementations/Localization/Core/sv.json @@ -123,5 +123,6 @@ "TaskOptimizeDatabaseDescription": "Komprimerar databasen och trunkerar ledigt utrymme. Prestandan kan förbättras genom att köra denna aktivitet efter att du har skannat biblioteket eller gjort andra förändringar som indikerar att databasen har modifierats.", "TaskKeyframeExtractorDescription": "Exporterar nyckelbildrutor från videofiler för att skapa mer exakta HLS-spellistor. Denna rutin kan ta lång tid.", "TaskKeyframeExtractor": "Extraktor för nyckelbildrutor", - "External": "Extern" + "External": "Extern", + "HearingImpaired": "Hörselskadad" } -- cgit v1.2.3 From 9ec5782555ba19a476847e4222565a4381779a68 Mon Sep 17 00:00:00 2001 From: NorwayFun Date: Sat, 3 Dec 2022 18:23:45 +0000 Subject: Translated using Weblate (Georgian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ka/ --- .../Localization/Core/ka.json | 83 +++++++++++++++++++++- 1 file changed, 82 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ka.json b/Emby.Server.Implementations/Localization/Core/ka.json index 78cfda3bdc..3a8b89f446 100644 --- a/Emby.Server.Implementations/Localization/Core/ka.json +++ b/Emby.Server.Implementations/Localization/Core/ka.json @@ -27,5 +27,86 @@ "TasksLibraryCategory": "ბიბლიოთეკა", "ChapterNameValue": "თავი {0}", "HeaderContinueWatching": "ყურების გაგრძელება", - "HeaderFavoriteArtists": "რჩეული შემსრულებლები" + "HeaderFavoriteArtists": "რჩეული შემსრულებლები", + "DeviceOfflineWithName": "{0} გაითიშა", + "External": "გარე", + "HeaderFavoriteEpisodes": "რჩეული ეპიზოდები", + "HeaderFavoriteSongs": "რჩეული სიმღერები", + "HeaderRecordingGroups": "ჩამწერი ჯგუფები", + "HearingImpaired": "სმენადაქვეითებული", + "LabelRunningTimeValue": "გაშვებულობის დრო: {0}", + "MessageApplicationUpdatedTo": "Jellyfin-ის სერვერი განახლდა {0}-ზე", + "MessageNamedServerConfigurationUpdatedWithValue": "სერვერის კონფიგურაციის სექცია {0} განახლდა", + "MixedContent": "შერეული შემცველობა", + "MusicVideos": "მუსიკის ვიდეოები", + "NotificationOptionInstallationFailed": "დაყენების შეცდომა", + "NotificationOptionApplicationUpdateInstalled": "აპლიკაციის განახლება დაყენებულია", + "NotificationOptionAudioPlayback": "აუდიოს დაკვრა დაწყებულია", + "NotificationOptionCameraImageUploaded": "კამერის გამოსახულება ატვირთულია", + "NotificationOptionVideoPlaybackStopped": "ვიდეოს დაკვრა გაჩერებულია", + "PluginUninstalledWithName": "{0} წაიშალა", + "ScheduledTaskStartedWithName": "{0} გაეშვა", + "VersionNumber": "ვერსია {0}", + "TasksChannelsCategory": "ინტერნეტ-არხები", + "ValueSpecialEpisodeName": "სპეციალური - {0}", + "TaskRefreshChannelsDescription": "ინტერნეტ-არხის ინფორმაციის განახლება.", + "Channels": "არხები", + "Collections": "კოლექციები", + "Default": "ნაგულისხმები", + "Favorites": "რჩეულები", + "Folders": "საქაღალდეები", + "HeaderFavoriteShows": "რჩეული სერიალები", + "HeaderLiveTV": "ცოცხალი TV", + "HeaderNextUp": "შემდეგი ზემოთ", + "HomeVideos": "სახლის ვიდეოები", + "NameSeasonNumber": "სეზონი {0}", + "NameSeasonUnknown": "სეზონი უცნობია", + "NotificationOptionPluginError": "დამატების შეცდომა", + "NotificationOptionPluginInstalled": "დამატება დაყენებულია", + "NotificationOptionPluginUninstalled": "დამატება წაიშალა", + "ProviderValue": "მომწოდებელი: {0}", + "ScheduledTaskFailedWithName": "{0} ავარიულია", + "TvShows": "TV სერიალები", + "TaskRefreshPeople": "ხალხის განახლება", + "TaskUpdatePlugins": "დამატებების განახლება", + "TaskRefreshChannels": "არხების განახლება", + "TaskOptimizeDatabase": "ბაზების ოპტიმიზაცია", + "TaskKeyframeExtractor": "საკვანძო კადრის გამომღები", + "DeviceOnlineWithName": "{0} შეერთებულია", + "LabelIpAddressValue": "IP მისამართი: {0}", + "NameInstallFailed": "{0}-ის დაყენების შეცდომა", + "NotificationOptionApplicationUpdateAvailable": "ხელმისაწვდომია აპლიკაციის განახლება", + "NotificationOptionAudioPlaybackStopped": "აუდიოს დაკვრა გაჩერებულია", + "NotificationOptionNewLibraryContent": "ახალი შემცველობა დამატებულია", + "NotificationOptionPluginUpdateInstalled": "დამატების განახლება დაყენებულია", + "NotificationOptionServerRestartRequired": "სერვერის გადატვირთვა აუცილებელია", + "NotificationOptionTaskFailed": "დაგეგმილი ამოცანის შეცდომა", + "NotificationOptionUserLockedOut": "მომხმარებელი დაიბლოკა", + "NotificationOptionVideoPlayback": "ვიდეოს დაკვრა დაწყებულია", + "PluginInstalledWithName": "{0} დაყენებულია", + "PluginUpdatedWithName": "{0} განახლდა", + "TaskCleanActivityLog": "აქტივობების ჟურნალის გასუფთავება", + "TaskCleanCache": "ქეშის საქაღალდის გასუფთავება", + "TaskRefreshChapterImages": "თავის სურათების გაშლა", + "TaskRefreshLibrary": "მედიის ბიბლიოთეკის სკანირება", + "TaskCleanLogs": "ჟურნალის საქაღალდის გასუფთავება", + "TaskCleanTranscode": "ტრანსკოდირების საქაღალდის გასუფთავება", + "TaskDownloadMissingSubtitles": "ნაკლული სუბტიტრების გადმოწერა", + "UserDownloadingItemWithValues": "{0} -ი {0}-ს იწერს", + "FailedLoginAttemptWithUserName": "{0}-დან შემოსვლის შეცდომა", + "MessageApplicationUpdated": "Jellyfin-ის სერვერი განახლდა", + "MessageServerConfigurationUpdated": "სერვერის კონფიგურაცია განახლდა", + "ServerNameNeedsToBeRestarted": "საჭიროა {0}-ის გადატვირთვა", + "UserCreatedWithName": "მომხმარებელი {0} შეიქმნა", + "UserDeletedWithName": "მომხმარებელი {0} წაშლილია", + "UserOnlineFromDevice": "{0}-ი ხაზზეა {1}-დან", + "UserOfflineFromDevice": "{0}-ი {1}-დან გაითიშა", + "ItemAddedWithName": "{0} ჩამატებულია ბიბლიოთეკაში", + "ItemRemovedWithName": "{0} წაშლილია ბიბლიოთეკიდან", + "UserLockedOutWithName": "მომხმარებელი {0} დაბლოკილია", + "UserStartedPlayingItemWithValues": "{0} თამაშობს {1}-ს {2}-ზე", + "UserPasswordChangedWithName": "მომხმარებლისთვის {0} პაროლი შეცვლილია", + "UserPolicyUpdatedWithName": "{0}-ის მომხმარებლის პოლიტიკა განახლდა", + "UserStoppedPlayingItemWithValues": "{0}-მა დაამთავრა {1}-ის დაკვრა {2}-ზე", + "TaskRefreshChapterImagesDescription": "თავების მქონე ვიდეოებისთვის მინიატურების შექმნა." } -- cgit v1.2.3 From b2def4c9ea6cf5e406bf5f865867d6cb5b54f640 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Mon, 5 Dec 2022 14:56:58 +0100 Subject: Fix build (#8859) --- Emby.Server.Implementations/Data/SqliteItemRepository.cs | 1 - MediaBrowser.Controller/Providers/IRemoteMetadataProvider.cs | 3 +++ MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 1 - 3 files changed, 3 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 4f0a15df18..9d44fd9d18 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -2458,7 +2458,6 @@ namespace Emby.Server.Implementations.Data builder.Append("((CleanName like @SearchTermStartsWith or (OriginalTitle not null and OriginalTitle like @SearchTermStartsWith)) * 10)"); builder.Append("+ ((CleanName = @SearchTermStartsWith COLLATE NOCASE or (OriginalTitle not null and OriginalTitle = @SearchTermStartsWith COLLATE NOCASE)) * 10)"); - if (query.SearchTerm.Length > 1) { builder.Append("+ ((CleanName like @SearchTermContains or (OriginalTitle not null and OriginalTitle like @SearchTermContains)) * 10)"); diff --git a/MediaBrowser.Controller/Providers/IRemoteMetadataProvider.cs b/MediaBrowser.Controller/Providers/IRemoteMetadataProvider.cs index 888ca6c72c..9ad0c26b0c 100644 --- a/MediaBrowser.Controller/Providers/IRemoteMetadataProvider.cs +++ b/MediaBrowser.Controller/Providers/IRemoteMetadataProvider.cs @@ -16,6 +16,8 @@ namespace MediaBrowser.Controller.Providers /// /// Interface IRemoteMetadataProvider. /// + /// The type of . + /// The type of . public interface IRemoteMetadataProvider : IMetadataProvider, IRemoteMetadataProvider, IRemoteSearchProvider where TItemType : BaseItem, IHasLookupInfo where TLookupInfoType : ItemLookupInfo, new() @@ -32,6 +34,7 @@ namespace MediaBrowser.Controller.Providers /// /// Interface IRemoteMetadataProvider. /// + /// The type of . public interface IRemoteSearchProvider : IRemoteSearchProvider where TLookupInfoType : ItemLookupInfo { diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 93c035c5cf..e50aa679a3 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -635,7 +635,6 @@ namespace MediaBrowser.MediaEncoding.Encoder return imageResolutionParameter; } - private async Task ExtractImageInternal(string inputPath, string container, MediaStream videoStream, int? imageStreamIndex, Video3DFormat? threedFormat, TimeSpan? offset, bool useIFrame, ImageFormat? targetFormat, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(inputPath)) -- cgit v1.2.3 From c7d50d640e614a3c13699e3041fbfcb258861c5a Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Mon, 5 Dec 2022 15:00:20 +0100 Subject: Replace == null with is null --- Emby.Dlna/Didl/DidlBuilder.cs | 20 ++--- Emby.Dlna/DlnaManager.cs | 8 +- Emby.Dlna/Main/DlnaEntryPoint.cs | 2 +- Emby.Dlna/PlayTo/Device.cs | 94 +++++++++++----------- Emby.Dlna/PlayTo/PlayToController.cs | 8 +- Emby.Dlna/PlayTo/PlayToManager.cs | 4 +- Emby.Dlna/Ssdp/DeviceDiscovery.cs | 6 +- Emby.Drawing/ImageProcessor.cs | 2 +- Emby.Naming/AudioBook/AudioBookListResolver.cs | 2 +- Emby.Naming/AudioBook/AudioBookNameParser.cs | 2 +- Emby.Naming/ExternalFiles/ExternalPathParser.cs | 2 +- Emby.Naming/Video/VideoListResolver.cs | 4 +- Emby.Naming/Video/VideoResolver.cs | 2 +- Emby.Notifications/NotificationEntryPoint.cs | 2 +- Emby.Notifications/NotificationManager.cs | 2 +- .../AppBase/BaseConfigurationManager.cs | 4 +- .../AppBase/ConfigurationHelper.cs | 2 +- .../Channels/ChannelManager.cs | 16 ++-- .../Collections/CollectionManager.cs | 8 +- .../Data/SqliteExtensions.cs | 2 +- .../Data/SqliteItemRepository.cs | 18 ++--- Emby.Server.Implementations/Dto/DtoService.cs | 16 ++-- .../EntryPoints/LibraryChangedNotifier.cs | 6 +- .../EntryPoints/UserDataChangeNotifier.cs | 2 +- .../HttpServer/WebSocketConnection.cs | 4 +- Emby.Server.Implementations/IO/FileRefresher.cs | 6 +- .../Library/LibraryManager.cs | 46 +++++------ .../Library/LiveStreamHelper.cs | 4 +- .../Library/MediaSourceManager.cs | 14 ++-- .../Library/ResolverHelper.cs | 2 +- .../Library/Resolvers/Audio/AudioResolver.cs | 4 +- .../Library/Resolvers/BaseVideoResolver.cs | 4 +- .../Library/Resolvers/ExtraResolver.cs | 2 +- .../Library/Resolvers/GenericFolderResolver.cs | 2 +- .../Library/Resolvers/Movies/MovieResolver.cs | 8 +- .../Library/Resolvers/TV/EpisodeResolver.cs | 6 +- .../Library/UserViewManager.cs | 4 +- .../Library/Validators/CollectionPostScanTask.cs | 2 +- .../LiveTv/EmbyTV/EmbyTV.cs | 22 ++--- .../LiveTv/EmbyTV/ItemDataProvider.cs | 2 +- .../LiveTv/Listings/SchedulesDirect.cs | 12 +-- .../LiveTv/Listings/XmlTvListingsProvider.cs | 2 +- .../LiveTv/LiveTvDtoService.cs | 8 +- .../LiveTv/LiveTvManager.cs | 22 ++--- .../LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs | 2 +- .../Localization/LocalizationManager.cs | 4 +- .../Playlists/PlaylistManager.cs | 4 +- .../Playlists/PlaylistsFolder.cs | 2 +- .../Plugins/PluginManager.cs | 10 +-- .../ScheduledTasks/ScheduledTaskWorker.cs | 2 +- .../ScheduledTasks/TaskManager.cs | 6 +- .../ScheduledTasks/Triggers/IntervalTrigger.cs | 2 +- .../Session/SessionManager.cs | 32 ++++---- .../Session/SessionWebSocketListener.cs | 2 +- .../Session/WebSocketController.cs | 2 +- .../Sorting/AiredEpisodeOrderComparer.cs | 6 +- .../Sorting/PlayCountComparer.cs | 2 +- .../Sorting/PremiereDateComparer.cs | 2 +- .../Sorting/ProductionYearComparer.cs | 2 +- Emby.Server.Implementations/SyncPlay/Group.cs | 4 +- .../SyncPlay/SyncPlayManager.cs | 22 ++--- Emby.Server.Implementations/TV/TVSeriesManager.cs | 4 +- .../Updates/InstallationManager.cs | 8 +- .../AnonymousLanAccessHandler.cs | 2 +- Jellyfin.Api/Auth/BaseAuthorizationHandler.cs | 2 +- .../Controllers/ConfigurationController.cs | 2 +- Jellyfin.Api/Controllers/DashboardController.cs | 4 +- Jellyfin.Api/Controllers/DevicesController.cs | 6 +- Jellyfin.Api/Controllers/DlnaController.cs | 6 +- Jellyfin.Api/Controllers/DlnaServerController.cs | 2 +- Jellyfin.Api/Controllers/DynamicHlsController.cs | 10 +-- Jellyfin.Api/Controllers/EnvironmentController.cs | 2 +- Jellyfin.Api/Controllers/FilterController.cs | 2 +- Jellyfin.Api/Controllers/HlsSegmentController.cs | 2 +- Jellyfin.Api/Controllers/ImageByNameController.cs | 2 +- Jellyfin.Api/Controllers/ImageController.cs | 48 +++++------ Jellyfin.Api/Controllers/ItemLookupController.cs | 2 +- Jellyfin.Api/Controllers/ItemRefreshController.cs | 2 +- Jellyfin.Api/Controllers/ItemUpdateController.cs | 4 +- Jellyfin.Api/Controllers/LibraryController.cs | 14 ++-- Jellyfin.Api/Controllers/LiveTvController.cs | 6 +- Jellyfin.Api/Controllers/MediaInfoController.cs | 2 +- Jellyfin.Api/Controllers/PackageController.cs | 4 +- Jellyfin.Api/Controllers/PersonsController.cs | 2 +- Jellyfin.Api/Controllers/PlaylistsController.cs | 2 +- Jellyfin.Api/Controllers/PlaystateController.cs | 2 +- Jellyfin.Api/Controllers/PluginsController.cs | 12 +-- Jellyfin.Api/Controllers/RemoteImageController.cs | 6 +- .../Controllers/ScheduledTasksController.cs | 8 +- Jellyfin.Api/Controllers/SearchController.cs | 2 +- Jellyfin.Api/Controllers/SessionController.cs | 2 +- Jellyfin.Api/Controllers/SubtitleController.cs | 2 +- Jellyfin.Api/Controllers/TvShowsController.cs | 2 +- Jellyfin.Api/Controllers/UserController.cs | 12 +-- Jellyfin.Api/Controllers/UserLibraryController.cs | 4 +- Jellyfin.Api/Controllers/UserViewsController.cs | 2 +- .../Controllers/VideoAttachmentsController.cs | 2 +- Jellyfin.Api/Controllers/VideosController.cs | 6 +- Jellyfin.Api/Controllers/YearsController.cs | 2 +- Jellyfin.Api/Helpers/AudioHelper.cs | 4 +- Jellyfin.Api/Helpers/DynamicHlsHelper.cs | 6 +- Jellyfin.Api/Helpers/HlsHelpers.cs | 2 +- Jellyfin.Api/Helpers/MediaInfoHelper.cs | 2 +- Jellyfin.Api/Helpers/RequestHelpers.cs | 2 +- Jellyfin.Api/Helpers/StreamingHelpers.cs | 12 +-- Jellyfin.Api/Helpers/TranscodingJobHelper.cs | 2 +- .../NullableEnumModelBinderProvider.cs | 2 +- .../Models/PlaybackDtos/TranscodingJobDto.cs | 2 +- Jellyfin.Data/Entities/User.cs | 6 +- Jellyfin.Drawing.Skia/SkiaEncoder.cs | 4 +- Jellyfin.Drawing.Skia/SplashscreenBuilder.cs | 2 +- Jellyfin.Drawing.Skia/StripCollageBuilder.cs | 4 +- Jellyfin.Networking/Manager/NetworkManager.cs | 10 +-- .../Devices/DeviceManager.cs | 4 +- .../Consumers/Session/PlaybackStartLogger.cs | 2 +- .../Events/Consumers/Session/PlaybackStopLogger.cs | 4 +- .../Events/EventManager.cs | 2 +- .../Extensions/ServiceCollectionExtensions.cs | 2 +- .../Security/AuthenticationManager.cs | 2 +- .../Users/DefaultAuthenticationProvider.cs | 4 +- .../Users/DisplayPreferencesManager.cs | 4 +- .../Users/UserManager.cs | 6 +- Jellyfin.Server/Filters/FileResponseFilter.cs | 2 +- Jellyfin.Server/Formatters/CssOutputFormatter.cs | 2 +- Jellyfin.Server/Formatters/XmlOutputFormatter.cs | 2 +- .../Routines/MigrateDisplayPreferencesDb.cs | 4 +- .../Migrations/Routines/MigrateUserDb.cs | 2 +- Jellyfin.Server/Program.cs | 2 +- .../Extensions/HttpContextExtensions.cs | 4 +- MediaBrowser.Common/Net/NetworkExtensions.cs | 4 +- MediaBrowser.Common/Plugins/BasePluginOfT.cs | 2 +- MediaBrowser.Common/Plugins/LocalPlugin.cs | 8 +- .../BaseItemManager/BaseItemManager.cs | 4 +- .../Drawing/ImageProcessorExtensions.cs | 2 +- .../Entities/AggregateFolder.cs | 2 +- MediaBrowser.Controller/Entities/BaseItem.cs | 36 ++++----- .../Entities/BaseItemExtensions.cs | 2 +- MediaBrowser.Controller/Entities/Extensions.cs | 2 +- MediaBrowser.Controller/Entities/Folder.cs | 18 ++--- .../Entities/InternalItemsQuery.cs | 2 +- MediaBrowser.Controller/Entities/ItemImageInfo.cs | 2 +- MediaBrowser.Controller/Entities/PeopleHelper.cs | 4 +- MediaBrowser.Controller/Entities/TV/Episode.cs | 14 ++-- MediaBrowser.Controller/Entities/TV/Season.cs | 8 +- MediaBrowser.Controller/Entities/TV/Series.cs | 4 +- MediaBrowser.Controller/Entities/UserRootFolder.cs | 2 +- .../Entities/UserViewBuilder.cs | 14 ++-- MediaBrowser.Controller/Library/ItemResolveArgs.cs | 8 +- MediaBrowser.Controller/Library/NameExtensions.cs | 2 +- MediaBrowser.Controller/LiveTv/LiveTvChannel.cs | 2 +- .../MediaEncoding/EncodingHelper.cs | 30 +++---- .../MediaEncoding/EncodingJobInfo.cs | 8 +- .../Providers/MetadataResult.cs | 4 +- MediaBrowser.Controller/Session/SessionInfo.cs | 12 +-- MediaBrowser.LocalMetadata/BaseXmlProvider.cs | 4 +- .../Images/EpisodeLocalImageProvider.cs | 2 +- .../Images/LocalImageProvider.cs | 2 +- .../Attachments/AttachmentExtractor.cs | 4 +- .../BdInfo/BdInfoExaminer.cs | 2 +- .../Encoder/EncoderValidator.cs | 2 +- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 2 +- .../Probing/ProbeResultNormalizer.cs | 16 ++-- MediaBrowser.Model/Dlna/AudioOptions.cs | 2 +- MediaBrowser.Model/Dlna/ContainerProfile.cs | 2 +- MediaBrowser.Model/Dlna/StreamBuilder.cs | 44 +++++----- MediaBrowser.Model/Dlna/StreamInfo.cs | 4 +- MediaBrowser.Model/Drawing/DrawingUtils.cs | 8 +- MediaBrowser.Model/Dto/MediaSourceInfo.cs | 2 +- .../Entities/ProviderIdsExtensions.cs | 2 +- .../Notifications/NotificationOptions.cs | 2 +- MediaBrowser.Model/Updates/VersionInfo.cs | 2 +- .../BoxSets/BoxSetMetadataService.cs | 2 +- .../Manager/ItemImageProvider.cs | 6 +- MediaBrowser.Providers/Manager/MetadataService.cs | 8 +- MediaBrowser.Providers/Manager/ProviderManager.cs | 12 +-- .../MediaInfo/EmbeddedImageProvider.cs | 2 +- .../MediaInfo/FFProbeVideoInfo.cs | 4 +- MediaBrowser.Providers/MediaInfo/ProbeProvider.cs | 2 +- .../MediaInfo/SubtitleScheduledTask.cs | 4 +- .../MediaInfo/VideoImageProvider.cs | 4 +- .../Omdb/JsonOmdbNotAvailableInt32Converter.cs | 2 +- .../Plugins/Omdb/OmdbProvider.cs | 6 +- .../Plugins/StudioImages/StudiosImageProvider.cs | 2 +- .../Tmdb/BoxSets/TmdbBoxSetImageProvider.cs | 2 +- .../Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs | 2 +- .../Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs | 2 +- .../Plugins/Tmdb/Movies/TmdbMovieProvider.cs | 2 +- .../Plugins/Tmdb/People/TmdbPersonImageProvider.cs | 2 +- .../Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs | 2 +- .../Plugins/Tmdb/TV/TmdbEpisodeProvider.cs | 2 +- .../Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs | 4 +- .../Plugins/Tmdb/TV/TmdbSeasonProvider.cs | 2 +- .../Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs | 2 +- .../Plugins/Tmdb/TmdbClientManager.cs | 4 +- MediaBrowser.Providers/TV/SeriesMetadataService.cs | 4 +- MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs | 4 +- .../Providers/BaseNfoProvider.cs | 4 +- MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs | 2 +- src/Jellyfin.Extensions/AlphanumericComparator.cs | 6 +- .../Dlna/StreamBuilderTests.cs | 2 +- tests/Jellyfin.Naming.Tests/Video/Format3DTests.cs | 2 +- .../Video/MultiVersionTests.cs | 4 +- .../Manager/MetadataServiceTests.cs | 8 +- .../Manager/ProviderManagerTests.cs | 6 +- .../MediaInfo/EmbeddedImageProviderTests.cs | 4 +- .../TypedBaseItem/BaseItemKindTests.cs | 2 +- 206 files changed, 627 insertions(+), 627 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Dlna/Didl/DidlBuilder.cs b/Emby.Dlna/Didl/DidlBuilder.cs index 8e3a335c66..e2e3b2d8b6 100644 --- a/Emby.Dlna/Didl/DidlBuilder.cs +++ b/Emby.Dlna/Didl/DidlBuilder.cs @@ -191,7 +191,7 @@ namespace Emby.Dlna.Didl private void AddVideoResource(XmlWriter writer, BaseItem video, string deviceId, Filter filter, StreamInfo streamInfo = null) { - if (streamInfo == null) + if (streamInfo is null) { var sources = _mediaSourceManager.GetStaticMediaSources(video, true, _user); @@ -263,7 +263,7 @@ namespace Emby.Dlna.Didl .FirstOrDefault(i => string.Equals(info.Format, i.Format, StringComparison.OrdinalIgnoreCase) && i.Method == SubtitleDeliveryMethod.External); - if (subtitleProfile == null) + if (subtitleProfile is null) { return false; } @@ -392,7 +392,7 @@ namespace Emby.Dlna.Didl var filename = url.Substring(0, url.IndexOf('?', StringComparison.Ordinal)); - var mimeType = mediaProfile == null || string.IsNullOrEmpty(mediaProfile.MimeType) + var mimeType = mediaProfile is null || string.IsNullOrEmpty(mediaProfile.MimeType) ? MimeTypes.GetMimeType(filename) : mediaProfile.MimeType; @@ -533,7 +533,7 @@ namespace Emby.Dlna.Didl { writer.WriteStartElement(string.Empty, "res", NsDidl); - if (streamInfo == null) + if (streamInfo is null) { var sources = _mediaSourceManager.GetStaticMediaSources(audio, true, _user); @@ -598,7 +598,7 @@ namespace Emby.Dlna.Didl var filename = url.Substring(0, url.IndexOf('?', StringComparison.Ordinal)); - var mimeType = mediaProfile == null || string.IsNullOrEmpty(mediaProfile.MimeType) + var mimeType = mediaProfile is null || string.IsNullOrEmpty(mediaProfile.MimeType) ? MimeTypes.GetMimeType(filename) : mediaProfile.MimeType; @@ -695,7 +695,7 @@ namespace Emby.Dlna.Didl } // Not a samsung device - if (secAttribute == null) + if (secAttribute is null) { return; } @@ -909,7 +909,7 @@ namespace Emby.Dlna.Didl AddValue(writer, "dc", "creator", artist, NsDc); // If it doesn't support album artists (musicvideo), then tag as both - if (hasAlbumArtists == null) + if (hasAlbumArtists is null) { AddAlbumArtist(writer, artist); } @@ -973,7 +973,7 @@ namespace Emby.Dlna.Didl { ImageDownloadInfo imageInfo = GetImageInfo(item); - if (imageInfo == null) + if (imageInfo is null) { return; } @@ -1036,7 +1036,7 @@ namespace Emby.Dlna.Didl { var imageInfo = GetImageInfo(item); - if (imageInfo == null) + if (imageInfo is null) { return; } @@ -1116,7 +1116,7 @@ namespace Emby.Dlna.Didl private BaseItem GetFirstParentWithImageBelowUserRoot(BaseItem item) { - if (item == null) + if (item is null) { return null; } diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs index 2ea2c130f2..31b999f64c 100644 --- a/Emby.Dlna/DlnaManager.cs +++ b/Emby.Dlna/DlnaManager.cs @@ -107,7 +107,7 @@ namespace Emby.Dlna var profile = GetProfiles() .FirstOrDefault(i => i.Identification != null && IsMatch(deviceInfo, i.Identification)); - if (profile == null) + if (profile is null) { _logger.LogInformation("No matching device profile found. The default will need to be used. \n{@Profile}", deviceInfo); } @@ -172,7 +172,7 @@ namespace Emby.Dlna ArgumentNullException.ThrowIfNull(headers); var profile = GetProfiles().FirstOrDefault(i => i.Identification != null && IsMatch(headers, i.Identification)); - if (profile == null) + if (profile is null) { _logger.LogDebug("No matching device profile found. {@Headers}", headers); } @@ -272,7 +272,7 @@ namespace Emby.Dlna var info = GetProfileInfosInternal().FirstOrDefault(i => string.Equals(i.Info.Id, id, StringComparison.OrdinalIgnoreCase)); - if (info == null) + if (info is null) { return null; } @@ -470,7 +470,7 @@ namespace Emby.Dlna var resource = GetType().Namespace + ".Images." + filename.ToLowerInvariant(); var stream = _assembly.GetManifestResourceStream(resource); - if (stream == null) + if (stream is null) { return null; } diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs index 15021c19d6..bcd7ece085 100644 --- a/Emby.Dlna/Main/DlnaEntryPoint.cs +++ b/Emby.Dlna/Main/DlnaEntryPoint.cs @@ -199,7 +199,7 @@ namespace Emby.Dlna.Main { try { - if (_communicationsServer == null) + if (_communicationsServer is null) { var enableMultiSocketBinding = OperatingSystem.IsWindows() || OperatingSystem.IsLinux(); diff --git a/Emby.Dlna/PlayTo/Device.cs b/Emby.Dlna/PlayTo/Device.cs index 34981bd3f4..1c7730187b 100644 --- a/Emby.Dlna/PlayTo/Device.cs +++ b/Emby.Dlna/PlayTo/Device.cs @@ -220,14 +220,14 @@ namespace Emby.Dlna.PlayTo var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false); var command = rendererCommands?.ServiceActions.FirstOrDefault(c => c.Name == "SetMute"); - if (command == null) + if (command is null) { return false; } var service = GetServiceRenderingControl(); - if (service == null) + if (service is null) { return false; } @@ -260,14 +260,14 @@ namespace Emby.Dlna.PlayTo var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false); var command = rendererCommands?.ServiceActions.FirstOrDefault(c => c.Name == "SetVolume"); - if (command == null) + if (command is null) { return; } var service = GetServiceRenderingControl(); - if (service == null) + if (service is null) { throw new InvalidOperationException("Unable to find service"); } @@ -291,14 +291,14 @@ namespace Emby.Dlna.PlayTo var avCommands = await GetAVProtocolAsync(cancellationToken).ConfigureAwait(false); var command = avCommands?.ServiceActions.FirstOrDefault(c => c.Name == "Seek"); - if (command == null) + if (command is null) { return; } var service = GetAvTransportService(); - if (service == null) + if (service is null) { throw new InvalidOperationException("Unable to find service"); } @@ -324,7 +324,7 @@ namespace Emby.Dlna.PlayTo _logger.LogDebug("{0} - SetAvTransport Uri: {1} DlnaHeaders: {2}", Properties.Name, url, header); var command = avCommands?.ServiceActions.FirstOrDefault(c => c.Name == "SetAVTransportURI"); - if (command == null) + if (command is null) { return; } @@ -337,7 +337,7 @@ namespace Emby.Dlna.PlayTo var service = GetAvTransportService(); - if (service == null) + if (service is null) { throw new InvalidOperationException("Unable to find service"); } @@ -381,7 +381,7 @@ namespace Emby.Dlna.PlayTo _logger.LogDebug("{PropertyName} - SetNextAvTransport Uri: {Url} DlnaHeaders: {Header}", Properties.Name, url, header); var command = avCommands.ServiceActions.FirstOrDefault(c => string.Equals(c.Name, "SetNextAVTransportURI", StringComparison.OrdinalIgnoreCase)); - if (command == null) + if (command is null) { return; } @@ -394,7 +394,7 @@ namespace Emby.Dlna.PlayTo var service = GetAvTransportService(); - if (service == null) + if (service is null) { throw new InvalidOperationException("Unable to find service"); } @@ -418,13 +418,13 @@ namespace Emby.Dlna.PlayTo private Task SetPlay(TransportCommands avCommands, CancellationToken cancellationToken) { var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "Play"); - if (command == null) + if (command is null) { return Task.CompletedTask; } var service = GetAvTransportService(); - if (service == null) + if (service is null) { throw new InvalidOperationException("Unable to find service"); } @@ -440,7 +440,7 @@ namespace Emby.Dlna.PlayTo public async Task SetPlay(CancellationToken cancellationToken) { var avCommands = await GetAVProtocolAsync(cancellationToken).ConfigureAwait(false); - if (avCommands == null) + if (avCommands is null) { return; } @@ -455,7 +455,7 @@ namespace Emby.Dlna.PlayTo var avCommands = await GetAVProtocolAsync(cancellationToken).ConfigureAwait(false); var command = avCommands?.ServiceActions.FirstOrDefault(c => c.Name == "Stop"); - if (command == null) + if (command is null) { return; } @@ -479,7 +479,7 @@ namespace Emby.Dlna.PlayTo var avCommands = await GetAVProtocolAsync(cancellationToken).ConfigureAwait(false); var command = avCommands?.ServiceActions.FirstOrDefault(c => c.Name == "Pause"); - if (command == null) + if (command is null) { return; } @@ -513,7 +513,7 @@ namespace Emby.Dlna.PlayTo var avCommands = await GetAVProtocolAsync(cancellationToken).ConfigureAwait(false); - if (avCommands == null) + if (avCommands is null) { return; } @@ -538,7 +538,7 @@ namespace Emby.Dlna.PlayTo var currentObject = tuple.Track; - if (tuple.Success && currentObject == null) + if (tuple.Success && currentObject is null) { currentObject = await GetMediaInfo(avCommands, cancellationToken).ConfigureAwait(false); } @@ -607,14 +607,14 @@ namespace Emby.Dlna.PlayTo var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false); var command = rendererCommands?.ServiceActions.FirstOrDefault(c => c.Name == "GetVolume"); - if (command == null) + if (command is null) { return; } var service = GetServiceRenderingControl(); - if (service == null) + if (service is null) { return; } @@ -626,7 +626,7 @@ namespace Emby.Dlna.PlayTo rendererCommands.BuildPost(command, service.ServiceType), cancellationToken: cancellationToken).ConfigureAwait(false); - if (result == null || result.Document == null) + if (result is null || result.Document is null) { return; } @@ -657,14 +657,14 @@ namespace Emby.Dlna.PlayTo var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false); var command = rendererCommands?.ServiceActions.FirstOrDefault(c => c.Name == "GetMute"); - if (command == null) + if (command is null) { return; } var service = GetServiceRenderingControl(); - if (service == null) + if (service is null) { return; } @@ -676,7 +676,7 @@ namespace Emby.Dlna.PlayTo rendererCommands.BuildPost(command, service.ServiceType), cancellationToken: cancellationToken).ConfigureAwait(false); - if (result == null || result.Document == null) + if (result is null || result.Document is null) { return; } @@ -691,13 +691,13 @@ namespace Emby.Dlna.PlayTo private async Task GetTransportInfo(TransportCommands avCommands, CancellationToken cancellationToken) { var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetTransportInfo"); - if (command == null) + if (command is null) { return null; } var service = GetAvTransportService(); - if (service == null) + if (service is null) { return null; } @@ -709,7 +709,7 @@ namespace Emby.Dlna.PlayTo avCommands.BuildPost(command, service.ServiceType), cancellationToken: cancellationToken).ConfigureAwait(false); - if (result == null || result.Document == null) + if (result is null || result.Document is null) { return null; } @@ -731,19 +731,19 @@ namespace Emby.Dlna.PlayTo private async Task GetMediaInfo(TransportCommands avCommands, CancellationToken cancellationToken) { var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetMediaInfo"); - if (command == null) + if (command is null) { return null; } var service = GetAvTransportService(); - if (service == null) + if (service is null) { throw new InvalidOperationException("Unable to find service"); } var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false); - if (rendererCommands == null) + if (rendererCommands is null) { return null; } @@ -755,14 +755,14 @@ namespace Emby.Dlna.PlayTo rendererCommands.BuildPost(command, service.ServiceType), cancellationToken: cancellationToken).ConfigureAwait(false); - if (result == null || result.Document == null) + if (result is null || result.Document is null) { return null; } var track = result.Document.Descendants("CurrentURIMetaData").FirstOrDefault(); - if (track == null) + if (track is null) { return null; } @@ -778,7 +778,7 @@ namespace Emby.Dlna.PlayTo track = result.Document.Descendants("CurrentURI").FirstOrDefault(); - if (track == null) + if (track is null) { return null; } @@ -801,21 +801,21 @@ namespace Emby.Dlna.PlayTo private async Task<(bool Success, UBaseObject Track)> GetPositionInfo(TransportCommands avCommands, CancellationToken cancellationToken) { var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetPositionInfo"); - if (command == null) + if (command is null) { return (false, null); } var service = GetAvTransportService(); - if (service == null) + if (service is null) { throw new InvalidOperationException("Unable to find service"); } var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false); - if (rendererCommands == null) + if (rendererCommands is null) { return (false, null); } @@ -827,7 +827,7 @@ namespace Emby.Dlna.PlayTo rendererCommands.BuildPost(command, service.ServiceType), cancellationToken: cancellationToken).ConfigureAwait(false); - if (result == null || result.Document == null) + if (result is null || result.Document is null) { return (false, null); } @@ -858,7 +858,7 @@ namespace Emby.Dlna.PlayTo var track = result.Document.Descendants("TrackMetaData").FirstOrDefault(); - if (track == null) + if (track is null) { // If track is null, some vendors do this, use GetMediaInfo instead. return (true, null); @@ -882,7 +882,7 @@ namespace Emby.Dlna.PlayTo _logger.LogError(ex, "Uncaught exception while parsing xml"); } - if (uPnpResponse == null) + if (uPnpResponse is null) { _logger.LogError("Failed to parse xml: \n {Xml}", trackString); return (true, null); @@ -985,7 +985,7 @@ namespace Emby.Dlna.PlayTo } var avService = GetAvTransportService(); - if (avService == null) + if (avService is null) { return null; } @@ -995,7 +995,7 @@ namespace Emby.Dlna.PlayTo var httpClient = new DlnaHttpClient(_logger, _httpClientFactory); var document = await httpClient.GetDataAsync(url, cancellationToken).ConfigureAwait(false); - if (document == null) + if (document is null) { return null; } @@ -1017,7 +1017,7 @@ namespace Emby.Dlna.PlayTo } var avService = GetServiceRenderingControl(); - if (avService == null) + if (avService is null) { throw new ArgumentException("Device AvService is null"); } @@ -1027,7 +1027,7 @@ namespace Emby.Dlna.PlayTo var httpClient = new DlnaHttpClient(_logger, _httpClientFactory); _logger.LogDebug("Dlna Device.GetRenderingProtocolAsync"); var document = await httpClient.GetDataAsync(url, cancellationToken).ConfigureAwait(false); - if (document == null) + if (document is null) { return null; } @@ -1062,7 +1062,7 @@ namespace Emby.Dlna.PlayTo var ssdpHttpClient = new DlnaHttpClient(logger, httpClientFactory); var document = await ssdpHttpClient.GetDataAsync(url.ToString(), cancellationToken).ConfigureAwait(false); - if (document == null) + if (document is null) { return null; } @@ -1149,13 +1149,13 @@ namespace Emby.Dlna.PlayTo foreach (var services in document.Descendants(UPnpNamespaces.Ud.GetName("serviceList"))) { - if (services == null) + if (services is null) { continue; } var servicesList = services.Descendants(UPnpNamespaces.Ud.GetName("service")); - if (servicesList == null) + if (servicesList is null) { continue; } @@ -1212,14 +1212,14 @@ namespace Emby.Dlna.PlayTo var previousMediaInfo = CurrentMediaInfo; CurrentMediaInfo = mediaInfo; - if (mediaInfo == null) + if (mediaInfo is null) { if (previousMediaInfo != null) { OnPlaybackStop(previousMediaInfo); } } - else if (previousMediaInfo == null) + else if (previousMediaInfo is null) { if (state != TransportState.STOPPED) { diff --git a/Emby.Dlna/PlayTo/PlayToController.cs b/Emby.Dlna/PlayTo/PlayToController.cs index 65367e24f2..fca5fc2a0d 100644 --- a/Emby.Dlna/PlayTo/PlayToController.cs +++ b/Emby.Dlna/PlayTo/PlayToController.cs @@ -164,7 +164,7 @@ namespace Emby.Dlna.PlayTo } streamInfo = StreamParams.ParseFromUrl(e.NewMediaInfo.Url, _libraryManager, _mediaSourceManager); - if (streamInfo.Item == null) + if (streamInfo.Item is null) { return; } @@ -199,7 +199,7 @@ namespace Emby.Dlna.PlayTo { var streamInfo = StreamParams.ParseFromUrl(e.MediaInfo.Url, _libraryManager, _mediaSourceManager); - if (streamInfo.Item == null) + if (streamInfo.Item is null) { return; } @@ -210,7 +210,7 @@ namespace Emby.Dlna.PlayTo var mediaSource = await streamInfo.GetMediaSource(CancellationToken.None).ConfigureAwait(false); - var duration = mediaSource == null + var duration = mediaSource is null ? _device.Duration?.Ticks : mediaSource.RunTimeTicks; @@ -865,7 +865,7 @@ namespace Emby.Dlna.PlayTo throw new ObjectDisposedException(GetType().Name); } - if (_device == null) + if (_device is null) { return Task.CompletedTask; } diff --git a/Emby.Dlna/PlayTo/PlayToManager.cs b/Emby.Dlna/PlayTo/PlayToManager.cs index 294bda5b6a..f4a9a90af4 100644 --- a/Emby.Dlna/PlayTo/PlayToManager.cs +++ b/Emby.Dlna/PlayTo/PlayToManager.cs @@ -176,10 +176,10 @@ namespace Emby.Dlna.PlayTo var controller = sessionInfo.SessionControllers.OfType().FirstOrDefault(); - if (controller == null) + if (controller is null) { var device = await Device.CreateuPnpDeviceAsync(uri, _httpClientFactory, _logger, cancellationToken).ConfigureAwait(false); - if (device == null) + if (device is null) { _logger.LogError("Ignoring device as xml response is invalid."); return; diff --git a/Emby.Dlna/Ssdp/DeviceDiscovery.cs b/Emby.Dlna/Ssdp/DeviceDiscovery.cs index 391dda1479..bb58a5d1b6 100644 --- a/Emby.Dlna/Ssdp/DeviceDiscovery.cs +++ b/Emby.Dlna/Ssdp/DeviceDiscovery.cs @@ -71,7 +71,7 @@ namespace Emby.Dlna.Ssdp { lock (_syncLock) { - if (_listenerCount > 0 && _deviceLocator == null && _commsServer != null) + if (_listenerCount > 0 && _deviceLocator is null && _commsServer != null) { _deviceLocator = new SsdpDeviceLocator(_commsServer); @@ -97,7 +97,7 @@ namespace Emby.Dlna.Ssdp { var originalHeaders = e.DiscoveredDevice.ResponseHeaders; - var headerDict = originalHeaders == null ? new Dictionary>>() : originalHeaders.ToDictionary(i => i.Key, StringComparer.OrdinalIgnoreCase); + var headerDict = originalHeaders is null ? new Dictionary>>() : originalHeaders.ToDictionary(i => i.Key, StringComparer.OrdinalIgnoreCase); var headers = headerDict.ToDictionary(i => i.Key, i => i.Value.Value.FirstOrDefault(), StringComparer.OrdinalIgnoreCase); @@ -116,7 +116,7 @@ namespace Emby.Dlna.Ssdp { var originalHeaders = e.DiscoveredDevice.ResponseHeaders; - var headerDict = originalHeaders == null ? new Dictionary>>() : originalHeaders.ToDictionary(i => i.Key, StringComparer.OrdinalIgnoreCase); + var headerDict = originalHeaders is null ? new Dictionary>>() : originalHeaders.ToDictionary(i => i.Key, StringComparer.OrdinalIgnoreCase); var headers = headerDict.ToDictionary(i => i.Key, i => i.Value.Value.FirstOrDefault(), StringComparer.OrdinalIgnoreCase); diff --git a/Emby.Drawing/ImageProcessor.cs b/Emby.Drawing/ImageProcessor.cs index 11256dafde..d2c430415a 100644 --- a/Emby.Drawing/ImageProcessor.cs +++ b/Emby.Drawing/ImageProcessor.cs @@ -436,7 +436,7 @@ namespace Emby.Drawing /// public string? GetImageCacheTag(User user) { - if (user.ProfileImage == null) + if (user.ProfileImage is null) { return null; } diff --git a/Emby.Naming/AudioBook/AudioBookListResolver.cs b/Emby.Naming/AudioBook/AudioBookListResolver.cs index 6e491185d8..7a241aab2b 100644 --- a/Emby.Naming/AudioBook/AudioBookListResolver.cs +++ b/Emby.Naming/AudioBook/AudioBookListResolver.cs @@ -75,7 +75,7 @@ namespace Emby.Naming.AudioBook foreach (var group in groupedBy) { - if (group.Key.ChapterNumber == null && group.Key.PartNumber == null) + if (group.Key.ChapterNumber is null && group.Key.PartNumber is null) { if (group.Count() > 1 || haveChaptersOrPages) { diff --git a/Emby.Naming/AudioBook/AudioBookNameParser.cs b/Emby.Naming/AudioBook/AudioBookNameParser.cs index 120482bc2c..97b34199e0 100644 --- a/Emby.Naming/AudioBook/AudioBookNameParser.cs +++ b/Emby.Naming/AudioBook/AudioBookNameParser.cs @@ -33,7 +33,7 @@ namespace Emby.Naming.AudioBook var match = new Regex(expression, RegexOptions.IgnoreCase).Match(name); if (match.Success) { - if (result.Name == null) + if (result.Name is null) { var value = match.Groups["name"]; if (value.Success) diff --git a/Emby.Naming/ExternalFiles/ExternalPathParser.cs b/Emby.Naming/ExternalFiles/ExternalPathParser.cs index 1fa4fa5371..cfe3256e3d 100644 --- a/Emby.Naming/ExternalFiles/ExternalPathParser.cs +++ b/Emby.Naming/ExternalFiles/ExternalPathParser.cs @@ -94,7 +94,7 @@ namespace Emby.Naming.ExternalFiles // Try to translate to three character code var culture = _localizationManager.FindLanguageInfo(currentSliceWithoutSeparator); - if (culture != null && pathInfo.Language == null) + if (culture != null && pathInfo.Language is null) { pathInfo.Language = culture.ThreeLetterISOLanguageName; extraString = extraString.Replace(currentSlice, string.Empty, StringComparison.OrdinalIgnoreCase); diff --git a/Emby.Naming/Video/VideoListResolver.cs b/Emby.Naming/Video/VideoListResolver.cs index 11f82525f3..1366bdb2c3 100644 --- a/Emby.Naming/Video/VideoListResolver.cs +++ b/Emby.Naming/Video/VideoListResolver.cs @@ -26,7 +26,7 @@ namespace Emby.Naming.Video // Filter out all extras, otherwise they could cause stacks to not be resolved // See the unit test TestStackedWithTrailer var nonExtras = videoInfos - .Where(i => i.ExtraType == null) + .Where(i => i.ExtraType is null) .Select(i => new FileSystemMetadata { FullName = i.Path, IsDirectory = i.IsDirectory }); var stackResult = StackResolver.Resolve(nonExtras, namingOptions).ToList(); @@ -42,7 +42,7 @@ namespace Emby.Naming.Video continue; } - if (current.ExtraType == null) + if (current.ExtraType is null) { standaloneMedia.Add(current); } diff --git a/Emby.Naming/Video/VideoResolver.cs b/Emby.Naming/Video/VideoResolver.cs index de8e177d8b..858e9dd2f5 100644 --- a/Emby.Naming/Video/VideoResolver.cs +++ b/Emby.Naming/Video/VideoResolver.cs @@ -87,7 +87,7 @@ namespace Emby.Naming.Video name = cleanDateTimeResult.Name; year = cleanDateTimeResult.Year; - if (extraResult.ExtraType == null + if (extraResult.ExtraType is null && TryCleanString(name, namingOptions, out var newName)) { name = newName; diff --git a/Emby.Notifications/NotificationEntryPoint.cs b/Emby.Notifications/NotificationEntryPoint.cs index 668c059b47..3763b1e922 100644 --- a/Emby.Notifications/NotificationEntryPoint.cs +++ b/Emby.Notifications/NotificationEntryPoint.cs @@ -141,7 +141,7 @@ namespace Emby.Notifications lock (_libraryChangedSyncLock) { - if (_libraryUpdateTimer == null) + if (_libraryUpdateTimer is null) { _libraryUpdateTimer = new Timer( LibraryUpdateTimerCallback, diff --git a/Emby.Notifications/NotificationManager.cs b/Emby.Notifications/NotificationManager.cs index ac90cc8ec5..2b9d2c5774 100644 --- a/Emby.Notifications/NotificationManager.cs +++ b/Emby.Notifications/NotificationManager.cs @@ -68,7 +68,7 @@ namespace Emby.Notifications var users = GetUserIds(request, options) .Select(i => _userManager.GetUserById(i)) - .Where(i => relatedItem == null || relatedItem.IsVisibleStandalone(i)) + .Where(i => relatedItem is null || relatedItem.IsVisibleStandalone(i)) .ToArray(); var title = request.Name; diff --git a/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs index 26b4649dd8..5876c9b4c6 100644 --- a/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs +++ b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs @@ -144,7 +144,7 @@ namespace Emby.Server.Implementations.AppBase { IConfigurationFactory factory = Activator.CreateInstance(); - if (_configurationFactories == null) + if (_configurationFactories is null) { _configurationFactories = new[] { factory }; } @@ -306,7 +306,7 @@ namespace Emby.Server.Implementations.AppBase configurationManager._configurationStores, i => string.Equals(i.Key, k, StringComparison.OrdinalIgnoreCase)); - if (configurationInfo == null) + if (configurationInfo is null) { throw new ResourceNotFoundException("Configuration with key " + k + " not found."); } diff --git a/Emby.Server.Implementations/AppBase/ConfigurationHelper.cs b/Emby.Server.Implementations/AppBase/ConfigurationHelper.cs index f923e59efb..1c84776059 100644 --- a/Emby.Server.Implementations/AppBase/ConfigurationHelper.cs +++ b/Emby.Server.Implementations/AppBase/ConfigurationHelper.cs @@ -43,7 +43,7 @@ namespace Emby.Server.Implementations.AppBase Span newBytes = stream.GetBuffer().AsSpan(0, (int)stream.Length); // If the file didn't exist before, or if something has changed, re-save - if (buffer == null || !newBytes.SequenceEqual(buffer)) + if (buffer is null || !newBytes.SequenceEqual(buffer)) { var directory = Path.GetDirectoryName(path) ?? throw new ArgumentException($"Provided path ({path}) is not valid.", nameof(path)); diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs index 6837cce5cc..d7eb7e3aca 100644 --- a/Emby.Server.Implementations/Channels/ChannelManager.cs +++ b/Emby.Server.Implementations/Channels/ChannelManager.cs @@ -129,7 +129,7 @@ namespace Emby.Server.Implementations.Channels public Task DeleteItem(BaseItem item) { var internalChannel = _libraryManager.GetItemById(item.ChannelId); - if (internalChannel == null) + if (internalChannel is null) { throw new ArgumentException(nameof(item.ChannelId)); } @@ -253,7 +253,7 @@ namespace Emby.Server.Implementations.Channels if (query.StartIndex.HasValue || query.Limit.HasValue) { int startIndex = query.StartIndex ?? 0; - int count = query.Limit == null ? totalCount - startIndex : Math.Min(query.Limit.Value, totalCount - startIndex); + int count = query.Limit is null ? totalCount - startIndex : Math.Min(query.Limit.Value, totalCount - startIndex); all = all.GetRange(startIndex, count); } @@ -355,7 +355,7 @@ namespace Emby.Server.Implementations.Channels { var path = Path.Combine(item.GetInternalMetadataPath(), "channelmediasourceinfos.json"); - if (mediaSources == null || mediaSources.Count == 0) + if (mediaSources is null || mediaSources.Count == 0) { try { @@ -447,7 +447,7 @@ namespace Emby.Server.Implementations.Channels var item = _libraryManager.GetItemById(id) as Channel; - if (item == null) + if (item is null) { item = new Channel { @@ -861,7 +861,7 @@ namespace Emby.Server.Implementations.Channels var result = await channel.GetChannelItems(query, cancellationToken).ConfigureAwait(false); - if (result == null) + if (result is null) { throw new InvalidOperationException("Channel returned a null result from GetChannelItems"); } @@ -955,7 +955,7 @@ namespace Emby.Server.Implementations.Channels _logger.LogError(ex, "Error retrieving channel item from database"); } - if (item == null) + if (item is null) { item = new T(); isNew = true; @@ -1193,7 +1193,7 @@ namespace Emby.Server.Implementations.Channels var result = GetAllChannels() .FirstOrDefault(i => GetInternalChannelId(i.Name).Equals(channel.ChannelId) || string.Equals(i.Name, channel.Name, StringComparison.OrdinalIgnoreCase)); - if (result == null) + if (result is null) { throw new ResourceNotFoundException("No channel provider found for channel " + channel.Name); } @@ -1206,7 +1206,7 @@ namespace Emby.Server.Implementations.Channels var result = GetAllChannels() .FirstOrDefault(i => internalChannelId.Equals(GetInternalChannelId(i.Name))); - if (result == null) + if (result is null) { throw new ResourceNotFoundException("No channel provider found for channel id " + internalChannelId); } diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs index 187e0c9b3b..701c7b61dc 100644 --- a/Emby.Server.Implementations/Collections/CollectionManager.cs +++ b/Emby.Server.Implementations/Collections/CollectionManager.cs @@ -121,7 +121,7 @@ namespace Emby.Server.Implementations.Collections { var folder = GetCollectionsFolder(false).GetAwaiter().GetResult(); - return folder == null + return folder is null ? Enumerable.Empty() : folder.GetChildren(user, true).OfType(); } @@ -138,7 +138,7 @@ namespace Emby.Server.Implementations.Collections var parentFolder = await GetCollectionsFolder(true).ConfigureAwait(false); - if (parentFolder == null) + if (parentFolder is null) { throw new ArgumentException(nameof(parentFolder)); } @@ -216,7 +216,7 @@ namespace Emby.Server.Implementations.Collections { var item = _libraryManager.GetItemById(id); - if (item == null) + if (item is null) { throw new ArgumentException("No item exists with the supplied Id"); } @@ -267,7 +267,7 @@ namespace Emby.Server.Implementations.Collections var child = collection.LinkedChildren.FirstOrDefault(i => (i.ItemId.HasValue && i.ItemId.Value.Equals(guidId)) || (childItem != null && string.Equals(childItem.Path, i.Path, StringComparison.OrdinalIgnoreCase))); - if (child == null) + if (child is null) { _logger.LogWarning("No collection title exists with the supplied Id"); continue; diff --git a/Emby.Server.Implementations/Data/SqliteExtensions.cs b/Emby.Server.Implementations/Data/SqliteExtensions.cs index 736b8125d7..4055b0ba17 100644 --- a/Emby.Server.Implementations/Data/SqliteExtensions.cs +++ b/Emby.Server.Implementations/Data/SqliteExtensions.cs @@ -253,7 +253,7 @@ namespace Emby.Server.Implementations.Data { if (statement.BindParameters.TryGetValue(name, out IBindParameter bindParam)) { - if (value == null) + if (value is null) { bindParam.BindNull(); } diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 9d44fd9d18..36fdfc8f27 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -691,7 +691,7 @@ namespace Emby.Server.Implementations.Data private string GetPathToSave(string path) { - if (path == null) + if (path is null) { return null; } @@ -890,7 +890,7 @@ namespace Emby.Server.Implementations.Data saveItemStatement.TryBind("@UnratedType", item.GetBlockUnratedType().ToString()); - if (topParent == null) + if (topParent is null) { saveItemStatement.TryBindNull("@TopParentId"); } @@ -1414,7 +1414,7 @@ namespace Emby.Server.Implementations.Data var type = _typeMapper.GetType(typeString); - if (type == null) + if (type is null) { return null; } @@ -1433,7 +1433,7 @@ namespace Emby.Server.Implementations.Data } } - if (item == null) + if (item is null) { try { @@ -1444,7 +1444,7 @@ namespace Emby.Server.Implementations.Data } } - if (item == null) + if (item is null) { return null; } @@ -2151,7 +2151,7 @@ namespace Emby.Server.Implementations.Data private static bool EnableJoinUserData(InternalItemsQuery query) { - if (query.User == null) + if (query.User is null) { return false; } @@ -2497,7 +2497,7 @@ namespace Emby.Server.Implementations.Data { var item = query.SimilarTo; - if (item == null) + if (item is null) { return; } @@ -4522,7 +4522,7 @@ namespace Emby.Server.Implementations.Data if (query.ExcludeInheritedTags.Length > 0) { var paramName = "@ExcludeInheritedTags"; - if (statement == null) + if (statement is null) { int index = 0; string excludedTags = string.Join(',', query.ExcludeInheritedTags.Select(_ => paramName + index++)); @@ -4732,7 +4732,7 @@ namespace Emby.Server.Implementations.Data return false; } - if (query.User == null) + if (query.User is null) { return false; } diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index f6d37421a8..e0e3366b4f 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -235,14 +235,14 @@ namespace Emby.Server.Implementations.Dto if (options.ContainsField(ItemFields.CanDelete)) { - dto.CanDelete = user == null + dto.CanDelete = user is null ? item.CanDelete() : item.CanDelete(user); } if (options.ContainsField(ItemFields.CanDownload)) { - dto.CanDownload = user == null + dto.CanDownload = user is null ? item.CanDownload() : item.CanDownload(user); } @@ -571,7 +571,7 @@ namespace Emby.Server.Implementations.Dto return null; } }).Where(i => i != null) - .Where(i => user == null ? + .Where(i => user is null ? true : i.IsVisible(user)) .GroupBy(i => i.Name, StringComparer.OrdinalIgnoreCase) @@ -1143,7 +1143,7 @@ namespace Emby.Server.Implementations.Dto if (episodeSeries != null) { dto.SeriesPrimaryImageTag = GetTagAndFillBlurhash(dto, episodeSeries, ImageType.Primary); - if (dto.ImageTags == null || !dto.ImageTags.ContainsKey(ImageType.Primary)) + if (dto.ImageTags is null || !dto.ImageTags.ContainsKey(ImageType.Primary)) { AttachPrimaryImageAspectRatio(dto, episodeSeries); } @@ -1193,7 +1193,7 @@ namespace Emby.Server.Implementations.Dto if (series != null) { dto.SeriesPrimaryImageTag = GetTagAndFillBlurhash(dto, series, ImageType.Primary); - if (dto.ImageTags == null || !dto.ImageTags.ContainsKey(ImageType.Primary)) + if (dto.ImageTags is null || !dto.ImageTags.ContainsKey(ImageType.Primary)) { AttachPrimaryImageAspectRatio(dto, series); } @@ -1276,7 +1276,7 @@ namespace Emby.Server.Implementations.Dto var parent = currentItem.DisplayParent ?? currentItem.GetOwner() ?? currentItem.GetParent(); - if (parent == null && originalItem is not UserRootFolder && originalItem is not UserView && originalItem is not AggregateFolder && originalItem is not ICollectionFolder && originalItem is not Channel) + if (parent is null && originalItem is not UserRootFolder && originalItem is not UserView && originalItem is not AggregateFolder && originalItem is not ICollectionFolder && originalItem is not Channel) { parent = _libraryManager.GetCollectionFolders(originalItem).FirstOrDefault(); } @@ -1315,7 +1315,7 @@ namespace Emby.Server.Implementations.Dto || parent is Series) { parent ??= isFirst ? GetImageDisplayParent(item, item) ?? owner : parent; - if (parent == null) + if (parent is null) { break; } @@ -1403,7 +1403,7 @@ namespace Emby.Server.Implementations.Dto { var imageInfo = item.GetImageInfo(ImageType.Primary, 0); - if (imageInfo == null) + if (imageInfo is null) { return null; } diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs index d5e4a636ef..fb6f52332f 100644 --- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs @@ -191,7 +191,7 @@ namespace Emby.Server.Implementations.EntryPoints lock (_libraryChangedSyncLock) { - if (LibraryUpdateTimer == null) + if (LibraryUpdateTimer is null) { LibraryUpdateTimer = new Timer( LibraryUpdateTimerCallback, @@ -227,7 +227,7 @@ namespace Emby.Server.Implementations.EntryPoints lock (_libraryChangedSyncLock) { - if (LibraryUpdateTimer == null) + if (LibraryUpdateTimer is null) { LibraryUpdateTimer = new Timer(LibraryUpdateTimerCallback, null, LibraryUpdateDuration, Timeout.Infinite); } @@ -254,7 +254,7 @@ namespace Emby.Server.Implementations.EntryPoints lock (_libraryChangedSyncLock) { - if (LibraryUpdateTimer == null) + if (LibraryUpdateTimer is null) { LibraryUpdateTimer = new Timer(LibraryUpdateTimerCallback, null, LibraryUpdateDuration, Timeout.Infinite); } diff --git a/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs b/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs index 82c8d3ab6d..ce81f9e42f 100644 --- a/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs @@ -51,7 +51,7 @@ namespace Emby.Server.Implementations.EntryPoints lock (_syncLock) { - if (_updateTimer == null) + if (_updateTimer is null) { _updateTimer = new Timer( UpdateTimerCallback, diff --git a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs index d095248fab..b1a99853ad 100644 --- a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs +++ b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs @@ -164,7 +164,7 @@ namespace Emby.Server.Implementations.HttpServer ReadResult result = await reader.ReadAsync().ConfigureAwait(false); ReadOnlySequence buffer = result.Buffer; - if (OnReceive == null) + if (OnReceive is null) { // Tell the PipeReader how much of the buffer we have consumed reader.AdvanceTo(buffer.End); @@ -185,7 +185,7 @@ namespace Emby.Server.Implementations.HttpServer return; } - if (stub == null) + if (stub is null) { _logger.LogError("Error processing web socket message"); return; diff --git a/Emby.Server.Implementations/IO/FileRefresher.cs b/Emby.Server.Implementations/IO/FileRefresher.cs index 6326208f71..6337952c12 100644 --- a/Emby.Server.Implementations/IO/FileRefresher.cs +++ b/Emby.Server.Implementations/IO/FileRefresher.cs @@ -80,7 +80,7 @@ namespace Emby.Server.Implementations.IO return; } - if (_timer == null) + if (_timer is null) { _timer = new Timer(OnTimerCallback, null, TimeSpan.FromSeconds(_configurationManager.Configuration.LibraryMonitorDelay), TimeSpan.FromMilliseconds(-1)); } @@ -178,7 +178,7 @@ namespace Emby.Server.Implementations.IO { BaseItem? item = null; - while (item == null && !string.IsNullOrEmpty(path)) + while (item is null && !string.IsNullOrEmpty(path)) { item = _libraryManager.FindByPath(path, null); @@ -192,7 +192,7 @@ namespace Emby.Server.Implementations.IO { item = item.GetOwner() ?? item.GetParent(); - if (item == null) + if (item is null) { break; } diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index b688af5286..e8f6d2d236 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -176,7 +176,7 @@ namespace Emby.Server.Implementations.Library { get { - if (_rootFolder == null) + if (_rootFolder is null) { lock (_rootFolderSyncLock) { @@ -656,7 +656,7 @@ namespace Emby.Server.Implementations.Library if (parent != null) { - var multiItemResolvers = resolvers == null ? MultiItemResolvers : resolvers.OfType().ToArray(); + var multiItemResolvers = resolvers is null ? MultiItemResolvers : resolvers.OfType().ToArray(); foreach (var resolver in multiItemResolvers) { @@ -770,11 +770,11 @@ namespace Emby.Server.Implementations.Library public Folder GetUserRootFolder() { - if (_userRootFolder == null) + if (_userRootFolder is null) { lock (_userRootFolderSyncLock) { - if (_userRootFolder == null) + if (_userRootFolder is null) { var userRootPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath; @@ -792,7 +792,7 @@ namespace Emby.Server.Implementations.Library _logger.LogError(ex, "Error creating UserRootFolder {Path}", newItemId); } - if (tmpItem == null) + if (tmpItem is null) { _logger.LogDebug("Creating new userRootFolder with DeepCopy"); tmpItem = ((Folder)ResolvePath(_fileSystem.GetDirectoryInfo(userRootPath))).DeepCopy(); @@ -961,7 +961,7 @@ namespace Emby.Server.Implementations.Library var path = getPathFn(name); var id = GetItemByNameId(path); var item = GetItemById(id) as T; - if (item == null) + if (item is null) { item = new T { @@ -1627,7 +1627,7 @@ namespace Emby.Server.Implementations.Library // Get an existing item by Id video = GetItemById(info.ItemId.Value) as Video; - if (video == null) + if (video is null) { _logger.LogError("Unable to locate item with Id {ID}.", info.ItemId.Value); } @@ -1639,7 +1639,7 @@ namespace Emby.Server.Implementations.Library // Try to resolve the path into a video video = ResolvePath(_fileSystem.GetFileSystemInfo(info.Path)) as Video; - if (video == null) + if (video is null) { _logger.LogError("Intro resolver returned null for {Path}.", info.Path); } @@ -1711,7 +1711,7 @@ namespace Emby.Server.Implementations.Library foreach (var (name, sortOrder) in orderBy) { var comparer = GetComparer(name, user); - if (comparer == null) + if (comparer is null) { continue; } @@ -2010,7 +2010,7 @@ namespace Emby.Server.Implementations.Library { var parent = item.GetParent(); - if (parent == null || parent is AggregateFolder) + if (parent is null || parent is AggregateFolder) { break; } @@ -2018,7 +2018,7 @@ namespace Emby.Server.Implementations.Library item = parent; } - if (item == null) + if (item is null) { return new List(); } @@ -2032,7 +2032,7 @@ namespace Emby.Server.Implementations.Library { var parent = item.GetParent(); - if (parent == null || parent is AggregateFolder) + if (parent is null || parent is AggregateFolder) { break; } @@ -2040,7 +2040,7 @@ namespace Emby.Server.Implementations.Library item = parent; } - if (item == null) + if (item is null) { return new List(); } @@ -2064,7 +2064,7 @@ namespace Emby.Server.Implementations.Library .Find(folder => folder is CollectionFolder) as CollectionFolder; } - return collectionFolder == null ? new LibraryOptions() : collectionFolder.GetLibraryOptions(); + return collectionFolder is null ? new LibraryOptions() : collectionFolder.GetLibraryOptions(); } public string GetContentType(BaseItem item) @@ -2129,7 +2129,7 @@ namespace Emby.Server.Implementations.Library private string GetTopFolderContentType(BaseItem item) { - if (item == null) + if (item is null) { return null; } @@ -2137,7 +2137,7 @@ namespace Emby.Server.Implementations.Library while (!item.ParentId.Equals(default)) { var parent = item.GetParent(); - if (parent == null || parent is AggregateFolder) + if (parent is null || parent is AggregateFolder) { break; } @@ -2177,7 +2177,7 @@ namespace Emby.Server.Implementations.Library var refresh = false; - if (item == null || !string.Equals(item.Path, path, StringComparison.OrdinalIgnoreCase)) + if (item is null || !string.Equals(item.Path, path, StringComparison.OrdinalIgnoreCase)) { Directory.CreateDirectory(path); @@ -2225,7 +2225,7 @@ namespace Emby.Server.Implementations.Library var isNew = false; - if (item == null) + if (item is null) { Directory.CreateDirectory(path); @@ -2289,7 +2289,7 @@ namespace Emby.Server.Implementations.Library var isNew = false; - if (item == null) + if (item is null) { Directory.CreateDirectory(path); @@ -2362,7 +2362,7 @@ namespace Emby.Server.Implementations.Library var isNew = false; - if (item == null) + if (item is null) { Directory.CreateDirectory(path); @@ -2459,7 +2459,7 @@ namespace Emby.Server.Implementations.Library episodeInfo = resolver.Resolve(episode.Path, isFolder, null, null, isAbsoluteNaming); // Resolve from parent folder if it's not the Season folder var parent = episode.GetParent(); - if (episodeInfo == null && parent.GetType() == typeof(Folder)) + if (episodeInfo is null && parent.GetType() == typeof(Folder)) { episodeInfo = resolver.Resolve(parent.Path, true, null, null, isAbsoluteNaming); if (episodeInfo != null) @@ -2620,7 +2620,7 @@ namespace Emby.Server.Implementations.Library public IEnumerable FindExtras(BaseItem owner, IReadOnlyList fileSystemChildren, IDirectoryService directoryService) { var ownerVideoInfo = VideoResolver.Resolve(owner.Path, owner.IsFolder, _namingOptions); - if (ownerVideoInfo == null) + if (ownerVideoInfo is null) { yield break; } @@ -2754,7 +2754,7 @@ namespace Emby.Server.Implementations.Library } }) .Where(i => i != null) - .Where(i => query.User == null ? + .Where(i => query.User is null ? true : i.IsVisible(query.User)) .ToList(); diff --git a/Emby.Server.Implementations/Library/LiveStreamHelper.cs b/Emby.Server.Implementations/Library/LiveStreamHelper.cs index 20624cc7ae..1010335512 100644 --- a/Emby.Server.Implementations/Library/LiveStreamHelper.cs +++ b/Emby.Server.Implementations/Library/LiveStreamHelper.cs @@ -60,7 +60,7 @@ namespace Emby.Server.Implementations.Library } } - if (mediaInfo == null) + if (mediaInfo is null) { if (addProbeDelay) { @@ -130,7 +130,7 @@ namespace Emby.Server.Implementations.Library var audioStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio); - if (audioStream == null || audioStream.Index == -1) + if (audioStream is null || audioStream.Index == -1) { mediaSource.DefaultAudioStreamIndex = null; } diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs index bfccc7db72..e2474f5081 100644 --- a/Emby.Server.Implementations/Library/MediaSourceManager.cs +++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs @@ -383,7 +383,7 @@ namespace Emby.Server.Implementations.Library var preferredSubs = NormalizeLanguage(user.SubtitleLanguagePreference); var defaultAudioIndex = source.DefaultAudioStreamIndex; - var audioLangage = defaultAudioIndex == null + var audioLangage = defaultAudioIndex is null ? null : source.MediaStreams.Where(i => i.Type == MediaStreamType.Audio && i.Index == defaultAudioIndex).Select(i => i.Language).FirstOrDefault(); @@ -417,13 +417,13 @@ namespace Emby.Server.Implementations.Library public void SetDefaultAudioAndSubtitleStreamIndexes(BaseItem item, MediaSourceInfo source, User user) { // Item would only be null if the app didn't supply ItemId as part of the live stream open request - var mediaType = item == null ? MediaType.Video : item.MediaType; + var mediaType = item is null ? MediaType.Video : item.MediaType; if (string.Equals(mediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase)) { - var userData = item == null ? new UserItemData() : _userDataManager.GetUserData(user, item); + var userData = item is null ? new UserItemData() : _userDataManager.GetUserData(user, item); - var allowRememberingSelection = item == null || item.EnableRememberingTrackSelections; + var allowRememberingSelection = item is null || item.EnableRememberingTrackSelections; SetDefaultAudioStreamIndex(source, userData, user, allowRememberingSelection); SetDefaultSubtitleStreamIndex(source, userData, user, allowRememberingSelection); @@ -543,7 +543,7 @@ namespace Emby.Server.Implementations.Library var audioStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio); - if (audioStream == null || audioStream.Index == -1) + if (audioStream is null || audioStream.Index == -1) { mediaSource.DefaultAudioStreamIndex = null; } @@ -638,7 +638,7 @@ namespace Emby.Server.Implementations.Library } } - if (mediaInfo == null) + if (mediaInfo is null) { if (addProbeDelay) { @@ -713,7 +713,7 @@ namespace Emby.Server.Implementations.Library var audioStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio); - if (audioStream == null || audioStream.Index == -1) + if (audioStream is null || audioStream.Index == -1) { mediaSource.DefaultAudioStreamIndex = null; } diff --git a/Emby.Server.Implementations/Library/ResolverHelper.cs b/Emby.Server.Implementations/Library/ResolverHelper.cs index 4100a74a58..590d37b926 100644 --- a/Emby.Server.Implementations/Library/ResolverHelper.cs +++ b/Emby.Server.Implementations/Library/ResolverHelper.cs @@ -43,7 +43,7 @@ namespace Emby.Server.Implementations.Library // Make sure DateCreated and DateModified have values var fileInfo = directoryService.GetFile(item.Path); - if (fileInfo == null) + if (fileInfo is null) { return false; } diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs index 7a6aea9c1f..bc1f5d08a8 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs @@ -116,7 +116,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio // Use regular audio type for mixed libraries, owned items and music if (isMixedCollectionType || - args.Parent == null || + args.Parent is null || isMusicCollectionType) { item = new MediaBrowser.Controller.Entities.Audio.Audio(); @@ -144,7 +144,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio // TODO: Allow GetMultiDiscMovie in here var result = ResolveMultipleAudio(args.Parent, args.GetActualFileSystemChildren(), parseName); - if (result == null || result.Items.Count != 1 || result.Items[0] is not AudioBook item) + if (result is null || result.Items.Count != 1 || result.Items[0] is not AudioBook item) { return null; } diff --git a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs index b2a7abb1bd..cb377136ad 100644 --- a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs @@ -79,7 +79,7 @@ namespace Emby.Server.Implementations.Library.Resolvers videoType = VideoType.Dvd; } - if (videoType == null) + if (videoType is null) { continue; } @@ -93,7 +93,7 @@ namespace Emby.Server.Implementations.Library.Resolvers videoInfo = VideoResolver.Resolve(args.Path, false, NamingOptions, parseName); } - if (videoInfo == null || (!videoInfo.IsStub && !VideoResolver.IsVideoFile(args.Path, NamingOptions))) + if (videoInfo is null || (!videoInfo.IsStub && !VideoResolver.IsVideoFile(args.Path, NamingOptions))) { return null; } diff --git a/Emby.Server.Implementations/Library/Resolvers/ExtraResolver.cs b/Emby.Server.Implementations/Library/Resolvers/ExtraResolver.cs index 408e640f9d..30c52e19d3 100644 --- a/Emby.Server.Implementations/Library/Resolvers/ExtraResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/ExtraResolver.cs @@ -48,7 +48,7 @@ namespace Emby.Server.Implementations.Library.Resolvers public bool TryGetExtraTypeForOwner(string path, VideoFileInfo ownerVideoFileInfo, [NotNullWhen(true)] out ExtraType? extraType) { var extraResult = GetExtraInfo(path, _namingOptions); - if (extraResult.ExtraType == null) + if (extraResult.ExtraType is null) { extraType = null; return false; diff --git a/Emby.Server.Implementations/Library/Resolvers/GenericFolderResolver.cs b/Emby.Server.Implementations/Library/Resolvers/GenericFolderResolver.cs index 0799622825..1c2de912aa 100644 --- a/Emby.Server.Implementations/Library/Resolvers/GenericFolderResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/GenericFolderResolver.cs @@ -22,7 +22,7 @@ namespace Emby.Server.Implementations.Library.Resolvers { base.SetInitialItemValues(item, args); - item.IsRoot = args.Parent == null; + item.IsRoot = args.Parent is null; } } } diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs index 84d4688aff..6b18ad237f 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs @@ -108,7 +108,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies if (string.IsNullOrEmpty(collectionType)) { // Owned items will be caught by the video extra resolver - if (args.Parent == null) + if (args.Parent is null) { return null; } @@ -127,10 +127,10 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies } // ignore extras - return movie?.ExtraType == null ? movie : null; + return movie?.ExtraType is null ? movie : null; } - if (args.Parent == null) + if (args.Parent is null) { return base.Resolve(args); } @@ -205,7 +205,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies if (string.IsNullOrEmpty(collectionType)) { // Owned items should just use the plain video type - if (parent == null) + if (parent is null) { return ResolveVideos diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs index 002193baf5..d70b8f3ab7 100644 --- a/Jellyfin.Server/CoreAppHost.cs +++ b/Jellyfin.Server/CoreAppHost.cs @@ -1,10 +1,10 @@ using System; using System.Collections.Generic; using System.Reflection; -using Emby.Drawing; using Emby.Server.Implementations; using Emby.Server.Implementations.Session; using Jellyfin.Api.WebSocketListeners; +using Jellyfin.Drawing; using Jellyfin.Drawing.Skia; using Jellyfin.Server.Implementations; using Jellyfin.Server.Implementations.Activity; diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index ac20869353..2be628ac22 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -52,7 +52,7 @@ - + diff --git a/Jellyfin.sln b/Jellyfin.sln index 0514b9614c..347716eb9a 100644 --- a/Jellyfin.sln +++ b/Jellyfin.sln @@ -17,7 +17,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MediaBrowser.XbmcMetadata", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MediaBrowser.LocalMetadata", "MediaBrowser.LocalMetadata\MediaBrowser.LocalMetadata.csproj", "{7EF9F3E0-697D-42F3-A08F-19DEB5F84392}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emby.Drawing", "Emby.Drawing\Emby.Drawing.csproj", "{08FFF49B-F175-4807-A2B5-73B0EBD9F716}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Drawing", "src\Jellyfin.Drawing\Jellyfin.Drawing.csproj", "{08FFF49B-F175-4807-A2B5-73B0EBD9F716}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emby.Photos", "Emby.Photos\Emby.Photos.csproj", "{89AB4548-770D-41FD-A891-8DAFF44F452C}" EndProject @@ -287,6 +287,7 @@ Global {DA9FD356-4894-4830-B208-D6BCE3E65B11} = {C9F0AB5D-F4D7-40C8-A353-3305C86D6D4C} {FE47334C-EFDE-4519-BD50-F24430FF360B} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} {24960660-DE6C-47BF-AEEF-CEE8F19FE6C2} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} + {08FFF49B-F175-4807-A2B5-73B0EBD9F716} = {C9F0AB5D-F4D7-40C8-A353-3305C86D6D4C} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3448830C-EBDC-426C-85CD-7BBB9651A7FE} diff --git a/src/Jellyfin.Drawing/ImageProcessor.cs b/src/Jellyfin.Drawing/ImageProcessor.cs new file mode 100644 index 0000000000..3c7bc394f0 --- /dev/null +++ b/src/Jellyfin.Drawing/ImageProcessor.cs @@ -0,0 +1,569 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Net.Mime; +using System.Text; +using System.Threading.Tasks; +using Jellyfin.Data.Entities; +using MediaBrowser.Common.Extensions; +using MediaBrowser.Controller; +using MediaBrowser.Controller.Drawing; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.MediaEncoding; +using MediaBrowser.Model.Drawing; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Net; +using Microsoft.Extensions.Logging; +using Photo = MediaBrowser.Controller.Entities.Photo; + +namespace Jellyfin.Drawing +{ + /// + /// Class ImageProcessor. + /// + public sealed class ImageProcessor : IImageProcessor, IDisposable + { + // Increment this when there's a change requiring caches to be invalidated + private const char Version = '3'; + + private static readonly HashSet _transparentImageTypes + = new HashSet(StringComparer.OrdinalIgnoreCase) { ".png", ".webp", ".gif" }; + + private readonly ILogger _logger; + private readonly IFileSystem _fileSystem; + private readonly IServerApplicationPaths _appPaths; + private readonly IImageEncoder _imageEncoder; + private readonly IMediaEncoder _mediaEncoder; + + private bool _disposed; + + /// + /// Initializes a new instance of the class. + /// + /// The logger. + /// The server application paths. + /// The filesystem. + /// The image encoder. + /// The media encoder. + public ImageProcessor( + ILogger logger, + IServerApplicationPaths appPaths, + IFileSystem fileSystem, + IImageEncoder imageEncoder, + IMediaEncoder mediaEncoder) + { + _logger = logger; + _fileSystem = fileSystem; + _imageEncoder = imageEncoder; + _mediaEncoder = mediaEncoder; + _appPaths = appPaths; + } + + private string ResizedImageCachePath => Path.Combine(_appPaths.ImageCachePath, "resized-images"); + + /// + public IReadOnlyCollection SupportedInputFormats => + new HashSet(StringComparer.OrdinalIgnoreCase) + { + "tiff", + "tif", + "jpeg", + "jpg", + "png", + "aiff", + "cr2", + "crw", + "nef", + "orf", + "pef", + "arw", + "webp", + "gif", + "bmp", + "erf", + "raf", + "rw2", + "nrw", + "dng", + "ico", + "astc", + "ktx", + "pkm", + "wbmp" + }; + + /// + public bool SupportsImageCollageCreation => _imageEncoder.SupportsImageCollageCreation; + + /// + public async Task ProcessImage(ImageProcessingOptions options, Stream toStream) + { + var file = await ProcessImage(options).ConfigureAwait(false); + using (var fileStream = AsyncFile.OpenRead(file.Path)) + { + await fileStream.CopyToAsync(toStream).ConfigureAwait(false); + } + } + + /// + public IReadOnlyCollection GetSupportedImageOutputFormats() + => _imageEncoder.SupportedOutputFormats; + + /// + public bool SupportsTransparency(string path) + => _transparentImageTypes.Contains(Path.GetExtension(path)); + + /// + public async Task<(string Path, string? MimeType, DateTime DateModified)> ProcessImage(ImageProcessingOptions options) + { + ItemImageInfo originalImage = options.Image; + BaseItem item = options.Item; + + string originalImagePath = originalImage.Path; + DateTime dateModified = originalImage.DateModified; + ImageDimensions? originalImageSize = null; + if (originalImage.Width > 0 && originalImage.Height > 0) + { + originalImageSize = new ImageDimensions(originalImage.Width, originalImage.Height); + } + + var mimeType = MimeTypes.GetMimeType(originalImagePath); + if (!_imageEncoder.SupportsImageEncoding) + { + return (originalImagePath, mimeType, dateModified); + } + + var supportedImageInfo = await GetSupportedImage(originalImagePath, dateModified).ConfigureAwait(false); + originalImagePath = supportedImageInfo.Path; + + // Original file doesn't exist, or original file is gif. + if (!File.Exists(originalImagePath) || string.Equals(mimeType, MediaTypeNames.Image.Gif, StringComparison.OrdinalIgnoreCase)) + { + return (originalImagePath, mimeType, dateModified); + } + + dateModified = supportedImageInfo.DateModified; + bool requiresTransparency = _transparentImageTypes.Contains(Path.GetExtension(originalImagePath)); + + bool autoOrient = false; + ImageOrientation? orientation = null; + if (item is Photo photo) + { + if (photo.Orientation.HasValue) + { + if (photo.Orientation.Value != ImageOrientation.TopLeft) + { + autoOrient = true; + orientation = photo.Orientation; + } + } + else + { + // Orientation unknown, so do it + autoOrient = true; + orientation = photo.Orientation; + } + } + + if (options.HasDefaultOptions(originalImagePath, originalImageSize) && (!autoOrient || !options.RequiresAutoOrientation)) + { + // Just spit out the original file if all the options are default + return (originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified); + } + + int quality = options.Quality; + + ImageFormat outputFormat = GetOutputFormat(options.SupportedOutputFormats, requiresTransparency); + string cacheFilePath = GetCacheFilePath( + originalImagePath, + options.Width, + options.Height, + options.MaxWidth, + options.MaxHeight, + options.FillWidth, + options.FillHeight, + quality, + dateModified, + outputFormat, + options.AddPlayedIndicator, + options.PercentPlayed, + options.UnplayedCount, + options.Blur, + options.BackgroundColor, + options.ForegroundLayer); + + try + { + if (!File.Exists(cacheFilePath)) + { + string resultPath = _imageEncoder.EncodeImage(originalImagePath, dateModified, cacheFilePath, autoOrient, orientation, quality, options, outputFormat); + + if (string.Equals(resultPath, originalImagePath, StringComparison.OrdinalIgnoreCase)) + { + return (originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified); + } + } + + return (cacheFilePath, GetMimeType(outputFormat, cacheFilePath), _fileSystem.GetLastWriteTimeUtc(cacheFilePath)); + } + catch (Exception ex) + { + // If it fails for whatever reason, return the original image + _logger.LogError(ex, "Error encoding image"); + return (originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified); + } + } + + private ImageFormat GetOutputFormat(IReadOnlyCollection clientSupportedFormats, bool requiresTransparency) + { + var serverFormats = GetSupportedImageOutputFormats(); + + // Client doesn't care about format, so start with webp if supported + if (serverFormats.Contains(ImageFormat.Webp) && clientSupportedFormats.Contains(ImageFormat.Webp)) + { + return ImageFormat.Webp; + } + + // If transparency is needed and webp isn't supported, than png is the only option + if (requiresTransparency && clientSupportedFormats.Contains(ImageFormat.Png)) + { + return ImageFormat.Png; + } + + foreach (var format in clientSupportedFormats) + { + if (serverFormats.Contains(format)) + { + return format; + } + } + + // We should never actually get here + return ImageFormat.Jpg; + } + + private string GetMimeType(ImageFormat format, string path) + => format switch + { + ImageFormat.Bmp => MimeTypes.GetMimeType("i.bmp"), + ImageFormat.Gif => MimeTypes.GetMimeType("i.gif"), + ImageFormat.Jpg => MimeTypes.GetMimeType("i.jpg"), + ImageFormat.Png => MimeTypes.GetMimeType("i.png"), + ImageFormat.Webp => MimeTypes.GetMimeType("i.webp"), + _ => MimeTypes.GetMimeType(path) + }; + + /// + /// Gets the cache file path based on a set of parameters. + /// + private string GetCacheFilePath( + string originalPath, + int? width, + int? height, + int? maxWidth, + int? maxHeight, + int? fillWidth, + int? fillHeight, + int quality, + DateTime dateModified, + ImageFormat format, + bool addPlayedIndicator, + double percentPlayed, + int? unwatchedCount, + int? blur, + string backgroundColor, + string foregroundLayer) + { + var filename = new StringBuilder(256); + filename.Append(originalPath); + + filename.Append(",quality="); + filename.Append(quality); + + filename.Append(",datemodified="); + filename.Append(dateModified.Ticks); + + filename.Append(",f="); + filename.Append(format); + + if (width.HasValue) + { + filename.Append(",width="); + filename.Append(width.Value); + } + + if (height.HasValue) + { + filename.Append(",height="); + filename.Append(height.Value); + } + + if (maxWidth.HasValue) + { + filename.Append(",maxwidth="); + filename.Append(maxWidth.Value); + } + + if (maxHeight.HasValue) + { + filename.Append(",maxheight="); + filename.Append(maxHeight.Value); + } + + if (fillWidth.HasValue) + { + filename.Append(",fillwidth="); + filename.Append(fillWidth.Value); + } + + if (fillHeight.HasValue) + { + filename.Append(",fillheight="); + filename.Append(fillHeight.Value); + } + + if (addPlayedIndicator) + { + filename.Append(",pl=true"); + } + + if (percentPlayed > 0) + { + filename.Append(",p="); + filename.Append(percentPlayed); + } + + if (unwatchedCount.HasValue) + { + filename.Append(",p="); + filename.Append(unwatchedCount.Value); + } + + if (blur.HasValue) + { + filename.Append(",blur="); + filename.Append(blur.Value); + } + + if (!string.IsNullOrEmpty(backgroundColor)) + { + filename.Append(",b="); + filename.Append(backgroundColor); + } + + if (!string.IsNullOrEmpty(foregroundLayer)) + { + filename.Append(",fl="); + filename.Append(foregroundLayer); + } + + filename.Append(",v="); + filename.Append(Version); + + return GetCachePath(ResizedImageCachePath, filename.ToString(), "." + format.ToString().ToLowerInvariant()); + } + + /// + public ImageDimensions GetImageDimensions(BaseItem item, ItemImageInfo info) + { + int width = info.Width; + int height = info.Height; + + if (height > 0 && width > 0) + { + return new ImageDimensions(width, height); + } + + string path = info.Path; + _logger.LogDebug("Getting image size for item {ItemType} {Path}", item.GetType().Name, path); + + ImageDimensions size = GetImageDimensions(path); + info.Width = size.Width; + info.Height = size.Height; + + return size; + } + + /// + public ImageDimensions GetImageDimensions(string path) + => _imageEncoder.GetImageSize(path); + + /// + public string GetImageBlurHash(string path) + { + var size = GetImageDimensions(path); + return GetImageBlurHash(path, size); + } + + /// + public string GetImageBlurHash(string path, ImageDimensions imageDimensions) + { + if (imageDimensions.Width <= 0 || imageDimensions.Height <= 0) + { + return string.Empty; + } + + // We want tiles to be as close to square as possible, and to *mostly* keep under 16 tiles for performance. + // One tile is (width / xComp) x (height / yComp) pixels, which means that ideally yComp = xComp * height / width. + // See more at https://github.com/woltapp/blurhash/#how-do-i-pick-the-number-of-x-and-y-components + float xCompF = MathF.Sqrt(16.0f * imageDimensions.Width / imageDimensions.Height); + float yCompF = xCompF * imageDimensions.Height / imageDimensions.Width; + + int xComp = Math.Min((int)xCompF + 1, 9); + int yComp = Math.Min((int)yCompF + 1, 9); + + return _imageEncoder.GetImageBlurHash(xComp, yComp, path); + } + + /// + public string GetImageCacheTag(BaseItem item, ItemImageInfo image) + => (item.Path + image.DateModified.Ticks).GetMD5().ToString("N", CultureInfo.InvariantCulture); + + /// + public string GetImageCacheTag(BaseItem item, ChapterInfo chapter) + { + return GetImageCacheTag(item, new ItemImageInfo + { + Path = chapter.ImagePath, + Type = ImageType.Chapter, + DateModified = chapter.ImageDateModified + }); + } + + /// + public string? GetImageCacheTag(User user) + { + if (user.ProfileImage is null) + { + return null; + } + + return (user.ProfileImage.Path + user.ProfileImage.LastModified.Ticks).GetMD5() + .ToString("N", CultureInfo.InvariantCulture); + } + + private Task<(string Path, DateTime DateModified)> GetSupportedImage(string originalImagePath, DateTime dateModified) + { + var inputFormat = Path.GetExtension(originalImagePath.AsSpan()).TrimStart('.').ToString(); + + // These are just jpg files renamed as tbn + if (string.Equals(inputFormat, "tbn", StringComparison.OrdinalIgnoreCase)) + { + return Task.FromResult((originalImagePath, dateModified)); + } + + // TODO _mediaEncoder.ConvertImage is not implemented + // if (!_imageEncoder.SupportedInputFormats.Contains(inputFormat)) + // { + // try + // { + // string filename = (originalImagePath + dateModified.Ticks.ToString(CultureInfo.InvariantCulture)).GetMD5().ToString("N", CultureInfo.InvariantCulture); + // + // string cacheExtension = _mediaEncoder.SupportsEncoder("libwebp") ? ".webp" : ".png"; + // var outputPath = Path.Combine(_appPaths.ImageCachePath, "converted-images", filename + cacheExtension); + // + // var file = _fileSystem.GetFileInfo(outputPath); + // if (!file.Exists) + // { + // await _mediaEncoder.ConvertImage(originalImagePath, outputPath).ConfigureAwait(false); + // dateModified = _fileSystem.GetLastWriteTimeUtc(outputPath); + // } + // else + // { + // dateModified = file.LastWriteTimeUtc; + // } + // + // originalImagePath = outputPath; + // } + // catch (Exception ex) + // { + // _logger.LogError(ex, "Image conversion failed for {Path}", originalImagePath); + // } + // } + + return Task.FromResult((originalImagePath, dateModified)); + } + + /// + /// Gets the cache path. + /// + /// The path. + /// Name of the unique. + /// The file extension. + /// System.String. + /// + /// path + /// or + /// uniqueName + /// or + /// fileExtension. + /// + public string GetCachePath(string path, string uniqueName, string fileExtension) + { + ArgumentException.ThrowIfNullOrEmpty(path); + ArgumentException.ThrowIfNullOrEmpty(uniqueName); + ArgumentException.ThrowIfNullOrEmpty(fileExtension); + + var filename = uniqueName.GetMD5() + fileExtension; + + return GetCachePath(path, filename); + } + + /// + /// Gets the cache path. + /// + /// The path. + /// The filename. + /// System.String. + /// + /// path + /// or + /// filename. + /// + public string GetCachePath(ReadOnlySpan path, ReadOnlySpan filename) + { + if (path.IsEmpty) + { + throw new ArgumentException("Path can't be empty.", nameof(path)); + } + + if (filename.IsEmpty) + { + throw new ArgumentException("Filename can't be empty.", nameof(filename)); + } + + var prefix = filename.Slice(0, 1); + + return Path.Join(path, prefix, filename); + } + + /// + public void CreateImageCollage(ImageCollageOptions options, string? libraryName) + { + _logger.LogInformation("Creating image collage and saving to {Path}", options.OutputPath); + + _imageEncoder.CreateImageCollage(options, libraryName); + + _logger.LogInformation("Completed creation of image collage and saved to {Path}", options.OutputPath); + } + + /// + public void Dispose() + { + if (_disposed) + { + return; + } + + if (_imageEncoder is IDisposable disposable) + { + disposable.Dispose(); + } + + _disposed = true; + } + } +} diff --git a/src/Jellyfin.Drawing/Jellyfin.Drawing.csproj b/src/Jellyfin.Drawing/Jellyfin.Drawing.csproj new file mode 100644 index 0000000000..a5bc8eaa7e --- /dev/null +++ b/src/Jellyfin.Drawing/Jellyfin.Drawing.csproj @@ -0,0 +1,35 @@ + + + + + {08FFF49B-F175-4807-A2B5-73B0EBD9F716} + + + + net7.0 + false + true + + + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + + + diff --git a/src/Jellyfin.Drawing/NullImageEncoder.cs b/src/Jellyfin.Drawing/NullImageEncoder.cs new file mode 100644 index 0000000000..24dda108ec --- /dev/null +++ b/src/Jellyfin.Drawing/NullImageEncoder.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using MediaBrowser.Controller.Drawing; +using MediaBrowser.Model.Drawing; + +namespace Jellyfin.Drawing +{ + /// + /// A fallback implementation of . + /// + public class NullImageEncoder : IImageEncoder + { + /// + public IReadOnlyCollection SupportedInputFormats + => new HashSet(StringComparer.OrdinalIgnoreCase) { "png", "jpeg", "jpg" }; + + /// + public IReadOnlyCollection SupportedOutputFormats + => new HashSet() { ImageFormat.Jpg, ImageFormat.Png }; + + /// + public string Name => "Null Image Encoder"; + + /// + public bool SupportsImageCollageCreation => false; + + /// + public bool SupportsImageEncoding => false; + + /// + public ImageDimensions GetImageSize(string path) + => throw new NotImplementedException(); + + /// + public string EncodeImage(string inputPath, DateTime dateModified, string outputPath, bool autoOrient, ImageOrientation? orientation, int quality, ImageProcessingOptions options, ImageFormat outputFormat) + { + throw new NotImplementedException(); + } + + /// + public void CreateImageCollage(ImageCollageOptions options, string? libraryName) + { + throw new NotImplementedException(); + } + + /// + public void CreateSplashscreen(IReadOnlyList posters, IReadOnlyList backdrops) + { + throw new NotImplementedException(); + } + + /// + public string GetImageBlurHash(int xComp, int yComp, string path) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Jellyfin.Drawing/Properties/AssemblyInfo.cs b/src/Jellyfin.Drawing/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..3851bf9241 --- /dev/null +++ b/src/Jellyfin.Drawing/Properties/AssemblyInfo.cs @@ -0,0 +1,30 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Jellyfin.Drawing")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Jellyfin Project")] +[assembly: AssemblyProduct("Jellyfin Server")] +[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("87b6f14e-16d8-4a58-a553-fd9945e47458")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// -- cgit v1.2.3 From 7516e61c5b1992a2488f73ca481b86fa079c04fa Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 10 Jan 2023 14:50:35 +0000 Subject: chore(deps): update dotnet monorepo to v7.0.2 --- Emby.Server.Implementations/Emby.Server.Implementations.csproj | 2 +- Jellyfin.Api/Jellyfin.Api.csproj | 2 +- .../Jellyfin.Server.Implementations.csproj | 8 ++++---- Jellyfin.Server/Jellyfin.Server.csproj | 4 ++-- MediaBrowser.Controller/MediaBrowser.Controller.csproj | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index b18a3174a0..7accc3b8ba 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -29,7 +29,7 @@ - + diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj index eca68c6dfa..b5444138fb 100644 --- a/Jellyfin.Api/Jellyfin.Api.csproj +++ b/Jellyfin.Api/Jellyfin.Api.csproj @@ -13,7 +13,7 @@ - + diff --git a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj index 68e9205e40..c2c3513ad9 100644 --- a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj +++ b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj @@ -28,13 +28,13 @@ - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index 2be628ac22..1cd65ea825 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -37,8 +37,8 @@ - - + + diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 4a66edb16f..6434621c46 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -19,7 +19,7 @@ - + -- cgit v1.2.3 From ab645653bc0b6fbc071832f8f27722225e6d5234 Mon Sep 17 00:00:00 2001 From: 0TTA Date: Tue, 10 Jan 2023 11:30:18 +0000 Subject: Translated using Weblate (Arabic) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ar/ --- .../Localization/Core/ar.json | 28 +++++++++++----------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ar.json b/Emby.Server.Implementations/Localization/Core/ar.json index 4508363b0c..93d50e6e3b 100644 --- a/Emby.Server.Implementations/Localization/Core/ar.json +++ b/Emby.Server.Implementations/Localization/Core/ar.json @@ -3,9 +3,9 @@ "AppDeviceValues": "تطبيق: {0}, جهاز: {1}", "Application": "تطبيق", "Artists": "الفنانين", - "AuthenticationSucceededWithUserName": "تمت مصادقة {0} بنجاح", + "AuthenticationSucceededWithUserName": "نجحت عملية التوثيق بـ {0}", "Books": "الكتب", - "CameraImageUploadedFrom": "صورة كاميرا جديدة تم رفعها من {0}", + "CameraImageUploadedFrom": "رُفعت صورة الكاميرا الجديدة من {0}", "Channels": "القنوات", "ChapterNameValue": "الفصل {0}", "Collections": "التجميعات", @@ -16,7 +16,7 @@ "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": "يجب إعادة تشغيل الخادم", -- cgit v1.2.3 From d39dcb8ad2433a93524660a0cf47684a03f9cdd5 Mon Sep 17 00:00:00 2001 From: kshantum Date: Thu, 12 Jan 2023 04:05:02 +0000 Subject: Translated using Weblate (Russian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ru/ --- Emby.Server.Implementations/Localization/Core/ru.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ru.json b/Emby.Server.Implementations/Localization/Core/ru.json index dc45a8264d..65cf29e807 100644 --- a/Emby.Server.Implementations/Localization/Core/ru.json +++ b/Emby.Server.Implementations/Localization/Core/ru.json @@ -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": "Плагин удалён", -- cgit v1.2.3 From 0571d3e403906167ce4204e63f8697f97e35d0bf Mon Sep 17 00:00:00 2001 From: Alex Popovic Date: Wed, 11 Jan 2023 22:25:39 +0000 Subject: Translated using Weblate (Serbian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/sr/ --- Emby.Server.Implementations/Localization/Core/sr.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/sr.json b/Emby.Server.Implementations/Localization/Core/sr.json index 1be8867f47..9739358df0 100644 --- a/Emby.Server.Implementations/Localization/Core/sr.json +++ b/Emby.Server.Implementations/Localization/Core/sr.json @@ -122,5 +122,6 @@ "TaskOptimizeDatabaseDescription": "Сажима базу података и скраћује слободан простор. Покретање овог задатка након скенирања библиотеке или других промена које подразумевају измене базе података које могу побољшати перформансе.", "External": "Спољно", "TaskKeyframeExtractorDescription": "Екстрактује кљулне сличице из видео датотека да би креирао више преицзну HLS плеј-листу. Овај задатак може да потраје дуже време.", - "TaskKeyframeExtractor": "Екстрактор кључних сличица" + "TaskKeyframeExtractor": "Екстрактор кључних сличица", + "HearingImpaired": "ослабљен слух" } -- cgit v1.2.3 From 37edb2188782fcae25175767e6713c64cf24b5a5 Mon Sep 17 00:00:00 2001 From: Napaul Intrarasing Date: Fri, 13 Jan 2023 15:11:16 +0000 Subject: Translated using Weblate (Thai) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/th/ --- Emby.Server.Implementations/Localization/Core/th.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/th.json b/Emby.Server.Implementations/Localization/Core/th.json index 9407a7b921..1a4fef64e8 100644 --- a/Emby.Server.Implementations/Localization/Core/th.json +++ b/Emby.Server.Implementations/Localization/Core/th.json @@ -120,5 +120,6 @@ "Forced": "บังคับใช้", "TaskOptimizeDatabase": "ปรับปรุงประสิทธิภาพฐานข้อมูล", "TaskOptimizeDatabaseDescription": "ลดขนาดการจัดเก็บฐานข้อมูล ใช้งานคำสั่งนี้หลังจากสแกนไลบรารีหรือหลังจากการเปลี่ยนแปลงฐานข้อมูล อาจจะทำให้ระบบทำงานเร็วขึ้น", - "External": "ภายนอก" + "External": "ภายนอก", + "HearingImpaired": "บกพร่องทางการได้ยิน" } -- cgit v1.2.3 From ab918c6292bb27a0e39f3d54f0f449b97246e59b Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Mon, 9 Jan 2023 00:07:53 +0100 Subject: Fine tune DB settings --- Emby.Server.Implementations/Data/BaseSqliteRepository.cs | 12 +++++++++++- Emby.Server.Implementations/Data/SqliteItemRepository.cs | 2 -- Jellyfin.Server/Program.cs | 4 ---- 3 files changed, 11 insertions(+), 7 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs index ff9aa4c2a3..230f633a7c 100644 --- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs +++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs @@ -60,11 +60,16 @@ namespace Emby.Server.Implementations.Data /// The cache size or null. protected virtual int? CacheSize => null; + /// + /// Gets the locking mode. . + /// + protected virtual string LockingMode => "EXCLUSIVE"; + /// /// Gets the journal mode. . /// /// The journal mode. - protected virtual string JournalMode => "TRUNCATE"; + protected virtual string JournalMode => "WAL"; /// /// Gets the page size. @@ -116,6 +121,11 @@ 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); diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 763ff77f10..fb6902c398 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -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))", diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 1506530f0c..540375dce6 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -273,10 +273,6 @@ namespace Jellyfin.Server ServicePointManager.Expect100Continue = false; Batteries_V2.Init(); - if (raw.sqlite3_enable_shared_cache(1) != raw.SQLITE_OK) - { - _logger.LogWarning("Failed to enable shared cache for SQLite"); - } } /// -- cgit v1.2.3 From 73740f6c6ea8e75ed57ab27228e3796938a93c81 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Mon, 9 Jan 2023 14:14:19 +0100 Subject: Change synchronous_mode to normal --- Emby.Server.Implementations/Data/BaseSqliteRepository.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs index 230f633a7c..acbccc6655 100644 --- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs +++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs @@ -89,7 +89,7 @@ namespace Emby.Server.Implementations.Data /// /// The synchronous mode or null. /// - protected virtual SynchronousMode? Synchronous => null; + protected virtual SynchronousMode? Synchronous => SynchronousMode.Normal; /// /// Gets or sets the write lock. -- cgit v1.2.3 From 6a8d24d9e926bd7b8f54132bf769f92790d9d68a Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Tue, 10 Jan 2023 22:29:05 +0100 Subject: Set journal_size_limit --- Emby.Server.Implementations/Data/BaseSqliteRepository.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs index acbccc6655..1d61667f86 100644 --- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs +++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs @@ -71,6 +71,12 @@ namespace Emby.Server.Implementations.Data /// The journal mode. protected virtual string JournalMode => "WAL"; + /// + /// Gets the journal size limit. . + /// + /// The journal size limit. + protected virtual int? JournalSizeLimit => 0; + /// /// Gets the page size. /// @@ -131,6 +137,11 @@ namespace Emby.Server.Implementations.Data 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); -- cgit v1.2.3 From 8ff0cb1e9dc0ac53ed099e58efa5366c377e393a Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 14 Jan 2023 21:39:42 +0100 Subject: Remove AddPeopleQueryIndex migration --- .../Data/SqliteItemRepository.cs | 36 ++-------------- Jellyfin.Server/Migrations/MigrationRunner.cs | 1 - .../Migrations/Routines/AddPeopleQueryIndex.cs | 49 ---------------------- 3 files changed, 3 insertions(+), 83 deletions(-) delete mode 100644 Jellyfin.Server/Migrations/Routines/AddPeopleQueryIndex.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index fb6902c398..bc703fe90d 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -383,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)", @@ -456,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()) diff --git a/Jellyfin.Server/Migrations/MigrationRunner.cs b/Jellyfin.Server/Migrations/MigrationRunner.cs index e9a45c140f..23fb9e3708 100644 --- a/Jellyfin.Server/Migrations/MigrationRunner.cs +++ b/Jellyfin.Server/Migrations/MigrationRunner.cs @@ -38,7 +38,6 @@ namespace Jellyfin.Server.Migrations typeof(Routines.ReaddDefaultPluginRepository), typeof(Routines.MigrateDisplayPreferencesDb), typeof(Routines.RemoveDownloadImagesInAdvance), - typeof(Routines.AddPeopleQueryIndex), typeof(Routines.MigrateAuthenticationDb) }; diff --git a/Jellyfin.Server/Migrations/Routines/AddPeopleQueryIndex.cs b/Jellyfin.Server/Migrations/Routines/AddPeopleQueryIndex.cs deleted file mode 100644 index 6343c422d5..0000000000 --- a/Jellyfin.Server/Migrations/Routines/AddPeopleQueryIndex.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; -using System.IO; -using MediaBrowser.Controller; -using Microsoft.Extensions.Logging; -using SQLitePCL.pretty; - -namespace Jellyfin.Server.Migrations.Routines -{ - /// - /// Migration to add table indexes to optimize the Persons query. - /// - public class AddPeopleQueryIndex : IMigrationRoutine - { - private const string DbFilename = "library.db"; - private readonly ILogger _logger; - private readonly IServerApplicationPaths _serverApplicationPaths; - - /// - /// Initializes a new instance of the class. - /// - /// Instance of the interface. - /// Instance of the interface. - public AddPeopleQueryIndex(ILogger logger, IServerApplicationPaths serverApplicationPaths) - { - _logger = logger; - _serverApplicationPaths = serverApplicationPaths; - } - - /// - public Guid Id => new Guid("DE009B59-BAAE-428D-A810-F67762DC05B8"); - - /// - public string Name => "AddPeopleQueryIndex"; - - /// - public bool PerformOnNewInstall => true; - - /// - public void Perform() - { - var databasePath = Path.Join(_serverApplicationPaths.DataPath, DbFilename); - using var connection = SQLite3.Open(databasePath, ConnectionFlags.ReadWrite, null); - _logger.LogInformation("Creating index idx_TypedBaseItemsUserDataKeyType"); - connection.Execute("CREATE INDEX IF NOT EXISTS idx_TypedBaseItemsUserDataKeyType ON TypedBaseItems(UserDataKey, Type);"); - _logger.LogInformation("Creating index idx_PeopleNameListOrder"); - connection.Execute("CREATE INDEX IF NOT EXISTS idx_PeopleNameListOrder ON People(Name, ListOrder);"); - } - } -} -- cgit v1.2.3 From dc85d86ea1db7608368eadbfee80f7618653f42d Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Sun, 15 Jan 2023 15:39:57 -0500 Subject: Enable in-process restarting --- Emby.Server.Implementations/ApplicationHost.cs | 13 +--- Emby.Server.Implementations/IStartupOptions.cs | 10 --- .../Plugins/PluginManager.cs | 12 +++- Jellyfin.Server/Program.cs | 81 ++++++++-------------- Jellyfin.Server/StartupOptions.cs | 8 --- MediaBrowser.Common/IApplicationHost.cs | 6 -- MediaBrowser.Common/Plugins/IPluginManager.cs | 5 ++ 7 files changed, 46 insertions(+), 89 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 7b3d07dfc1..7b40f530c9 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -193,11 +193,6 @@ namespace Emby.Server.Implementations /// private string PublishedServerUrl => _startupConfig[AddressOverrideKey]; - /// - /// Gets a value indicating whether this instance can self restart. - /// - public bool CanSelfRestart => _startupOptions.RestartPath is not null; - public bool CoreStartupHasCompleted { get; private set; } public virtual bool CanLaunchWebBrowser @@ -935,17 +930,13 @@ namespace Emby.Server.Implementations /// 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,7 @@ namespace Emby.Server.Implementations CachePath = ApplicationPaths.CachePath, OperatingSystem = MediaBrowser.Common.System.OperatingSystem.Id.ToString(), OperatingSystemDisplayName = MediaBrowser.Common.System.OperatingSystem.Name, - CanSelfRestart = CanSelfRestart, + CanSelfRestart = true, CanLaunchWebBrowser = CanLaunchWebBrowser, TranscodingTempPath = ConfigurationManager.GetTranscodePath(), ServerName = FriendlyName, diff --git a/Emby.Server.Implementations/IStartupOptions.cs b/Emby.Server.Implementations/IStartupOptions.cs index 3769ae4dd8..b7bcaace1b 100644 --- a/Emby.Server.Implementations/IStartupOptions.cs +++ b/Emby.Server.Implementations/IStartupOptions.cs @@ -20,16 +20,6 @@ namespace Emby.Server.Implementations /// string? PackageName { get; } - /// - /// Gets the value of the --restartpath command line option. - /// - string? RestartPath { get; } - - /// - /// Gets the value of the --restartargs command line option. - /// - string? RestartArgs { get; } - /// /// Gets the value of the --published-server-url command line option. /// diff --git a/Emby.Server.Implementations/Plugins/PluginManager.cs b/Emby.Server.Implementations/Plugins/PluginManager.cs index 14e7c22696..6ef66f2b5d 100644 --- a/Emby.Server.Implementations/Plugins/PluginManager.cs +++ b/Emby.Server.Implementations/Plugins/PluginManager.cs @@ -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 AssemblyLoadContext _assemblyLoadContext; private readonly JsonSerializerOptions _jsonOptions; private readonly ILogger _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(); + + _assemblyLoadContext = new AssemblyLoadContext("PluginContext", true); } private IHttpClientFactory HttpClientFactory @@ -124,7 +128,7 @@ namespace Emby.Server.Implementations.Plugins Assembly assembly; try { - assembly = Assembly.LoadFrom(file); + assembly = _assemblyLoadContext.LoadFromAssemblyPath(file); // Load all required types to verify that the plugin will load assembly.GetTypes(); @@ -156,6 +160,12 @@ namespace Emby.Server.Implementations.Plugins } } + /// + public void UnloadAssemblies() + { + _assemblyLoadContext.Unload(); + } + /// /// Creates all the plugin instances. /// diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index b817ea6275..dded20347b 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -12,6 +12,7 @@ using Jellyfin.Server.Extensions; using Jellyfin.Server.Helpers; using Jellyfin.Server.Implementations; using MediaBrowser.Common.Configuration; +using MediaBrowser.Controller; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -40,8 +41,9 @@ namespace Jellyfin.Server /// public const string LoggingConfigFileSystem = "logging.json"; - private static readonly CancellationTokenSource _tokenSource = new CancellationTokenSource(); private static readonly ILoggerFactory _loggerFactory = new SerilogLoggerFactory(); + private static CancellationTokenSource _tokenSource = new(); + private static long _startTimestamp; private static ILogger _logger = NullLogger.Instance; private static bool _restartOnShutdown; @@ -86,11 +88,11 @@ namespace Jellyfin.Server private static async Task StartApp(StartupOptions options) { - var startTimestamp = Stopwatch.GetTimestamp(); + _startTimestamp = Stopwatch.GetTimestamp(); // Log all uncaught exceptions to std error static void UnhandledExceptionToConsole(object sender, UnhandledExceptionEventArgs e) => - Console.Error.WriteLine("Unhandled Exception\n" + e.ExceptionObject.ToString()); + Console.Error.WriteLine("Unhandled Exception\n" + e.ExceptionObject); AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionToConsole; ServerApplicationPaths appPaths = StartupHelpers.CreateApplicationPaths(options); @@ -151,14 +153,14 @@ namespace Jellyfin.Server // If hosting the web client, validate the client content path if (startupConfig.HostWebClient()) { - string? webContentPath = appPaths.WebPath; + var webContentPath = appPaths.WebPath; if (!Directory.Exists(webContentPath) || !Directory.EnumerateFiles(webContentPath).Any()) { _logger.LogError( "The server is expected to host the web client, but the provided content directory is either " + "invalid or empty: {WebContentPath}. If you do not want to host the web client with the " + "server, you may set the '--nowebclient' command line flag, or set" + - "'{ConfigKey}=false' in your config settings.", + "'{ConfigKey}=false' in your config settings", webContentPath, HostWebClientKey); Environment.ExitCode = 1; @@ -169,15 +171,31 @@ namespace Jellyfin.Server StartupHelpers.PerformStaticInitialization(); Migrations.MigrationRunner.RunPreStartup(appPaths, _loggerFactory); + do + { + _restartOnShutdown = false; + await StartServer(appPaths, options, startupConfig).ConfigureAwait(false); + + if (_restartOnShutdown) + { + _tokenSource = new CancellationTokenSource(); + _startTimestamp = Stopwatch.GetTimestamp(); + } + } while (_restartOnShutdown); + } + + private static async Task StartServer(IServerApplicationPaths appPaths, StartupOptions options, IConfiguration startupConfig) + { var appHost = new CoreAppHost( appPaths, _loggerFactory, options, startupConfig); + IHost? host = null; try { - var host = Host.CreateDefaultBuilder() + host = Host.CreateDefaultBuilder() .ConfigureServices(services => appHost.Init(services)) .ConfigureWebHostDefaults(webHostBuilder => webHostBuilder.ConfigureWebHostBuilder(appHost, startupConfig, appPaths, _logger)) .ConfigureAppConfiguration(config => config.ConfigureAppConfiguration(options, appPaths, startupConfig)) @@ -203,13 +221,13 @@ namespace Jellyfin.Server } catch (Exception ex) when (ex is not TaskCanceledException) { - _logger.LogError("Kestrel failed to start! This is most likely due to an invalid address or port bind - correct your bind configuration in network.xml and try again."); + _logger.LogError("Kestrel failed to start! This is most likely due to an invalid address or port bind - correct your bind configuration in network.xml and try again"); throw; } await appHost.RunStartupTasksAsync(_tokenSource.Token).ConfigureAwait(false); - _logger.LogInformation("Startup complete {Time:g}", Stopwatch.GetElapsedTime(startTimestamp)); + _logger.LogInformation("Startup complete {Time:g}", Stopwatch.GetElapsedTime(_startTimestamp)); // Block main thread until shutdown await Task.Delay(-1, _tokenSource.Token).ConfigureAwait(false); @@ -220,7 +238,7 @@ namespace Jellyfin.Server } catch (Exception ex) { - _logger.LogCritical(ex, "Error while starting server."); + _logger.LogCritical(ex, "Error while starting server"); } finally { @@ -240,11 +258,7 @@ namespace Jellyfin.Server } await appHost.DisposeAsync().ConfigureAwait(false); - } - - if (_restartOnShutdown) - { - StartNewInstance(options); + host?.Dispose(); } } @@ -282,44 +296,5 @@ namespace Jellyfin.Server .AddEnvironmentVariables("JELLYFIN_") .AddInMemoryCollection(commandLineOpts.ConvertToConfig()); } - - private static void StartNewInstance(StartupOptions options) - { - _logger.LogInformation("Starting new instance"); - - var module = options.RestartPath; - - if (string.IsNullOrWhiteSpace(module)) - { - module = Environment.GetCommandLineArgs()[0]; - } - - string commandLineArgsString; - if (options.RestartArgs is not null) - { - commandLineArgsString = options.RestartArgs; - } - else - { - commandLineArgsString = string.Join( - ' ', - Environment.GetCommandLineArgs().Skip(1).Select(NormalizeCommandLineArgument)); - } - - _logger.LogInformation("Executable: {0}", module); - _logger.LogInformation("Arguments: {0}", commandLineArgsString); - - Process.Start(module, commandLineArgsString); - } - - private static string NormalizeCommandLineArgument(string arg) - { - if (!arg.Contains(' ', StringComparison.Ordinal)) - { - return arg; - } - - return "\"" + arg + "\""; - } } } diff --git a/Jellyfin.Server/StartupOptions.cs b/Jellyfin.Server/StartupOptions.cs index 0d9f379e0e..c3989751ca 100644 --- a/Jellyfin.Server/StartupOptions.cs +++ b/Jellyfin.Server/StartupOptions.cs @@ -63,14 +63,6 @@ namespace Jellyfin.Server [Option("package-name", Required = false, HelpText = "Used when packaging Jellyfin (example, synology).")] public string? PackageName { get; set; } - /// - [Option("restartpath", Required = false, HelpText = "Path to restart script.")] - public string? RestartPath { get; set; } - - /// - [Option("restartargs", Required = false, HelpText = "Arguments for restart script.")] - public string? RestartArgs { get; set; } - /// [Option("published-server-url", Required = false, HelpText = "Jellyfin Server URL to publish via auto discover process")] public string? PublishedServerUrl { get; set; } diff --git a/MediaBrowser.Common/IApplicationHost.cs b/MediaBrowser.Common/IApplicationHost.cs index 53683cdbdf..96ee701b38 100644 --- a/MediaBrowser.Common/IApplicationHost.cs +++ b/MediaBrowser.Common/IApplicationHost.cs @@ -47,12 +47,6 @@ namespace MediaBrowser.Common /// true if this instance is shutting down; otherwise, false. bool IsShuttingDown { get; } - /// - /// Gets a value indicating whether this instance can self restart. - /// - /// true if this instance can self restart; otherwise, false. - bool CanSelfRestart { get; } - /// /// Gets the application version. /// diff --git a/MediaBrowser.Common/Plugins/IPluginManager.cs b/MediaBrowser.Common/Plugins/IPluginManager.cs index 176bcbbd54..fa92d383a2 100644 --- a/MediaBrowser.Common/Plugins/IPluginManager.cs +++ b/MediaBrowser.Common/Plugins/IPluginManager.cs @@ -29,6 +29,11 @@ namespace MediaBrowser.Common.Plugins /// An IEnumerable{Assembly}. IEnumerable LoadAssemblies(); + /// + /// Unloads all of the assemblies. + /// + void UnloadAssemblies(); + /// /// Registers the plugin's services with the DI. /// Note: DI is not yet instantiated yet. -- cgit v1.2.3 From a48f18887468015876a8b056f15b68d6ef49ce04 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Sun, 15 Jan 2023 17:00:38 -0500 Subject: Use separate assembly load contexts per plugin --- Emby.Server.Implementations/Plugins/PluginManager.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Plugins/PluginManager.cs b/Emby.Server.Implementations/Plugins/PluginManager.cs index 6ef66f2b5d..3be20e7e3f 100644 --- a/Emby.Server.Implementations/Plugins/PluginManager.cs +++ b/Emby.Server.Implementations/Plugins/PluginManager.cs @@ -31,7 +31,7 @@ namespace Emby.Server.Implementations.Plugins { private readonly string _pluginsPath; private readonly Version _appVersion; - private readonly AssemblyLoadContext _assemblyLoadContext; + private readonly List _assemblyLoadContexts; private readonly JsonSerializerOptions _jsonOptions; private readonly ILogger _logger; private readonly IApplicationHost _appHost; @@ -79,7 +79,7 @@ namespace Emby.Server.Implementations.Plugins _minimumVersion = new Version(0, 0, 0, 1); _plugins = Directory.Exists(_pluginsPath) ? DiscoverPlugins().ToList() : new List(); - _assemblyLoadContext = new AssemblyLoadContext("PluginContext", true); + _assemblyLoadContexts = new List(); } private IHttpClientFactory HttpClientFactory @@ -128,7 +128,10 @@ namespace Emby.Server.Implementations.Plugins Assembly assembly; try { - assembly = _assemblyLoadContext.LoadFromAssemblyPath(file); + var assemblyLoadContext = new AssemblyLoadContext($"{plugin.Name} ${plugin.Version}", true); + _assemblyLoadContexts.Add(assemblyLoadContext); + + assembly = assemblyLoadContext.LoadFromAssemblyPath(file); // Load all required types to verify that the plugin will load assembly.GetTypes(); @@ -163,7 +166,10 @@ namespace Emby.Server.Implementations.Plugins /// public void UnloadAssemblies() { - _assemblyLoadContext.Unload(); + foreach (var assemblyLoadContext in _assemblyLoadContexts) + { + assemblyLoadContext.Unload(); + } } /// -- cgit v1.2.3 From 577d396649f44a26f9f8bf291a58994d414e23a6 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Sun, 15 Jan 2023 17:35:36 -0500 Subject: Use custom plugin assembly load context --- .../Plugins/PluginLoadContext.cs | 33 ++++++++++++++++++++++ .../Plugins/PluginManager.cs | 2 +- 2 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 Emby.Server.Implementations/Plugins/PluginLoadContext.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Plugins/PluginLoadContext.cs b/Emby.Server.Implementations/Plugins/PluginLoadContext.cs new file mode 100644 index 0000000000..d04e9cf685 --- /dev/null +++ b/Emby.Server.Implementations/Plugins/PluginLoadContext.cs @@ -0,0 +1,33 @@ +using System.Reflection; +using System.Runtime.Loader; + +namespace Emby.Server.Implementations.Plugins; + +/// +/// A custom for loading Jellyfin plugins. +/// +public class PluginLoadContext : AssemblyLoadContext +{ + private readonly AssemblyDependencyResolver _resolver; + + /// + /// Initializes a new instance of the class. + /// + /// The path of the plugin assembly. + public PluginLoadContext(string path) : base(true) + { + _resolver = new AssemblyDependencyResolver(path); + } + + /// + protected override Assembly? Load(AssemblyName assemblyName) + { + var assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName); + if (assemblyPath is not null) + { + return LoadFromAssemblyPath(assemblyPath); + } + + return null; + } +} diff --git a/Emby.Server.Implementations/Plugins/PluginManager.cs b/Emby.Server.Implementations/Plugins/PluginManager.cs index 3be20e7e3f..f2212f4dcb 100644 --- a/Emby.Server.Implementations/Plugins/PluginManager.cs +++ b/Emby.Server.Implementations/Plugins/PluginManager.cs @@ -128,7 +128,7 @@ namespace Emby.Server.Implementations.Plugins Assembly assembly; try { - var assemblyLoadContext = new AssemblyLoadContext($"{plugin.Name} ${plugin.Version}", true); + var assemblyLoadContext = new PluginLoadContext(file); _assemblyLoadContexts.Add(assemblyLoadContext); assembly = assemblyLoadContext.LoadFromAssemblyPath(file); -- cgit v1.2.3 From 776294b5f8499434de515ec6c595bc6db354d9a2 Mon Sep 17 00:00:00 2001 From: DarrenRuane Date: Sun, 15 Jan 2023 23:09:33 +0000 Subject: Fix pre-existing chapter images not being deleted after the chapter image generation setting is disabled --- Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs | 1 - 1 file changed, 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs index abc203618d..6ad6c4cbd6 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs @@ -100,7 +100,6 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks EnableImages = false }, SourceTypes = new SourceType[] { SourceType.Library }, - HasChapterImages = false, IsVirtualItem = false }) .OfType public class DeviceManager : IDeviceManager { - private readonly IDbContextFactory _dbProvider; + private readonly IDbContextFactory _dbProvider; private readonly IUserManager _userManager; private readonly ConcurrentDictionary _capabilitiesMap = new(); @@ -32,7 +32,7 @@ namespace Jellyfin.Server.Implementations.Devices /// /// The database provider. /// The user manager. - public DeviceManager(IDbContextFactory dbProvider, IUserManager userManager) + public DeviceManager(IDbContextFactory dbProvider, IUserManager userManager) { _dbProvider = dbProvider; _userManager = userManager; diff --git a/Jellyfin.Server.Implementations/Extensions/ServiceCollectionExtensions.cs b/Jellyfin.Server.Implementations/Extensions/ServiceCollectionExtensions.cs index 05c6229316..de64911b42 100644 --- a/Jellyfin.Server.Implementations/Extensions/ServiceCollectionExtensions.cs +++ b/Jellyfin.Server.Implementations/Extensions/ServiceCollectionExtensions.cs @@ -29,7 +29,7 @@ public static class ServiceCollectionExtensions .SkipCachingResults(result => result.Value is null || (result.Value is EFTableRows rows && rows.RowsCount == 0))); - serviceCollection.AddPooledDbContextFactory((serviceProvider, opt) => + serviceCollection.AddPooledDbContextFactory((serviceProvider, opt) => { var applicationPaths = serviceProvider.GetRequiredService(); var loggerFactory = serviceProvider.GetRequiredService(); diff --git a/Jellyfin.Server.Implementations/JellyfinDb.cs b/Jellyfin.Server.Implementations/JellyfinDb.cs deleted file mode 100644 index 064118ca54..0000000000 --- a/Jellyfin.Server.Implementations/JellyfinDb.cs +++ /dev/null @@ -1,188 +0,0 @@ -using System; -using System.Linq; -using Jellyfin.Data.Entities; -using Jellyfin.Data.Entities.Security; -using Jellyfin.Data.Interfaces; -using Microsoft.EntityFrameworkCore; - -namespace Jellyfin.Server.Implementations; - -/// -public class JellyfinDb : DbContext -{ - /// - /// Initializes a new instance of the class. - /// - /// The database context options. - public JellyfinDb(DbContextOptions options) : base(options) - { - } - - /// - /// Gets the containing the access schedules. - /// - public DbSet AccessSchedules => Set(); - - /// - /// Gets the containing the activity logs. - /// - public DbSet ActivityLogs => Set(); - - /// - /// Gets the containing the API keys. - /// - public DbSet ApiKeys => Set(); - - /// - /// Gets the containing the devices. - /// - public DbSet Devices => Set(); - - /// - /// Gets the containing the device options. - /// - public DbSet DeviceOptions => Set(); - - /// - /// Gets the containing the display preferences. - /// - public DbSet DisplayPreferences => Set(); - - /// - /// Gets the containing the image infos. - /// - public DbSet ImageInfos => Set(); - - /// - /// Gets the containing the item display preferences. - /// - public DbSet ItemDisplayPreferences => Set(); - - /// - /// Gets the containing the custom item display preferences. - /// - public DbSet CustomItemDisplayPreferences => Set(); - - /// - /// Gets the containing the permissions. - /// - public DbSet Permissions => Set(); - - /// - /// Gets the containing the preferences. - /// - public DbSet Preferences => Set(); - - /// - /// Gets the containing the users. - /// - public DbSet Users => Set(); - - /*public DbSet Artwork => Set(); - - public DbSet Books => Set(); - - public DbSet BookMetadata => Set(); - - public DbSet Chapters => Set(); - - public DbSet Collections => Set(); - - public DbSet CollectionItems => Set(); - - public DbSet Companies => Set(); - - public DbSet CompanyMetadata => Set(); - - public DbSet CustomItems => Set(); - - public DbSet CustomItemMetadata => Set(); - - public DbSet Episodes => Set(); - - public DbSet EpisodeMetadata => Set(); - - public DbSet Genres => Set(); - - public DbSet Groups => Set(); - - public DbSet Libraries => Set(); - - public DbSet LibraryItems => Set(); - - public DbSet LibraryRoot => Set(); - - public DbSet MediaFiles => Set(); - - public DbSet MediaFileStream => Set(); - - public DbSet Metadata => Set(); - - public DbSet MetadataProviders => Set(); - - public DbSet MetadataProviderIds => Set(); - - public DbSet Movies => Set(); - - public DbSet MovieMetadata => Set(); - - public DbSet MusicAlbums => Set(); - - public DbSet MusicAlbumMetadata => Set(); - - public DbSet People => Set(); - - public DbSet PersonRoles => Set(); - - public DbSet Photo => Set(); - - public DbSet PhotoMetadata => Set(); - - public DbSet ProviderMappings => Set(); - - public DbSet Ratings => Set(); - - /// - /// Repository for global::Jellyfin.Data.Entities.RatingSource - This is the entity to - /// store review ratings, not age ratings. - /// - public DbSet RatingSources => Set(); - - public DbSet Releases => Set(); - - public DbSet Seasons => Set(); - - public DbSet SeasonMetadata => Set(); - - public DbSet Series => Set(); - - public DbSet SeriesMetadata => Set Tracks => Set(); - - public DbSet TrackMetadata => Set();*/ - - /// - public override int SaveChanges() - { - foreach (var saveEntity in ChangeTracker.Entries() - .Where(e => e.State == EntityState.Modified) - .Select(entry => entry.Entity) - .OfType()) - { - saveEntity.OnSavingChanges(); - } - - return base.SaveChanges(); - } - - /// - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - modelBuilder.SetDefaultDateTimeKind(DateTimeKind.Utc); - base.OnModelCreating(modelBuilder); - - // Configuration for each entity is in it's own class inside 'ModelConfiguration'. - modelBuilder.ApplyConfigurationsFromAssembly(typeof(JellyfinDb).Assembly); - } -} diff --git a/Jellyfin.Server.Implementations/JellyfinDbContext.cs b/Jellyfin.Server.Implementations/JellyfinDbContext.cs new file mode 100644 index 0000000000..0d91707e3e --- /dev/null +++ b/Jellyfin.Server.Implementations/JellyfinDbContext.cs @@ -0,0 +1,188 @@ +using System; +using System.Linq; +using Jellyfin.Data.Entities; +using Jellyfin.Data.Entities.Security; +using Jellyfin.Data.Interfaces; +using Microsoft.EntityFrameworkCore; + +namespace Jellyfin.Server.Implementations; + +/// +public class JellyfinDbContext : DbContext +{ + /// + /// Initializes a new instance of the class. + /// + /// The database context options. + public JellyfinDbContext(DbContextOptions options) : base(options) + { + } + + /// + /// Gets the containing the access schedules. + /// + public DbSet AccessSchedules => Set(); + + /// + /// Gets the containing the activity logs. + /// + public DbSet ActivityLogs => Set(); + + /// + /// Gets the containing the API keys. + /// + public DbSet ApiKeys => Set(); + + /// + /// Gets the containing the devices. + /// + public DbSet Devices => Set(); + + /// + /// Gets the containing the device options. + /// + public DbSet DeviceOptions => Set(); + + /// + /// Gets the containing the display preferences. + /// + public DbSet DisplayPreferences => Set(); + + /// + /// Gets the containing the image infos. + /// + public DbSet ImageInfos => Set(); + + /// + /// Gets the containing the item display preferences. + /// + public DbSet ItemDisplayPreferences => Set(); + + /// + /// Gets the containing the custom item display preferences. + /// + public DbSet CustomItemDisplayPreferences => Set(); + + /// + /// Gets the containing the permissions. + /// + public DbSet Permissions => Set(); + + /// + /// Gets the containing the preferences. + /// + public DbSet Preferences => Set(); + + /// + /// Gets the containing the users. + /// + public DbSet Users => Set(); + + /*public DbSet Artwork => Set(); + + public DbSet Books => Set(); + + public DbSet BookMetadata => Set(); + + public DbSet Chapters => Set(); + + public DbSet Collections => Set(); + + public DbSet CollectionItems => Set(); + + public DbSet Companies => Set(); + + public DbSet CompanyMetadata => Set(); + + public DbSet CustomItems => Set(); + + public DbSet CustomItemMetadata => Set(); + + public DbSet Episodes => Set(); + + public DbSet EpisodeMetadata => Set(); + + public DbSet Genres => Set(); + + public DbSet Groups => Set(); + + public DbSet Libraries => Set(); + + public DbSet LibraryItems => Set(); + + public DbSet LibraryRoot => Set(); + + public DbSet MediaFiles => Set(); + + public DbSet MediaFileStream => Set(); + + public DbSet Metadata => Set(); + + public DbSet MetadataProviders => Set(); + + public DbSet MetadataProviderIds => Set(); + + public DbSet Movies => Set(); + + public DbSet MovieMetadata => Set(); + + public DbSet MusicAlbums => Set(); + + public DbSet MusicAlbumMetadata => Set(); + + public DbSet People => Set(); + + public DbSet PersonRoles => Set(); + + public DbSet Photo => Set(); + + public DbSet PhotoMetadata => Set(); + + public DbSet ProviderMappings => Set(); + + public DbSet Ratings => Set(); + + /// + /// Repository for global::Jellyfin.Data.Entities.RatingSource - This is the entity to + /// store review ratings, not age ratings. + /// + public DbSet RatingSources => Set(); + + public DbSet Releases => Set(); + + public DbSet Seasons => Set(); + + public DbSet SeasonMetadata => Set(); + + public DbSet Series => Set(); + + public DbSet SeriesMetadata => Set Tracks => Set(); + + public DbSet TrackMetadata => Set();*/ + + /// + public override int SaveChanges() + { + foreach (var saveEntity in ChangeTracker.Entries() + .Where(e => e.State == EntityState.Modified) + .Select(entry => entry.Entity) + .OfType()) + { + saveEntity.OnSavingChanges(); + } + + return base.SaveChanges(); + } + + /// + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.SetDefaultDateTimeKind(DateTimeKind.Utc); + base.OnModelCreating(modelBuilder); + + // Configuration for each entity is in it's own class inside 'ModelConfiguration'. + modelBuilder.ApplyConfigurationsFromAssembly(typeof(JellyfinDbContext).Assembly); + } +} diff --git a/Jellyfin.Server.Implementations/Migrations/20200514181226_AddActivityLog.Designer.cs b/Jellyfin.Server.Implementations/Migrations/20200514181226_AddActivityLog.Designer.cs index 98a83b7450..4be6c2faa3 100644 --- a/Jellyfin.Server.Implementations/Migrations/20200514181226_AddActivityLog.Designer.cs +++ b/Jellyfin.Server.Implementations/Migrations/20200514181226_AddActivityLog.Designer.cs @@ -10,7 +10,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace Jellyfin.Server.Implementations.Migrations { - [DbContext(typeof(JellyfinDb))] + [DbContext(typeof(JellyfinDbContext))] [Migration("20200514181226_AddActivityLog")] partial class AddActivityLog { diff --git a/Jellyfin.Server.Implementations/Migrations/20200613202153_AddUsers.Designer.cs b/Jellyfin.Server.Implementations/Migrations/20200613202153_AddUsers.Designer.cs index 6342ce9cf3..f3254734ad 100644 --- a/Jellyfin.Server.Implementations/Migrations/20200613202153_AddUsers.Designer.cs +++ b/Jellyfin.Server.Implementations/Migrations/20200613202153_AddUsers.Designer.cs @@ -10,7 +10,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace Jellyfin.Server.Implementations.Migrations { - [DbContext(typeof(JellyfinDb))] + [DbContext(typeof(JellyfinDbContext))] [Migration("20200613202153_AddUsers")] partial class AddUsers { diff --git a/Jellyfin.Server.Implementations/Migrations/20200728005145_AddDisplayPreferences.Designer.cs b/Jellyfin.Server.Implementations/Migrations/20200728005145_AddDisplayPreferences.Designer.cs index d44707d069..12d6faa8f5 100644 --- a/Jellyfin.Server.Implementations/Migrations/20200728005145_AddDisplayPreferences.Designer.cs +++ b/Jellyfin.Server.Implementations/Migrations/20200728005145_AddDisplayPreferences.Designer.cs @@ -10,7 +10,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace Jellyfin.Server.Implementations.Migrations { - [DbContext(typeof(JellyfinDb))] + [DbContext(typeof(JellyfinDbContext))] [Migration("20200728005145_AddDisplayPreferences")] partial class AddDisplayPreferences { diff --git a/Jellyfin.Server.Implementations/Migrations/20200905220533_FixDisplayPreferencesIndex.Designer.cs b/Jellyfin.Server.Implementations/Migrations/20200905220533_FixDisplayPreferencesIndex.Designer.cs index 2234f9d5fd..f1cc208058 100644 --- a/Jellyfin.Server.Implementations/Migrations/20200905220533_FixDisplayPreferencesIndex.Designer.cs +++ b/Jellyfin.Server.Implementations/Migrations/20200905220533_FixDisplayPreferencesIndex.Designer.cs @@ -10,7 +10,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace Jellyfin.Server.Implementations.Migrations { - [DbContext(typeof(JellyfinDb))] + [DbContext(typeof(JellyfinDbContext))] [Migration("20200905220533_FixDisplayPreferencesIndex")] partial class FixDisplayPreferencesIndex { diff --git a/Jellyfin.Server.Implementations/Migrations/20201004171403_AddMaxActiveSessions.Designer.cs b/Jellyfin.Server.Implementations/Migrations/20201004171403_AddMaxActiveSessions.Designer.cs index e5c326a326..f134d363c8 100644 --- a/Jellyfin.Server.Implementations/Migrations/20201004171403_AddMaxActiveSessions.Designer.cs +++ b/Jellyfin.Server.Implementations/Migrations/20201004171403_AddMaxActiveSessions.Designer.cs @@ -10,7 +10,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace Jellyfin.Server.Implementations.Migrations { - [DbContext(typeof(JellyfinDb))] + [DbContext(typeof(JellyfinDbContext))] [Migration("20201004171403_AddMaxActiveSessions")] partial class AddMaxActiveSessions { diff --git a/Jellyfin.Server.Implementations/Migrations/20201204223655_AddCustomDisplayPreferences.Designer.cs b/Jellyfin.Server.Implementations/Migrations/20201204223655_AddCustomDisplayPreferences.Designer.cs index 10663d0655..ec65205d45 100644 --- a/Jellyfin.Server.Implementations/Migrations/20201204223655_AddCustomDisplayPreferences.Designer.cs +++ b/Jellyfin.Server.Implementations/Migrations/20201204223655_AddCustomDisplayPreferences.Designer.cs @@ -9,7 +9,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace Jellyfin.Server.Implementations.Migrations { - [DbContext(typeof(JellyfinDb))] + [DbContext(typeof(JellyfinDbContext))] [Migration("20201204223655_AddCustomDisplayPreferences")] partial class AddCustomDisplayPreferences { diff --git a/Jellyfin.Server.Implementations/Migrations/20210320181425_AddIndexesAndCollations.Designer.cs b/Jellyfin.Server.Implementations/Migrations/20210320181425_AddIndexesAndCollations.Designer.cs index 8696768245..45dad6be68 100644 --- a/Jellyfin.Server.Implementations/Migrations/20210320181425_AddIndexesAndCollations.Designer.cs +++ b/Jellyfin.Server.Implementations/Migrations/20210320181425_AddIndexesAndCollations.Designer.cs @@ -10,7 +10,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace Jellyfin.Server.Implementations.Migrations { - [DbContext(typeof(JellyfinDb))] + [DbContext(typeof(JellyfinDbContext))] [Migration("20210320181425_AddIndexesAndCollations")] partial class AddIndexesAndCollations { diff --git a/Jellyfin.Server.Implementations/Migrations/20210407110544_NullableCustomPrefValue.Designer.cs b/Jellyfin.Server.Implementations/Migrations/20210407110544_NullableCustomPrefValue.Designer.cs index d332d19f28..eff84b4574 100644 --- a/Jellyfin.Server.Implementations/Migrations/20210407110544_NullableCustomPrefValue.Designer.cs +++ b/Jellyfin.Server.Implementations/Migrations/20210407110544_NullableCustomPrefValue.Designer.cs @@ -9,7 +9,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace Jellyfin.Server.Implementations.Migrations { - [DbContext(typeof(JellyfinDb))] + [DbContext(typeof(JellyfinDbContext))] [Migration("20210407110544_NullableCustomPrefValue")] partial class NullableCustomPrefValue { diff --git a/Jellyfin.Server.Implementations/Migrations/20210814002109_AddDevices.Designer.cs b/Jellyfin.Server.Implementations/Migrations/20210814002109_AddDevices.Designer.cs index 7e9566e2ea..ad7c2dd2c9 100644 --- a/Jellyfin.Server.Implementations/Migrations/20210814002109_AddDevices.Designer.cs +++ b/Jellyfin.Server.Implementations/Migrations/20210814002109_AddDevices.Designer.cs @@ -10,7 +10,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace Jellyfin.Server.Implementations.Migrations { - [DbContext(typeof(JellyfinDb))] + [DbContext(typeof(JellyfinDbContext))] [Migration("20210814002109_AddDevices")] partial class AddDevices { diff --git a/Jellyfin.Server.Implementations/Migrations/20221022080052_AddIndexActivityLogsDateCreated.Designer.cs b/Jellyfin.Server.Implementations/Migrations/20221022080052_AddIndexActivityLogsDateCreated.Designer.cs index 03e3f3c921..f9497a3b69 100644 --- a/Jellyfin.Server.Implementations/Migrations/20221022080052_AddIndexActivityLogsDateCreated.Designer.cs +++ b/Jellyfin.Server.Implementations/Migrations/20221022080052_AddIndexActivityLogsDateCreated.Designer.cs @@ -12,7 +12,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace Jellyfin.Server.Implementations.Migrations { - [DbContext(typeof(JellyfinDb))] + [DbContext(typeof(JellyfinDbContext))] [Migration("20221022080052_AddIndexActivityLogsDateCreated")] partial class AddIndexActivityLogsDateCreated { diff --git a/Jellyfin.Server.Implementations/Migrations/DesignTimeJellyfinDbFactory.cs b/Jellyfin.Server.Implementations/Migrations/DesignTimeJellyfinDbFactory.cs index 72a4a8c3b6..940cf7c5d5 100644 --- a/Jellyfin.Server.Implementations/Migrations/DesignTimeJellyfinDbFactory.cs +++ b/Jellyfin.Server.Implementations/Migrations/DesignTimeJellyfinDbFactory.cs @@ -4,17 +4,17 @@ using Microsoft.EntityFrameworkCore.Design; namespace Jellyfin.Server.Implementations.Migrations { /// - /// The design time factory for . + /// The design time factory for . /// This is only used for the creation of migrations and not during runtime. /// - internal class DesignTimeJellyfinDbFactory : IDesignTimeDbContextFactory + internal class DesignTimeJellyfinDbFactory : IDesignTimeDbContextFactory { - public JellyfinDb CreateDbContext(string[] args) + public JellyfinDbContext CreateDbContext(string[] args) { - var optionsBuilder = new DbContextOptionsBuilder(); + var optionsBuilder = new DbContextOptionsBuilder(); optionsBuilder.UseSqlite("Data Source=jellyfin.db"); - return new JellyfinDb(optionsBuilder.Options); + return new JellyfinDbContext(optionsBuilder.Options); } } } diff --git a/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs b/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs index 2dd7b094aa..dd5f7f0121 100644 --- a/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs +++ b/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs @@ -9,7 +9,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace Jellyfin.Server.Implementations.Migrations { - [DbContext(typeof(JellyfinDb))] + [DbContext(typeof(JellyfinDbContext))] partial class JellyfinDbModelSnapshot : ModelSnapshot { protected override void BuildModel(ModelBuilder modelBuilder) diff --git a/Jellyfin.Server.Implementations/Security/AuthenticationManager.cs b/Jellyfin.Server.Implementations/Security/AuthenticationManager.cs index 1b56237fe7..b2dfe60a14 100644 --- a/Jellyfin.Server.Implementations/Security/AuthenticationManager.cs +++ b/Jellyfin.Server.Implementations/Security/AuthenticationManager.cs @@ -10,13 +10,13 @@ namespace Jellyfin.Server.Implementations.Security /// public class AuthenticationManager : IAuthenticationManager { - private readonly IDbContextFactory _dbProvider; + private readonly IDbContextFactory _dbProvider; /// /// Initializes a new instance of the class. /// /// The database provider. - public AuthenticationManager(IDbContextFactory dbProvider) + public AuthenticationManager(IDbContextFactory dbProvider) { _dbProvider = dbProvider; } diff --git a/Jellyfin.Server.Implementations/Security/AuthorizationContext.cs b/Jellyfin.Server.Implementations/Security/AuthorizationContext.cs index ec5742bab0..63d3e8a04c 100644 --- a/Jellyfin.Server.Implementations/Security/AuthorizationContext.cs +++ b/Jellyfin.Server.Implementations/Security/AuthorizationContext.cs @@ -16,12 +16,12 @@ namespace Jellyfin.Server.Implementations.Security { public class AuthorizationContext : IAuthorizationContext { - private readonly IDbContextFactory _jellyfinDbProvider; + private readonly IDbContextFactory _jellyfinDbProvider; private readonly IUserManager _userManager; private readonly IServerApplicationHost _serverApplicationHost; public AuthorizationContext( - IDbContextFactory jellyfinDb, + IDbContextFactory jellyfinDb, IUserManager userManager, IServerApplicationHost serverApplicationHost) { diff --git a/Jellyfin.Server.Implementations/Users/DisplayPreferencesManager.cs b/Jellyfin.Server.Implementations/Users/DisplayPreferencesManager.cs index 8936f57c62..bfae81e4ca 100644 --- a/Jellyfin.Server.Implementations/Users/DisplayPreferencesManager.cs +++ b/Jellyfin.Server.Implementations/Users/DisplayPreferencesManager.cs @@ -15,13 +15,13 @@ namespace Jellyfin.Server.Implementations.Users /// public class DisplayPreferencesManager : IDisplayPreferencesManager { - private readonly JellyfinDb _dbContext; + private readonly JellyfinDbContext _dbContext; /// /// Initializes a new instance of the class. /// /// The database context factory. - public DisplayPreferencesManager(IDbContextFactory dbContextFactory) + public DisplayPreferencesManager(IDbContextFactory dbContextFactory) { _dbContext = dbContextFactory.CreateDbContext(); } diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 266bff5e8d..dc9d78857e 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -33,7 +33,7 @@ namespace Jellyfin.Server.Implementations.Users /// public class UserManager : IUserManager { - private readonly IDbContextFactory _dbProvider; + private readonly IDbContextFactory _dbProvider; private readonly IEventManager _eventManager; private readonly ICryptoProvider _cryptoProvider; private readonly INetworkManager _networkManager; @@ -59,7 +59,7 @@ namespace Jellyfin.Server.Implementations.Users /// The image processor. /// The logger. public UserManager( - IDbContextFactory dbProvider, + IDbContextFactory dbProvider, IEventManager eventManager, ICryptoProvider cryptoProvider, INetworkManager networkManager, @@ -172,7 +172,7 @@ namespace Jellyfin.Server.Implementations.Users } } - internal async Task CreateUserInternalAsync(string name, JellyfinDb dbContext) + internal async Task CreateUserInternalAsync(string name, JellyfinDbContext dbContext) { // TODO: Remove after user item data is migrated. var max = await dbContext.Users.AsQueryable().AnyAsync().ConfigureAwait(false) @@ -886,7 +886,7 @@ namespace Jellyfin.Server.Implementations.Users await UpdateUserAsync(user).ConfigureAwait(false); } - private async Task UpdateUserInternalAsync(JellyfinDb dbContext, User user) + private async Task UpdateUserInternalAsync(JellyfinDbContext dbContext, User user) { dbContext.Users.Update(user); _users[user.Id] = user; diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs index d70b8f3ab7..40cd5a0446 100644 --- a/Jellyfin.Server/CoreAppHost.cs +++ b/Jellyfin.Server/CoreAppHost.cs @@ -107,7 +107,7 @@ namespace Jellyfin.Server yield return typeof(CoreAppHost).Assembly; // Jellyfin.Server.Implementations - yield return typeof(JellyfinDb).Assembly; + yield return typeof(JellyfinDbContext).Assembly; } /// diff --git a/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs index bf66f75ff9..e8a0af9f88 100644 --- a/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs +++ b/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs @@ -19,7 +19,7 @@ namespace Jellyfin.Server.Migrations.Routines private const string DbFilename = "activitylog.db"; private readonly ILogger _logger; - private readonly IDbContextFactory _provider; + private readonly IDbContextFactory _provider; private readonly IServerApplicationPaths _paths; /// @@ -28,7 +28,7 @@ namespace Jellyfin.Server.Migrations.Routines /// The logger. /// The server application paths. /// The database provider. - public MigrateActivityLogDb(ILogger logger, IServerApplicationPaths paths, IDbContextFactory provider) + public MigrateActivityLogDb(ILogger logger, IServerApplicationPaths paths, IDbContextFactory provider) { _logger = logger; _provider = provider; diff --git a/Jellyfin.Server/Migrations/Routines/MigrateAuthenticationDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateAuthenticationDb.cs index bf1ea8233d..09daae0ff9 100644 --- a/Jellyfin.Server/Migrations/Routines/MigrateAuthenticationDb.cs +++ b/Jellyfin.Server/Migrations/Routines/MigrateAuthenticationDb.cs @@ -20,7 +20,7 @@ namespace Jellyfin.Server.Migrations.Routines private const string DbFilename = "authentication.db"; private readonly ILogger _logger; - private readonly IDbContextFactory _dbProvider; + private readonly IDbContextFactory _dbProvider; private readonly IServerApplicationPaths _appPaths; private readonly IUserManager _userManager; @@ -33,7 +33,7 @@ namespace Jellyfin.Server.Migrations.Routines /// The user manager. public MigrateAuthenticationDb( ILogger logger, - IDbContextFactory dbProvider, + IDbContextFactory dbProvider, IServerApplicationPaths appPaths, IUserManager userManager) { diff --git a/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs index 0fad77cfe6..4b692d14f0 100644 --- a/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs +++ b/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs @@ -25,7 +25,7 @@ namespace Jellyfin.Server.Migrations.Routines private readonly ILogger _logger; private readonly IServerApplicationPaths _paths; - private readonly IDbContextFactory _provider; + private readonly IDbContextFactory _provider; private readonly JsonSerializerOptions _jsonOptions; private readonly IUserManager _userManager; @@ -39,7 +39,7 @@ namespace Jellyfin.Server.Migrations.Routines public MigrateDisplayPreferencesDb( ILogger logger, IServerApplicationPaths paths, - IDbContextFactory provider, + IDbContextFactory provider, IUserManager userManager) { _logger = logger; diff --git a/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs index 2dbd82e8fd..ea2f033027 100644 --- a/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs +++ b/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs @@ -27,7 +27,7 @@ namespace Jellyfin.Server.Migrations.Routines private readonly ILogger _logger; private readonly IServerApplicationPaths _paths; - private readonly IDbContextFactory _provider; + private readonly IDbContextFactory _provider; private readonly IXmlSerializer _xmlSerializer; /// @@ -40,7 +40,7 @@ namespace Jellyfin.Server.Migrations.Routines public MigrateUserDb( ILogger logger, IServerApplicationPaths paths, - IDbContextFactory provider, + IDbContextFactory provider, IXmlSerializer xmlSerializer) { _logger = logger; diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 540375dce6..70d7a07011 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -236,7 +236,7 @@ namespace Jellyfin.Server { _logger.LogInformation("Running query planner optimizations in the database... This might take a while"); // Run before disposing the application - var context = await appHost.ServiceProvider.GetRequiredService>().CreateDbContextAsync().ConfigureAwait(false); + var context = await appHost.ServiceProvider.GetRequiredService>().CreateDbContextAsync().ConfigureAwait(false); await using (context.ConfigureAwait(false)) { if (context.Database.IsSqlite()) diff --git a/Jellyfin.Server/Startup.cs b/Jellyfin.Server/Startup.cs index f89f81c766..c5f20b205b 100644 --- a/Jellyfin.Server/Startup.cs +++ b/Jellyfin.Server/Startup.cs @@ -119,7 +119,7 @@ namespace Jellyfin.Server .ConfigurePrimaryHttpMessageHandler(defaultHttpClientHandlerDelegate); services.AddHealthChecks() - .AddCheck>(nameof(JellyfinDb)); + .AddCheck>(nameof(JellyfinDbContext)); services.AddHlsPlaylistGenerator(); } -- cgit v1.2.3 From 8e5bd360645c4cd183d56feeee43a5f511cab36a Mon Sep 17 00:00:00 2001 From: DarrenRuane Date: Mon, 16 Jan 2023 18:34:15 +0000 Subject: Ensure existing chapter images are deleted when user disables chapter generation --- .../MediaEncoder/EncodingManager.cs | 24 ++++++++++++---------- 1 file changed, 13 insertions(+), 11 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs b/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs index 9fe51f083f..8103e22649 100644 --- a/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs +++ b/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs @@ -15,6 +15,7 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; @@ -62,23 +63,16 @@ namespace Emby.Server.Implementations.MediaEncoder /// Determines whether [is eligible for chapter image extraction] [the specified video]. /// /// The video. + /// /// true if [is eligible for chapter image extraction] [the specified video]; otherwise, false. - private bool IsEligibleForChapterImageExtraction(Video video) + private bool IsEligibleForChapterImageExtraction(Video video, LibraryOptions libraryOptions) { if (video.IsPlaceHolder) { return false; } - var libraryOptions = _libraryManager.GetLibraryOptions(video); - if (libraryOptions is not null) - { - if (!libraryOptions.EnableChapterImageExtraction) - { - return false; - } - } - else + if (libraryOptions is null || !libraryOptions.EnableChapterImageExtraction) { return false; } @@ -99,7 +93,9 @@ namespace Emby.Server.Implementations.MediaEncoder public async Task RefreshChapterImages(Video video, IDirectoryService directoryService, IReadOnlyList chapters, bool extractImages, bool saveChapters, CancellationToken cancellationToken) { - if (!IsEligibleForChapterImageExtraction(video)) + var libraryOptions = _libraryManager.GetLibraryOptions(video); + + if (!IsEligibleForChapterImageExtraction(video, libraryOptions)) { extractImages = false; } @@ -179,6 +175,12 @@ namespace Emby.Server.Implementations.MediaEncoder chapter.ImageDateModified = _fileSystem.GetLastWriteTimeUtc(path); changesMade = true; } + else if (libraryOptions?.EnableChapterImageExtraction != true) + { + // We have an image for the current chapter but the user has disabled chapter image extraction -> delete this chapter's image + chapter.ImagePath = null; + changesMade = true; + } } if (saveChapters && changesMade) -- cgit v1.2.3 From cf8db70af8ed407ce35983fd74c1f7a6b34b5003 Mon Sep 17 00:00:00 2001 From: DarrenRuane Date: Mon, 16 Jan 2023 20:06:56 +0000 Subject: Fix build failing --- Emby.Server.Implementations/MediaEncoder/EncodingManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs b/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs index 8103e22649..7732e32d0a 100644 --- a/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs +++ b/Emby.Server.Implementations/MediaEncoder/EncodingManager.cs @@ -63,7 +63,7 @@ namespace Emby.Server.Implementations.MediaEncoder /// Determines whether [is eligible for chapter image extraction] [the specified video]. /// /// The video. - /// + /// The library options for the video. /// true if [is eligible for chapter image extraction] [the specified video]; otherwise, false. private bool IsEligibleForChapterImageExtraction(Video video, LibraryOptions libraryOptions) { -- cgit v1.2.3 From bf055a2f990d90272915bd7d6732bcaa09ac4fce Mon Sep 17 00:00:00 2001 From: Slug-Cat Date: Mon, 16 Jan 2023 06:57:34 +0000 Subject: Translated using Weblate (Pirate (pr)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/pr/ --- Emby.Server.Implementations/Localization/Core/pr.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/pr.json b/Emby.Server.Implementations/Localization/Core/pr.json index 506c14fdc2..466c8a9905 100644 --- a/Emby.Server.Implementations/Localization/Core/pr.json +++ b/Emby.Server.Implementations/Localization/Core/pr.json @@ -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}" } -- cgit v1.2.3 From c59f2a3c46dd3fad42dd987e04e48d6576373b13 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Tue, 17 Jan 2023 14:41:10 -0500 Subject: Mark CanSelfRestart as Obsolete --- Emby.Server.Implementations/ApplicationHost.cs | 1 - MediaBrowser.Model/System/SystemInfo.cs | 5 +++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 7b40f530c9..3db48e42f2 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -1038,7 +1038,6 @@ namespace Emby.Server.Implementations CachePath = ApplicationPaths.CachePath, OperatingSystem = MediaBrowser.Common.System.OperatingSystem.Id.ToString(), OperatingSystemDisplayName = MediaBrowser.Common.System.OperatingSystem.Name, - CanSelfRestart = true, CanLaunchWebBrowser = CanLaunchWebBrowser, TranscodingTempPath = ConfigurationManager.GetTranscodePath(), ServerName = FriendlyName, diff --git a/MediaBrowser.Model/System/SystemInfo.cs b/MediaBrowser.Model/System/SystemInfo.cs index a82c1c8c0c..9e56849c7c 100644 --- a/MediaBrowser.Model/System/SystemInfo.cs +++ b/MediaBrowser.Model/System/SystemInfo.cs @@ -79,8 +79,9 @@ namespace MediaBrowser.Model.System /// /// Gets or sets a value indicating whether this instance can self restart. /// - /// true if this instance can self restart; otherwise, false. - public bool CanSelfRestart { get; set; } + /// true. + [Obsolete("This is always true")] + public bool CanSelfRestart { get; set; } = true; public bool CanLaunchWebBrowser { get; set; } -- cgit v1.2.3 From 79a7815be7ffaeb0c93cc38fe454b5a0bc5df3c2 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Tue, 17 Jan 2023 18:49:00 -0500 Subject: Use one AssemblyLoadContext per plugin --- Emby.Server.Implementations/Plugins/PluginManager.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Plugins/PluginManager.cs b/Emby.Server.Implementations/Plugins/PluginManager.cs index f2212f4dcb..15ed081572 100644 --- a/Emby.Server.Implementations/Plugins/PluginManager.cs +++ b/Emby.Server.Implementations/Plugins/PluginManager.cs @@ -123,14 +123,14 @@ namespace Emby.Server.Implementations.Plugins continue; } + var assemblyLoadContext = new PluginLoadContext(plugin.Path); + _assemblyLoadContexts.Add(assemblyLoadContext); + foreach (var file in plugin.DllFiles) { Assembly assembly; try { - var assemblyLoadContext = new PluginLoadContext(file); - _assemblyLoadContexts.Add(assemblyLoadContext); - assembly = assemblyLoadContext.LoadFromAssemblyPath(file); // Load all required types to verify that the plugin will load -- cgit v1.2.3 From 971e338b71afe0675934700bd4ac3494f22e6afa Mon Sep 17 00:00:00 2001 From: SuperDumbTM Date: Tue, 17 Jan 2023 09:16:59 +0000 Subject: Translated using Weblate (Chinese (Traditional, Hong Kong)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/zh_Hant_HK/ --- Emby.Server.Implementations/Localization/Core/zh-HK.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/zh-HK.json b/Emby.Server.Implementations/Localization/Core/zh-HK.json index baa9ecc1c1..cdc25ec7c7 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-HK.json +++ b/Emby.Server.Implementations/Localization/Core/zh-HK.json @@ -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": "已添加新内容", -- cgit v1.2.3 From e408da46518e98f0c46c794e75085a8c9417ab00 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 18 Jan 2023 04:16:59 -0700 Subject: chore(deps): update dependency microsoft.codeanalysis.bannedapianalyzers to v3.3.4 (#9117) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Emby.Dlna/Emby.Dlna.csproj | 2 +- Emby.Naming/Emby.Naming.csproj | 2 +- Emby.Notifications/Emby.Notifications.csproj | 2 +- Emby.Photos/Emby.Photos.csproj | 2 +- Emby.Server.Implementations/Emby.Server.Implementations.csproj | 2 +- Jellyfin.Api/Jellyfin.Api.csproj | 2 +- Jellyfin.Data/Jellyfin.Data.csproj | 2 +- Jellyfin.Networking/Jellyfin.Networking.csproj | 2 +- Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj | 2 +- Jellyfin.Server/Jellyfin.Server.csproj | 2 +- MediaBrowser.Common/MediaBrowser.Common.csproj | 2 +- MediaBrowser.Controller/MediaBrowser.Controller.csproj | 2 +- MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj | 2 +- MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj | 2 +- MediaBrowser.Model/MediaBrowser.Model.csproj | 2 +- MediaBrowser.Providers/MediaBrowser.Providers.csproj | 2 +- MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj | 2 +- src/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj | 2 +- src/Jellyfin.Drawing/Jellyfin.Drawing.csproj | 2 +- src/Jellyfin.Extensions/Jellyfin.Extensions.csproj | 2 +- src/Jellyfin.MediaEncoding.Hls/Jellyfin.MediaEncoding.Hls.csproj | 2 +- .../Jellyfin.MediaEncoding.Keyframes.csproj | 2 +- tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj | 2 +- tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj | 2 +- tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj | 2 +- tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj | 2 +- tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj | 2 +- .../Jellyfin.MediaEncoding.Hls.Tests.csproj | 2 +- .../Jellyfin.MediaEncoding.Keyframes.Tests.csproj | 2 +- tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj | 2 +- tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj | 2 +- tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj | 2 +- tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj | 2 +- tests/Jellyfin.Providers.Tests/Jellyfin.Providers.Tests.csproj | 2 +- .../Jellyfin.Server.Implementations.Tests.csproj | 2 +- .../Jellyfin.Server.Integration.Tests.csproj | 2 +- tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj | 2 +- tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj | 2 +- 38 files changed, 38 insertions(+), 38 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Dlna/Emby.Dlna.csproj b/Emby.Dlna/Emby.Dlna.csproj index 7ffb7118aa..60e6dd644d 100644 --- a/Emby.Dlna/Emby.Dlna.csproj +++ b/Emby.Dlna/Emby.Dlna.csproj @@ -28,7 +28,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/Emby.Naming/Emby.Naming.csproj b/Emby.Naming/Emby.Naming.csproj index 80bc57a5d6..3106e22465 100644 --- a/Emby.Naming/Emby.Naming.csproj +++ b/Emby.Naming/Emby.Naming.csproj @@ -47,7 +47,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/Emby.Notifications/Emby.Notifications.csproj b/Emby.Notifications/Emby.Notifications.csproj index 138965c89f..eb269183e9 100644 --- a/Emby.Notifications/Emby.Notifications.csproj +++ b/Emby.Notifications/Emby.Notifications.csproj @@ -23,7 +23,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/Emby.Photos/Emby.Photos.csproj b/Emby.Photos/Emby.Photos.csproj index 34bc8f32f6..ae6bc2db1f 100644 --- a/Emby.Photos/Emby.Photos.csproj +++ b/Emby.Photos/Emby.Photos.csproj @@ -26,7 +26,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 7accc3b8ba..7eaef094b5 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -54,7 +54,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj index b5444138fb..45725ec3e6 100644 --- a/Jellyfin.Api/Jellyfin.Api.csproj +++ b/Jellyfin.Api/Jellyfin.Api.csproj @@ -27,7 +27,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/Jellyfin.Data/Jellyfin.Data.csproj b/Jellyfin.Data/Jellyfin.Data.csproj index 7fe6466d4a..540534e1ba 100644 --- a/Jellyfin.Data/Jellyfin.Data.csproj +++ b/Jellyfin.Data/Jellyfin.Data.csproj @@ -29,7 +29,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/Jellyfin.Networking/Jellyfin.Networking.csproj b/Jellyfin.Networking/Jellyfin.Networking.csproj index 975d1c8ce2..2c153d88b5 100644 --- a/Jellyfin.Networking/Jellyfin.Networking.csproj +++ b/Jellyfin.Networking/Jellyfin.Networking.csproj @@ -11,7 +11,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj index bc437c5d77..b078db0169 100644 --- a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj +++ b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj @@ -8,7 +8,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index 829f294ce3..9ea8508f24 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -24,7 +24,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj index 0296974b54..1b0ff27d98 100644 --- a/MediaBrowser.Common/MediaBrowser.Common.csproj +++ b/MediaBrowser.Common/MediaBrowser.Common.csproj @@ -49,7 +49,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 6434621c46..20909c9d57 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -51,7 +51,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj b/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj index de3987b1e2..039127f9e3 100644 --- a/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj +++ b/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj @@ -22,7 +22,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj index e33cfc7a12..1233fb1108 100644 --- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj +++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj @@ -31,7 +31,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index 284e89f1cb..521ba0f107 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -49,7 +49,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 97ad1ffbcb..13de86a929 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -36,7 +36,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj b/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj index 6e82d96d10..c25932a5a1 100644 --- a/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj +++ b/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj @@ -22,7 +22,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/src/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj b/src/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj index c686b229a6..a62ebf78c7 100644 --- a/src/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj +++ b/src/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj @@ -31,7 +31,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/src/Jellyfin.Drawing/Jellyfin.Drawing.csproj b/src/Jellyfin.Drawing/Jellyfin.Drawing.csproj index a5bc8eaa7e..7aa9945033 100644 --- a/src/Jellyfin.Drawing/Jellyfin.Drawing.csproj +++ b/src/Jellyfin.Drawing/Jellyfin.Drawing.csproj @@ -23,7 +23,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/src/Jellyfin.Extensions/Jellyfin.Extensions.csproj b/src/Jellyfin.Extensions/Jellyfin.Extensions.csproj index 9fed8cbd9a..d7c05ea576 100644 --- a/src/Jellyfin.Extensions/Jellyfin.Extensions.csproj +++ b/src/Jellyfin.Extensions/Jellyfin.Extensions.csproj @@ -34,7 +34,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/src/Jellyfin.MediaEncoding.Hls/Jellyfin.MediaEncoding.Hls.csproj b/src/Jellyfin.MediaEncoding.Hls/Jellyfin.MediaEncoding.Hls.csproj index 32f80812ab..9a025d5586 100644 --- a/src/Jellyfin.MediaEncoding.Hls/Jellyfin.MediaEncoding.Hls.csproj +++ b/src/Jellyfin.MediaEncoding.Hls/Jellyfin.MediaEncoding.Hls.csproj @@ -7,7 +7,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/src/Jellyfin.MediaEncoding.Keyframes/Jellyfin.MediaEncoding.Keyframes.csproj b/src/Jellyfin.MediaEncoding.Keyframes/Jellyfin.MediaEncoding.Keyframes.csproj index b11bdc4779..fe4e576937 100644 --- a/src/Jellyfin.MediaEncoding.Keyframes/Jellyfin.MediaEncoding.Keyframes.csproj +++ b/src/Jellyfin.MediaEncoding.Keyframes/Jellyfin.MediaEncoding.Keyframes.csproj @@ -11,7 +11,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj index c0e0d2b6b3..6966d81d46 100644 --- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj +++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj @@ -29,7 +29,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj index c74127f044..5110d59176 100644 --- a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj +++ b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj @@ -24,7 +24,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj b/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj index 1ddf5139c9..97350fedad 100644 --- a/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj +++ b/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj @@ -24,7 +24,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj b/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj index dc4b58fecf..a2ecd60838 100644 --- a/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj +++ b/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj @@ -19,7 +19,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj b/tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj index 16b18cc85e..313192b241 100644 --- a/tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj +++ b/tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj @@ -22,7 +22,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/tests/Jellyfin.MediaEncoding.Hls.Tests/Jellyfin.MediaEncoding.Hls.Tests.csproj b/tests/Jellyfin.MediaEncoding.Hls.Tests/Jellyfin.MediaEncoding.Hls.Tests.csproj index c20f3dd999..22b0c417b0 100644 --- a/tests/Jellyfin.MediaEncoding.Hls.Tests/Jellyfin.MediaEncoding.Hls.Tests.csproj +++ b/tests/Jellyfin.MediaEncoding.Hls.Tests/Jellyfin.MediaEncoding.Hls.Tests.csproj @@ -21,7 +21,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/tests/Jellyfin.MediaEncoding.Keyframes.Tests/Jellyfin.MediaEncoding.Keyframes.Tests.csproj b/tests/Jellyfin.MediaEncoding.Keyframes.Tests/Jellyfin.MediaEncoding.Keyframes.Tests.csproj index 5cfad93a6a..373a54504e 100644 --- a/tests/Jellyfin.MediaEncoding.Keyframes.Tests/Jellyfin.MediaEncoding.Keyframes.Tests.csproj +++ b/tests/Jellyfin.MediaEncoding.Keyframes.Tests/Jellyfin.MediaEncoding.Keyframes.Tests.csproj @@ -22,7 +22,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj b/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj index f824b6f3b2..a9a0dbc226 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj +++ b/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj @@ -33,7 +33,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj b/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj index b6578a7f11..9858623f82 100644 --- a/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj +++ b/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj @@ -26,7 +26,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj b/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj index f10f9159dd..920f490ed0 100644 --- a/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj +++ b/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj @@ -28,7 +28,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj b/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj index 3a39daa364..74bf7cb0e0 100644 --- a/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj +++ b/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj @@ -25,7 +25,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/tests/Jellyfin.Providers.Tests/Jellyfin.Providers.Tests.csproj b/tests/Jellyfin.Providers.Tests/Jellyfin.Providers.Tests.csproj index 6cc998d274..d3292c38eb 100644 --- a/tests/Jellyfin.Providers.Tests/Jellyfin.Providers.Tests.csproj +++ b/tests/Jellyfin.Providers.Tests/Jellyfin.Providers.Tests.csproj @@ -28,7 +28,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj index 82628d7339..b796e07d1a 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj +++ b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj @@ -34,7 +34,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj index 006b38a11d..c40f6942b1 100644 --- a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj +++ b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj @@ -31,7 +31,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj index 771fad6357..a72a6f1855 100644 --- a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj +++ b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj @@ -24,7 +24,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj b/tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj index 0d69c3f611..dc5b5b9e6b 100644 --- a/tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj +++ b/tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj @@ -25,7 +25,7 @@ - + all runtime; build; native; contentfiles; analyzers -- cgit v1.2.3 From 8cabac0cf24e9b94f95e6118ef994c9346df7efc Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Wed, 18 Jan 2023 10:26:39 -0500 Subject: Load all plugin assemblies before attempting to load types --- .../Plugins/PluginManager.cs | 47 ++++++++++++++++------ 1 file changed, 35 insertions(+), 12 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Plugins/PluginManager.cs b/Emby.Server.Implementations/Plugins/PluginManager.cs index 15ed081572..7c23254a12 100644 --- a/Emby.Server.Implementations/Plugins/PluginManager.cs +++ b/Emby.Server.Implementations/Plugins/PluginManager.cs @@ -126,38 +126,61 @@ namespace Emby.Server.Implementations.Plugins var assemblyLoadContext = new PluginLoadContext(plugin.Path); _assemblyLoadContexts.Add(assemblyLoadContext); + var assemblies = new List(plugin.DllFiles.Count); + var loadedAll = true; + foreach (var file in plugin.DllFiles) { - Assembly assembly; try { - assembly = assemblyLoadContext.LoadFromAssemblyPath(file); - - // Load all required types to verify that the plugin will load - assembly.GetTypes(); + assemblies.Add(assemblyLoadContext.LoadFromAssemblyPath(file)); } catch (FileLoadException ex) { - _logger.LogError(ex, "Failed to load assembly {Path}. Disabling plugin.", file); + _logger.LogError(ex, "Failed to load assembly {Path}. Disabling plugin", file); ChangePluginState(plugin, PluginStatus.Malfunctioned); - continue; + loadedAll = false; + break; + } +#pragma warning disable CA1031 // Do not catch general exception types + catch (Exception ex) +#pragma warning restore CA1031 // Do not catch general exception types + { + _logger.LogError(ex, "Failed to load assembly {Path}. Unknown exception was thrown. Disabling plugin", file); + ChangePluginState(plugin, PluginStatus.Malfunctioned); + loadedAll = false; + break; + } + } + + if (!loadedAll) + { + continue; + } + + foreach (var assembly in assemblies) + { + try + { + // Load all required types to verify that the plugin will load + assembly.GetTypes(); } catch (SystemException ex) when (ex is TypeLoadException or ReflectionTypeLoadException) // Undocumented exception { - _logger.LogError(ex, "Failed to load assembly {Path}. This error occurs when a plugin references an incompatible version of one of the shared libraries. Disabling plugin.", file); + _logger.LogError(ex, "Failed to load assembly {Path}. This error occurs when a plugin references an incompatible version of one of the shared libraries. Disabling plugin", assembly.Location); ChangePluginState(plugin, PluginStatus.NotSupported); - continue; + break; } #pragma warning disable CA1031 // Do not catch general exception types catch (Exception ex) #pragma warning restore CA1031 // Do not catch general exception types { - _logger.LogError(ex, "Failed to load assembly {Path}. Unknown exception was thrown. Disabling plugin.", file); + _logger.LogError(ex, "Failed to load assembly {Path}. Unknown exception was thrown. Disabling plugin", assembly.Location); ChangePluginState(plugin, PluginStatus.Malfunctioned); - continue; + break; } - _logger.LogInformation("Loaded assembly {Assembly} from {Path}", assembly.FullName, file); + _logger.LogInformation("Loaded assembly {Assembly} from {Path}", assembly.FullName, assembly.Location); yield return assembly; } } -- cgit v1.2.3 From c3a6b6c69d2db38e1d02fb922621eb7a925774f5 Mon Sep 17 00:00:00 2001 From: marcus2799 Date: Wed, 18 Jan 2023 19:46:30 +0000 Subject: Translated using Weblate (Bulgarian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/bg/ --- Emby.Server.Implementations/Localization/Core/bg-BG.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/bg-BG.json b/Emby.Server.Implementations/Localization/Core/bg-BG.json index 64cb36fd82..13b99cc997 100644 --- a/Emby.Server.Implementations/Localization/Core/bg-BG.json +++ b/Emby.Server.Implementations/Localization/Core/bg-BG.json @@ -123,5 +123,6 @@ "TaskOptimizeDatabase": "Оптимизирай базата данни", "TaskKeyframeExtractorDescription": "Извличат се ключови кадри от видеофайловете ,за да се създаде по точен ХЛС списък . Задачата може да отнеме много време.", "TaskKeyframeExtractor": "Извличане на ключови кадри", - "External": "Външен" + "External": "Външен", + "HearingImpaired": "Увреден слух" } -- cgit v1.2.3 From db1913b08fac0749133634efebd1ee7a7876147a Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Fri, 20 Jan 2023 07:29:45 -0500 Subject: Remove DvdLib (#9068) * Remove DvdLib * Update error message for blu-ray folders Co-authored-by: Shadowghost * Remove BDInfo * Remove MediaEncoder.GetPrimaryPlaylistVobFiles * Remove BlurayDiscInfo Co-authored-by: Shadowghost --- DvdLib/BigEndianBinaryReader.cs | 25 --- DvdLib/DvdLib.csproj | 20 --- DvdLib/Ifo/Cell.cs | 23 --- DvdLib/Ifo/CellPlaybackInfo.cs | 52 ------ DvdLib/Ifo/CellPositionInfo.cs | 19 -- DvdLib/Ifo/Chapter.cs | 20 --- DvdLib/Ifo/Dvd.cs | 167 ------------------ DvdLib/Ifo/DvdTime.cs | 39 ----- DvdLib/Ifo/Program.cs | 16 -- DvdLib/Ifo/ProgramChain.cs | 121 ------------- DvdLib/Ifo/Title.cs | 70 -------- DvdLib/Ifo/UserOperation.cs | 37 ---- DvdLib/Properties/AssemblyInfo.cs | 21 --- Emby.Server.Implementations/ApplicationHost.cs | 4 - Jellyfin.sln | 6 - .../MediaEncoding/IMediaEncoder.cs | 8 - .../BdInfo/BdInfoDirectoryInfo.cs | 83 --------- .../BdInfo/BdInfoExaminer.cs | 194 --------------------- .../BdInfo/BdInfoFileInfo.cs | 41 ----- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 79 --------- .../MediaBrowser.MediaEncoding.csproj | 1 - MediaBrowser.Model/MediaInfo/BlurayDiscInfo.cs | 39 ----- MediaBrowser.Model/MediaInfo/IBlurayExaminer.cs | 15 -- .../MediaBrowser.Providers.csproj | 1 - .../MediaInfo/FFProbeVideoInfo.cs | 155 +--------------- MediaBrowser.Providers/MediaInfo/ProbeProvider.cs | 3 - 26 files changed, 1 insertion(+), 1258 deletions(-) delete mode 100644 DvdLib/BigEndianBinaryReader.cs delete mode 100644 DvdLib/DvdLib.csproj delete mode 100644 DvdLib/Ifo/Cell.cs delete mode 100644 DvdLib/Ifo/CellPlaybackInfo.cs delete mode 100644 DvdLib/Ifo/CellPositionInfo.cs delete mode 100644 DvdLib/Ifo/Chapter.cs delete mode 100644 DvdLib/Ifo/Dvd.cs delete mode 100644 DvdLib/Ifo/DvdTime.cs delete mode 100644 DvdLib/Ifo/Program.cs delete mode 100644 DvdLib/Ifo/ProgramChain.cs delete mode 100644 DvdLib/Ifo/Title.cs delete mode 100644 DvdLib/Ifo/UserOperation.cs delete mode 100644 DvdLib/Properties/AssemblyInfo.cs delete mode 100644 MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs delete mode 100644 MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs delete mode 100644 MediaBrowser.MediaEncoding/BdInfo/BdInfoFileInfo.cs delete mode 100644 MediaBrowser.Model/MediaInfo/BlurayDiscInfo.cs delete mode 100644 MediaBrowser.Model/MediaInfo/IBlurayExaminer.cs (limited to 'Emby.Server.Implementations') diff --git a/DvdLib/BigEndianBinaryReader.cs b/DvdLib/BigEndianBinaryReader.cs deleted file mode 100644 index b3aad85cec..0000000000 --- a/DvdLib/BigEndianBinaryReader.cs +++ /dev/null @@ -1,25 +0,0 @@ -#pragma warning disable CS1591 - -using System.Buffers.Binary; -using System.IO; - -namespace DvdLib -{ - public class BigEndianBinaryReader : BinaryReader - { - public BigEndianBinaryReader(Stream input) - : base(input) - { - } - - public override ushort ReadUInt16() - { - return BinaryPrimitives.ReadUInt16BigEndian(base.ReadBytes(2)); - } - - public override uint ReadUInt32() - { - return BinaryPrimitives.ReadUInt32BigEndian(base.ReadBytes(4)); - } - } -} diff --git a/DvdLib/DvdLib.csproj b/DvdLib/DvdLib.csproj deleted file mode 100644 index 1053c0089f..0000000000 --- a/DvdLib/DvdLib.csproj +++ /dev/null @@ -1,20 +0,0 @@ - - - - - {713F42B5-878E-499D-A878-E4C652B1D5E8} - - - - - - - - net7.0 - false - true - AllDisabledByDefault - disable - - - diff --git a/DvdLib/Ifo/Cell.cs b/DvdLib/Ifo/Cell.cs deleted file mode 100644 index ea0b50e430..0000000000 --- a/DvdLib/Ifo/Cell.cs +++ /dev/null @@ -1,23 +0,0 @@ -#pragma warning disable CS1591 - -using System.IO; - -namespace DvdLib.Ifo -{ - public class Cell - { - public CellPlaybackInfo PlaybackInfo { get; private set; } - - public CellPositionInfo PositionInfo { get; private set; } - - internal void ParsePlayback(BinaryReader br) - { - PlaybackInfo = new CellPlaybackInfo(br); - } - - internal void ParsePosition(BinaryReader br) - { - PositionInfo = new CellPositionInfo(br); - } - } -} diff --git a/DvdLib/Ifo/CellPlaybackInfo.cs b/DvdLib/Ifo/CellPlaybackInfo.cs deleted file mode 100644 index 6e33a0ec5a..0000000000 --- a/DvdLib/Ifo/CellPlaybackInfo.cs +++ /dev/null @@ -1,52 +0,0 @@ -#pragma warning disable CS1591 - -using System.IO; - -namespace DvdLib.Ifo -{ - public enum BlockMode - { - NotInBlock = 0, - FirstCell = 1, - InBlock = 2, - LastCell = 3, - } - - public enum BlockType - { - Normal = 0, - Angle = 1, - } - - public enum PlaybackMode - { - Normal = 0, - StillAfterEachVOBU = 1, - } - - public class CellPlaybackInfo - { - public readonly BlockMode Mode; - public readonly BlockType Type; - public readonly bool SeamlessPlay; - public readonly bool Interleaved; - public readonly bool STCDiscontinuity; - public readonly bool SeamlessAngle; - public readonly PlaybackMode PlaybackMode; - public readonly bool Restricted; - public readonly byte StillTime; - public readonly byte CommandNumber; - public readonly DvdTime PlaybackTime; - public readonly uint FirstSector; - public readonly uint FirstILVUEndSector; - public readonly uint LastVOBUStartSector; - public readonly uint LastSector; - - internal CellPlaybackInfo(BinaryReader br) - { - br.BaseStream.Seek(0x4, SeekOrigin.Current); - PlaybackTime = new DvdTime(br.ReadBytes(4)); - br.BaseStream.Seek(0x10, SeekOrigin.Current); - } - } -} diff --git a/DvdLib/Ifo/CellPositionInfo.cs b/DvdLib/Ifo/CellPositionInfo.cs deleted file mode 100644 index 216aa0f77a..0000000000 --- a/DvdLib/Ifo/CellPositionInfo.cs +++ /dev/null @@ -1,19 +0,0 @@ -#pragma warning disable CS1591 - -using System.IO; - -namespace DvdLib.Ifo -{ - public class CellPositionInfo - { - public readonly ushort VOBId; - public readonly byte CellId; - - internal CellPositionInfo(BinaryReader br) - { - VOBId = br.ReadUInt16(); - br.ReadByte(); - CellId = br.ReadByte(); - } - } -} diff --git a/DvdLib/Ifo/Chapter.cs b/DvdLib/Ifo/Chapter.cs deleted file mode 100644 index e786cb5536..0000000000 --- a/DvdLib/Ifo/Chapter.cs +++ /dev/null @@ -1,20 +0,0 @@ -#pragma warning disable CS1591 - -namespace DvdLib.Ifo -{ - public class Chapter - { - public ushort ProgramChainNumber { get; private set; } - - public ushort ProgramNumber { get; private set; } - - public uint ChapterNumber { get; private set; } - - public Chapter(ushort pgcNum, ushort programNum, uint chapterNum) - { - ProgramChainNumber = pgcNum; - ProgramNumber = programNum; - ChapterNumber = chapterNum; - } - } -} diff --git a/DvdLib/Ifo/Dvd.cs b/DvdLib/Ifo/Dvd.cs deleted file mode 100644 index 7f8ece47dc..0000000000 --- a/DvdLib/Ifo/Dvd.cs +++ /dev/null @@ -1,167 +0,0 @@ -#pragma warning disable CS1591 - -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; - -namespace DvdLib.Ifo -{ - public class Dvd - { - private readonly ushort _titleSetCount; - public readonly List Titles; - - private ushort _titleCount; - public readonly Dictionary<ushort, string> VTSPaths = new Dictionary<ushort, string>(); - public Dvd(string path) - { - Titles = new List<Title>(); - var allFiles = new DirectoryInfo(path).GetFiles(path, SearchOption.AllDirectories); - - var vmgPath = allFiles.FirstOrDefault(i => string.Equals(i.Name, "VIDEO_TS.IFO", StringComparison.OrdinalIgnoreCase)) ?? - allFiles.FirstOrDefault(i => string.Equals(i.Name, "VIDEO_TS.BUP", StringComparison.OrdinalIgnoreCase)); - - if (vmgPath == null) - { - foreach (var ifo in allFiles) - { - if (!string.Equals(ifo.Extension, ".ifo", StringComparison.OrdinalIgnoreCase)) - { - continue; - } - - var nums = ifo.Name.Split('_', StringSplitOptions.RemoveEmptyEntries); - if (nums.Length >= 2 && ushort.TryParse(nums[1], out var ifoNumber)) - { - ReadVTS(ifoNumber, ifo.FullName); - } - } - } - else - { - using (var vmgFs = new FileStream(vmgPath.FullName, FileMode.Open, FileAccess.Read, FileShare.Read)) - { - using (var vmgRead = new BigEndianBinaryReader(vmgFs)) - { - vmgFs.Seek(0x3E, SeekOrigin.Begin); - _titleSetCount = vmgRead.ReadUInt16(); - - // read address of TT_SRPT - vmgFs.Seek(0xC4, SeekOrigin.Begin); - uint ttSectorPtr = vmgRead.ReadUInt32(); - vmgFs.Seek(ttSectorPtr * 2048, SeekOrigin.Begin); - ReadTT_SRPT(vmgRead); - } - } - - for (ushort titleSetNum = 1; titleSetNum <= _titleSetCount; titleSetNum++) - { - ReadVTS(titleSetNum, allFiles); - } - } - } - - private void ReadTT_SRPT(BinaryReader read) - { - _titleCount = read.ReadUInt16(); - read.BaseStream.Seek(6, SeekOrigin.Current); - for (uint titleNum = 1; titleNum <= _titleCount; titleNum++) - { - var t = new Title(titleNum); - t.ParseTT_SRPT(read); - Titles.Add(t); - } - } - - private void ReadVTS(ushort vtsNum, IReadOnlyList<FileInfo> allFiles) - { - var filename = string.Format(CultureInfo.InvariantCulture, "VTS_{0:00}_0.IFO", vtsNum); - - var vtsPath = allFiles.FirstOrDefault(i => string.Equals(i.Name, filename, StringComparison.OrdinalIgnoreCase)) ?? - allFiles.FirstOrDefault(i => string.Equals(i.Name, Path.ChangeExtension(filename, ".bup"), StringComparison.OrdinalIgnoreCase)); - - if (vtsPath == null) - { - throw new FileNotFoundException("Unable to find VTS IFO file"); - } - - ReadVTS(vtsNum, vtsPath.FullName); - } - - private void ReadVTS(ushort vtsNum, string vtsPath) - { - VTSPaths[vtsNum] = vtsPath; - - using (var vtsFs = new FileStream(vtsPath, FileMode.Open, FileAccess.Read, FileShare.Read)) - { - using (var vtsRead = new BigEndianBinaryReader(vtsFs)) - { - // Read VTS_PTT_SRPT - vtsFs.Seek(0xC8, SeekOrigin.Begin); - uint vtsPttSrptSecPtr = vtsRead.ReadUInt32(); - uint baseAddr = (vtsPttSrptSecPtr * 2048); - vtsFs.Seek(baseAddr, SeekOrigin.Begin); - - ushort numTitles = vtsRead.ReadUInt16(); - vtsRead.ReadUInt16(); - uint endaddr = vtsRead.ReadUInt32(); - uint[] offsets = new uint[numTitles]; - for (ushort titleNum = 0; titleNum < numTitles; titleNum++) - { - offsets[titleNum] = vtsRead.ReadUInt32(); - } - - for (uint titleNum = 0; titleNum < numTitles; titleNum++) - { - uint chapNum = 1; - vtsFs.Seek(baseAddr + offsets[titleNum], SeekOrigin.Begin); - var t = Titles.FirstOrDefault(vtst => vtst.IsVTSTitle(vtsNum, titleNum + 1)); - if (t == null) - { - continue; - } - - do - { - t.Chapters.Add(new Chapter(vtsRead.ReadUInt16(), vtsRead.ReadUInt16(), chapNum)); - if (titleNum + 1 < numTitles && vtsFs.Position == (baseAddr + offsets[titleNum + 1])) - { - break; - } - - chapNum++; - } - while (vtsFs.Position < (baseAddr + endaddr)); - } - - // Read VTS_PGCI - vtsFs.Seek(0xCC, SeekOrigin.Begin); - uint vtsPgciSecPtr = vtsRead.ReadUInt32(); - vtsFs.Seek(vtsPgciSecPtr * 2048, SeekOrigin.Begin); - - long startByte = vtsFs.Position; - - ushort numPgcs = vtsRead.ReadUInt16(); - vtsFs.Seek(6, SeekOrigin.Current); - for (ushort pgcNum = 1; pgcNum <= numPgcs; pgcNum++) - { - byte pgcCat = vtsRead.ReadByte(); - bool entryPgc = (pgcCat & 0x80) != 0; - uint titleNum = (uint)(pgcCat & 0x7F); - - vtsFs.Seek(3, SeekOrigin.Current); - uint vtsPgcOffset = vtsRead.ReadUInt32(); - - var t = Titles.FirstOrDefault(vtst => vtst.IsVTSTitle(vtsNum, titleNum)); - if (t != null) - { - t.AddPgc(vtsRead, startByte + vtsPgcOffset, entryPgc, pgcNum); - } - } - } - } - } - } -} diff --git a/DvdLib/Ifo/DvdTime.cs b/DvdLib/Ifo/DvdTime.cs deleted file mode 100644 index d231406106..0000000000 --- a/DvdLib/Ifo/DvdTime.cs +++ /dev/null @@ -1,39 +0,0 @@ -#pragma warning disable CS1591 - -using System; - -namespace DvdLib.Ifo -{ - public class DvdTime - { - public readonly byte Hour, Minute, Second, Frames, FrameRate; - - public DvdTime(byte[] data) - { - Hour = GetBCDValue(data[0]); - Minute = GetBCDValue(data[1]); - Second = GetBCDValue(data[2]); - Frames = GetBCDValue((byte)(data[3] & 0x3F)); - - if ((data[3] & 0x80) != 0) - { - FrameRate = 30; - } - else if ((data[3] & 0x40) != 0) - { - FrameRate = 25; - } - } - - private static byte GetBCDValue(byte data) - { - return (byte)((((data & 0xF0) >> 4) * 10) + (data & 0x0F)); - } - - public static explicit operator TimeSpan(DvdTime time) - { - int ms = (int)(((1.0 / (double)time.FrameRate) * time.Frames) * 1000.0); - return new TimeSpan(0, time.Hour, time.Minute, time.Second, ms); - } - } -} diff --git a/DvdLib/Ifo/Program.cs b/DvdLib/Ifo/Program.cs deleted file mode 100644 index 3d94fa7dc1..0000000000 --- a/DvdLib/Ifo/Program.cs +++ /dev/null @@ -1,16 +0,0 @@ -#pragma warning disable CS1591 - -using System.Collections.Generic; - -namespace DvdLib.Ifo -{ - public class Program - { - public IReadOnlyList<Cell> Cells { get; } - - public Program(List<Cell> cells) - { - Cells = cells; - } - } -} diff --git a/DvdLib/Ifo/ProgramChain.cs b/DvdLib/Ifo/ProgramChain.cs deleted file mode 100644 index 83c0051b90..0000000000 --- a/DvdLib/Ifo/ProgramChain.cs +++ /dev/null @@ -1,121 +0,0 @@ -#pragma warning disable CS1591 - -using System.Collections.Generic; -using System.IO; -using System.Linq; - -namespace DvdLib.Ifo -{ - public enum ProgramPlaybackMode - { - Sequential, - Random, - Shuffle - } - - public class ProgramChain - { - private byte _programCount; - public readonly List<Program> Programs; - - private byte _cellCount; - public readonly List<Cell> Cells; - - public DvdTime PlaybackTime { get; private set; } - - public UserOperation ProhibitedUserOperations { get; private set; } - - public byte[] AudioStreamControl { get; private set; } // 8*2 entries - public byte[] SubpictureStreamControl { get; private set; } // 32*4 entries - - private ushort _nextProgramNumber; - - private ushort _prevProgramNumber; - - private ushort _goupProgramNumber; - - public ProgramPlaybackMode PlaybackMode { get; private set; } - - public uint ProgramCount { get; private set; } - - public byte StillTime { get; private set; } - - public byte[] Palette { get; private set; } // 16*4 entries - - private ushort _commandTableOffset; - - private ushort _programMapOffset; - private ushort _cellPlaybackOffset; - private ushort _cellPositionOffset; - - public readonly uint VideoTitleSetIndex; - - internal ProgramChain(uint vtsPgcNum) - { - VideoTitleSetIndex = vtsPgcNum; - Cells = new List<Cell>(); - Programs = new List<Program>(); - } - - internal void ParseHeader(BinaryReader br) - { - long startPos = br.BaseStream.Position; - - br.ReadUInt16(); - _programCount = br.ReadByte(); - _cellCount = br.ReadByte(); - PlaybackTime = new DvdTime(br.ReadBytes(4)); - ProhibitedUserOperations = (UserOperation)br.ReadUInt32(); - AudioStreamControl = br.ReadBytes(16); - SubpictureStreamControl = br.ReadBytes(128); - - _nextProgramNumber = br.ReadUInt16(); - _prevProgramNumber = br.ReadUInt16(); - _goupProgramNumber = br.ReadUInt16(); - - StillTime = br.ReadByte(); - byte pbMode = br.ReadByte(); - if (pbMode == 0) - { - PlaybackMode = ProgramPlaybackMode.Sequential; - } - else - { - PlaybackMode = ((pbMode & 0x80) == 0) ? ProgramPlaybackMode.Random : ProgramPlaybackMode.Shuffle; - } - - ProgramCount = (uint)(pbMode & 0x7F); - - Palette = br.ReadBytes(64); - _commandTableOffset = br.ReadUInt16(); - _programMapOffset = br.ReadUInt16(); - _cellPlaybackOffset = br.ReadUInt16(); - _cellPositionOffset = br.ReadUInt16(); - - // read position info - br.BaseStream.Seek(startPos + _cellPositionOffset, SeekOrigin.Begin); - for (int cellNum = 0; cellNum < _cellCount; cellNum++) - { - var c = new Cell(); - c.ParsePosition(br); - Cells.Add(c); - } - - br.BaseStream.Seek(startPos + _cellPlaybackOffset, SeekOrigin.Begin); - for (int cellNum = 0; cellNum < _cellCount; cellNum++) - { - Cells[cellNum].ParsePlayback(br); - } - - br.BaseStream.Seek(startPos + _programMapOffset, SeekOrigin.Begin); - var cellNumbers = new List<int>(); - for (int progNum = 0; progNum < _programCount; progNum++) cellNumbers.Add(br.ReadByte() - 1); - - for (int i = 0; i < cellNumbers.Count; i++) - { - int max = (i + 1 == cellNumbers.Count) ? _cellCount : cellNumbers[i + 1]; - Programs.Add(new Program(Cells.Where((c, idx) => idx >= cellNumbers[i] && idx < max).ToList())); - } - } - } -} diff --git a/DvdLib/Ifo/Title.cs b/DvdLib/Ifo/Title.cs deleted file mode 100644 index 29a0b95c72..0000000000 --- a/DvdLib/Ifo/Title.cs +++ /dev/null @@ -1,70 +0,0 @@ -#pragma warning disable CS1591 - -using System.Collections.Generic; -using System.IO; - -namespace DvdLib.Ifo -{ - public class Title - { - public uint TitleNumber { get; private set; } - - public uint AngleCount { get; private set; } - - public ushort ChapterCount { get; private set; } - - public byte VideoTitleSetNumber { get; private set; } - - private ushort _parentalManagementMask; - private byte _titleNumberInVTS; - private uint _vtsStartSector; // relative to start of entire disk - - public ProgramChain EntryProgramChain { get; private set; } - - public readonly List<ProgramChain> ProgramChains; - - public readonly List<Chapter> Chapters; - - public Title(uint titleNum) - { - ProgramChains = new List<ProgramChain>(); - Chapters = new List<Chapter>(); - Chapters = new List<Chapter>(); - TitleNumber = titleNum; - } - - public bool IsVTSTitle(uint vtsNum, uint vtsTitleNum) - { - return (vtsNum == VideoTitleSetNumber && vtsTitleNum == _titleNumberInVTS); - } - - internal void ParseTT_SRPT(BinaryReader br) - { - byte titleType = br.ReadByte(); - // TODO parse Title Type - - AngleCount = br.ReadByte(); - ChapterCount = br.ReadUInt16(); - _parentalManagementMask = br.ReadUInt16(); - VideoTitleSetNumber = br.ReadByte(); - _titleNumberInVTS = br.ReadByte(); - _vtsStartSector = br.ReadUInt32(); - } - - internal void AddPgc(BinaryReader br, long startByte, bool entryPgc, uint pgcNum) - { - long curPos = br.BaseStream.Position; - br.BaseStream.Seek(startByte, SeekOrigin.Begin); - - var pgc = new ProgramChain(pgcNum); - pgc.ParseHeader(br); - ProgramChains.Add(pgc); - if (entryPgc) - { - EntryProgramChain = pgc; - } - - br.BaseStream.Seek(curPos, SeekOrigin.Begin); - } - } -} diff --git a/DvdLib/Ifo/UserOperation.cs b/DvdLib/Ifo/UserOperation.cs deleted file mode 100644 index 5d111ebc06..0000000000 --- a/DvdLib/Ifo/UserOperation.cs +++ /dev/null @@ -1,37 +0,0 @@ -#pragma warning disable CS1591 - -using System; - -namespace DvdLib.Ifo -{ - [Flags] - public enum UserOperation - { - None = 0, - TitleOrTimePlay = 1, - ChapterSearchOrPlay = 2, - TitlePlay = 4, - Stop = 8, - GoUp = 16, - TimeOrChapterSearch = 32, - PrevOrTopProgramSearch = 64, - NextProgramSearch = 128, - ForwardScan = 256, - BackwardScan = 512, - TitleMenuCall = 1024, - RootMenuCall = 2048, - SubpictureMenuCall = 4096, - AudioMenuCall = 8192, - AngleMenuCall = 16384, - ChapterMenuCall = 32768, - Resume = 65536, - ButtonSelectOrActive = 131072, - StillOff = 262144, - PauseOn = 524288, - AudioStreamChange = 1048576, - SubpictureStreamChange = 2097152, - AngleChange = 4194304, - KaraokeAudioPresentationModeChange = 8388608, - VideoPresentationModeChange = 16777216, - } -} diff --git a/DvdLib/Properties/AssemblyInfo.cs b/DvdLib/Properties/AssemblyInfo.cs deleted file mode 100644 index 6acd571d68..0000000000 --- a/DvdLib/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Reflection; -using System.Resources; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("DvdLib")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Jellyfin Project")] -[assembly: AssemblyProduct("Jellyfin Server")] -[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] -[assembly: NeutralResourcesLanguage("en")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index c187966111..fe1f4defe4 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -83,13 +83,11 @@ using MediaBrowser.Controller.Subtitles; using MediaBrowser.Controller.SyncPlay; using MediaBrowser.Controller.TV; using MediaBrowser.LocalMetadata.Savers; -using MediaBrowser.MediaEncoding.BdInfo; using MediaBrowser.MediaEncoding.Subtitles; using MediaBrowser.Model.Cryptography; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; -using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.Net; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.System; @@ -562,8 +560,6 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton<ILocalizationManager, LocalizationManager>(); - serviceCollection.AddSingleton<IBlurayExaminer, BdInfoExaminer>(); - serviceCollection.AddSingleton<IUserDataRepository, SqliteUserDataRepository>(); serviceCollection.AddSingleton<IUserDataManager, UserDataManager>(); diff --git a/Jellyfin.sln b/Jellyfin.sln index c0d2ec0687..f3fb7efa98 100644 --- a/Jellyfin.sln +++ b/Jellyfin.sln @@ -21,8 +21,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Drawing", "src\Jel EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emby.Photos", "Emby.Photos\Emby.Photos.csproj", "{89AB4548-770D-41FD-A891-8DAFF44F452C}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DvdLib", "DvdLib\DvdLib.csproj", "{713F42B5-878E-499D-A878-E4C652B1D5E8}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emby.Server.Implementations", "Emby.Server.Implementations\Emby.Server.Implementations.csproj", "{E383961B-9356-4D5D-8233-9A1079D03055}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RSSDP", "RSSDP\RSSDP.csproj", "{21002819-C39A-4D3E-BE83-2A276A77FB1F}" @@ -139,10 +137,6 @@ Global {89AB4548-770D-41FD-A891-8DAFF44F452C}.Debug|Any CPU.Build.0 = Debug|Any CPU {89AB4548-770D-41FD-A891-8DAFF44F452C}.Release|Any CPU.ActiveCfg = Release|Any CPU {89AB4548-770D-41FD-A891-8DAFF44F452C}.Release|Any CPU.Build.0 = Release|Any CPU - {713F42B5-878E-499D-A878-E4C652B1D5E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {713F42B5-878E-499D-A878-E4C652B1D5E8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {713F42B5-878E-499D-A878-E4C652B1D5E8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {713F42B5-878E-499D-A878-E4C652B1D5E8}.Release|Any CPU.Build.0 = Release|Any CPU {E383961B-9356-4D5D-8233-9A1079D03055}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E383961B-9356-4D5D-8233-9A1079D03055}.Debug|Any CPU.Build.0 = Debug|Any CPU {E383961B-9356-4D5D-8233-9A1079D03055}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs index fe8e9063ee..bc6207ac51 100644 --- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs +++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs @@ -187,13 +187,5 @@ namespace MediaBrowser.Controller.MediaEncoding /// <param name="path">The path.</param> /// <param name="pathType">The type of path.</param> void UpdateEncoderPath(string path, string pathType); - - /// <summary> - /// Gets the primary playlist of .vob files. - /// </summary> - /// <param name="path">The to the .vob files.</param> - /// <param name="titleNumber">The title number to start with.</param> - /// <returns>A playlist.</returns> - IEnumerable<string> GetPrimaryPlaylistVobFiles(string path, uint? titleNumber); } } diff --git a/MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs b/MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs deleted file mode 100644 index 7e026b42e3..0000000000 --- a/MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs +++ /dev/null @@ -1,83 +0,0 @@ -#pragma warning disable CS1591 - -using System; -using System.Linq; -using BDInfo.IO; -using MediaBrowser.Model.IO; - -namespace MediaBrowser.MediaEncoding.BdInfo -{ - public class BdInfoDirectoryInfo : IDirectoryInfo - { - private readonly IFileSystem _fileSystem; - - private readonly FileSystemMetadata _impl; - - public BdInfoDirectoryInfo(IFileSystem fileSystem, string path) - { - _fileSystem = fileSystem; - _impl = _fileSystem.GetDirectoryInfo(path); - } - - private BdInfoDirectoryInfo(IFileSystem fileSystem, FileSystemMetadata impl) - { - _fileSystem = fileSystem; - _impl = impl; - } - - public string Name => _impl.Name; - - public string FullName => _impl.FullName; - - public IDirectoryInfo? Parent - { - get - { - var parentFolder = System.IO.Path.GetDirectoryName(_impl.FullName); - if (parentFolder is not null) - { - return new BdInfoDirectoryInfo(_fileSystem, parentFolder); - } - - return null; - } - } - - public IDirectoryInfo[] GetDirectories() - { - return Array.ConvertAll( - _fileSystem.GetDirectories(_impl.FullName).ToArray(), - x => new BdInfoDirectoryInfo(_fileSystem, x)); - } - - public IFileInfo[] GetFiles() - { - return Array.ConvertAll( - _fileSystem.GetFiles(_impl.FullName).ToArray(), - x => new BdInfoFileInfo(x)); - } - - public IFileInfo[] GetFiles(string searchPattern) - { - return Array.ConvertAll( - _fileSystem.GetFiles(_impl.FullName, new[] { searchPattern }, false, false).ToArray(), - x => new BdInfoFileInfo(x)); - } - - public IFileInfo[] GetFiles(string searchPattern, System.IO.SearchOption searchOption) - { - return Array.ConvertAll( - _fileSystem.GetFiles( - _impl.FullName, - new[] { searchPattern }, - false, - (searchOption & System.IO.SearchOption.AllDirectories) == System.IO.SearchOption.AllDirectories).ToArray(), - x => new BdInfoFileInfo(x)); - } - - public static IDirectoryInfo FromFileSystemPath(IFileSystem fs, string path) - { - return new BdInfoDirectoryInfo(fs, path); - } - } -} diff --git a/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs b/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs deleted file mode 100644 index 3e53cbf29f..0000000000 --- a/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs +++ /dev/null @@ -1,194 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using BDInfo; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.MediaInfo; - -namespace MediaBrowser.MediaEncoding.BdInfo -{ - /// <summary> - /// Class BdInfoExaminer. - /// </summary> - public class BdInfoExaminer : IBlurayExaminer - { - private readonly IFileSystem _fileSystem; - - /// <summary> - /// Initializes a new instance of the <see cref="BdInfoExaminer" /> class. - /// </summary> - /// <param name="fileSystem">The filesystem.</param> - public BdInfoExaminer(IFileSystem fileSystem) - { - _fileSystem = fileSystem; - } - - /// <summary> - /// Gets the disc info. - /// </summary> - /// <param name="path">The path.</param> - /// <returns>BlurayDiscInfo.</returns> - public BlurayDiscInfo GetDiscInfo(string path) - { - if (string.IsNullOrWhiteSpace(path)) - { - throw new ArgumentNullException(nameof(path)); - } - - var bdrom = new BDROM(BdInfoDirectoryInfo.FromFileSystemPath(_fileSystem, path)); - - bdrom.Scan(); - - // Get the longest playlist - var playlist = bdrom.PlaylistFiles.Values.OrderByDescending(p => p.TotalLength).FirstOrDefault(p => p.IsValid); - - var outputStream = new BlurayDiscInfo - { - MediaStreams = Array.Empty<MediaStream>() - }; - - if (playlist is null) - { - return outputStream; - } - - outputStream.Chapters = playlist.Chapters.ToArray(); - - outputStream.RunTimeTicks = TimeSpan.FromSeconds(playlist.TotalLength).Ticks; - - var mediaStreams = new List<MediaStream>(); - - foreach (var stream in playlist.SortedStreams) - { - if (stream is TSVideoStream videoStream) - { - AddVideoStream(mediaStreams, videoStream); - continue; - } - - if (stream is TSAudioStream audioStream) - { - AddAudioStream(mediaStreams, audioStream); - continue; - } - - if (stream is TSTextStream textStream) - { - AddSubtitleStream(mediaStreams, textStream); - continue; - } - - if (stream is TSGraphicsStream graphicsStream) - { - AddSubtitleStream(mediaStreams, graphicsStream); - } - } - - outputStream.MediaStreams = mediaStreams.ToArray(); - - outputStream.PlaylistName = playlist.Name; - - if (playlist.StreamClips is not null && playlist.StreamClips.Any()) - { - // Get the files in the playlist - outputStream.Files = playlist.StreamClips.Select(i => i.StreamFile.Name).ToArray(); - } - - return outputStream; - } - - /// <summary> - /// Adds the video stream. - /// </summary> - /// <param name="streams">The streams.</param> - /// <param name="videoStream">The video stream.</param> - private void AddVideoStream(List<MediaStream> streams, TSVideoStream videoStream) - { - var mediaStream = new MediaStream - { - BitRate = Convert.ToInt32(videoStream.BitRate), - Width = videoStream.Width, - Height = videoStream.Height, - Codec = videoStream.CodecShortName, - IsInterlaced = videoStream.IsInterlaced, - Type = MediaStreamType.Video, - Index = streams.Count - }; - - if (videoStream.FrameRateDenominator > 0) - { - float frameRateEnumerator = videoStream.FrameRateEnumerator; - float frameRateDenominator = videoStream.FrameRateDenominator; - - mediaStream.AverageFrameRate = mediaStream.RealFrameRate = frameRateEnumerator / frameRateDenominator; - } - - streams.Add(mediaStream); - } - - /// <summary> - /// Adds the audio stream. - /// </summary> - /// <param name="streams">The streams.</param> - /// <param name="audioStream">The audio stream.</param> - private void AddAudioStream(List<MediaStream> streams, TSAudioStream audioStream) - { - var stream = new MediaStream - { - Codec = audioStream.CodecShortName, - Language = audioStream.LanguageCode, - Channels = audioStream.ChannelCount, - SampleRate = audioStream.SampleRate, - Type = MediaStreamType.Audio, - Index = streams.Count - }; - - var bitrate = Convert.ToInt32(audioStream.BitRate); - - if (bitrate > 0) - { - stream.BitRate = bitrate; - } - - if (audioStream.LFE > 0) - { - stream.Channels = audioStream.ChannelCount + 1; - } - - streams.Add(stream); - } - - /// <summary> - /// Adds the subtitle stream. - /// </summary> - /// <param name="streams">The streams.</param> - /// <param name="textStream">The text stream.</param> - private void AddSubtitleStream(List<MediaStream> streams, TSTextStream textStream) - { - streams.Add(new MediaStream - { - Language = textStream.LanguageCode, - Codec = textStream.CodecShortName, - Type = MediaStreamType.Subtitle, - Index = streams.Count - }); - } - - /// <summary> - /// Adds the subtitle stream. - /// </summary> - /// <param name="streams">The streams.</param> - /// <param name="textStream">The text stream.</param> - private void AddSubtitleStream(List<MediaStream> streams, TSGraphicsStream textStream) - { - streams.Add(new MediaStream - { - Language = textStream.LanguageCode, - Codec = textStream.CodecShortName, - Type = MediaStreamType.Subtitle, - Index = streams.Count - }); - } - } -} diff --git a/MediaBrowser.MediaEncoding/BdInfo/BdInfoFileInfo.cs b/MediaBrowser.MediaEncoding/BdInfo/BdInfoFileInfo.cs deleted file mode 100644 index d55688e3df..0000000000 --- a/MediaBrowser.MediaEncoding/BdInfo/BdInfoFileInfo.cs +++ /dev/null @@ -1,41 +0,0 @@ -#pragma warning disable CS1591 - -using System.IO; -using MediaBrowser.Model.IO; - -namespace MediaBrowser.MediaEncoding.BdInfo -{ - public class BdInfoFileInfo : BDInfo.IO.IFileInfo - { - private FileSystemMetadata _impl; - - public BdInfoFileInfo(FileSystemMetadata impl) - { - _impl = impl; - } - - public string Name => _impl.Name; - - public string FullName => _impl.FullName; - - public string Extension => _impl.Extension; - - public long Length => _impl.Length; - - public bool IsDir => _impl.IsDirectory; - - public Stream OpenRead() - { - return new FileStream( - FullName, - FileMode.Open, - FileAccess.Read, - FileShare.Read); - } - - public StreamReader OpenText() - { - return new StreamReader(OpenRead()); - } - } -} diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index d95f894c52..d2240b5aff 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -862,85 +862,6 @@ namespace MediaBrowser.MediaEncoding.Encoder throw new NotImplementedException(); } - /// <inheritdoc /> - public IEnumerable<string> GetPrimaryPlaylistVobFiles(string path, uint? titleNumber) - { - // min size 300 mb - const long MinPlayableSize = 314572800; - - // Try to eliminate menus and intros by skipping all files at the front of the list that are less than the minimum size - // Once we reach a file that is at least the minimum, return all subsequent ones - var allVobs = _fileSystem.GetFiles(path, true) - .Where(file => string.Equals(file.Extension, ".vob", StringComparison.OrdinalIgnoreCase)) - .OrderBy(i => i.FullName) - .ToList(); - - // If we didn't find any satisfying the min length, just take them all - if (allVobs.Count == 0) - { - _logger.LogWarning("No vobs found in dvd structure."); - return Enumerable.Empty<string>(); - } - - if (titleNumber.HasValue) - { - var prefix = string.Format( - CultureInfo.InvariantCulture, - titleNumber.Value >= 10 ? "VTS_{0}_" : "VTS_0{0}_", - titleNumber.Value); - var vobs = allVobs.Where(i => i.Name.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)).ToList(); - - if (vobs.Count > 0) - { - var minSizeVobs = vobs - .SkipWhile(f => f.Length < MinPlayableSize) - .ToList(); - - return minSizeVobs.Count == 0 ? vobs.Select(i => i.FullName) : minSizeVobs.Select(i => i.FullName); - } - - _logger.LogWarning("Could not determine vob file list for {Path} using DvdLib. Will scan using file sizes.", path); - } - - var files = allVobs - .SkipWhile(f => f.Length < MinPlayableSize) - .ToList(); - - // If we didn't find any satisfying the min length, just take them all - if (files.Count == 0) - { - _logger.LogWarning("Vob size filter resulted in zero matches. Taking all vobs."); - files = allVobs; - } - - // Assuming they're named "vts_05_01", take all files whose second part matches that of the first file - if (files.Count > 0) - { - var parts = _fileSystem.GetFileNameWithoutExtension(files[0]).Split('_'); - - if (parts.Length == 3) - { - var title = parts[1]; - - files = files.TakeWhile(f => - { - var fileParts = _fileSystem.GetFileNameWithoutExtension(f).Split('_'); - - return fileParts.Length == 3 && string.Equals(title, fileParts[1], StringComparison.OrdinalIgnoreCase); - }).ToList(); - - // If this resulted in not getting any vobs, just take them all - if (files.Count == 0) - { - _logger.LogWarning("Vob filename filter resulted in zero matches. Taking all vobs."); - files = allVobs; - } - } - } - - return files.Select(i => i.FullName); - } - public bool CanExtractSubtitles(string codec) { // TODO is there ever a case when a subtitle can't be extracted?? diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj index 1233fb1108..93177298f9 100644 --- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj +++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj @@ -22,7 +22,6 @@ </ItemGroup> <ItemGroup> - <PackageReference Include="BDInfo" Version="0.7.6.2" /> <PackageReference Include="libse" Version="3.6.10" /> <PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" /> <PackageReference Include="System.Text.Encoding.CodePages" Version="7.0.0" /> diff --git a/MediaBrowser.Model/MediaInfo/BlurayDiscInfo.cs b/MediaBrowser.Model/MediaInfo/BlurayDiscInfo.cs deleted file mode 100644 index 83f982a5c8..0000000000 --- a/MediaBrowser.Model/MediaInfo/BlurayDiscInfo.cs +++ /dev/null @@ -1,39 +0,0 @@ -#nullable disable -#pragma warning disable CS1591 - -using MediaBrowser.Model.Entities; - -namespace MediaBrowser.Model.MediaInfo -{ - /// <summary> - /// Represents the result of BDInfo output. - /// </summary> - public class BlurayDiscInfo - { - /// <summary> - /// Gets or sets the media streams. - /// </summary> - /// <value>The media streams.</value> - public MediaStream[] MediaStreams { get; set; } - - /// <summary> - /// Gets or sets the run time ticks. - /// </summary> - /// <value>The run time ticks.</value> - public long? RunTimeTicks { get; set; } - - /// <summary> - /// Gets or sets the files. - /// </summary> - /// <value>The files.</value> - public string[] Files { get; set; } - - public string PlaylistName { get; set; } - - /// <summary> - /// Gets or sets the chapters. - /// </summary> - /// <value>The chapters.</value> - public double[] Chapters { get; set; } - } -} diff --git a/MediaBrowser.Model/MediaInfo/IBlurayExaminer.cs b/MediaBrowser.Model/MediaInfo/IBlurayExaminer.cs deleted file mode 100644 index 5b7d1d03c3..0000000000 --- a/MediaBrowser.Model/MediaInfo/IBlurayExaminer.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace MediaBrowser.Model.MediaInfo -{ - /// <summary> - /// Interface IBlurayExaminer. - /// </summary> - public interface IBlurayExaminer - { - /// <summary> - /// Gets the disc info. - /// </summary> - /// <param name="path">The path.</param> - /// <returns>BlurayDiscInfo.</returns> - BlurayDiscInfo GetDiscInfo(string path); - } -} diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 13de86a929..9e238e9f3a 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -8,7 +8,6 @@ <ItemGroup> <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" /> <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" /> - <ProjectReference Include="..\DvdLib\DvdLib.csproj" /> </ItemGroup> <ItemGroup> diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index 81434b8620..0f35c6a5ea 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -9,7 +9,6 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; -using DvdLib.Ifo; using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Chapters; using MediaBrowser.Controller.Configuration; @@ -37,7 +36,6 @@ namespace MediaBrowser.Providers.MediaInfo private readonly ILogger<FFProbeVideoInfo> _logger; private readonly IMediaEncoder _mediaEncoder; private readonly IItemRepository _itemRepo; - private readonly IBlurayExaminer _blurayExaminer; private readonly ILocalizationManager _localization; private readonly IEncodingManager _encodingManager; private readonly IServerConfigurationManager _config; @@ -53,7 +51,6 @@ namespace MediaBrowser.Providers.MediaInfo IMediaSourceManager mediaSourceManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, - IBlurayExaminer blurayExaminer, ILocalizationManager localization, IEncodingManager encodingManager, IServerConfigurationManager config, @@ -67,7 +64,6 @@ namespace MediaBrowser.Providers.MediaInfo _mediaSourceManager = mediaSourceManager; _mediaEncoder = mediaEncoder; _itemRepo = itemRepo; - _blurayExaminer = blurayExaminer; _localization = localization; _encodingManager = encodingManager; _config = config; @@ -84,47 +80,16 @@ namespace MediaBrowser.Providers.MediaInfo CancellationToken cancellationToken) where T : Video { - BlurayDiscInfo blurayDiscInfo = null; - Model.MediaInfo.MediaInfo mediaInfoResult = null; if (!item.IsShortcut || options.EnableRemoteContentProbe) { - string[] streamFileNames = null; - - if (item.VideoType == VideoType.Dvd) - { - streamFileNames = FetchFromDvdLib(item); - - if (streamFileNames.Length == 0) - { - _logger.LogError("No playable vobs found in dvd structure, skipping ffprobe."); - return ItemUpdateType.MetadataImport; - } - } - else if (item.VideoType == VideoType.BluRay) - { - var inputPath = item.Path; - - blurayDiscInfo = GetBDInfo(inputPath); - - streamFileNames = blurayDiscInfo.Files; - - if (streamFileNames.Length == 0) - { - _logger.LogError("No playable vobs found in bluray structure, skipping ffprobe."); - return ItemUpdateType.MetadataImport; - } - } - - streamFileNames ??= Array.Empty<string>(); - mediaInfoResult = await GetMediaInfo(item, cancellationToken).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); } - await Fetch(item, cancellationToken, mediaInfoResult, blurayDiscInfo, options).ConfigureAwait(false); + await Fetch(item, cancellationToken, mediaInfoResult, options).ConfigureAwait(false); return ItemUpdateType.MetadataImport; } @@ -164,7 +129,6 @@ namespace MediaBrowser.Providers.MediaInfo Video video, CancellationToken cancellationToken, Model.MediaInfo.MediaInfo mediaInfo, - BlurayDiscInfo blurayInfo, MetadataRefreshOptions options) { List<MediaStream> mediaStreams; @@ -218,10 +182,6 @@ namespace MediaBrowser.Providers.MediaInfo video.Container = mediaInfo.Container; chapters = mediaInfo.Chapters ?? Array.Empty<ChapterInfo>(); - if (blurayInfo is not null) - { - FetchBdInfo(video, ref chapters, mediaStreams, blurayInfo); - } } else { @@ -317,91 +277,6 @@ namespace MediaBrowser.Providers.MediaInfo } } - private void FetchBdInfo(BaseItem item, ref ChapterInfo[] chapters, List<MediaStream> mediaStreams, BlurayDiscInfo blurayInfo) - { - var video = (Video)item; - - // video.PlayableStreamFileNames = blurayInfo.Files.ToList(); - - // Use BD Info if it has multiple m2ts. Otherwise, treat it like a video file and rely more on ffprobe output - if (blurayInfo.Files.Length > 1) - { - int? currentHeight = null; - int? currentWidth = null; - int? currentBitRate = null; - - var videoStream = mediaStreams.FirstOrDefault(s => s.Type == MediaStreamType.Video); - - // Grab the values that ffprobe recorded - if (videoStream is not null) - { - currentBitRate = videoStream.BitRate; - currentWidth = videoStream.Width; - currentHeight = videoStream.Height; - } - - // Fill video properties from the BDInfo result - mediaStreams.Clear(); - mediaStreams.AddRange(blurayInfo.MediaStreams); - - if (blurayInfo.RunTimeTicks.HasValue && blurayInfo.RunTimeTicks.Value > 0) - { - video.RunTimeTicks = blurayInfo.RunTimeTicks; - } - - if (blurayInfo.Chapters is not null) - { - double[] brChapter = blurayInfo.Chapters; - chapters = new ChapterInfo[brChapter.Length]; - for (int i = 0; i < brChapter.Length; i++) - { - chapters[i] = new ChapterInfo - { - StartPositionTicks = TimeSpan.FromSeconds(brChapter[i]).Ticks - }; - } - } - - videoStream = mediaStreams.FirstOrDefault(s => s.Type == MediaStreamType.Video); - - // Use the ffprobe values if these are empty - if (videoStream is not null) - { - videoStream.BitRate = IsEmpty(videoStream.BitRate) ? currentBitRate : videoStream.BitRate; - videoStream.Width = IsEmpty(videoStream.Width) ? currentWidth : videoStream.Width; - videoStream.Height = IsEmpty(videoStream.Height) ? currentHeight : videoStream.Height; - } - } - } - - private bool IsEmpty(int? num) - { - return !num.HasValue || num.Value == 0; - } - - /// <summary> - /// Gets information about the longest playlist on a bdrom. - /// </summary> - /// <param name="path">The path.</param> - /// <returns>VideoStream.</returns> - private BlurayDiscInfo GetBDInfo(string path) - { - if (string.IsNullOrWhiteSpace(path)) - { - throw new ArgumentNullException(nameof(path)); - } - - try - { - return _blurayExaminer.GetDiscInfo(path); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error getting BDInfo"); - return null; - } - } - private void FetchEmbeddedInfo(Video video, Model.MediaInfo.MediaInfo data, MetadataRefreshOptions refreshOptions, LibraryOptions libraryOptions) { var replaceData = refreshOptions.ReplaceAllMetadata; @@ -683,33 +558,5 @@ namespace MediaBrowser.Providers.MediaInfo return chapters; } - - private string[] FetchFromDvdLib(Video item) - { - var path = item.Path; - var dvd = new Dvd(path); - - var primaryTitle = dvd.Titles.OrderByDescending(GetRuntime).FirstOrDefault(); - - byte? titleNumber = null; - - if (primaryTitle is not null) - { - titleNumber = primaryTitle.VideoTitleSetNumber; - item.RunTimeTicks = GetRuntime(primaryTitle); - } - - return _mediaEncoder.GetPrimaryPlaylistVobFiles(item.Path, titleNumber) - .Select(Path.GetFileName) - .ToArray(); - } - - private long GetRuntime(Title title) - { - return title.ProgramChains - .Select(i => (TimeSpan)i.PlaybackTime) - .Select(i => i.Ticks) - .Sum(); - } } } diff --git a/MediaBrowser.Providers/MediaInfo/ProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/ProbeProvider.cs index 2800219552..31fa3da1ce 100644 --- a/MediaBrowser.Providers/MediaInfo/ProbeProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/ProbeProvider.cs @@ -53,7 +53,6 @@ namespace MediaBrowser.Providers.MediaInfo /// <param name="mediaSourceManager">Instance of the <see cref="IMediaSourceManager"/> interface.</param> /// <param name="mediaEncoder">Instance of the <see cref="IMediaEncoder"/> interface.</param> /// <param name="itemRepo">Instance of the <see cref="IItemRepository"/> interface.</param> - /// <param name="blurayExaminer">Instance of the <see cref="IBlurayExaminer"/> interface.</param> /// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param> /// <param name="encodingManager">Instance of the <see cref="IEncodingManager"/> interface.</param> /// <param name="config">Instance of the <see cref="IServerConfigurationManager"/> interface.</param> @@ -67,7 +66,6 @@ namespace MediaBrowser.Providers.MediaInfo IMediaSourceManager mediaSourceManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, - IBlurayExaminer blurayExaminer, ILocalizationManager localization, IEncodingManager encodingManager, IServerConfigurationManager config, @@ -87,7 +85,6 @@ namespace MediaBrowser.Providers.MediaInfo mediaSourceManager, mediaEncoder, itemRepo, - blurayExaminer, localization, encodingManager, config, -- cgit v1.2.3 From 7fa6d4c81e9d1f22fd67a7cab2d70cba27d89275 Mon Sep 17 00:00:00 2001 From: Jpuc1143 <jaime.perez@uc.cl> Date: Thu, 19 Jan 2023 23:28:52 -0300 Subject: Add "Allowed Tags" to Parental Controls --- CONTRIBUTORS.md | 1 + .../Data/SqliteItemRepository.cs | 18 ++++++++++++++++++ Jellyfin.Data/Enums/PreferenceKind.cs | 7 ++++++- Jellyfin.Server.Implementations/Users/UserManager.cs | 2 ++ Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs | 1 + MediaBrowser.Controller/Entities/BaseItem.cs | 5 +++++ MediaBrowser.Controller/Entities/InternalItemsQuery.cs | 4 ++++ MediaBrowser.Model/Users/UserPolicy.cs | 3 +++ 8 files changed, 40 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index ec3c6fd2af..74c9dbdaf0 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -231,3 +231,4 @@ - [Matthew Jones](https://github.com/matthew-jones-uk) - [Jakob Kukla](https://github.com/jakobkukla) - [Utku Özdemir](https://github.com/utkuozdemir) + - [JPUC1143](https://github.com/Jpuc1143/) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index bc703fe90d..e828bfabfb 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -4477,6 +4477,24 @@ namespace Emby.Server.Implementations.Data } } + if (query.IncludeInheritedTags.Length > 0) + { + var paramName = "@IncludeInheritedTags"; + if (statement is null) + { + int index = 0; + string includedTags = string.Join(',', query.IncludeInheritedTags.Select(_ => paramName + index++)); + whereClauses.Add("((select CleanValue from ItemValues where ItemId=Guid and Type=6 and cleanvalue in (" + includedTags + ")) is not null)"); + } + else + { + for (int index = 0; index < query.IncludeInheritedTags.Length; index++) + { + statement.TryBind(paramName + index, GetCleanValue(query.IncludeInheritedTags[index])); + } + } + } + if (query.SeriesStatuses.Length > 0) { var statuses = new List<string>(); diff --git a/Jellyfin.Data/Enums/PreferenceKind.cs b/Jellyfin.Data/Enums/PreferenceKind.cs index a54d789afb..d2b412e459 100644 --- a/Jellyfin.Data/Enums/PreferenceKind.cs +++ b/Jellyfin.Data/Enums/PreferenceKind.cs @@ -63,6 +63,11 @@ namespace Jellyfin.Data.Enums /// <summary> /// A list of ordered views. /// </summary> - OrderedViews = 11 + OrderedViews = 11, + + /// <summary> + /// A list of allowed tags. + /// </summary> + AllowedTags = 12 } } diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index dc9d78857e..f9679510d7 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -371,6 +371,7 @@ namespace Jellyfin.Server.Implementations.Users EnablePublicSharing = user.HasPermission(PermissionKind.EnablePublicSharing), AccessSchedules = user.AccessSchedules.ToArray(), BlockedTags = user.GetPreference(PreferenceKind.BlockedTags), + AllowedTags = user.GetPreference(PreferenceKind.AllowedTags), EnabledChannels = user.GetPreferenceValues<Guid>(PreferenceKind.EnabledChannels), EnabledDevices = user.GetPreference(PreferenceKind.EnabledDevices), EnabledFolders = user.GetPreferenceValues<Guid>(PreferenceKind.EnabledFolders), @@ -696,6 +697,7 @@ namespace Jellyfin.Server.Implementations.Users // TODO: fix this at some point user.SetPreference(PreferenceKind.BlockUnratedItems, policy.BlockUnratedItems ?? Array.Empty<UnratedItem>()); user.SetPreference(PreferenceKind.BlockedTags, policy.BlockedTags); + user.SetPreference(PreferenceKind.AllowedTags, policy.AllowedTags); user.SetPreference(PreferenceKind.EnabledChannels, policy.EnabledChannels); user.SetPreference(PreferenceKind.EnabledDevices, policy.EnabledDevices); user.SetPreference(PreferenceKind.EnabledFolders, policy.EnabledFolders); diff --git a/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs index ea2f033027..1efd071ec5 100644 --- a/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs +++ b/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs @@ -170,6 +170,7 @@ namespace Jellyfin.Server.Migrations.Routines } user.SetPreference(PreferenceKind.BlockedTags, policy.BlockedTags); + user.SetPreference(PreferenceKind.AllowedTags, policy.AllowedTags); user.SetPreference(PreferenceKind.EnabledChannels, policy.EnabledChannels); user.SetPreference(PreferenceKind.EnabledDevices, policy.EnabledDevices); user.SetPreference(PreferenceKind.EnabledFolders, policy.EnabledFolders); diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index f2c2007f7a..0cd9720549 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -1607,6 +1607,11 @@ namespace MediaBrowser.Controller.Entities return false; } + if (user.GetPreference(PreferenceKind.AllowedTags).Any() && !user.GetPreference(PreferenceKind.AllowedTags).Any(i => Tags.Contains(i, StringComparison.OrdinalIgnoreCase))) + { + return false; + } + return true; } diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs index a1e5319048..a51299284b 100644 --- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs +++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs @@ -26,6 +26,7 @@ namespace MediaBrowser.Controller.Entities EnableTotalRecordCount = true; ExcludeArtistIds = Array.Empty<Guid>(); ExcludeInheritedTags = Array.Empty<string>(); + IncludeInheritedTags = Array.Empty<string>(); ExcludeItemIds = Array.Empty<Guid>(); ExcludeItemTypes = Array.Empty<BaseItemKind>(); ExcludeTags = Array.Empty<string>(); @@ -95,6 +96,8 @@ namespace MediaBrowser.Controller.Entities public string[] ExcludeInheritedTags { get; set; } + public string[] IncludeInheritedTags { get; set; } + public IReadOnlyList<string> Genres { get; set; } public bool? IsSpecialSeason { get; set; } @@ -368,6 +371,7 @@ namespace MediaBrowser.Controller.Entities } ExcludeInheritedTags = user.GetPreference(PreferenceKind.BlockedTags); + IncludeInheritedTags = user.GetPreference(PreferenceKind.AllowedTags); User = user; } diff --git a/MediaBrowser.Model/Users/UserPolicy.cs b/MediaBrowser.Model/Users/UserPolicy.cs index 3634d07058..1619dac5ad 100644 --- a/MediaBrowser.Model/Users/UserPolicy.cs +++ b/MediaBrowser.Model/Users/UserPolicy.cs @@ -35,6 +35,7 @@ namespace MediaBrowser.Model.Users EnableSharedDeviceControl = true; BlockedTags = Array.Empty<string>(); + AllowedTags = Array.Empty<string>(); BlockUnratedItems = Array.Empty<UnratedItem>(); EnableUserPreferenceAccess = true; @@ -86,6 +87,8 @@ namespace MediaBrowser.Model.Users public string[] BlockedTags { get; set; } + public string[] AllowedTags { get; set; } + public bool EnableUserPreferenceAccess { get; set; } public AccessSchedule[] AccessSchedules { get; set; } -- cgit v1.2.3 From 63b013256224a747acba0b5523aad5fbeb3f254e Mon Sep 17 00:00:00 2001 From: Patrick Barron <barronpm@gmail.com> Date: Thu, 26 Jan 2023 03:05:00 -0500 Subject: Remove OS information from System Info (#9175) Co-authored-by: Bond-009 <bond.009@outlook.com> --- Emby.Dlna/Main/DlnaEntryPoint.cs | 3 +- Emby.Server.Implementations/ApplicationHost.cs | 6 +-- Jellyfin.Server/Startup.cs | 3 +- MediaBrowser.Common/System/OperatingSystem.cs | 74 -------------------------- MediaBrowser.Model/System/OperatingSystemId.cs | 12 ----- MediaBrowser.Model/System/PublicSystemInfo.cs | 5 +- MediaBrowser.Model/System/SystemInfo.cs | 6 ++- 7 files changed, 13 insertions(+), 96 deletions(-) delete mode 100644 MediaBrowser.Common/System/OperatingSystem.cs delete mode 100644 MediaBrowser.Model/System/OperatingSystemId.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs index 2dc079254a..aab475153b 100644 --- a/Emby.Dlna/Main/DlnaEntryPoint.cs +++ b/Emby.Dlna/Main/DlnaEntryPoint.cs @@ -7,6 +7,7 @@ using System.Globalization; using System.Linq; using System.Net.Http; using System.Net.Sockets; +using System.Runtime.InteropServices; using System.Threading.Tasks; using Emby.Dlna.PlayTo; using Emby.Dlna.Ssdp; @@ -262,7 +263,7 @@ namespace Emby.Dlna.Main { _publisher = new SsdpDevicePublisher( _communicationsServer, - MediaBrowser.Common.System.OperatingSystem.Name, + Environment.OSVersion.Platform.ToString(), Environment.OSVersion.VersionString, _config.GetDlnaConfiguration().SendOnlyMatchedHost) { diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index fe1f4defe4..560ba7d10d 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -690,7 +690,7 @@ namespace Emby.Server.Implementations logger.LogInformation("Environment Variables: {EnvVars}", relevantEnvVars); logger.LogInformation("Arguments: {Args}", commandLineArgs); - logger.LogInformation("Operating system: {OS}", MediaBrowser.Common.System.OperatingSystem.Name); + logger.LogInformation("Operating system: {OS}", RuntimeInformation.OSDescription); logger.LogInformation("Architecture: {Architecture}", RuntimeInformation.OSArchitecture); logger.LogInformation("64-Bit Process: {Is64Bit}", Environment.Is64BitProcess); logger.LogInformation("User Interactive: {IsUserInteractive}", Environment.UserInteractive); @@ -1032,14 +1032,11 @@ namespace Emby.Server.Implementations ItemsByNamePath = ApplicationPaths.InternalMetadataPath, InternalMetadataPath = ApplicationPaths.InternalMetadataPath, CachePath = ApplicationPaths.CachePath, - OperatingSystem = MediaBrowser.Common.System.OperatingSystem.Id.ToString(), - OperatingSystemDisplayName = MediaBrowser.Common.System.OperatingSystem.Name, CanLaunchWebBrowser = CanLaunchWebBrowser, TranscodingTempPath = ConfigurationManager.GetTranscodePath(), ServerName = FriendlyName, LocalAddress = GetSmartApiUrl(request), SupportsLibraryMonitor = true, - SystemArchitecture = RuntimeInformation.OSArchitecture, PackageName = _startupOptions.PackageName }; } @@ -1051,7 +1048,6 @@ namespace Emby.Server.Implementations Version = ApplicationVersionString, ProductName = ApplicationProductName, Id = SystemId, - OperatingSystem = MediaBrowser.Common.System.OperatingSystem.Id.ToString(), ServerName = FriendlyName, LocalAddress = GetSmartApiUrl(request), StartupWizardCompleted = ConfigurationManager.CommonConfiguration.IsStartupWizardCompleted diff --git a/Jellyfin.Server/Startup.cs b/Jellyfin.Server/Startup.cs index 7abd2fbef9..155f9fc8c1 100644 --- a/Jellyfin.Server/Startup.cs +++ b/Jellyfin.Server/Startup.cs @@ -4,6 +4,7 @@ using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Net.Mime; +using System.Runtime.InteropServices; using System.Text; using Jellyfin.Api.Middleware; using Jellyfin.MediaEncoding.Hls.Extensions; @@ -108,7 +109,7 @@ namespace Jellyfin.Server string.Format( CultureInfo.InvariantCulture, "{0}/{1} UPnP/1.0 {2}/{3}", - MediaBrowser.Common.System.OperatingSystem.Name, + Environment.OSVersion.Platform, Environment.OSVersion, _serverApplicationHost.Name, _serverApplicationHost.ApplicationVersionString)); diff --git a/MediaBrowser.Common/System/OperatingSystem.cs b/MediaBrowser.Common/System/OperatingSystem.cs deleted file mode 100644 index 5f673d3208..0000000000 --- a/MediaBrowser.Common/System/OperatingSystem.cs +++ /dev/null @@ -1,74 +0,0 @@ -#pragma warning disable CS1591 - -using System; -using System.Runtime.InteropServices; -using System.Threading; -using MediaBrowser.Model.System; - -namespace MediaBrowser.Common.System -{ - public static class OperatingSystem - { - // We can't use Interlocked.CompareExchange for enums - private static int _id = int.MaxValue; - - public static OperatingSystemId Id - { - get - { - if (_id == int.MaxValue) - { - Interlocked.CompareExchange(ref _id, (int)GetId(), int.MaxValue); - } - - return (OperatingSystemId)_id; - } - } - - public static string Name - { - get - { - switch (Id) - { - case OperatingSystemId.BSD: return "BSD"; - case OperatingSystemId.Linux: return "Linux"; - case OperatingSystemId.Darwin: return "macOS"; - case OperatingSystemId.Windows: return "Windows"; - default: throw new PlatformNotSupportedException($"Unknown OS {Id}"); - } - } - } - - private static OperatingSystemId GetId() - { - switch (Environment.OSVersion.Platform) - { - // On .NET Core `MacOSX` got replaced by `Unix`, this case should never be hit. - case PlatformID.MacOSX: - return OperatingSystemId.Darwin; - case PlatformID.Win32NT: - return OperatingSystemId.Windows; - case PlatformID.Unix: - default: - { - string osDescription = RuntimeInformation.OSDescription; - if (osDescription.Contains("linux", StringComparison.OrdinalIgnoreCase)) - { - return OperatingSystemId.Linux; - } - else if (osDescription.Contains("darwin", StringComparison.OrdinalIgnoreCase)) - { - return OperatingSystemId.Darwin; - } - else if (osDescription.Contains("bsd", StringComparison.OrdinalIgnoreCase)) - { - return OperatingSystemId.BSD; - } - - throw new PlatformNotSupportedException($"Can't resolve OS with description: '{osDescription}'"); - } - } - } - } -} diff --git a/MediaBrowser.Model/System/OperatingSystemId.cs b/MediaBrowser.Model/System/OperatingSystemId.cs deleted file mode 100644 index 2e417f6b56..0000000000 --- a/MediaBrowser.Model/System/OperatingSystemId.cs +++ /dev/null @@ -1,12 +0,0 @@ -#pragma warning disable CS1591 - -namespace MediaBrowser.Model.System -{ - public enum OperatingSystemId - { - Windows, - Linux, - Darwin, - BSD - } -} diff --git a/MediaBrowser.Model/System/PublicSystemInfo.cs b/MediaBrowser.Model/System/PublicSystemInfo.cs index 53030843ae..31a8956427 100644 --- a/MediaBrowser.Model/System/PublicSystemInfo.cs +++ b/MediaBrowser.Model/System/PublicSystemInfo.cs @@ -1,6 +1,8 @@ #nullable disable #pragma warning disable CS1591 +using System; + namespace MediaBrowser.Model.System { public class PublicSystemInfo @@ -32,7 +34,8 @@ namespace MediaBrowser.Model.System /// Gets or sets the operating system. /// </summary> /// <value>The operating system.</value> - public string OperatingSystem { get; set; } + [Obsolete("This is no longer set")] + public string OperatingSystem { get; set; } = string.Empty; /// <summary> /// Gets or sets the id. diff --git a/MediaBrowser.Model/System/SystemInfo.cs b/MediaBrowser.Model/System/SystemInfo.cs index 9e56849c7c..bd0099af70 100644 --- a/MediaBrowser.Model/System/SystemInfo.cs +++ b/MediaBrowser.Model/System/SystemInfo.cs @@ -42,7 +42,8 @@ namespace MediaBrowser.Model.System /// Gets or sets the display name of the operating system. /// </summary> /// <value>The display name of the operating system.</value> - public string OperatingSystemDisplayName { get; set; } + [Obsolete("This is no longer set")] + public string OperatingSystemDisplayName { get; set; } = string.Empty; /// <summary> /// Gets or sets the package name. @@ -137,6 +138,7 @@ namespace MediaBrowser.Model.System [Obsolete("This isn't set correctly anymore")] public FFmpegLocation EncoderLocation { get; set; } - public Architecture SystemArchitecture { get; set; } + [Obsolete("This is no longer set")] + public Architecture SystemArchitecture { get; set; } = Architecture.X64; } } -- cgit v1.2.3 From 0df899943f45db9d95b7d7ba07f593dffe98e597 Mon Sep 17 00:00:00 2001 From: Patrick Barron <barronpm@gmail.com> Date: Fri, 27 Jan 2023 18:24:53 -0500 Subject: Move LogEnvironmentInfo to StartupHelpers --- Emby.Server.Implementations/ApplicationHost.cs | 36 ----------------------- Jellyfin.Server/Helpers/StartupHelpers.cs | 40 ++++++++++++++++++++++++++ Jellyfin.Server/Program.cs | 2 +- 3 files changed, 41 insertions(+), 37 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 560ba7d10d..4ed055cdf4 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -11,7 +11,6 @@ using System.IO; using System.Linq; using System.Net; using System.Reflection; -using System.Runtime.InteropServices; using System.Security.Cryptography.X509Certificates; using System.Threading; using System.Threading.Tasks; @@ -115,11 +114,6 @@ namespace Emby.Server.Implementations /// </summary> public abstract class ApplicationHost : IServerApplicationHost, IAsyncDisposable, IDisposable { - /// <summary> - /// The environment variable prefixes to log at server startup. - /// </summary> - private static readonly string[] _relevantEnvVarPrefixes = { "JELLYFIN_", "DOTNET_", "ASPNETCORE_" }; - /// <summary> /// The disposable parts. /// </summary> @@ -670,36 +664,6 @@ namespace Emby.Server.Implementations FindParts(); } - public static void LogEnvironmentInfo(ILogger logger, IApplicationPaths appPaths) - { - // Distinct these to prevent users from reporting problems that aren't actually problems - var commandLineArgs = Environment - .GetCommandLineArgs() - .Distinct(); - - // Get all relevant environment variables - var allEnvVars = Environment.GetEnvironmentVariables(); - var relevantEnvVars = new Dictionary<object, object>(); - foreach (var key in allEnvVars.Keys) - { - if (_relevantEnvVarPrefixes.Any(prefix => key.ToString().StartsWith(prefix, StringComparison.OrdinalIgnoreCase))) - { - relevantEnvVars.Add(key, allEnvVars[key]); - } - } - - logger.LogInformation("Environment Variables: {EnvVars}", relevantEnvVars); - logger.LogInformation("Arguments: {Args}", commandLineArgs); - logger.LogInformation("Operating system: {OS}", RuntimeInformation.OSDescription); - logger.LogInformation("Architecture: {Architecture}", RuntimeInformation.OSArchitecture); - logger.LogInformation("64-Bit Process: {Is64Bit}", Environment.Is64BitProcess); - logger.LogInformation("User Interactive: {IsUserInteractive}", Environment.UserInteractive); - logger.LogInformation("Processor count: {ProcessorCount}", Environment.ProcessorCount); - logger.LogInformation("Program data path: {ProgramDataPath}", appPaths.ProgramDataPath); - logger.LogInformation("Web resources path: {WebPath}", appPaths.WebPath); - logger.LogInformation("Application directory: {ApplicationPath}", appPaths.ProgramSystemPath); - } - private X509Certificate2 GetCertificate(string path, string password) { if (string.IsNullOrWhiteSpace(path)) diff --git a/Jellyfin.Server/Helpers/StartupHelpers.cs b/Jellyfin.Server/Helpers/StartupHelpers.cs index f1bb9b2831..9a1299fe20 100644 --- a/Jellyfin.Server/Helpers/StartupHelpers.cs +++ b/Jellyfin.Server/Helpers/StartupHelpers.cs @@ -1,7 +1,10 @@ using System; +using System.Collections.Generic; using System.Globalization; using System.IO; +using System.Linq; using System.Net; +using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text; using System.Threading.Tasks; @@ -22,6 +25,43 @@ namespace Jellyfin.Server.Helpers; /// </summary> public static class StartupHelpers { + private static readonly string[] _relevantEnvVarPrefixes = { "JELLYFIN_", "DOTNET_", "ASPNETCORE_" }; + + /// <summary> + /// Logs relevant environment variables and information about the host. + /// </summary> + /// <param name="logger">The logger to use.</param> + /// <param name="appPaths">The application paths to use.</param> + public static void LogEnvironmentInfo(ILogger logger, IApplicationPaths appPaths) + { + // Distinct these to prevent users from reporting problems that aren't actually problems + var commandLineArgs = Environment + .GetCommandLineArgs() + .Distinct(); + + // Get all relevant environment variables + var allEnvVars = Environment.GetEnvironmentVariables(); + var relevantEnvVars = new Dictionary<object, object>(); + foreach (var key in allEnvVars.Keys) + { + if (_relevantEnvVarPrefixes.Any(prefix => key.ToString()!.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))) + { + relevantEnvVars.Add(key, allEnvVars[key]!); + } + } + + logger.LogInformation("Environment Variables: {EnvVars}", relevantEnvVars); + logger.LogInformation("Arguments: {Args}", commandLineArgs); + logger.LogInformation("Operating system: {OS}", RuntimeInformation.OSDescription); + logger.LogInformation("Architecture: {Architecture}", RuntimeInformation.OSArchitecture); + logger.LogInformation("64-Bit Process: {Is64Bit}", Environment.Is64BitProcess); + logger.LogInformation("User Interactive: {IsUserInteractive}", Environment.UserInteractive); + logger.LogInformation("Processor count: {ProcessorCount}", Environment.ProcessorCount); + logger.LogInformation("Program data path: {ProgramDataPath}", appPaths.ProgramDataPath); + logger.LogInformation("Web resources path: {WebPath}", appPaths.WebPath); + logger.LogInformation("Application directory: {ApplicationPath}", appPaths.ProgramSystemPath); + } + /// <summary> /// Create the data, config and log paths from the variety of inputs(command line args, /// environment variables) or decide on what default to use. For Windows it's %AppPath% diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 25fe30a392..6e8b17a737 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -148,7 +148,7 @@ namespace Jellyfin.Server "Jellyfin version: {Version}", Assembly.GetEntryAssembly()!.GetName().Version!.ToString(3)); - ApplicationHost.LogEnvironmentInfo(_logger, appPaths); + StartupHelpers.LogEnvironmentInfo(_logger, appPaths); // If hosting the web client, validate the client content path if (startupConfig.HostWebClient()) -- cgit v1.2.3 From 990bd7d1eef46dba1e22a83d6144b769680e042c Mon Sep 17 00:00:00 2001 From: Patrick Barron <barronpm@gmail.com> Date: Fri, 27 Jan 2023 18:29:35 -0500 Subject: Initialize device id in constructor --- Emby.Server.Implementations/ApplicationHost.cs | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 4ed055cdf4..c9720de1a5 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -118,6 +118,7 @@ namespace Emby.Server.Implementations /// The disposable parts. /// </summary> private readonly ConcurrentDictionary<IDisposable, byte> _disposableParts = new(); + private readonly DeviceId _deviceId; private readonly IFileSystem _fileSystemManager; private readonly IConfiguration _startupConfig; @@ -135,8 +136,6 @@ namespace Emby.Server.Implementations /// <value>All concrete types.</value> private Type[] _allConcreteTypes; - private DeviceId _deviceId; - private bool _disposed = false; /// <summary> @@ -160,6 +159,7 @@ namespace Emby.Server.Implementations Logger = LoggerFactory.CreateLogger<ApplicationHost>(); _fileSystemManager.AddShortcutHandler(new MbLinkShortcutHandler(_fileSystemManager)); + _deviceId = new DeviceId(ApplicationPaths, LoggerFactory); ApplicationVersion = typeof(ApplicationHost).Assembly.GetName().Version; ApplicationVersionString = ApplicationVersion.ToString(3); @@ -280,15 +280,7 @@ namespace Emby.Server.Implementations /// <value>The application name.</value> public string ApplicationProductName { get; } = FileVersionInfo.GetVersionInfo(Assembly.GetEntryAssembly().Location).ProductName; - public string SystemId - { - get - { - _deviceId ??= new DeviceId(ApplicationPaths, LoggerFactory); - - return _deviceId.Value; - } - } + public string SystemId => _deviceId.Value; /// <inheritdoc/> public string Name => ApplicationProductName; -- cgit v1.2.3 From 8898012121e99e23c0507e396d6a9330fb80831d Mon Sep 17 00:00:00 2001 From: Patrick Barron <barronpm@gmail.com> Date: Fri, 27 Jan 2023 18:33:32 -0500 Subject: Convert CanLaunchWebBrowser to expression body --- Emby.Server.Implementations/ApplicationHost.cs | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index c9720de1a5..2e7386bf32 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -187,23 +187,9 @@ namespace Emby.Server.Implementations public bool CoreStartupHasCompleted { get; private set; } - public virtual bool CanLaunchWebBrowser - { - get - { - if (!Environment.UserInteractive) - { - return false; - } - - if (_startupOptions.IsService) - { - return false; - } - - return OperatingSystem.IsWindows() || OperatingSystem.IsMacOS(); - } - } + public virtual bool CanLaunchWebBrowser => Environment.UserInteractive + && !_startupOptions.IsService + && (OperatingSystem.IsWindows() || OperatingSystem.IsMacOS()); /// <summary> /// Gets the <see cref="INetworkManager"/> singleton instance. -- cgit v1.2.3 From 4f81f4daaa77ac58381a95f1fd32fecd4e0a7d2b Mon Sep 17 00:00:00 2001 From: Patrick Barron <barronpm@gmail.com> Date: Fri, 27 Jan 2023 18:41:10 -0500 Subject: Use depencency injection for ISubtitleProvider --- Emby.Server.Implementations/ApplicationHost.cs | 2 -- MediaBrowser.Controller/Subtitles/ISubtitleManager.cs | 6 ------ MediaBrowser.Providers/Subtitles/SubtitleManager.cs | 16 ++++++---------- 3 files changed, 6 insertions(+), 18 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 2e7386bf32..be4845c29a 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -728,8 +728,6 @@ namespace Emby.Server.Implementations Resolve<ILiveTvManager>().AddParts(GetExports<ILiveTvService>(), GetExports<ITunerHost>(), GetExports<IListingsProvider>()); - Resolve<ISubtitleManager>().AddParts(GetExports<ISubtitleProvider>()); - Resolve<IChannelManager>().AddParts(GetExports<IChannel>()); Resolve<IMediaSourceManager>().AddParts(GetExports<IMediaSourceProvider>()); diff --git a/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs b/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs index 52aa44024d..841b320376 100644 --- a/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs +++ b/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs @@ -19,12 +19,6 @@ namespace MediaBrowser.Controller.Subtitles /// </summary> event EventHandler<SubtitleDownloadFailureEventArgs> SubtitleDownloadFailure; - /// <summary> - /// Adds the parts. - /// </summary> - /// <param name="subtitleProviders">The subtitle providers.</param> - void AddParts(IEnumerable<ISubtitleProvider> subtitleProviders); - /// <summary> /// Searches the subtitles. /// </summary> diff --git a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs index b1a26cfba3..12570f0a7e 100644 --- a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs +++ b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs @@ -35,33 +35,29 @@ namespace MediaBrowser.Providers.Subtitles private readonly IMediaSourceManager _mediaSourceManager; private readonly ILocalizationManager _localization; - private ISubtitleProvider[] _subtitleProviders; + private readonly ISubtitleProvider[] _subtitleProviders; public SubtitleManager( ILogger<SubtitleManager> logger, IFileSystem fileSystem, ILibraryMonitor monitor, IMediaSourceManager mediaSourceManager, - ILocalizationManager localizationManager) + ILocalizationManager localizationManager, + IEnumerable<ISubtitleProvider> subtitleProviders) { _logger = logger; _fileSystem = fileSystem; _monitor = monitor; _mediaSourceManager = mediaSourceManager; _localization = localizationManager; - } - - /// <inheritdoc /> - public event EventHandler<SubtitleDownloadFailureEventArgs> SubtitleDownloadFailure; - - /// <inheritdoc /> - public void AddParts(IEnumerable<ISubtitleProvider> subtitleProviders) - { _subtitleProviders = subtitleProviders .OrderBy(i => i is IHasOrder hasOrder ? hasOrder.Order : 0) .ToArray(); } + /// <inheritdoc /> + public event EventHandler<SubtitleDownloadFailureEventArgs> SubtitleDownloadFailure; + /// <inheritdoc /> public async Task<RemoteSubtitleInfo[]> SearchSubtitles(SubtitleSearchRequest request, CancellationToken cancellationToken) { -- cgit v1.2.3 From f7ec85d7a0d30619721d13064437993d2f3a86d4 Mon Sep 17 00:00:00 2001 From: Patrick Barron <barronpm@gmail.com> Date: Fri, 27 Jan 2023 18:46:54 -0500 Subject: Use dependency injection for IChannel --- Emby.Server.Implementations/ApplicationHost.cs | 2 -- Emby.Server.Implementations/Channels/ChannelManager.cs | 13 +++++-------- MediaBrowser.Controller/Channels/IChannelManager.cs | 6 ------ 3 files changed, 5 insertions(+), 16 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index be4845c29a..3527e40480 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -728,8 +728,6 @@ namespace Emby.Server.Implementations Resolve<ILiveTvManager>().AddParts(GetExports<ILiveTvService>(), GetExports<ITunerHost>(), GetExports<IListingsProvider>()); - Resolve<IChannelManager>().AddParts(GetExports<IChannel>()); - Resolve<IMediaSourceManager>().AddParts(GetExports<IMediaSourceProvider>()); Resolve<INotificationManager>().AddParts(GetExports<INotificationService>(), GetExports<INotificationTypeFactory>()); diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs index 85ccbc0284..84ba194648 100644 --- a/Emby.Server.Implementations/Channels/ChannelManager.cs +++ b/Emby.Server.Implementations/Channels/ChannelManager.cs @@ -66,6 +66,7 @@ namespace Emby.Server.Implementations.Channels /// <param name="userDataManager">The user data manager.</param> /// <param name="providerManager">The provider manager.</param> /// <param name="memoryCache">The memory cache.</param> + /// <param name="channels">The channels.</param> public ChannelManager( IUserManager userManager, IDtoService dtoService, @@ -75,7 +76,8 @@ namespace Emby.Server.Implementations.Channels IFileSystem fileSystem, IUserDataManager userDataManager, IProviderManager providerManager, - IMemoryCache memoryCache) + IMemoryCache memoryCache, + IEnumerable<IChannel> channels) { _userManager = userManager; _dtoService = dtoService; @@ -86,18 +88,13 @@ namespace Emby.Server.Implementations.Channels _userDataManager = userDataManager; _providerManager = providerManager; _memoryCache = memoryCache; + Channels = channels.ToArray(); } - internal IChannel[] Channels { get; private set; } + internal IChannel[] Channels { get; } private static TimeSpan CacheLength => TimeSpan.FromHours(3); - /// <inheritdoc /> - public void AddParts(IEnumerable<IChannel> channels) - { - Channels = channels.ToArray(); - } - /// <inheritdoc /> public bool EnableMediaSourceDisplay(BaseItem item) { diff --git a/MediaBrowser.Controller/Channels/IChannelManager.cs b/MediaBrowser.Controller/Channels/IChannelManager.cs index 49be897ef3..e392a3493e 100644 --- a/MediaBrowser.Controller/Channels/IChannelManager.cs +++ b/MediaBrowser.Controller/Channels/IChannelManager.cs @@ -15,12 +15,6 @@ namespace MediaBrowser.Controller.Channels { public interface IChannelManager { - /// <summary> - /// Adds the parts. - /// </summary> - /// <param name="channels">The channels.</param> - void AddParts(IEnumerable<IChannel> channels); - /// <summary> /// Gets the channel features. /// </summary> -- cgit v1.2.3 From 0bbeead6c7d68bdb09b8b3d0367e9f186042167d Mon Sep 17 00:00:00 2001 From: Patrick Barron <barronpm@gmail.com> Date: Fri, 27 Jan 2023 20:32:15 -0500 Subject: Don't store media encoder as field --- Emby.Server.Implementations/ApplicationHost.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 3527e40480..0b2dd00b96 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -127,7 +127,6 @@ namespace Emby.Server.Implementations private readonly IPluginManager _pluginManager; private List<Type> _creatingInstances; - private IMediaEncoder _mediaEncoder; private ISessionManager _sessionManager; /// <summary> @@ -419,7 +418,7 @@ namespace Emby.Server.Implementations ConfigurationManager.ConfigurationUpdated += OnConfigurationUpdated; ConfigurationManager.NamedConfigurationUpdated += OnConfigurationUpdated; - _mediaEncoder.SetFFmpegPath(); + Resolve<IMediaEncoder>().SetFFmpegPath(); Logger.LogInformation("ServerId: {ServerId}", SystemId); @@ -631,7 +630,6 @@ namespace Emby.Server.Implementations var localizationManager = (LocalizationManager)Resolve<ILocalizationManager>(); await localizationManager.LoadAll().ConfigureAwait(false); - _mediaEncoder = Resolve<IMediaEncoder>(); _sessionManager = Resolve<ISessionManager>(); SetStaticProperties(); -- cgit v1.2.3 From fec23de427fe1c46e4ce1aaf31f1695c90232059 Mon Sep 17 00:00:00 2001 From: Patrick Barron <barronpm@gmail.com> Date: Sat, 28 Jan 2023 09:43:55 -0500 Subject: Remove Emby.Notifications (#9147) --- Emby.Notifications/CoreNotificationTypes.cs | 123 -------- Emby.Notifications/Emby.Notifications.csproj | 35 --- .../NotificationConfigurationFactory.cs | 23 -- Emby.Notifications/NotificationEntryPoint.cs | 314 --------------------- Emby.Notifications/NotificationManager.cs | 224 --------------- Emby.Notifications/Properties/AssemblyInfo.cs | 21 -- Emby.Server.Implementations/ApplicationHost.cs | 9 - .../Emby.Server.Implementations.csproj | 1 - .../Controllers/NotificationsController.cs | 53 ---- Jellyfin.sln | 6 - .../Notifications/INotificationManager.cs | 43 --- .../Notifications/INotificationService.cs | 34 --- .../Notifications/INotificationTypeFactory.cs | 16 -- .../Notifications/UserNotification.cs | 25 -- .../Notifications/NotificationLevel.cs | 11 - .../Notifications/NotificationOption.cs | 55 ---- .../Notifications/NotificationOptions.cs | 131 --------- .../Notifications/NotificationRequest.cs | 35 --- .../Notifications/NotificationTypeInfo.cs | 18 -- MediaBrowser.Model/Notifications/SendToUserType.cs | 11 - 20 files changed, 1188 deletions(-) delete mode 100644 Emby.Notifications/CoreNotificationTypes.cs delete mode 100644 Emby.Notifications/Emby.Notifications.csproj delete mode 100644 Emby.Notifications/NotificationConfigurationFactory.cs delete mode 100644 Emby.Notifications/NotificationEntryPoint.cs delete mode 100644 Emby.Notifications/NotificationManager.cs delete mode 100644 Emby.Notifications/Properties/AssemblyInfo.cs delete mode 100644 Jellyfin.Api/Controllers/NotificationsController.cs delete mode 100644 MediaBrowser.Controller/Notifications/INotificationManager.cs delete mode 100644 MediaBrowser.Controller/Notifications/INotificationService.cs delete mode 100644 MediaBrowser.Controller/Notifications/INotificationTypeFactory.cs delete mode 100644 MediaBrowser.Controller/Notifications/UserNotification.cs delete mode 100644 MediaBrowser.Model/Notifications/NotificationLevel.cs delete mode 100644 MediaBrowser.Model/Notifications/NotificationOption.cs delete mode 100644 MediaBrowser.Model/Notifications/NotificationOptions.cs delete mode 100644 MediaBrowser.Model/Notifications/NotificationRequest.cs delete mode 100644 MediaBrowser.Model/Notifications/NotificationTypeInfo.cs delete mode 100644 MediaBrowser.Model/Notifications/SendToUserType.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Notifications/CoreNotificationTypes.cs b/Emby.Notifications/CoreNotificationTypes.cs deleted file mode 100644 index 35aac3a11c..0000000000 --- a/Emby.Notifications/CoreNotificationTypes.cs +++ /dev/null @@ -1,123 +0,0 @@ -#pragma warning disable CS1591 - -using System; -using System.Collections.Generic; -using System.Linq; -using MediaBrowser.Controller.Notifications; -using MediaBrowser.Model.Globalization; -using MediaBrowser.Model.Notifications; - -namespace Emby.Notifications -{ - public class CoreNotificationTypes : INotificationTypeFactory - { - private readonly ILocalizationManager _localization; - - public CoreNotificationTypes(ILocalizationManager localization) - { - _localization = localization; - } - - public IEnumerable<NotificationTypeInfo> GetNotificationTypes() - { - var knownTypes = new NotificationTypeInfo[] - { - new NotificationTypeInfo - { - Type = nameof(NotificationType.ApplicationUpdateInstalled) - }, - new NotificationTypeInfo - { - Type = nameof(NotificationType.InstallationFailed) - }, - new NotificationTypeInfo - { - Type = nameof(NotificationType.PluginInstalled) - }, - new NotificationTypeInfo - { - Type = nameof(NotificationType.PluginError) - }, - new NotificationTypeInfo - { - Type = nameof(NotificationType.PluginUninstalled) - }, - new NotificationTypeInfo - { - Type = nameof(NotificationType.PluginUpdateInstalled) - }, - new NotificationTypeInfo - { - Type = nameof(NotificationType.ServerRestartRequired) - }, - new NotificationTypeInfo - { - Type = nameof(NotificationType.TaskFailed) - }, - new NotificationTypeInfo - { - Type = nameof(NotificationType.NewLibraryContent) - }, - new NotificationTypeInfo - { - Type = nameof(NotificationType.AudioPlayback) - }, - new NotificationTypeInfo - { - Type = nameof(NotificationType.VideoPlayback) - }, - new NotificationTypeInfo - { - Type = nameof(NotificationType.AudioPlaybackStopped) - }, - new NotificationTypeInfo - { - Type = nameof(NotificationType.VideoPlaybackStopped) - }, - new NotificationTypeInfo - { - Type = nameof(NotificationType.UserLockedOut) - }, - new NotificationTypeInfo - { - Type = nameof(NotificationType.ApplicationUpdateAvailable) - } - }; - - foreach (var type in knownTypes) - { - Update(type); - } - - var systemName = _localization.GetLocalizedString("System"); - - return knownTypes.OrderByDescending(i => string.Equals(i.Category, systemName, StringComparison.OrdinalIgnoreCase)) - .ThenBy(i => i.Category) - .ThenBy(i => i.Name); - } - - private void Update(NotificationTypeInfo note) - { - note.Name = _localization.GetLocalizedString("NotificationOption" + note.Type); - - note.IsBasedOnUserEvent = note.Type.IndexOf("Playback", StringComparison.OrdinalIgnoreCase) != -1; - - if (note.Type.IndexOf("Playback", StringComparison.OrdinalIgnoreCase) != -1) - { - note.Category = _localization.GetLocalizedString("User"); - } - else if (note.Type.IndexOf("Plugin", StringComparison.OrdinalIgnoreCase) != -1) - { - note.Category = _localization.GetLocalizedString("Plugin"); - } - else if (note.Type.IndexOf("UserLockedOut", StringComparison.OrdinalIgnoreCase) != -1) - { - note.Category = _localization.GetLocalizedString("User"); - } - else - { - note.Category = _localization.GetLocalizedString("System"); - } - } - } -} diff --git a/Emby.Notifications/Emby.Notifications.csproj b/Emby.Notifications/Emby.Notifications.csproj deleted file mode 100644 index eb269183e9..0000000000 --- a/Emby.Notifications/Emby.Notifications.csproj +++ /dev/null @@ -1,35 +0,0 @@ -<Project Sdk="Microsoft.NET.Sdk"> - - <!-- ProjectGuid is only included as a requirement for SonarQube analysis --> - <PropertyGroup> - <ProjectGuid>{2E030C33-6923-4530-9E54-FA29FA6AD1A9}</ProjectGuid> - </PropertyGroup> - - <PropertyGroup> - <TargetFramework>net7.0</TargetFramework> - <GenerateAssemblyInfo>false</GenerateAssemblyInfo> - <GenerateDocumentationFile>true</GenerateDocumentationFile> - </PropertyGroup> - - <ItemGroup> - <Compile Include="..\SharedVersion.cs" /> - </ItemGroup> - - <ItemGroup> - <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" /> - <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" /> - <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" /> - </ItemGroup> - - <!-- Code analyzers--> - <ItemGroup Condition=" '$(Configuration)' == 'Debug' "> - <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4"> - <PrivateAssets>all</PrivateAssets> - <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> - </PackageReference> - <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" /> - <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="All" /> - <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" /> - </ItemGroup> - -</Project> diff --git a/Emby.Notifications/NotificationConfigurationFactory.cs b/Emby.Notifications/NotificationConfigurationFactory.cs deleted file mode 100644 index 3fb3553d0e..0000000000 --- a/Emby.Notifications/NotificationConfigurationFactory.cs +++ /dev/null @@ -1,23 +0,0 @@ -#pragma warning disable CS1591 - -using System.Collections.Generic; -using MediaBrowser.Common.Configuration; -using MediaBrowser.Model.Notifications; - -namespace Emby.Notifications -{ - public class NotificationConfigurationFactory : IConfigurationFactory - { - public IEnumerable<ConfigurationStore> GetConfigurations() - { - return new ConfigurationStore[] - { - new ConfigurationStore - { - Key = "notifications", - ConfigurationType = typeof(NotificationOptions) - } - }; - } - } -} diff --git a/Emby.Notifications/NotificationEntryPoint.cs b/Emby.Notifications/NotificationEntryPoint.cs deleted file mode 100644 index 3763b1e922..0000000000 --- a/Emby.Notifications/NotificationEntryPoint.cs +++ /dev/null @@ -1,314 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Jellyfin.Data.Events; -using Jellyfin.Extensions; -using MediaBrowser.Common.Configuration; -using MediaBrowser.Controller; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Notifications; -using MediaBrowser.Controller.Plugins; -using MediaBrowser.Model.Activity; -using MediaBrowser.Model.Globalization; -using MediaBrowser.Model.Notifications; -using Microsoft.Extensions.Logging; - -namespace Emby.Notifications -{ - /// <summary> - /// Creates notifications for various system events. - /// </summary> - public class NotificationEntryPoint : IServerEntryPoint - { - private readonly ILogger<NotificationEntryPoint> _logger; - private readonly IActivityManager _activityManager; - private readonly ILocalizationManager _localization; - private readonly INotificationManager _notificationManager; - private readonly ILibraryManager _libraryManager; - private readonly IServerApplicationHost _appHost; - private readonly IConfigurationManager _config; - - private readonly object _libraryChangedSyncLock = new object(); - private readonly List<BaseItem> _itemsAdded = new List<BaseItem>(); - - private Timer? _libraryUpdateTimer; - - private string[] _coreNotificationTypes; - - private bool _disposed = false; - - /// <summary> - /// Initializes a new instance of the <see cref="NotificationEntryPoint" /> class. - /// </summary> - /// <param name="logger">The logger.</param> - /// <param name="activityManager">The activity manager.</param> - /// <param name="localization">The localization manager.</param> - /// <param name="notificationManager">The notification manager.</param> - /// <param name="libraryManager">The library manager.</param> - /// <param name="appHost">The application host.</param> - /// <param name="config">The configuration manager.</param> - public NotificationEntryPoint( - ILogger<NotificationEntryPoint> logger, - IActivityManager activityManager, - ILocalizationManager localization, - INotificationManager notificationManager, - ILibraryManager libraryManager, - IServerApplicationHost appHost, - IConfigurationManager config) - { - _logger = logger; - _activityManager = activityManager; - _localization = localization; - _notificationManager = notificationManager; - _libraryManager = libraryManager; - _appHost = appHost; - _config = config; - - _coreNotificationTypes = new CoreNotificationTypes(localization).GetNotificationTypes().Select(i => i.Type).ToArray(); - } - - /// <inheritdoc /> - public Task RunAsync() - { - _libraryManager.ItemAdded += OnLibraryManagerItemAdded; - _appHost.HasPendingRestartChanged += OnAppHostHasPendingRestartChanged; - _activityManager.EntryCreated += OnActivityManagerEntryCreated; - - return Task.CompletedTask; - } - - private async void OnAppHostHasPendingRestartChanged(object? sender, EventArgs e) - { - var type = NotificationType.ServerRestartRequired.ToString(); - - var notification = new NotificationRequest - { - NotificationType = type, - Name = string.Format( - CultureInfo.InvariantCulture, - _localization.GetLocalizedString("ServerNameNeedsToBeRestarted"), - _appHost.Name) - }; - - await SendNotification(notification, null).ConfigureAwait(false); - } - - private async void OnActivityManagerEntryCreated(object? sender, GenericEventArgs<ActivityLogEntry> e) - { - var entry = e.Argument; - - var type = entry.Type; - - if (string.IsNullOrEmpty(type) || !_coreNotificationTypes.Contains(type, StringComparison.OrdinalIgnoreCase)) - { - return; - } - - var userId = e.Argument.UserId; - - if (!userId.Equals(default) && !GetOptions().IsEnabledToMonitorUser(type, userId)) - { - return; - } - - var notification = new NotificationRequest - { - NotificationType = type, - Name = entry.Name, - Description = entry.Overview - }; - - await SendNotification(notification, null).ConfigureAwait(false); - } - - private NotificationOptions GetOptions() - { - return _config.GetConfiguration<NotificationOptions>("notifications"); - } - - private void OnLibraryManagerItemAdded(object? sender, ItemChangeEventArgs e) - { - if (!FilterItem(e.Item)) - { - return; - } - - lock (_libraryChangedSyncLock) - { - if (_libraryUpdateTimer is null) - { - _libraryUpdateTimer = new Timer( - LibraryUpdateTimerCallback, - null, - 5000, - Timeout.Infinite); - } - else - { - _libraryUpdateTimer.Change(5000, Timeout.Infinite); - } - - _itemsAdded.Add(e.Item); - } - } - - private bool FilterItem(BaseItem item) - { - if (item.IsFolder) - { - return false; - } - - if (!item.HasPathProtocol) - { - return false; - } - - if (item is IItemByName) - { - return false; - } - - return item.SourceType == SourceType.Library; - } - - private async void LibraryUpdateTimerCallback(object? state) - { - List<BaseItem> items; - - lock (_libraryChangedSyncLock) - { - items = _itemsAdded.ToList(); - _itemsAdded.Clear(); - _libraryUpdateTimer!.Dispose(); // Shouldn't be null as it just set off this callback - _libraryUpdateTimer = null; - } - - if (items.Count > 10) - { - items = items.GetRange(0, 10); - } - - foreach (var item in items) - { - var notification = new NotificationRequest - { - NotificationType = NotificationType.NewLibraryContent.ToString(), - Name = string.Format( - CultureInfo.InvariantCulture, - _localization.GetLocalizedString("ValueHasBeenAddedToLibrary"), - GetItemName(item)), - Description = item.Overview - }; - - await SendNotification(notification, item).ConfigureAwait(false); - } - } - - /// <summary> - /// Creates a human readable name for the item. - /// </summary> - /// <param name="item">The item.</param> - /// <returns>A human readable name for the item.</returns> - public static string GetItemName(BaseItem item) - { - var name = item.Name; - if (item is Episode episode) - { - if (episode.IndexNumber.HasValue) - { - name = string.Format( - CultureInfo.InvariantCulture, - "Ep{0} - {1}", - episode.IndexNumber.Value, - name); - } - - if (episode.ParentIndexNumber.HasValue) - { - name = string.Format( - CultureInfo.InvariantCulture, - "S{0}, {1}", - episode.ParentIndexNumber.Value, - name); - } - } - - if (item is IHasSeries hasSeries) - { - name = hasSeries.SeriesName + " - " + name; - } - - if (item is IHasAlbumArtist hasAlbumArtist) - { - var artists = hasAlbumArtist.AlbumArtists; - - if (artists.Count > 0) - { - name = artists[0] + " - " + name; - } - } - else if (item is IHasArtist hasArtist) - { - var artists = hasArtist.Artists; - - if (artists.Count > 0) - { - name = artists[0] + " - " + name; - } - } - - return name; - } - - private async Task SendNotification(NotificationRequest notification, BaseItem? relatedItem) - { - try - { - await _notificationManager.SendNotification(notification, relatedItem, CancellationToken.None).ConfigureAwait(false); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error sending notification"); - } - } - - /// <inheritdoc /> - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// <summary> - /// Releases unmanaged and optionally managed resources. - /// </summary> - /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> - protected virtual void Dispose(bool disposing) - { - if (_disposed) - { - return; - } - - if (disposing) - { - _libraryUpdateTimer?.Dispose(); - } - - _libraryUpdateTimer = null; - - _libraryManager.ItemAdded -= OnLibraryManagerItemAdded; - _appHost.HasPendingRestartChanged -= OnAppHostHasPendingRestartChanged; - _activityManager.EntryCreated -= OnActivityManagerEntryCreated; - - _disposed = true; - } - } -} diff --git a/Emby.Notifications/NotificationManager.cs b/Emby.Notifications/NotificationManager.cs deleted file mode 100644 index a5a76b2af7..0000000000 --- a/Emby.Notifications/NotificationManager.cs +++ /dev/null @@ -1,224 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Jellyfin.Data.Entities; -using Jellyfin.Data.Enums; -using MediaBrowser.Common.Configuration; -using MediaBrowser.Common.Extensions; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Notifications; -using MediaBrowser.Model.Dto; -using MediaBrowser.Model.Notifications; -using Microsoft.Extensions.Logging; - -namespace Emby.Notifications -{ - /// <summary> - /// NotificationManager class. - /// </summary> - public class NotificationManager : INotificationManager - { - private readonly ILogger<NotificationManager> _logger; - private readonly IUserManager _userManager; - private readonly IServerConfigurationManager _config; - - private INotificationService[] _services = Array.Empty<INotificationService>(); - private INotificationTypeFactory[] _typeFactories = Array.Empty<INotificationTypeFactory>(); - - /// <summary> - /// Initializes a new instance of the <see cref="NotificationManager" /> class. - /// </summary> - /// <param name="logger">The logger.</param> - /// <param name="userManager">The user manager.</param> - /// <param name="config">The server configuration manager.</param> - public NotificationManager( - ILogger<NotificationManager> logger, - IUserManager userManager, - IServerConfigurationManager config) - { - _logger = logger; - _userManager = userManager; - _config = config; - } - - private NotificationOptions GetConfiguration() - { - return _config.GetConfiguration<NotificationOptions>("notifications"); - } - - /// <inheritdoc /> - public Task SendNotification(NotificationRequest request, CancellationToken cancellationToken) - { - return SendNotification(request, null, cancellationToken); - } - - /// <inheritdoc /> - public Task SendNotification(NotificationRequest request, BaseItem? relatedItem, CancellationToken cancellationToken) - { - var notificationType = request.NotificationType; - - var options = string.IsNullOrEmpty(notificationType) ? - null : - GetConfiguration().GetOptions(notificationType); - - var users = GetUserIds(request, options) - .Select(i => _userManager.GetUserById(i)) - .Where(i => relatedItem is null || relatedItem.IsVisibleStandalone(i)) - .ToArray(); - - var title = request.Name; - var description = request.Description; - - var tasks = _services.Where(i => IsEnabled(i, notificationType)) - .Select(i => SendNotification(request, i, users, title, description, cancellationToken)); - - return Task.WhenAll(tasks); - } - - private Task SendNotification( - NotificationRequest request, - INotificationService service, - IEnumerable<User> users, - string title, - string description, - CancellationToken cancellationToken) - { - users = users.Where(i => IsEnabledForUser(service, i)); - - var tasks = users.Select(i => SendNotification(request, service, title, description, i, cancellationToken)); - - return Task.WhenAll(tasks); - } - - private IEnumerable<Guid> GetUserIds(NotificationRequest request, NotificationOption? options) - { - if (request.SendToUserMode.HasValue) - { - switch (request.SendToUserMode.Value) - { - case SendToUserType.Admins: - return _userManager.Users.Where(i => i.HasPermission(PermissionKind.IsAdministrator)) - .Select(i => i.Id); - case SendToUserType.All: - return _userManager.UsersIds; - case SendToUserType.Custom: - return request.UserIds; - default: - throw new ArgumentException("Unrecognized SendToUserMode: " + request.SendToUserMode.Value); - } - } - - if (options is not null && !string.IsNullOrEmpty(request.NotificationType)) - { - var config = GetConfiguration(); - - return _userManager.Users - .Where(i => config.IsEnabledToSendToUser(request.NotificationType, i.Id.ToString("N", CultureInfo.InvariantCulture), i)) - .Select(i => i.Id); - } - - return request.UserIds; - } - - private async Task SendNotification( - NotificationRequest request, - INotificationService service, - string title, - string description, - User user, - CancellationToken cancellationToken) - { - var notification = new UserNotification - { - Date = request.Date, - Description = description, - Level = request.Level, - Name = title, - Url = request.Url, - User = user - }; - - _logger.LogDebug("Sending notification via {0} to user {1}", service.Name, user.Username); - - try - { - await service.SendNotification(notification, cancellationToken).ConfigureAwait(false); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error sending notification to {0}", service.Name); - } - } - - private bool IsEnabledForUser(INotificationService service, User user) - { - try - { - return service.IsEnabledForUser(user); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error in IsEnabledForUser"); - return false; - } - } - - private bool IsEnabled(INotificationService service, string notificationType) - { - if (string.IsNullOrEmpty(notificationType)) - { - return true; - } - - return GetConfiguration().IsServiceEnabled(service.Name, notificationType); - } - - /// <inheritdoc /> - public void AddParts(IEnumerable<INotificationService> services, IEnumerable<INotificationTypeFactory> notificationTypeFactories) - { - _services = services.ToArray(); - _typeFactories = notificationTypeFactories.ToArray(); - } - - /// <inheritdoc /> - public List<NotificationTypeInfo> GetNotificationTypes() - { - var list = _typeFactories.Select(i => - { - try - { - return i.GetNotificationTypes().ToList(); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error in GetNotificationTypes"); - return new List<NotificationTypeInfo>(); - } - }).SelectMany(i => i).ToList(); - - var config = GetConfiguration(); - - foreach (var i in list) - { - i.Enabled = config.IsEnabled(i.Type); - } - - return list; - } - - /// <inheritdoc /> - public IEnumerable<NameIdPair> GetNotificationServices() - { - return _services.Select(i => new NameIdPair - { - Name = i.Name, - Id = i.Name.GetMD5().ToString("N", CultureInfo.InvariantCulture) - }).OrderBy(i => i.Name); - } - } -} diff --git a/Emby.Notifications/Properties/AssemblyInfo.cs b/Emby.Notifications/Properties/AssemblyInfo.cs deleted file mode 100644 index 5c82c90c47..0000000000 --- a/Emby.Notifications/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Reflection; -using System.Resources; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Emby.Notifications")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Jellyfin Project")] -[assembly: AssemblyProduct("Jellyfin Server")] -[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] -[assembly: NeutralResourcesLanguage("en")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 560ba7d10d..07b0807b72 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -19,7 +19,6 @@ using Emby.Dlna; using Emby.Dlna.Main; using Emby.Dlna.Ssdp; using Emby.Naming.Common; -using Emby.Notifications; using Emby.Photos; using Emby.Server.Implementations.Channels; using Emby.Server.Implementations.Collections; @@ -70,7 +69,6 @@ using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.Lyrics; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Net; -using MediaBrowser.Controller.Notifications; using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Playlists; using MediaBrowser.Controller.Plugins; @@ -615,8 +613,6 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton<IUserViewManager, UserViewManager>(); - serviceCollection.AddSingleton<INotificationManager, NotificationManager>(); - serviceCollection.AddSingleton<IDeviceDiscovery, DeviceDiscovery>(); serviceCollection.AddSingleton<IChapterManager, ChapterManager>(); @@ -791,8 +787,6 @@ namespace Emby.Server.Implementations Resolve<IChannelManager>().AddParts(GetExports<IChannel>()); Resolve<IMediaSourceManager>().AddParts(GetExports<IMediaSourceProvider>()); - - Resolve<INotificationManager>().AddParts(GetExports<INotificationService>(), GetExports<INotificationTypeFactory>()); } /// <summary> @@ -991,9 +985,6 @@ namespace Emby.Server.Implementations // Local metadata yield return typeof(BoxSetXmlSaver).Assembly; - // Notifications - yield return typeof(NotificationManager).Assembly; - // Xbmc yield return typeof(ArtistNfoProvider).Assembly; diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 7eaef094b5..1b5c879beb 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -7,7 +7,6 @@ <ItemGroup> <ProjectReference Include="..\Emby.Naming\Emby.Naming.csproj" /> - <ProjectReference Include="..\Emby.Notifications\Emby.Notifications.csproj" /> <ProjectReference Include="..\Jellyfin.Api\Jellyfin.Api.csproj" /> <ProjectReference Include="..\Jellyfin.Server.Implementations\Jellyfin.Server.Implementations.csproj" /> <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" /> diff --git a/Jellyfin.Api/Controllers/NotificationsController.cs b/Jellyfin.Api/Controllers/NotificationsController.cs deleted file mode 100644 index a285564760..0000000000 --- a/Jellyfin.Api/Controllers/NotificationsController.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System.Collections.Generic; -using Jellyfin.Api.Constants; -using MediaBrowser.Controller.Notifications; -using MediaBrowser.Model.Dto; -using MediaBrowser.Model.Notifications; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; - -namespace Jellyfin.Api.Controllers -{ - /// <summary> - /// The notification controller. - /// </summary> - [Authorize(Policy = Policies.DefaultAuthorization)] - public class NotificationsController : BaseJellyfinApiController - { - private readonly INotificationManager _notificationManager; - - /// <summary> - /// Initializes a new instance of the <see cref="NotificationsController" /> class. - /// </summary> - /// <param name="notificationManager">The notification manager.</param> - public NotificationsController(INotificationManager notificationManager) - { - _notificationManager = notificationManager; - } - - /// <summary> - /// Gets notification types. - /// </summary> - /// <response code="200">All notification types returned.</response> - /// <returns>An <cref see="OkResult"/> containing a list of all notification types.</returns> - [HttpGet("Types")] - [ProducesResponseType(StatusCodes.Status200OK)] - public IEnumerable<NotificationTypeInfo> GetNotificationTypes() - { - return _notificationManager.GetNotificationTypes(); - } - - /// <summary> - /// Gets notification services. - /// </summary> - /// <response code="200">All notification services returned.</response> - /// <returns>An <cref see="OkResult"/> containing a list of all notification services.</returns> - [HttpGet("Services")] - [ProducesResponseType(StatusCodes.Status200OK)] - public IEnumerable<NameIdPair> GetNotificationServices() - { - return _notificationManager.GetNotificationServices(); - } - } -} diff --git a/Jellyfin.sln b/Jellyfin.sln index f3fb7efa98..cad23fc5ee 100644 --- a/Jellyfin.sln +++ b/Jellyfin.sln @@ -27,8 +27,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RSSDP", "RSSDP\RSSDP.csproj EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emby.Dlna", "Emby.Dlna\Emby.Dlna.csproj", "{805844AB-E92F-45E6-9D99-4F6D48D129A5}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emby.Notifications", "Emby.Notifications\Emby.Notifications.csproj", "{2E030C33-6923-4530-9E54-FA29FA6AD1A9}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emby.Naming", "Emby.Naming\Emby.Naming.csproj", "{E5AF7B26-2239-4CE0-B477-0AA2018EDAA2}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MediaBrowser.MediaEncoding", "MediaBrowser.MediaEncoding\MediaBrowser.MediaEncoding.csproj", "{960295EE-4AF4-4440-A525-B4C295B01A61}" @@ -149,10 +147,6 @@ Global {805844AB-E92F-45E6-9D99-4F6D48D129A5}.Debug|Any CPU.Build.0 = Debug|Any CPU {805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release|Any CPU.ActiveCfg = Release|Any CPU {805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release|Any CPU.Build.0 = Release|Any CPU - {2E030C33-6923-4530-9E54-FA29FA6AD1A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2E030C33-6923-4530-9E54-FA29FA6AD1A9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2E030C33-6923-4530-9E54-FA29FA6AD1A9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2E030C33-6923-4530-9E54-FA29FA6AD1A9}.Release|Any CPU.Build.0 = Release|Any CPU {E5AF7B26-2239-4CE0-B477-0AA2018EDAA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E5AF7B26-2239-4CE0-B477-0AA2018EDAA2}.Debug|Any CPU.Build.0 = Debug|Any CPU {E5AF7B26-2239-4CE0-B477-0AA2018EDAA2}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/MediaBrowser.Controller/Notifications/INotificationManager.cs b/MediaBrowser.Controller/Notifications/INotificationManager.cs deleted file mode 100644 index 7caba1097a..0000000000 --- a/MediaBrowser.Controller/Notifications/INotificationManager.cs +++ /dev/null @@ -1,43 +0,0 @@ -#pragma warning disable CS1591 - -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Model.Dto; -using MediaBrowser.Model.Notifications; - -namespace MediaBrowser.Controller.Notifications -{ - public interface INotificationManager - { - /// <summary> - /// Sends the notification. - /// </summary> - /// <param name="request">The request.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - Task SendNotification(NotificationRequest request, CancellationToken cancellationToken); - - Task SendNotification(NotificationRequest request, BaseItem? relatedItem, CancellationToken cancellationToken); - - /// <summary> - /// Adds the parts. - /// </summary> - /// <param name="services">The services.</param> - /// <param name="notificationTypeFactories">The notification type factories.</param> - void AddParts(IEnumerable<INotificationService> services, IEnumerable<INotificationTypeFactory> notificationTypeFactories); - - /// <summary> - /// Gets the notification types. - /// </summary> - /// <returns>IEnumerable{NotificationTypeInfo}.</returns> - List<NotificationTypeInfo> GetNotificationTypes(); - - /// <summary> - /// Gets the notification services. - /// </summary> - /// <returns>IEnumerable{NotificationServiceInfo}.</returns> - IEnumerable<NameIdPair> GetNotificationServices(); - } -} diff --git a/MediaBrowser.Controller/Notifications/INotificationService.cs b/MediaBrowser.Controller/Notifications/INotificationService.cs deleted file mode 100644 index 535c08795b..0000000000 --- a/MediaBrowser.Controller/Notifications/INotificationService.cs +++ /dev/null @@ -1,34 +0,0 @@ -#nullable disable - -#pragma warning disable CS1591 - -using System.Threading; -using System.Threading.Tasks; -using Jellyfin.Data.Entities; - -namespace MediaBrowser.Controller.Notifications -{ - public interface INotificationService - { - /// <summary> - /// Gets the name. - /// </summary> - /// <value>The name.</value> - string Name { get; } - - /// <summary> - /// Sends the notification. - /// </summary> - /// <param name="request">The request.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - Task SendNotification(UserNotification request, CancellationToken cancellationToken); - - /// <summary> - /// Determines whether [is enabled for user] [the specified user identifier]. - /// </summary> - /// <param name="user">The user.</param> - /// <returns><c>true</c> if [is enabled for user] [the specified user identifier]; otherwise, <c>false</c>.</returns> - bool IsEnabledForUser(User user); - } -} diff --git a/MediaBrowser.Controller/Notifications/INotificationTypeFactory.cs b/MediaBrowser.Controller/Notifications/INotificationTypeFactory.cs deleted file mode 100644 index 52a3e120b5..0000000000 --- a/MediaBrowser.Controller/Notifications/INotificationTypeFactory.cs +++ /dev/null @@ -1,16 +0,0 @@ -#pragma warning disable CS1591 - -using System.Collections.Generic; -using MediaBrowser.Model.Notifications; - -namespace MediaBrowser.Controller.Notifications -{ - public interface INotificationTypeFactory - { - /// <summary> - /// Gets the notification types. - /// </summary> - /// <returns>IEnumerable{NotificationTypeInfo}.</returns> - IEnumerable<NotificationTypeInfo> GetNotificationTypes(); - } -} diff --git a/MediaBrowser.Controller/Notifications/UserNotification.cs b/MediaBrowser.Controller/Notifications/UserNotification.cs deleted file mode 100644 index 4be0e09ae7..0000000000 --- a/MediaBrowser.Controller/Notifications/UserNotification.cs +++ /dev/null @@ -1,25 +0,0 @@ -#nullable disable - -#pragma warning disable CS1591 - -using System; -using Jellyfin.Data.Entities; -using MediaBrowser.Model.Notifications; - -namespace MediaBrowser.Controller.Notifications -{ - public class UserNotification - { - public string Name { get; set; } - - public string Description { get; set; } - - public string Url { get; set; } - - public NotificationLevel Level { get; set; } - - public DateTime Date { get; set; } - - public User User { get; set; } - } -} diff --git a/MediaBrowser.Model/Notifications/NotificationLevel.cs b/MediaBrowser.Model/Notifications/NotificationLevel.cs deleted file mode 100644 index 14fead3f01..0000000000 --- a/MediaBrowser.Model/Notifications/NotificationLevel.cs +++ /dev/null @@ -1,11 +0,0 @@ -#pragma warning disable CS1591 - -namespace MediaBrowser.Model.Notifications -{ - public enum NotificationLevel - { - Normal = 0, - Warning = 1, - Error = 2 - } -} diff --git a/MediaBrowser.Model/Notifications/NotificationOption.cs b/MediaBrowser.Model/Notifications/NotificationOption.cs deleted file mode 100644 index 58aecb3d35..0000000000 --- a/MediaBrowser.Model/Notifications/NotificationOption.cs +++ /dev/null @@ -1,55 +0,0 @@ -#pragma warning disable CA1819 // Properties should not return arrays -#pragma warning disable CS1591 - -using System; - -namespace MediaBrowser.Model.Notifications -{ - public class NotificationOption - { - public NotificationOption(string type) - { - Type = type; - DisabledServices = Array.Empty<string>(); - DisabledMonitorUsers = Array.Empty<string>(); - SendToUsers = Array.Empty<string>(); - } - - public NotificationOption() - { - DisabledServices = Array.Empty<string>(); - DisabledMonitorUsers = Array.Empty<string>(); - SendToUsers = Array.Empty<string>(); - } - - public string? Type { get; set; } - - /// <summary> - /// Gets or sets user Ids to not monitor (it's opt out). - /// </summary> - public string[] DisabledMonitorUsers { get; set; } - - /// <summary> - /// Gets or sets user Ids to send to (if SendToUserMode == Custom). - /// </summary> - public string[] SendToUsers { get; set; } - - /// <summary> - /// Gets or sets a value indicating whether this <see cref="NotificationOption"/> is enabled. - /// </summary> - /// <value><c>true</c> if enabled; otherwise, <c>false</c>.</value> - public bool Enabled { get; set; } - - /// <summary> - /// Gets or sets the disabled services. - /// </summary> - /// <value>The disabled services.</value> - public string[] DisabledServices { get; set; } - - /// <summary> - /// Gets or sets the send to user mode. - /// </summary> - /// <value>The send to user mode.</value> - public SendToUserType SendToUserMode { get; set; } - } -} diff --git a/MediaBrowser.Model/Notifications/NotificationOptions.cs b/MediaBrowser.Model/Notifications/NotificationOptions.cs deleted file mode 100644 index 804f51e166..0000000000 --- a/MediaBrowser.Model/Notifications/NotificationOptions.cs +++ /dev/null @@ -1,131 +0,0 @@ -#nullable disable -#pragma warning disable CS1591 - -using System; -using Jellyfin.Data.Entities; -using Jellyfin.Data.Enums; -using Jellyfin.Extensions; - -namespace MediaBrowser.Model.Notifications -{ - public class NotificationOptions - { - public NotificationOptions() - { - Options = new[] - { - new NotificationOption(NotificationType.TaskFailed.ToString()) - { - Enabled = true, - SendToUserMode = SendToUserType.Admins - }, - new NotificationOption(NotificationType.ServerRestartRequired.ToString()) - { - Enabled = true, - SendToUserMode = SendToUserType.Admins - }, - new NotificationOption(NotificationType.ApplicationUpdateAvailable.ToString()) - { - Enabled = true, - SendToUserMode = SendToUserType.Admins - }, - new NotificationOption(NotificationType.ApplicationUpdateInstalled.ToString()) - { - Enabled = true, - SendToUserMode = SendToUserType.Admins - }, - new NotificationOption(NotificationType.PluginUpdateInstalled.ToString()) - { - Enabled = true, - SendToUserMode = SendToUserType.Admins - }, - new NotificationOption(NotificationType.PluginUninstalled.ToString()) - { - Enabled = true, - SendToUserMode = SendToUserType.Admins - }, - new NotificationOption(NotificationType.InstallationFailed.ToString()) - { - Enabled = true, - SendToUserMode = SendToUserType.Admins - }, - new NotificationOption(NotificationType.PluginInstalled.ToString()) - { - Enabled = true, - SendToUserMode = SendToUserType.Admins - }, - new NotificationOption(NotificationType.PluginError.ToString()) - { - Enabled = true, - SendToUserMode = SendToUserType.Admins - }, - new NotificationOption(NotificationType.UserLockedOut.ToString()) - { - Enabled = true, - SendToUserMode = SendToUserType.Admins - } - }; - } - - public NotificationOption[] Options { get; set; } - - public NotificationOption GetOptions(string type) - { - foreach (NotificationOption i in Options) - { - if (string.Equals(type, i.Type, StringComparison.OrdinalIgnoreCase)) - { - return i; - } - } - - return null; - } - - public bool IsEnabled(string type) - { - NotificationOption opt = GetOptions(type); - - return opt is not null && opt.Enabled; - } - - public bool IsServiceEnabled(string service, string notificationType) - { - NotificationOption opt = GetOptions(notificationType); - - return opt is null - || !opt.DisabledServices.Contains(service, StringComparison.OrdinalIgnoreCase); - } - - public bool IsEnabledToMonitorUser(string type, Guid userId) - { - NotificationOption opt = GetOptions(type); - - return opt is not null - && opt.Enabled - && !opt.DisabledMonitorUsers.Contains(userId.ToString("N"), StringComparison.OrdinalIgnoreCase); - } - - public bool IsEnabledToSendToUser(string type, string userId, User user) - { - NotificationOption opt = GetOptions(type); - - if (opt is not null && opt.Enabled) - { - if (opt.SendToUserMode == SendToUserType.All) - { - return true; - } - - if (opt.SendToUserMode == SendToUserType.Admins && user.HasPermission(PermissionKind.IsAdministrator)) - { - return true; - } - - return opt.SendToUsers.Contains(userId, StringComparison.OrdinalIgnoreCase); - } - - return false; - } - } -} diff --git a/MediaBrowser.Model/Notifications/NotificationRequest.cs b/MediaBrowser.Model/Notifications/NotificationRequest.cs deleted file mode 100644 index 622c50cd88..0000000000 --- a/MediaBrowser.Model/Notifications/NotificationRequest.cs +++ /dev/null @@ -1,35 +0,0 @@ -#nullable disable -#pragma warning disable CS1591 - -using System; - -namespace MediaBrowser.Model.Notifications -{ - public class NotificationRequest - { - public NotificationRequest() - { - UserIds = Array.Empty<Guid>(); - Date = DateTime.UtcNow; - } - - public string Name { get; set; } - - public string Description { get; set; } - - public string Url { get; set; } - - public NotificationLevel Level { get; set; } - - public Guid[] UserIds { get; set; } - - public DateTime Date { get; set; } - - /// <summary> - /// Gets or sets the corresponding type name used in configuration. Not for display. - /// </summary> - public string NotificationType { get; set; } - - public SendToUserType? SendToUserMode { get; set; } - } -} diff --git a/MediaBrowser.Model/Notifications/NotificationTypeInfo.cs b/MediaBrowser.Model/Notifications/NotificationTypeInfo.cs deleted file mode 100644 index 402fbe81a0..0000000000 --- a/MediaBrowser.Model/Notifications/NotificationTypeInfo.cs +++ /dev/null @@ -1,18 +0,0 @@ -#nullable disable -#pragma warning disable CS1591 - -namespace MediaBrowser.Model.Notifications -{ - public class NotificationTypeInfo - { - public string Type { get; set; } - - public string Name { get; set; } - - public bool Enabled { get; set; } - - public string Category { get; set; } - - public bool IsBasedOnUserEvent { get; set; } - } -} diff --git a/MediaBrowser.Model/Notifications/SendToUserType.cs b/MediaBrowser.Model/Notifications/SendToUserType.cs deleted file mode 100644 index 65fc4e1abd..0000000000 --- a/MediaBrowser.Model/Notifications/SendToUserType.cs +++ /dev/null @@ -1,11 +0,0 @@ -#pragma warning disable CS1591 - -namespace MediaBrowser.Model.Notifications -{ - public enum SendToUserType - { - All = 0, - Admins = 1, - Custom = 2 - } -} -- cgit v1.2.3 From 65d605b17dfc978ebc089b7500a5aacd8fcd863f Mon Sep 17 00:00:00 2001 From: Bond_009 <bond.009@outlook.com> Date: Wed, 1 Feb 2023 14:58:04 +0100 Subject: Improve ffprobe json parsing and don't log error for Codec Type attachment --- .../LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs | 4 +- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 5 +- MediaBrowser.MediaEncoding/Probing/CodecType.cs | 32 +++++++ .../Probing/MediaStreamInfo.cs | 6 +- .../Probing/ProbeResultNormalizer.cs | 99 +++++++------------ .../Json/Converters/JsonBoolStringConverter.cs | 34 +++++++ src/Jellyfin.Extensions/Json/JsonDefaults.cs | 1 - .../Json/Converters/JsonBoolStringTests.cs | 37 ++++++++ .../FFprobeParserTests.cs | 25 ----- .../Probing/ProbeResultNormalizerTests.cs | 22 ++++- .../Test Data/Probing/video_ts.json | 105 +++++++++++++++++++++ .../Test Data/ffprobe1.json | 105 --------------------- 12 files changed, 274 insertions(+), 201 deletions(-) create mode 100644 MediaBrowser.MediaEncoding/Probing/CodecType.cs create mode 100644 src/Jellyfin.Extensions/Json/Converters/JsonBoolStringConverter.cs create mode 100644 tests/Jellyfin.Extensions.Tests/Json/Converters/JsonBoolStringTests.cs delete mode 100644 tests/Jellyfin.MediaEncoding.Tests/FFprobeParserTests.cs create mode 100644 tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_ts.json delete mode 100644 tests/Jellyfin.MediaEncoding.Tests/Test Data/ffprobe1.json (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index 5327b3d748..98bbc15406 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -14,6 +14,7 @@ using System.Threading; using System.Threading.Tasks; using Jellyfin.Extensions; using Jellyfin.Extensions.Json; +using Jellyfin.Extensions.Json.Converters; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller; @@ -58,7 +59,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun _socketFactory = socketFactory; _streamHelper = streamHelper; - _jsonOptions = JsonDefaults.Options; + _jsonOptions = new JsonSerializerOptions(JsonDefaults.Options); + _jsonOptions.Converters.Add(new JsonBoolNumberConverter()); } public string Name => "HD Homerun"; diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index d2240b5aff..cef02d5f8f 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -12,6 +12,7 @@ using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using Jellyfin.Extensions.Json; +using Jellyfin.Extensions.Json.Converters; using MediaBrowser.Common; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; @@ -105,7 +106,9 @@ namespace MediaBrowser.MediaEncoding.Encoder _config = config; _serverConfig = serverConfig; _startupOptionFFmpegPath = config.GetValue<string>(Controller.Extensions.ConfigurationExtensions.FfmpegPathKey) ?? string.Empty; - _jsonSerializerOptions = JsonDefaults.Options; + + _jsonSerializerOptions = new JsonSerializerOptions(JsonDefaults.Options); + _jsonSerializerOptions.Converters.Add(new JsonBoolStringConverter()); } /// <inheritdoc /> diff --git a/MediaBrowser.MediaEncoding/Probing/CodecType.cs b/MediaBrowser.MediaEncoding/Probing/CodecType.cs new file mode 100644 index 0000000000..d7c68e5f38 --- /dev/null +++ b/MediaBrowser.MediaEncoding/Probing/CodecType.cs @@ -0,0 +1,32 @@ +namespace MediaBrowser.MediaEncoding.Probing; + +/// <summary> +/// FFmpeg Codec Type. +/// </summary> +public enum CodecType +{ + /// <summary> + /// Video. + /// </summary> + Video, + + /// <summary> + /// Audio. + /// </summary> + Audio, + + /// <summary> + /// Opaque data information usually continuous. + /// </summary> + Data, + + /// <summary> + /// Subtitles. + /// </summary> + Subtitle, + + /// <summary> + /// Opaque data information usually sparse. + /// </summary> + Attachment +} diff --git a/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs b/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs index eab8f79bb3..2944423248 100644 --- a/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs +++ b/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs @@ -43,7 +43,7 @@ namespace MediaBrowser.MediaEncoding.Probing /// </summary> /// <value>The codec_type.</value> [JsonPropertyName("codec_type")] - public string CodecType { get; set; } + public CodecType CodecType { get; set; } /// <summary> /// Gets or sets the sample_rate. @@ -228,11 +228,11 @@ namespace MediaBrowser.MediaEncoding.Probing public long StartPts { get; set; } /// <summary> - /// Gets or sets the is_avc. + /// Gets or sets a value indicating whether the stream is AVC. /// </summary> /// <value>The is_avc.</value> [JsonPropertyName("is_avc")] - public string IsAvc { get; set; } + public bool IsAvc { get; set; } /// <summary> /// Gets or sets the nal_length_size. diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index c667f5f57c..99310a75dd 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -107,9 +107,9 @@ namespace MediaBrowser.MediaEncoding.Probing } var tags = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); - var tagStreamType = isAudio ? "audio" : "video"; + var tagStreamType = isAudio ? CodecType.Audio : CodecType.Video; - var tagStream = data.Streams?.FirstOrDefault(i => string.Equals(i.CodecType, tagStreamType, StringComparison.OrdinalIgnoreCase)); + var tagStream = data.Streams?.FirstOrDefault(i => i.CodecType == tagStreamType); if (tagStream?.Tags is not null) { @@ -599,7 +599,7 @@ namespace MediaBrowser.MediaEncoding.Probing /// <returns>MediaAttachments.</returns> private MediaAttachment GetMediaAttachment(MediaStreamInfo streamInfo) { - if (!string.Equals(streamInfo.CodecType, "attachment", StringComparison.OrdinalIgnoreCase) + if (streamInfo.CodecType != CodecType.Attachment && streamInfo.Disposition?.GetValueOrDefault("attached_pic") != 1) { return null; @@ -651,20 +651,10 @@ namespace MediaBrowser.MediaEncoding.Probing PixelFormat = streamInfo.PixelFormat, NalLengthSize = streamInfo.NalLengthSize, TimeBase = streamInfo.TimeBase, - CodecTimeBase = streamInfo.CodecTimeBase + CodecTimeBase = streamInfo.CodecTimeBase, + IsAVC = streamInfo.IsAvc }; - if (string.Equals(streamInfo.IsAvc, "true", StringComparison.OrdinalIgnoreCase) || - string.Equals(streamInfo.IsAvc, "1", StringComparison.OrdinalIgnoreCase)) - { - stream.IsAVC = true; - } - else if (string.Equals(streamInfo.IsAvc, "false", StringComparison.OrdinalIgnoreCase) || - string.Equals(streamInfo.IsAvc, "0", StringComparison.OrdinalIgnoreCase)) - { - stream.IsAVC = false; - } - // Filter out junk if (!string.IsNullOrWhiteSpace(streamInfo.CodecTagString) && !streamInfo.CodecTagString.Contains("[0]", StringComparison.OrdinalIgnoreCase)) { @@ -678,18 +668,15 @@ namespace MediaBrowser.MediaEncoding.Probing stream.Title = GetDictionaryValue(streamInfo.Tags, "title"); } - if (string.Equals(streamInfo.CodecType, "audio", StringComparison.OrdinalIgnoreCase)) + if (streamInfo.CodecType == CodecType.Audio) { stream.Type = MediaStreamType.Audio; stream.Channels = streamInfo.Channels; - if (!string.IsNullOrEmpty(streamInfo.SampleRate)) + if (int.TryParse(streamInfo.SampleRate, NumberStyles.Any, CultureInfo.InvariantCulture, out var value)) { - if (int.TryParse(streamInfo.SampleRate, NumberStyles.Any, CultureInfo.InvariantCulture, out var value)) - { - stream.SampleRate = value; - } + stream.SampleRate = value; } stream.ChannelLayout = ParseChannelLayout(streamInfo.ChannelLayout); @@ -713,7 +700,7 @@ namespace MediaBrowser.MediaEncoding.Probing } } } - else if (string.Equals(streamInfo.CodecType, "subtitle", StringComparison.OrdinalIgnoreCase)) + else if (streamInfo.CodecType == CodecType.Subtitle) { stream.Type = MediaStreamType.Subtitle; stream.Codec = NormalizeSubtitleCodec(stream.Codec); @@ -733,7 +720,7 @@ namespace MediaBrowser.MediaEncoding.Probing } } } - else if (string.Equals(streamInfo.CodecType, "video", StringComparison.OrdinalIgnoreCase)) + else if (streamInfo.CodecType == CodecType.Video) { stream.AverageFrameRate = GetFrameRate(streamInfo.AverageFrameRate); stream.RealFrameRate = GetFrameRate(streamInfo.RFrameRate); @@ -854,13 +841,12 @@ namespace MediaBrowser.MediaEncoding.Probing } } } - else if (string.Equals(streamInfo.CodecType, "data", StringComparison.OrdinalIgnoreCase)) + else if (streamInfo.CodecType == CodecType.Data) { stream.Type = MediaStreamType.Data; } else { - _logger.LogError("Codec Type {CodecType} unknown. The stream (index: {Index}) will be ignored. Warning: Subsequential streams will have a wrong stream specifier!", streamInfo.CodecType, streamInfo.Index); return null; } @@ -895,29 +881,26 @@ namespace MediaBrowser.MediaEncoding.Probing // Extract bitrate info from tag "BPS" if possible. if (!stream.BitRate.HasValue - && (string.Equals(streamInfo.CodecType, "audio", StringComparison.OrdinalIgnoreCase) - || string.Equals(streamInfo.CodecType, "video", StringComparison.OrdinalIgnoreCase))) + && (streamInfo.CodecType == CodecType.Audio + || streamInfo.CodecType == CodecType.Video)) { var bps = GetBPSFromTags(streamInfo); if (bps > 0) { stream.BitRate = bps; } - } - - // Get average bitrate info from tag "NUMBER_OF_BYTES" and "DURATION" if possible. - if (!stream.BitRate.HasValue - && (string.Equals(streamInfo.CodecType, "audio", StringComparison.OrdinalIgnoreCase) - || string.Equals(streamInfo.CodecType, "video", StringComparison.OrdinalIgnoreCase))) - { - var durationInSeconds = GetRuntimeSecondsFromTags(streamInfo); - var bytes = GetNumberOfBytesFromTags(streamInfo); - if (durationInSeconds is not null && bytes is not null) + else { - var bps = Convert.ToInt32(bytes * 8 / durationInSeconds, CultureInfo.InvariantCulture); - if (bps > 0) + // Get average bitrate info from tag "NUMBER_OF_BYTES" and "DURATION" if possible. + var durationInSeconds = GetRuntimeSecondsFromTags(streamInfo); + var bytes = GetNumberOfBytesFromTags(streamInfo); + if (durationInSeconds is not null && bytes is not null) { - stream.BitRate = bps; + bps = Convert.ToInt32(bytes * 8 / durationInSeconds, CultureInfo.InvariantCulture); + if (bps > 0) + { + stream.BitRate = bps; + } } } } @@ -948,12 +931,8 @@ namespace MediaBrowser.MediaEncoding.Probing private void NormalizeStreamTitle(MediaStream stream) { - if (string.Equals(stream.Title, "cc", StringComparison.OrdinalIgnoreCase)) - { - stream.Title = null; - } - - if (stream.Type == MediaStreamType.EmbeddedImage) + if (string.Equals(stream.Title, "cc", StringComparison.OrdinalIgnoreCase) + || stream.Type == MediaStreamType.EmbeddedImage) { stream.Title = null; } @@ -984,7 +963,7 @@ namespace MediaBrowser.MediaEncoding.Probing return null; } - return input.Split('(').FirstOrDefault(); + return input.AsSpan().LeftPart('(').ToString(); } private string GetAspectRatio(MediaStreamInfo info) @@ -992,11 +971,11 @@ namespace MediaBrowser.MediaEncoding.Probing var original = info.DisplayAspectRatio; var parts = (original ?? string.Empty).Split(':'); - if (!(parts.Length == 2 && - int.TryParse(parts[0], NumberStyles.Any, CultureInfo.InvariantCulture, out var width) && - int.TryParse(parts[1], NumberStyles.Any, CultureInfo.InvariantCulture, out var height) && - width > 0 && - height > 0)) + if (!(parts.Length == 2 + && int.TryParse(parts[0], NumberStyles.Any, CultureInfo.InvariantCulture, out var width) + && int.TryParse(parts[1], NumberStyles.Any, CultureInfo.InvariantCulture, out var height) + && width > 0 + && height > 0)) { width = info.Width; height = info.Height; @@ -1077,12 +1056,6 @@ namespace MediaBrowser.MediaEncoding.Probing int index = value.IndexOf('/'); if (index == -1) { - // REVIEW: is this branch actually required? (i.e. does ffprobe ever output something other than a fraction?) - if (float.TryParse(value, NumberStyles.AllowThousands | NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) - { - return result; - } - return null; } @@ -1098,7 +1071,7 @@ namespace MediaBrowser.MediaEncoding.Probing private void SetAudioRuntimeTicks(InternalMediaInfoResult result, MediaInfo data) { // Get the first info stream - var stream = result.Streams?.FirstOrDefault(s => string.Equals(s.CodecType, "audio", StringComparison.OrdinalIgnoreCase)); + var stream = result.Streams?.FirstOrDefault(s => s.CodecType == CodecType.Audio); if (stream is null) { return; @@ -1128,8 +1101,7 @@ namespace MediaBrowser.MediaEncoding.Probing } var bps = GetDictionaryValue(streamInfo.Tags, "BPS-eng") ?? GetDictionaryValue(streamInfo.Tags, "BPS"); - if (!string.IsNullOrEmpty(bps) - && int.TryParse(bps, NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedBps)) + if (int.TryParse(bps, NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedBps)) { return parsedBps; } @@ -1162,8 +1134,7 @@ namespace MediaBrowser.MediaEncoding.Probing var numberOfBytes = GetDictionaryValue(streamInfo.Tags, "NUMBER_OF_BYTES-eng") ?? GetDictionaryValue(streamInfo.Tags, "NUMBER_OF_BYTES"); - if (!string.IsNullOrEmpty(numberOfBytes) - && long.TryParse(numberOfBytes, NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedBytes)) + if (long.TryParse(numberOfBytes, NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedBytes)) { return parsedBytes; } @@ -1455,7 +1426,7 @@ namespace MediaBrowser.MediaEncoding.Probing { var disc = tags.GetValueOrDefault(tagName); - if (!string.IsNullOrEmpty(disc) && int.TryParse(disc.AsSpan().LeftPart('/'), out var discNum)) + if (int.TryParse(disc.AsSpan().LeftPart('/'), out var discNum)) { return discNum; } diff --git a/src/Jellyfin.Extensions/Json/Converters/JsonBoolStringConverter.cs b/src/Jellyfin.Extensions/Json/Converters/JsonBoolStringConverter.cs new file mode 100644 index 0000000000..2936fe4d6d --- /dev/null +++ b/src/Jellyfin.Extensions/Json/Converters/JsonBoolStringConverter.cs @@ -0,0 +1,34 @@ +using System; +using System.Buffers; +using System.Buffers.Text; +using System.Linq; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Jellyfin.Extensions.Json.Converters; + +/// <summary> +/// Converts a string to a boolean. +/// This is needed for FFprobe. +/// </summary> +public class JsonBoolStringConverter : JsonConverter<bool> +{ + /// <inheritdoc /> + public override bool Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.String) + { + ReadOnlySpan<byte> utf8Span = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan; + if (Utf8Parser.TryParse(utf8Span, out bool val, out _, 'l')) + { + return val; + } + } + + return reader.GetBoolean(); + } + + /// <inheritdoc /> + public override void Write(Utf8JsonWriter writer, bool value, JsonSerializerOptions options) + => writer.WriteBooleanValue(value); +} diff --git a/src/Jellyfin.Extensions/Json/JsonDefaults.cs b/src/Jellyfin.Extensions/Json/JsonDefaults.cs index 97cbee9710..4d56ca6151 100644 --- a/src/Jellyfin.Extensions/Json/JsonDefaults.cs +++ b/src/Jellyfin.Extensions/Json/JsonDefaults.cs @@ -39,7 +39,6 @@ namespace Jellyfin.Extensions.Json new JsonFlagEnumConverterFactory(), new JsonStringEnumConverter(), new JsonNullableStructConverterFactory(), - new JsonBoolNumberConverter(), new JsonDateTimeConverter(), new JsonStringConverter() } diff --git a/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonBoolStringTests.cs b/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonBoolStringTests.cs new file mode 100644 index 0000000000..be256da2ea --- /dev/null +++ b/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonBoolStringTests.cs @@ -0,0 +1,37 @@ +using System.Text.Json; +using Jellyfin.Extensions.Json.Converters; +using Xunit; + +namespace Jellyfin.Extensions.Tests.Json.Converters +{ + public class JsonBoolStringTests + { + private readonly JsonSerializerOptions _jsonOptions = new JsonSerializerOptions() + { + Converters = + { + new JsonBoolStringConverter() + } + }; + + [Theory] + [InlineData(@"{ ""Value"": ""true"" }", true)] + [InlineData(@"{ ""Value"": ""false"" }", false)] + public void Deserialize_String_Valid_Success(string input, bool output) + { + var s = JsonSerializer.Deserialize<TestStruct>(input, _jsonOptions); + Assert.Equal(s.Value, output); + } + + [Theory] + [InlineData(true, "true")] + [InlineData(false, "false")] + public void Serialize_Bool_Success(bool input, string output) + { + var value = JsonSerializer.Serialize(input, _jsonOptions); + Assert.Equal(value, output); + } + + private readonly record struct TestStruct(bool Value); + } +} diff --git a/tests/Jellyfin.MediaEncoding.Tests/FFprobeParserTests.cs b/tests/Jellyfin.MediaEncoding.Tests/FFprobeParserTests.cs deleted file mode 100644 index 97dbb3be03..0000000000 --- a/tests/Jellyfin.MediaEncoding.Tests/FFprobeParserTests.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.IO; -using System.Text.Json; -using System.Threading.Tasks; -using Jellyfin.Extensions.Json; -using MediaBrowser.MediaEncoding.Probing; -using MediaBrowser.Model.IO; -using Xunit; - -namespace Jellyfin.MediaEncoding.Tests -{ - public class FFprobeParserTests - { - [Theory] - [InlineData("ffprobe1.json")] - public async Task Test(string fileName) - { - var path = Path.Join("Test Data", fileName); - await using (var stream = AsyncFile.OpenRead(path)) - { - var res = await JsonSerializer.DeserializeAsync<InternalMediaInfoResult>(stream, JsonDefaults.Options).ConfigureAwait(false); - Assert.NotNull(res); - } - } - } -} diff --git a/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs b/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs index a64604e99f..6cb98b2b86 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs @@ -3,6 +3,7 @@ using System.Globalization; using System.IO; using System.Text.Json; using Jellyfin.Extensions.Json; +using Jellyfin.Extensions.Json.Converters; using MediaBrowser.MediaEncoding.Probing; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Globalization; @@ -15,9 +16,15 @@ namespace Jellyfin.MediaEncoding.Tests.Probing { public class ProbeResultNormalizerTests { - private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options; + private readonly JsonSerializerOptions _jsonOptions; private readonly ProbeResultNormalizer _probeResultNormalizer = new ProbeResultNormalizer(new NullLogger<EncoderValidatorTests>(), null); + public ProbeResultNormalizerTests() + { + _jsonOptions = new JsonSerializerOptions(JsonDefaults.Options); + _jsonOptions.Converters.Add(new JsonBoolStringConverter()); + } + [Theory] [InlineData("2997/125", 23.976f)] [InlineData("1/50", 0.02f)] @@ -148,6 +155,19 @@ namespace Jellyfin.MediaEncoding.Tests.Probing Assert.False(res.MediaStreams[5].IsHearingImpaired); } + [Fact] + public void GetMediaInfo_TS_Success() + { + var bytes = File.ReadAllBytes("Test Data/Probing/video_ts.json"); + var internalMediaInfoResult = JsonSerializer.Deserialize<InternalMediaInfoResult>(bytes, _jsonOptions); + + MediaInfo res = _probeResultNormalizer.GetMediaInfo(internalMediaInfoResult, VideoType.VideoFile, false, "Test Data/Probing/video_metadata.mkv", MediaProtocol.File); + + Assert.Equal(2, res.MediaStreams.Count); + + Assert.False(res.MediaStreams[0].IsAVC); + } + [Fact] public void GetMediaInfo_ProgressiveVideoNoFieldOrder_Success() { diff --git a/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_ts.json b/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_ts.json new file mode 100644 index 0000000000..cdad5df502 --- /dev/null +++ b/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_ts.json @@ -0,0 +1,105 @@ +{ + "streams": [ + { + "index": 0, + "codec_name": "h264", + "codec_long_name": "H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10", + "profile": "Main", + "codec_type": "video", + "codec_time_base": "1/50", + "codec_tag_string": "[27][0][0][0]", + "codec_tag": "0x001b", + "width": 1920, + "height": 1080, + "coded_width": 1920, + "coded_height": 1080, + "has_b_frames": 0, + "sample_aspect_ratio": "0:1", + "display_aspect_ratio": "0:1", + "pix_fmt": "yuvj420p", + "level": 42, + "color_range": "pc", + "color_space": "bt709", + "color_transfer": "bt709", + "color_primaries": "bt709", + "chroma_location": "left", + "field_order": "progressive", + "refs": 1, + "is_avc": "false", + "nal_length_size": "0", + "id": "0x1", + "r_frame_rate": "25/1", + "avg_frame_rate": "25/1", + "time_base": "1/90000", + "start_pts": 8570867078, + "start_time": "95231.856422", + "duration_ts": 31694552, + "duration": "352.161689", + "bits_per_raw_sample": "8", + "disposition": { + "default": 0, + "dub": 0, + "original": 0, + "comment": 0, + "lyrics": 0, + "karaoke": 0, + "forced": 0, + "hearing_impaired": 0, + "visual_impaired": 0, + "clean_effects": 0, + "attached_pic": 0, + "timed_thumbnails": 0 + } + }, + { + "index": 1, + "codec_name": "aac", + "codec_long_name": "AAC (Advanced Audio Coding)", + "profile": "LC", + "codec_type": "audio", + "codec_time_base": "1/44100", + "codec_tag_string": "[15][0][0][0]", + "codec_tag": "0x000f", + "sample_fmt": "fltp", + "sample_rate": "44100", + "channels": 2, + "channel_layout": "stereo", + "bits_per_sample": 0, + "id": "0x2", + "r_frame_rate": "0/0", + "avg_frame_rate": "0/0", + "time_base": "1/90000", + "start_pts": 8570867697, + "start_time": "95231.863300", + "duration_ts": 31695687, + "duration": "352.174300", + "bit_rate": "98191", + "disposition": { + "default": 0, + "dub": 0, + "original": 0, + "comment": 0, + "lyrics": 0, + "karaoke": 0, + "forced": 0, + "hearing_impaired": 0, + "visual_impaired": 0, + "clean_effects": 0, + "attached_pic": 0, + "timed_thumbnails": 0 + } + } + ], + "format": { + "filename": "TS Test record.ts", + "nb_streams": 2, + "nb_programs": 1, + "format_name": "mpegts", + "format_long_name": "MPEG-TS (MPEG-2 Transport Stream)", + "start_time": "95231.856422", + "duration": "352.181178", + "size": "179003772", + "bit_rate": "4066174", + "probe_score": 50 + } +} diff --git a/tests/Jellyfin.MediaEncoding.Tests/Test Data/ffprobe1.json b/tests/Jellyfin.MediaEncoding.Tests/Test Data/ffprobe1.json deleted file mode 100644 index cdad5df502..0000000000 --- a/tests/Jellyfin.MediaEncoding.Tests/Test Data/ffprobe1.json +++ /dev/null @@ -1,105 +0,0 @@ -{ - "streams": [ - { - "index": 0, - "codec_name": "h264", - "codec_long_name": "H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10", - "profile": "Main", - "codec_type": "video", - "codec_time_base": "1/50", - "codec_tag_string": "[27][0][0][0]", - "codec_tag": "0x001b", - "width": 1920, - "height": 1080, - "coded_width": 1920, - "coded_height": 1080, - "has_b_frames": 0, - "sample_aspect_ratio": "0:1", - "display_aspect_ratio": "0:1", - "pix_fmt": "yuvj420p", - "level": 42, - "color_range": "pc", - "color_space": "bt709", - "color_transfer": "bt709", - "color_primaries": "bt709", - "chroma_location": "left", - "field_order": "progressive", - "refs": 1, - "is_avc": "false", - "nal_length_size": "0", - "id": "0x1", - "r_frame_rate": "25/1", - "avg_frame_rate": "25/1", - "time_base": "1/90000", - "start_pts": 8570867078, - "start_time": "95231.856422", - "duration_ts": 31694552, - "duration": "352.161689", - "bits_per_raw_sample": "8", - "disposition": { - "default": 0, - "dub": 0, - "original": 0, - "comment": 0, - "lyrics": 0, - "karaoke": 0, - "forced": 0, - "hearing_impaired": 0, - "visual_impaired": 0, - "clean_effects": 0, - "attached_pic": 0, - "timed_thumbnails": 0 - } - }, - { - "index": 1, - "codec_name": "aac", - "codec_long_name": "AAC (Advanced Audio Coding)", - "profile": "LC", - "codec_type": "audio", - "codec_time_base": "1/44100", - "codec_tag_string": "[15][0][0][0]", - "codec_tag": "0x000f", - "sample_fmt": "fltp", - "sample_rate": "44100", - "channels": 2, - "channel_layout": "stereo", - "bits_per_sample": 0, - "id": "0x2", - "r_frame_rate": "0/0", - "avg_frame_rate": "0/0", - "time_base": "1/90000", - "start_pts": 8570867697, - "start_time": "95231.863300", - "duration_ts": 31695687, - "duration": "352.174300", - "bit_rate": "98191", - "disposition": { - "default": 0, - "dub": 0, - "original": 0, - "comment": 0, - "lyrics": 0, - "karaoke": 0, - "forced": 0, - "hearing_impaired": 0, - "visual_impaired": 0, - "clean_effects": 0, - "attached_pic": 0, - "timed_thumbnails": 0 - } - } - ], - "format": { - "filename": "TS Test record.ts", - "nb_streams": 2, - "nb_programs": 1, - "format_name": "mpegts", - "format_long_name": "MPEG-TS (MPEG-2 Transport Stream)", - "start_time": "95231.856422", - "duration": "352.181178", - "size": "179003772", - "bit_rate": "4066174", - "probe_score": 50 - } -} -- cgit v1.2.3 From c38157acb9131093109ba5c52f6b601557dfe18f Mon Sep 17 00:00:00 2001 From: Peyman M <peymanr34@outlook.com> Date: Wed, 1 Feb 2023 00:06:53 +0000 Subject: Translated using Weblate (Persian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/fa/ --- Emby.Server.Implementations/Localization/Core/fa.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/fa.json b/Emby.Server.Implementations/Localization/Core/fa.json index 026648af46..8e4bba25b6 100644 --- a/Emby.Server.Implementations/Localization/Core/fa.json +++ b/Emby.Server.Implementations/Localization/Core/fa.json @@ -123,5 +123,6 @@ "TaskOptimizeDatabaseDescription": "فشرده سازی پایگاه داده و باز کردن فضای آزاد.اجرای این گزینه بعد از اسکن کردن کتابخانه یا تغییرات دیگر که روی پایگاه داده تأثیر میگذارند میتواند کارایی را بهبود ببخشد.", "TaskKeyframeExtractorDescription": "فریم های کلیدی را از فایل های ویدئویی استخراج می کند تا لیست های پخش HLS دقیق تری ایجاد کند. این کار ممکن است برای مدت طولانی اجرا شود.", "TaskKeyframeExtractor": "استخراج کننده فریم کلیدی", - "External": "خارجی" + "External": "خارجی", + "HearingImpaired": "مشکل شنوایی" } -- cgit v1.2.3 From fe48c47e292a531c82120d083984620ce011a480 Mon Sep 17 00:00:00 2001 From: Robert Englund <kastanjen3@gmail.com> Date: Tue, 31 Jan 2023 07:57:43 +0000 Subject: Translated using Weblate (Swedish) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/sv/ --- Emby.Server.Implementations/Localization/Core/sv.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/sv.json b/Emby.Server.Implementations/Localization/Core/sv.json index 318a0f3cf1..785e6b2262 100644 --- a/Emby.Server.Implementations/Localization/Core/sv.json +++ b/Emby.Server.Implementations/Localization/Core/sv.json @@ -66,7 +66,7 @@ "PluginInstalledWithName": "{0} installerades", "PluginUninstalledWithName": "{0} avinstallerades", "PluginUpdatedWithName": "{0} uppdaterades", - "ProviderValue": "Källa: {0}", + "ProviderValue": "Leverantör: {0}", "ScheduledTaskFailedWithName": "{0} misslyckades", "ScheduledTaskStartedWithName": "{0} startades", "ServerNameNeedsToBeRestarted": "{0} behöver startas om", -- cgit v1.2.3 From b769417e9402b843e1adb6b19e8fff7a1aa81b84 Mon Sep 17 00:00:00 2001 From: Plntote <plntote980@outlook.com> Date: Fri, 3 Feb 2023 00:26:23 +0000 Subject: Translated using Weblate (Spanish (Latin America)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/es_419/ --- Emby.Server.Implementations/Localization/Core/es_419.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/es_419.json b/Emby.Server.Implementations/Localization/Core/es_419.json index d6078c9c6d..3d5c046336 100644 --- a/Emby.Server.Implementations/Localization/Core/es_419.json +++ b/Emby.Server.Implementations/Localization/Core/es_419.json @@ -122,5 +122,6 @@ "TaskOptimizeDatabase": "Optimizar base de datos", "External": "Externo", "TaskKeyframeExtractorDescription": "Extrae Fotogramas Clave de los archivos de vídeo para crear Listas de Reproducción HLS más precisas. Esta tarea puede durar mucho tiempo.", - "TaskKeyframeExtractor": "Extractor de Fotogramas Clave" + "TaskKeyframeExtractor": "Extractor de Fotogramas Clave", + "HearingImpaired": "Discapacidad auditiva" } -- cgit v1.2.3 From 5612091df3f496960ef4cad6e94ff821a02173cf Mon Sep 17 00:00:00 2001 From: Wiki <deadly.devils@outlook.com> Date: Thu, 2 Feb 2023 11:53:17 +0000 Subject: Translated using Weblate (Pirate (pr)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/pr/ --- Emby.Server.Implementations/Localization/Core/pr.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/pr.json b/Emby.Server.Implementations/Localization/Core/pr.json index 466c8a9905..87800a2fe8 100644 --- a/Emby.Server.Implementations/Localization/Core/pr.json +++ b/Emby.Server.Implementations/Localization/Core/pr.json @@ -19,5 +19,10 @@ "FailedLoginAttemptWithUserName": "Ye failed to get in, try from {0}", "Favorites": "Finest Loot", "ItemRemovedWithName": "{0} was taken from yer treasure", - "LabelIpAddressValue": "Ship's coordinates: {0}" + "LabelIpAddressValue": "Ship's coordinates: {0}", + "Genres": "types o' booty", + "TaskDownloadMissingSubtitlesDescription": "Scours the seven seas o' the internet for subtitles that be missin' based on the captain's map o' metadata.", + "HeaderAlbumArtists": "Buccaneers o' the musical arts", + "HeaderFavoriteAlbums": "Beloved booty o' musical adventures", + "HeaderFavoriteArtists": "Treasured scallywags o' the creative seas" } -- cgit v1.2.3 From e0519189b25bc4605889e46d9583fea9aef41732 Mon Sep 17 00:00:00 2001 From: Zoltan Csizmadia <zcsizmadia@gmail.com> Date: Sat, 4 Feb 2023 11:15:08 -0600 Subject: Use Directory.Packages.props (#9135) Co-authored-by: Zoltan Csizmadia <CsizmadiaZ@valassis.com> --- Directory.Packages.props | 90 ++++++++++++++++++++++ Emby.Dlna/Emby.Dlna.csproj | 10 +-- Emby.Naming/Emby.Naming.csproj | 10 +-- Emby.Photos/Emby.Photos.csproj | 10 +-- .../Emby.Server.Implementations.csproj | 30 ++++---- Jellyfin.Api/Jellyfin.Api.csproj | 16 ++-- Jellyfin.Data/Jellyfin.Data.csproj | 12 +-- Jellyfin.Networking/Jellyfin.Networking.csproj | 8 +- .../Jellyfin.Server.Implementations.csproj | 20 ++--- Jellyfin.Server/Jellyfin.Server.csproj | 38 ++++----- MediaBrowser.Common/MediaBrowser.Common.csproj | 14 ++-- .../MediaBrowser.Controller.csproj | 16 ++-- .../MediaBrowser.LocalMetadata.csproj | 8 +- .../MediaBrowser.MediaEncoding.csproj | 16 ++-- MediaBrowser.Model/MediaBrowser.Model.csproj | 18 ++--- .../MediaBrowser.Providers.csproj | 26 +++---- .../MediaBrowser.XbmcMetadata.csproj | 8 +- .../Emby.Server.Implementations.Fuzz.csproj | 8 +- .../Jellyfin.Server.Fuzz.csproj | 2 +- .../Jellyfin.Drawing.Skia.csproj | 18 ++--- src/Jellyfin.Drawing/Jellyfin.Drawing.csproj | 8 +- src/Jellyfin.Extensions/Jellyfin.Extensions.csproj | 10 +-- .../Jellyfin.MediaEncoding.Hls.csproj | 10 +-- .../Jellyfin.MediaEncoding.Keyframes.csproj | 12 +-- tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj | 28 +++---- .../Jellyfin.Common.Tests.csproj | 18 ++--- .../Jellyfin.Controller.Tests.csproj | 18 ++--- .../Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj | 18 ++--- .../Jellyfin.Extensions.Tests.csproj | 18 ++--- .../Jellyfin.MediaEncoding.Hls.Tests.csproj | 16 ++-- .../Jellyfin.MediaEncoding.Keyframes.Tests.csproj | 16 ++-- .../Jellyfin.MediaEncoding.Tests.csproj | 24 +++--- .../Jellyfin.Model.Tests.csproj | 20 ++--- .../Jellyfin.Naming.Tests.csproj | 18 ++--- .../Jellyfin.Networking.Tests.csproj | 20 ++--- .../Jellyfin.Providers.Tests.csproj | 18 ++--- .../Jellyfin.Server.Implementations.Tests.csproj | 24 +++--- .../Jellyfin.Server.Integration.Tests.csproj | 30 ++++---- .../Jellyfin.Server.Tests.csproj | 28 +++---- .../Jellyfin.XbmcMetadata.Tests.csproj | 18 ++--- 40 files changed, 420 insertions(+), 330 deletions(-) create mode 100644 Directory.Packages.props (limited to 'Emby.Server.Implementations') diff --git a/Directory.Packages.props b/Directory.Packages.props new file mode 100644 index 0000000000..26f0692115 --- /dev/null +++ b/Directory.Packages.props @@ -0,0 +1,90 @@ +<Project> + <PropertyGroup> + <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally> + </PropertyGroup> + + <!-- Run "dotnet list package (dash,dash)outdated" to see the latest versions of each package.--> + + <ItemGroup Label="Package Dependencies"> + <PackageVersion Include="AutoFixture.AutoMoq" Version="4.17.0" /> + <PackageVersion Include="AutoFixture.Xunit2" Version="4.17.0" /> + <PackageVersion Include="AutoFixture" Version="4.17.0" /> + <PackageVersion Include="BDInfo" Version="0.7.6.2" /> + <PackageVersion Include="BlurHashSharp.SkiaSharp" Version="1.2.0" /> + <PackageVersion Include="BlurHashSharp" Version="1.2.0" /> + <PackageVersion Include="CommandLineParser" Version="2.9.1" /> + <PackageVersion Include="coverlet.collector" Version="3.2.0" /> + <PackageVersion Include="Diacritics" Version="3.3.14" /> + <PackageVersion Include="DiscUtils.Udf" Version="0.16.13" /> + <PackageVersion Include="DotNet.Glob" Version="3.1.3" /> + <PackageVersion Include="EFCoreSecondLevelCacheInterceptor" Version="3.8.3" /> + <PackageVersion Include="FsCheck.Xunit" Version="2.16.5" /> + <PackageVersion Include="Jellyfin.XmlTv" Version="10.8.0" /> + <PackageVersion Include="libse" Version="3.6.10" /> + <PackageVersion Include="LrcParser" Version="2022.529.1" /> + <PackageVersion Include="MetaBrainz.MusicBrainz" Version="5.0.0" /> + <PackageVersion Include="Microsoft.AspNetCore.Authorization" Version="7.0.2" /> + <PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="7.0.2" /> + <PackageVersion Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4" /> + <PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.2" /> + <PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="7.0.2" /> + <PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.2" /> + <PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.2" /> + <PackageVersion Include="Microsoft.Extensions.Caching.Abstractions" Version="7.0.0" /> + <PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="7.0.0" /> + <PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="7.0.0" /> + <PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.2" /> + <PackageVersion Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="7.0.0" /> + <PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" /> + <PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" /> + <PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" /> + <PackageVersion Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="7.0.2" /> + <PackageVersion Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="7.0.2" /> + <PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="7.0.0" /> + <PackageVersion Include="Microsoft.Extensions.Http" Version="7.0.0" /> + <PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.0" /> + <PackageVersion Include="Microsoft.Extensions.Logging" Version="7.0.0" /> + <PackageVersion Include="Microsoft.Extensions.Options" Version="7.0.0" /> + <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.4.1" /> + <PackageVersion Include="Microsoft.SourceLink.GitHub" Version="1.1.1" /> + <PackageVersion Include="MimeTypes" Version="2.4.0" /> + <PackageVersion Include="Mono.Nat" Version="3.0.4" /> + <PackageVersion Include="Moq" Version="4.18.4" /> + <PackageVersion Include="NEbml" Version="0.11.0" /> + <PackageVersion Include="Newtonsoft.Json" Version="13.0.2" /> + <PackageVersion Include="PlaylistsNET" Version="1.3.1" /> + <PackageVersion Include="prometheus-net.AspNetCore" Version="7.0.0" /> + <PackageVersion Include="prometheus-net.DotNetRuntime" Version="4.4.0" /> + <PackageVersion Include="prometheus-net" Version="7.0.0" /> + <PackageVersion Include="Serilog.AspNetCore" Version="6.1.0" /> + <PackageVersion Include="Serilog.Enrichers.Thread" Version="3.1.0" /> + <PackageVersion Include="Serilog.Settings.Configuration" Version="3.4.0" /> + <PackageVersion Include="Serilog.Sinks.Async" Version="1.5.0" /> + <PackageVersion Include="Serilog.Sinks.Console" Version="4.1.0" /> + <PackageVersion Include="Serilog.Sinks.File" Version="5.0.0" /> + <PackageVersion Include="Serilog.Sinks.Graylog" Version="2.3.0" /> + <PackageVersion Include="SerilogAnalyzer" Version="0.15.0" /> + <PackageVersion Include="SharpFuzz" Version="2.0.1" /> + <PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.3" /> + <PackageVersion Include="SkiaSharp.Svg" Version="1.60.0" /> + <PackageVersion Include="SkiaSharp" Version="2.88.3" /> + <PackageVersion Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" /> + <PackageVersion Include="SQLitePCL.pretty.netstandard" Version="3.1.0" /> + <PackageVersion Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.4" /> + <PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.435" /> + <PackageVersion Include="Swashbuckle.AspNetCore.ReDoc" Version="6.4.0" /> + <PackageVersion Include="Swashbuckle.AspNetCore" Version="6.2.3" /> + <PackageVersion Include="System.Globalization" Version="4.3.0" /> + <PackageVersion Include="System.Linq.Async" Version="6.0.1" /> + <PackageVersion Include="System.Text.Encoding.CodePages" Version="7.0.0" /> + <PackageVersion Include="System.Text.Json" Version="7.0.1" /> + <PackageVersion Include="System.Threading.Tasks.Dataflow" Version="7.0.0" /> + <PackageVersion Include="TagLibSharp" Version="2.3.0" /> + <PackageVersion Include="TMDbLib" Version="2.0.0" /> + <PackageVersion Include="UTF.Unknown" Version="2.5.1" /> + <PackageVersion Include="Xunit.Priority" Version="1.1.6" /> + <PackageVersion Include="xunit.runner.visualstudio" Version="2.4.5" /> + <PackageVersion Include="Xunit.SkippableFact" Version="1.4.13" /> + <PackageVersion Include="xunit" Version="2.4.2" /> + </ItemGroup> +</Project> diff --git a/Emby.Dlna/Emby.Dlna.csproj b/Emby.Dlna/Emby.Dlna.csproj index 60e6dd644d..aca2399644 100644 --- a/Emby.Dlna/Emby.Dlna.csproj +++ b/Emby.Dlna/Emby.Dlna.csproj @@ -28,13 +28,13 @@ <!-- Code Analyzers--> <ItemGroup Condition=" '$(Configuration)' == 'Debug' "> - <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4"> + <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference> - <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" /> - <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="All" /> - <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" /> + <PackageReference Include="SerilogAnalyzer" PrivateAssets="All" /> + <PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" /> + <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" /> </ItemGroup> <ItemGroup> @@ -80,7 +80,7 @@ </ItemGroup> <ItemGroup> - <PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" /> + <PackageReference Include="Microsoft.Extensions.Http" /> </ItemGroup> </Project> diff --git a/Emby.Naming/Emby.Naming.csproj b/Emby.Naming/Emby.Naming.csproj index 3106e22465..f3973dad95 100644 --- a/Emby.Naming/Emby.Naming.csproj +++ b/Emby.Naming/Emby.Naming.csproj @@ -42,18 +42,18 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" /> + <PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="All" /> </ItemGroup> <!-- Code Analyzers--> <ItemGroup Condition=" '$(Configuration)' == 'Debug' "> - <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4"> + <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference> - <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" /> - <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="All" /> - <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" /> + <PackageReference Include="SerilogAnalyzer" PrivateAssets="All" /> + <PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" /> + <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" /> </ItemGroup> </Project> diff --git a/Emby.Photos/Emby.Photos.csproj b/Emby.Photos/Emby.Photos.csproj index ae6bc2db1f..0f97a06867 100644 --- a/Emby.Photos/Emby.Photos.csproj +++ b/Emby.Photos/Emby.Photos.csproj @@ -15,7 +15,7 @@ </ItemGroup> <ItemGroup> - <PackageReference Include="TagLibSharp" Version="2.3.0" /> + <PackageReference Include="TagLibSharp" /> </ItemGroup> <PropertyGroup> @@ -26,13 +26,13 @@ <!-- Code Analyzers--> <ItemGroup Condition=" '$(Configuration)' == 'Debug' "> - <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4"> + <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference> - <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="All" /> - <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" /> - <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" /> + <PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" /> + <PackageReference Include="SerilogAnalyzer" PrivateAssets="All" /> + <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" /> </ItemGroup> </Project> diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 1b5c879beb..b8655c7600 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -22,17 +22,17 @@ </ItemGroup> <ItemGroup> - <PackageReference Include="DiscUtils.Udf" Version="0.16.13" /> - <PackageReference Include="Jellyfin.XmlTv" Version="10.8.0" /> - <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" /> - <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.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" /> - <PackageReference Include="DotNet.Glob" Version="3.1.3" /> + <PackageReference Include="DiscUtils.Udf" /> + <PackageReference Include="Jellyfin.XmlTv" /> + <PackageReference Include="Microsoft.Extensions.DependencyInjection" /> + <PackageReference Include="Microsoft.Extensions.Caching.Memory" /> + <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" /> + <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" /> + <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" /> + <PackageReference Include="Mono.Nat" /> + <PackageReference Include="prometheus-net.DotNetRuntime" /> + <PackageReference Include="SQLitePCL.pretty.netstandard" /> + <PackageReference Include="DotNet.Glob" /> </ItemGroup> <ItemGroup> @@ -53,13 +53,13 @@ <!-- Code Analyzers--> <ItemGroup Condition=" '$(Configuration)' == 'Debug' "> - <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4"> + <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference> - <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" /> - <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="All" /> - <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" /> + <PackageReference Include="SerilogAnalyzer" PrivateAssets="All" /> + <PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" /> + <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" /> </ItemGroup> <ItemGroup> diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj index 45725ec3e6..a8a44fd3e0 100644 --- a/Jellyfin.Api/Jellyfin.Api.csproj +++ b/Jellyfin.Api/Jellyfin.Api.csproj @@ -13,10 +13,10 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="Microsoft.AspNetCore.Authorization" Version="7.0.2" /> - <PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" /> - <PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" /> - <PackageReference Include="Swashbuckle.AspNetCore.ReDoc" Version="6.4.0" /> + <PackageReference Include="Microsoft.AspNetCore.Authorization" /> + <PackageReference Include="Microsoft.Extensions.Http" /> + <PackageReference Include="Swashbuckle.AspNetCore" /> + <PackageReference Include="Swashbuckle.AspNetCore.ReDoc" /> </ItemGroup> <ItemGroup> @@ -27,13 +27,13 @@ <!-- Code Analyzers--> <ItemGroup Condition=" '$(Configuration)' == 'Debug' "> - <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4"> + <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference> - <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" /> - <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="All" /> - <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" /> + <PackageReference Include="SerilogAnalyzer" PrivateAssets="All" /> + <PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" /> + <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" /> </ItemGroup> <ItemGroup> diff --git a/Jellyfin.Data/Jellyfin.Data.csproj b/Jellyfin.Data/Jellyfin.Data.csproj index 540534e1ba..1bc5d8bf91 100644 --- a/Jellyfin.Data/Jellyfin.Data.csproj +++ b/Jellyfin.Data/Jellyfin.Data.csproj @@ -24,22 +24,22 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" /> + <PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="All" /> </ItemGroup> <!-- Code analysers--> <ItemGroup Condition=" '$(Configuration)' == 'Debug' "> - <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4"> + <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference> - <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" /> - <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="All" /> - <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" /> + <PackageReference Include="SerilogAnalyzer" PrivateAssets="All" /> + <PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" /> + <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" /> </ItemGroup> <ItemGroup> - <PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" /> + <PackageReference Include="Microsoft.Extensions.Logging" /> </ItemGroup> <ItemGroup> diff --git a/Jellyfin.Networking/Jellyfin.Networking.csproj b/Jellyfin.Networking/Jellyfin.Networking.csproj index 2c153d88b5..4cff5927fd 100644 --- a/Jellyfin.Networking/Jellyfin.Networking.csproj +++ b/Jellyfin.Networking/Jellyfin.Networking.csproj @@ -11,13 +11,13 @@ <!-- Code Analyzers--> <ItemGroup Condition=" '$(Configuration)' == 'Debug' "> - <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4"> + <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference> - <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" /> - <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="All" /> - <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" /> + <PackageReference Include="SerilogAnalyzer" PrivateAssets="All" /> + <PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" /> + <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" /> </ItemGroup> <ItemGroup> diff --git a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj index 1c53274e23..390ed58b3b 100644 --- a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj +++ b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj @@ -8,13 +8,13 @@ <!-- Code analysers--> <ItemGroup Condition=" '$(Configuration)' == 'Debug' "> - <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4"> + <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference> - <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" /> - <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="All" /> - <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" /> + <PackageReference Include="SerilogAnalyzer" PrivateAssets="All" /> + <PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" /> + <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" /> </ItemGroup> <ItemGroup> @@ -22,15 +22,15 @@ </ItemGroup> <ItemGroup> - <PackageReference Include="EFCoreSecondLevelCacheInterceptor" Version="3.8.3" /> - <PackageReference Include="System.Linq.Async" Version="6.0.1" /> - <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.2" /> - <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="7.0.2" /> - <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.2"> + <PackageReference Include="EFCoreSecondLevelCacheInterceptor" /> + <PackageReference Include="System.Linq.Async" /> + <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" /> + <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" /> + <PackageReference Include="Microsoft.EntityFrameworkCore.Design"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> - <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.2"> + <PackageReference Include="Microsoft.EntityFrameworkCore.Tools"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index 9ea8508f24..146de3ae13 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -24,31 +24,31 @@ <!-- Code Analyzers--> <ItemGroup Condition=" '$(Configuration)' == 'Debug' "> - <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4"> + <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference> - <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" /> - <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="All" /> - <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" /> + <PackageReference Include="SerilogAnalyzer" PrivateAssets="All" /> + <PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" /> + <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" /> </ItemGroup> <ItemGroup> - <PackageReference Include="CommandLineParser" Version="2.9.1" /> - <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="7.0.0" /> - <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" /> - <PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="7.0.2" /> - <PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="7.0.2" /> - <PackageReference Include="prometheus-net" Version="7.0.0" /> - <PackageReference Include="prometheus-net.AspNetCore" Version="7.0.0" /> - <PackageReference Include="Serilog.AspNetCore" Version="6.1.0" /> - <PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0" /> - <PackageReference Include="Serilog.Settings.Configuration" Version="3.4.0" /> - <PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" /> - <PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" /> - <PackageReference Include="Serilog.Sinks.File" Version="5.0.0" /> - <PackageReference Include="Serilog.Sinks.Graylog" Version="2.3.0" /> - <PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.4" /> + <PackageReference Include="CommandLineParser" /> + <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" /> + <PackageReference Include="Microsoft.Extensions.Configuration.Json" /> + <PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" /> + <PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" /> + <PackageReference Include="prometheus-net" /> + <PackageReference Include="prometheus-net.AspNetCore" /> + <PackageReference Include="Serilog.AspNetCore" /> + <PackageReference Include="Serilog.Enrichers.Thread" /> + <PackageReference Include="Serilog.Settings.Configuration" /> + <PackageReference Include="Serilog.Sinks.Async" /> + <PackageReference Include="Serilog.Sinks.Console" /> + <PackageReference Include="Serilog.Sinks.File" /> + <PackageReference Include="Serilog.Sinks.Graylog" /> + <PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" /> </ItemGroup> <ItemGroup> diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj index 1b0ff27d98..3f1a098e45 100644 --- a/MediaBrowser.Common/MediaBrowser.Common.csproj +++ b/MediaBrowser.Common/MediaBrowser.Common.csproj @@ -19,9 +19,9 @@ </ItemGroup> <ItemGroup> - <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="7.0.0" /> - <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" /> - <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" /> + <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" /> + <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" /> + <PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="All" /> </ItemGroup> <ItemGroup> @@ -49,13 +49,13 @@ <!-- Code analyzers--> <ItemGroup Condition=" '$(Configuration)' == 'Debug' "> - <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4"> + <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference> - <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" /> - <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="All" /> - <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" /> + <PackageReference Include="SerilogAnalyzer" PrivateAssets="All" /> + <PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" /> + <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" /> </ItemGroup> <ItemGroup> diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 20909c9d57..69c0d26b6b 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -18,10 +18,10 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="7.0.0" /> - <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.2" /> - <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" /> - <PackageReference Include="System.Threading.Tasks.Dataflow" Version="7.0.0" /> + <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" /> + <PackageReference Include="Microsoft.Extensions.Configuration.Binder" /> + <PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="All" /> + <PackageReference Include="System.Threading.Tasks.Dataflow" /> </ItemGroup> <ItemGroup> @@ -51,13 +51,13 @@ <!-- Code Analyzers--> <ItemGroup Condition=" '$(Configuration)' == 'Debug' "> - <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4"> + <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference> - <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" /> - <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="All" /> - <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" /> + <PackageReference Include="SerilogAnalyzer" PrivateAssets="All" /> + <PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" /> + <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" /> </ItemGroup> </Project> diff --git a/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj b/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj index 039127f9e3..71cdea5298 100644 --- a/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj +++ b/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj @@ -22,13 +22,13 @@ <!-- Code Analyzers--> <ItemGroup Condition=" '$(Configuration)' == 'Debug' "> - <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4"> + <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference> - <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" /> - <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="All" /> - <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" /> + <PackageReference Include="SerilogAnalyzer" PrivateAssets="All" /> + <PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" /> + <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" /> </ItemGroup> </Project> diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj index 93177298f9..f4438fe194 100644 --- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj +++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj @@ -22,21 +22,21 @@ </ItemGroup> <ItemGroup> - <PackageReference Include="libse" Version="3.6.10" /> - <PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" /> - <PackageReference Include="System.Text.Encoding.CodePages" Version="7.0.0" /> - <PackageReference Include="UTF.Unknown" Version="2.5.1" /> + <PackageReference Include="libse" /> + <PackageReference Include="Microsoft.Extensions.Http" /> + <PackageReference Include="System.Text.Encoding.CodePages" /> + <PackageReference Include="UTF.Unknown" /> </ItemGroup> <!-- Code Analyzers--> <ItemGroup Condition=" '$(Configuration)' == 'Debug' "> - <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4"> + <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference> - <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" /> - <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="All" /> - <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" /> + <PackageReference Include="SerilogAnalyzer" PrivateAssets="All" /> + <PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" /> + <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" /> </ItemGroup> </Project> diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index 521ba0f107..9a58044853 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -33,14 +33,14 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" /> - <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.0" /> - <PackageReference Include="MimeTypes" Version="2.4.0"> + <PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="All" /> + <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" /> + <PackageReference Include="MimeTypes"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> - <PackageReference Include="System.Globalization" Version="4.3.0" /> - <PackageReference Include="System.Text.Json" Version="7.0.1" /> + <PackageReference Include="System.Globalization" /> + <PackageReference Include="System.Text.Json" /> </ItemGroup> <ItemGroup> @@ -49,13 +49,13 @@ <!-- Code Analyzers--> <ItemGroup Condition=" '$(Configuration)' == 'Debug' "> - <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4"> + <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference> - <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" /> - <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="All" /> - <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" /> + <PackageReference Include="SerilogAnalyzer" PrivateAssets="All" /> + <PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" /> + <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" /> </ItemGroup> <ItemGroup> <ProjectReference Include="../Jellyfin.Data/Jellyfin.Data.csproj" /> diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 9e238e9f3a..6a40833d7f 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -15,15 +15,15 @@ </ItemGroup> <ItemGroup> - <PackageReference Include="LrcParser" Version="2022.529.1" /> - <PackageReference Include="MetaBrainz.MusicBrainz" Version="5.0.0" /> - <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="7.0.0" /> - <PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="7.0.0" /> - <PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" /> - <PackageReference Include="Newtonsoft.Json" Version="13.0.2" /> - <PackageReference Include="PlaylistsNET" Version="1.3.1" /> - <PackageReference Include="TagLibSharp" Version="2.3.0" /> - <PackageReference Include="TMDbLib" Version="2.0.0" /> + <PackageReference Include="LrcParser" /> + <PackageReference Include="MetaBrainz.MusicBrainz" /> + <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" /> + <PackageReference Include="Microsoft.Extensions.Caching.Abstractions" /> + <PackageReference Include="Microsoft.Extensions.Http" /> + <PackageReference Include="Newtonsoft.Json" /> + <PackageReference Include="PlaylistsNET" /> + <PackageReference Include="TagLibSharp" /> + <PackageReference Include="TMDbLib" /> </ItemGroup> <PropertyGroup> @@ -35,13 +35,13 @@ <!-- Code Analyzers--> <ItemGroup Condition=" '$(Configuration)' == 'Debug' "> - <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4"> + <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference> - <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" /> - <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="All" /> - <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" /> + <PackageReference Include="SerilogAnalyzer" PrivateAssets="All" /> + <PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" /> + <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" /> </ItemGroup> <ItemGroup> diff --git a/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj b/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj index c25932a5a1..8072349152 100644 --- a/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj +++ b/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj @@ -22,13 +22,13 @@ <!-- Code Analyzers--> <ItemGroup Condition=" '$(Configuration)' == 'Debug' "> - <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4"> + <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference> - <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" /> - <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="All" /> - <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" /> + <PackageReference Include="SerilogAnalyzer" PrivateAssets="All" /> + <PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" /> + <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" /> </ItemGroup> </Project> diff --git a/fuzz/Emby.Server.Implementations.Fuzz/Emby.Server.Implementations.Fuzz.csproj b/fuzz/Emby.Server.Implementations.Fuzz/Emby.Server.Implementations.Fuzz.csproj index 51df09a210..1e3f8a0482 100644 --- a/fuzz/Emby.Server.Implementations.Fuzz/Emby.Server.Implementations.Fuzz.csproj +++ b/fuzz/Emby.Server.Implementations.Fuzz/Emby.Server.Implementations.Fuzz.csproj @@ -16,10 +16,10 @@ </ItemGroup> <ItemGroup> - <PackageReference Include="AutoFixture" Version="4.17.0" /> - <PackageReference Include="AutoFixture.AutoMoq" Version="4.17.0" /> - <PackageReference Include="Moq" Version="4.18.4" /> - <PackageReference Include="SharpFuzz" Version="2.0.1" /> + <PackageReference Include="AutoFixture" /> + <PackageReference Include="AutoFixture.AutoMoq" /> + <PackageReference Include="Moq" /> + <PackageReference Include="SharpFuzz" /> </ItemGroup> </Project> diff --git a/fuzz/Jellyfin.Server.Fuzz/Jellyfin.Server.Fuzz.csproj b/fuzz/Jellyfin.Server.Fuzz/Jellyfin.Server.Fuzz.csproj index 226ab60daa..20bc4c7244 100644 --- a/fuzz/Jellyfin.Server.Fuzz/Jellyfin.Server.Fuzz.csproj +++ b/fuzz/Jellyfin.Server.Fuzz/Jellyfin.Server.Fuzz.csproj @@ -16,7 +16,7 @@ </ItemGroup> <ItemGroup> - <PackageReference Include="SharpFuzz" Version="2.0.1" /> + <PackageReference Include="SharpFuzz" /> </ItemGroup> </Project> diff --git a/src/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj b/src/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj index a62ebf78c7..3b03332994 100644 --- a/src/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj +++ b/src/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj @@ -16,11 +16,11 @@ </ItemGroup> <ItemGroup> - <PackageReference Include="BlurHashSharp" Version="1.2.0" /> - <PackageReference Include="BlurHashSharp.SkiaSharp" Version="1.2.0" /> - <PackageReference Include="SkiaSharp" Version="2.88.3" /> - <PackageReference Include="SkiaSharp.NativeAssets.Linux" Version="2.88.3" /> - <PackageReference Include="SkiaSharp.Svg" Version="1.60.0" /> + <PackageReference Include="BlurHashSharp" /> + <PackageReference Include="BlurHashSharp.SkiaSharp" /> + <PackageReference Include="SkiaSharp" /> + <PackageReference Include="SkiaSharp.NativeAssets.Linux" /> + <PackageReference Include="SkiaSharp.Svg" /> </ItemGroup> <ItemGroup> @@ -31,13 +31,13 @@ <!-- Code analysers--> <ItemGroup Condition=" '$(Configuration)' == 'Debug' "> - <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4"> + <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference> - <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" /> - <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="All" /> - <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" /> + <PackageReference Include="SerilogAnalyzer" PrivateAssets="All" /> + <PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" /> + <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" /> </ItemGroup> </Project> diff --git a/src/Jellyfin.Drawing/Jellyfin.Drawing.csproj b/src/Jellyfin.Drawing/Jellyfin.Drawing.csproj index 7aa9945033..e0963ac34e 100644 --- a/src/Jellyfin.Drawing/Jellyfin.Drawing.csproj +++ b/src/Jellyfin.Drawing/Jellyfin.Drawing.csproj @@ -23,13 +23,13 @@ <!-- Code analysers--> <ItemGroup Condition=" '$(Configuration)' == 'Debug' "> - <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4"> + <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference> - <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" /> - <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="All" /> - <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" /> + <PackageReference Include="SerilogAnalyzer" PrivateAssets="All" /> + <PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" /> + <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" /> </ItemGroup> </Project> diff --git a/src/Jellyfin.Extensions/Jellyfin.Extensions.csproj b/src/Jellyfin.Extensions/Jellyfin.Extensions.csproj index d7c05ea576..15261bb65e 100644 --- a/src/Jellyfin.Extensions/Jellyfin.Extensions.csproj +++ b/src/Jellyfin.Extensions/Jellyfin.Extensions.csproj @@ -29,18 +29,18 @@ <ItemGroup> - <PackageReference Include="Diacritics" Version="3.3.14" /> + <PackageReference Include="Diacritics" /> </ItemGroup> <!-- Code Analyzers--> <ItemGroup> - <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4"> + <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference> - <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" /> - <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="All" /> - <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" /> + <PackageReference Include="SerilogAnalyzer" PrivateAssets="All" /> + <PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" /> + <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" /> </ItemGroup> </Project> diff --git a/src/Jellyfin.MediaEncoding.Hls/Jellyfin.MediaEncoding.Hls.csproj b/src/Jellyfin.MediaEncoding.Hls/Jellyfin.MediaEncoding.Hls.csproj index 9a025d5586..f489d6fd0a 100644 --- a/src/Jellyfin.MediaEncoding.Hls/Jellyfin.MediaEncoding.Hls.csproj +++ b/src/Jellyfin.MediaEncoding.Hls/Jellyfin.MediaEncoding.Hls.csproj @@ -7,13 +7,13 @@ <!-- Code Analyzers--> <ItemGroup> - <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4"> + <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference> - <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" /> - <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="All" /> - <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" /> + <PackageReference Include="SerilogAnalyzer" PrivateAssets="All" /> + <PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" /> + <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" /> </ItemGroup> <ItemGroup> @@ -23,7 +23,7 @@ </ItemGroup> <ItemGroup> - <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" /> + <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" /> </ItemGroup> <ItemGroup> diff --git a/src/Jellyfin.MediaEncoding.Keyframes/Jellyfin.MediaEncoding.Keyframes.csproj b/src/Jellyfin.MediaEncoding.Keyframes/Jellyfin.MediaEncoding.Keyframes.csproj index fe4e576937..3801a1cc33 100644 --- a/src/Jellyfin.MediaEncoding.Keyframes/Jellyfin.MediaEncoding.Keyframes.csproj +++ b/src/Jellyfin.MediaEncoding.Keyframes/Jellyfin.MediaEncoding.Keyframes.csproj @@ -6,22 +6,22 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="NEbml" Version="0.11.0" /> + <PackageReference Include="NEbml" /> </ItemGroup> <!-- Code Analyzers--> <ItemGroup> - <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4"> + <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference> - <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" /> - <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="All" /> - <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" /> + <PackageReference Include="SerilogAnalyzer" PrivateAssets="All" /> + <PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" /> + <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" /> </ItemGroup> <ItemGroup> - <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.0" /> + <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" /> </ItemGroup> <ItemGroup> diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj index 6966d81d46..6202f83dcf 100644 --- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj +++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj @@ -12,30 +12,30 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="AutoFixture" Version="4.17.0" /> - <PackageReference Include="AutoFixture.AutoMoq" Version="4.17.0" /> - <PackageReference Include="AutoFixture.Xunit2" Version="4.17.0" /> - <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="7.0.2" /> - <PackageReference Include="Microsoft.Extensions.Options" Version="7.0.0" /> - <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" /> - <PackageReference Include="xunit" Version="2.4.2" /> - <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5"> + <PackageReference Include="AutoFixture" /> + <PackageReference Include="AutoFixture.AutoMoq" /> + <PackageReference Include="AutoFixture.Xunit2" /> + <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" /> + <PackageReference Include="Microsoft.Extensions.Options" /> + <PackageReference Include="Microsoft.NET.Test.Sdk" /> + <PackageReference Include="xunit" /> + <PackageReference Include="xunit.runner.visualstudio"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> - <PackageReference Include="coverlet.collector" Version="3.2.0" /> - <PackageReference Include="Moq" Version="4.18.4" /> + <PackageReference Include="coverlet.collector" /> + <PackageReference Include="Moq" /> </ItemGroup> <!-- Code Analyzers --> <ItemGroup Condition=" '$(Configuration)' == 'Debug' "> - <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4"> + <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference> - <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" /> - <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="All" /> - <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" /> + <PackageReference Include="SerilogAnalyzer" PrivateAssets="All" /> + <PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" /> + <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" /> </ItemGroup> <ItemGroup> diff --git a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj index 5110d59176..699c12217c 100644 --- a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj +++ b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj @@ -12,25 +12,25 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" /> - <PackageReference Include="xunit" Version="2.4.2" /> - <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5"> + <PackageReference Include="Microsoft.NET.Test.Sdk" /> + <PackageReference Include="xunit" /> + <PackageReference Include="xunit.runner.visualstudio"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> - <PackageReference Include="coverlet.collector" Version="3.2.0" /> - <PackageReference Include="FsCheck.Xunit" Version="2.16.5" /> + <PackageReference Include="coverlet.collector" /> + <PackageReference Include="FsCheck.Xunit" /> </ItemGroup> <!-- Code Analyzers --> <ItemGroup Condition=" '$(Configuration)' == 'Debug' "> - <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4"> + <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference> - <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" /> - <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="All" /> - <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" /> + <PackageReference Include="SerilogAnalyzer" PrivateAssets="All" /> + <PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" /> + <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" /> </ItemGroup> <ItemGroup> diff --git a/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj b/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj index 97350fedad..1e729a46a4 100644 --- a/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj +++ b/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj @@ -12,25 +12,25 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" /> - <PackageReference Include="Moq" Version="4.18.4" /> - <PackageReference Include="xunit" Version="2.4.2" /> - <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5"> + <PackageReference Include="Microsoft.NET.Test.Sdk" /> + <PackageReference Include="Moq" /> + <PackageReference Include="xunit" /> + <PackageReference Include="xunit.runner.visualstudio"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> - <PackageReference Include="coverlet.collector" Version="3.2.0" /> + <PackageReference Include="coverlet.collector" /> </ItemGroup> <!-- Code Analyzers --> <ItemGroup Condition=" '$(Configuration)' == 'Debug' "> - <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4"> + <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference> - <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" /> - <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="All" /> - <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" /> + <PackageReference Include="SerilogAnalyzer" PrivateAssets="All" /> + <PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" /> + <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" /> </ItemGroup> <ItemGroup> diff --git a/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj b/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj index a2ecd60838..2be5da2c2c 100644 --- a/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj +++ b/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj @@ -7,25 +7,25 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" /> - <PackageReference Include="Moq" Version="4.18.4" /> - <PackageReference Include="xunit" Version="2.4.2" /> - <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5"> + <PackageReference Include="Microsoft.NET.Test.Sdk" /> + <PackageReference Include="Moq" /> + <PackageReference Include="xunit" /> + <PackageReference Include="xunit.runner.visualstudio"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> - <PackageReference Include="coverlet.collector" Version="3.2.0" /> + <PackageReference Include="coverlet.collector" /> </ItemGroup> <!-- Code Analyzers --> <ItemGroup Condition=" '$(Configuration)' == 'Debug' "> - <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4"> + <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference> - <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" /> - <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="All" /> - <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" /> + <PackageReference Include="SerilogAnalyzer" PrivateAssets="All" /> + <PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" /> + <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" /> </ItemGroup> <ItemGroup> diff --git a/tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj b/tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj index 313192b241..dbbb61cc45 100644 --- a/tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj +++ b/tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj @@ -7,28 +7,28 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" /> - <PackageReference Include="xunit" Version="2.4.2" /> - <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5"> + <PackageReference Include="Microsoft.NET.Test.Sdk" /> + <PackageReference Include="xunit" /> + <PackageReference Include="xunit.runner.visualstudio"> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <PrivateAssets>all</PrivateAssets> </PackageReference> - <PackageReference Include="coverlet.collector" Version="3.2.0"> + <PackageReference Include="coverlet.collector"> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <PrivateAssets>all</PrivateAssets> </PackageReference> - <PackageReference Include="FsCheck.Xunit" Version="2.16.5" /> + <PackageReference Include="FsCheck.Xunit" /> </ItemGroup> <!-- Code Analyzers --> <ItemGroup Condition=" '$(Configuration)' == 'Debug' "> - <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4"> + <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference> - <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" /> - <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="All" /> - <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" /> + <PackageReference Include="SerilogAnalyzer" PrivateAssets="All" /> + <PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" /> + <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" /> </ItemGroup> <ItemGroup> diff --git a/tests/Jellyfin.MediaEncoding.Hls.Tests/Jellyfin.MediaEncoding.Hls.Tests.csproj b/tests/Jellyfin.MediaEncoding.Hls.Tests/Jellyfin.MediaEncoding.Hls.Tests.csproj index 22b0c417b0..10c1418731 100644 --- a/tests/Jellyfin.MediaEncoding.Hls.Tests/Jellyfin.MediaEncoding.Hls.Tests.csproj +++ b/tests/Jellyfin.MediaEncoding.Hls.Tests/Jellyfin.MediaEncoding.Hls.Tests.csproj @@ -7,13 +7,13 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" /> - <PackageReference Include="xunit" Version="2.4.2" /> - <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5"> + <PackageReference Include="Microsoft.NET.Test.Sdk" /> + <PackageReference Include="xunit" /> + <PackageReference Include="xunit.runner.visualstudio"> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <PrivateAssets>all</PrivateAssets> </PackageReference> - <PackageReference Include="coverlet.collector" Version="3.2.0"> + <PackageReference Include="coverlet.collector"> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <PrivateAssets>all</PrivateAssets> </PackageReference> @@ -21,13 +21,13 @@ <!-- Code Analyzers --> <ItemGroup> - <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4"> + <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference> - <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" /> - <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="All" /> - <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" /> + <PackageReference Include="SerilogAnalyzer" PrivateAssets="All" /> + <PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" /> + <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\..\src\Jellyfin.MediaEncoding.Hls\Jellyfin.MediaEncoding.Hls.csproj" /> diff --git a/tests/Jellyfin.MediaEncoding.Keyframes.Tests/Jellyfin.MediaEncoding.Keyframes.Tests.csproj b/tests/Jellyfin.MediaEncoding.Keyframes.Tests/Jellyfin.MediaEncoding.Keyframes.Tests.csproj index 373a54504e..4910a041a3 100644 --- a/tests/Jellyfin.MediaEncoding.Keyframes.Tests/Jellyfin.MediaEncoding.Keyframes.Tests.csproj +++ b/tests/Jellyfin.MediaEncoding.Keyframes.Tests/Jellyfin.MediaEncoding.Keyframes.Tests.csproj @@ -8,13 +8,13 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" /> - <PackageReference Include="xunit" Version="2.4.2" /> - <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5"> + <PackageReference Include="Microsoft.NET.Test.Sdk" /> + <PackageReference Include="xunit" /> + <PackageReference Include="xunit.runner.visualstudio"> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <PrivateAssets>all</PrivateAssets> </PackageReference> - <PackageReference Include="coverlet.collector" Version="3.2.0"> + <PackageReference Include="coverlet.collector"> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <PrivateAssets>all</PrivateAssets> </PackageReference> @@ -22,13 +22,13 @@ <!-- Code Analyzers --> <ItemGroup Condition=" '$(Configuration)' == 'Debug' "> - <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4"> + <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference> - <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" /> - <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="All" /> - <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" /> + <PackageReference Include="SerilogAnalyzer" PrivateAssets="All" /> + <PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" /> + <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" /> </ItemGroup> <ItemGroup> diff --git a/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj b/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj index a9a0dbc226..077466b6e6 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj +++ b/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj @@ -18,14 +18,14 @@ </ItemGroup> <ItemGroup> - <PackageReference Include="AutoFixture" Version="4.17.0" /> - <PackageReference Include="AutoFixture.AutoMoq" Version="4.17.0" /> - <PackageReference Include="AutoFixture.Xunit2" Version="4.17.0" /> - <PackageReference Include="coverlet.collector" Version="3.2.0" /> - <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" /> - <PackageReference Include="Moq" Version="4.18.4" /> - <PackageReference Include="xunit" Version="2.4.2" /> - <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5"> + <PackageReference Include="AutoFixture" /> + <PackageReference Include="AutoFixture.AutoMoq" /> + <PackageReference Include="AutoFixture.Xunit2" /> + <PackageReference Include="coverlet.collector" /> + <PackageReference Include="Microsoft.NET.Test.Sdk" /> + <PackageReference Include="Moq" /> + <PackageReference Include="xunit" /> + <PackageReference Include="xunit.runner.visualstudio"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> @@ -33,13 +33,13 @@ <!-- Code Analyzers --> <ItemGroup Condition=" '$(Configuration)' == 'Debug' "> - <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4"> + <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference> - <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" /> - <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="All" /> - <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" /> + <PackageReference Include="SerilogAnalyzer" PrivateAssets="All" /> + <PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" /> + <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" /> </ItemGroup> <ItemGroup> diff --git a/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj b/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj index 9858623f82..cffd7bc0bb 100644 --- a/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj +++ b/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj @@ -7,15 +7,15 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" /> - <PackageReference Include="Moq" Version="4.18.4" /> - <PackageReference Include="xunit" Version="2.4.2" /> - <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5"> + <PackageReference Include="Microsoft.NET.Test.Sdk" /> + <PackageReference Include="Moq" /> + <PackageReference Include="xunit" /> + <PackageReference Include="xunit.runner.visualstudio"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> - <PackageReference Include="coverlet.collector" Version="3.2.0" /> - <PackageReference Include="FsCheck.Xunit" Version="2.16.5" /> + <PackageReference Include="coverlet.collector" /> + <PackageReference Include="FsCheck.Xunit" /> </ItemGroup> <ItemGroup> @@ -26,13 +26,13 @@ <!-- Code Analyzers --> <ItemGroup Condition=" '$(Configuration)' == 'Debug' "> - <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4"> + <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference> - <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" /> - <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="All" /> - <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" /> + <PackageReference Include="SerilogAnalyzer" PrivateAssets="All" /> + <PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" /> + <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" /> </ItemGroup> <ItemGroup> diff --git a/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj b/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj index 920f490ed0..c5e93f0bbe 100644 --- a/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj +++ b/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj @@ -12,14 +12,14 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" /> - <PackageReference Include="Moq" Version="4.18.4" /> - <PackageReference Include="xunit" Version="2.4.2" /> - <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5"> + <PackageReference Include="Microsoft.NET.Test.Sdk" /> + <PackageReference Include="Moq" /> + <PackageReference Include="xunit" /> + <PackageReference Include="xunit.runner.visualstudio"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> - <PackageReference Include="coverlet.collector" Version="3.2.0" /> + <PackageReference Include="coverlet.collector" /> </ItemGroup> <ItemGroup> @@ -28,13 +28,13 @@ <!-- Code Analyzers--> <ItemGroup Condition=" '$(Configuration)' == 'Debug' "> - <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4"> + <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference> - <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" /> - <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="All" /> - <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" /> + <PackageReference Include="SerilogAnalyzer" PrivateAssets="All" /> + <PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" /> + <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" /> </ItemGroup> </Project> diff --git a/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj b/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj index 74bf7cb0e0..e245699269 100644 --- a/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj +++ b/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj @@ -12,26 +12,26 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" /> - <PackageReference Include="xunit" Version="2.4.2" /> - <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5"> + <PackageReference Include="Microsoft.NET.Test.Sdk" /> + <PackageReference Include="xunit" /> + <PackageReference Include="xunit.runner.visualstudio"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> - <PackageReference Include="coverlet.collector" Version="3.2.0" /> - <PackageReference Include="FsCheck.Xunit" Version="2.16.5" /> - <PackageReference Include="Moq" Version="4.18.4" /> + <PackageReference Include="coverlet.collector" /> + <PackageReference Include="FsCheck.Xunit" /> + <PackageReference Include="Moq" /> </ItemGroup> <!-- Code Analyzers--> <ItemGroup Condition=" '$(Configuration)' == 'Debug' "> - <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4"> + <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference> - <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" /> - <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="All" /> - <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" /> + <PackageReference Include="SerilogAnalyzer" PrivateAssets="All" /> + <PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" /> + <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" /> </ItemGroup> <ItemGroup> diff --git a/tests/Jellyfin.Providers.Tests/Jellyfin.Providers.Tests.csproj b/tests/Jellyfin.Providers.Tests/Jellyfin.Providers.Tests.csproj index d3292c38eb..27151c8479 100644 --- a/tests/Jellyfin.Providers.Tests/Jellyfin.Providers.Tests.csproj +++ b/tests/Jellyfin.Providers.Tests/Jellyfin.Providers.Tests.csproj @@ -13,14 +13,14 @@ </ItemGroup> <ItemGroup> - <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" /> - <PackageReference Include="Moq" Version="4.18.4" /> - <PackageReference Include="xunit" Version="2.4.2" /> - <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5"> + <PackageReference Include="Microsoft.NET.Test.Sdk" /> + <PackageReference Include="Moq" /> + <PackageReference Include="xunit" /> + <PackageReference Include="xunit.runner.visualstudio"> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <PrivateAssets>all</PrivateAssets> </PackageReference> - <PackageReference Include="coverlet.collector" Version="3.2.0"> + <PackageReference Include="coverlet.collector"> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <PrivateAssets>all</PrivateAssets> </PackageReference> @@ -28,13 +28,13 @@ <!-- Code Analyzers --> <ItemGroup Condition=" '$(Configuration)' == 'Debug' "> - <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4"> + <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference> - <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" /> - <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="All" /> - <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" /> + <PackageReference Include="SerilogAnalyzer" PrivateAssets="All" /> + <PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" /> + <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" /> </ItemGroup> <ItemGroup> diff --git a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj index b796e07d1a..2150520e5f 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj +++ b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj @@ -19,28 +19,28 @@ </ItemGroup> <ItemGroup> - <PackageReference Include="AutoFixture" Version="4.17.0" /> - <PackageReference Include="AutoFixture.AutoMoq" Version="4.17.0" /> - <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" /> - <PackageReference Include="Moq" Version="4.18.4" /> - <PackageReference Include="xunit" Version="2.4.2" /> - <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5"> + <PackageReference Include="AutoFixture" /> + <PackageReference Include="AutoFixture.AutoMoq" /> + <PackageReference Include="Microsoft.NET.Test.Sdk" /> + <PackageReference Include="Moq" /> + <PackageReference Include="xunit" /> + <PackageReference Include="xunit.runner.visualstudio"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> - <PackageReference Include="Xunit.SkippableFact" Version="1.4.13" /> - <PackageReference Include="coverlet.collector" Version="3.2.0" /> + <PackageReference Include="Xunit.SkippableFact" /> + <PackageReference Include="coverlet.collector" /> </ItemGroup> <!-- Code Analyzers --> <ItemGroup Condition=" '$(Configuration)' == 'Debug' "> - <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4"> + <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference> - <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" /> - <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="All" /> - <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" /> + <PackageReference Include="SerilogAnalyzer" PrivateAssets="All" /> + <PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" /> + <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" /> </ItemGroup> <ItemGroup> diff --git a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj index c40f6942b1..26b2cd2395 100644 --- a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj +++ b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj @@ -6,20 +6,20 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="AutoFixture" Version="4.17.0" /> - <PackageReference Include="AutoFixture.AutoMoq" Version="4.17.0" /> - <PackageReference Include="AutoFixture.Xunit2" Version="4.17.0" /> - <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="7.0.2" /> - <PackageReference Include="Microsoft.Extensions.Options" Version="7.0.0" /> - <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" /> - <PackageReference Include="xunit" Version="2.4.2" /> - <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5"> + <PackageReference Include="AutoFixture" /> + <PackageReference Include="AutoFixture.AutoMoq" /> + <PackageReference Include="AutoFixture.Xunit2" /> + <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" /> + <PackageReference Include="Microsoft.Extensions.Options" /> + <PackageReference Include="Microsoft.NET.Test.Sdk" /> + <PackageReference Include="xunit" /> + <PackageReference Include="xunit.runner.visualstudio"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> - <PackageReference Include="Xunit.Priority" Version="1.1.6" /> - <PackageReference Include="coverlet.collector" Version="3.2.0" /> - <PackageReference Include="Moq" Version="4.18.4" /> + <PackageReference Include="Xunit.Priority" /> + <PackageReference Include="coverlet.collector" /> + <PackageReference Include="Moq" /> </ItemGroup> <ItemGroup> @@ -31,13 +31,13 @@ <!-- Code Analyzers --> <ItemGroup Condition=" '$(Configuration)' == 'Debug' "> - <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4"> + <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference> - <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" /> - <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="All" /> - <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" /> + <PackageReference Include="SerilogAnalyzer" PrivateAssets="All" /> + <PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" /> + <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" /> </ItemGroup> <ItemGroup> diff --git a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj index a72a6f1855..d47f70cffa 100644 --- a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj +++ b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj @@ -7,30 +7,30 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="AutoFixture" Version="4.17.0" /> - <PackageReference Include="AutoFixture.AutoMoq" Version="4.17.0" /> - <PackageReference Include="AutoFixture.Xunit2" Version="4.17.0" /> - <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="7.0.2" /> - <PackageReference Include="Microsoft.Extensions.Options" Version="7.0.0" /> - <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" /> - <PackageReference Include="xunit" Version="2.4.2" /> - <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5"> + <PackageReference Include="AutoFixture" /> + <PackageReference Include="AutoFixture.AutoMoq" /> + <PackageReference Include="AutoFixture.Xunit2" /> + <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" /> + <PackageReference Include="Microsoft.Extensions.Options" /> + <PackageReference Include="Microsoft.NET.Test.Sdk" /> + <PackageReference Include="xunit" /> + <PackageReference Include="xunit.runner.visualstudio"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> - <PackageReference Include="coverlet.collector" Version="3.2.0" /> - <PackageReference Include="Moq" Version="4.18.4" /> + <PackageReference Include="coverlet.collector" /> + <PackageReference Include="Moq" /> </ItemGroup> <!-- Code Analyzers --> <ItemGroup Condition=" '$(Configuration)' == 'Debug' "> - <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4"> + <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference> - <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" /> - <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="All" /> - <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" /> + <PackageReference Include="SerilogAnalyzer" PrivateAssets="All" /> + <PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" /> + <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" /> </ItemGroup> <ItemGroup> diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj b/tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj index dc5b5b9e6b..fb7864cd13 100644 --- a/tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj +++ b/tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj @@ -13,25 +13,25 @@ </ItemGroup> <ItemGroup> - <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" /> - <PackageReference Include="Moq" Version="4.18.4" /> - <PackageReference Include="xunit" Version="2.4.2" /> - <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5"> + <PackageReference Include="Microsoft.NET.Test.Sdk" /> + <PackageReference Include="Moq" /> + <PackageReference Include="xunit" /> + <PackageReference Include="xunit.runner.visualstudio"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> - <PackageReference Include="coverlet.collector" Version="3.2.0" /> + <PackageReference Include="coverlet.collector" /> </ItemGroup> <!-- Code Analyzers --> <ItemGroup Condition=" '$(Configuration)' == 'Debug' "> - <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4"> + <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference> - <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" /> - <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="All" /> - <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" /> + <PackageReference Include="SerilogAnalyzer" PrivateAssets="All" /> + <PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" /> + <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" /> </ItemGroup> <ItemGroup> -- cgit v1.2.3 From 519709bf10e30a3666af100df4fcca206dcef498 Mon Sep 17 00:00:00 2001 From: Shadowghost <Ghost_of_Stone@web.de> Date: Fri, 3 Feb 2023 18:49:23 +0100 Subject: Revert "Remove DvdLib (#9068)" This reverts commit db1913b08fac0749133634efebd1ee7a7876147a. --- DvdLib/BigEndianBinaryReader.cs | 25 +++ DvdLib/DvdLib.csproj | 20 +++ DvdLib/Ifo/Cell.cs | 23 +++ DvdLib/Ifo/CellPlaybackInfo.cs | 52 ++++++ DvdLib/Ifo/CellPositionInfo.cs | 19 ++ DvdLib/Ifo/Chapter.cs | 20 +++ DvdLib/Ifo/Dvd.cs | 167 ++++++++++++++++++ DvdLib/Ifo/DvdTime.cs | 39 +++++ DvdLib/Ifo/Program.cs | 16 ++ DvdLib/Ifo/ProgramChain.cs | 121 +++++++++++++ DvdLib/Ifo/Title.cs | 70 ++++++++ DvdLib/Ifo/UserOperation.cs | 37 ++++ DvdLib/Properties/AssemblyInfo.cs | 21 +++ Emby.Server.Implementations/ApplicationHost.cs | 4 + Jellyfin.sln | 6 + .../MediaEncoding/IMediaEncoder.cs | 8 + .../BdInfo/BdInfoDirectoryInfo.cs | 83 +++++++++ .../BdInfo/BdInfoExaminer.cs | 194 +++++++++++++++++++++ .../BdInfo/BdInfoFileInfo.cs | 41 +++++ MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 79 +++++++++ .../MediaBrowser.MediaEncoding.csproj | 1 + MediaBrowser.Model/MediaInfo/BlurayDiscInfo.cs | 39 +++++ MediaBrowser.Model/MediaInfo/IBlurayExaminer.cs | 15 ++ .../MediaBrowser.Providers.csproj | 1 + .../MediaInfo/FFProbeVideoInfo.cs | 155 +++++++++++++++- MediaBrowser.Providers/MediaInfo/ProbeProvider.cs | 3 + 26 files changed, 1258 insertions(+), 1 deletion(-) create mode 100644 DvdLib/BigEndianBinaryReader.cs create mode 100644 DvdLib/DvdLib.csproj create mode 100644 DvdLib/Ifo/Cell.cs create mode 100644 DvdLib/Ifo/CellPlaybackInfo.cs create mode 100644 DvdLib/Ifo/CellPositionInfo.cs create mode 100644 DvdLib/Ifo/Chapter.cs create mode 100644 DvdLib/Ifo/Dvd.cs create mode 100644 DvdLib/Ifo/DvdTime.cs create mode 100644 DvdLib/Ifo/Program.cs create mode 100644 DvdLib/Ifo/ProgramChain.cs create mode 100644 DvdLib/Ifo/Title.cs create mode 100644 DvdLib/Ifo/UserOperation.cs create mode 100644 DvdLib/Properties/AssemblyInfo.cs create mode 100644 MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs create mode 100644 MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs create mode 100644 MediaBrowser.MediaEncoding/BdInfo/BdInfoFileInfo.cs create mode 100644 MediaBrowser.Model/MediaInfo/BlurayDiscInfo.cs create mode 100644 MediaBrowser.Model/MediaInfo/IBlurayExaminer.cs (limited to 'Emby.Server.Implementations') diff --git a/DvdLib/BigEndianBinaryReader.cs b/DvdLib/BigEndianBinaryReader.cs new file mode 100644 index 0000000000..b3aad85cec --- /dev/null +++ b/DvdLib/BigEndianBinaryReader.cs @@ -0,0 +1,25 @@ +#pragma warning disable CS1591 + +using System.Buffers.Binary; +using System.IO; + +namespace DvdLib +{ + public class BigEndianBinaryReader : BinaryReader + { + public BigEndianBinaryReader(Stream input) + : base(input) + { + } + + public override ushort ReadUInt16() + { + return BinaryPrimitives.ReadUInt16BigEndian(base.ReadBytes(2)); + } + + public override uint ReadUInt32() + { + return BinaryPrimitives.ReadUInt32BigEndian(base.ReadBytes(4)); + } + } +} diff --git a/DvdLib/DvdLib.csproj b/DvdLib/DvdLib.csproj new file mode 100644 index 0000000000..1053c0089f --- /dev/null +++ b/DvdLib/DvdLib.csproj @@ -0,0 +1,20 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <!-- ProjectGuid is only included as a requirement for SonarQube analysis --> + <PropertyGroup> + <ProjectGuid>{713F42B5-878E-499D-A878-E4C652B1D5E8}</ProjectGuid> + </PropertyGroup> + + <ItemGroup> + <Compile Include="..\SharedVersion.cs" /> + </ItemGroup> + + <PropertyGroup> + <TargetFramework>net7.0</TargetFramework> + <GenerateAssemblyInfo>false</GenerateAssemblyInfo> + <GenerateDocumentationFile>true</GenerateDocumentationFile> + <AnalysisMode>AllDisabledByDefault</AnalysisMode> + <Nullable>disable</Nullable> + </PropertyGroup> + +</Project> diff --git a/DvdLib/Ifo/Cell.cs b/DvdLib/Ifo/Cell.cs new file mode 100644 index 0000000000..ea0b50e430 --- /dev/null +++ b/DvdLib/Ifo/Cell.cs @@ -0,0 +1,23 @@ +#pragma warning disable CS1591 + +using System.IO; + +namespace DvdLib.Ifo +{ + public class Cell + { + public CellPlaybackInfo PlaybackInfo { get; private set; } + + public CellPositionInfo PositionInfo { get; private set; } + + internal void ParsePlayback(BinaryReader br) + { + PlaybackInfo = new CellPlaybackInfo(br); + } + + internal void ParsePosition(BinaryReader br) + { + PositionInfo = new CellPositionInfo(br); + } + } +} diff --git a/DvdLib/Ifo/CellPlaybackInfo.cs b/DvdLib/Ifo/CellPlaybackInfo.cs new file mode 100644 index 0000000000..6e33a0ec5a --- /dev/null +++ b/DvdLib/Ifo/CellPlaybackInfo.cs @@ -0,0 +1,52 @@ +#pragma warning disable CS1591 + +using System.IO; + +namespace DvdLib.Ifo +{ + public enum BlockMode + { + NotInBlock = 0, + FirstCell = 1, + InBlock = 2, + LastCell = 3, + } + + public enum BlockType + { + Normal = 0, + Angle = 1, + } + + public enum PlaybackMode + { + Normal = 0, + StillAfterEachVOBU = 1, + } + + public class CellPlaybackInfo + { + public readonly BlockMode Mode; + public readonly BlockType Type; + public readonly bool SeamlessPlay; + public readonly bool Interleaved; + public readonly bool STCDiscontinuity; + public readonly bool SeamlessAngle; + public readonly PlaybackMode PlaybackMode; + public readonly bool Restricted; + public readonly byte StillTime; + public readonly byte CommandNumber; + public readonly DvdTime PlaybackTime; + public readonly uint FirstSector; + public readonly uint FirstILVUEndSector; + public readonly uint LastVOBUStartSector; + public readonly uint LastSector; + + internal CellPlaybackInfo(BinaryReader br) + { + br.BaseStream.Seek(0x4, SeekOrigin.Current); + PlaybackTime = new DvdTime(br.ReadBytes(4)); + br.BaseStream.Seek(0x10, SeekOrigin.Current); + } + } +} diff --git a/DvdLib/Ifo/CellPositionInfo.cs b/DvdLib/Ifo/CellPositionInfo.cs new file mode 100644 index 0000000000..216aa0f77a --- /dev/null +++ b/DvdLib/Ifo/CellPositionInfo.cs @@ -0,0 +1,19 @@ +#pragma warning disable CS1591 + +using System.IO; + +namespace DvdLib.Ifo +{ + public class CellPositionInfo + { + public readonly ushort VOBId; + public readonly byte CellId; + + internal CellPositionInfo(BinaryReader br) + { + VOBId = br.ReadUInt16(); + br.ReadByte(); + CellId = br.ReadByte(); + } + } +} diff --git a/DvdLib/Ifo/Chapter.cs b/DvdLib/Ifo/Chapter.cs new file mode 100644 index 0000000000..e786cb5536 --- /dev/null +++ b/DvdLib/Ifo/Chapter.cs @@ -0,0 +1,20 @@ +#pragma warning disable CS1591 + +namespace DvdLib.Ifo +{ + public class Chapter + { + public ushort ProgramChainNumber { get; private set; } + + public ushort ProgramNumber { get; private set; } + + public uint ChapterNumber { get; private set; } + + public Chapter(ushort pgcNum, ushort programNum, uint chapterNum) + { + ProgramChainNumber = pgcNum; + ProgramNumber = programNum; + ChapterNumber = chapterNum; + } + } +} diff --git a/DvdLib/Ifo/Dvd.cs b/DvdLib/Ifo/Dvd.cs new file mode 100644 index 0000000000..7f8ece47dc --- /dev/null +++ b/DvdLib/Ifo/Dvd.cs @@ -0,0 +1,167 @@ +#pragma warning disable CS1591 + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; + +namespace DvdLib.Ifo +{ + public class Dvd + { + private readonly ushort _titleSetCount; + public readonly List<Title> Titles; + + private ushort _titleCount; + public readonly Dictionary<ushort, string> VTSPaths = new Dictionary<ushort, string>(); + public Dvd(string path) + { + Titles = new List<Title>(); + var allFiles = new DirectoryInfo(path).GetFiles(path, SearchOption.AllDirectories); + + var vmgPath = allFiles.FirstOrDefault(i => string.Equals(i.Name, "VIDEO_TS.IFO", StringComparison.OrdinalIgnoreCase)) ?? + allFiles.FirstOrDefault(i => string.Equals(i.Name, "VIDEO_TS.BUP", StringComparison.OrdinalIgnoreCase)); + + if (vmgPath == null) + { + foreach (var ifo in allFiles) + { + if (!string.Equals(ifo.Extension, ".ifo", StringComparison.OrdinalIgnoreCase)) + { + continue; + } + + var nums = ifo.Name.Split('_', StringSplitOptions.RemoveEmptyEntries); + if (nums.Length >= 2 && ushort.TryParse(nums[1], out var ifoNumber)) + { + ReadVTS(ifoNumber, ifo.FullName); + } + } + } + else + { + using (var vmgFs = new FileStream(vmgPath.FullName, FileMode.Open, FileAccess.Read, FileShare.Read)) + { + using (var vmgRead = new BigEndianBinaryReader(vmgFs)) + { + vmgFs.Seek(0x3E, SeekOrigin.Begin); + _titleSetCount = vmgRead.ReadUInt16(); + + // read address of TT_SRPT + vmgFs.Seek(0xC4, SeekOrigin.Begin); + uint ttSectorPtr = vmgRead.ReadUInt32(); + vmgFs.Seek(ttSectorPtr * 2048, SeekOrigin.Begin); + ReadTT_SRPT(vmgRead); + } + } + + for (ushort titleSetNum = 1; titleSetNum <= _titleSetCount; titleSetNum++) + { + ReadVTS(titleSetNum, allFiles); + } + } + } + + private void ReadTT_SRPT(BinaryReader read) + { + _titleCount = read.ReadUInt16(); + read.BaseStream.Seek(6, SeekOrigin.Current); + for (uint titleNum = 1; titleNum <= _titleCount; titleNum++) + { + var t = new Title(titleNum); + t.ParseTT_SRPT(read); + Titles.Add(t); + } + } + + private void ReadVTS(ushort vtsNum, IReadOnlyList<FileInfo> allFiles) + { + var filename = string.Format(CultureInfo.InvariantCulture, "VTS_{0:00}_0.IFO", vtsNum); + + var vtsPath = allFiles.FirstOrDefault(i => string.Equals(i.Name, filename, StringComparison.OrdinalIgnoreCase)) ?? + allFiles.FirstOrDefault(i => string.Equals(i.Name, Path.ChangeExtension(filename, ".bup"), StringComparison.OrdinalIgnoreCase)); + + if (vtsPath == null) + { + throw new FileNotFoundException("Unable to find VTS IFO file"); + } + + ReadVTS(vtsNum, vtsPath.FullName); + } + + private void ReadVTS(ushort vtsNum, string vtsPath) + { + VTSPaths[vtsNum] = vtsPath; + + using (var vtsFs = new FileStream(vtsPath, FileMode.Open, FileAccess.Read, FileShare.Read)) + { + using (var vtsRead = new BigEndianBinaryReader(vtsFs)) + { + // Read VTS_PTT_SRPT + vtsFs.Seek(0xC8, SeekOrigin.Begin); + uint vtsPttSrptSecPtr = vtsRead.ReadUInt32(); + uint baseAddr = (vtsPttSrptSecPtr * 2048); + vtsFs.Seek(baseAddr, SeekOrigin.Begin); + + ushort numTitles = vtsRead.ReadUInt16(); + vtsRead.ReadUInt16(); + uint endaddr = vtsRead.ReadUInt32(); + uint[] offsets = new uint[numTitles]; + for (ushort titleNum = 0; titleNum < numTitles; titleNum++) + { + offsets[titleNum] = vtsRead.ReadUInt32(); + } + + for (uint titleNum = 0; titleNum < numTitles; titleNum++) + { + uint chapNum = 1; + vtsFs.Seek(baseAddr + offsets[titleNum], SeekOrigin.Begin); + var t = Titles.FirstOrDefault(vtst => vtst.IsVTSTitle(vtsNum, titleNum + 1)); + if (t == null) + { + continue; + } + + do + { + t.Chapters.Add(new Chapter(vtsRead.ReadUInt16(), vtsRead.ReadUInt16(), chapNum)); + if (titleNum + 1 < numTitles && vtsFs.Position == (baseAddr + offsets[titleNum + 1])) + { + break; + } + + chapNum++; + } + while (vtsFs.Position < (baseAddr + endaddr)); + } + + // Read VTS_PGCI + vtsFs.Seek(0xCC, SeekOrigin.Begin); + uint vtsPgciSecPtr = vtsRead.ReadUInt32(); + vtsFs.Seek(vtsPgciSecPtr * 2048, SeekOrigin.Begin); + + long startByte = vtsFs.Position; + + ushort numPgcs = vtsRead.ReadUInt16(); + vtsFs.Seek(6, SeekOrigin.Current); + for (ushort pgcNum = 1; pgcNum <= numPgcs; pgcNum++) + { + byte pgcCat = vtsRead.ReadByte(); + bool entryPgc = (pgcCat & 0x80) != 0; + uint titleNum = (uint)(pgcCat & 0x7F); + + vtsFs.Seek(3, SeekOrigin.Current); + uint vtsPgcOffset = vtsRead.ReadUInt32(); + + var t = Titles.FirstOrDefault(vtst => vtst.IsVTSTitle(vtsNum, titleNum)); + if (t != null) + { + t.AddPgc(vtsRead, startByte + vtsPgcOffset, entryPgc, pgcNum); + } + } + } + } + } + } +} diff --git a/DvdLib/Ifo/DvdTime.cs b/DvdLib/Ifo/DvdTime.cs new file mode 100644 index 0000000000..d231406106 --- /dev/null +++ b/DvdLib/Ifo/DvdTime.cs @@ -0,0 +1,39 @@ +#pragma warning disable CS1591 + +using System; + +namespace DvdLib.Ifo +{ + public class DvdTime + { + public readonly byte Hour, Minute, Second, Frames, FrameRate; + + public DvdTime(byte[] data) + { + Hour = GetBCDValue(data[0]); + Minute = GetBCDValue(data[1]); + Second = GetBCDValue(data[2]); + Frames = GetBCDValue((byte)(data[3] & 0x3F)); + + if ((data[3] & 0x80) != 0) + { + FrameRate = 30; + } + else if ((data[3] & 0x40) != 0) + { + FrameRate = 25; + } + } + + private static byte GetBCDValue(byte data) + { + return (byte)((((data & 0xF0) >> 4) * 10) + (data & 0x0F)); + } + + public static explicit operator TimeSpan(DvdTime time) + { + int ms = (int)(((1.0 / (double)time.FrameRate) * time.Frames) * 1000.0); + return new TimeSpan(0, time.Hour, time.Minute, time.Second, ms); + } + } +} diff --git a/DvdLib/Ifo/Program.cs b/DvdLib/Ifo/Program.cs new file mode 100644 index 0000000000..3d94fa7dc1 --- /dev/null +++ b/DvdLib/Ifo/Program.cs @@ -0,0 +1,16 @@ +#pragma warning disable CS1591 + +using System.Collections.Generic; + +namespace DvdLib.Ifo +{ + public class Program + { + public IReadOnlyList<Cell> Cells { get; } + + public Program(List<Cell> cells) + { + Cells = cells; + } + } +} diff --git a/DvdLib/Ifo/ProgramChain.cs b/DvdLib/Ifo/ProgramChain.cs new file mode 100644 index 0000000000..83c0051b90 --- /dev/null +++ b/DvdLib/Ifo/ProgramChain.cs @@ -0,0 +1,121 @@ +#pragma warning disable CS1591 + +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace DvdLib.Ifo +{ + public enum ProgramPlaybackMode + { + Sequential, + Random, + Shuffle + } + + public class ProgramChain + { + private byte _programCount; + public readonly List<Program> Programs; + + private byte _cellCount; + public readonly List<Cell> Cells; + + public DvdTime PlaybackTime { get; private set; } + + public UserOperation ProhibitedUserOperations { get; private set; } + + public byte[] AudioStreamControl { get; private set; } // 8*2 entries + public byte[] SubpictureStreamControl { get; private set; } // 32*4 entries + + private ushort _nextProgramNumber; + + private ushort _prevProgramNumber; + + private ushort _goupProgramNumber; + + public ProgramPlaybackMode PlaybackMode { get; private set; } + + public uint ProgramCount { get; private set; } + + public byte StillTime { get; private set; } + + public byte[] Palette { get; private set; } // 16*4 entries + + private ushort _commandTableOffset; + + private ushort _programMapOffset; + private ushort _cellPlaybackOffset; + private ushort _cellPositionOffset; + + public readonly uint VideoTitleSetIndex; + + internal ProgramChain(uint vtsPgcNum) + { + VideoTitleSetIndex = vtsPgcNum; + Cells = new List<Cell>(); + Programs = new List<Program>(); + } + + internal void ParseHeader(BinaryReader br) + { + long startPos = br.BaseStream.Position; + + br.ReadUInt16(); + _programCount = br.ReadByte(); + _cellCount = br.ReadByte(); + PlaybackTime = new DvdTime(br.ReadBytes(4)); + ProhibitedUserOperations = (UserOperation)br.ReadUInt32(); + AudioStreamControl = br.ReadBytes(16); + SubpictureStreamControl = br.ReadBytes(128); + + _nextProgramNumber = br.ReadUInt16(); + _prevProgramNumber = br.ReadUInt16(); + _goupProgramNumber = br.ReadUInt16(); + + StillTime = br.ReadByte(); + byte pbMode = br.ReadByte(); + if (pbMode == 0) + { + PlaybackMode = ProgramPlaybackMode.Sequential; + } + else + { + PlaybackMode = ((pbMode & 0x80) == 0) ? ProgramPlaybackMode.Random : ProgramPlaybackMode.Shuffle; + } + + ProgramCount = (uint)(pbMode & 0x7F); + + Palette = br.ReadBytes(64); + _commandTableOffset = br.ReadUInt16(); + _programMapOffset = br.ReadUInt16(); + _cellPlaybackOffset = br.ReadUInt16(); + _cellPositionOffset = br.ReadUInt16(); + + // read position info + br.BaseStream.Seek(startPos + _cellPositionOffset, SeekOrigin.Begin); + for (int cellNum = 0; cellNum < _cellCount; cellNum++) + { + var c = new Cell(); + c.ParsePosition(br); + Cells.Add(c); + } + + br.BaseStream.Seek(startPos + _cellPlaybackOffset, SeekOrigin.Begin); + for (int cellNum = 0; cellNum < _cellCount; cellNum++) + { + Cells[cellNum].ParsePlayback(br); + } + + br.BaseStream.Seek(startPos + _programMapOffset, SeekOrigin.Begin); + var cellNumbers = new List<int>(); + for (int progNum = 0; progNum < _programCount; progNum++) cellNumbers.Add(br.ReadByte() - 1); + + for (int i = 0; i < cellNumbers.Count; i++) + { + int max = (i + 1 == cellNumbers.Count) ? _cellCount : cellNumbers[i + 1]; + Programs.Add(new Program(Cells.Where((c, idx) => idx >= cellNumbers[i] && idx < max).ToList())); + } + } + } +} diff --git a/DvdLib/Ifo/Title.cs b/DvdLib/Ifo/Title.cs new file mode 100644 index 0000000000..29a0b95c72 --- /dev/null +++ b/DvdLib/Ifo/Title.cs @@ -0,0 +1,70 @@ +#pragma warning disable CS1591 + +using System.Collections.Generic; +using System.IO; + +namespace DvdLib.Ifo +{ + public class Title + { + public uint TitleNumber { get; private set; } + + public uint AngleCount { get; private set; } + + public ushort ChapterCount { get; private set; } + + public byte VideoTitleSetNumber { get; private set; } + + private ushort _parentalManagementMask; + private byte _titleNumberInVTS; + private uint _vtsStartSector; // relative to start of entire disk + + public ProgramChain EntryProgramChain { get; private set; } + + public readonly List<ProgramChain> ProgramChains; + + public readonly List<Chapter> Chapters; + + public Title(uint titleNum) + { + ProgramChains = new List<ProgramChain>(); + Chapters = new List<Chapter>(); + Chapters = new List<Chapter>(); + TitleNumber = titleNum; + } + + public bool IsVTSTitle(uint vtsNum, uint vtsTitleNum) + { + return (vtsNum == VideoTitleSetNumber && vtsTitleNum == _titleNumberInVTS); + } + + internal void ParseTT_SRPT(BinaryReader br) + { + byte titleType = br.ReadByte(); + // TODO parse Title Type + + AngleCount = br.ReadByte(); + ChapterCount = br.ReadUInt16(); + _parentalManagementMask = br.ReadUInt16(); + VideoTitleSetNumber = br.ReadByte(); + _titleNumberInVTS = br.ReadByte(); + _vtsStartSector = br.ReadUInt32(); + } + + internal void AddPgc(BinaryReader br, long startByte, bool entryPgc, uint pgcNum) + { + long curPos = br.BaseStream.Position; + br.BaseStream.Seek(startByte, SeekOrigin.Begin); + + var pgc = new ProgramChain(pgcNum); + pgc.ParseHeader(br); + ProgramChains.Add(pgc); + if (entryPgc) + { + EntryProgramChain = pgc; + } + + br.BaseStream.Seek(curPos, SeekOrigin.Begin); + } + } +} diff --git a/DvdLib/Ifo/UserOperation.cs b/DvdLib/Ifo/UserOperation.cs new file mode 100644 index 0000000000..5d111ebc06 --- /dev/null +++ b/DvdLib/Ifo/UserOperation.cs @@ -0,0 +1,37 @@ +#pragma warning disable CS1591 + +using System; + +namespace DvdLib.Ifo +{ + [Flags] + public enum UserOperation + { + None = 0, + TitleOrTimePlay = 1, + ChapterSearchOrPlay = 2, + TitlePlay = 4, + Stop = 8, + GoUp = 16, + TimeOrChapterSearch = 32, + PrevOrTopProgramSearch = 64, + NextProgramSearch = 128, + ForwardScan = 256, + BackwardScan = 512, + TitleMenuCall = 1024, + RootMenuCall = 2048, + SubpictureMenuCall = 4096, + AudioMenuCall = 8192, + AngleMenuCall = 16384, + ChapterMenuCall = 32768, + Resume = 65536, + ButtonSelectOrActive = 131072, + StillOff = 262144, + PauseOn = 524288, + AudioStreamChange = 1048576, + SubpictureStreamChange = 2097152, + AngleChange = 4194304, + KaraokeAudioPresentationModeChange = 8388608, + VideoPresentationModeChange = 16777216, + } +} diff --git a/DvdLib/Properties/AssemblyInfo.cs b/DvdLib/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..6acd571d68 --- /dev/null +++ b/DvdLib/Properties/AssemblyInfo.cs @@ -0,0 +1,21 @@ +using System.Reflection; +using System.Resources; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("DvdLib")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Jellyfin Project")] +[assembly: AssemblyProduct("Jellyfin Server")] +[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: NeutralResourcesLanguage("en")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 37a9e7715d..d43d368e0b 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -80,11 +80,13 @@ using MediaBrowser.Controller.Subtitles; using MediaBrowser.Controller.SyncPlay; using MediaBrowser.Controller.TV; using MediaBrowser.LocalMetadata.Savers; +using MediaBrowser.MediaEncoding.BdInfo; using MediaBrowser.MediaEncoding.Subtitles; using MediaBrowser.Model.Cryptography; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; +using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.Net; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.System; @@ -529,6 +531,8 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton<ILocalizationManager, LocalizationManager>(); + serviceCollection.AddSingleton<IBlurayExaminer, BdInfoExaminer>(); + serviceCollection.AddSingleton<IUserDataRepository, SqliteUserDataRepository>(); serviceCollection.AddSingleton<IUserDataManager, UserDataManager>(); diff --git a/Jellyfin.sln b/Jellyfin.sln index cad23fc5ee..ee772cbe40 100644 --- a/Jellyfin.sln +++ b/Jellyfin.sln @@ -21,6 +21,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jellyfin.Drawing", "src\Jel EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emby.Photos", "Emby.Photos\Emby.Photos.csproj", "{89AB4548-770D-41FD-A891-8DAFF44F452C}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DvdLib", "DvdLib\DvdLib.csproj", "{713F42B5-878E-499D-A878-E4C652B1D5E8}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emby.Server.Implementations", "Emby.Server.Implementations\Emby.Server.Implementations.csproj", "{E383961B-9356-4D5D-8233-9A1079D03055}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RSSDP", "RSSDP\RSSDP.csproj", "{21002819-C39A-4D3E-BE83-2A276A77FB1F}" @@ -135,6 +137,10 @@ Global {89AB4548-770D-41FD-A891-8DAFF44F452C}.Debug|Any CPU.Build.0 = Debug|Any CPU {89AB4548-770D-41FD-A891-8DAFF44F452C}.Release|Any CPU.ActiveCfg = Release|Any CPU {89AB4548-770D-41FD-A891-8DAFF44F452C}.Release|Any CPU.Build.0 = Release|Any CPU + {713F42B5-878E-499D-A878-E4C652B1D5E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {713F42B5-878E-499D-A878-E4C652B1D5E8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {713F42B5-878E-499D-A878-E4C652B1D5E8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {713F42B5-878E-499D-A878-E4C652B1D5E8}.Release|Any CPU.Build.0 = Release|Any CPU {E383961B-9356-4D5D-8233-9A1079D03055}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E383961B-9356-4D5D-8233-9A1079D03055}.Debug|Any CPU.Build.0 = Debug|Any CPU {E383961B-9356-4D5D-8233-9A1079D03055}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs index bc6207ac51..fe8e9063ee 100644 --- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs +++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs @@ -187,5 +187,13 @@ namespace MediaBrowser.Controller.MediaEncoding /// <param name="path">The path.</param> /// <param name="pathType">The type of path.</param> void UpdateEncoderPath(string path, string pathType); + + /// <summary> + /// Gets the primary playlist of .vob files. + /// </summary> + /// <param name="path">The to the .vob files.</param> + /// <param name="titleNumber">The title number to start with.</param> + /// <returns>A playlist.</returns> + IEnumerable<string> GetPrimaryPlaylistVobFiles(string path, uint? titleNumber); } } diff --git a/MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs b/MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs new file mode 100644 index 0000000000..7e026b42e3 --- /dev/null +++ b/MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs @@ -0,0 +1,83 @@ +#pragma warning disable CS1591 + +using System; +using System.Linq; +using BDInfo.IO; +using MediaBrowser.Model.IO; + +namespace MediaBrowser.MediaEncoding.BdInfo +{ + public class BdInfoDirectoryInfo : IDirectoryInfo + { + private readonly IFileSystem _fileSystem; + + private readonly FileSystemMetadata _impl; + + public BdInfoDirectoryInfo(IFileSystem fileSystem, string path) + { + _fileSystem = fileSystem; + _impl = _fileSystem.GetDirectoryInfo(path); + } + + private BdInfoDirectoryInfo(IFileSystem fileSystem, FileSystemMetadata impl) + { + _fileSystem = fileSystem; + _impl = impl; + } + + public string Name => _impl.Name; + + public string FullName => _impl.FullName; + + public IDirectoryInfo? Parent + { + get + { + var parentFolder = System.IO.Path.GetDirectoryName(_impl.FullName); + if (parentFolder is not null) + { + return new BdInfoDirectoryInfo(_fileSystem, parentFolder); + } + + return null; + } + } + + public IDirectoryInfo[] GetDirectories() + { + return Array.ConvertAll( + _fileSystem.GetDirectories(_impl.FullName).ToArray(), + x => new BdInfoDirectoryInfo(_fileSystem, x)); + } + + public IFileInfo[] GetFiles() + { + return Array.ConvertAll( + _fileSystem.GetFiles(_impl.FullName).ToArray(), + x => new BdInfoFileInfo(x)); + } + + public IFileInfo[] GetFiles(string searchPattern) + { + return Array.ConvertAll( + _fileSystem.GetFiles(_impl.FullName, new[] { searchPattern }, false, false).ToArray(), + x => new BdInfoFileInfo(x)); + } + + public IFileInfo[] GetFiles(string searchPattern, System.IO.SearchOption searchOption) + { + return Array.ConvertAll( + _fileSystem.GetFiles( + _impl.FullName, + new[] { searchPattern }, + false, + (searchOption & System.IO.SearchOption.AllDirectories) == System.IO.SearchOption.AllDirectories).ToArray(), + x => new BdInfoFileInfo(x)); + } + + public static IDirectoryInfo FromFileSystemPath(IFileSystem fs, string path) + { + return new BdInfoDirectoryInfo(fs, path); + } + } +} diff --git a/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs b/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs new file mode 100644 index 0000000000..3e53cbf29f --- /dev/null +++ b/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs @@ -0,0 +1,194 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using BDInfo; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.MediaInfo; + +namespace MediaBrowser.MediaEncoding.BdInfo +{ + /// <summary> + /// Class BdInfoExaminer. + /// </summary> + public class BdInfoExaminer : IBlurayExaminer + { + private readonly IFileSystem _fileSystem; + + /// <summary> + /// Initializes a new instance of the <see cref="BdInfoExaminer" /> class. + /// </summary> + /// <param name="fileSystem">The filesystem.</param> + public BdInfoExaminer(IFileSystem fileSystem) + { + _fileSystem = fileSystem; + } + + /// <summary> + /// Gets the disc info. + /// </summary> + /// <param name="path">The path.</param> + /// <returns>BlurayDiscInfo.</returns> + public BlurayDiscInfo GetDiscInfo(string path) + { + if (string.IsNullOrWhiteSpace(path)) + { + throw new ArgumentNullException(nameof(path)); + } + + var bdrom = new BDROM(BdInfoDirectoryInfo.FromFileSystemPath(_fileSystem, path)); + + bdrom.Scan(); + + // Get the longest playlist + var playlist = bdrom.PlaylistFiles.Values.OrderByDescending(p => p.TotalLength).FirstOrDefault(p => p.IsValid); + + var outputStream = new BlurayDiscInfo + { + MediaStreams = Array.Empty<MediaStream>() + }; + + if (playlist is null) + { + return outputStream; + } + + outputStream.Chapters = playlist.Chapters.ToArray(); + + outputStream.RunTimeTicks = TimeSpan.FromSeconds(playlist.TotalLength).Ticks; + + var mediaStreams = new List<MediaStream>(); + + foreach (var stream in playlist.SortedStreams) + { + if (stream is TSVideoStream videoStream) + { + AddVideoStream(mediaStreams, videoStream); + continue; + } + + if (stream is TSAudioStream audioStream) + { + AddAudioStream(mediaStreams, audioStream); + continue; + } + + if (stream is TSTextStream textStream) + { + AddSubtitleStream(mediaStreams, textStream); + continue; + } + + if (stream is TSGraphicsStream graphicsStream) + { + AddSubtitleStream(mediaStreams, graphicsStream); + } + } + + outputStream.MediaStreams = mediaStreams.ToArray(); + + outputStream.PlaylistName = playlist.Name; + + if (playlist.StreamClips is not null && playlist.StreamClips.Any()) + { + // Get the files in the playlist + outputStream.Files = playlist.StreamClips.Select(i => i.StreamFile.Name).ToArray(); + } + + return outputStream; + } + + /// <summary> + /// Adds the video stream. + /// </summary> + /// <param name="streams">The streams.</param> + /// <param name="videoStream">The video stream.</param> + private void AddVideoStream(List<MediaStream> streams, TSVideoStream videoStream) + { + var mediaStream = new MediaStream + { + BitRate = Convert.ToInt32(videoStream.BitRate), + Width = videoStream.Width, + Height = videoStream.Height, + Codec = videoStream.CodecShortName, + IsInterlaced = videoStream.IsInterlaced, + Type = MediaStreamType.Video, + Index = streams.Count + }; + + if (videoStream.FrameRateDenominator > 0) + { + float frameRateEnumerator = videoStream.FrameRateEnumerator; + float frameRateDenominator = videoStream.FrameRateDenominator; + + mediaStream.AverageFrameRate = mediaStream.RealFrameRate = frameRateEnumerator / frameRateDenominator; + } + + streams.Add(mediaStream); + } + + /// <summary> + /// Adds the audio stream. + /// </summary> + /// <param name="streams">The streams.</param> + /// <param name="audioStream">The audio stream.</param> + private void AddAudioStream(List<MediaStream> streams, TSAudioStream audioStream) + { + var stream = new MediaStream + { + Codec = audioStream.CodecShortName, + Language = audioStream.LanguageCode, + Channels = audioStream.ChannelCount, + SampleRate = audioStream.SampleRate, + Type = MediaStreamType.Audio, + Index = streams.Count + }; + + var bitrate = Convert.ToInt32(audioStream.BitRate); + + if (bitrate > 0) + { + stream.BitRate = bitrate; + } + + if (audioStream.LFE > 0) + { + stream.Channels = audioStream.ChannelCount + 1; + } + + streams.Add(stream); + } + + /// <summary> + /// Adds the subtitle stream. + /// </summary> + /// <param name="streams">The streams.</param> + /// <param name="textStream">The text stream.</param> + private void AddSubtitleStream(List<MediaStream> streams, TSTextStream textStream) + { + streams.Add(new MediaStream + { + Language = textStream.LanguageCode, + Codec = textStream.CodecShortName, + Type = MediaStreamType.Subtitle, + Index = streams.Count + }); + } + + /// <summary> + /// Adds the subtitle stream. + /// </summary> + /// <param name="streams">The streams.</param> + /// <param name="textStream">The text stream.</param> + private void AddSubtitleStream(List<MediaStream> streams, TSGraphicsStream textStream) + { + streams.Add(new MediaStream + { + Language = textStream.LanguageCode, + Codec = textStream.CodecShortName, + Type = MediaStreamType.Subtitle, + Index = streams.Count + }); + } + } +} diff --git a/MediaBrowser.MediaEncoding/BdInfo/BdInfoFileInfo.cs b/MediaBrowser.MediaEncoding/BdInfo/BdInfoFileInfo.cs new file mode 100644 index 0000000000..d55688e3df --- /dev/null +++ b/MediaBrowser.MediaEncoding/BdInfo/BdInfoFileInfo.cs @@ -0,0 +1,41 @@ +#pragma warning disable CS1591 + +using System.IO; +using MediaBrowser.Model.IO; + +namespace MediaBrowser.MediaEncoding.BdInfo +{ + public class BdInfoFileInfo : BDInfo.IO.IFileInfo + { + private FileSystemMetadata _impl; + + public BdInfoFileInfo(FileSystemMetadata impl) + { + _impl = impl; + } + + public string Name => _impl.Name; + + public string FullName => _impl.FullName; + + public string Extension => _impl.Extension; + + public long Length => _impl.Length; + + public bool IsDir => _impl.IsDirectory; + + public Stream OpenRead() + { + return new FileStream( + FullName, + FileMode.Open, + FileAccess.Read, + FileShare.Read); + } + + public StreamReader OpenText() + { + return new StreamReader(OpenRead()); + } + } +} diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index cef02d5f8f..4a795625d0 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -865,6 +865,85 @@ namespace MediaBrowser.MediaEncoding.Encoder throw new NotImplementedException(); } + /// <inheritdoc /> + public IEnumerable<string> GetPrimaryPlaylistVobFiles(string path, uint? titleNumber) + { + // min size 300 mb + const long MinPlayableSize = 314572800; + + // Try to eliminate menus and intros by skipping all files at the front of the list that are less than the minimum size + // Once we reach a file that is at least the minimum, return all subsequent ones + var allVobs = _fileSystem.GetFiles(path, true) + .Where(file => string.Equals(file.Extension, ".vob", StringComparison.OrdinalIgnoreCase)) + .OrderBy(i => i.FullName) + .ToList(); + + // If we didn't find any satisfying the min length, just take them all + if (allVobs.Count == 0) + { + _logger.LogWarning("No vobs found in dvd structure."); + return Enumerable.Empty<string>(); + } + + if (titleNumber.HasValue) + { + var prefix = string.Format( + CultureInfo.InvariantCulture, + titleNumber.Value >= 10 ? "VTS_{0}_" : "VTS_0{0}_", + titleNumber.Value); + var vobs = allVobs.Where(i => i.Name.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)).ToList(); + + if (vobs.Count > 0) + { + var minSizeVobs = vobs + .SkipWhile(f => f.Length < MinPlayableSize) + .ToList(); + + return minSizeVobs.Count == 0 ? vobs.Select(i => i.FullName) : minSizeVobs.Select(i => i.FullName); + } + + _logger.LogWarning("Could not determine vob file list for {Path} using DvdLib. Will scan using file sizes.", path); + } + + var files = allVobs + .SkipWhile(f => f.Length < MinPlayableSize) + .ToList(); + + // If we didn't find any satisfying the min length, just take them all + if (files.Count == 0) + { + _logger.LogWarning("Vob size filter resulted in zero matches. Taking all vobs."); + files = allVobs; + } + + // Assuming they're named "vts_05_01", take all files whose second part matches that of the first file + if (files.Count > 0) + { + var parts = _fileSystem.GetFileNameWithoutExtension(files[0]).Split('_'); + + if (parts.Length == 3) + { + var title = parts[1]; + + files = files.TakeWhile(f => + { + var fileParts = _fileSystem.GetFileNameWithoutExtension(f).Split('_'); + + return fileParts.Length == 3 && string.Equals(title, fileParts[1], StringComparison.OrdinalIgnoreCase); + }).ToList(); + + // If this resulted in not getting any vobs, just take them all + if (files.Count == 0) + { + _logger.LogWarning("Vob filename filter resulted in zero matches. Taking all vobs."); + files = allVobs; + } + } + } + + return files.Select(i => i.FullName); + } + public bool CanExtractSubtitles(string codec) { // TODO is there ever a case when a subtitle can't be extracted?? diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj index f4438fe194..a0624fe76b 100644 --- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj +++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj @@ -22,6 +22,7 @@ </ItemGroup> <ItemGroup> + <PackageReference Include="BDInfo" /> <PackageReference Include="libse" /> <PackageReference Include="Microsoft.Extensions.Http" /> <PackageReference Include="System.Text.Encoding.CodePages" /> diff --git a/MediaBrowser.Model/MediaInfo/BlurayDiscInfo.cs b/MediaBrowser.Model/MediaInfo/BlurayDiscInfo.cs new file mode 100644 index 0000000000..83f982a5c8 --- /dev/null +++ b/MediaBrowser.Model/MediaInfo/BlurayDiscInfo.cs @@ -0,0 +1,39 @@ +#nullable disable +#pragma warning disable CS1591 + +using MediaBrowser.Model.Entities; + +namespace MediaBrowser.Model.MediaInfo +{ + /// <summary> + /// Represents the result of BDInfo output. + /// </summary> + public class BlurayDiscInfo + { + /// <summary> + /// Gets or sets the media streams. + /// </summary> + /// <value>The media streams.</value> + public MediaStream[] MediaStreams { get; set; } + + /// <summary> + /// Gets or sets the run time ticks. + /// </summary> + /// <value>The run time ticks.</value> + public long? RunTimeTicks { get; set; } + + /// <summary> + /// Gets or sets the files. + /// </summary> + /// <value>The files.</value> + public string[] Files { get; set; } + + public string PlaylistName { get; set; } + + /// <summary> + /// Gets or sets the chapters. + /// </summary> + /// <value>The chapters.</value> + public double[] Chapters { get; set; } + } +} diff --git a/MediaBrowser.Model/MediaInfo/IBlurayExaminer.cs b/MediaBrowser.Model/MediaInfo/IBlurayExaminer.cs new file mode 100644 index 0000000000..5b7d1d03c3 --- /dev/null +++ b/MediaBrowser.Model/MediaInfo/IBlurayExaminer.cs @@ -0,0 +1,15 @@ +namespace MediaBrowser.Model.MediaInfo +{ + /// <summary> + /// Interface IBlurayExaminer. + /// </summary> + public interface IBlurayExaminer + { + /// <summary> + /// Gets the disc info. + /// </summary> + /// <param name="path">The path.</param> + /// <returns>BlurayDiscInfo.</returns> + BlurayDiscInfo GetDiscInfo(string path); + } +} diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 6a40833d7f..a0f97df40d 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -8,6 +8,7 @@ <ItemGroup> <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" /> <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" /> + <ProjectReference Include="..\DvdLib\DvdLib.csproj" /> </ItemGroup> <ItemGroup> diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index 0f35c6a5ea..81434b8620 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -9,6 +9,7 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using DvdLib.Ifo; using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Chapters; using MediaBrowser.Controller.Configuration; @@ -36,6 +37,7 @@ namespace MediaBrowser.Providers.MediaInfo private readonly ILogger<FFProbeVideoInfo> _logger; private readonly IMediaEncoder _mediaEncoder; private readonly IItemRepository _itemRepo; + private readonly IBlurayExaminer _blurayExaminer; private readonly ILocalizationManager _localization; private readonly IEncodingManager _encodingManager; private readonly IServerConfigurationManager _config; @@ -51,6 +53,7 @@ namespace MediaBrowser.Providers.MediaInfo IMediaSourceManager mediaSourceManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, + IBlurayExaminer blurayExaminer, ILocalizationManager localization, IEncodingManager encodingManager, IServerConfigurationManager config, @@ -64,6 +67,7 @@ namespace MediaBrowser.Providers.MediaInfo _mediaSourceManager = mediaSourceManager; _mediaEncoder = mediaEncoder; _itemRepo = itemRepo; + _blurayExaminer = blurayExaminer; _localization = localization; _encodingManager = encodingManager; _config = config; @@ -80,16 +84,47 @@ namespace MediaBrowser.Providers.MediaInfo CancellationToken cancellationToken) where T : Video { + BlurayDiscInfo blurayDiscInfo = null; + Model.MediaInfo.MediaInfo mediaInfoResult = null; if (!item.IsShortcut || options.EnableRemoteContentProbe) { + string[] streamFileNames = null; + + if (item.VideoType == VideoType.Dvd) + { + streamFileNames = FetchFromDvdLib(item); + + if (streamFileNames.Length == 0) + { + _logger.LogError("No playable vobs found in dvd structure, skipping ffprobe."); + return ItemUpdateType.MetadataImport; + } + } + else if (item.VideoType == VideoType.BluRay) + { + var inputPath = item.Path; + + blurayDiscInfo = GetBDInfo(inputPath); + + streamFileNames = blurayDiscInfo.Files; + + if (streamFileNames.Length == 0) + { + _logger.LogError("No playable vobs found in bluray structure, skipping ffprobe."); + return ItemUpdateType.MetadataImport; + } + } + + streamFileNames ??= Array.Empty<string>(); + mediaInfoResult = await GetMediaInfo(item, cancellationToken).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); } - await Fetch(item, cancellationToken, mediaInfoResult, options).ConfigureAwait(false); + await Fetch(item, cancellationToken, mediaInfoResult, blurayDiscInfo, options).ConfigureAwait(false); return ItemUpdateType.MetadataImport; } @@ -129,6 +164,7 @@ namespace MediaBrowser.Providers.MediaInfo Video video, CancellationToken cancellationToken, Model.MediaInfo.MediaInfo mediaInfo, + BlurayDiscInfo blurayInfo, MetadataRefreshOptions options) { List<MediaStream> mediaStreams; @@ -182,6 +218,10 @@ namespace MediaBrowser.Providers.MediaInfo video.Container = mediaInfo.Container; chapters = mediaInfo.Chapters ?? Array.Empty<ChapterInfo>(); + if (blurayInfo is not null) + { + FetchBdInfo(video, ref chapters, mediaStreams, blurayInfo); + } } else { @@ -277,6 +317,91 @@ namespace MediaBrowser.Providers.MediaInfo } } + private void FetchBdInfo(BaseItem item, ref ChapterInfo[] chapters, List<MediaStream> mediaStreams, BlurayDiscInfo blurayInfo) + { + var video = (Video)item; + + // video.PlayableStreamFileNames = blurayInfo.Files.ToList(); + + // Use BD Info if it has multiple m2ts. Otherwise, treat it like a video file and rely more on ffprobe output + if (blurayInfo.Files.Length > 1) + { + int? currentHeight = null; + int? currentWidth = null; + int? currentBitRate = null; + + var videoStream = mediaStreams.FirstOrDefault(s => s.Type == MediaStreamType.Video); + + // Grab the values that ffprobe recorded + if (videoStream is not null) + { + currentBitRate = videoStream.BitRate; + currentWidth = videoStream.Width; + currentHeight = videoStream.Height; + } + + // Fill video properties from the BDInfo result + mediaStreams.Clear(); + mediaStreams.AddRange(blurayInfo.MediaStreams); + + if (blurayInfo.RunTimeTicks.HasValue && blurayInfo.RunTimeTicks.Value > 0) + { + video.RunTimeTicks = blurayInfo.RunTimeTicks; + } + + if (blurayInfo.Chapters is not null) + { + double[] brChapter = blurayInfo.Chapters; + chapters = new ChapterInfo[brChapter.Length]; + for (int i = 0; i < brChapter.Length; i++) + { + chapters[i] = new ChapterInfo + { + StartPositionTicks = TimeSpan.FromSeconds(brChapter[i]).Ticks + }; + } + } + + videoStream = mediaStreams.FirstOrDefault(s => s.Type == MediaStreamType.Video); + + // Use the ffprobe values if these are empty + if (videoStream is not null) + { + videoStream.BitRate = IsEmpty(videoStream.BitRate) ? currentBitRate : videoStream.BitRate; + videoStream.Width = IsEmpty(videoStream.Width) ? currentWidth : videoStream.Width; + videoStream.Height = IsEmpty(videoStream.Height) ? currentHeight : videoStream.Height; + } + } + } + + private bool IsEmpty(int? num) + { + return !num.HasValue || num.Value == 0; + } + + /// <summary> + /// Gets information about the longest playlist on a bdrom. + /// </summary> + /// <param name="path">The path.</param> + /// <returns>VideoStream.</returns> + private BlurayDiscInfo GetBDInfo(string path) + { + if (string.IsNullOrWhiteSpace(path)) + { + throw new ArgumentNullException(nameof(path)); + } + + try + { + return _blurayExaminer.GetDiscInfo(path); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error getting BDInfo"); + return null; + } + } + private void FetchEmbeddedInfo(Video video, Model.MediaInfo.MediaInfo data, MetadataRefreshOptions refreshOptions, LibraryOptions libraryOptions) { var replaceData = refreshOptions.ReplaceAllMetadata; @@ -558,5 +683,33 @@ namespace MediaBrowser.Providers.MediaInfo return chapters; } + + private string[] FetchFromDvdLib(Video item) + { + var path = item.Path; + var dvd = new Dvd(path); + + var primaryTitle = dvd.Titles.OrderByDescending(GetRuntime).FirstOrDefault(); + + byte? titleNumber = null; + + if (primaryTitle is not null) + { + titleNumber = primaryTitle.VideoTitleSetNumber; + item.RunTimeTicks = GetRuntime(primaryTitle); + } + + return _mediaEncoder.GetPrimaryPlaylistVobFiles(item.Path, titleNumber) + .Select(Path.GetFileName) + .ToArray(); + } + + private long GetRuntime(Title title) + { + return title.ProgramChains + .Select(i => (TimeSpan)i.PlaybackTime) + .Select(i => i.Ticks) + .Sum(); + } } } diff --git a/MediaBrowser.Providers/MediaInfo/ProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/ProbeProvider.cs index 31fa3da1ce..2800219552 100644 --- a/MediaBrowser.Providers/MediaInfo/ProbeProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/ProbeProvider.cs @@ -53,6 +53,7 @@ namespace MediaBrowser.Providers.MediaInfo /// <param name="mediaSourceManager">Instance of the <see cref="IMediaSourceManager"/> interface.</param> /// <param name="mediaEncoder">Instance of the <see cref="IMediaEncoder"/> interface.</param> /// <param name="itemRepo">Instance of the <see cref="IItemRepository"/> interface.</param> + /// <param name="blurayExaminer">Instance of the <see cref="IBlurayExaminer"/> interface.</param> /// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param> /// <param name="encodingManager">Instance of the <see cref="IEncodingManager"/> interface.</param> /// <param name="config">Instance of the <see cref="IServerConfigurationManager"/> interface.</param> @@ -66,6 +67,7 @@ namespace MediaBrowser.Providers.MediaInfo IMediaSourceManager mediaSourceManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, + IBlurayExaminer blurayExaminer, ILocalizationManager localization, IEncodingManager encodingManager, IServerConfigurationManager config, @@ -85,6 +87,7 @@ namespace MediaBrowser.Providers.MediaInfo mediaSourceManager, mediaEncoder, itemRepo, + blurayExaminer, localization, encodingManager, config, -- cgit v1.2.3 From 3d4b2f840a3652490e0dbe559e5aa853a45130e2 Mon Sep 17 00:00:00 2001 From: Shadowghost <Ghost_of_Stone@web.de> Date: Sat, 4 Feb 2023 12:34:24 +0100 Subject: Fix BD and DVD folder recognition for tv episodes --- .../Library/Resolvers/BaseVideoResolver.cs | 16 +++++++++++-- .../MediaInfo/FFProbeVideoInfo.cs | 26 +++++++++++++--------- 2 files changed, 30 insertions(+), 12 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs index e8615e7db7..27062228fa 100644 --- a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs @@ -67,11 +67,23 @@ namespace Emby.Server.Implementations.Library.Resolvers { if (IsDvdDirectory(child.FullName, filename, args.DirectoryService)) { - videoType = VideoType.Dvd; + var videoTmp = new TVideoType + { + Path = args.Path, + VideoType = VideoType.Dvd + }; + Set3DFormat(videoTmp); + return videoTmp; } else if (IsBluRayDirectory(filename)) { - videoType = VideoType.BluRay; + var videoTmp = new TVideoType + { + Path = args.Path, + VideoType = VideoType.BluRay + }; + Set3DFormat(videoTmp); + return videoTmp; } } else if (IsDvdFile(filename)) diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index 393e1f22a7..f2f6d8a124 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -117,19 +117,25 @@ namespace MediaBrowser.Providers.MediaInfo mediaInfoResult.RunTimeTicks += tmpMediaInfo.RunTimeTicks; } } - else + else if (item.VideoType == VideoType.BluRay) { - if (item.VideoType == VideoType.BluRay) - { - blurayDiscInfo = GetBDInfo(item.Path); - - if (blurayDiscInfo.Files.Length == 0) + blurayDiscInfo = GetBDInfo(item.Path); + var m2ts = _mediaEncoder.GetPrimaryPlaylistM2TsFiles(item.Path, null).ToList(); + mediaInfoResult = await GetMediaInfo( + new Video { - _logger.LogError("No playable vobs found in bluray structure, skipping ffprobe."); - return ItemUpdateType.MetadataImport; - } - } + Path = m2ts.First() + }, + cancellationToken).ConfigureAwait(false); + if (blurayDiscInfo.Files.Length == 0) + { + _logger.LogError("No playable vobs found in bluray structure, skipping ffprobe."); + return ItemUpdateType.MetadataImport; + } + } + else + { mediaInfoResult = await GetMediaInfo(item, cancellationToken).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); } -- cgit v1.2.3 From 666c6d321f7842f1d5ea445ff367fc540303baf1 Mon Sep 17 00:00:00 2001 From: Elias Frehner <swiss.elk@hotmail.com> Date: Sat, 4 Feb 2023 13:42:28 +0000 Subject: Translated using Weblate (Alemannic) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/gsw/ --- Emby.Server.Implementations/Localization/Core/gsw.json | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/gsw.json b/Emby.Server.Implementations/Localization/Core/gsw.json index bd8cec710b..ac9da1dd12 100644 --- a/Emby.Server.Implementations/Localization/Core/gsw.json +++ b/Emby.Server.Implementations/Localization/Core/gsw.json @@ -1,7 +1,7 @@ { "Albums": "Alben", "AppDeviceValues": "App: {0}, Gerät: {1}", - "Application": "Anwendung", + "Application": "Applikation", "Artists": "Künstler", "AuthenticationSucceededWithUserName": "{0} hat sich angemeldet", "Books": "Bücher", @@ -14,7 +14,7 @@ "FailedLoginAttemptWithUserName": "Fehlgeschlagener Anmeldeversuch von {0}", "Favorites": "Favoriten", "Folders": "Ordner", - "Genres": "Genres", + "Genres": "Genre", "HeaderAlbumArtists": "Album-Künstler", "HeaderContinueWatching": "weiter schauen", "HeaderFavoriteAlbums": "Lieblingsalben", @@ -49,7 +49,7 @@ "NotificationOptionAudioPlayback": "Audiowedergab gstartet", "NotificationOptionAudioPlaybackStopped": "Audiwedergab gstoppt", "NotificationOptionCameraImageUploaded": "Foti ueglade", - "NotificationOptionInstallationFailed": "Installationsfehler", + "NotificationOptionInstallationFailed": "Installationsfähler", "NotificationOptionNewLibraryContent": "Nöie Inhaut hinzuegfüegt", "NotificationOptionPluginError": "Plugin-Fäuer", "NotificationOptionPluginInstalled": "Plugin installiert", @@ -120,5 +120,9 @@ "Forced": "Erzwungen", "Default": "Standard", "TaskOptimizeDatabase": "Datenbank optimieren", - "External": "Extern" + "External": "Extern", + "TaskOptimizeDatabaseDescription": "Kompromiert d Datenbank und trennt freie Speicherplatz. Durch die Ufagb cha d Leistig nach em ne Scan vor Bibliothek oder andere Ufgabe verbesseret werde.", + "HearingImpaired": "Hörgschädigti", + "TaskKeyframeExtractor": "Keyframe-Extraktor", + "TaskKeyframeExtractorDescription": "Extrahiert Keyframes us Videodateien zum erstelle vo genauere HLS Playliste. Die Ufgab cha für e langi Zyt laufe." } -- cgit v1.2.3 From c139b0a7f086ac4b2e102d3f911ab9f4410f09b2 Mon Sep 17 00:00:00 2001 From: Troja <d.iwowi.b@gmail.com> Date: Sat, 4 Feb 2023 21:09:52 +0000 Subject: Translated using Weblate (Belarusian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/be/ --- .../Localization/Core/be.json | 52 +++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/be.json b/Emby.Server.Implementations/Localization/Core/be.json index 56c4e7d39d..0b053600b4 100644 --- a/Emby.Server.Implementations/Localization/Core/be.json +++ b/Emby.Server.Implementations/Localization/Core/be.json @@ -1,4 +1,54 @@ { "Sync": "Сінхранізацыя", - "Playlists": "Плэйліст" + "Playlists": "Плэйлісты", + "Latest": "Апошні", + "LabelIpAddressValue": "IP-адрас: {0}", + "ItemAddedWithName": "{0} быў дададзены ў бібліятэку", + "MessageApplicationUpdated": "Сервер Jellyfin быў абноўлены", + "NotificationOptionApplicationUpdateInstalled": "Усталявана абнаўленне прыкладання", + "PluginInstalledWithName": "{0} быў усталяваны", + "UserCreatedWithName": "Карыстальнік {0} быў створаны", + "Albums": "Альбомы", + "Application": "Прыкладанне", + "AuthenticationSucceededWithUserName": "{0} паспяхова аўтэнтыфікаваны", + "Channels": "Каналы", + "ChapterNameValue": "Раздзел {0}", + "Collections": "Калекцыя", + "Default": "Па змаўчанні", + "FailedLoginAttemptWithUserName": "Няўдалая спроба ўваходу з {0}", + "Folders": "Папкі", + "Favorites": "Любімыя", + "External": "Знешні", + "Genres": "Жанры", + "HeaderContinueWatching": "Працягнуць прагляд", + "HeaderFavoriteAlbums": "Любімыя альбомы", + "HeaderFavoriteEpisodes": "Любімыя Серыі", + "HeaderFavoriteShows": "Любімыя Шоу", + "HeaderFavoriteSongs": "Любімыя Песні", + "HeaderLiveTV": "Прамы эфір", + "HeaderAlbumArtists": "Выканаўцы альбома", + "LabelRunningTimeValue": "Працягласць: {0}", + "HomeVideos": "Хатняе відэа", + "ItemRemovedWithName": "{0} быў выдалены з бібліятэкі", + "MessageApplicationUpdatedTo": "Сервер Jellyfin абноўлены да {0}", + "Movies": "Фільмы", + "Music": "Музыка", + "MusicVideos": "Музычныя кліпы", + "NameInstallFailed": "{0} памылка ўстаноўкі", + "NameSeasonNumber": "Сезон {0}", + "NotificationOptionApplicationUpdateAvailable": "Даступна абнаўленне прыкладання", + "NotificationOptionPluginInstalled": "Плагін усталяваны", + "NotificationOptionPluginUpdateInstalled": "Усталявана абнаўленне плагіна", + "NotificationOptionServerRestartRequired": "Патрабуецца перазапуск сервера", + "Photos": "Фатаграфіі", + "Plugin": "Плагін", + "PluginUninstalledWithName": "{0} быў выдалены", + "PluginUpdatedWithName": "{0} быў абноўлены", + "ProviderValue": "Пастаўшчык: {0}", + "Songs": "Песні", + "System": "Сістэма", + "User": "Карыстальнік", + "UserDeletedWithName": "Карыстальнік {0} быў выдалены", + "UserDownloadingItemWithValues": "{0} спампоўваецца {1}", + "TaskOptimizeDatabase": "Аптымізацыя базы дадзеных" } -- cgit v1.2.3 From 3042c16a79ce417e589dbd65e905668c5c7f82df Mon Sep 17 00:00:00 2001 From: rushmash <rushmash@gmail.com> Date: Sun, 5 Feb 2023 08:06:49 +0000 Subject: Translated using Weblate (Belarusian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/be/ --- Emby.Server.Implementations/Localization/Core/be.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/be.json b/Emby.Server.Implementations/Localization/Core/be.json index 0b053600b4..b4e1a14e1f 100644 --- a/Emby.Server.Implementations/Localization/Core/be.json +++ b/Emby.Server.Implementations/Localization/Core/be.json @@ -50,5 +50,6 @@ "User": "Карыстальнік", "UserDeletedWithName": "Карыстальнік {0} быў выдалены", "UserDownloadingItemWithValues": "{0} спампоўваецца {1}", - "TaskOptimizeDatabase": "Аптымізацыя базы дадзеных" + "TaskOptimizeDatabase": "Аптымізацыя базы дадзеных", + "Artists": "Артысты" } -- cgit v1.2.3 From abf6acf9d7c3d89cf16c94cc147e4fb6baf84fb9 Mon Sep 17 00:00:00 2001 From: rushmash <rushmash@gmail.com> Date: Sun, 5 Feb 2023 08:36:30 +0000 Subject: Translated using Weblate (Belarusian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/be/ --- .../Localization/Core/be.json | 100 ++++++++++++++++++--- 1 file changed, 86 insertions(+), 14 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/be.json b/Emby.Server.Implementations/Localization/Core/be.json index b4e1a14e1f..6de7c711c1 100644 --- a/Emby.Server.Implementations/Localization/Core/be.json +++ b/Emby.Server.Implementations/Localization/Core/be.json @@ -1,11 +1,11 @@ { - "Sync": "Сінхранізацыя", + "Sync": "Сінхранізаваць", "Playlists": "Плэйлісты", "Latest": "Апошні", "LabelIpAddressValue": "IP-адрас: {0}", "ItemAddedWithName": "{0} быў дададзены ў бібліятэку", - "MessageApplicationUpdated": "Сервер Jellyfin быў абноўлены", - "NotificationOptionApplicationUpdateInstalled": "Усталявана абнаўленне прыкладання", + "MessageApplicationUpdated": "Сервер Jellyfin абноўлены", + "NotificationOptionApplicationUpdateInstalled": "Абнаўленне прыкладання ўсталявана", "PluginInstalledWithName": "{0} быў усталяваны", "UserCreatedWithName": "Карыстальнік {0} быў створаны", "Albums": "Альбомы", @@ -13,32 +13,32 @@ "AuthenticationSucceededWithUserName": "{0} паспяхова аўтэнтыфікаваны", "Channels": "Каналы", "ChapterNameValue": "Раздзел {0}", - "Collections": "Калекцыя", + "Collections": "Калекцыі", "Default": "Па змаўчанні", "FailedLoginAttemptWithUserName": "Няўдалая спроба ўваходу з {0}", "Folders": "Папкі", - "Favorites": "Любімыя", + "Favorites": "Абранае", "External": "Знешні", "Genres": "Жанры", "HeaderContinueWatching": "Працягнуць прагляд", - "HeaderFavoriteAlbums": "Любімыя альбомы", - "HeaderFavoriteEpisodes": "Любімыя Серыі", - "HeaderFavoriteShows": "Любімыя Шоу", - "HeaderFavoriteSongs": "Любімыя Песні", + "HeaderFavoriteAlbums": "Абраныя альбомы", + "HeaderFavoriteEpisodes": "Абраныя серыі", + "HeaderFavoriteShows": "Абраныя шоу", + "HeaderFavoriteSongs": "Абраныя песні", "HeaderLiveTV": "Прамы эфір", "HeaderAlbumArtists": "Выканаўцы альбома", "LabelRunningTimeValue": "Працягласць: {0}", - "HomeVideos": "Хатняе відэа", + "HomeVideos": "Хатнія відэа", "ItemRemovedWithName": "{0} быў выдалены з бібліятэкі", "MessageApplicationUpdatedTo": "Сервер Jellyfin абноўлены да {0}", "Movies": "Фільмы", "Music": "Музыка", "MusicVideos": "Музычныя кліпы", - "NameInstallFailed": "{0} памылка ўстаноўкі", + "NameInstallFailed": "Устаноўка {0} не атрымалася", "NameSeasonNumber": "Сезон {0}", "NotificationOptionApplicationUpdateAvailable": "Даступна абнаўленне прыкладання", "NotificationOptionPluginInstalled": "Плагін усталяваны", - "NotificationOptionPluginUpdateInstalled": "Усталявана абнаўленне плагіна", + "NotificationOptionPluginUpdateInstalled": "Абнаўленне плагіна усталявана", "NotificationOptionServerRestartRequired": "Патрабуецца перазапуск сервера", "Photos": "Фатаграфіі", "Plugin": "Плагін", @@ -50,6 +50,78 @@ "User": "Карыстальнік", "UserDeletedWithName": "Карыстальнік {0} быў выдалены", "UserDownloadingItemWithValues": "{0} спампоўваецца {1}", - "TaskOptimizeDatabase": "Аптымізацыя базы дадзеных", - "Artists": "Артысты" + "TaskOptimizeDatabase": "Аптымізаваць базу дадзеных", + "Artists": "Выканаўцы", + "UserOfflineFromDevice": "{0} адключыўся ад {1}", + "UserPolicyUpdatedWithName": "Палітыка карыстальніка абноўлена для {0}", + "TaskCleanActivityLogDescription": "Выдаляе старэйшыя за зададзены ўзрост запісы ў журнале актыўнасці.", + "TaskRefreshChapterImagesDescription": "Стварае мініяцюры для відэа, якія маюць раздзелы.", + "TaskCleanLogsDescription": "Выдаляе файлы журналу, якім больш за {0} дзён.", + "TaskUpdatePluginsDescription": "Спампоўвае і ўсталёўвае абнаўленні для плагінаў, якія настроены на аўтаматычнае абнаўленне.", + "TaskRefreshChannelsDescription": "Абнаўляе інфармацыю аб інтэрнэт-канале.", + "TaskDownloadMissingSubtitlesDescription": "Шукае ў інтэрнэце адсутныя субтытры на аснове канфігурацыі метададзеных.", + "TaskOptimizeDatabaseDescription": "Ушчыльняе базу дадзеных і скарачае вольную прастору. Выкананне гэтай задачы пасля сканавання бібліятэкі або ўнясення іншых змяненняў, якія прадугледжваюць мадыфікацыю базы дадзеных, можа палепшыць прадукцыйнасць.", + "TaskKeyframeExtractor": "Экстрактар ключавых кадраў", + "TasksApplicationCategory": "Прыкладанне", + "AppDeviceValues": "Прыкладанне: {0}, Прылада: {1}", + "Books": "Кнігі", + "CameraImageUploadedFrom": "Новая выява камеры была загружана з {0}", + "DeviceOfflineWithName": "{0} адключыўся", + "DeviceOnlineWithName": "{0} падлучаны", + "Forced": "Прымусова", + "HeaderRecordingGroups": "Групы запісаў", + "HeaderNextUp": "Наступнае", + "HeaderFavoriteArtists": "Абраныя выканаўцы", + "HearingImpaired": "Са слабым слыхам", + "Inherit": "Атрымаць у спадчыну", + "MessageNamedServerConfigurationUpdatedWithValue": "Канфігурацыя сервера {0} абноўлена", + "MessageServerConfigurationUpdated": "Канфігурацыя сервера абноўлена", + "MixedContent": "Змешаны змест", + "NameSeasonUnknown": "Невядомы сезон", + "NotificationOptionInstallationFailed": "Збой усталёўкі", + "NewVersionIsAvailable": "Новая версія Jellyfin Server даступна для спампоўкі.", + "NotificationOptionCameraImageUploaded": "Выява камеры запампавана", + "NotificationOptionAudioPlaybackStopped": "Прайграванне аўдыё спынена", + "NotificationOptionAudioPlayback": "Прайграванне аўдыё пачалося", + "NotificationOptionNewLibraryContent": "Дададзены новы кантэнт", + "NotificationOptionPluginError": "Збой плагіна", + "NotificationOptionPluginUninstalled": "Плагін выдалены", + "NotificationOptionTaskFailed": "Збой запланаванага задання", + "NotificationOptionUserLockedOut": "Карыстальнік заблакіраваны", + "NotificationOptionVideoPlayback": "Пачалося прайграванне відэа", + "NotificationOptionVideoPlaybackStopped": "Прайграванне відэа спынена", + "ScheduledTaskFailedWithName": "{0} не атрымалася", + "ScheduledTaskStartedWithName": "{0} пачалося", + "ServerNameNeedsToBeRestarted": "{0} трэба перазапусціць", + "Shows": "Шоу", + "StartupEmbyServerIsLoading": "Jellyfin Server загружаецца. Калі ласка, паўтарыце спробу крыху пазней.", + "SubtitleDownloadFailureFromForItem": "Не атрымалася спампаваць субтытры з {0} для {1}", + "TvShows": "ТБ-шоу", + "Undefined": "Нявызначана", + "UserLockedOutWithName": "Карыстальнік {0} быў заблакіраваны", + "UserOnlineFromDevice": "{0} падключаны з {1}", + "UserPasswordChangedWithName": "Пароль быў зменены для карыстальніка {0}", + "UserStartedPlayingItemWithValues": "{0} грае {1} на {2}", + "UserStoppedPlayingItemWithValues": "{0} скончыў прайграванне {1} на {2}", + "ValueHasBeenAddedToLibrary": "{0} быў дададзены ў вашу медыятэку", + "ValueSpecialEpisodeName": "Спецэпізод - {0}", + "VersionNumber": "Версія {0}", + "TasksMaintenanceCategory": "Абслугоўванне", + "TasksLibraryCategory": "Медыятэка", + "TasksChannelsCategory": "Інтэрнэт-каналы", + "TaskCleanActivityLog": "Ачысціць журнал актыўнасці", + "TaskCleanCache": "Ачысціць кэш", + "TaskCleanCacheDescription": "Выдаляе файлы кэша, якія больш не патрэбныя сістэме.", + "TaskRefreshChapterImages": "Выняць выявы раздзелаў", + "TaskRefreshLibrary": "Сканіраваць медыятэку", + "TaskRefreshLibraryDescription": "Сканіруе вашу медыятэку на наяўнасць новых файлаў і абнаўляе метададзеныя.", + "TaskCleanLogs": "Ачысціць часопіс", + "TaskRefreshPeople": "Абнавіць людзей", + "TaskRefreshPeopleDescription": "Абнаўленне метаданых для акцёраў і рэжысёраў у вашай медыятэцы.", + "TaskUpdatePlugins": "Абнавіць плагіны", + "TaskCleanTranscode": "Ачысціць каталог перакадзіравання", + "TaskCleanTranscodeDescription": "Выдаляе перакадзіраваныя файлы, старэйшыя за адзін дзень.", + "TaskRefreshChannels": "Абнавіць каналы", + "TaskDownloadMissingSubtitles": "Спампаваць адсутныя субтытры", + "TaskKeyframeExtractorDescription": "Выдае ключавыя кадры з відэафайлаў для стварэння больш дакладных спісаў прайгравання HLS. Гэта задача можа працаваць у працягу доўгага часу." } -- cgit v1.2.3 From 16a0537a01fded6dd24de9d11deda6d207496734 Mon Sep 17 00:00:00 2001 From: Troja <d.iwowi.b@gmail.com> Date: Sun, 5 Feb 2023 14:38:08 +0000 Subject: Translated using Weblate (Belarusian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/be/ --- Emby.Server.Implementations/Localization/Core/be.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/be.json b/Emby.Server.Implementations/Localization/Core/be.json index 6de7c711c1..3af124678f 100644 --- a/Emby.Server.Implementations/Localization/Core/be.json +++ b/Emby.Server.Implementations/Localization/Core/be.json @@ -79,7 +79,7 @@ "MixedContent": "Змешаны змест", "NameSeasonUnknown": "Невядомы сезон", "NotificationOptionInstallationFailed": "Збой усталёўкі", - "NewVersionIsAvailable": "Новая версія Jellyfin Server даступна для спампоўкі.", + "NewVersionIsAvailable": "Новая версія сервера Jellyfin даступная для cпампоўкі.", "NotificationOptionCameraImageUploaded": "Выява камеры запампавана", "NotificationOptionAudioPlaybackStopped": "Прайграванне аўдыё спынена", "NotificationOptionAudioPlayback": "Прайграванне аўдыё пачалося", -- cgit v1.2.3 From 32eccc139c4b35aef04a29cefb11c6130726f617 Mon Sep 17 00:00:00 2001 From: Cody Robibero <cody@robibe.ro> Date: Sat, 11 Feb 2023 07:46:52 -0700 Subject: LiveTV fixes --- .../Data/SqliteItemRepository.cs | 3 +++ .../LiveTv/Listings/XmlTvListingsProvider.cs | 23 +++++++++++----------- .../LiveTv/Listings/XmlTvListingsProviderTests.cs | 19 ++++++++++++++++++ .../LiveTv/Listings/XmlTv/emptycategory.xml | 6 ++++++ 4 files changed, 40 insertions(+), 11 deletions(-) create mode 100644 tests/Jellyfin.Server.Implementations.Tests/Test Data/LiveTv/Listings/XmlTv/emptycategory.xml (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index bc703fe90d..9aa7eea848 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -5440,6 +5440,9 @@ AND Type = @InternalPersonType)"); list.AddRange(inheritedTags.Select(i => (6, i))); + // Remove all invalid values. + list.RemoveAll(i => string.IsNullOrEmpty(i.Item2)); + return list; } diff --git a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs index e874990da1..066afb956b 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs @@ -137,32 +137,33 @@ namespace Emby.Server.Implementations.LiveTv.Listings private static ProgramInfo GetProgramInfo(XmlTvProgram program, ListingsProviderInfo info) { - string episodeTitle = program.Episode?.Title; + string episodeTitle = program.Episode.Title; + var programCategories = program.Categories.Where(c => !string.IsNullOrWhiteSpace(c)).ToList(); var programInfo = new ProgramInfo { ChannelId = program.ChannelId, EndDate = program.EndDate.UtcDateTime, - EpisodeNumber = program.Episode?.Episode, + EpisodeNumber = program.Episode.Episode, EpisodeTitle = episodeTitle, - Genres = program.Categories, + Genres = programCategories, StartDate = program.StartDate.UtcDateTime, Name = program.Title, Overview = program.Description, ProductionYear = program.CopyrightDate?.Year, - SeasonNumber = program.Episode?.Series, - IsSeries = program.Episode is not null, + SeasonNumber = program.Episode.Series, + IsSeries = program.Episode.Series is not null, IsRepeat = program.IsPreviouslyShown && !program.IsNew, IsPremiere = program.Premiere is not null, - IsKids = program.Categories.Any(c => info.KidsCategories.Contains(c, StringComparison.OrdinalIgnoreCase)), - IsMovie = program.Categories.Any(c => info.MovieCategories.Contains(c, StringComparison.OrdinalIgnoreCase)), - IsNews = program.Categories.Any(c => info.NewsCategories.Contains(c, StringComparison.OrdinalIgnoreCase)), - IsSports = program.Categories.Any(c => info.SportsCategories.Contains(c, StringComparison.OrdinalIgnoreCase)), + IsKids = programCategories.Any(c => info.KidsCategories.Contains(c, StringComparison.OrdinalIgnoreCase)), + IsMovie = programCategories.Any(c => info.MovieCategories.Contains(c, StringComparison.OrdinalIgnoreCase)), + IsNews = programCategories.Any(c => info.NewsCategories.Contains(c, StringComparison.OrdinalIgnoreCase)), + IsSports = programCategories.Any(c => info.SportsCategories.Contains(c, StringComparison.OrdinalIgnoreCase)), ImageUrl = string.IsNullOrEmpty(program.Icon?.Source) ? null : program.Icon.Source, HasImage = !string.IsNullOrEmpty(program.Icon?.Source), OfficialRating = string.IsNullOrEmpty(program.Rating?.Value) ? null : program.Rating.Value, CommunityRating = program.StarRating, - SeriesId = program.Episode is null ? null : program.Title?.GetMD5().ToString("N", CultureInfo.InvariantCulture) + SeriesId = program.Episode.Episode is null ? null : program.Title?.GetMD5().ToString("N", CultureInfo.InvariantCulture) }; if (string.IsNullOrWhiteSpace(program.ProgramId)) @@ -243,7 +244,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings { Id = c.Id, Name = c.DisplayName, - ImageUrl = string.IsNullOrEmpty(c.Icon.Source) ? null : c.Icon.Source, + ImageUrl = string.IsNullOrEmpty(c.Icon?.Source) ? null : c.Icon.Source, Number = string.IsNullOrWhiteSpace(c.Number) ? c.Id : c.Number }).ToList(); } diff --git a/tests/Jellyfin.Server.Implementations.Tests/LiveTv/Listings/XmlTvListingsProviderTests.cs b/tests/Jellyfin.Server.Implementations.Tests/LiveTv/Listings/XmlTvListingsProviderTests.cs index 82ce8fc4ec..ab1896c0d7 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/LiveTv/Listings/XmlTvListingsProviderTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/LiveTv/Listings/XmlTvListingsProviderTests.cs @@ -67,4 +67,23 @@ public class XmlTvListingsProviderTests Assert.Equal("https://domain.tld/image.png", program.ImageUrl); Assert.Equal("3297", program.ChannelId); } + + [Theory] + [InlineData("Test Data/LiveTv/Listings/XmlTv/emptycategory.xml")] + [InlineData("https://example.com/emptycategory.xml")] + public async Task GetProgramsAsync_EmptyCategories_Success(string path) + { + var info = new ListingsProviderInfo() + { + Path = path + }; + + var startDate = new DateTime(2022, 11, 4); + var programs = await _xmlTvListingsProvider.GetProgramsAsync(info, "3297", startDate, startDate.AddDays(1), CancellationToken.None); + var programsList = programs.ToList(); + Assert.Single(programsList); + var program = programsList[0]; + Assert.DoesNotContain(program.Genres, g => string.Equals(g, string.Empty, StringComparison.Ordinal)); + Assert.Equal("3297", program.ChannelId); + } } diff --git a/tests/Jellyfin.Server.Implementations.Tests/Test Data/LiveTv/Listings/XmlTv/emptycategory.xml b/tests/Jellyfin.Server.Implementations.Tests/Test Data/LiveTv/Listings/XmlTv/emptycategory.xml new file mode 100644 index 0000000000..dd4aa89774 --- /dev/null +++ b/tests/Jellyfin.Server.Implementations.Tests/Test Data/LiveTv/Listings/XmlTv/emptycategory.xml @@ -0,0 +1,6 @@ +<tv date="20221104"> + <programme channel="3297" start="20221104130000 -0400" stop="20221105235959 -0400"> + <category lang="en" /> + <category lang="en">sports</category> + </programme> +</tv> -- cgit v1.2.3 From 92f6e19a25def0fc699ff9a2190d54383f46451b Mon Sep 17 00:00:00 2001 From: Bond-009 <bond.009@outlook.com> Date: Tue, 14 Feb 2023 20:09:07 +0100 Subject: Enable nullable for more files (#9310) --- .../AppBase/BaseConfigurationManager.cs | 52 +++++++--------------- .../Configuration/ServerConfigurationManager.cs | 4 +- .../Images/BaseFolderImageProvider.cs | 2 - .../Images/FolderImageProvider.cs | 2 - .../Images/GenreImageProvider.cs | 2 - 5 files changed, 18 insertions(+), 44 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs index 985a127d50..a4deeddb78 100644 --- a/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs +++ b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -33,15 +31,10 @@ namespace Emby.Server.Implementations.AppBase private ConfigurationStore[] _configurationStores = Array.Empty<ConfigurationStore>(); private IConfigurationFactory[] _configurationFactories = Array.Empty<IConfigurationFactory>(); - /// <summary> - /// The _configuration loaded. - /// </summary> - private bool _configurationLoaded; - /// <summary> /// The _configuration. /// </summary> - private BaseApplicationConfiguration _configuration; + private BaseApplicationConfiguration? _configuration; /// <summary> /// Initializes a new instance of the <see cref="BaseConfigurationManager" /> class. @@ -63,17 +56,17 @@ namespace Emby.Server.Implementations.AppBase /// <summary> /// Occurs when [configuration updated]. /// </summary> - public event EventHandler<EventArgs> ConfigurationUpdated; + public event EventHandler<EventArgs>? ConfigurationUpdated; /// <summary> /// Occurs when [configuration updating]. /// </summary> - public event EventHandler<ConfigurationUpdateEventArgs> NamedConfigurationUpdating; + public event EventHandler<ConfigurationUpdateEventArgs>? NamedConfigurationUpdating; /// <summary> /// Occurs when [named configuration updated]. /// </summary> - public event EventHandler<ConfigurationUpdateEventArgs> NamedConfigurationUpdated; + public event EventHandler<ConfigurationUpdateEventArgs>? NamedConfigurationUpdated; /// <summary> /// Gets the type of the configuration. @@ -107,31 +100,25 @@ namespace Emby.Server.Implementations.AppBase { get { - if (_configurationLoaded) + if (_configuration is not null) { return _configuration; } lock (_configurationSyncLock) { - if (_configurationLoaded) + if (_configuration is not null) { return _configuration; } - _configuration = (BaseApplicationConfiguration)ConfigurationHelper.GetXmlConfiguration(ConfigurationType, CommonApplicationPaths.SystemConfigurationFilePath, XmlSerializer); - - _configurationLoaded = true; - - return _configuration; + return _configuration = (BaseApplicationConfiguration)ConfigurationHelper.GetXmlConfiguration(ConfigurationType, CommonApplicationPaths.SystemConfigurationFilePath, XmlSerializer); } } protected set { _configuration = value; - - _configurationLoaded = value is not null; } } @@ -183,7 +170,7 @@ namespace Emby.Server.Implementations.AppBase Logger.LogInformation("Saving system configuration"); var path = CommonApplicationPaths.SystemConfigurationFilePath; - Directory.CreateDirectory(Path.GetDirectoryName(path)); + Directory.CreateDirectory(Path.GetDirectoryName(path) ?? throw new InvalidOperationException("Path can't be a root directory.")); lock (_configurationSyncLock) { @@ -323,25 +310,20 @@ namespace Emby.Server.Implementations.AppBase private object LoadConfiguration(string path, Type configurationType) { - if (!File.Exists(path)) - { - return Activator.CreateInstance(configurationType); - } - try { - return XmlSerializer.DeserializeFromFile(configurationType, path); - } - catch (IOException) - { - return Activator.CreateInstance(configurationType); + if (File.Exists(path)) + { + return XmlSerializer.DeserializeFromFile(configurationType, path); + } } - catch (Exception ex) + catch (Exception ex) when (ex is not IOException) { Logger.LogError(ex, "Error loading configuration file: {Path}", path); - - return Activator.CreateInstance(configurationType); } + + return Activator.CreateInstance(configurationType) + ?? throw new InvalidOperationException("Configuration type can't be Nullable<T>."); } /// <inheritdoc /> @@ -367,7 +349,7 @@ namespace Emby.Server.Implementations.AppBase _configurations.AddOrUpdate(key, configuration, (_, _) => configuration); var path = GetConfigurationFile(key); - Directory.CreateDirectory(Path.GetDirectoryName(path)); + Directory.CreateDirectory(Path.GetDirectoryName(path) ?? throw new InvalidOperationException("Path can't be a root directory.")); lock (_configurationSyncLock) { diff --git a/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs b/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs index ff5602f243..6b8b1a620f 100644 --- a/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs +++ b/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using System.Globalization; using System.IO; @@ -36,7 +34,7 @@ namespace Emby.Server.Implementations.Configuration /// <summary> /// Configuration updating event. /// </summary> - public event EventHandler<GenericEventArgs<ServerConfiguration>> ConfigurationUpdating; + public event EventHandler<GenericEventArgs<ServerConfiguration>>? ConfigurationUpdating; /// <summary> /// Gets the type of the configuration. diff --git a/Emby.Server.Implementations/Images/BaseFolderImageProvider.cs b/Emby.Server.Implementations/Images/BaseFolderImageProvider.cs index 6fc7f1ac3a..84c21931c3 100644 --- a/Emby.Server.Implementations/Images/BaseFolderImageProvider.cs +++ b/Emby.Server.Implementations/Images/BaseFolderImageProvider.cs @@ -1,5 +1,3 @@ -#nullable disable - #pragma warning disable CS1591 using System.Collections.Generic; diff --git a/Emby.Server.Implementations/Images/FolderImageProvider.cs b/Emby.Server.Implementations/Images/FolderImageProvider.cs index 4376bd356c..90f7568a90 100644 --- a/Emby.Server.Implementations/Images/FolderImageProvider.cs +++ b/Emby.Server.Implementations/Images/FolderImageProvider.cs @@ -1,5 +1,3 @@ -#nullable disable - #pragma warning disable CS1591 using MediaBrowser.Common.Configuration; diff --git a/Emby.Server.Implementations/Images/GenreImageProvider.cs b/Emby.Server.Implementations/Images/GenreImageProvider.cs index 968bf5fa33..c9b41f8193 100644 --- a/Emby.Server.Implementations/Images/GenreImageProvider.cs +++ b/Emby.Server.Implementations/Images/GenreImageProvider.cs @@ -1,5 +1,3 @@ -#nullable disable - #pragma warning disable CS1591 using System.Collections.Generic; -- cgit v1.2.3 From c338aa7cb5c1ce2c011a419c251184d247ea1e5b Mon Sep 17 00:00:00 2001 From: MBR#0001 <mbr@mbr.pw> Date: Wed, 15 Feb 2023 21:07:36 +0100 Subject: Fix NRE in DisposeAsyncCore --- Emby.Server.Implementations/ApplicationHost.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 37a9e7715d..d104058cc1 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -1184,10 +1184,13 @@ namespace Emby.Server.Implementations } } - // used for closing websockets - foreach (var session in _sessionManager.Sessions) + if (_sessionManager != null) { - await session.DisposeAsync().ConfigureAwait(false); + // used for closing websockets + foreach (var session in _sessionManager.Sessions) + { + await session.DisposeAsync().ConfigureAwait(false); + } } } } -- cgit v1.2.3 From cb85fc688f84de928e046cb6bd02629f04d68df6 Mon Sep 17 00:00:00 2001 From: Bond_009 <bond.009@outlook.com> Date: Wed, 15 Feb 2023 23:41:28 +0100 Subject: Enable nullable for more files --- Emby.Server.Implementations/ScheduledTasks/TaskManager.cs | 6 ++---- .../Session/SessionWebSocketListener.cs | 13 ++++++++----- Emby.Server.Implementations/Sorting/RuntimeComparer.cs | 5 +---- .../Sorting/SeriesSortNameComparer.cs | 7 ++----- Emby.Server.Implementations/Sorting/SortNameComparer.cs | 5 +---- Emby.Server.Implementations/Sorting/StartDateComparer.cs | 6 ++---- Emby.Server.Implementations/Sorting/StudioComparer.cs | 5 +---- Emby.Server.Implementations/TV/TVSeriesManager.cs | 12 +++++------- MediaBrowser.Controller/Session/ISessionController.cs | 2 -- MediaBrowser.Model/Tasks/ITaskManager.cs | 4 ++-- 10 files changed, 24 insertions(+), 41 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs b/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs index 63f0beb105..6dc20e66ba 100644 --- a/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs +++ b/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs @@ -1,5 +1,3 @@ -#nullable disable - #pragma warning disable CS1591 using System; @@ -43,9 +41,9 @@ namespace Emby.Server.Implementations.ScheduledTasks ScheduledTasks = Array.Empty<IScheduledTaskWorker>(); } - public event EventHandler<GenericEventArgs<IScheduledTaskWorker>> TaskExecuting; + public event EventHandler<GenericEventArgs<IScheduledTaskWorker>>? TaskExecuting; - public event EventHandler<TaskCompletionEventArgs> TaskCompleted; + public event EventHandler<TaskCompletionEventArgs>? TaskCompleted; /// <summary> /// Gets the list of Scheduled Tasks. diff --git a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs index aebb559073..4e427b1a4b 100644 --- a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs +++ b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using System.Collections.Generic; using System.Linq; @@ -58,7 +56,7 @@ namespace Emby.Server.Implementations.Session /// <summary> /// The KeepAlive cancellation token. /// </summary> - private CancellationTokenSource _keepAliveCancellationToken; + private CancellationTokenSource? _keepAliveCancellationToken; /// <summary> /// Initializes a new instance of the <see cref="SessionWebSocketListener" /> class. @@ -105,7 +103,7 @@ namespace Emby.Server.Implementations.Session } } - private async Task<SessionInfo> GetSession(HttpContext httpContext, string remoteEndpoint) + private async Task<SessionInfo?> GetSession(HttpContext httpContext, string? remoteEndpoint) { if (!httpContext.User.Identity?.IsAuthenticated ?? false) { @@ -138,8 +136,13 @@ namespace Emby.Server.Implementations.Session /// </summary> /// <param name="sender">The WebSocket.</param> /// <param name="e">The event arguments.</param> - private void OnWebSocketClosed(object sender, EventArgs e) + private void OnWebSocketClosed(object? sender, EventArgs e) { + if (sender is null) + { + return; + } + var webSocket = (IWebSocketConnection)sender; _logger.LogDebug("WebSocket {0} is closed.", webSocket); RemoveWebSocket(webSocket); diff --git a/Emby.Server.Implementations/Sorting/RuntimeComparer.cs b/Emby.Server.Implementations/Sorting/RuntimeComparer.cs index 646bafbb54..753e58324c 100644 --- a/Emby.Server.Implementations/Sorting/RuntimeComparer.cs +++ b/Emby.Server.Implementations/Sorting/RuntimeComparer.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Sorting; @@ -24,10 +22,9 @@ namespace Emby.Server.Implementations.Sorting /// <param name="x">The x.</param> /// <param name="y">The y.</param> /// <returns>System.Int32.</returns> - public int Compare(BaseItem x, BaseItem y) + public int Compare(BaseItem? x, BaseItem? y) { ArgumentNullException.ThrowIfNull(x); - ArgumentNullException.ThrowIfNull(y); return (x.RunTimeTicks ?? 0).CompareTo(y.RunTimeTicks ?? 0); diff --git a/Emby.Server.Implementations/Sorting/SeriesSortNameComparer.cs b/Emby.Server.Implementations/Sorting/SeriesSortNameComparer.cs index 0bd9600b98..5b6c64f63a 100644 --- a/Emby.Server.Implementations/Sorting/SeriesSortNameComparer.cs +++ b/Emby.Server.Implementations/Sorting/SeriesSortNameComparer.cs @@ -1,5 +1,3 @@ -#nullable disable - #pragma warning disable CS1591 using System; @@ -23,15 +21,14 @@ namespace Emby.Server.Implementations.Sorting /// <param name="x">The x.</param> /// <param name="y">The y.</param> /// <returns>System.Int32.</returns> - public int Compare(BaseItem x, BaseItem y) + public int Compare(BaseItem? x, BaseItem? y) { return string.Compare(GetValue(x), GetValue(y), StringComparison.OrdinalIgnoreCase); } - private static string GetValue(BaseItem item) + private static string? GetValue(BaseItem? item) { var hasSeries = item as IHasSeries; - return hasSeries?.FindSeriesSortName(); } } diff --git a/Emby.Server.Implementations/Sorting/SortNameComparer.cs b/Emby.Server.Implementations/Sorting/SortNameComparer.cs index 628b9b3dda..19abafe192 100644 --- a/Emby.Server.Implementations/Sorting/SortNameComparer.cs +++ b/Emby.Server.Implementations/Sorting/SortNameComparer.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Sorting; @@ -24,10 +22,9 @@ namespace Emby.Server.Implementations.Sorting /// <param name="x">The x.</param> /// <param name="y">The y.</param> /// <returns>System.Int32.</returns> - public int Compare(BaseItem x, BaseItem y) + public int Compare(BaseItem? x, BaseItem? y) { ArgumentNullException.ThrowIfNull(x); - ArgumentNullException.ThrowIfNull(y); return string.Compare(x.SortName, y.SortName, StringComparison.OrdinalIgnoreCase); diff --git a/Emby.Server.Implementations/Sorting/StartDateComparer.cs b/Emby.Server.Implementations/Sorting/StartDateComparer.cs index c3df7c47e6..2759d20de8 100644 --- a/Emby.Server.Implementations/Sorting/StartDateComparer.cs +++ b/Emby.Server.Implementations/Sorting/StartDateComparer.cs @@ -1,5 +1,3 @@ -#nullable disable - #pragma warning disable CS1591 using System; @@ -24,7 +22,7 @@ namespace Emby.Server.Implementations.Sorting /// <param name="x">The x.</param> /// <param name="y">The y.</param> /// <returns>System.Int32.</returns> - public int Compare(BaseItem x, BaseItem y) + public int Compare(BaseItem? x, BaseItem? y) { return GetDate(x).CompareTo(GetDate(y)); } @@ -34,7 +32,7 @@ namespace Emby.Server.Implementations.Sorting /// </summary> /// <param name="x">The x.</param> /// <returns>DateTime.</returns> - private static DateTime GetDate(BaseItem x) + private static DateTime GetDate(BaseItem? x) { if (x is LiveTvProgram hasStartDate) { diff --git a/Emby.Server.Implementations/Sorting/StudioComparer.cs b/Emby.Server.Implementations/Sorting/StudioComparer.cs index 457c062714..89d10f3d23 100644 --- a/Emby.Server.Implementations/Sorting/StudioComparer.cs +++ b/Emby.Server.Implementations/Sorting/StudioComparer.cs @@ -1,5 +1,3 @@ -#nullable disable - #pragma warning disable CS1591 using System; @@ -24,10 +22,9 @@ namespace Emby.Server.Implementations.Sorting /// <param name="x">The x.</param> /// <param name="y">The y.</param> /// <returns>System.Int32.</returns> - public int Compare(BaseItem x, BaseItem y) + public int Compare(BaseItem? x, BaseItem? y) { ArgumentNullException.ThrowIfNull(x); - ArgumentNullException.ThrowIfNull(y); return AlphanumericComparator.CompareValues(x.Studios.FirstOrDefault(), y.Studios.FirstOrDefault()); diff --git a/Emby.Server.Implementations/TV/TVSeriesManager.cs b/Emby.Server.Implementations/TV/TVSeriesManager.cs index 967f90b55f..f0e173f0b1 100644 --- a/Emby.Server.Implementations/TV/TVSeriesManager.cs +++ b/Emby.Server.Implementations/TV/TVSeriesManager.cs @@ -1,5 +1,3 @@ -#nullable disable - #pragma warning disable CS1591 using System; @@ -42,7 +40,7 @@ namespace Emby.Server.Implementations.TV throw new ArgumentException("User not found"); } - string presentationUniqueKey = null; + string? presentationUniqueKey = null; if (query.SeriesId.HasValue && !query.SeriesId.Value.Equals(default)) { if (_libraryManager.GetItemById(query.SeriesId.Value) is Series series) @@ -91,7 +89,7 @@ namespace Emby.Server.Implementations.TV throw new ArgumentException("User not found"); } - string presentationUniqueKey = null; + string? presentationUniqueKey = null; int? limit = null; if (request.SeriesId.HasValue && !request.SeriesId.Value.Equals(default)) { @@ -168,7 +166,7 @@ namespace Emby.Server.Implementations.TV return !anyFound && i.LastWatchedDate == DateTime.MinValue; }) .Select(i => i.GetEpisodeFunction()) - .Where(i => i is not null); + .Where(i => i is not null)!; } private static string GetUniqueSeriesKey(Episode episode) @@ -185,7 +183,7 @@ namespace Emby.Server.Implementations.TV /// Gets the next up. /// </summary> /// <returns>Task{Episode}.</returns> - private (DateTime LastWatchedDate, Func<Episode> GetEpisodeFunction) GetNextUp(string seriesKey, User user, DtoOptions dtoOptions, bool rewatching) + private (DateTime LastWatchedDate, Func<Episode?> GetEpisodeFunction) GetNextUp(string seriesKey, User user, DtoOptions dtoOptions, bool rewatching) { var lastQuery = new InternalItemsQuery(user) { @@ -209,7 +207,7 @@ namespace Emby.Server.Implementations.TV var lastWatchedEpisode = _libraryManager.GetItemList(lastQuery).Cast<Episode>().FirstOrDefault(); - Episode GetEpisode() + Episode? GetEpisode() { var nextQuery = new InternalItemsQuery(user) { diff --git a/MediaBrowser.Controller/Session/ISessionController.cs b/MediaBrowser.Controller/Session/ISessionController.cs index b38ee11462..c8b29aa1f3 100644 --- a/MediaBrowser.Controller/Session/ISessionController.cs +++ b/MediaBrowser.Controller/Session/ISessionController.cs @@ -1,5 +1,3 @@ -#nullable disable - #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Model/Tasks/ITaskManager.cs b/MediaBrowser.Model/Tasks/ITaskManager.cs index 13bebc479e..5b55667e82 100644 --- a/MediaBrowser.Model/Tasks/ITaskManager.cs +++ b/MediaBrowser.Model/Tasks/ITaskManager.cs @@ -9,9 +9,9 @@ namespace MediaBrowser.Model.Tasks { public interface ITaskManager : IDisposable { - event EventHandler<GenericEventArgs<IScheduledTaskWorker>> TaskExecuting; + event EventHandler<GenericEventArgs<IScheduledTaskWorker>>? TaskExecuting; - event EventHandler<TaskCompletionEventArgs> TaskCompleted; + event EventHandler<TaskCompletionEventArgs>? TaskCompleted; /// <summary> /// Gets the list of Scheduled Tasks. -- cgit v1.2.3 From 785e8c4085d5a83fec30fd02f835d0aa1c885633 Mon Sep 17 00:00:00 2001 From: Ruben Kremer <rubenkremer@gmail.com> Date: Wed, 15 Feb 2023 21:41:19 +0000 Subject: Translated using Weblate (Dutch) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/nl/ --- Emby.Server.Implementations/Localization/Core/nl.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/nl.json b/Emby.Server.Implementations/Localization/Core/nl.json index e03747cbec..081ba0cc7e 100644 --- a/Emby.Server.Implementations/Localization/Core/nl.json +++ b/Emby.Server.Implementations/Localization/Core/nl.json @@ -58,8 +58,8 @@ "NotificationOptionServerRestartRequired": "Server herstart nodig", "NotificationOptionTaskFailed": "Geplande taak mislukt", "NotificationOptionUserLockedOut": "Gebruiker is vergrendeld", - "NotificationOptionVideoPlayback": "Video gestart", - "NotificationOptionVideoPlaybackStopped": "Video gestopt", + "NotificationOptionVideoPlayback": "Afspelen van video gestart", + "NotificationOptionVideoPlaybackStopped": "Afspelen van video gestopt", "Photos": "Foto's", "Playlists": "Afspeellijsten", "Plugin": "Plug-in", @@ -95,26 +95,26 @@ "TaskDownloadMissingSubtitlesDescription": "Zoekt op het internet naar ontbrekende ondertiteling gebaseerd op metadataconfiguratie.", "TaskDownloadMissingSubtitles": "Ontbrekende ondertiteling downloaden", "TaskRefreshChannelsDescription": "Vernieuwt informatie van internet kanalen.", - "TaskRefreshChannels": "Vernieuw Kanalen", + "TaskRefreshChannels": "Vernieuw kanalen", "TaskCleanTranscodeDescription": "Verwijdert transcode bestanden ouder dan 1 dag.", "TaskCleanLogs": "Logboekmap opschonen", "TaskCleanTranscode": "Transcoderingsmap opschonen", "TaskUpdatePluginsDescription": "Downloadt en installeert updates van plug-ins waarvoor automatisch bijwerken is ingeschakeld.", "TaskUpdatePlugins": "Plug-ins bijwerken", - "TaskRefreshPeopleDescription": "Update metadata for acteurs en regisseurs in de media bibliotheek.", + "TaskRefreshPeopleDescription": "Update metadata voor acteurs en regisseurs in de media bibliotheek.", "TaskRefreshPeople": "Personen vernieuwen", "TaskCleanLogsDescription": "Verwijdert log bestanden ouder dan {0} dagen.", "TaskRefreshLibraryDescription": "Scant de mediabibliotheek op nieuwe bestanden en vernieuwt de metadata.", "TaskRefreshLibrary": "Mediabibliotheek scannen", - "TaskRefreshChapterImagesDescription": "Maakt thumbnails aan voor videos met hoofdstukken.", - "TaskRefreshChapterImages": "Hoofdstukafbeeldingen uitpakken", + "TaskRefreshChapterImagesDescription": "Maakt voorbeeldafbeedingen aan voor video's met hoofdstukken.", + "TaskRefreshChapterImages": "Hoofdstukafbeeldingen extraheren", "TaskCleanCacheDescription": "Verwijdert gecachte bestanden die het systeem niet langer nodig heeft.", "TaskCleanCache": "Cache-map opschonen", - "TasksChannelsCategory": "Internet Kanalen", + "TasksChannelsCategory": "Internetkanalen", "TasksApplicationCategory": "Toepassing", "TasksLibraryCategory": "Bibliotheek", "TasksMaintenanceCategory": "Onderhoud", - "TaskCleanActivityLogDescription": "Verwijdert activiteiten logs ouder dan de ingestelde tijd.", + "TaskCleanActivityLogDescription": "Verwijdert activiteiten logs ouder dan de ingestelde leeftijd.", "TaskCleanActivityLog": "Activiteitenlogboek legen", "Undefined": "Niet gedefinieerd", "Forced": "Geforceerd", -- cgit v1.2.3 From 13589ceb064fc9b12f252b1eb107ae5f1e63d723 Mon Sep 17 00:00:00 2001 From: ikoch <sanekdavydov@gmail.com> Date: Wed, 15 Feb 2023 12:03:24 +0000 Subject: Translated using Weblate (Russian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ru/ --- Emby.Server.Implementations/Localization/Core/ru.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ru.json b/Emby.Server.Implementations/Localization/Core/ru.json index 65cf29e807..855223381c 100644 --- a/Emby.Server.Implementations/Localization/Core/ru.json +++ b/Emby.Server.Implementations/Localization/Core/ru.json @@ -23,7 +23,7 @@ "HeaderFavoriteShows": "Избранные сериалы", "HeaderFavoriteSongs": "Избранные композиции", "HeaderLiveTV": "Эфир", - "HeaderNextUp": "Очередное", + "HeaderNextUp": "Следующий", "HeaderRecordingGroups": "Группы записей", "HomeVideos": "Домашние видео", "Inherit": "Наследуемое", @@ -70,7 +70,7 @@ "ScheduledTaskFailedWithName": "{0} - неудачна", "ScheduledTaskStartedWithName": "{0} - запущена", "ServerNameNeedsToBeRestarted": "Необходим перезапуск {0}", - "Shows": "Передачи", + "Shows": "Телешоу", "Songs": "Композиции", "StartupEmbyServerIsLoading": "Jellyfin Server загружается. Повторите попытку в ближайшее время.", "SubtitleDownloadFailureForItem": "Субтитры к {0} не удалось загрузить", -- cgit v1.2.3 From bfb31a9bceb294f1c75e0c6530e917cc0a9bf108 Mon Sep 17 00:00:00 2001 From: stegl <primsteg@gmail.com> Date: Tue, 14 Feb 2023 22:38:50 +0000 Subject: Translated using Weblate (Slovenian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/sl/ --- Emby.Server.Implementations/Localization/Core/sl-SI.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/sl-SI.json b/Emby.Server.Implementations/Localization/Core/sl-SI.json index d845accac2..4c23f71efa 100644 --- a/Emby.Server.Implementations/Localization/Core/sl-SI.json +++ b/Emby.Server.Implementations/Localization/Core/sl-SI.json @@ -123,5 +123,6 @@ "TaskOptimizeDatabase": "Optimiziraj bazo podatkov", "TaskKeyframeExtractor": "Ekstraktor ključnih sličic", "External": "Zunanji", - "TaskKeyframeExtractorDescription": "Iz video datoteke Izvleče ključne sličice, da ustvari bolj natančne sezname predvajanja HLS. Proces lahko traja dolgo časa." + "TaskKeyframeExtractorDescription": "Iz video datoteke Izvleče ključne sličice, da ustvari bolj natančne sezname predvajanja HLS. Proces lahko traja dolgo časa.", + "HearingImpaired": "Oslabljen sluh" } -- cgit v1.2.3 From 65f6c2e2fd7b3a7f308463e3bc096ba6e8bf65da Mon Sep 17 00:00:00 2001 From: Ruben Wealth Hu <ArthurMacTavish@outlook.com> Date: Wed, 15 Feb 2023 13:06:43 +0000 Subject: Translated using Weblate (Indonesian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/id/ --- Emby.Server.Implementations/Localization/Core/id.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/id.json b/Emby.Server.Implementations/Localization/Core/id.json index 695c0f4048..87ce07da31 100644 --- a/Emby.Server.Implementations/Localization/Core/id.json +++ b/Emby.Server.Implementations/Localization/Core/id.json @@ -82,7 +82,7 @@ "MessageServerConfigurationUpdated": "Konfigurasi server telah diperbarui", "MessageNamedServerConfigurationUpdatedWithValue": "Bagian konfigurasi server {0} telah diperbarui", "FailedLoginAttemptWithUserName": "Gagal melakukan login dari {0}", - "CameraImageUploadedFrom": "Gambar kamera baru telah diunggah dari {0}", + "CameraImageUploadedFrom": "Sebuah gambar kamera baru telah diunggah dari {0}", "DeviceOfflineWithName": "{0} telah terputus", "DeviceOnlineWithName": "{0} telah terhubung", "NotificationOptionVideoPlaybackStopped": "Pemutaran video berhenti", -- cgit v1.2.3 From 48263078b46aa4ef46c0fb6944665b2c317bf077 Mon Sep 17 00:00:00 2001 From: Bond_009 <bond.009@outlook.com> Date: Fri, 17 Feb 2023 15:00:06 +0100 Subject: Reduce string allocations by regex --- Emby.Naming/AudioBook/AudioBookFilePathParser.cs | 4 +-- Emby.Naming/AudioBook/AudioBookNameParser.cs | 2 +- Emby.Naming/Common/NamingOptions.cs | 32 ---------------------- Emby.Naming/TV/EpisodePathParser.cs | 14 +++++----- Emby.Naming/Video/CleanDateTimeParser.cs | 2 +- Emby.Naming/Video/ExtraRuleResolver.cs | 2 +- Emby.Naming/Video/VideoListResolver.cs | 9 +++--- .../Library/Resolvers/Movies/MovieResolver.cs | 9 ++---- .../Users/UserManager.cs | 2 +- .../Encoder/EncoderValidator.cs | 6 ++-- .../Common/NamingOptionsTest.cs | 2 -- 11 files changed, 22 insertions(+), 62 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Naming/AudioBook/AudioBookFilePathParser.cs b/Emby.Naming/AudioBook/AudioBookFilePathParser.cs index 7b4429ab15..219599d569 100644 --- a/Emby.Naming/AudioBook/AudioBookFilePathParser.cs +++ b/Emby.Naming/AudioBook/AudioBookFilePathParser.cs @@ -40,7 +40,7 @@ namespace Emby.Naming.AudioBook var value = match.Groups["chapter"]; if (value.Success) { - if (int.TryParse(value.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var intValue)) + if (int.TryParse(value.ValueSpan, NumberStyles.Integer, CultureInfo.InvariantCulture, out var intValue)) { result.ChapterNumber = intValue; } @@ -52,7 +52,7 @@ namespace Emby.Naming.AudioBook var value = match.Groups["part"]; if (value.Success) { - if (int.TryParse(value.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var intValue)) + if (int.TryParse(value.ValueSpan, NumberStyles.Integer, CultureInfo.InvariantCulture, out var intValue)) { result.PartNumber = intValue; } diff --git a/Emby.Naming/AudioBook/AudioBookNameParser.cs b/Emby.Naming/AudioBook/AudioBookNameParser.cs index 97b34199e0..f49c3f0e75 100644 --- a/Emby.Naming/AudioBook/AudioBookNameParser.cs +++ b/Emby.Naming/AudioBook/AudioBookNameParser.cs @@ -47,7 +47,7 @@ namespace Emby.Naming.AudioBook var value = match.Groups["year"]; if (value.Success) { - if (int.TryParse(value.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var intValue)) + if (int.TryParse(value.ValueSpan, NumberStyles.Integer, CultureInfo.InvariantCulture, out var intValue)) { result.Year = intValue; } diff --git a/Emby.Naming/Common/NamingOptions.cs b/Emby.Naming/Common/NamingOptions.cs index 54f62a1570..c16a71e02f 100644 --- a/Emby.Naming/Common/NamingOptions.cs +++ b/Emby.Naming/Common/NamingOptions.cs @@ -453,16 +453,6 @@ namespace Emby.Naming.Common }, }; - EpisodeWithoutSeasonExpressions = new[] - { - @"[/\._ \-]()([0-9]+)(-[0-9]+)?" - }; - - EpisodeMultiPartExpressions = new[] - { - @"^[-_ex]+([0-9]+(?:(?:[a-i]|\\.[1-9])(?![0-9]))?)" - }; - VideoExtraRules = new[] { new ExtraRule( @@ -797,16 +787,6 @@ namespace Emby.Naming.Common /// </summary> public EpisodeExpression[] EpisodeExpressions { get; set; } - /// <summary> - /// Gets or sets list of raw episode without season regular expressions strings. - /// </summary> - public string[] EpisodeWithoutSeasonExpressions { get; set; } - - /// <summary> - /// Gets or sets list of raw multi-part episodes regular expressions strings. - /// </summary> - public string[] EpisodeMultiPartExpressions { get; set; } - /// <summary> /// Gets or sets list of video file extensions. /// </summary> @@ -877,16 +857,6 @@ namespace Emby.Naming.Common /// </summary> public Regex[] CleanStringRegexes { get; private set; } = Array.Empty<Regex>(); - /// <summary> - /// Gets list of episode without season regular expressions. - /// </summary> - public Regex[] EpisodeWithoutSeasonRegexes { get; private set; } = Array.Empty<Regex>(); - - /// <summary> - /// Gets list of multi-part episode regular expressions. - /// </summary> - public Regex[] EpisodeMultiPartRegexes { get; private set; } = Array.Empty<Regex>(); - /// <summary> /// Compiles raw regex strings into regexes. /// </summary> @@ -894,8 +864,6 @@ namespace Emby.Naming.Common { CleanDateTimeRegexes = CleanDateTimes.Select(Compile).ToArray(); CleanStringRegexes = CleanStrings.Select(Compile).ToArray(); - EpisodeWithoutSeasonRegexes = EpisodeWithoutSeasonExpressions.Select(Compile).ToArray(); - EpisodeMultiPartRegexes = EpisodeMultiPartExpressions.Select(Compile).ToArray(); } private Regex Compile(string exp) diff --git a/Emby.Naming/TV/EpisodePathParser.cs b/Emby.Naming/TV/EpisodePathParser.cs index d706be2802..8cd5a126e0 100644 --- a/Emby.Naming/TV/EpisodePathParser.cs +++ b/Emby.Naming/TV/EpisodePathParser.cs @@ -113,7 +113,7 @@ namespace Emby.Naming.TV if (expression.DateTimeFormats.Length > 0) { if (DateTime.TryParseExact( - match.Groups[0].Value, + match.Groups[0].ValueSpan, expression.DateTimeFormats, CultureInfo.InvariantCulture, DateTimeStyles.None, @@ -125,7 +125,7 @@ namespace Emby.Naming.TV result.Success = true; } } - else if (DateTime.TryParse(match.Groups[0].Value, out date)) + else if (DateTime.TryParse(match.Groups[0].ValueSpan, out date)) { result.Year = date.Year; result.Month = date.Month; @@ -138,12 +138,12 @@ namespace Emby.Naming.TV } else if (expression.IsNamed) { - if (int.TryParse(match.Groups["seasonnumber"].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var num)) + if (int.TryParse(match.Groups["seasonnumber"].ValueSpan, NumberStyles.Integer, CultureInfo.InvariantCulture, out var num)) { result.SeasonNumber = num; } - if (int.TryParse(match.Groups["epnumber"].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out num)) + if (int.TryParse(match.Groups["epnumber"].ValueSpan, NumberStyles.Integer, CultureInfo.InvariantCulture, out num)) { result.EpisodeNumber = num; } @@ -158,7 +158,7 @@ namespace Emby.Naming.TV if (nextIndex >= name.Length || !"0123456789iIpP".Contains(name[nextIndex], StringComparison.Ordinal)) { - if (int.TryParse(endingNumberGroup.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out num)) + if (int.TryParse(endingNumberGroup.ValueSpan, NumberStyles.Integer, CultureInfo.InvariantCulture, out num)) { result.EndingEpisodeNumber = num; } @@ -170,12 +170,12 @@ namespace Emby.Naming.TV } else { - if (int.TryParse(match.Groups[1].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var num)) + if (int.TryParse(match.Groups[1].ValueSpan, NumberStyles.Integer, CultureInfo.InvariantCulture, out var num)) { result.SeasonNumber = num; } - if (int.TryParse(match.Groups[2].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out num)) + if (int.TryParse(match.Groups[2].ValueSpan, NumberStyles.Integer, CultureInfo.InvariantCulture, out num)) { result.EpisodeNumber = num; } diff --git a/Emby.Naming/Video/CleanDateTimeParser.cs b/Emby.Naming/Video/CleanDateTimeParser.cs index 0ee633dcc6..9a6c6e978b 100644 --- a/Emby.Naming/Video/CleanDateTimeParser.cs +++ b/Emby.Naming/Video/CleanDateTimeParser.cs @@ -43,7 +43,7 @@ namespace Emby.Naming.Video && match.Groups.Count == 5 && match.Groups[1].Success && match.Groups[2].Success - && int.TryParse(match.Groups[2].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var year)) + && int.TryParse(match.Groups[2].ValueSpan, NumberStyles.Integer, CultureInfo.InvariantCulture, out var year)) { result = new CleanDateTimeResult(match.Groups[1].Value.TrimEnd(), year); return true; diff --git a/Emby.Naming/Video/ExtraRuleResolver.cs b/Emby.Naming/Video/ExtraRuleResolver.cs index 21d0da3642..3219472eff 100644 --- a/Emby.Naming/Video/ExtraRuleResolver.cs +++ b/Emby.Naming/Video/ExtraRuleResolver.cs @@ -56,7 +56,7 @@ namespace Emby.Naming.Video } else if (rule.RuleType == ExtraRuleType.Regex) { - var filename = Path.GetFileName(path); + var filename = Path.GetFileName(path.AsSpan()); var isMatch = Regex.IsMatch(filename, rule.Token, RegexOptions.IgnoreCase | RegexOptions.Compiled); diff --git a/Emby.Naming/Video/VideoListResolver.cs b/Emby.Naming/Video/VideoListResolver.cs index 01e383d1c0..8247c374d0 100644 --- a/Emby.Naming/Video/VideoListResolver.cs +++ b/Emby.Naming/Video/VideoListResolver.cs @@ -176,16 +176,15 @@ namespace Emby.Naming.Video } // There are no span overloads for regex unfortunately - var tmpTestFilename = testFilename.ToString(); - if (CleanStringParser.TryClean(tmpTestFilename, namingOptions.CleanStringRegexes, out var cleanName)) + if (CleanStringParser.TryClean(testFilename.ToString(), namingOptions.CleanStringRegexes, out var cleanName)) { - tmpTestFilename = cleanName.Trim(); + testFilename = cleanName.AsSpan().Trim(); } // The CleanStringParser should have removed common keywords etc. - return string.IsNullOrEmpty(tmpTestFilename) + return testFilename.IsEmpty || testFilename[0] == '-' - || Regex.IsMatch(tmpTestFilename, @"^\[([^]]*)\]", RegexOptions.Compiled); + || Regex.IsMatch(testFilename, @"^\[([^]]*)\]", RegexOptions.Compiled); } } } diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs index 1522cd3aef..ef4fa1fd2d 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs @@ -313,13 +313,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies return result; } - private static bool IsIgnored(string filename) - { - // Ignore samples - Match m = Regex.Match(filename, @"\bsample\b", RegexOptions.IgnoreCase | RegexOptions.Compiled); - - return m.Success; - } + private static bool IsIgnored(ReadOnlySpan<char> filename) + => Regex.IsMatch(filename, @"\bsample\b", RegexOptions.IgnoreCase | RegexOptions.Compiled); private static bool ContainsFile(IReadOnlyList<VideoInfo> result, FileSystemMetadata file) { diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 92384986af..c4756433e0 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -740,7 +740,7 @@ namespace Jellyfin.Server.Implementations.Users throw new ArgumentException("Usernames can contain unicode symbols, numbers (0-9), dashes (-), underscores (_), apostrophes ('), and periods (.)", nameof(name)); } - private static bool IsValidUsername(string name) + private static bool IsValidUsername(ReadOnlySpan<char> name) { // This is some regex that matches only on unicode "word" characters, as well as -, _ and @ // In theory this will cut out most if not all 'control' characters which should help minimize any weirdness diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index 9e6134b524..540d50bf15 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -277,7 +277,7 @@ namespace MediaBrowser.MediaEncoding.Encoder if (match.Success) { - if (Version.TryParse(match.Groups[1].Value, out var result)) + if (Version.TryParse(match.Groups[1].ValueSpan, out var result)) { return result; } @@ -327,8 +327,8 @@ namespace MediaBrowser.MediaEncoding.Encoder RegexOptions.Multiline)) { var version = new Version( - int.Parse(match.Groups["major"].Value, CultureInfo.InvariantCulture), - int.Parse(match.Groups["minor"].Value, CultureInfo.InvariantCulture)); + int.Parse(match.Groups["major"].ValueSpan, CultureInfo.InvariantCulture), + int.Parse(match.Groups["minor"].ValueSpan, CultureInfo.InvariantCulture)); map.Add(match.Groups["name"].Value, version); } diff --git a/tests/Jellyfin.Naming.Tests/Common/NamingOptionsTest.cs b/tests/Jellyfin.Naming.Tests/Common/NamingOptionsTest.cs index 58aaed023a..c496632482 100644 --- a/tests/Jellyfin.Naming.Tests/Common/NamingOptionsTest.cs +++ b/tests/Jellyfin.Naming.Tests/Common/NamingOptionsTest.cs @@ -12,8 +12,6 @@ namespace Jellyfin.Naming.Tests.Common Assert.NotEmpty(options.CleanDateTimeRegexes); Assert.NotEmpty(options.CleanStringRegexes); - Assert.NotEmpty(options.EpisodeWithoutSeasonRegexes); - Assert.NotEmpty(options.EpisodeMultiPartRegexes); } [Fact] -- cgit v1.2.3 From 80939b922c08c926dd470ce19bc961cd4cc7f8b4 Mon Sep 17 00:00:00 2001 From: adrian gustavo martinez <adrian.gustavo.martinez@gmail.com> Date: Fri, 17 Feb 2023 02:06:29 +0000 Subject: Translated using Weblate (Spanish (Argentina)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/es_AR/ --- Emby.Server.Implementations/Localization/Core/es-AR.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/es-AR.json b/Emby.Server.Implementations/Localization/Core/es-AR.json index 8ad9e8c716..8bd3c5defe 100644 --- a/Emby.Server.Implementations/Localization/Core/es-AR.json +++ b/Emby.Server.Implementations/Localization/Core/es-AR.json @@ -118,11 +118,11 @@ "TaskCleanActivityLog": "Borrar log de actividades", "Undefined": "Indefinido", "Forced": "Forzado", - "Default": "Por Defecto", + "Default": "Predeterminado", "TaskOptimizeDatabaseDescription": "Compacta la base de datos y restaura el espacio libre. Ejecutar esta tarea después de actualizar las librerías o realizar otros cambios que impliquen modificar las bases de datos puede mejorar la performance.", "TaskOptimizeDatabase": "Optimización de base de datos", "External": "Externo", "TaskKeyframeExtractorDescription": "Extrae Fotogramas Clave de los archivos de vídeo para crear Listas de Reprodución HLS más precisas. Esta tarea puede durar mucho tiempo.", "TaskKeyframeExtractor": "Extractor de Fotogramas Clave", - "HearingImpaired": "Personas con discapacidad auditiva" + "HearingImpaired": "Discapacidad Auditiva" } -- cgit v1.2.3 From de96fe1f521a9869544f4550fa2e7186f192fcd3 Mon Sep 17 00:00:00 2001 From: rushmash <rushmash@gmail.com> Date: Sun, 19 Feb 2023 13:12:32 +0000 Subject: Translated using Weblate (Russian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ru/ --- Emby.Server.Implementations/Localization/Core/ru.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ru.json b/Emby.Server.Implementations/Localization/Core/ru.json index 855223381c..839bbcb6d3 100644 --- a/Emby.Server.Implementations/Localization/Core/ru.json +++ b/Emby.Server.Implementations/Localization/Core/ru.json @@ -16,7 +16,7 @@ "Folders": "Папки", "Genres": "Жанры", "HeaderAlbumArtists": "Исполнители альбома", - "HeaderContinueWatching": "Продолжение просмотра", + "HeaderContinueWatching": "Продолжить просмотр", "HeaderFavoriteAlbums": "Избранные альбомы", "HeaderFavoriteArtists": "Избранные исполнители", "HeaderFavoriteEpisodes": "Избранные эпизоды", -- cgit v1.2.3 From 24a7e210c377bf828f21b5812f25c6545f7de006 Mon Sep 17 00:00:00 2001 From: Bond_009 <bond.009@outlook.com> Date: Sun, 19 Feb 2023 16:52:29 +0100 Subject: Optimize tryparse * Don't check for null before * Don't try different formats when not needed (NumberFormat.Integer is the fast path) --- Emby.Naming/Audio/AlbumParser.cs | 9 ++---- .../Data/SqliteItemRepository.cs | 2 +- .../LiveTv/Listings/SchedulesDirect.cs | 12 ++++---- .../LiveTv/TunerHosts/M3uParser.cs | 35 ++++++++-------------- .../Extensions/ClaimsPrincipalExtensions.cs | 3 +- Jellyfin.Api/Helpers/StreamingHelpers.cs | 4 +-- Jellyfin.Api/Helpers/TranscodingJobHelper.cs | 3 +- Jellyfin.Networking/Manager/NetworkManager.cs | 2 +- .../Routines/MigrateDisplayPreferencesDb.cs | 2 +- MediaBrowser.Common/Net/IPHost.cs | 4 +-- MediaBrowser.Controller/LiveTv/LiveTvChannel.cs | 12 +++----- .../MediaEncoding/EncodingHelper.cs | 7 ++--- .../MediaEncoding/EncodingJobInfo.cs | 15 ++++------ MediaBrowser.Controller/MediaEncoding/JobLogger.cs | 8 ++--- .../Parsers/BaseItemXmlParser.cs | 7 ++--- .../Probing/ProbeResultNormalizer.cs | 33 ++++++++------------ MediaBrowser.Model/Dlna/ConditionProcessor.cs | 4 +-- MediaBrowser.Model/Dlna/SortCriteria.cs | 2 +- MediaBrowser.Model/Dlna/StreamBuilder.cs | 23 +++++++------- MediaBrowser.Model/Dlna/StreamInfo.cs | 24 +++------------ MediaBrowser.Providers/Manager/MetadataService.cs | 1 - .../Plugins/Omdb/OmdbProvider.cs | 8 ++--- MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs | 7 ++--- 23 files changed, 83 insertions(+), 144 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Naming/Audio/AlbumParser.cs b/Emby.Naming/Audio/AlbumParser.cs index bbfdccc902..86a5641531 100644 --- a/Emby.Naming/Audio/AlbumParser.cs +++ b/Emby.Naming/Audio/AlbumParser.cs @@ -3,6 +3,7 @@ using System.Globalization; using System.IO; using System.Text.RegularExpressions; using Emby.Naming.Common; +using Jellyfin.Extensions; namespace Emby.Naming.Audio { @@ -58,13 +59,7 @@ namespace Emby.Naming.Audio var tmp = trimmedFilename.Slice(prefix.Length).Trim(); - int index = tmp.IndexOf(' '); - if (index != -1) - { - tmp = tmp.Slice(0, index); - } - - if (int.TryParse(tmp, NumberStyles.Integer, CultureInfo.InvariantCulture, out _)) + if (int.TryParse(tmp.LeftPart(' '), CultureInfo.InvariantCulture, out _)) { return true; } diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 602d2a8538..90f03995e8 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -1195,7 +1195,7 @@ namespace Emby.Server.Implementations.Data Path = RestorePath(path.ToString()) }; - if (long.TryParse(dateModified, NumberStyles.Any, CultureInfo.InvariantCulture, out var ticks) + if (long.TryParse(dateModified, CultureInfo.InvariantCulture, out var ticks) && ticks >= DateTime.MinValue.Ticks && ticks <= DateTime.MaxValue.Ticks) { diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs index 3f7914d3bb..b5e742f98f 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs @@ -570,15 +570,13 @@ namespace Emby.Server.Implementations.LiveTv.Listings _tokens.TryAdd(username, savedToken); } - if (!string.IsNullOrEmpty(savedToken.Name) && !string.IsNullOrEmpty(savedToken.Value)) + if (!string.IsNullOrEmpty(savedToken.Name) + && long.TryParse(savedToken.Value, CultureInfo.InvariantCulture, out long ticks)) { - if (long.TryParse(savedToken.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out long ticks)) + // If it's under 24 hours old we can still use it + if (DateTime.UtcNow.Ticks - ticks < TimeSpan.FromHours(20).Ticks) { - // If it's under 24 hours old we can still use it - if (DateTime.UtcNow.Ticks - ticks < TimeSpan.FromHours(20).Ticks) - { - return savedToken.Name; - } + return savedToken.Name; } } diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs index a423ec8f48..c18cb00740 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs @@ -168,28 +168,24 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts string numberString = null; string attributeValue; - if (attributes.TryGetValue("tvg-chno", out attributeValue)) + if (attributes.TryGetValue("tvg-chno", out attributeValue) + && double.TryParse(attributeValue, CultureInfo.InvariantCulture, out _)) { - if (double.TryParse(attributeValue, NumberStyles.Any, CultureInfo.InvariantCulture, out _)) - { - numberString = attributeValue; - } + numberString = attributeValue; } if (!IsValidChannelNumber(numberString)) { if (attributes.TryGetValue("tvg-id", out attributeValue)) { - if (double.TryParse(attributeValue, NumberStyles.Any, CultureInfo.InvariantCulture, out _)) + if (double.TryParse(attributeValue, CultureInfo.InvariantCulture, out _)) { numberString = attributeValue; } - else if (attributes.TryGetValue("channel-id", out attributeValue)) + else if (attributes.TryGetValue("channel-id", out attributeValue) + && double.TryParse(attributeValue, CultureInfo.InvariantCulture, out _)) { - if (double.TryParse(attributeValue, NumberStyles.Any, CultureInfo.InvariantCulture, out _)) - { - numberString = attributeValue; - } + numberString = attributeValue; } } @@ -207,7 +203,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts { var numberPart = nameInExtInf.Slice(0, numberIndex).Trim(new[] { ' ', '.' }); - if (double.TryParse(numberPart, NumberStyles.Any, CultureInfo.InvariantCulture, out _)) + if (double.TryParse(numberPart, CultureInfo.InvariantCulture, out _)) { numberString = numberPart.ToString(); } @@ -255,19 +251,14 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts private static bool IsValidChannelNumber(string numberString) { - if (string.IsNullOrWhiteSpace(numberString) || - string.Equals(numberString, "-1", StringComparison.OrdinalIgnoreCase) || - string.Equals(numberString, "0", StringComparison.OrdinalIgnoreCase)) - { - return false; - } - - if (!double.TryParse(numberString, NumberStyles.Any, CultureInfo.InvariantCulture, out _)) + if (string.IsNullOrWhiteSpace(numberString) + || string.Equals(numberString, "-1", StringComparison.OrdinalIgnoreCase) + || string.Equals(numberString, "0", StringComparison.OrdinalIgnoreCase)) { return false; } - return true; + return double.TryParse(numberString, CultureInfo.InvariantCulture, out _); } private static string GetChannelName(string extInf, Dictionary<string, string> attributes) @@ -285,7 +276,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts { var numberPart = nameInExtInf.Substring(0, numberIndex).Trim(new[] { ' ', '.' }); - if (double.TryParse(numberPart, NumberStyles.Any, CultureInfo.InvariantCulture, out _)) + if (double.TryParse(numberPart, CultureInfo.InvariantCulture, out _)) { // channel.Number = number.ToString(); nameInExtInf = nameInExtInf.Substring(numberIndex + 1).Trim(new[] { ' ', '-' }); diff --git a/Jellyfin.Api/Extensions/ClaimsPrincipalExtensions.cs b/Jellyfin.Api/Extensions/ClaimsPrincipalExtensions.cs index 6b3e78d4d1..d2e8eb378b 100644 --- a/Jellyfin.Api/Extensions/ClaimsPrincipalExtensions.cs +++ b/Jellyfin.Api/Extensions/ClaimsPrincipalExtensions.cs @@ -71,8 +71,7 @@ public static class ClaimsPrincipalExtensions public static bool GetIsApiKey(this ClaimsPrincipal user) { var claimValue = GetClaimValue(user, InternalClaimTypes.IsApiKey); - return !string.IsNullOrEmpty(claimValue) - && bool.TryParse(claimValue, out var parsedClaimValue) + return bool.TryParse(claimValue, out var parsedClaimValue) && parsedClaimValue; } diff --git a/Jellyfin.Api/Helpers/StreamingHelpers.cs b/Jellyfin.Api/Helpers/StreamingHelpers.cs index d867df86ee..9b5a14c4de 100644 --- a/Jellyfin.Api/Helpers/StreamingHelpers.cs +++ b/Jellyfin.Api/Helpers/StreamingHelpers.cs @@ -337,10 +337,10 @@ public static class StreamingHelpers value = index == -1 ? value.Slice(npt.Length) : value.Slice(npt.Length, index - npt.Length); - if (value.IndexOf(':') == -1) + if (!value.Contains(':')) { // Parses npt times in the format of '417.33' - if (double.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var seconds)) + if (double.TryParse(value, CultureInfo.InvariantCulture, out var seconds)) { return TimeSpan.FromSeconds(seconds).Ticks; } diff --git a/Jellyfin.Api/Helpers/TranscodingJobHelper.cs b/Jellyfin.Api/Helpers/TranscodingJobHelper.cs index 12960f87a2..cd8ac49820 100644 --- a/Jellyfin.Api/Helpers/TranscodingJobHelper.cs +++ b/Jellyfin.Api/Helpers/TranscodingJobHelper.cs @@ -457,8 +457,7 @@ public class TranscodingJobHelper : IDisposable var videoCodec = state.ActualOutputVideoCodec; var hardwareAccelerationTypeString = _serverConfigurationManager.GetEncodingOptions().HardwareAccelerationType; HardwareEncodingType? hardwareAccelerationType = null; - if (!string.IsNullOrEmpty(hardwareAccelerationTypeString) - && Enum.TryParse<HardwareEncodingType>(hardwareAccelerationTypeString, out var parsedHardwareAccelerationType)) + if (Enum.TryParse<HardwareEncodingType>(hardwareAccelerationTypeString, out var parsedHardwareAccelerationType)) { hardwareAccelerationType = parsedHardwareAccelerationType; } diff --git a/Jellyfin.Networking/Manager/NetworkManager.cs b/Jellyfin.Networking/Manager/NetworkManager.cs index 86989bfde6..88332ce393 100644 --- a/Jellyfin.Networking/Manager/NetworkManager.cs +++ b/Jellyfin.Networking/Manager/NetworkManager.cs @@ -316,7 +316,7 @@ namespace Jellyfin.Networking.Manager /// <inheritdoc/> public string GetBindInterface(string source, out int? port) { - if (!string.IsNullOrEmpty(source) && IPHost.TryParse(source, out IPHost host)) + if (IPHost.TryParse(source, out IPHost host)) { return GetBindInterface(host, out port); } diff --git a/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs index 4b692d14f0..7c4ffdbc00 100644 --- a/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs +++ b/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs @@ -130,7 +130,7 @@ namespace Jellyfin.Server.Migrations.Routines SkipForwardLength = dto.CustomPrefs.TryGetValue("skipForwardLength", out var length) && int.TryParse(length, out var skipForwardLength) ? skipForwardLength : 30000, - SkipBackwardLength = dto.CustomPrefs.TryGetValue("skipBackLength", out length) && !string.IsNullOrEmpty(length) && int.TryParse(length, out var skipBackwardLength) + SkipBackwardLength = dto.CustomPrefs.TryGetValue("skipBackLength", out length) && int.TryParse(length, out var skipBackwardLength) ? skipBackwardLength : 10000, EnableNextVideoInfoOverlay = dto.CustomPrefs.TryGetValue("enableNextVideoInfoOverlay", out var enabled) && !string.IsNullOrEmpty(enabled) diff --git a/MediaBrowser.Common/Net/IPHost.cs b/MediaBrowser.Common/Net/IPHost.cs index 7cf1b8aa0b..ec76a43b6f 100644 --- a/MediaBrowser.Common/Net/IPHost.cs +++ b/MediaBrowser.Common/Net/IPHost.cs @@ -190,7 +190,7 @@ namespace MediaBrowser.Common.Net /// <returns>Object representing the string, if it has successfully been parsed.</returns> public static IPHost Parse(string host) { - if (!string.IsNullOrEmpty(host) && IPHost.TryParse(host, out IPHost res)) + if (IPHost.TryParse(host, out IPHost res)) { return res; } @@ -206,7 +206,7 @@ namespace MediaBrowser.Common.Net /// <returns>Object representing the string, if it has successfully been parsed.</returns> public static IPHost Parse(string host, AddressFamily family) { - if (!string.IsNullOrEmpty(host) && IPHost.TryParse(host, out IPHost res)) + if (IPHost.TryParse(host, out IPHost res)) { if (family == AddressFamily.InterNetwork) { diff --git a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs index 9788260420..f11e3c8f68 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.Linq; using System.Text.Json.Serialization; using Jellyfin.Data.Enums; using Jellyfin.Extensions; @@ -105,12 +106,9 @@ namespace MediaBrowser.Controller.LiveTv protected override string CreateSortName() { - if (!string.IsNullOrEmpty(Number)) + if (double.TryParse(Number, CultureInfo.InvariantCulture, out double number)) { - if (double.TryParse(Number, NumberStyles.Any, CultureInfo.InvariantCulture, out double number)) - { - return string.Format(CultureInfo.InvariantCulture, "{0:00000.0}", number) + "-" + (Name ?? string.Empty); - } + return string.Format(CultureInfo.InvariantCulture, "{0:00000.0}", number) + "-" + (Name ?? string.Empty); } return (Number ?? string.Empty) + "-" + (Name ?? string.Empty); @@ -122,9 +120,7 @@ namespace MediaBrowser.Controller.LiveTv } public IEnumerable<BaseItem> GetTaggedItems() - { - return new List<BaseItem>(); - } + => Enumerable.Empty<BaseItem>(); public override List<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution) { diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 14547d4406..11b17eec32 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -1143,7 +1143,7 @@ namespace MediaBrowser.Controller.MediaEncoding public static string NormalizeTranscodingLevel(EncodingJobInfo state, string level) { - if (double.TryParse(level, NumberStyles.Any, CultureInfo.InvariantCulture, out double requestLevel)) + if (double.TryParse(level, CultureInfo.InvariantCulture, out double requestLevel)) { if (string.Equals(state.ActualOutputVideoCodec, "hevc", StringComparison.OrdinalIgnoreCase) || string.Equals(state.ActualOutputVideoCodec, "h265", StringComparison.OrdinalIgnoreCase)) @@ -1737,7 +1737,7 @@ namespace MediaBrowser.Controller.MediaEncoding else if (string.Equals(videoEncoder, "hevc_qsv", StringComparison.OrdinalIgnoreCase)) { // hevc_qsv use -level 51 instead of -level 153. - if (double.TryParse(level, NumberStyles.Any, CultureInfo.InvariantCulture, out double hevcLevel)) + if (double.TryParse(level, CultureInfo.InvariantCulture, out double hevcLevel)) { param += " -level " + (hevcLevel / 3); } @@ -1916,8 +1916,7 @@ namespace MediaBrowser.Controller.MediaEncoding // If a specific level was requested, the source must match or be less than var level = state.GetRequestedLevel(videoStream.Codec); - if (!string.IsNullOrEmpty(level) - && double.TryParse(level, NumberStyles.Any, CultureInfo.InvariantCulture, out var requestLevel)) + if (double.TryParse(level, CultureInfo.InvariantCulture, out var requestLevel)) { if (!videoStream.Level.HasValue) { diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs index 179cabc84a..a6b5416601 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs @@ -250,8 +250,7 @@ namespace MediaBrowser.Controller.MediaEncoding } var level = GetRequestedLevel(ActualOutputVideoCodec); - if (!string.IsNullOrEmpty(level) - && double.TryParse(level, NumberStyles.Any, CultureInfo.InvariantCulture, out var result)) + if (double.TryParse(level, CultureInfo.InvariantCulture, out var result)) { return result; } @@ -645,8 +644,7 @@ namespace MediaBrowser.Controller.MediaEncoding if (!string.IsNullOrEmpty(codec)) { var value = BaseRequest.GetOption(codec, "maxrefframes"); - if (!string.IsNullOrEmpty(value) - && int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result)) + if (int.TryParse(value, CultureInfo.InvariantCulture, out var result)) { return result; } @@ -665,8 +663,7 @@ namespace MediaBrowser.Controller.MediaEncoding if (!string.IsNullOrEmpty(codec)) { var value = BaseRequest.GetOption(codec, "videobitdepth"); - if (!string.IsNullOrEmpty(value) - && int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result)) + if (int.TryParse(value, CultureInfo.InvariantCulture, out var result)) { return result; } @@ -685,8 +682,7 @@ namespace MediaBrowser.Controller.MediaEncoding if (!string.IsNullOrEmpty(codec)) { var value = BaseRequest.GetOption(codec, "audiobitdepth"); - if (!string.IsNullOrEmpty(value) - && int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result)) + if (int.TryParse(value, CultureInfo.InvariantCulture, out var result)) { return result; } @@ -700,8 +696,7 @@ namespace MediaBrowser.Controller.MediaEncoding if (!string.IsNullOrEmpty(codec)) { var value = BaseRequest.GetOption(codec, "audiochannels"); - if (!string.IsNullOrEmpty(value) - && int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result)) + if (int.TryParse(value, CultureInfo.InvariantCulture, out var result)) { return result; } diff --git a/MediaBrowser.Controller/MediaEncoding/JobLogger.cs b/MediaBrowser.Controller/MediaEncoding/JobLogger.cs index d8475f12ae..3b34af4e96 100644 --- a/MediaBrowser.Controller/MediaEncoding/JobLogger.cs +++ b/MediaBrowser.Controller/MediaEncoding/JobLogger.cs @@ -86,7 +86,7 @@ namespace MediaBrowser.Controller.MediaEncoding { var rate = parts[i + 1]; - if (float.TryParse(rate, NumberStyles.Any, CultureInfo.InvariantCulture, out var val)) + if (float.TryParse(rate, CultureInfo.InvariantCulture, out var val)) { framerate = val; } @@ -95,7 +95,7 @@ namespace MediaBrowser.Controller.MediaEncoding { var rate = part.Split('=', 2)[^1]; - if (float.TryParse(rate, NumberStyles.Any, CultureInfo.InvariantCulture, out var val)) + if (float.TryParse(rate, CultureInfo.InvariantCulture, out var val)) { framerate = val; } @@ -127,7 +127,7 @@ namespace MediaBrowser.Controller.MediaEncoding if (scale.HasValue) { - if (long.TryParse(size, NumberStyles.Any, CultureInfo.InvariantCulture, out var val)) + if (long.TryParse(size, CultureInfo.InvariantCulture, out var val)) { bytesTranscoded = val * scale.Value; } @@ -146,7 +146,7 @@ namespace MediaBrowser.Controller.MediaEncoding if (scale.HasValue) { - if (float.TryParse(rate, NumberStyles.Any, CultureInfo.InvariantCulture, out var val)) + if (float.TryParse(rate, CultureInfo.InvariantCulture, out var val)) { bitRate = (int)Math.Ceiling(val * scale.Value); } diff --git a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs index 1030cf0558..c8912807ea 100644 --- a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs +++ b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs @@ -169,12 +169,9 @@ namespace MediaBrowser.LocalMetadata.Parsers { var text = reader.ReadElementContentAsString(); - if (!string.IsNullOrEmpty(text)) + if (float.TryParse(text, CultureInfo.InvariantCulture, out var value)) { - if (float.TryParse(text, NumberStyles.Any, CultureInfo.InvariantCulture, out var value)) - { - item.CriticRating = value; - } + item.CriticRating = value; } break; diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index 99310a75dd..8b82795883 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -97,12 +97,9 @@ namespace MediaBrowser.MediaEncoding.Probing { info.Container = NormalizeFormat(data.Format.FormatName); - if (!string.IsNullOrEmpty(data.Format.BitRate)) + if (int.TryParse(data.Format.BitRate, CultureInfo.InvariantCulture, out var value)) { - if (int.TryParse(data.Format.BitRate, NumberStyles.Any, CultureInfo.InvariantCulture, out var value)) - { - info.Bitrate = value; - } + info.Bitrate = value; } } @@ -561,8 +558,8 @@ namespace MediaBrowser.MediaEncoding.Probing } } - if (string.IsNullOrWhiteSpace(name) || - string.IsNullOrWhiteSpace(value)) + if (string.IsNullOrWhiteSpace(name) + || string.IsNullOrWhiteSpace(value)) { return null; } @@ -674,9 +671,9 @@ namespace MediaBrowser.MediaEncoding.Probing stream.Channels = streamInfo.Channels; - if (int.TryParse(streamInfo.SampleRate, NumberStyles.Any, CultureInfo.InvariantCulture, out var value)) + if (int.TryParse(streamInfo.SampleRate, CultureInfo.InvariantCulture, out var sampleRate)) { - stream.SampleRate = value; + stream.SampleRate = sampleRate; } stream.ChannelLayout = ParseChannelLayout(streamInfo.ChannelLayout); @@ -853,22 +850,18 @@ namespace MediaBrowser.MediaEncoding.Probing // Get stream bitrate var bitrate = 0; - if (!string.IsNullOrEmpty(streamInfo.BitRate)) + if (int.TryParse(streamInfo.BitRate, CultureInfo.InvariantCulture, out var value)) { - if (int.TryParse(streamInfo.BitRate, NumberStyles.Any, CultureInfo.InvariantCulture, out var value)) - { - bitrate = value; - } + bitrate = value; } // The bitrate info of FLAC musics and some videos is included in formatInfo. if (bitrate == 0 && formatInfo is not null - && !string.IsNullOrEmpty(formatInfo.BitRate) && (stream.Type == MediaStreamType.Video || (isAudio && stream.Type == MediaStreamType.Audio))) { // If the stream info doesn't have a bitrate get the value from the media format info - if (int.TryParse(formatInfo.BitRate, NumberStyles.Any, CultureInfo.InvariantCulture, out var value)) + if (int.TryParse(formatInfo.BitRate, CultureInfo.InvariantCulture, out value)) { bitrate = value; } @@ -972,8 +965,8 @@ namespace MediaBrowser.MediaEncoding.Probing var parts = (original ?? string.Empty).Split(':'); if (!(parts.Length == 2 - && int.TryParse(parts[0], NumberStyles.Any, CultureInfo.InvariantCulture, out var width) - && int.TryParse(parts[1], NumberStyles.Any, CultureInfo.InvariantCulture, out var height) + && int.TryParse(parts[0], CultureInfo.InvariantCulture, out var width) + && int.TryParse(parts[1], CultureInfo.InvariantCulture, out var height) && width > 0 && height > 0)) { @@ -1117,7 +1110,7 @@ namespace MediaBrowser.MediaEncoding.Probing } var duration = GetDictionaryValue(streamInfo.Tags, "DURATION-eng") ?? GetDictionaryValue(streamInfo.Tags, "DURATION"); - if (!string.IsNullOrEmpty(duration) && TimeSpan.TryParse(duration, out var parsedDuration)) + if (TimeSpan.TryParse(duration, out var parsedDuration)) { return parsedDuration.TotalSeconds; } @@ -1446,7 +1439,7 @@ namespace MediaBrowser.MediaEncoding.Probing // Limit accuracy to milliseconds to match xml saving var secondsString = chapter.StartTime; - if (double.TryParse(secondsString, NumberStyles.Any, CultureInfo.InvariantCulture, out var seconds)) + if (double.TryParse(secondsString, CultureInfo.InvariantCulture, out var seconds)) { var ms = Math.Round(TimeSpan.FromSeconds(seconds).TotalMilliseconds); info.StartPositionTicks = TimeSpan.FromMilliseconds(ms).Ticks; diff --git a/MediaBrowser.Model/Dlna/ConditionProcessor.cs b/MediaBrowser.Model/Dlna/ConditionProcessor.cs index 5734224167..00b406bbe6 100644 --- a/MediaBrowser.Model/Dlna/ConditionProcessor.cs +++ b/MediaBrowser.Model/Dlna/ConditionProcessor.cs @@ -136,7 +136,7 @@ namespace MediaBrowser.Model.Dlna return !condition.IsRequired; } - if (int.TryParse(condition.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out var expected)) + if (int.TryParse(condition.Value, CultureInfo.InvariantCulture, out var expected)) { switch (condition.Condition) { @@ -212,7 +212,7 @@ namespace MediaBrowser.Model.Dlna return !condition.IsRequired; } - if (double.TryParse(condition.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out var expected)) + if (double.TryParse(condition.Value, CultureInfo.InvariantCulture, out var expected)) { switch (condition.Condition) { diff --git a/MediaBrowser.Model/Dlna/SortCriteria.cs b/MediaBrowser.Model/Dlna/SortCriteria.cs index 7fef16e535..7df53c6d19 100644 --- a/MediaBrowser.Model/Dlna/SortCriteria.cs +++ b/MediaBrowser.Model/Dlna/SortCriteria.cs @@ -9,7 +9,7 @@ namespace MediaBrowser.Model.Dlna { public SortCriteria(string sortOrder) { - if (!string.IsNullOrEmpty(sortOrder) && Enum.TryParse<SortOrder>(sortOrder, true, out var sortOrderValue)) + if (Enum.TryParse<SortOrder>(sortOrder, true, out var sortOrderValue)) { SortOrder = sortOrderValue; } diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index eb6d602959..ab81bfb34c 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -551,8 +551,7 @@ namespace MediaBrowser.Model.Dlna } playlistItem.TranscodeSeekInfo = transcodingProfile.TranscodeSeekInfo; - if (!string.IsNullOrEmpty(transcodingProfile.MaxAudioChannels) - && int.TryParse(transcodingProfile.MaxAudioChannels, NumberStyles.Any, CultureInfo.InvariantCulture, out int transcodingMaxAudioChannels)) + if (int.TryParse(transcodingProfile.MaxAudioChannels, CultureInfo.InvariantCulture, out int transcodingMaxAudioChannels)) { playlistItem.TranscodingMaxAudioChannels = transcodingMaxAudioChannels; } @@ -1607,7 +1606,7 @@ namespace MediaBrowser.Model.Dlna continue; } - if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var num)) + if (int.TryParse(value, CultureInfo.InvariantCulture, out var num)) { if (condition.Condition == ProfileConditionType.Equals) { @@ -1633,7 +1632,7 @@ namespace MediaBrowser.Model.Dlna continue; } - if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var num)) + if (int.TryParse(value, CultureInfo.InvariantCulture, out var num)) { if (condition.Condition == ProfileConditionType.Equals) { @@ -1669,7 +1668,7 @@ namespace MediaBrowser.Model.Dlna } } - if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var num)) + if (int.TryParse(value, CultureInfo.InvariantCulture, out var num)) { if (condition.Condition == ProfileConditionType.Equals) { @@ -1793,7 +1792,7 @@ namespace MediaBrowser.Model.Dlna } } - if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var num)) + if (int.TryParse(value, CultureInfo.InvariantCulture, out var num)) { if (condition.Condition == ProfileConditionType.Equals) { @@ -1829,7 +1828,7 @@ namespace MediaBrowser.Model.Dlna } } - if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var num)) + if (int.TryParse(value, CultureInfo.InvariantCulture, out var num)) { if (condition.Condition == ProfileConditionType.Equals) { @@ -1919,7 +1918,7 @@ namespace MediaBrowser.Model.Dlna continue; } - if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var num)) + if (int.TryParse(value, CultureInfo.InvariantCulture, out var num)) { if (condition.Condition == ProfileConditionType.Equals) { @@ -1945,7 +1944,7 @@ namespace MediaBrowser.Model.Dlna continue; } - if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var num)) + if (int.TryParse(value, CultureInfo.InvariantCulture, out var num)) { if (condition.Condition == ProfileConditionType.Equals) { @@ -1971,7 +1970,7 @@ namespace MediaBrowser.Model.Dlna continue; } - if (float.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var num)) + if (float.TryParse(value, CultureInfo.InvariantCulture, out var num)) { if (condition.Condition == ProfileConditionType.Equals) { @@ -1997,7 +1996,7 @@ namespace MediaBrowser.Model.Dlna continue; } - if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var num)) + if (int.TryParse(value, CultureInfo.InvariantCulture, out var num)) { if (condition.Condition == ProfileConditionType.Equals) { @@ -2023,7 +2022,7 @@ namespace MediaBrowser.Model.Dlna continue; } - if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var num)) + if (int.TryParse(value, CultureInfo.InvariantCulture, out var num)) { if (condition.Condition == ProfileConditionType.Equals) { diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs index 3b55099079..93ace43df4 100644 --- a/MediaBrowser.Model/Dlna/StreamInfo.cs +++ b/MediaBrowser.Model/Dlna/StreamInfo.cs @@ -922,12 +922,8 @@ namespace MediaBrowser.Model.Dlna public int? GetTargetVideoBitDepth(string codec) { var value = GetOption(codec, "videobitdepth"); - if (string.IsNullOrEmpty(value)) - { - return null; - } - if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) + if (int.TryParse(value, CultureInfo.InvariantCulture, out var result)) { return result; } @@ -938,12 +934,8 @@ namespace MediaBrowser.Model.Dlna public int? GetTargetAudioBitDepth(string codec) { var value = GetOption(codec, "audiobitdepth"); - if (string.IsNullOrEmpty(value)) - { - return null; - } - if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) + if (int.TryParse(value, CultureInfo.InvariantCulture, out var result)) { return result; } @@ -954,12 +946,8 @@ namespace MediaBrowser.Model.Dlna public double? GetTargetVideoLevel(string codec) { var value = GetOption(codec, "level"); - if (string.IsNullOrEmpty(value)) - { - return null; - } - if (double.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result)) + if (double.TryParse(value, CultureInfo.InvariantCulture, out var result)) { return result; } @@ -970,12 +958,8 @@ namespace MediaBrowser.Model.Dlna public int? GetTargetRefFrames(string codec) { var value = GetOption(codec, "maxrefframes"); - if (string.IsNullOrEmpty(value)) - { - return null; - } - if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result)) + if (int.TryParse(value, CultureInfo.InvariantCulture, out var result)) { return result; } diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs index 9f287766af..0605b0bd78 100644 --- a/MediaBrowser.Providers/Manager/MetadataService.cs +++ b/MediaBrowser.Providers/Manager/MetadataService.cs @@ -151,7 +151,6 @@ namespace MediaBrowser.Providers.Manager ApplySearchResult(id, refreshOptions.SearchResult); } - // await FindIdentities(id, cancellationToken).ConfigureAwait(false); id.IsAutomated = refreshOptions.IsAutomated; var result = await RefreshWithProviders(metadataResult, id, refreshOptions, providers, ImageProvider, cancellationToken).ConfigureAwait(false); diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs index 497437bd8a..dfaba6423b 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs @@ -98,8 +98,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb // item.VoteCount = voteCount; } - if (!string.IsNullOrEmpty(result.imdbRating) - && float.TryParse(result.imdbRating, NumberStyles.Any, CultureInfo.InvariantCulture, out var imdbRating) + if (float.TryParse(result.imdbRating, CultureInfo.InvariantCulture, out var imdbRating) && imdbRating >= 0) { item.CommunityRating = imdbRating; @@ -209,8 +208,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb // item.VoteCount = voteCount; } - if (!string.IsNullOrEmpty(result.imdbRating) - && float.TryParse(result.imdbRating, NumberStyles.Any, CultureInfo.InvariantCulture, out var imdbRating) + if (float.TryParse(result.imdbRating, CultureInfo.InvariantCulture, out var imdbRating) && imdbRating >= 0) { item.CommunityRating = imdbRating; @@ -552,7 +550,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb if (rating?.Value is not null) { var value = rating.Value.TrimEnd('%'); - if (float.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var score)) + if (float.TryParse(value, CultureInfo.InvariantCulture, out var score)) { return score; } diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs index c3a735c6de..159b8d6580 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs @@ -315,12 +315,9 @@ namespace MediaBrowser.XbmcMetadata.Parsers { var text = reader.ReadElementContentAsString(); - if (!string.IsNullOrEmpty(text)) + if (float.TryParse(text, CultureInfo.InvariantCulture, out var value)) { - if (float.TryParse(text, NumberStyles.Any, CultureInfo.InvariantCulture, out var value)) - { - item.CriticRating = value; - } + item.CriticRating = value; } break; -- cgit v1.2.3 From b119c6d868c24212300285c501f43af91b31d404 Mon Sep 17 00:00:00 2001 From: Bond_009 <bond.009@outlook.com> Date: Mon, 20 Feb 2023 10:14:08 +0100 Subject: StringComparison.Ordinal instead of StringComparison.OrdinalIgnoreCase --- Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs index c18cb00740..1c0ed6505c 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs @@ -252,8 +252,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts private static bool IsValidChannelNumber(string numberString) { if (string.IsNullOrWhiteSpace(numberString) - || string.Equals(numberString, "-1", StringComparison.OrdinalIgnoreCase) - || string.Equals(numberString, "0", StringComparison.OrdinalIgnoreCase)) + || string.Equals(numberString, "-1", StringComparison.Ordinal) + || string.Equals(numberString, "0", StringComparison.Ordinal)) { return false; } -- cgit v1.2.3 From ed2280a0605c7e971d151b9b2239d332ee204579 Mon Sep 17 00:00:00 2001 From: Shadowghost <Ghost_of_Stone@web.de> Date: Sun, 9 Oct 2022 22:56:23 +0200 Subject: Overhaul content ratings --- .../Localization/LocalizationManager.cs | 18 +++++- .../Localization/Ratings/0-prefer.csv | 11 ++++ .../Localization/Ratings/au.csv | 20 +++--- .../Localization/Ratings/be.csv | 17 +++-- .../Localization/Ratings/br.csv | 14 +++-- .../Localization/Ratings/ca.csv | 26 ++++++-- .../Localization/Ratings/co.csv | 15 +++-- .../Localization/Ratings/de.csv | 22 ++++--- .../Localization/Ratings/dk.csv | 11 ++-- .../Localization/Ratings/es.csv | 30 +++++++-- .../Localization/Ratings/fi.csv | 20 +++--- .../Localization/Ratings/fr.csv | 17 +++-- .../Localization/Ratings/gb.csv | 29 ++++++--- .../Localization/Ratings/ie.csv | 15 +++-- .../Localization/Ratings/jp.csv | 15 +++-- .../Localization/Ratings/kz.csv | 13 ++-- .../Localization/Ratings/mx.csv | 12 ++-- .../Localization/Ratings/nl.csv | 14 +++-- .../Localization/Ratings/no.csv | 15 +++-- .../Localization/Ratings/nz.csv | 26 ++++---- .../Localization/Ratings/ro.csv | 7 ++- .../Localization/Ratings/ru.csv | 11 ++-- .../Localization/Ratings/se.csv | 15 +++-- .../Localization/Ratings/uk.csv | 29 ++++++--- .../Localization/Ratings/us.csv | 73 +++++++++++++++------- .../Localization/LocalizationManagerTests.cs | 25 ++++---- 26 files changed, 346 insertions(+), 174 deletions(-) create mode 100644 Emby.Server.Implementations/Localization/Ratings/0-prefer.csv (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs index b418c7877e..5e22e78867 100644 --- a/Emby.Server.Implementations/Localization/LocalizationManager.cs +++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs @@ -3,6 +3,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Globalization; using System.IO; +using System.Linq; using System.Reflection; using System.Text.Json; using System.Threading.Tasks; @@ -25,7 +26,7 @@ namespace Emby.Server.Implementations.Localization private const string CulturesPath = "Emby.Server.Implementations.Localization.iso6392.txt"; private const string CountriesPath = "Emby.Server.Implementations.Localization.countries.json"; private static readonly Assembly _assembly = typeof(LocalizationManager).Assembly; - private static readonly string[] _unratedValues = { "n/a", "unrated", "not rated" }; + private static readonly string[] _unratedValues = { "n/a", "unrated", "not rated", "nr" }; private readonly IServerConfigurationManager _configurationManager; private readonly ILogger<LocalizationManager> _logger; @@ -194,6 +195,7 @@ namespace Emby.Server.Implementations.Localization { var countryCode = _configurationManager.Configuration.MetadataCountryCode; + // Fall back to US ratings if no country code is specified or country code does not exist. if (string.IsNullOrEmpty(countryCode)) { countryCode = "us"; @@ -221,12 +223,14 @@ namespace Emby.Server.Implementations.Localization { ArgumentException.ThrowIfNullOrEmpty(rating); + // Handle unrated content if (_unratedValues.Contains(rating.AsSpan(), StringComparison.OrdinalIgnoreCase)) { return null; } // Fairly common for some users to have "Rated R" in their rating field + rating = rating.Replace("Rated :", string.Empty, StringComparison.OrdinalIgnoreCase); rating = rating.Replace("Rated ", string.Empty, StringComparison.OrdinalIgnoreCase); var ratingsDictionary = GetParentalRatingsDictionary(); @@ -257,6 +261,18 @@ namespace Emby.Server.Implementations.Localization } } + // Remove prefix country code to handle "DE-18" + index = rating.IndexOf('-', StringComparison.Ordinal); + if (index != -1) + { + var trimmedRating = rating.AsSpan(index).TrimStart('-').Trim(); + + if (!trimmedRating.IsEmpty) + { + return GetRatingLevel(trimmedRating.ToString()); + } + } + // TODO: Further improve by normalizing out all spaces and dashes return null; } diff --git a/Emby.Server.Implementations/Localization/Ratings/0-prefer.csv b/Emby.Server.Implementations/Localization/Ratings/0-prefer.csv new file mode 100644 index 0000000000..36886ba760 --- /dev/null +++ b/Emby.Server.Implementations/Localization/Ratings/0-prefer.csv @@ -0,0 +1,11 @@ +E,0 +EC,0 +T,7 +M,18 +AO,18 +UR,18 +RP,18 +X,1000 +XX,1000 +XXX,1000 +XXXX,1000 diff --git a/Emby.Server.Implementations/Localization/Ratings/au.csv b/Emby.Server.Implementations/Localization/Ratings/au.csv index 11f4ed94cd..4ab808ae9a 100644 --- a/Emby.Server.Implementations/Localization/Ratings/au.csv +++ b/Emby.Server.Implementations/Localization/Ratings/au.csv @@ -1,7 +1,13 @@ -AU-G,1 -AU-PG,5 -AU-M,6 -AU-MA15+,7 -AU-R18+,9 -AU-X18+,10 -AU-RC,11 +Exempt,0 +G,0 +7+,7 +M,15 +MA,15 +MA15+,15 +PG,16 +16+,16 +R,18 +R18+,18 +X18+,18 +18+,18 +X,1000 diff --git a/Emby.Server.Implementations/Localization/Ratings/be.csv b/Emby.Server.Implementations/Localization/Ratings/be.csv index d3937caf78..5588f63b45 100644 --- a/Emby.Server.Implementations/Localization/Ratings/be.csv +++ b/Emby.Server.Implementations/Localization/Ratings/be.csv @@ -1,6 +1,11 @@ -BE-AL,1 -BE-MG6,2 -BE-6,3 -BE-9,5 -BE-12,6 -BE-16,8 +AL,0 +KT,0 +TOUS,0 +MG6,6 +6,6 +9,9 +KNT,12 +12,12 +BE_14,14 +16,16 +18,18 diff --git a/Emby.Server.Implementations/Localization/Ratings/br.csv b/Emby.Server.Implementations/Localization/Ratings/br.csv index e5edaf62cf..5ec1eb2627 100644 --- a/Emby.Server.Implementations/Localization/Ratings/br.csv +++ b/Emby.Server.Implementations/Localization/Ratings/br.csv @@ -1,6 +1,8 @@ -BR-L,1 -BR-10,5 -BR-12,7 -BR-14,8 -BR-16,8 -BR-18,9 +Livre,0 +L,0 +ER,9 +10,10 +12,12 +14,14 +16,16 +18,18 diff --git a/Emby.Server.Implementations/Localization/Ratings/ca.csv b/Emby.Server.Implementations/Localization/Ratings/ca.csv index 5aef0580f8..336ee28067 100644 --- a/Emby.Server.Implementations/Localization/Ratings/ca.csv +++ b/Emby.Server.Implementations/Localization/Ratings/ca.csv @@ -1,6 +1,20 @@ -CA-G,1 -CA-PG,5 -CA-14A,7 -CA-A,8 -CA-18A,9 -CA-R,10 +E,0 +G,0 +TV-Y,0 +TV-G,0 +TV-Y7,7 +TV-Y7-FV,7 +PG,9 +TV-PG,9 +PG-13,13 +13+,13 +TV-14,14 +14A,14 +16+,16 +NC-17,17 +R,18 +TV-MA,18 +18A,18 +18+,18 +A,1000 +Prohibited,1001 diff --git a/Emby.Server.Implementations/Localization/Ratings/co.csv b/Emby.Server.Implementations/Localization/Ratings/co.csv index 9684fa0524..e1e96c5909 100644 --- a/Emby.Server.Implementations/Localization/Ratings/co.csv +++ b/Emby.Server.Implementations/Localization/Ratings/co.csv @@ -1,8 +1,7 @@ -CO-T,1 -CO-7,5 -CO-12,7 -CO-15,8 -CO-18,10 -CO-X,100 -CO-BANNED,15 -CO-E,15 +T,0 +7,7 +12,12 +15,15 +18,18 +X,1000 +Prohibited,1001 diff --git a/Emby.Server.Implementations/Localization/Ratings/de.csv b/Emby.Server.Implementations/Localization/Ratings/de.csv index f944a140d0..d633a5dab7 100644 --- a/Emby.Server.Implementations/Localization/Ratings/de.csv +++ b/Emby.Server.Implementations/Localization/Ratings/de.csv @@ -1,10 +1,12 @@ -DE-0,1 -FSK-0,1 -DE-6,5 -FSK-6,5 -DE-12,7 -FSK-12,7 -DE-16,8 -FSK-16,8 -DE-18,9 -FSK-18,9 +Educational,0 +Infoprogramm,0 +FSK-0,0 +0,0 +FSK-6,6 +6,6 +FSK-12,12 +12,12 +FSK-16,16 +16,16 +FSK-18,18 +18,18 diff --git a/Emby.Server.Implementations/Localization/Ratings/dk.csv b/Emby.Server.Implementations/Localization/Ratings/dk.csv index 5364ae1f27..4ef63b2eac 100644 --- a/Emby.Server.Implementations/Localization/Ratings/dk.csv +++ b/Emby.Server.Implementations/Localization/Ratings/dk.csv @@ -1,4 +1,7 @@ -DA-A,1 -DA-7,5 -DA-11,6 -DA-15,8 +F,0 +A,0 +7,7 +11,11 +12,12 +15,15 +16,16 diff --git a/Emby.Server.Implementations/Localization/Ratings/es.csv b/Emby.Server.Implementations/Localization/Ratings/es.csv index 887d91ba63..0bc1d3f7d0 100644 --- a/Emby.Server.Implementations/Localization/Ratings/es.csv +++ b/Emby.Server.Implementations/Localization/Ratings/es.csv @@ -1,6 +1,24 @@ -ES-A,1 -ES-APTA,1 -ES-7,3 -ES-12,6 -ES-16,8 -ES-18,11 +A,0 +A/fig,0 +A/i,0 +A/fig/i,0 +APTA,0 +TP,0 +0+,0 +6+,6 +7/fig,7 +7/i,7 +7/i/fig,7 +7,7 +9+,9 +10,10 +12,12 +12/fig,12 +13,13 +14,14 +16,16 +16/fig,16 +18,18 +18/fig,18 +X,1000 +Banned,1001 diff --git a/Emby.Server.Implementations/Localization/Ratings/fi.csv b/Emby.Server.Implementations/Localization/Ratings/fi.csv index 782785890f..7ff92f259b 100644 --- a/Emby.Server.Implementations/Localization/Ratings/fi.csv +++ b/Emby.Server.Implementations/Localization/Ratings/fi.csv @@ -1,10 +1,10 @@ -FI-S,1 -FI-T,1 -FI-7,4 -FI-12,5 -FI-16,8 -FI-18,9 -FI-K7,4 -FI-K12,5 -FI-K16,8 -FI-K18,9 +S,0 +T,0 +K7,7 +7,7 +K12,12 +12,12 +K16,16 +16,16 +K18,18 +18,18 diff --git a/Emby.Server.Implementations/Localization/Ratings/fr.csv b/Emby.Server.Implementations/Localization/Ratings/fr.csv index f586a3fa91..774a705891 100644 --- a/Emby.Server.Implementations/Localization/Ratings/fr.csv +++ b/Emby.Server.Implementations/Localization/Ratings/fr.csv @@ -1,5 +1,12 @@ -FR-U,1 -FR-10,5 -FR-12,7 -FR-16,9 -FR-18,10 +Public Averti,0 +Tous Publics,0 +U,0 +0+,0 +6+,6 +9+,9 +10,10 +12,12 +14+,14 +16,16 +18,18 +X,1000 diff --git a/Emby.Server.Implementations/Localization/Ratings/gb.csv b/Emby.Server.Implementations/Localization/Ratings/gb.csv index c1f7d04529..75b1c20589 100644 --- a/Emby.Server.Implementations/Localization/Ratings/gb.csv +++ b/Emby.Server.Implementations/Localization/Ratings/gb.csv @@ -1,7 +1,22 @@ -GB-U,1 -GB-PG,5 -GB-12,6 -GB-12A,7 -GB-15,8 -GB-18,9 -GB-R18,15 +All,0 +E,0 +G,0 +U,0 +0+,0 +6+,6 +7+,7 +PG,8 +9+,9 +12,12 +12+,12 +12A,12 +Teen,13 +13+,13 +14+,14 +15,15 +16,16 +Caution,18 +18,18 +Mature,1000 +Adult,1000 +R18,1000 diff --git a/Emby.Server.Implementations/Localization/Ratings/ie.csv b/Emby.Server.Implementations/Localization/Ratings/ie.csv index e42be5cd49..6ef2e50128 100644 --- a/Emby.Server.Implementations/Localization/Ratings/ie.csv +++ b/Emby.Server.Implementations/Localization/Ratings/ie.csv @@ -1,6 +1,9 @@ -IE-G,1 -IE-PG,5 -IE-12A,7 -IE-15A,8 -IE-16,9 -IE-18,10 +G,4 +PG,12 +12,12 +12A,12 +12PG,12 +15,15 +15A,15 +16,16 +18,18 diff --git a/Emby.Server.Implementations/Localization/Ratings/jp.csv b/Emby.Server.Implementations/Localization/Ratings/jp.csv index a8fc2d1431..bfb5fdaae9 100644 --- a/Emby.Server.Implementations/Localization/Ratings/jp.csv +++ b/Emby.Server.Implementations/Localization/Ratings/jp.csv @@ -1,4 +1,11 @@ -JP-G,1 -JP-PG12,7 -JP-15+,8 -JP-18+,10 +A,0 +G,0 +B,12 +PG12,12 +C,15 +15+,15 +R15+,15 +16+,16 +D,17 +Z,18 +18+,18 diff --git a/Emby.Server.Implementations/Localization/Ratings/kz.csv b/Emby.Server.Implementations/Localization/Ratings/kz.csv index d546bff53d..e26b32b67e 100644 --- a/Emby.Server.Implementations/Localization/Ratings/kz.csv +++ b/Emby.Server.Implementations/Localization/Ratings/kz.csv @@ -1,7 +1,6 @@ -KZ-6-,0 -KZ-6+,6 -KZ-12+,12 -KZ-14+,14 -KZ-16+,16 -KZ-18+,18 -KZ-21+,21 +K,0 +БА,12 +Б14,14 +E16,16 +E18,18 +HA,18 diff --git a/Emby.Server.Implementations/Localization/Ratings/mx.csv b/Emby.Server.Implementations/Localization/Ratings/mx.csv index 785a8ba227..305912f239 100644 --- a/Emby.Server.Implementations/Localization/Ratings/mx.csv +++ b/Emby.Server.Implementations/Localization/Ratings/mx.csv @@ -1,6 +1,6 @@ -MX-AA,1 -MX-A,5 -MX-B,7 -MX-B-15,8 -MX-C,9 -MX-D,10 +A,0 +AA,0 +B,12 +B-15,15 +C,18 +D,1000 diff --git a/Emby.Server.Implementations/Localization/Ratings/nl.csv b/Emby.Server.Implementations/Localization/Ratings/nl.csv index 8c005092e4..44f372b2d6 100644 --- a/Emby.Server.Implementations/Localization/Ratings/nl.csv +++ b/Emby.Server.Implementations/Localization/Ratings/nl.csv @@ -1,6 +1,8 @@ -NL-AL,1 -NL-MG6,2 -NL-6,3 -NL-9,5 -NL-12,6 -NL-16,8 +AL,0 +MG6,6 +6,6 +9,9 +12,12 +14,14 +16,16 +18,18 diff --git a/Emby.Server.Implementations/Localization/Ratings/no.csv b/Emby.Server.Implementations/Localization/Ratings/no.csv index 127407be86..c8f8e93db7 100644 --- a/Emby.Server.Implementations/Localization/Ratings/no.csv +++ b/Emby.Server.Implementations/Localization/Ratings/no.csv @@ -1,6 +1,9 @@ -NO-A,1 -NO-6,3 -NO-9,4 -NO-12,5 -NO-15,8 -NO-18,9 +A,0 +6,6 +7,7 +9,9 +11,11 +12,12 +15,15 +18,18 +Not approved,1001 diff --git a/Emby.Server.Implementations/Localization/Ratings/nz.csv b/Emby.Server.Implementations/Localization/Ratings/nz.csv index bba99b764a..f617f0c39d 100644 --- a/Emby.Server.Implementations/Localization/Ratings/nz.csv +++ b/Emby.Server.Implementations/Localization/Ratings/nz.csv @@ -1,11 +1,15 @@ -NZ-G,1 -NZ-PG,5 -NZ-M,6 -NZ-R13,7 -NZ-RP13,7 -NZ-R15,8 -NZ-RP16,9 -NZ-R16,9 -NZ-R18,10 -NZ-R,10 -NZ-MA,10 +Exempt,0 +G,0 +GY,13 +PG,13 +R13,13 +RP13,13 +R15,15 +M,16 +R16,16 +RP16,16 +GA,18 +R18,18 +MA,1000 +R,1001 +Objectionable,1001 diff --git a/Emby.Server.Implementations/Localization/Ratings/ro.csv b/Emby.Server.Implementations/Localization/Ratings/ro.csv index 4089b282f0..44c23e2486 100644 --- a/Emby.Server.Implementations/Localization/Ratings/ro.csv +++ b/Emby.Server.Implementations/Localization/Ratings/ro.csv @@ -1 +1,6 @@ -RO-AG,1 +AG,0 +AP-12,12 +N-15,15 +IM-18,18 +IM-18-XXX,1000 +IC,1001 diff --git a/Emby.Server.Implementations/Localization/Ratings/ru.csv b/Emby.Server.Implementations/Localization/Ratings/ru.csv index 1bc94affd6..8b264070ba 100644 --- a/Emby.Server.Implementations/Localization/Ratings/ru.csv +++ b/Emby.Server.Implementations/Localization/Ratings/ru.csv @@ -1,5 +1,6 @@ -RU-0+,1 -RU-6+,3 -RU-12+,7 -RU-16+,9 -RU-18+,10 +0+,0 +6+,6 +12+,12 +16+,16 +18+,18 +Refused classification,1001 diff --git a/Emby.Server.Implementations/Localization/Ratings/se.csv b/Emby.Server.Implementations/Localization/Ratings/se.csv index 1443c07df7..e129c35617 100644 --- a/Emby.Server.Implementations/Localization/Ratings/se.csv +++ b/Emby.Server.Implementations/Localization/Ratings/se.csv @@ -1,5 +1,10 @@ -SE-Btl,1 -SE-Barntillåten,1 -SE-7,3 -SE-11,5 -SE-15,8 +Alla,0 +Barntillåten,0 +Btl,0 +0+,0 +7,7 +9+,9 +10+,10 +11,11 +14,14 +15,15 diff --git a/Emby.Server.Implementations/Localization/Ratings/uk.csv b/Emby.Server.Implementations/Localization/Ratings/uk.csv index 6c8005b3f3..75b1c20589 100644 --- a/Emby.Server.Implementations/Localization/Ratings/uk.csv +++ b/Emby.Server.Implementations/Localization/Ratings/uk.csv @@ -1,7 +1,22 @@ -UK-U,1 -UK-PG,5 -UK-12,7 -UK-12A,7 -UK-15,9 -UK-18,10 -UK-R18,15 +All,0 +E,0 +G,0 +U,0 +0+,0 +6+,6 +7+,7 +PG,8 +9+,9 +12,12 +12+,12 +12A,12 +Teen,13 +13+,13 +14+,14 +15,15 +16,16 +Caution,18 +18,18 +Mature,1000 +Adult,1000 +R18,1000 diff --git a/Emby.Server.Implementations/Localization/Ratings/us.csv b/Emby.Server.Implementations/Localization/Ratings/us.csv index 34c897fe3f..d103ddf42d 100644 --- a/Emby.Server.Implementations/Localization/Ratings/us.csv +++ b/Emby.Server.Implementations/Localization/Ratings/us.csv @@ -1,23 +1,50 @@ -TV-Y,1 -APPROVED,1 -G,1 -E,1 -EC,1 -TV-G,1 -TV-Y7,3 -TV-Y7-FV,4 -PG,5 -TV-PG,5 -PG-13,7 -T,7 -TV-14,8 -R,9 -M,9 -TV-MA,9 -NC-17,10 -AO,15 -RP,15 -UR,15 -NR,15 -X,15 -XXX,100 +Approved,0 +G,0 +TV-G,0 +TV-Y,0 +TV-Y7,7 +TV-Y7-FV,7 +PG,10 +PG-13,13 +TV-PG,13 +TV-PG-D,13 +TV-PG-L,13 +TV-PG-S,13 +TV-PG-V,13 +TV-PG-DL,13 +TV-PG-DS,13 +TV-PG-DV,13 +TV-PG-LS,13 +TV-PG-LV,13 +TV-PG-SV,13 +TV-PG-DLS,13 +TV-PG-DLV,13 +TV-PG-DSV,13 +TV-PG-LSV,13 +TV-PG-DLSV,13 +TV-14,14 +TV-14-D,14 +TV-14-L,14 +TV-14-S,14 +TV-14-V,14 +TV-14-DL,14 +TV-14-DS,14 +TV-14-DV,14 +TV-14-LS,14 +TV-14-LV,14 +TV-14-SV,14 +TV-14-DLS,14 +TV-14-DLV,14 +TV-14-DSV,14 +TV-14-LSV,14 +TV-14-DLSV,14 +NC-17,17 +R,17 +TV-MA,17 +TV-MA-L,17 +TV-MA-S,17 +TV-MA-V,17 +TV-MA-LS,17 +TV-MA-LV,17 +TV-MA-SV,17 +TV-MA-LSV,17 diff --git a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs index 16eb7a75c6..256bde8194 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs @@ -83,11 +83,11 @@ namespace Jellyfin.Server.Implementations.Tests.Localization await localizationManager.LoadAll(); var ratings = localizationManager.GetParentalRatings().ToList(); - Assert.Equal(23, ratings.Count); + Assert.Equal(50, ratings.Count); var tvma = ratings.FirstOrDefault(x => x.Name.Equals("TV-MA", StringComparison.Ordinal)); Assert.NotNull(tvma); - Assert.Equal(9, tvma!.Value); + Assert.Equal(17, tvma!.Value); } [Fact] @@ -100,21 +100,21 @@ namespace Jellyfin.Server.Implementations.Tests.Localization await localizationManager.LoadAll(); var ratings = localizationManager.GetParentalRatings().ToList(); - Assert.Equal(10, ratings.Count); + Assert.Equal(12, ratings.Count); var fsk = ratings.FirstOrDefault(x => x.Name.Equals("FSK-12", StringComparison.Ordinal)); Assert.NotNull(fsk); - Assert.Equal(7, fsk!.Value); + Assert.Equal(12, fsk!.Value); } [Theory] - [InlineData("CA-R", "CA", 10)] - [InlineData("FSK-16", "DE", 8)] - [InlineData("FSK-18", "DE", 9)] - [InlineData("FSK-18", "US", 9)] - [InlineData("TV-MA", "US", 9)] - [InlineData("XXX", "asdf", 100)] - [InlineData("Germany: FSK-18", "DE", 9)] + [InlineData("CA-R", "CA", 18)] + [InlineData("FSK-16", "DE", 16)] + [InlineData("FSK-18", "DE", 18)] + [InlineData("FSK-18", "US", 18)] + [InlineData("TV-MA", "US", 17)] + [InlineData("XXX", "asdf", 1000)] + [InlineData("Germany: FSK-18", "DE", 18)] public async Task GetRatingLevel_GivenValidString_Success(string value, string countryCode, int expectedLevel) { var localizationManager = Setup(new ServerConfiguration() @@ -135,6 +135,9 @@ namespace Jellyfin.Server.Implementations.Tests.Localization UICulture = "de-DE" }); await localizationManager.LoadAll(); + Assert.Null(localizationManager.GetRatingLevel("NR")); + Assert.Null(localizationManager.GetRatingLevel("unrated")); + Assert.Null(localizationManager.GetRatingLevel("Not Rated")); Assert.Null(localizationManager.GetRatingLevel("n/a")); } -- cgit v1.2.3 From 2e315b7f085ca0b9a2f61a7ac71a46ca84750f9f Mon Sep 17 00:00:00 2001 From: Shadowghost <Ghost_of_Stone@web.de> Date: Mon, 28 Nov 2022 13:07:57 +0100 Subject: Properly build where clause for rating checks --- .../Data/SqliteItemRepository.cs | 133 +++++++++++++++------ .../Localization/LocalizationManager.cs | 2 +- Jellyfin.Api/Controllers/ItemUpdateController.cs | 2 +- Jellyfin.Api/Controllers/ItemsController.cs | 7 ++ 4 files changed, 107 insertions(+), 37 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 90f03995e8..797990d296 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -4020,34 +4020,116 @@ namespace Emby.Server.Implementations.Data whereClauses.Add(clause); } - if (query.MinParentalRating.HasValue) + var ratingClause = "("; + if (query.HasParentalRating.HasValue && query.HasParentalRating.Value) { - whereClauses.Add("InheritedParentalRatingValue>=@MinParentalRating"); - if (statement is not null) + ratingClause += "InheritedParentalRatingValue not null"; + if (query.MinParentalRating.HasValue) { - statement.TryBind("@MinParentalRating", query.MinParentalRating.Value); + ratingClause += " AND InheritedParentalRatingValue >= @MinParentalRating"; + if (statement is not null) + { + statement.TryBind("@MinParentalRating", query.MinParentalRating.Value); + } } - } - if (query.MaxParentalRating.HasValue) - { - whereClauses.Add("InheritedParentalRatingValue<=@MaxParentalRating"); - if (statement is not null) + if (query.MaxParentalRating.HasValue) { - statement.TryBind("@MaxParentalRating", query.MaxParentalRating.Value); + ratingClause += " AND InheritedParentalRatingValue <= @MaxParentalRating"; + if (statement is not null) + { + statement.TryBind("@MaxParentalRating", query.MaxParentalRating.Value); + } } } + else if (query.BlockUnratedItems.Length > 0) + { + var paramName = "@UnratedType"; + var index = 0; + string blockedUnratedItems = string.Join(',', query.BlockUnratedItems.Select(_ => paramName + index++)); + ratingClause += "(InheritedParentalRatingValue is null AND UnratedType not in (" + blockedUnratedItems + "))"; + + if (statement != null) + { + for (var ind = 0; ind < query.BlockUnratedItems.Length; ind++) + { + statement.TryBind(paramName + ind, query.BlockUnratedItems[ind].ToString()); + } + } + + if (query.MinParentalRating.HasValue || query.MaxParentalRating.HasValue) + { + ratingClause += " OR ("; + } - if (query.HasParentalRating.HasValue) + if (query.MinParentalRating.HasValue) + { + ratingClause += "InheritedParentalRatingValue >= @MinParentalRating"; + if (statement != null) + { + statement.TryBind("@MinParentalRating", query.MinParentalRating.Value); + } + } + + if (query.MaxParentalRating.HasValue) + { + if (query.MinParentalRating.HasValue) + { + ratingClause += " AND "; + } + + ratingClause += "InheritedParentalRatingValue <= @MaxParentalRating"; + if (statement != null) + { + statement.TryBind("@MaxParentalRating", query.MaxParentalRating.Value); + } + } + + if (query.MinParentalRating.HasValue || query.MaxParentalRating.HasValue) + { + ratingClause += ")"; + } + + if (!(query.MinParentalRating.HasValue || query.MaxParentalRating.HasValue)) + { + ratingClause += " OR InheritedParentalRatingValue not null"; + } + } + else if (query.MinParentalRating.HasValue) { - if (query.HasParentalRating.Value) + ratingClause += "InheritedParentalRatingValue is null OR (InheritedParentalRatingValue >= @MinParentalRating"; + if (statement != null) { - whereClauses.Add("InheritedParentalRatingValue > 0"); + statement.TryBind("@MinParentalRating", query.MinParentalRating.Value); } - else + + if (query.MaxParentalRating.HasValue) { - whereClauses.Add("InheritedParentalRatingValue = 0"); + ratingClause += " AND InheritedParentalRatingValue <= @MaxParentalRating"; + if (statement != null) + { + statement.TryBind("@MaxParentalRating", query.MaxParentalRating.Value); + } } + + ratingClause += ")"; + } + else if (query.MaxParentalRating.HasValue) + { + ratingClause += "InheritedParentalRatingValue is null OR InheritedParentalRatingValue <= @MaxParentalRating"; + if (statement != null) + { + statement.TryBind("@MaxParentalRating", query.MaxParentalRating.Value); + } + } + else if (query.HasParentalRating.HasValue && !query.HasParentalRating.Value) + { + ratingClause += "InheritedParentalRatingValue is null"; + } + + if (!string.Equals(ratingClause, "(", StringComparison.OrdinalIgnoreCase)) + { + whereClauses.Add(ratingClause + ")"); } if (query.HasOfficialRating.HasValue) @@ -4312,7 +4394,7 @@ namespace Emby.Server.Implementations.Data } // TODO this seems to be an idea for a better schema where ProviderIds are their own table - // buut this is not implemented + // but this is not implemented // hasProviderIds.Add("(COALESCE((select value from ProviderIds where ItemId=Guid and Name = '" + pair.Key + "'), '') <> " + paramName + ")"); // TODO this is a really BAD way to do it since the pair: @@ -4440,25 +4522,6 @@ namespace Emby.Server.Implementations.Data } } - if (query.BlockUnratedItems.Length == 1) - { - whereClauses.Add("(InheritedParentalRatingValue > 0 or UnratedType <> @UnratedType)"); - if (statement is not null) - { - statement.TryBind("@UnratedType", query.BlockUnratedItems[0].ToString()); - } - } - - if (query.BlockUnratedItems.Length > 1) - { - var inClause = string.Join(',', query.BlockUnratedItems.Select(i => "'" + i.ToString() + "'")); - whereClauses.Add( - string.Format( - CultureInfo.InvariantCulture, - "(InheritedParentalRatingValue > 0 or UnratedType not in ({0}))", - inClause)); - } - if (query.ExcludeInheritedTags.Length > 0) { var paramName = "@ExcludeInheritedTags"; diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs index 5e22e78867..0e19e80a88 100644 --- a/Emby.Server.Implementations/Localization/LocalizationManager.cs +++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs @@ -273,7 +273,7 @@ namespace Emby.Server.Implementations.Localization } } - // TODO: Further improve by normalizing out all spaces and dashes + // TODO: Further improve when necessary return null; } diff --git a/Jellyfin.Api/Controllers/ItemUpdateController.cs b/Jellyfin.Api/Controllers/ItemUpdateController.cs index 57d446dbd0..9c71482413 100644 --- a/Jellyfin.Api/Controllers/ItemUpdateController.cs +++ b/Jellyfin.Api/Controllers/ItemUpdateController.cs @@ -147,7 +147,7 @@ public class ItemUpdateController : BaseJellyfinApiController var info = new MetadataEditorInfo { - ParentalRatingOptions = _localizationManager.GetParentalRatings().ToArray(), + ParentalRatingOptions = _localizationManager.GetParentalRatings().ToList(), ExternalIdInfos = _providerManager.GetExternalIdInfos(item).ToArray(), Countries = _localizationManager.GetCountries().ToArray(), Cultures = _localizationManager.GetCultures().ToArray() diff --git a/Jellyfin.Api/Controllers/ItemsController.cs b/Jellyfin.Api/Controllers/ItemsController.cs index 728e628109..377526729a 100644 --- a/Jellyfin.Api/Controllers/ItemsController.cs +++ b/Jellyfin.Api/Controllers/ItemsController.cs @@ -411,6 +411,13 @@ public class ItemsController : BaseJellyfinApiController query.SeriesStatuses = seriesStatus; } + // Exclude Blocked Unrated Items + var blockedUnratedItems = user?.GetPreferenceValues<UnratedItem>(PreferenceKind.BlockUnratedItems); + if (blockedUnratedItems is not null) + { + query.BlockUnratedItems = blockedUnratedItems; + } + // ExcludeLocationTypes if (excludeLocationTypes.Any(t => t == LocationType.Virtual)) { -- cgit v1.2.3 From 9d21f078c71373d956893d2d298cc4d7fcc0adf1 Mon Sep 17 00:00:00 2001 From: Shadowghost <Ghost_of_Stone@web.de> Date: Tue, 29 Nov 2022 22:04:28 +0100 Subject: Add default rating selections --- .../Localization/LocalizationManager.cs | 59 +++++++++++++++++++--- MediaBrowser.Model/Entities/ParentalRating.cs | 4 +- .../Localization/LocalizationManagerTests.cs | 4 +- 3 files changed, 57 insertions(+), 10 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs index 0e19e80a88..ca7c2c21f1 100644 --- a/Emby.Server.Implementations/Localization/LocalizationManager.cs +++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs @@ -87,12 +87,10 @@ namespace Emby.Server.Implementations.Localization var name = parts[0]; dict.Add(name, new ParentalRating(name, value)); } -#if DEBUG else { _logger.LogWarning("Malformed line in ratings file for country {CountryCode}", countryCode); } -#endif } _allParentalRatings[countryCode] = dict; @@ -185,7 +183,56 @@ namespace Emby.Server.Implementations.Localization /// <inheritdoc /> public IEnumerable<ParentalRating> GetParentalRatings() - => GetParentalRatingsDictionary().Values; + { + var ratings = GetParentalRatingsDictionary().Values.ToList(); + + // Add common ratings to ensure them being available for selection. + // Based on the US rating system due to it being the main source of rating in the metadata providers + // Minimum rating possible + if (!ratings.Any(x => x.Value == 0)) + { + ratings.Add(new ParentalRating("Approved", 0)); + } + + // Matches PG (this has differnet age restrictions depending on country) + if (!ratings.Any(x => x.Value == 10)) + { + ratings.Add(new ParentalRating("10", 10)); + } + + // Matches PG-13 + if (!ratings.Any(x => x.Value == 13)) + { + ratings.Add(new ParentalRating("13", 13)); + } + + // Matches TV-14 + if (!ratings.Any(x => x.Value == 14)) + { + ratings.Add(new ParentalRating("14", 14)); + } + + // Catchall if max rating of country is less than 21 + // Using 21 instead of 18 to be sure to allow access to all rated content except adult and banned + if (!ratings.Any(x => x.Value >= 21)) + { + ratings.Add(new ParentalRating("21", 21)); + } + + // A lot of countries don't excplicitly have a seperate rating for adult content + if (!ratings.Any(x => x.Value == 1000)) + { + ratings.Add(new ParentalRating("XXX", 1000)); + } + + // A lot of countries don't excplicitly have a seperate rating for banned content + if (!ratings.Any(x => x.Value == 1001)) + { + ratings.Add(new ParentalRating("Banned", 1001)); + } + + return ratings.OrderBy(r => r.Value); + } /// <summary> /// Gets the parental ratings dictionary. @@ -207,15 +254,15 @@ namespace Emby.Server.Implementations.Localization } /// <summary> - /// Gets the ratings. + /// Gets the ratings for a country. /// </summary> /// <param name="countryCode">The country code.</param> /// <returns>The ratings.</returns> private Dictionary<string, ParentalRating>? GetRatings(string countryCode) { - _allParentalRatings.TryGetValue(countryCode, out var value); + _allParentalRatings.TryGetValue(countryCode, out var countryValue); - return value; + return countryValue; } /// <inheritdoc /> diff --git a/MediaBrowser.Model/Entities/ParentalRating.cs b/MediaBrowser.Model/Entities/ParentalRating.cs index 17b2868a31..c92640818c 100644 --- a/MediaBrowser.Model/Entities/ParentalRating.cs +++ b/MediaBrowser.Model/Entities/ParentalRating.cs @@ -12,7 +12,7 @@ namespace MediaBrowser.Model.Entities { } - public ParentalRating(string name, int value) + public ParentalRating(string name, int? value) { Name = name; Value = value; @@ -28,6 +28,6 @@ namespace MediaBrowser.Model.Entities /// Gets or sets the value. /// </summary> /// <value>The value.</value> - public int Value { get; set; } + public int? Value { get; set; } } } diff --git a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs index 256bde8194..ab3682ccf6 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs @@ -83,7 +83,7 @@ namespace Jellyfin.Server.Implementations.Tests.Localization await localizationManager.LoadAll(); var ratings = localizationManager.GetParentalRatings().ToList(); - Assert.Equal(50, ratings.Count); + Assert.Equal(53, ratings.Count); var tvma = ratings.FirstOrDefault(x => x.Name.Equals("TV-MA", StringComparison.Ordinal)); Assert.NotNull(tvma); @@ -100,7 +100,7 @@ namespace Jellyfin.Server.Implementations.Tests.Localization await localizationManager.LoadAll(); var ratings = localizationManager.GetParentalRatings().ToList(); - Assert.Equal(12, ratings.Count); + Assert.Equal(18, ratings.Count); var fsk = ratings.FirstOrDefault(x => x.Name.Equals("FSK-12", StringComparison.Ordinal)); Assert.NotNull(fsk); -- cgit v1.2.3 From 07dc163844091e3335081578ed5cc7fd427c5c0d Mon Sep 17 00:00:00 2001 From: Shadowghost <Ghost_of_Stone@web.de> Date: Wed, 30 Nov 2022 11:55:40 +0100 Subject: Fix playlist parental control and no parental control skipping forbidden unrated items --- Emby.Server.Implementations/Dto/DtoService.cs | 7 ++++--- MediaBrowser.Controller/Entities/BaseItem.cs | 15 +++++---------- 2 files changed, 9 insertions(+), 13 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index 5103b1fbfb..e928f1ff3a 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -83,13 +83,14 @@ namespace Emby.Server.Implementations.Dto /// <inheritdoc /> public IReadOnlyList<BaseItemDto> GetBaseItemDtos(IReadOnlyList<BaseItem> items, DtoOptions options, User user = null, BaseItem owner = null) { - var returnItems = new BaseItemDto[items.Count]; + var accessibleItems = user is null ? items : items.Where(x => x.IsVisible(user)).ToList(); + var returnItems = new BaseItemDto[accessibleItems.Count]; var programTuples = new List<(BaseItem, BaseItemDto)>(); var channelTuples = new List<(BaseItemDto, LiveTvChannel)>(); - for (int index = 0; index < items.Count; index++) + for (int index = 0; index < accessibleItems.Count; index++) { - var item = items[index]; + var item = accessibleItems[index]; var dto = GetBaseItemDtoInternal(item, options, user, owner); if (item is LiveTvChannel tvChannel) diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index cf369e84b2..b8601cccd9 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -1534,12 +1534,6 @@ namespace MediaBrowser.Controller.Entities } var maxAllowedRating = user.MaxParentalAgeRating; - - if (maxAllowedRating is null) - { - return true; - } - var rating = CustomRatingForComparison; if (string.IsNullOrEmpty(rating)) @@ -1549,12 +1543,13 @@ namespace MediaBrowser.Controller.Entities if (string.IsNullOrEmpty(rating)) { + Logger.LogDebug("{0} has no parental rating set.", Name); return !GetBlockUnratedValue(user); } var value = LocalizationManager.GetRatingLevel(rating); - // Could not determine the integer value + // Could not determine rating level if (!value.HasValue) { var isAllowed = !GetBlockUnratedValue(user); @@ -1567,7 +1562,7 @@ namespace MediaBrowser.Controller.Entities return isAllowed; } - return value.Value <= maxAllowedRating.Value; + return !maxAllowedRating.HasValue || value.Value <= maxAllowedRating.Value; } public int? GetInheritedParentalRatingValue() @@ -1627,10 +1622,10 @@ namespace MediaBrowser.Controller.Entities } /// <summary> - /// Gets the block unrated value. + /// Gets a bool indicating if access to the unrated item is blocked or not. /// </summary> /// <param name="user">The configuration.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns> + /// <returns><c>true</c> if blocked, <c>false</c> otherwise.</returns> protected virtual bool GetBlockUnratedValue(User user) { // Don't block plain folders that are unrated. Let the media underneath get blocked -- cgit v1.2.3 From 15efb9935c2f786c12343f158ef2c9db542e71ba Mon Sep 17 00:00:00 2001 From: Shadowghost <Ghost_of_Stone@web.de> Date: Thu, 8 Dec 2022 16:15:26 +0100 Subject: Fix typo and migration description --- Emby.Server.Implementations/Localization/LocalizationManager.cs | 2 +- Jellyfin.Server/Migrations/Routines/MigrateRatingLevels.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs index ca7c2c21f1..8faebadd61 100644 --- a/Emby.Server.Implementations/Localization/LocalizationManager.cs +++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs @@ -194,7 +194,7 @@ namespace Emby.Server.Implementations.Localization ratings.Add(new ParentalRating("Approved", 0)); } - // Matches PG (this has differnet age restrictions depending on country) + // Matches PG (this has different age restrictions depending on country) if (!ratings.Any(x => x.Value == 10)) { ratings.Add(new ParentalRating("10", 10)); diff --git a/Jellyfin.Server/Migrations/Routines/MigrateRatingLevels.cs b/Jellyfin.Server/Migrations/Routines/MigrateRatingLevels.cs index 5b27681f61..21f1872bc5 100644 --- a/Jellyfin.Server/Migrations/Routines/MigrateRatingLevels.cs +++ b/Jellyfin.Server/Migrations/Routines/MigrateRatingLevels.cs @@ -9,7 +9,7 @@ using SQLitePCL.pretty; namespace Jellyfin.Server.Migrations.Routines { /// <summary> - /// Remove duplicate entries which were caused by a bug where a file was considered to be an "Extra" to itself. + /// Migrate rating levels to new rating level system. /// </summary> internal class MigrateRatingLevels : IMigrationRoutine { -- cgit v1.2.3 From 5cdb0c7932303d2d9a59d7d21860172fd97fe031 Mon Sep 17 00:00:00 2001 From: Shadowghost <Ghost_of_Stone@web.de> Date: Fri, 27 Jan 2023 15:49:08 +0100 Subject: Apply review suggestions --- .../Data/SqliteItemRepository.cs | 208 ++++----------------- .../Localization/LocalizationManager.cs | 21 +-- .../Localization/Ratings/be.csv | 2 +- .../Migrations/Routines/MigrateRatingLevels.cs | 2 +- 4 files changed, 46 insertions(+), 187 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 797990d296..055131c8e6 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -3202,7 +3202,8 @@ namespace Emby.Server.Implementations.Data return IsAlphaNumeric(value); } - private List<string> GetWhereClauses(InternalItemsQuery query, IStatement statement) +#nullable enable + private List<string> GetWhereClauses(InternalItemsQuery query, IStatement? statement) { if (query.IsResumable ?? false) { @@ -3622,12 +3623,8 @@ namespace Emby.Server.Implementations.Data clauseBuilder.Append("(guid in (select itemid from People where Name = (select Name from TypedBaseItems where guid=") .Append(paramName) .Append("))) OR "); - - if (statement is not null) - { - query.PersonIds[i].TryWriteBytes(idBytes); - statement.TryBind(paramName, idBytes); - } + query.PersonIds[i].TryWriteBytes(idBytes); + statement?.TryBind(paramName, idBytes); } // Remove last " OR " @@ -3677,7 +3674,6 @@ namespace Emby.Server.Implementations.Data if (statement is not null) { nameContains = FixUnicodeChars(nameContains); - statement.TryBind("@NameContains", "%" + GetCleanValue(nameContains) + "%"); } } @@ -3803,13 +3799,8 @@ namespace Emby.Server.Implementations.Data foreach (var artistId in query.ArtistIds) { var paramName = "@ArtistIds" + index; - clauses.Add("(guid in (select itemid from ItemValues where CleanValue = (select CleanName from TypedBaseItems where guid=" + paramName + ") and Type<=1))"); - if (statement is not null) - { - statement.TryBind(paramName, artistId); - } - + statement?.TryBind(paramName, artistId); index++; } @@ -3824,13 +3815,8 @@ namespace Emby.Server.Implementations.Data foreach (var artistId in query.AlbumArtistIds) { var paramName = "@ArtistIds" + index; - clauses.Add("(guid in (select itemid from ItemValues where CleanValue = (select CleanName from TypedBaseItems where guid=" + paramName + ") and Type=1))"); - if (statement is not null) - { - statement.TryBind(paramName, artistId); - } - + statement?.TryBind(paramName, artistId); index++; } @@ -3845,13 +3831,8 @@ namespace Emby.Server.Implementations.Data foreach (var artistId in query.ContributingArtistIds) { var paramName = "@ArtistIds" + index; - clauses.Add("((select CleanName from TypedBaseItems where guid=" + paramName + ") in (select CleanValue from ItemValues where ItemId=Guid and Type=0) AND (select CleanName from TypedBaseItems where guid=" + paramName + ") not in (select CleanValue from ItemValues where ItemId=Guid and Type=1))"); - if (statement is not null) - { - statement.TryBind(paramName, artistId); - } - + statement?.TryBind(paramName, artistId); index++; } @@ -3866,13 +3847,8 @@ namespace Emby.Server.Implementations.Data foreach (var albumId in query.AlbumIds) { var paramName = "@AlbumIds" + index; - clauses.Add("Album in (select Name from typedbaseitems where guid=" + paramName + ")"); - if (statement is not null) - { - statement.TryBind(paramName, albumId); - } - + statement?.TryBind(paramName, albumId); index++; } @@ -3887,13 +3863,8 @@ namespace Emby.Server.Implementations.Data foreach (var artistId in query.ExcludeArtistIds) { var paramName = "@ExcludeArtistId" + index; - clauses.Add("(guid not in (select itemid from ItemValues where CleanValue = (select CleanName from TypedBaseItems where guid=" + paramName + ") and Type<=1))"); - if (statement is not null) - { - statement.TryBind(paramName, artistId); - } - + statement?.TryBind(paramName, artistId); index++; } @@ -3908,13 +3879,8 @@ namespace Emby.Server.Implementations.Data foreach (var genreId in query.GenreIds) { var paramName = "@GenreId" + index; - clauses.Add("(guid in (select itemid from ItemValues where CleanValue = (select CleanName from TypedBaseItems where guid=" + paramName + ") and Type=2))"); - if (statement is not null) - { - statement.TryBind(paramName, genreId); - } - + statement?.TryBind(paramName, genreId); index++; } @@ -3929,11 +3895,7 @@ namespace Emby.Server.Implementations.Data foreach (var item in query.Genres) { clauses.Add("@Genre" + index + " in (select CleanValue from ItemValues where ItemId=Guid and Type=2)"); - if (statement is not null) - { - statement.TryBind("@Genre" + index, GetCleanValue(item)); - } - + statement?.TryBind("@Genre" + index, GetCleanValue(item)); index++; } @@ -3948,11 +3910,7 @@ namespace Emby.Server.Implementations.Data foreach (var item in tags) { clauses.Add("@Tag" + index + " in (select CleanValue from ItemValues where ItemId=Guid and Type=4)"); - if (statement is not null) - { - statement.TryBind("@Tag" + index, GetCleanValue(item)); - } - + statement?.TryBind("@Tag" + index, GetCleanValue(item)); index++; } @@ -3967,11 +3925,7 @@ namespace Emby.Server.Implementations.Data foreach (var item in excludeTags) { clauses.Add("@ExcludeTag" + index + " not in (select CleanValue from ItemValues where ItemId=Guid and Type=4)"); - if (statement is not null) - { - statement.TryBind("@ExcludeTag" + index, GetCleanValue(item)); - } - + statement?.TryBind("@ExcludeTag" + index, GetCleanValue(item)); index++; } @@ -3986,14 +3940,8 @@ namespace Emby.Server.Implementations.Data foreach (var studioId in query.StudioIds) { var paramName = "@StudioId" + index; - clauses.Add("(guid in (select itemid from ItemValues where CleanValue = (select CleanName from TypedBaseItems where guid=" + paramName + ") and Type=3))"); - - if (statement is not null) - { - statement.TryBind(paramName, studioId); - } - + statement?.TryBind(paramName, studioId); index++; } @@ -4008,11 +3956,7 @@ namespace Emby.Server.Implementations.Data foreach (var item in query.OfficialRatings) { clauses.Add("OfficialRating=@OfficialRating" + index); - if (statement is not null) - { - statement.TryBind("@OfficialRating" + index, item); - } - + statement?.TryBind("@OfficialRating" + index, item); index++; } @@ -4021,25 +3965,19 @@ namespace Emby.Server.Implementations.Data } var ratingClause = "("; - if (query.HasParentalRating.HasValue && query.HasParentalRating.Value) + if (query.HasParentalRating ?? false) { ratingClause += "InheritedParentalRatingValue not null"; if (query.MinParentalRating.HasValue) { ratingClause += " AND InheritedParentalRatingValue >= @MinParentalRating"; - if (statement is not null) - { - statement.TryBind("@MinParentalRating", query.MinParentalRating.Value); - } + statement?.TryBind("@MinParentalRating", query.MinParentalRating.Value); } if (query.MaxParentalRating.HasValue) { ratingClause += " AND InheritedParentalRatingValue <= @MaxParentalRating"; - if (statement is not null) - { - statement.TryBind("@MaxParentalRating", query.MaxParentalRating.Value); - } + statement?.TryBind("@MaxParentalRating", query.MaxParentalRating.Value); } } else if (query.BlockUnratedItems.Length > 0) @@ -4049,7 +3987,7 @@ namespace Emby.Server.Implementations.Data string blockedUnratedItems = string.Join(',', query.BlockUnratedItems.Select(_ => paramName + index++)); ratingClause += "(InheritedParentalRatingValue is null AND UnratedType not in (" + blockedUnratedItems + "))"; - if (statement != null) + if (statement is not null) { for (var ind = 0; ind < query.BlockUnratedItems.Length; ind++) { @@ -4065,10 +4003,7 @@ namespace Emby.Server.Implementations.Data if (query.MinParentalRating.HasValue) { ratingClause += "InheritedParentalRatingValue >= @MinParentalRating"; - if (statement != null) - { - statement.TryBind("@MinParentalRating", query.MinParentalRating.Value); - } + statement?.TryBind("@MinParentalRating", query.MinParentalRating.Value); } if (query.MaxParentalRating.HasValue) @@ -4079,10 +4014,7 @@ namespace Emby.Server.Implementations.Data } ratingClause += "InheritedParentalRatingValue <= @MaxParentalRating"; - if (statement != null) - { - statement.TryBind("@MaxParentalRating", query.MaxParentalRating.Value); - } + statement?.TryBind("@MaxParentalRating", query.MaxParentalRating.Value); } if (query.MinParentalRating.HasValue || query.MaxParentalRating.HasValue) @@ -4098,18 +4030,12 @@ namespace Emby.Server.Implementations.Data else if (query.MinParentalRating.HasValue) { ratingClause += "InheritedParentalRatingValue is null OR (InheritedParentalRatingValue >= @MinParentalRating"; - if (statement != null) - { - statement.TryBind("@MinParentalRating", query.MinParentalRating.Value); - } + statement?.TryBind("@MinParentalRating", query.MinParentalRating.Value); if (query.MaxParentalRating.HasValue) { ratingClause += " AND InheritedParentalRatingValue <= @MaxParentalRating"; - if (statement != null) - { - statement.TryBind("@MaxParentalRating", query.MaxParentalRating.Value); - } + statement?.TryBind("@MaxParentalRating", query.MaxParentalRating.Value); } ratingClause += ")"; @@ -4117,12 +4043,9 @@ namespace Emby.Server.Implementations.Data else if (query.MaxParentalRating.HasValue) { ratingClause += "InheritedParentalRatingValue is null OR InheritedParentalRatingValue <= @MaxParentalRating"; - if (statement != null) - { - statement.TryBind("@MaxParentalRating", query.MaxParentalRating.Value); - } + statement?.TryBind("@MaxParentalRating", query.MaxParentalRating.Value); } - else if (query.HasParentalRating.HasValue && !query.HasParentalRating.Value) + else if (!query.HasParentalRating ?? false) { ratingClause += "InheritedParentalRatingValue is null"; } @@ -4171,37 +4094,25 @@ namespace Emby.Server.Implementations.Data if (!string.IsNullOrWhiteSpace(query.HasNoAudioTrackWithLanguage)) { whereClauses.Add("((select language from MediaStreams where MediaStreams.ItemId=A.Guid and MediaStreams.StreamType='Audio' and MediaStreams.Language=@HasNoAudioTrackWithLanguage limit 1) is null)"); - if (statement is not null) - { - statement.TryBind("@HasNoAudioTrackWithLanguage", query.HasNoAudioTrackWithLanguage); - } + statement?.TryBind("@HasNoAudioTrackWithLanguage", query.HasNoAudioTrackWithLanguage); } if (!string.IsNullOrWhiteSpace(query.HasNoInternalSubtitleTrackWithLanguage)) { whereClauses.Add("((select language from MediaStreams where MediaStreams.ItemId=A.Guid and MediaStreams.StreamType='Subtitle' and MediaStreams.IsExternal=0 and MediaStreams.Language=@HasNoInternalSubtitleTrackWithLanguage limit 1) is null)"); - if (statement is not null) - { - statement.TryBind("@HasNoInternalSubtitleTrackWithLanguage", query.HasNoInternalSubtitleTrackWithLanguage); - } + statement?.TryBind("@HasNoInternalSubtitleTrackWithLanguage", query.HasNoInternalSubtitleTrackWithLanguage); } if (!string.IsNullOrWhiteSpace(query.HasNoExternalSubtitleTrackWithLanguage)) { whereClauses.Add("((select language from MediaStreams where MediaStreams.ItemId=A.Guid and MediaStreams.StreamType='Subtitle' and MediaStreams.IsExternal=1 and MediaStreams.Language=@HasNoExternalSubtitleTrackWithLanguage limit 1) is null)"); - if (statement is not null) - { - statement.TryBind("@HasNoExternalSubtitleTrackWithLanguage", query.HasNoExternalSubtitleTrackWithLanguage); - } + statement?.TryBind("@HasNoExternalSubtitleTrackWithLanguage", query.HasNoExternalSubtitleTrackWithLanguage); } if (!string.IsNullOrWhiteSpace(query.HasNoSubtitleTrackWithLanguage)) { whereClauses.Add("((select language from MediaStreams where MediaStreams.ItemId=A.Guid and MediaStreams.StreamType='Subtitle' and MediaStreams.Language=@HasNoSubtitleTrackWithLanguage limit 1) is null)"); - if (statement is not null) - { - statement.TryBind("@HasNoSubtitleTrackWithLanguage", query.HasNoSubtitleTrackWithLanguage); - } + statement?.TryBind("@HasNoSubtitleTrackWithLanguage", query.HasNoSubtitleTrackWithLanguage); } if (query.HasSubtitles.HasValue) @@ -4251,15 +4162,11 @@ namespace Emby.Server.Implementations.Data if (query.Years.Length == 1) { whereClauses.Add("ProductionYear=@Years"); - if (statement is not null) - { - statement.TryBind("@Years", query.Years[0].ToString(CultureInfo.InvariantCulture)); - } + statement?.TryBind("@Years", query.Years[0].ToString(CultureInfo.InvariantCulture)); } else if (query.Years.Length > 1) { var val = string.Join(',', query.Years); - whereClauses.Add("ProductionYear in (" + val + ")"); } @@ -4267,10 +4174,7 @@ namespace Emby.Server.Implementations.Data if (isVirtualItem.HasValue) { whereClauses.Add("IsVirtualItem=@IsVirtualItem"); - if (statement is not null) - { - statement.TryBind("@IsVirtualItem", isVirtualItem.Value); - } + statement?.TryBind("@IsVirtualItem", isVirtualItem.Value); } if (query.IsSpecialSeason.HasValue) @@ -4301,31 +4205,22 @@ namespace Emby.Server.Implementations.Data if (queryMediaTypes.Length == 1) { whereClauses.Add("MediaType=@MediaTypes"); - if (statement is not null) - { - statement.TryBind("@MediaTypes", queryMediaTypes[0]); - } + statement?.TryBind("@MediaTypes", queryMediaTypes[0]); } else if (queryMediaTypes.Length > 1) { var val = string.Join(',', queryMediaTypes.Select(i => "'" + i + "'")); - whereClauses.Add("MediaType in (" + val + ")"); } if (query.ItemIds.Length > 0) { var includeIds = new List<string>(); - var index = 0; foreach (var id in query.ItemIds) { includeIds.Add("Guid = @IncludeId" + index); - if (statement is not null) - { - statement.TryBind("@IncludeId" + index, id); - } - + statement?.TryBind("@IncludeId" + index, id); index++; } @@ -4335,16 +4230,11 @@ namespace Emby.Server.Implementations.Data if (query.ExcludeItemIds.Length > 0) { var excludeIds = new List<string>(); - var index = 0; foreach (var id in query.ExcludeItemIds) { excludeIds.Add("Guid <> @ExcludeId" + index); - if (statement is not null) - { - statement.TryBind("@ExcludeId" + index, id); - } - + statement?.TryBind("@ExcludeId" + index, id); index++; } @@ -4365,11 +4255,7 @@ namespace Emby.Server.Implementations.Data var paramName = "@ExcludeProviderId" + index; excludeIds.Add("(ProviderIds is null or ProviderIds not like " + paramName + ")"); - if (statement is not null) - { - statement.TryBind(paramName, "%" + pair.Key + "=" + pair.Value + "%"); - } - + statement?.TryBind(paramName, "%" + pair.Key + "=" + pair.Value + "%"); index++; break; @@ -4408,11 +4294,7 @@ namespace Emby.Server.Implementations.Data hasProviderIds.Add("ProviderIds like " + paramName); // this replaces the placeholder with a value, here: %key=val% - if (statement is not null) - { - statement.TryBind(paramName, "%" + pair.Key + "=" + pair.Value + "%"); - } - + statement?.TryBind(paramName, "%" + pair.Key + "=" + pair.Value + "%"); index++; break; @@ -4489,11 +4371,7 @@ namespace Emby.Server.Implementations.Data if (query.AncestorIds.Length == 1) { whereClauses.Add("Guid in (select itemId from AncestorIds where AncestorId=@AncestorId)"); - - if (statement is not null) - { - statement.TryBind("@AncestorId", query.AncestorIds[0]); - } + statement?.TryBind("@AncestorId", query.AncestorIds[0]); } if (query.AncestorIds.Length > 1) @@ -4506,20 +4384,13 @@ namespace Emby.Server.Implementations.Data { var inClause = "select guid from TypedBaseItems where PresentationUniqueKey=@AncestorWithPresentationUniqueKey"; whereClauses.Add(string.Format(CultureInfo.InvariantCulture, "Guid in (select itemId from AncestorIds where AncestorId in ({0}))", inClause)); - if (statement is not null) - { - statement.TryBind("@AncestorWithPresentationUniqueKey", query.AncestorWithPresentationUniqueKey); - } + statement?.TryBind("@AncestorWithPresentationUniqueKey", query.AncestorWithPresentationUniqueKey); } if (!string.IsNullOrWhiteSpace(query.SeriesPresentationUniqueKey)) { whereClauses.Add("SeriesPresentationUniqueKey=@SeriesPresentationUniqueKey"); - - if (statement is not null) - { - statement.TryBind("@SeriesPresentationUniqueKey", query.SeriesPresentationUniqueKey); - } + statement?.TryBind("@SeriesPresentationUniqueKey", query.SeriesPresentationUniqueKey); } if (query.ExcludeInheritedTags.Length > 0) @@ -4668,6 +4539,7 @@ namespace Emby.Server.Implementations.Data return whereClauses; } +#nullable disable /// <summary> /// Formats a where clause for the specified provider. diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs index 8faebadd61..6e2a33fd56 100644 --- a/Emby.Server.Implementations/Localization/LocalizationManager.cs +++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs @@ -297,30 +297,17 @@ namespace Emby.Server.Implementations.Localization } // Try splitting by : to handle "Germany: FSK 18" - var index = rating.IndexOf(':', StringComparison.Ordinal); - if (index != -1) + if (rating.Contains(':', StringComparison.OrdinalIgnoreCase)) { - var trimmedRating = rating.AsSpan(index).TrimStart(':').Trim(); - - if (!trimmedRating.IsEmpty) - { - return GetRatingLevel(trimmedRating.ToString()); - } + return GetRatingLevel(rating.AsSpan().RightPart(':').ToString()); } // Remove prefix country code to handle "DE-18" - index = rating.IndexOf('-', StringComparison.Ordinal); - if (index != -1) + if (rating.Contains('-', StringComparison.OrdinalIgnoreCase)) { - var trimmedRating = rating.AsSpan(index).TrimStart('-').Trim(); - - if (!trimmedRating.IsEmpty) - { - return GetRatingLevel(trimmedRating.ToString()); - } + return GetRatingLevel(rating.AsSpan().RightPart('-').ToString()); } - // TODO: Further improve when necessary return null; } diff --git a/Emby.Server.Implementations/Localization/Ratings/be.csv b/Emby.Server.Implementations/Localization/Ratings/be.csv index 5588f63b45..d171a71328 100644 --- a/Emby.Server.Implementations/Localization/Ratings/be.csv +++ b/Emby.Server.Implementations/Localization/Ratings/be.csv @@ -6,6 +6,6 @@ MG6,6 9,9 KNT,12 12,12 -BE_14,14 +14,14 16,16 18,18 diff --git a/Jellyfin.Server/Migrations/Routines/MigrateRatingLevels.cs b/Jellyfin.Server/Migrations/Routines/MigrateRatingLevels.cs index 21f1872bc5..f30eb84213 100644 --- a/Jellyfin.Server/Migrations/Routines/MigrateRatingLevels.cs +++ b/Jellyfin.Server/Migrations/Routines/MigrateRatingLevels.cs @@ -27,7 +27,7 @@ namespace Jellyfin.Server.Migrations.Routines public Guid Id => Guid.Parse("{67445D54-B895-4B24-9F4C-35CE0690EA07}"); /// <inheritdoc/> - public string Name => "RemoveDuplicateExtras"; + public string Name => "MigrateRatingLevels"; /// <inheritdoc/> public bool PerformOnNewInstall => false; -- cgit v1.2.3 From 5f938de337537ac1e03633c82d2a27a34808d9d0 Mon Sep 17 00:00:00 2001 From: Shadowghost <Ghost_of_Stone@web.de> Date: Mon, 20 Feb 2023 11:49:46 +0100 Subject: Build ratingClause with StringBuilder --- .../Data/SqliteItemRepository.cs | 37 +++++++++++----------- 1 file changed, 19 insertions(+), 18 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 055131c8e6..ecc2a2c910 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -3964,19 +3964,19 @@ namespace Emby.Server.Implementations.Data whereClauses.Add(clause); } - var ratingClause = "("; + var ratingClauseBuilder = new StringBuilder("("); if (query.HasParentalRating ?? false) { - ratingClause += "InheritedParentalRatingValue not null"; + ratingClauseBuilder.Append("InheritedParentalRatingValue not null"); if (query.MinParentalRating.HasValue) { - ratingClause += " AND InheritedParentalRatingValue >= @MinParentalRating"; + ratingClauseBuilder.Append(" AND InheritedParentalRatingValue >= @MinParentalRating"); statement?.TryBind("@MinParentalRating", query.MinParentalRating.Value); } if (query.MaxParentalRating.HasValue) { - ratingClause += " AND InheritedParentalRatingValue <= @MaxParentalRating"; + ratingClauseBuilder.Append(" AND InheritedParentalRatingValue <= @MaxParentalRating"); statement?.TryBind("@MaxParentalRating", query.MaxParentalRating.Value); } } @@ -3985,7 +3985,7 @@ namespace Emby.Server.Implementations.Data var paramName = "@UnratedType"; var index = 0; string blockedUnratedItems = string.Join(',', query.BlockUnratedItems.Select(_ => paramName + index++)); - ratingClause += "(InheritedParentalRatingValue is null AND UnratedType not in (" + blockedUnratedItems + "))"; + ratingClauseBuilder.Append("(InheritedParentalRatingValue is null AND UnratedType not in (" + blockedUnratedItems + "))"); if (statement is not null) { @@ -3997,12 +3997,12 @@ namespace Emby.Server.Implementations.Data if (query.MinParentalRating.HasValue || query.MaxParentalRating.HasValue) { - ratingClause += " OR ("; + ratingClauseBuilder.Append(" OR ("); } if (query.MinParentalRating.HasValue) { - ratingClause += "InheritedParentalRatingValue >= @MinParentalRating"; + ratingClauseBuilder.Append("InheritedParentalRatingValue >= @MinParentalRating"); statement?.TryBind("@MinParentalRating", query.MinParentalRating.Value); } @@ -4010,49 +4010,50 @@ namespace Emby.Server.Implementations.Data { if (query.MinParentalRating.HasValue) { - ratingClause += " AND "; + ratingClauseBuilder.Append(" AND "); } - ratingClause += "InheritedParentalRatingValue <= @MaxParentalRating"; + ratingClauseBuilder.Append("InheritedParentalRatingValue <= @MaxParentalRating"); statement?.TryBind("@MaxParentalRating", query.MaxParentalRating.Value); } if (query.MinParentalRating.HasValue || query.MaxParentalRating.HasValue) { - ratingClause += ")"; + ratingClauseBuilder.Append(")"); } if (!(query.MinParentalRating.HasValue || query.MaxParentalRating.HasValue)) { - ratingClause += " OR InheritedParentalRatingValue not null"; + ratingClauseBuilder.Append(" OR InheritedParentalRatingValue not null"); } } else if (query.MinParentalRating.HasValue) { - ratingClause += "InheritedParentalRatingValue is null OR (InheritedParentalRatingValue >= @MinParentalRating"; + ratingClauseBuilder.Append("InheritedParentalRatingValue is null OR (InheritedParentalRatingValue >= @MinParentalRating"); statement?.TryBind("@MinParentalRating", query.MinParentalRating.Value); if (query.MaxParentalRating.HasValue) { - ratingClause += " AND InheritedParentalRatingValue <= @MaxParentalRating"; + ratingClauseBuilder.Append(" AND InheritedParentalRatingValue <= @MaxParentalRating"); statement?.TryBind("@MaxParentalRating", query.MaxParentalRating.Value); } - ratingClause += ")"; + ratingClauseBuilder.Append(")"); } else if (query.MaxParentalRating.HasValue) { - ratingClause += "InheritedParentalRatingValue is null OR InheritedParentalRatingValue <= @MaxParentalRating"; + ratingClauseBuilder.Append("InheritedParentalRatingValue is null OR InheritedParentalRatingValue <= @MaxParentalRating"); statement?.TryBind("@MaxParentalRating", query.MaxParentalRating.Value); } else if (!query.HasParentalRating ?? false) { - ratingClause += "InheritedParentalRatingValue is null"; + ratingClauseBuilder.Append("InheritedParentalRatingValue is null"); } - if (!string.Equals(ratingClause, "(", StringComparison.OrdinalIgnoreCase)) + var ratingClauseString = ratingClauseBuilder.ToString(); + if (!string.Equals(ratingClauseString, "(", StringComparison.OrdinalIgnoreCase)) { - whereClauses.Add(ratingClause + ")"); + whereClauses.Add(ratingClauseString + ")"); } if (query.HasOfficialRating.HasValue) -- cgit v1.2.3 From 5b493e14accd6cd8b10b5a14b15e105f73a2724e Mon Sep 17 00:00:00 2001 From: Bond-009 <bond.009@outlook.com> Date: Mon, 20 Feb 2023 16:07:51 +0100 Subject: Improve alternate ordering (#9336) --- Emby.Naming/AudioBook/AudioBookFilePathParser.cs | 2 +- Emby.Naming/AudioBook/AudioBookNameParser.cs | 2 +- Emby.Naming/TV/SeriesResolver.cs | 2 +- Emby.Naming/Video/FileStackRule.cs | 2 +- Emby.Naming/Video/VideoListResolver.cs | 29 +++++++++++---- .../HdHomerun/LegacyHdHomerunChannelCommands.cs | 3 +- .../LiveTv/TunerHosts/M3uParser.cs | 3 +- src/Jellyfin.Extensions/StringExtensions.cs | 2 +- .../Video/MultiVersionTests.cs | 42 ++++++++++++++-------- 9 files changed, 58 insertions(+), 29 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Naming/AudioBook/AudioBookFilePathParser.cs b/Emby.Naming/AudioBook/AudioBookFilePathParser.cs index 219599d569..75fdedfeab 100644 --- a/Emby.Naming/AudioBook/AudioBookFilePathParser.cs +++ b/Emby.Naming/AudioBook/AudioBookFilePathParser.cs @@ -32,7 +32,7 @@ namespace Emby.Naming.AudioBook var fileName = Path.GetFileNameWithoutExtension(path); foreach (var expression in _options.AudioBookPartsExpressions) { - var match = new Regex(expression, RegexOptions.IgnoreCase).Match(fileName); + var match = Regex.Match(fileName, expression, RegexOptions.IgnoreCase); if (match.Success) { if (!result.ChapterNumber.HasValue) diff --git a/Emby.Naming/AudioBook/AudioBookNameParser.cs b/Emby.Naming/AudioBook/AudioBookNameParser.cs index f49c3f0e75..5ea649dbf7 100644 --- a/Emby.Naming/AudioBook/AudioBookNameParser.cs +++ b/Emby.Naming/AudioBook/AudioBookNameParser.cs @@ -30,7 +30,7 @@ namespace Emby.Naming.AudioBook AudioBookNameParserResult result = default; foreach (var expression in _options.AudioBookNamesExpressions) { - var match = new Regex(expression, RegexOptions.IgnoreCase).Match(name); + var match = Regex.Match(name, expression, RegexOptions.IgnoreCase); if (match.Success) { if (result.Name is null) diff --git a/Emby.Naming/TV/SeriesResolver.cs b/Emby.Naming/TV/SeriesResolver.cs index 156a03c9ed..307a840964 100644 --- a/Emby.Naming/TV/SeriesResolver.cs +++ b/Emby.Naming/TV/SeriesResolver.cs @@ -14,7 +14,7 @@ namespace Emby.Naming.TV /// Used for removing separators between words, i.e turns "The_show" into "The show" while /// preserving namings like "S.H.O.W". /// </summary> - private static readonly Regex _seriesNameRegex = new Regex(@"((?<a>[^\._]{2,})[\._]*)|([\._](?<b>[^\._]{2,}))"); + private static readonly Regex _seriesNameRegex = new Regex(@"((?<a>[^\._]{2,})[\._]*)|([\._](?<b>[^\._]{2,}))", RegexOptions.Compiled); /// <summary> /// Resolve information about series from path. diff --git a/Emby.Naming/Video/FileStackRule.cs b/Emby.Naming/Video/FileStackRule.cs index 76b487f428..be0f79d33a 100644 --- a/Emby.Naming/Video/FileStackRule.cs +++ b/Emby.Naming/Video/FileStackRule.cs @@ -17,7 +17,7 @@ public class FileStackRule /// <param name="isNumerical">Whether the file stack rule uses numerical or alphabetical numbering.</param> public FileStackRule(string token, bool isNumerical) { - _tokenRegex = new Regex(token, RegexOptions.IgnoreCase); + _tokenRegex = new Regex(token, RegexOptions.IgnoreCase | RegexOptions.Compiled); IsNumerical = isNumerical; } diff --git a/Emby.Naming/Video/VideoListResolver.cs b/Emby.Naming/Video/VideoListResolver.cs index 8247c374d0..6209cd46f4 100644 --- a/Emby.Naming/Video/VideoListResolver.cs +++ b/Emby.Naming/Video/VideoListResolver.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using System.Text.RegularExpressions; using Emby.Naming.Common; +using Jellyfin.Extensions; using MediaBrowser.Model.IO; namespace Emby.Naming.Video @@ -13,6 +14,8 @@ namespace Emby.Naming.Video /// </summary> public static class VideoListResolver { + private static readonly Regex _resolutionRegex = new Regex("[0-9]{2}[0-9]+[ip]", RegexOptions.IgnoreCase | RegexOptions.Compiled); + /// <summary> /// Resolves alternative versions and extras from list of video files. /// </summary> @@ -115,19 +118,34 @@ namespace Emby.Naming.Video continue; } - if (!IsEligibleForMultiVersion(folderName, video.Files[0].Path, namingOptions)) + if (!IsEligibleForMultiVersion(folderName, video.Files[0].FileNameWithoutExtension, namingOptions)) { return videos; } - if (folderName.Equals(Path.GetFileNameWithoutExtension(video.Files[0].Path.AsSpan()), StringComparison.Ordinal)) + if (folderName.Equals(video.Files[0].FileNameWithoutExtension, StringComparison.Ordinal)) { primary = video; } } - // The list is created and overwritten in the caller, so we are allowed to do in-place sorting - videos.Sort((x, y) => string.Compare(x.Name, y.Name, StringComparison.Ordinal)); + if (videos.Count > 1) + { + var groups = videos.GroupBy(x => _resolutionRegex.IsMatch(x.Files[0].FileNameWithoutExtension)).ToList(); + videos.Clear(); + foreach (var group in groups) + { + if (group.Key) + { + videos.InsertRange(0, group.OrderByDescending(x => x.Files[0].FileNameWithoutExtension.ToString(), new AlphanumericComparator())); + } + else + { + videos.AddRange(group.OrderBy(x => x.Files[0].FileNameWithoutExtension.ToString(), new AlphanumericComparator())); + } + } + } + primary ??= videos[0]; videos.Remove(primary); @@ -161,9 +179,8 @@ namespace Emby.Naming.Video return true; } - private static bool IsEligibleForMultiVersion(ReadOnlySpan<char> folderName, string testFilePath, NamingOptions namingOptions) + private static bool IsEligibleForMultiVersion(ReadOnlySpan<char> folderName, ReadOnlySpan<char> testFilename, NamingOptions namingOptions) { - var testFilename = Path.GetFileNameWithoutExtension(testFilePath.AsSpan()); if (!testFilename.StartsWith(folderName, StringComparison.OrdinalIgnoreCase)) { return false; diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/LegacyHdHomerunChannelCommands.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/LegacyHdHomerunChannelCommands.cs index 80d9d07247..3450f971fc 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/LegacyHdHomerunChannelCommands.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/LegacyHdHomerunChannelCommands.cs @@ -13,8 +13,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun public LegacyHdHomerunChannelCommands(string url) { // parse url for channel and program - var regExp = new Regex(@"\/ch([0-9]+)-?([0-9]*)"); - var match = regExp.Match(url); + var match = Regex.Match(url, @"\/ch([0-9]+)-?([0-9]*)"); if (match.Success) { _channel = match.Groups[1].Value; diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs index 1c0ed6505c..046be7c5c7 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs @@ -308,8 +308,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts { var dict = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); - var reg = new Regex(@"([a-z0-9\-_]+)=\""([^""]+)\""", RegexOptions.IgnoreCase); - var matches = reg.Matches(line); + var matches = Regex.Matches(line, @"([a-z0-9\-_]+)=\""([^""]+)\""", RegexOptions.IgnoreCase); remaining = line; diff --git a/src/Jellyfin.Extensions/StringExtensions.cs b/src/Jellyfin.Extensions/StringExtensions.cs index f30b639459..7c61248757 100644 --- a/src/Jellyfin.Extensions/StringExtensions.cs +++ b/src/Jellyfin.Extensions/StringExtensions.cs @@ -12,7 +12,7 @@ namespace Jellyfin.Extensions { // Matches non-conforming unicode chars // https://mnaoumov.wordpress.com/2014/06/14/stripping-invalid-characters-from-utf-16-strings/ - private static readonly Regex _nonConformingUnicode = new Regex("([\ud800-\udbff](?![\udc00-\udfff]))|((?<![\ud800-\udbff])[\udc00-\udfff])|(\ufffd)"); + private static readonly Regex _nonConformingUnicode = new Regex("([\ud800-\udbff](?![\udc00-\udfff]))|((?<![\ud800-\udbff])[\udc00-\udfff])|(\ufffd)", RegexOptions.Compiled); /// <summary> /// Removes the diacritics character from the strings. diff --git a/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs b/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs index 02e6f6368c..294f11ee74 100644 --- a/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs @@ -188,8 +188,7 @@ namespace Jellyfin.Naming.Tests.Video @"/movies/Iron Man/Iron Man-bluray.mkv", @"/movies/Iron Man/Iron Man-3d.mkv", @"/movies/Iron Man/Iron Man-3d-hsbs.mkv", - @"/movies/Iron Man/Iron Man-3d.hsbs.mkv", - @"/movies/Iron Man/Iron Man[test].mkv", + @"/movies/Iron Man/Iron Man[test].mkv" }; var result = VideoListResolver.Resolve( @@ -197,10 +196,14 @@ namespace Jellyfin.Naming.Tests.Video _namingOptions).ToList(); Assert.Single(result); - Assert.Equal(7, result[0].AlternateVersions.Count); - Assert.False(result[0].AlternateVersions[2].Is3D); - Assert.True(result[0].AlternateVersions[3].Is3D); - Assert.True(result[0].AlternateVersions[4].Is3D); + Assert.Equal("/movies/Iron Man/Iron Man.mkv", result[0].Files[0].Path); + Assert.Equal(6, result[0].AlternateVersions.Count); + Assert.Equal("/movies/Iron Man/Iron Man-720p.mkv", result[0].AlternateVersions[0].Path); + Assert.Equal("/movies/Iron Man/Iron Man-3d.mkv", result[0].AlternateVersions[1].Path); + Assert.Equal("/movies/Iron Man/Iron Man-3d-hsbs.mkv", result[0].AlternateVersions[2].Path); + Assert.Equal("/movies/Iron Man/Iron Man-bluray.mkv", result[0].AlternateVersions[3].Path); + Assert.Equal("/movies/Iron Man/Iron Man-test.mkv", result[0].AlternateVersions[4].Path); + Assert.Equal("/movies/Iron Man/Iron Man[test].mkv", result[0].AlternateVersions[5].Path); } [Fact] @@ -214,7 +217,6 @@ namespace Jellyfin.Naming.Tests.Video @"/movies/Iron Man/Iron Man - bluray.mkv", @"/movies/Iron Man/Iron Man - 3d.mkv", @"/movies/Iron Man/Iron Man - 3d-hsbs.mkv", - @"/movies/Iron Man/Iron Man - 3d.hsbs.mkv", @"/movies/Iron Man/Iron Man [test].mkv" }; @@ -223,10 +225,14 @@ namespace Jellyfin.Naming.Tests.Video _namingOptions).ToList(); Assert.Single(result); - Assert.Equal(7, result[0].AlternateVersions.Count); - Assert.False(result[0].AlternateVersions[3].Is3D); - Assert.True(result[0].AlternateVersions[4].Is3D); - Assert.True(result[0].AlternateVersions[5].Is3D); + Assert.Equal("/movies/Iron Man/Iron Man.mkv", result[0].Files[0].Path); + Assert.Equal(6, result[0].AlternateVersions.Count); + Assert.Equal("/movies/Iron Man/Iron Man - 720p.mkv", result[0].AlternateVersions[0].Path); + Assert.Equal("/movies/Iron Man/Iron Man - 3d.mkv", result[0].AlternateVersions[1].Path); + Assert.Equal("/movies/Iron Man/Iron Man - 3d-hsbs.mkv", result[0].AlternateVersions[2].Path); + Assert.Equal("/movies/Iron Man/Iron Man - bluray.mkv", result[0].AlternateVersions[3].Path); + Assert.Equal("/movies/Iron Man/Iron Man - test.mkv", result[0].AlternateVersions[4].Path); + Assert.Equal("/movies/Iron Man/Iron Man [test].mkv", result[0].AlternateVersions[5].Path); } [Fact] @@ -328,8 +334,12 @@ namespace Jellyfin.Naming.Tests.Video { var files = new[] { + @"/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016) - Theatrical Release.mkv", + @"/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016) - Directors Cut.mkv", @"/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016) - 1080p.mkv", - @"/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016).mkv" + @"/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016) - 2160p.mkv", + @"/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016) - 720p.mkv", + @"/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016).mkv", }; var result = VideoListResolver.Resolve( @@ -338,8 +348,12 @@ namespace Jellyfin.Naming.Tests.Video Assert.Single(result); Assert.Equal("/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016).mkv", result[0].Files[0].Path); - Assert.Single(result[0].AlternateVersions); - Assert.Equal("/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016) - 1080p.mkv", result[0].AlternateVersions[0].Path); + Assert.Equal(5, result[0].AlternateVersions.Count); + Assert.Equal("/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016) - 2160p.mkv", result[0].AlternateVersions[0].Path); + Assert.Equal("/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016) - 1080p.mkv", result[0].AlternateVersions[1].Path); + Assert.Equal("/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016) - 720p.mkv", result[0].AlternateVersions[2].Path); + Assert.Equal("/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016) - Directors Cut.mkv", result[0].AlternateVersions[3].Path); + Assert.Equal("/movies/X-Men Apocalypse (2016)/X-Men Apocalypse (2016) - Theatrical Release.mkv", result[0].AlternateVersions[4].Path); } [Fact] -- cgit v1.2.3 From 6300d01fcceba56932741251443f5b2aa4f76de2 Mon Sep 17 00:00:00 2001 From: Shadowghost <Ghost_of_Stone@web.de> Date: Mon, 20 Feb 2023 21:58:31 +0100 Subject: Apply review suggestion --- Emby.Server.Implementations/Data/SqliteItemRepository.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index ecc2a2c910..0aa943270e 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -3623,8 +3623,12 @@ namespace Emby.Server.Implementations.Data clauseBuilder.Append("(guid in (select itemid from People where Name = (select Name from TypedBaseItems where guid=") .Append(paramName) .Append("))) OR "); - query.PersonIds[i].TryWriteBytes(idBytes); - statement?.TryBind(paramName, idBytes); + + if (statement is not null) + { + query.PersonIds[i].TryWriteBytes(idBytes); + statement.TryBind(paramName, idBytes); + } } // Remove last " OR " -- cgit v1.2.3 From 37560784670ea12b63bfc0d573c42e9599f776da Mon Sep 17 00:00:00 2001 From: Asahi Oka <asahi.oka.20@gmail.com> Date: Sun, 19 Feb 2023 20:55:16 +0000 Subject: Translated using Weblate (Spanish) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/es/ --- Emby.Server.Implementations/Localization/Core/es.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/es.json b/Emby.Server.Implementations/Localization/Core/es.json index afffdf3bfa..5e41462db1 100644 --- a/Emby.Server.Implementations/Localization/Core/es.json +++ b/Emby.Server.Implementations/Localization/Core/es.json @@ -31,7 +31,7 @@ "ItemRemovedWithName": "{0} ha sido eliminado de la biblioteca", "LabelIpAddressValue": "Dirección IP: {0}", "LabelRunningTimeValue": "Tiempo de funcionamiento: {0}", - "Latest": "Últimos", + "Latest": "Último contenido en", "MessageApplicationUpdated": "Se ha actualizado el servidor Jellyfin", "MessageApplicationUpdatedTo": "Se ha actualizado el servidor Jellyfin a la versión {0}", "MessageNamedServerConfigurationUpdatedWithValue": "La sección {0} de configuración del servidor ha sido actualizada", -- cgit v1.2.3 From 0af699621aa4794866c24ed9315318ebee66356a Mon Sep 17 00:00:00 2001 From: lyaschuchenko <lyaschuchenko@gmail.com> Date: Tue, 21 Feb 2023 05:46:11 +0000 Subject: Translated using Weblate (Ukrainian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/uk/ --- Emby.Server.Implementations/Localization/Core/uk.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/uk.json b/Emby.Server.Implementations/Localization/Core/uk.json index 92ce616f2e..ff77fb8c56 100644 --- a/Emby.Server.Implementations/Localization/Core/uk.json +++ b/Emby.Server.Implementations/Localization/Core/uk.json @@ -86,7 +86,7 @@ "Shows": "Шоу", "ServerNameNeedsToBeRestarted": "{0} потрібно перезапустити", "ScheduledTaskStartedWithName": "{0} розпочато", - "ScheduledTaskFailedWithName": "Помилка {0}", + "ScheduledTaskFailedWithName": "{0} незавершено, збій", "ProviderValue": "Постачальник: {0}", "PluginUpdatedWithName": "{0} оновлено", "PluginUninstalledWithName": "{0} видалено", -- cgit v1.2.3 From a328dba3b16adb1707a8b7fb504cc47eabed18d8 Mon Sep 17 00:00:00 2001 From: Pranav Avva <pranav.avva@gmail.com> Date: Wed, 22 Feb 2023 20:38:26 +0000 Subject: Translated using Weblate (Hindi) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/hi/ --- Emby.Server.Implementations/Localization/Core/hi.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/hi.json b/Emby.Server.Implementations/Localization/Core/hi.json index 182b43ffca..a0e2f04a16 100644 --- a/Emby.Server.Implementations/Localization/Core/hi.json +++ b/Emby.Server.Implementations/Localization/Core/hi.json @@ -67,5 +67,11 @@ "Plugin": "प्लग-इन", "Playlists": "प्लेलिस्ट", "Photos": "तस्वीरें", - "External": "बाहरी" + "External": "बाहरी", + "PluginUpdatedWithName": "{0} अपडेट हुए", + "ScheduledTaskStartedWithName": "{0} शुरू हुए", + "Songs": "गाने", + "UserStartedPlayingItemWithValues": "{0} {2} पर {1} खेल रहे हैं", + "UserStoppedPlayingItemWithValues": "{0} ने {2} पर {1} खेलना खत्म किया", + "StartupEmbyServerIsLoading": "जेलीफ़िन सर्वर लोड हो रहा है। कृपया शीघ्र ही पुन: प्रयास करें।" } -- cgit v1.2.3 From 54cd3e6d551d797bace33d65334cf1e98669676d Mon Sep 17 00:00:00 2001 From: Bas <weblate@hanka.mp> Date: Mon, 27 Feb 2023 20:42:46 +0000 Subject: Translated using Weblate (Dutch) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/nl/ --- Emby.Server.Implementations/Localization/Core/nl.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/nl.json b/Emby.Server.Implementations/Localization/Core/nl.json index 081ba0cc7e..383096f7e2 100644 --- a/Emby.Server.Implementations/Localization/Core/nl.json +++ b/Emby.Server.Implementations/Localization/Core/nl.json @@ -95,13 +95,13 @@ "TaskDownloadMissingSubtitlesDescription": "Zoekt op het internet naar ontbrekende ondertiteling gebaseerd op metadataconfiguratie.", "TaskDownloadMissingSubtitles": "Ontbrekende ondertiteling downloaden", "TaskRefreshChannelsDescription": "Vernieuwt informatie van internet kanalen.", - "TaskRefreshChannels": "Vernieuw kanalen", + "TaskRefreshChannels": "Kanalen vernieuwen", "TaskCleanTranscodeDescription": "Verwijdert transcode bestanden ouder dan 1 dag.", "TaskCleanLogs": "Logboekmap opschonen", "TaskCleanTranscode": "Transcoderingsmap opschonen", "TaskUpdatePluginsDescription": "Downloadt en installeert updates van plug-ins waarvoor automatisch bijwerken is ingeschakeld.", "TaskUpdatePlugins": "Plug-ins bijwerken", - "TaskRefreshPeopleDescription": "Update metadata voor acteurs en regisseurs in de media bibliotheek.", + "TaskRefreshPeopleDescription": "Updatet metadata voor acteurs en regisseurs in je mediabibliotheek.", "TaskRefreshPeople": "Personen vernieuwen", "TaskCleanLogsDescription": "Verwijdert log bestanden ouder dan {0} dagen.", "TaskRefreshLibraryDescription": "Scant de mediabibliotheek op nieuwe bestanden en vernieuwt de metadata.", -- cgit v1.2.3 From 4b01aaa0f7c52557d1500daaae2bc457a56dbffe Mon Sep 17 00:00:00 2001 From: Bond_009 <bond.009@outlook.com> Date: Wed, 1 Mar 2023 00:44:57 +0100 Subject: Allocate less Lists --- .../ConnectionManagerXmlBuilder.cs | 4 +-- .../ContentDirectory/ContentDirectoryXmlBuilder.cs | 4 +-- Emby.Naming/AudioBook/AudioBookListResolver.cs | 20 ++++++------- .../Channels/ChannelManager.cs | 2 +- .../Collections/CollectionManager.cs | 18 +++++++----- .../Data/SqliteItemRepository.cs | 10 ++++--- Emby.Server.Implementations/Dto/DtoService.cs | 12 ++++---- .../Library/LibraryManager.cs | 33 ++++++++++------------ .../Library/Resolvers/Audio/AudioResolver.cs | 3 +- .../Library/UserViewManager.cs | 2 +- .../Persistence/IItemRepository.cs | 2 +- MediaBrowser.Providers/Manager/ProviderManager.cs | 4 +-- .../Playlists/PlaylistItemsProvider.cs | 2 +- .../Plugins/AudioDb/AudioDbArtistImageProvider.cs | 5 ++-- .../MusicBrainz/MusicBrainzAlbumProvider.cs | 10 +++---- .../Plugins/Omdb/OmdbImageProvider.cs | 5 +--- .../Tmdb/BoxSets/TmdbBoxSetImageProvider.cs | 2 +- .../Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs | 4 +-- .../Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs | 2 +- .../Plugins/Tmdb/People/TmdbPersonImageProvider.cs | 5 +--- .../Plugins/Tmdb/People/TmdbPersonProvider.cs | 4 +-- .../Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs | 5 +--- .../Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs | 5 +--- .../Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs | 2 +- 24 files changed, 76 insertions(+), 89 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Dlna/ConnectionManager/ConnectionManagerXmlBuilder.cs b/Emby.Dlna/ConnectionManager/ConnectionManagerXmlBuilder.cs index c484dac542..db1190ae7c 100644 --- a/Emby.Dlna/ConnectionManager/ConnectionManagerXmlBuilder.cs +++ b/Emby.Dlna/ConnectionManager/ConnectionManagerXmlBuilder.cs @@ -27,7 +27,7 @@ namespace Emby.Dlna.ConnectionManager /// <returns>The <see cref="IEnumerable{StateVariable}"/>.</returns> private static IEnumerable<StateVariable> GetStateVariables() { - var list = new List<StateVariable> + return new StateVariable[] { new StateVariable { @@ -114,8 +114,6 @@ namespace Emby.Dlna.ConnectionManager SendsEvents = false } }; - - return list; } } } diff --git a/Emby.Dlna/ContentDirectory/ContentDirectoryXmlBuilder.cs b/Emby.Dlna/ContentDirectory/ContentDirectoryXmlBuilder.cs index 3edaabb70e..9af28aa7cb 100644 --- a/Emby.Dlna/ContentDirectory/ContentDirectoryXmlBuilder.cs +++ b/Emby.Dlna/ContentDirectory/ContentDirectoryXmlBuilder.cs @@ -27,7 +27,7 @@ namespace Emby.Dlna.ContentDirectory /// <returns>The <see cref="IEnumerable{StateVariable}"/>.</returns> private static IEnumerable<StateVariable> GetStateVariables() { - var list = new List<StateVariable> + return new StateVariable[] { new StateVariable { @@ -154,8 +154,6 @@ namespace Emby.Dlna.ContentDirectory SendsEvents = false } }; - - return list; } } } diff --git a/Emby.Naming/AudioBook/AudioBookListResolver.cs b/Emby.Naming/AudioBook/AudioBookListResolver.cs index bdae20b6b2..ca304102fd 100644 --- a/Emby.Naming/AudioBook/AudioBookListResolver.cs +++ b/Emby.Naming/AudioBook/AudioBookListResolver.cs @@ -79,25 +79,25 @@ namespace Emby.Naming.AudioBook { if (group.Count() > 1 || haveChaptersOrPages) { - var ex = new List<AudioBookFileInfo>(); - var alt = new List<AudioBookFileInfo>(); + List<AudioBookFileInfo>? ex = null; + List<AudioBookFileInfo>? alt = null; foreach (var audioFile in group) { - var name = Path.GetFileNameWithoutExtension(audioFile.Path); - if (name.Equals("audiobook", StringComparison.OrdinalIgnoreCase) || - name.Contains(nameParserResult.Name, StringComparison.OrdinalIgnoreCase) || - name.Contains(nameWithReplacedDots, StringComparison.OrdinalIgnoreCase)) + var name = Path.GetFileNameWithoutExtension(audioFile.Path.AsSpan()); + if (name.Equals("audiobook", StringComparison.OrdinalIgnoreCase) + || name.Contains(nameParserResult.Name, StringComparison.OrdinalIgnoreCase) + || name.Contains(nameWithReplacedDots, StringComparison.OrdinalIgnoreCase)) { - alt.Add(audioFile); + (alt ??= new()).Add(audioFile); } else { - ex.Add(audioFile); + (ex ??= new()).Add(audioFile); } } - if (ex.Count > 0) + if (ex is not null) { var extra = ex .OrderBy(x => x.Container) @@ -108,7 +108,7 @@ namespace Emby.Naming.AudioBook extras.AddRange(extra); } - if (alt.Count > 0) + if (alt is not null) { var alternatives = alt .OrderBy(x => x.Container) diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs index 84ba194648..1e3c4dea14 100644 --- a/Emby.Server.Implementations/Channels/ChannelManager.cs +++ b/Emby.Server.Implementations/Channels/ChannelManager.cs @@ -401,7 +401,7 @@ namespace Emby.Server.Implementations.Channels } else { - results = new List<MediaSourceInfo>(); + results = Enumerable.Empty<MediaSourceInfo>(); } return results diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs index b53c8ca512..1796830552 100644 --- a/Emby.Server.Implementations/Collections/CollectionManager.cs +++ b/Emby.Server.Implementations/Collections/CollectionManager.cs @@ -206,8 +206,7 @@ namespace Emby.Server.Implementations.Collections throw new ArgumentException("No collection exists with the supplied Id"); } - var list = new List<LinkedChild>(); - var itemList = new List<BaseItem>(); + List<BaseItem>? itemList = null; var linkedChildrenList = collection.GetLinkedChildren(); var currentLinkedChildrenIds = linkedChildrenList.Select(i => i.Id).ToList(); @@ -223,18 +222,23 @@ namespace Emby.Server.Implementations.Collections if (!currentLinkedChildrenIds.Contains(id)) { - itemList.Add(item); + (itemList ??= new()).Add(item); - list.Add(LinkedChild.Create(item)); linkedChildrenList.Add(item); } } - if (list.Count > 0) + if (itemList is not null) { - LinkedChild[] newChildren = new LinkedChild[collection.LinkedChildren.Length + list.Count]; + var originalLen = collection.LinkedChildren.Length; + var newItemCount = itemList.Count; + LinkedChild[] newChildren = new LinkedChild[originalLen + newItemCount]; collection.LinkedChildren.CopyTo(newChildren, 0); - list.CopyTo(newChildren, collection.LinkedChildren.Length); + for (int i = 0; i < newItemCount; i++) + { + newChildren[originalLen + i] = LinkedChild.Create(itemList[i]); + } + collection.LinkedChildren = newChildren; collection.UpdateRatingToItems(linkedChildrenList); diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 0aa943270e..3bf4d07c59 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -586,7 +586,7 @@ namespace Emby.Server.Implementations.Data /// <exception cref="ArgumentNullException"> /// <paramref name="items"/> or <paramref name="cancellationToken"/> is <c>null</c>. /// </exception> - public void SaveItems(IEnumerable<BaseItem> items, CancellationToken cancellationToken) + public void SaveItems(IReadOnlyList<BaseItem> items, CancellationToken cancellationToken) { ArgumentNullException.ThrowIfNull(items); @@ -594,9 +594,11 @@ namespace Emby.Server.Implementations.Data CheckDisposed(); - var tuples = new List<(BaseItem, List<Guid>, BaseItem, string, List<string>)>(); - foreach (var item in items) + var itemsLen = items.Count; + var tuples = new ValueTuple<BaseItem, List<Guid>, BaseItem, string, List<string>>[itemsLen]; + for (int i = 0; i < itemsLen; i++) { + var item = items[i]; var ancestorIds = item.SupportsAncestors ? item.GetAncestorIds().Distinct().ToList() : null; @@ -606,7 +608,7 @@ namespace Emby.Server.Implementations.Data var userdataKey = item.GetUserDataKeys().FirstOrDefault(); var inheritedTags = item.GetInheritedTags(); - tuples.Add((item, ancestorIds, topParent, userdataKey, inheritedTags)); + tuples[i] = (item, ancestorIds, topParent, userdataKey, inheritedTags); } using (var connection = GetConnection()) diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index e928f1ff3a..45270de893 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -85,8 +85,8 @@ namespace Emby.Server.Implementations.Dto { var accessibleItems = user is null ? items : items.Where(x => x.IsVisible(user)).ToList(); var returnItems = new BaseItemDto[accessibleItems.Count]; - var programTuples = new List<(BaseItem, BaseItemDto)>(); - var channelTuples = new List<(BaseItemDto, LiveTvChannel)>(); + List<(BaseItem, BaseItemDto)> programTuples = null; + List<(BaseItemDto, LiveTvChannel)> channelTuples = null; for (int index = 0; index < accessibleItems.Count; index++) { @@ -95,11 +95,11 @@ namespace Emby.Server.Implementations.Dto if (item is LiveTvChannel tvChannel) { - channelTuples.Add((dto, tvChannel)); + (channelTuples ??= new()).Add((dto, tvChannel)); } else if (item is LiveTvProgram) { - programTuples.Add((item, dto)); + (programTuples ??= new()).Add((item, dto)); } if (item is IItemByName byName) @@ -122,12 +122,12 @@ namespace Emby.Server.Implementations.Dto returnItems[index] = dto; } - if (programTuples.Count > 0) + if (programTuples is not null) { LivetvManager.AddInfoToProgramDto(programTuples, options.Fields, user).GetAwaiter().GetResult(); } - if (channelTuples.Count > 0) + if (channelTuples is not null) { LivetvManager.AddChannelInfo(channelTuples, options, user); } diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index a3c66dc798..66bd68ddd0 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -356,8 +356,8 @@ namespace Emby.Server.Implementations.Library } var children = item.IsFolder - ? ((Folder)item).GetRecursiveChildren(false).ToList() - : new List<BaseItem>(); + ? ((Folder)item).GetRecursiveChildren(false) + : Enumerable.Empty<BaseItem>(); foreach (var metadataPath in GetMetadataPaths(item, children)) { @@ -1253,7 +1253,7 @@ namespace Emby.Server.Implementations.Library var parent = GetItemById(query.ParentId); if (parent is not null) { - SetTopParentIdsOrAncestors(query, new List<BaseItem> { parent }); + SetTopParentIdsOrAncestors(query, new[] { parent }); } } @@ -1277,7 +1277,7 @@ namespace Emby.Server.Implementations.Library var parent = GetItemById(query.ParentId); if (parent is not null) { - SetTopParentIdsOrAncestors(query, new List<BaseItem> { parent }); + SetTopParentIdsOrAncestors(query, new[] { parent }); } } @@ -1435,7 +1435,7 @@ namespace Emby.Server.Implementations.Library var parent = GetItemById(query.ParentId); if (parent is not null) { - SetTopParentIdsOrAncestors(query, new List<BaseItem> { parent }); + SetTopParentIdsOrAncestors(query, new[] { parent }); } } @@ -1455,7 +1455,7 @@ namespace Emby.Server.Implementations.Library _itemRepository.GetItemList(query)); } - private void SetTopParentIdsOrAncestors(InternalItemsQuery query, List<BaseItem> parents) + private void SetTopParentIdsOrAncestors(InternalItemsQuery query, IReadOnlyCollection<BaseItem> parents) { if (parents.All(i => i is ICollectionFolder || i is UserView)) { @@ -1602,7 +1602,7 @@ namespace Emby.Server.Implementations.Library { _logger.LogError(ex, "Error getting intros"); - return new List<IntroInfo>(); + return Enumerable.Empty<IntroInfo>(); } } @@ -2876,7 +2876,7 @@ namespace Emby.Server.Implementations.Library private async Task SavePeopleMetadataAsync(IEnumerable<PersonInfo> people, CancellationToken cancellationToken) { - var personsToSave = new List<BaseItem>(); + List<BaseItem> personsToSave = null; foreach (var person in people) { @@ -2918,12 +2918,12 @@ namespace Emby.Server.Implementations.Library if (saveEntity) { - personsToSave.Add(personEntity); + (personsToSave ??= new()).Add(personEntity); await RunMetadataSavers(personEntity, itemUpdateType).ConfigureAwait(false); } } - if (personsToSave.Count > 0) + if (personsToSave is not null) { CreateItems(personsToSave, null, CancellationToken.None); } @@ -3085,22 +3085,19 @@ namespace Emby.Server.Implementations.Library throw new ArgumentNullException(nameof(path)); } - var removeList = new List<NameValuePair>(); + List<NameValuePair> removeList = null; foreach (var contentType in _configurationManager.Configuration.ContentTypes) { - if (string.IsNullOrWhiteSpace(contentType.Name)) - { - removeList.Add(contentType); - } - else if (_fileSystem.AreEqual(path, contentType.Name) + if (string.IsNullOrWhiteSpace(contentType.Name) + || _fileSystem.AreEqual(path, contentType.Name) || _fileSystem.ContainsSubPath(path, contentType.Name)) { - removeList.Add(contentType); + (removeList ??= new()).Add(contentType); } } - if (removeList.Count > 0) + if (removeList is not null) { _configurationManager.Configuration.ContentTypes = _configurationManager.Configuration.ContentTypes .Except(removeList) diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs index 06621700aa..69e9057984 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs @@ -158,7 +158,6 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio private MultiItemResolverResult ResolveMultipleAudio(Folder parent, IEnumerable<FileSystemMetadata> fileSystemEntries, bool parseName) { var files = new List<FileSystemMetadata>(); - var items = new List<BaseItem>(); var leftOver = new List<FileSystemMetadata>(); // Loop through each child file/folder and see if we find a video @@ -180,7 +179,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio var result = new MultiItemResolverResult { ExtraFiles = leftOver, - Items = items + Items = new List<BaseItem>() }; var isInMixedFolder = resolverResult.Count > 1 || (parent is not null && parent.IsTopParent); diff --git a/Emby.Server.Implementations/Library/UserViewManager.cs b/Emby.Server.Implementations/Library/UserViewManager.cs index 1137625f44..0e2d34d39c 100644 --- a/Emby.Server.Implementations/Library/UserViewManager.cs +++ b/Emby.Server.Implementations/Library/UserViewManager.cs @@ -286,7 +286,7 @@ namespace Emby.Server.Implementations.Library if (parents.Count == 0) { - return new List<BaseItem>(); + return Array.Empty<BaseItem>(); } if (includeItemTypes.Length == 0) diff --git a/MediaBrowser.Controller/Persistence/IItemRepository.cs b/MediaBrowser.Controller/Persistence/IItemRepository.cs index 24f7b5cd36..2c52b2b45e 100644 --- a/MediaBrowser.Controller/Persistence/IItemRepository.cs +++ b/MediaBrowser.Controller/Persistence/IItemRepository.cs @@ -28,7 +28,7 @@ namespace MediaBrowser.Controller.Persistence /// </summary> /// <param name="items">The items.</param> /// <param name="cancellationToken">The cancellation token.</param> - void SaveItems(IEnumerable<BaseItem> items, CancellationToken cancellationToken); + void SaveItems(IReadOnlyList<BaseItem> items, CancellationToken cancellationToken); void SaveImages(BaseItem item); diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index c07839ff25..81ccd86534 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -284,12 +284,12 @@ namespace MediaBrowser.Providers.Manager } catch (OperationCanceledException) { - return new List<RemoteImageInfo>(); + return Enumerable.Empty<RemoteImageInfo>(); } catch (Exception ex) { _logger.LogError(ex, "{ProviderName} failed in GetImageInfos for type {ItemType} at {ItemPath}", provider.GetType().Name, item.GetType().Name, item.Path); - return new List<RemoteImageInfo>(); + return Enumerable.Empty<RemoteImageInfo>(); } } diff --git a/MediaBrowser.Providers/Playlists/PlaylistItemsProvider.cs b/MediaBrowser.Providers/Playlists/PlaylistItemsProvider.cs index db4c5f436e..9bd36f25c3 100644 --- a/MediaBrowser.Providers/Playlists/PlaylistItemsProvider.cs +++ b/MediaBrowser.Providers/Playlists/PlaylistItemsProvider.cs @@ -87,7 +87,7 @@ namespace MediaBrowser.Providers.Playlists return GetPlsItems(stream); } - return new List<LinkedChild>(); + return Enumerable.Empty<LinkedChild>(); } private IEnumerable<LinkedChild> GetPlsItems(Stream stream) diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistImageProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistImageProvider.cs index b1a285a964..2232dfa0d7 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistImageProvider.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.IO; +using System.Linq; using System.Net.Http; using System.Text.Json; using System.Threading; @@ -42,7 +43,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb /// <inheritdoc /> public IEnumerable<ImageType> GetSupportedImages(BaseItem item) { - return new List<ImageType> + return new ImageType[] { ImageType.Primary, ImageType.Logo, @@ -74,7 +75,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb } } - return new List<RemoteImageInfo>(); + return Enumerable.Empty<RemoteImageInfo>(); } private IEnumerable<RemoteImageInfo> GetImages(AudioDbArtistProvider.Artist item) diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs index 4aa4269891..d0bd7d6098 100644 --- a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs +++ b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs @@ -157,10 +157,10 @@ public class MusicBrainzAlbumProvider : IRemoteMetadataProvider<MusicAlbum, Albu var artists = releaseSearchResult.ArtistCredit; if (artists is not null && artists.Count > 0) { - var artistResults = new List<RemoteSearchResult>(); - - foreach (var artist in artists) + var artistResults = new RemoteSearchResult[artists.Count]; + for (int i = 0; i < artists.Count; i++) { + var artist = artists[i]; var artistResult = new RemoteSearchResult { Name = artist.Name @@ -171,11 +171,11 @@ public class MusicBrainzAlbumProvider : IRemoteMetadataProvider<MusicAlbum, Albu artistResult.SetProviderId(MetadataProvider.MusicBrainzArtist, artist.Artist!.Id.ToString()); } - artistResults.Add(artistResult); + artistResults[i] = artistResult; } searchResult.AlbumArtist = artistResults[0]; - searchResult.Artists = artistResults.ToArray(); + searchResult.Artists = artistResults; } searchResult.SetProviderId(MetadataProvider.MusicBrainzAlbum, releaseSearchResult.Id.ToString()); diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs index 60b373483f..140a64f52f 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs @@ -38,10 +38,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb public IEnumerable<ImageType> GetSupportedImages(BaseItem item) { - return new List<ImageType> - { - ImageType.Primary - }; + yield return ImageType.Primary; } public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken) diff --git a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs index ef32b0a074..a4c6cb47de 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs @@ -48,7 +48,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets /// <inheritdoc /> public IEnumerable<ImageType> GetSupportedImages(BaseItem item) { - return new List<ImageType> + return new ImageType[] { ImageType.Primary, ImageType.Backdrop diff --git a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs index e6c7dba250..c2018d820e 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs @@ -72,7 +72,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets var collectionSearchResults = await _tmdbClientManager.SearchCollectionAsync(searchInfo.Name, language, cancellationToken).ConfigureAwait(false); - var collections = new List<RemoteSearchResult>(); + var collections = new RemoteSearchResult[collectionSearchResults.Count]; for (var i = 0; i < collectionSearchResults.Count; i++) { var collection = new RemoteSearchResult @@ -82,7 +82,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets }; collection.SetProviderId(MetadataProvider.Tmdb, collectionSearchResults[i].Id.ToString(CultureInfo.InvariantCulture)); - collections.Add(collection); + collections[i] = collection; } return collections; diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs index 655fa5a16d..bfec48e7c7 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs @@ -49,7 +49,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies /// <inheritdoc /> public IEnumerable<ImageType> GetSupportedImages(BaseItem item) { - return new List<ImageType> + return new ImageType[] { ImageType.Primary, ImageType.Backdrop, diff --git a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs index bc959ee2bd..9e5404b325 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs @@ -46,10 +46,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People /// <inheritdoc /> public IEnumerable<ImageType> GetSupportedImages(BaseItem item) { - return new List<ImageType> - { - ImageType.Primary - }; + yield return ImageType.Primary; } /// <inheritdoc /> diff --git a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs index c03a1ca3b8..5c6e71fd89 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs @@ -67,7 +67,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People var personSearchResult = await _tmdbClientManager.SearchPersonAsync(searchInfo.Name, cancellationToken).ConfigureAwait(false); - var remoteSearchResults = new List<RemoteSearchResult>(); + var remoteSearchResults = new RemoteSearchResult[personSearchResult.Count]; for (var i = 0; i < personSearchResult.Count; i++) { var person = personSearchResult[i]; @@ -79,7 +79,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People }; remoteSearchResult.SetProviderId(MetadataProvider.Tmdb, person.Id.ToString(CultureInfo.InvariantCulture)); - remoteSearchResults.Add(remoteSearchResult); + remoteSearchResults[i] = remoteSearchResult; } return remoteSearchResults; diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs index abef732bbb..d1fec7cb13 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs @@ -47,10 +47,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV /// <inheritdoc /> public IEnumerable<ImageType> GetSupportedImages(BaseItem item) { - return new List<ImageType> - { - ImageType.Primary - }; + yield return ImageType.Primary; } /// <inheritdoc /> diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs index b8d1460db9..a743601ed3 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs @@ -48,10 +48,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV /// <inheritdoc /> public IEnumerable<ImageType> GetSupportedImages(BaseItem item) { - return new List<ImageType> - { - ImageType.Primary - }; + yield return ImageType.Primary; } /// <inheritdoc /> diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs index 79cb6e86d4..192fb052d7 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs @@ -48,7 +48,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV /// <inheritdoc /> public IEnumerable<ImageType> GetSupportedImages(BaseItem item) { - return new List<ImageType> + return new ImageType[] { ImageType.Primary, ImageType.Backdrop, -- cgit v1.2.3 From d280dc65549b2507c058bcf1823e3ab040293da7 Mon Sep 17 00:00:00 2001 From: Bond_009 <bond.009@outlook.com> Date: Wed, 1 Mar 2023 16:43:55 +0100 Subject: Reduce log spam Fixes #7801 --- .../ScheduledTasks/ScheduledTaskWorker.cs | 9 +++------ Emby.Server.Implementations/ScheduledTasks/TaskManager.cs | 7 ++----- Jellyfin.Networking/Manager/NetworkManager.cs | 6 +++--- 3 files changed, 8 insertions(+), 14 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs index ee9aa85699..1af2c96d2f 100644 --- a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs +++ b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs @@ -93,11 +93,8 @@ namespace Emby.Server.Implementations.ScheduledTasks public ScheduledTaskWorker(IScheduledTask scheduledTask, IApplicationPaths applicationPaths, ITaskManager taskManager, ILogger logger) { ArgumentNullException.ThrowIfNull(scheduledTask); - ArgumentNullException.ThrowIfNull(applicationPaths); - ArgumentNullException.ThrowIfNull(taskManager); - ArgumentNullException.ThrowIfNull(logger); ScheduledTask = scheduledTask; @@ -332,7 +329,7 @@ namespace Emby.Server.Implementations.ScheduledTasks return; } - _logger.LogInformation("{0} fired for task: {1}", trigger.GetType().Name, Name); + _logger.LogDebug("{0} fired for task: {1}", trigger.GetType().Name, Name); trigger.Stop(); @@ -378,7 +375,7 @@ namespace Emby.Server.Implementations.ScheduledTasks CurrentCancellationTokenSource = new CancellationTokenSource(); - _logger.LogInformation("Executing {0}", Name); + _logger.LogDebug("Executing {0}", Name); ((TaskManager)_taskManager).OnTaskExecuting(this); @@ -406,7 +403,7 @@ namespace Emby.Server.Implementations.ScheduledTasks } catch (Exception ex) { - _logger.LogError(ex, "Error"); + _logger.LogError(ex, "Error executing Scheduled Task"); failureException = ex; diff --git a/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs b/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs index 6dc20e66ba..42c30c959d 100644 --- a/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs +++ b/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs @@ -132,7 +132,7 @@ namespace Emby.Server.Implementations.ScheduledTasks { var type = scheduledTask.ScheduledTask.GetType(); - _logger.LogInformation("Queuing task {0}", type.Name); + _logger.LogDebug("Queuing task {0}", type.Name); lock (_taskQueue) { @@ -172,7 +172,7 @@ namespace Emby.Server.Implementations.ScheduledTasks { var type = task.ScheduledTask.GetType(); - _logger.LogInformation("Queuing task {0}", type.Name); + _logger.LogDebug("Queuing task {0}", type.Name); lock (_taskQueue) { @@ -254,9 +254,6 @@ namespace Emby.Server.Implementations.ScheduledTasks /// </summary> private void ExecuteQueuedTasks() { - _logger.LogInformation("ExecuteQueuedTasks"); - - // Execute queued tasks lock (_taskQueue) { var list = new List<Tuple<Type, TaskOptions>>(); diff --git a/Jellyfin.Networking/Manager/NetworkManager.cs b/Jellyfin.Networking/Manager/NetworkManager.cs index 88332ce393..f406e27a6d 100644 --- a/Jellyfin.Networking/Manager/NetworkManager.cs +++ b/Jellyfin.Networking/Manager/NetworkManager.cs @@ -1019,8 +1019,8 @@ namespace Jellyfin.Networking.Manager _internalInterfaces = CreateCollection(_interfaceAddresses.Where(IsInLocalNetwork)); } - _logger.LogInformation("Defined LAN addresses : {0}", _lanSubnets.AsString()); - _logger.LogInformation("Defined LAN exclusions : {0}", _excludedSubnets.AsString()); + _logger.LogInformation("Defined LAN addresses: {0}", _lanSubnets.AsString()); + _logger.LogInformation("Defined LAN exclusions: {0}", _excludedSubnets.AsString()); _logger.LogInformation("Using LAN addresses: {0}", _lanSubnets.Exclude(_excludedSubnets, true).AsNetworks().AsString()); } } @@ -1145,7 +1145,7 @@ namespace Jellyfin.Networking.Manager } _logger.LogDebug("Discovered {0} interfaces.", _interfaceAddresses.Count); - _logger.LogDebug("Interfaces addresses : {0}", _interfaceAddresses.AsString()); + _logger.LogDebug("Interfaces addresses: {0}", _interfaceAddresses.AsString()); } } -- cgit v1.2.3 From 160baa02fd9a2e7b4e2b3dcc28769274d0588413 Mon Sep 17 00:00:00 2001 From: Joe Rogers <1337joe@gmail.com> Date: Mon, 6 Mar 2023 22:18:26 -0500 Subject: Remove some BaseItem references to make ItemResolveArgs more usable for testing. --- Emby.Server.Implementations/Library/LibraryManager.cs | 2 +- MediaBrowser.Controller/Entities/AggregateFolder.cs | 2 +- MediaBrowser.Controller/Entities/CollectionFolder.cs | 2 +- MediaBrowser.Controller/Library/ItemResolveArgs.cs | 17 +++++++---------- .../Library/EpisodeResolverTest.cs | 6 ++++-- .../Library/MovieResolverTests.cs | 3 ++- 6 files changed, 16 insertions(+), 16 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 66bd68ddd0..9fa4d3599f 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -537,7 +537,7 @@ namespace Emby.Server.Implementations.Library collectionType = GetContentTypeOverride(fullPath, true); } - var args = new ItemResolveArgs(_configurationManager.ApplicationPaths, directoryService) + var args = new ItemResolveArgs(_configurationManager.ApplicationPaths, directoryService, this) { Parent = parent, FileInfo = fileInfo, diff --git a/MediaBrowser.Controller/Entities/AggregateFolder.cs b/MediaBrowser.Controller/Entities/AggregateFolder.cs index 08c622cde0..aacb38607e 100644 --- a/MediaBrowser.Controller/Entities/AggregateFolder.cs +++ b/MediaBrowser.Controller/Entities/AggregateFolder.cs @@ -120,7 +120,7 @@ namespace MediaBrowser.Controller.Entities var path = ContainingFolderPath; - var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, directoryService) + var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, directoryService, LibraryManager) { FileInfo = FileSystem.GetDirectoryInfo(path) }; diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs index 5ac619d8f5..ff17797179 100644 --- a/MediaBrowser.Controller/Entities/CollectionFolder.cs +++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs @@ -288,7 +288,7 @@ namespace MediaBrowser.Controller.Entities { var path = ContainingFolderPath; - var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, directoryService) + var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, directoryService, LibraryManager) { FileInfo = FileSystem.GetDirectoryInfo(path), Parent = GetParent() as Folder, diff --git a/MediaBrowser.Controller/Library/ItemResolveArgs.cs b/MediaBrowser.Controller/Library/ItemResolveArgs.cs index 01986d3032..730d89356d 100644 --- a/MediaBrowser.Controller/Library/ItemResolveArgs.cs +++ b/MediaBrowser.Controller/Library/ItemResolveArgs.cs @@ -23,6 +23,7 @@ namespace MediaBrowser.Controller.Library /// </summary> private readonly IServerApplicationPaths _appPaths; + private readonly ILibraryManager _libraryManager; private LibraryOptions _libraryOptions; /// <summary> @@ -30,10 +31,12 @@ namespace MediaBrowser.Controller.Library /// </summary> /// <param name="appPaths">The app paths.</param> /// <param name="directoryService">The directory service.</param> - public ItemResolveArgs(IServerApplicationPaths appPaths, IDirectoryService directoryService) + /// <param name="libraryManager">The library manager.</param> + public ItemResolveArgs(IServerApplicationPaths appPaths, IDirectoryService directoryService, ILibraryManager libraryManager) { _appPaths = appPaths; DirectoryService = directoryService; + _libraryManager = libraryManager; } // TODO remove dependencies as properties, they should be injected where it makes sense @@ -47,7 +50,7 @@ namespace MediaBrowser.Controller.Library public LibraryOptions LibraryOptions { - get => _libraryOptions ??= Parent is null ? new LibraryOptions() : BaseItem.LibraryManager.GetLibraryOptions(Parent); + get => _libraryOptions ??= Parent is null ? new LibraryOptions() : _libraryManager.GetLibraryOptions(Parent); set => _libraryOptions = value; } @@ -231,21 +234,15 @@ namespace MediaBrowser.Controller.Library /// <summary> /// Gets the configured content type for the path. /// </summary> - /// <remarks> - /// This is subject to future refactoring as it relies on a static property in BaseItem. - /// </remarks> /// <returns>The configured content type.</returns> public string GetConfiguredContentType() { - return BaseItem.LibraryManager.GetConfiguredContentType(Path); + return _libraryManager.GetConfiguredContentType(Path); } /// <summary> /// Gets the file system children that do not hit the ignore file check. /// </summary> - /// <remarks> - /// This is subject to future refactoring as it relies on a static property in BaseItem. - /// </remarks> /// <returns>The file system children that are not ignored.</returns> public IEnumerable<FileSystemMetadata> GetActualFileSystemChildren() { @@ -253,7 +250,7 @@ namespace MediaBrowser.Controller.Library for (var i = 0; i < numberOfChildren; i++) { var child = FileSystemChildren[i]; - if (BaseItem.LibraryManager.IgnoreFile(child, Parent)) + if (_libraryManager.IgnoreFile(child, Parent)) { continue; } diff --git a/tests/Jellyfin.Server.Implementations.Tests/Library/EpisodeResolverTest.cs b/tests/Jellyfin.Server.Implementations.Tests/Library/EpisodeResolverTest.cs index 286ba04059..65f19a051a 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Library/EpisodeResolverTest.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Library/EpisodeResolverTest.cs @@ -25,7 +25,8 @@ namespace Jellyfin.Server.Implementations.Tests.Library var episodeResolver = new EpisodeResolver(Mock.Of<ILogger<EpisodeResolver>>(), _namingOptions); var itemResolveArgs = new ItemResolveArgs( Mock.Of<IServerApplicationPaths>(), - Mock.Of<IDirectoryService>()) + Mock.Of<IDirectoryService>(), + null) { Parent = parent, CollectionType = CollectionType.TvShows, @@ -48,7 +49,8 @@ namespace Jellyfin.Server.Implementations.Tests.Library var episodeResolver = new EpisodeResolverMock(Mock.Of<ILogger<EpisodeResolver>>(), _namingOptions); var itemResolveArgs = new ItemResolveArgs( Mock.Of<IServerApplicationPaths>(), - Mock.Of<IDirectoryService>()) + Mock.Of<IDirectoryService>(), + null) { Parent = series, CollectionType = CollectionType.TvShows, diff --git a/tests/Jellyfin.Server.Implementations.Tests/Library/MovieResolverTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Library/MovieResolverTests.cs index efc3ac0c2a..f96bd6138e 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Library/MovieResolverTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Library/MovieResolverTests.cs @@ -21,7 +21,8 @@ public class MovieResolverTests var movieResolver = new MovieResolver(Mock.Of<IImageProcessor>(), Mock.Of<ILogger<MovieResolver>>(), _namingOptions); var itemResolveArgs = new ItemResolveArgs( Mock.Of<IServerApplicationPaths>(), - Mock.Of<IDirectoryService>()) + Mock.Of<IDirectoryService>(), + null) { Parent = null, FileInfo = new FileSystemMetadata -- cgit v1.2.3 From 1c3a97bf6adadf4e3b22177e1e965691637d0426 Mon Sep 17 00:00:00 2001 From: Joe Rogers <1337joe@gmail.com> Date: Mon, 6 Mar 2023 23:00:55 -0500 Subject: Inject IDirectoryService where needed instead of passing it through ItemResolveArgs --- Emby.Server.Implementations/Library/LibraryManager.cs | 8 +++++--- .../Library/Resolvers/Audio/MusicAlbumResolver.cs | 7 +++++-- .../Library/Resolvers/Audio/MusicArtistResolver.cs | 15 +++++++++------ .../Library/Resolvers/BaseVideoResolver.cs | 7 +++++-- .../Library/Resolvers/ExtraResolver.cs | 8 +++++--- .../Library/Resolvers/GenericVideoResolver.cs | 6 ++++-- .../Library/Resolvers/Movies/MovieResolver.cs | 13 +++++++------ .../Library/Resolvers/PhotoResolver.cs | 18 ++++++++++++++---- .../Library/Resolvers/TV/EpisodeResolver.cs | 6 ++++-- MediaBrowser.Controller/Entities/AggregateFolder.cs | 2 +- MediaBrowser.Controller/Entities/CollectionFolder.cs | 2 +- MediaBrowser.Controller/Library/ItemResolveArgs.cs | 10 ++-------- .../Library/EpisodeResolverTest.cs | 8 +++----- .../Library/MovieResolverTests.cs | 3 +-- 14 files changed, 66 insertions(+), 47 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 9fa4d3599f..e5c520ca2b 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -113,6 +113,7 @@ namespace Emby.Server.Implementations.Library /// <param name="imageProcessor">The image processor.</param> /// <param name="memoryCache">The memory cache.</param> /// <param name="namingOptions">The naming options.</param> + /// <param name="directoryService">The directory service.</param> public LibraryManager( IServerApplicationHost appHost, ILoggerFactory loggerFactory, @@ -128,7 +129,8 @@ namespace Emby.Server.Implementations.Library IItemRepository itemRepository, IImageProcessor imageProcessor, IMemoryCache memoryCache, - NamingOptions namingOptions) + NamingOptions namingOptions, + IDirectoryService directoryService) { _appHost = appHost; _logger = loggerFactory.CreateLogger<LibraryManager>(); @@ -146,7 +148,7 @@ namespace Emby.Server.Implementations.Library _memoryCache = memoryCache; _namingOptions = namingOptions; - _extraResolver = new ExtraResolver(loggerFactory.CreateLogger<ExtraResolver>(), namingOptions); + _extraResolver = new ExtraResolver(loggerFactory.CreateLogger<ExtraResolver>(), namingOptions, directoryService); _configurationManager.ConfigurationUpdated += ConfigurationUpdated; @@ -537,7 +539,7 @@ namespace Emby.Server.Implementations.Library collectionType = GetContentTypeOverride(fullPath, true); } - var args = new ItemResolveArgs(_configurationManager.ApplicationPaths, directoryService, this) + var args = new ItemResolveArgs(_configurationManager.ApplicationPaths, this) { Parent = parent, FileInfo = fileInfo, diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs index a922e36855..bbc70701cb 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs @@ -25,16 +25,19 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio { private readonly ILogger<MusicAlbumResolver> _logger; private readonly NamingOptions _namingOptions; + private readonly IDirectoryService _directoryService; /// <summary> /// Initializes a new instance of the <see cref="MusicAlbumResolver"/> class. /// </summary> /// <param name="logger">The logger.</param> /// <param name="namingOptions">The naming options.</param> - public MusicAlbumResolver(ILogger<MusicAlbumResolver> logger, NamingOptions namingOptions) + /// <param name="directoryService">The directory service.</param> + public MusicAlbumResolver(ILogger<MusicAlbumResolver> logger, NamingOptions namingOptions, IDirectoryService directoryService) { _logger = logger; _namingOptions = namingOptions; + _directoryService = directoryService; } /// <summary> @@ -109,7 +112,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio } // If args contains music it's a music album - if (ContainsMusic(args.FileSystemChildren, true, args.DirectoryService)) + if (ContainsMusic(args.FileSystemChildren, true, _directoryService)) { return true; } diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs index 2538c2b5b4..c858dc53d9 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using Emby.Naming.Common; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Resolvers; using MediaBrowser.Model.Entities; using Microsoft.Extensions.Logging; @@ -18,19 +19,23 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio public class MusicArtistResolver : ItemResolver<MusicArtist> { private readonly ILogger<MusicAlbumResolver> _logger; - private NamingOptions _namingOptions; + private readonly NamingOptions _namingOptions; + private readonly IDirectoryService _directoryService; /// <summary> /// Initializes a new instance of the <see cref="MusicArtistResolver"/> class. /// </summary> /// <param name="logger">Instance of the <see cref="MusicAlbumResolver"/> interface.</param> /// <param name="namingOptions">The <see cref="NamingOptions"/>.</param> + /// <param name="directoryService">The directory service.</param> public MusicArtistResolver( ILogger<MusicAlbumResolver> logger, - NamingOptions namingOptions) + NamingOptions namingOptions, + IDirectoryService directoryService) { _logger = logger; _namingOptions = namingOptions; + _directoryService = directoryService; } /// <summary> @@ -78,9 +83,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio return null; } - var directoryService = args.DirectoryService; - - var albumResolver = new MusicAlbumResolver(_logger, _namingOptions); + var albumResolver = new MusicAlbumResolver(_logger, _namingOptions, _directoryService); var directories = args.FileSystemChildren.Where(i => i.IsDirectory); @@ -97,7 +100,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio } // If we contain a music album assume we are an artist folder - if (albumResolver.IsMusicAlbum(fileSystemInfo.FullName, directoryService)) + if (albumResolver.IsMusicAlbum(fileSystemInfo.FullName, _directoryService)) { // Stop once we see a music album state.Stop(); diff --git a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs index e8615e7db7..9b133bef48 100644 --- a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs @@ -25,14 +25,17 @@ namespace Emby.Server.Implementations.Library.Resolvers { private readonly ILogger _logger; - protected BaseVideoResolver(ILogger logger, NamingOptions namingOptions) + protected BaseVideoResolver(ILogger logger, NamingOptions namingOptions, IDirectoryService directoryService) { _logger = logger; NamingOptions = namingOptions; + DirectoryService = directoryService; } protected NamingOptions NamingOptions { get; } + protected IDirectoryService DirectoryService { get; } + /// <summary> /// Resolves the specified args. /// </summary> @@ -65,7 +68,7 @@ namespace Emby.Server.Implementations.Library.Resolvers var filename = child.Name; if (child.IsDirectory) { - if (IsDvdDirectory(child.FullName, filename, args.DirectoryService)) + if (IsDvdDirectory(child.FullName, filename, DirectoryService)) { videoType = VideoType.Dvd; } diff --git a/Emby.Server.Implementations/Library/Resolvers/ExtraResolver.cs b/Emby.Server.Implementations/Library/Resolvers/ExtraResolver.cs index 30c52e19d3..0b255f673b 100644 --- a/Emby.Server.Implementations/Library/Resolvers/ExtraResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/ExtraResolver.cs @@ -4,6 +4,7 @@ using System.IO; using Emby.Naming.Common; using Emby.Naming.Video; using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Resolvers; using MediaBrowser.Model.Entities; using Microsoft.Extensions.Logging; @@ -25,11 +26,12 @@ namespace Emby.Server.Implementations.Library.Resolvers /// </summary> /// <param name="logger">The logger.</param> /// <param name="namingOptions">An instance of <see cref="NamingOptions"/>.</param> - public ExtraResolver(ILogger<ExtraResolver> logger, NamingOptions namingOptions) + /// <param name="directoryService">The directory service.</param> + public ExtraResolver(ILogger<ExtraResolver> logger, NamingOptions namingOptions, IDirectoryService directoryService) { _namingOptions = namingOptions; - _trailerResolvers = new IItemResolver[] { new GenericVideoResolver<Trailer>(logger, namingOptions) }; - _videoResolvers = new IItemResolver[] { new GenericVideoResolver<Video>(logger, namingOptions) }; + _trailerResolvers = new IItemResolver[] { new GenericVideoResolver<Trailer>(logger, namingOptions, directoryService) }; + _videoResolvers = new IItemResolver[] { new GenericVideoResolver<Video>(logger, namingOptions, directoryService) }; } /// <summary> diff --git a/Emby.Server.Implementations/Library/Resolvers/GenericVideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/GenericVideoResolver.cs index 5e33b402d5..ba320266a4 100644 --- a/Emby.Server.Implementations/Library/Resolvers/GenericVideoResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/GenericVideoResolver.cs @@ -2,6 +2,7 @@ using Emby.Naming.Common; using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Providers; using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.Library.Resolvers @@ -18,8 +19,9 @@ namespace Emby.Server.Implementations.Library.Resolvers /// </summary> /// <param name="logger">The logger.</param> /// <param name="namingOptions">The naming options.</param> - public GenericVideoResolver(ILogger logger, NamingOptions namingOptions) - : base(logger, namingOptions) + /// <param name="directoryService">The directory service.</param> + public GenericVideoResolver(ILogger logger, NamingOptions namingOptions, IDirectoryService directoryService) + : base(logger, namingOptions, directoryService) { } } diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs index ef4fa1fd2d..ea980b9929 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs @@ -43,8 +43,9 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies /// <param name="imageProcessor">The image processor.</param> /// <param name="logger">The logger.</param> /// <param name="namingOptions">The naming options.</param> - public MovieResolver(IImageProcessor imageProcessor, ILogger<MovieResolver> logger, NamingOptions namingOptions) - : base(logger, namingOptions) + /// <param name="directoryService">The directory service.</param> + public MovieResolver(IImageProcessor imageProcessor, ILogger<MovieResolver> logger, NamingOptions namingOptions, IDirectoryService directoryService) + : base(logger, namingOptions, directoryService) { _imageProcessor = imageProcessor; } @@ -97,12 +98,12 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies if (string.Equals(collectionType, CollectionType.MusicVideos, StringComparison.OrdinalIgnoreCase)) { - movie = FindMovie<MusicVideo>(args, args.Path, args.Parent, files, args.DirectoryService, collectionType, false); + movie = FindMovie<MusicVideo>(args, args.Path, args.Parent, files, DirectoryService, collectionType, false); } if (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase)) { - movie = FindMovie<Video>(args, args.Path, args.Parent, files, args.DirectoryService, collectionType, false); + movie = FindMovie<Video>(args, args.Path, args.Parent, files, DirectoryService, collectionType, false); } if (string.IsNullOrEmpty(collectionType)) @@ -118,12 +119,12 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies return null; } - movie = FindMovie<Movie>(args, args.Path, args.Parent, files, args.DirectoryService, collectionType, true); + movie = FindMovie<Movie>(args, args.Path, args.Parent, files, DirectoryService, collectionType, true); } if (string.Equals(collectionType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase)) { - movie = FindMovie<Movie>(args, args.Path, args.Parent, files, args.DirectoryService, collectionType, true); + movie = FindMovie<Movie>(args, args.Path, args.Parent, files, DirectoryService, collectionType, true); } // ignore extras diff --git a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs index e11fb262eb..9026160ff0 100644 --- a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs @@ -1,7 +1,5 @@ #nullable disable -#pragma warning disable CS1591 - using System; using System.Collections.Generic; using System.IO; @@ -12,15 +10,20 @@ using Jellyfin.Extensions; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Resolvers; using MediaBrowser.Model.Entities; namespace Emby.Server.Implementations.Library.Resolvers { + /// <summary> + /// Class PhotoResolver. + /// </summary> public class PhotoResolver : ItemResolver<Photo> { private readonly IImageProcessor _imageProcessor; private readonly NamingOptions _namingOptions; + private readonly IDirectoryService _directoryService; private static readonly HashSet<string> _ignoreFiles = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { @@ -35,10 +38,17 @@ namespace Emby.Server.Implementations.Library.Resolvers "default" }; - public PhotoResolver(IImageProcessor imageProcessor, NamingOptions namingOptions) + /// <summary> + /// Initializes a new instance of the <see cref="PhotoResolver"/> class. + /// </summary> + /// <param name="imageProcessor">The image processor.</param> + /// <param name="namingOptions">The naming options.</param> + /// <param name="directoryService">The directory service.</param> + public PhotoResolver(IImageProcessor imageProcessor, NamingOptions namingOptions, IDirectoryService directoryService) { _imageProcessor = imageProcessor; _namingOptions = namingOptions; + _directoryService = directoryService; } /// <summary> @@ -61,7 +71,7 @@ namespace Emby.Server.Implementations.Library.Resolvers var filename = Path.GetFileNameWithoutExtension(args.Path); // Make sure the image doesn't belong to a video file - var files = args.DirectoryService.GetFiles(Path.GetDirectoryName(args.Path)); + var files = _directoryService.GetFiles(Path.GetDirectoryName(args.Path)); foreach (var file in files) { diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs index 0fcc5070b3..392ee4c771 100644 --- a/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs @@ -5,6 +5,7 @@ using System.Linq; using Emby.Naming.Common; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using Microsoft.Extensions.Logging; @@ -20,8 +21,9 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV /// </summary> /// <param name="logger">The logger.</param> /// <param name="namingOptions">The naming options.</param> - public EpisodeResolver(ILogger<EpisodeResolver> logger, NamingOptions namingOptions) - : base(logger, namingOptions) + /// <param name="directoryService">The directory service.</param> + public EpisodeResolver(ILogger<EpisodeResolver> logger, NamingOptions namingOptions, IDirectoryService directoryService) + : base(logger, namingOptions, directoryService) { } diff --git a/MediaBrowser.Controller/Entities/AggregateFolder.cs b/MediaBrowser.Controller/Entities/AggregateFolder.cs index aacb38607e..d789033f16 100644 --- a/MediaBrowser.Controller/Entities/AggregateFolder.cs +++ b/MediaBrowser.Controller/Entities/AggregateFolder.cs @@ -120,7 +120,7 @@ namespace MediaBrowser.Controller.Entities var path = ContainingFolderPath; - var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, directoryService, LibraryManager) + var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, LibraryManager) { FileInfo = FileSystem.GetDirectoryInfo(path) }; diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs index ff17797179..095b261c05 100644 --- a/MediaBrowser.Controller/Entities/CollectionFolder.cs +++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs @@ -288,7 +288,7 @@ namespace MediaBrowser.Controller.Entities { var path = ContainingFolderPath; - var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, directoryService, LibraryManager) + var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, LibraryManager) { FileInfo = FileSystem.GetDirectoryInfo(path), Parent = GetParent() as Folder, diff --git a/MediaBrowser.Controller/Library/ItemResolveArgs.cs b/MediaBrowser.Controller/Library/ItemResolveArgs.cs index 730d89356d..c701021679 100644 --- a/MediaBrowser.Controller/Library/ItemResolveArgs.cs +++ b/MediaBrowser.Controller/Library/ItemResolveArgs.cs @@ -1,12 +1,11 @@ #nullable disable -#pragma warning disable CA1721, CA1819, CS1591 +#pragma warning disable CS1591 using System; using System.Collections.Generic; using System.Linq; using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.IO; @@ -30,18 +29,13 @@ namespace MediaBrowser.Controller.Library /// Initializes a new instance of the <see cref="ItemResolveArgs" /> class. /// </summary> /// <param name="appPaths">The app paths.</param> - /// <param name="directoryService">The directory service.</param> /// <param name="libraryManager">The library manager.</param> - public ItemResolveArgs(IServerApplicationPaths appPaths, IDirectoryService directoryService, ILibraryManager libraryManager) + public ItemResolveArgs(IServerApplicationPaths appPaths, ILibraryManager libraryManager) { _appPaths = appPaths; - DirectoryService = directoryService; _libraryManager = libraryManager; } - // TODO remove dependencies as properties, they should be injected where it makes sense - public IDirectoryService DirectoryService { get; } - /// <summary> /// Gets or sets the file system children. /// </summary> diff --git a/tests/Jellyfin.Server.Implementations.Tests/Library/EpisodeResolverTest.cs b/tests/Jellyfin.Server.Implementations.Tests/Library/EpisodeResolverTest.cs index 65f19a051a..6d0ed7bbba 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Library/EpisodeResolverTest.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Library/EpisodeResolverTest.cs @@ -22,10 +22,9 @@ namespace Jellyfin.Server.Implementations.Tests.Library { var parent = new Folder { Name = "extras" }; - var episodeResolver = new EpisodeResolver(Mock.Of<ILogger<EpisodeResolver>>(), _namingOptions); + var episodeResolver = new EpisodeResolver(Mock.Of<ILogger<EpisodeResolver>>(), _namingOptions, Mock.Of<IDirectoryService>()); var itemResolveArgs = new ItemResolveArgs( Mock.Of<IServerApplicationPaths>(), - Mock.Of<IDirectoryService>(), null) { Parent = parent, @@ -46,10 +45,9 @@ namespace Jellyfin.Server.Implementations.Tests.Library // Have to create a mock because of moq proxies not being castable to a concrete implementation // https://github.com/jellyfin/jellyfin/blob/ab0cff8556403e123642dc9717ba778329554634/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs#L48 - var episodeResolver = new EpisodeResolverMock(Mock.Of<ILogger<EpisodeResolver>>(), _namingOptions); + var episodeResolver = new EpisodeResolverMock(Mock.Of<ILogger<EpisodeResolver>>(), _namingOptions, Mock.Of<IDirectoryService>()); var itemResolveArgs = new ItemResolveArgs( Mock.Of<IServerApplicationPaths>(), - Mock.Of<IDirectoryService>(), null) { Parent = series, @@ -64,7 +62,7 @@ namespace Jellyfin.Server.Implementations.Tests.Library private sealed class EpisodeResolverMock : EpisodeResolver { - public EpisodeResolverMock(ILogger<EpisodeResolver> logger, NamingOptions namingOptions) : base(logger, namingOptions) + public EpisodeResolverMock(ILogger<EpisodeResolver> logger, NamingOptions namingOptions, IDirectoryService directoryService) : base(logger, namingOptions, directoryService) { } diff --git a/tests/Jellyfin.Server.Implementations.Tests/Library/MovieResolverTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Library/MovieResolverTests.cs index f96bd6138e..aed584355c 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Library/MovieResolverTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Library/MovieResolverTests.cs @@ -18,10 +18,9 @@ public class MovieResolverTests [Fact] public void Resolve_GivenLocalAlternateVersion_ResolvesToVideo() { - var movieResolver = new MovieResolver(Mock.Of<IImageProcessor>(), Mock.Of<ILogger<MovieResolver>>(), _namingOptions); + var movieResolver = new MovieResolver(Mock.Of<IImageProcessor>(), Mock.Of<ILogger<MovieResolver>>(), _namingOptions, Mock.Of<IDirectoryService>()); var itemResolveArgs = new ItemResolveArgs( Mock.Of<IServerApplicationPaths>(), - Mock.Of<IDirectoryService>(), null) { Parent = null, -- cgit v1.2.3 From 361fff3a0c1b2e5c14e991d53f5736e909b889b6 Mon Sep 17 00:00:00 2001 From: Joe Rogers <1337joe@gmail.com> Date: Mon, 6 Mar 2023 23:27:21 -0500 Subject: Fix cases where multiple files are resolved as a single book --- Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs | 3 ++- .../Library/AudioResolverTests.cs | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs index 69e9057984..a74f824752 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs @@ -192,7 +192,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio continue; } - if (resolvedItem.Files.Count == 0) + // Until multi-part books are handled letting files stack hides them from browsing in the client + if (resolvedItem.Files.Count == 0 || resolvedItem.Extras.Count > 0 || resolvedItem.AlternateVersions.Count > 0) { continue; } diff --git a/tests/Jellyfin.Server.Implementations.Tests/Library/AudioResolverTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Library/AudioResolverTests.cs index ca5829f10a..d136c1bc68 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Library/AudioResolverTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Library/AudioResolverTests.cs @@ -40,6 +40,9 @@ public class AudioResolverTests [InlineData("chapter 01 part 01.mp3", "chapter 01 part 02.mp3")] /* Mismatched chapters, parts, and named files. */ [InlineData("chapter 01.mp3", "part 2.mp3")] + [InlineData("book title.mp3", "chapter name.mp3")] // "book title" resolves as alternate version of book based on directory name + [InlineData("01 Content.mp3", "01 Credits.mp3")] // resolves as alternate versions of chapter 1 + [InlineData("Chapter Name.mp3", "Part 1.mp3")] public void Resolve_AudiobookDirectory_NoResult(params string[] children) { var resolved = TestResolveChildren("/parent/book title", children); -- cgit v1.2.3 From daefdaf8b0867d8f46056c80ecd19a94c5d7561e Mon Sep 17 00:00:00 2001 From: Shadowghost <Ghost_of_Stone@web.de> Date: Thu, 9 Mar 2023 14:13:57 +0100 Subject: Extend language code handling --- .../Localization/LocalizationManager.cs | 80 +++++++++++++--------- .../Globalization/ILocalizationManager.cs | 3 +- .../Localization/LocalizationManagerTests.cs | 4 +- 3 files changed, 53 insertions(+), 34 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs index 6e2a33fd56..166b71b4a3 100644 --- a/Emby.Server.Implementations/Localization/LocalizationManager.cs +++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs @@ -184,10 +184,19 @@ namespace Emby.Server.Implementations.Localization /// <inheritdoc /> public IEnumerable<ParentalRating> GetParentalRatings() { - var ratings = GetParentalRatingsDictionary().Values.ToList(); + // Use server default language for ratings + // Fall back to empty list if there are no parental ratings for that language + var ratings = GetParentalRatingsDictionary()?.Values.ToList() + ?? new List<ParentalRating>(); - // Add common ratings to ensure them being available for selection. + // Add common ratings to ensure them being available for selection // Based on the US rating system due to it being the main source of rating in the metadata providers + // Unrated + if (!ratings.Any(x => x.Value is null)) + { + ratings.Add(new ParentalRating("Unrated", null)); + } + // Minimum rating possible if (!ratings.Any(x => x.Value == 0)) { @@ -237,36 +246,26 @@ namespace Emby.Server.Implementations.Localization /// <summary> /// Gets the parental ratings dictionary. /// </summary> + /// <param name="countryCode">The optional two letter ISO language string.</param> /// <returns><see cref="Dictionary{String, ParentalRating}" />.</returns> - private Dictionary<string, ParentalRating> GetParentalRatingsDictionary() + private Dictionary<string, ParentalRating>? GetParentalRatingsDictionary(string? countryCode = null) { - var countryCode = _configurationManager.Configuration.MetadataCountryCode; - - // Fall back to US ratings if no country code is specified or country code does not exist. + // Fallback to server default if no country code is specified. if (string.IsNullOrEmpty(countryCode)) { - countryCode = "us"; + countryCode = _configurationManager.Configuration.MetadataCountryCode; } - return GetRatings(countryCode) - ?? GetRatings("us") - ?? throw new InvalidOperationException($"Invalid resource path: '{CountriesPath}'"); - } - - /// <summary> - /// Gets the ratings for a country. - /// </summary> - /// <param name="countryCode">The country code.</param> - /// <returns>The ratings.</returns> - private Dictionary<string, ParentalRating>? GetRatings(string countryCode) - { - _allParentalRatings.TryGetValue(countryCode, out var countryValue); + if (_allParentalRatings.TryGetValue(countryCode, out var countryValue)) + { + return countryValue; + } - return countryValue; + return null; } /// <inheritdoc /> - public int? GetRatingLevel(string rating) + public int? GetRatingLevel(string rating, string? countryCode = null) { ArgumentException.ThrowIfNullOrEmpty(rating); @@ -280,32 +279,51 @@ namespace Emby.Server.Implementations.Localization rating = rating.Replace("Rated :", string.Empty, StringComparison.OrdinalIgnoreCase); rating = rating.Replace("Rated ", string.Empty, StringComparison.OrdinalIgnoreCase); - var ratingsDictionary = GetParentalRatingsDictionary(); - - if (ratingsDictionary.TryGetValue(rating, out ParentalRating? value)) + // Use rating system matching the language + if (!string.IsNullOrEmpty(countryCode)) + { + var ratingsDictionary = GetParentalRatingsDictionary(countryCode); + if (ratingsDictionary is not null && ratingsDictionary.TryGetValue(rating, out ParentalRating? value)) + { + return value.Value; + } + } + else { - return value.Value; + // Fall back to server default language for ratings check + // If it has no ratings, use the US ratings + var ratingsDictionary = GetParentalRatingsDictionary() ?? GetParentalRatingsDictionary("us"); + if (ratingsDictionary is not null && ratingsDictionary.TryGetValue(rating, out ParentalRating? value)) + { + return value.Value; + } } - // If we don't find anything check all ratings systems + // If we don't find anything, check all ratings systems foreach (var dictionary in _allParentalRatings.Values) { - if (dictionary.TryGetValue(rating, out value)) + if (dictionary.TryGetValue(rating, out var value)) { return value.Value; } } - // Try splitting by : to handle "Germany: FSK 18" + // Try splitting by : to handle "Germany: FSK-18" if (rating.Contains(':', StringComparison.OrdinalIgnoreCase)) { return GetRatingLevel(rating.AsSpan().RightPart(':').ToString()); } - // Remove prefix country code to handle "DE-18" + // Handle prefix country code to handle "DE-18" if (rating.Contains('-', StringComparison.OrdinalIgnoreCase)) { - return GetRatingLevel(rating.AsSpan().RightPart('-').ToString()); + var ratingSpan = rating.AsSpan(); + + // Extract culture from country prefix + var culture = FindLanguageInfo(ratingSpan.LeftPart('-').ToString()); + + // Check rating system of culture + return GetRatingLevel(ratingSpan.RightPart('-').ToString(), culture?.TwoLetterISOLanguageName); } return null; diff --git a/MediaBrowser.Model/Globalization/ILocalizationManager.cs b/MediaBrowser.Model/Globalization/ILocalizationManager.cs index e00157dce9..02a29e7faf 100644 --- a/MediaBrowser.Model/Globalization/ILocalizationManager.cs +++ b/MediaBrowser.Model/Globalization/ILocalizationManager.cs @@ -30,8 +30,9 @@ namespace MediaBrowser.Model.Globalization /// Gets the rating level. /// </summary> /// <param name="rating">The rating.</param> + /// <param name="countryCode">The optional two letter ISO language string.</param> /// <returns><see cref="int" /> or <c>null</c>.</returns> - int? GetRatingLevel(string rating); + int? GetRatingLevel(string rating, string? countryCode = null); /// <summary> /// Gets the localized string. diff --git a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs index ab3682ccf6..7fabe99045 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs @@ -83,7 +83,7 @@ namespace Jellyfin.Server.Implementations.Tests.Localization await localizationManager.LoadAll(); var ratings = localizationManager.GetParentalRatings().ToList(); - Assert.Equal(53, ratings.Count); + Assert.Equal(54, ratings.Count); var tvma = ratings.FirstOrDefault(x => x.Name.Equals("TV-MA", StringComparison.Ordinal)); Assert.NotNull(tvma); @@ -100,7 +100,7 @@ namespace Jellyfin.Server.Implementations.Tests.Localization await localizationManager.LoadAll(); var ratings = localizationManager.GetParentalRatings().ToList(); - Assert.Equal(18, ratings.Count); + Assert.Equal(19, ratings.Count); var fsk = ratings.FirstOrDefault(x => x.Name.Equals("FSK-12", StringComparison.Ordinal)); Assert.NotNull(fsk); -- cgit v1.2.3 From 6b0135d03b757600bf9066d9ddd9bc423b470192 Mon Sep 17 00:00:00 2001 From: Róbert Örn Ketilsson <robertornk@outlook.com> Date: Wed, 8 Mar 2023 16:42:27 +0000 Subject: Translated using Weblate (Icelandic) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/is/ --- Emby.Server.Implementations/Localization/Core/is.json | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/is.json b/Emby.Server.Implementations/Localization/Core/is.json index b262a8b424..a40f495061 100644 --- a/Emby.Server.Implementations/Localization/Core/is.json +++ b/Emby.Server.Implementations/Localization/Core/is.json @@ -107,5 +107,14 @@ "TasksApplicationCategory": "Forrit", "TasksLibraryCategory": "Miðlasafn", "TasksMaintenanceCategory": "Viðhald", - "Default": "Sjálfgefið" + "Default": "Sjálfgefið", + "TaskCleanActivityLog": "Hreinsa athafnaskrá", + "TaskRefreshPeople": "Endurnýja fólk", + "TaskDownloadMissingSubtitles": "Sækja texta sem vantar", + "TaskOptimizeDatabase": "Fínstilla gagnagrunn", + "Undefined": "Óskilgreint", + "TaskCleanLogsDescription": "Eyðir færslu skrám sem eru meira en {0} gömul.", + "TaskCleanLogs": "Hreinsa færslu skrá", + "TaskDownloadMissingSubtitlesDescription": "Leitar á netinu að texta sem vantar miðað við uppsetningu lýsigagna.", + "HearingImpaired": "Heyrnarskertur" } -- cgit v1.2.3 From b96420b7865538439c6a96305d70fa4f16dfd591 Mon Sep 17 00:00:00 2001 From: SenorSmartyPants <senorsmartypants@gmail.com> Date: Sun, 26 Feb 2023 13:32:46 -0600 Subject: Clean Extra Names - Adds regular expression to CleanStrings to remove suffix style extra naming from the name presented in JF. - Override Resolve for Extras to enable parsename - remove exclusion on parsename of extratypes --- Emby.Naming/Common/NamingOptions.cs | 3 ++- Emby.Naming/Video/VideoResolver.cs | 3 +-- .../Library/Resolvers/ExtraResolver.cs | 11 +++++++++-- 3 files changed, 12 insertions(+), 5 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Naming/Common/NamingOptions.cs b/Emby.Naming/Common/NamingOptions.cs index e9161a6b7b..1406c64435 100644 --- a/Emby.Naming/Common/NamingOptions.cs +++ b/Emby.Naming/Common/NamingOptions.cs @@ -157,7 +157,8 @@ namespace Emby.Naming.Common @"^(?<cleaned>.+?)(\[.*\])", @"^\s*(?<cleaned>.+?)\WE[0-9]+(-|~)E?[0-9]+(\W|$)", @"^\s*\[[^\]]+\](?!\.\w+$)\s*(?<cleaned>.+)", - @"^\s*(?<cleaned>.+?)\s+-\s+[0-9]+\s*$" + @"^\s*(?<cleaned>.+?)\s+-\s+[0-9]+\s*$", + @"^\s*(?<cleaned>.+?)(([-._ ](trailer|sample))|-(scene|clip|behindthescenes|deleted|deletedscene|featurette|short|interview|other|extra))$" }; SubtitleFileExtensions = new[] diff --git a/Emby.Naming/Video/VideoResolver.cs b/Emby.Naming/Video/VideoResolver.cs index 858e9dd2f5..db5bfdbf94 100644 --- a/Emby.Naming/Video/VideoResolver.cs +++ b/Emby.Naming/Video/VideoResolver.cs @@ -87,8 +87,7 @@ namespace Emby.Naming.Video name = cleanDateTimeResult.Name; year = cleanDateTimeResult.Year; - if (extraResult.ExtraType is null - && TryCleanString(name, namingOptions, out var newName)) + if (TryCleanString(name, namingOptions, out var newName)) { name = newName; } diff --git a/Emby.Server.Implementations/Library/Resolvers/ExtraResolver.cs b/Emby.Server.Implementations/Library/Resolvers/ExtraResolver.cs index 0b255f673b..b4791b9456 100644 --- a/Emby.Server.Implementations/Library/Resolvers/ExtraResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/ExtraResolver.cs @@ -4,6 +4,7 @@ using System.IO; using Emby.Naming.Common; using Emby.Naming.Video; using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Resolvers; using MediaBrowser.Model.Entities; @@ -15,7 +16,7 @@ namespace Emby.Server.Implementations.Library.Resolvers /// <summary> /// Resolves a Path into a Video or Video subclass. /// </summary> - internal class ExtraResolver + internal class ExtraResolver : BaseVideoResolver<Video> { private readonly NamingOptions _namingOptions; private readonly IItemResolver[] _trailerResolvers; @@ -28,10 +29,16 @@ namespace Emby.Server.Implementations.Library.Resolvers /// <param name="namingOptions">An instance of <see cref="NamingOptions"/>.</param> /// <param name="directoryService">The directory service.</param> public ExtraResolver(ILogger<ExtraResolver> logger, NamingOptions namingOptions, IDirectoryService directoryService) + : base(logger, namingOptions, directoryService) { _namingOptions = namingOptions; _trailerResolvers = new IItemResolver[] { new GenericVideoResolver<Trailer>(logger, namingOptions, directoryService) }; - _videoResolvers = new IItemResolver[] { new GenericVideoResolver<Video>(logger, namingOptions, directoryService) }; + _videoResolvers = new IItemResolver[] { this }; + } + + protected override Video Resolve(ItemResolveArgs args) + { + return ResolveVideo<Video>(args, true); } /// <summary> -- cgit v1.2.3 From 76ae599bd3ccfd9808f50dbf5d935aa430783e60 Mon Sep 17 00:00:00 2001 From: Shadowghost <Ghost_of_Stone@web.de> Date: Fri, 10 Mar 2023 17:46:59 +0100 Subject: Fix playlist creation and removal --- .../Playlists/PlaylistManager.cs | 46 +++++++++++++++++----- Jellyfin.Api/Controllers/UserController.cs | 8 +++- MediaBrowser.Controller/Entities/IHasShares.cs | 11 ------ MediaBrowser.Controller/Entities/Share.cs | 13 ------ .../Playlists/IPlaylistManager.cs | 8 ++++ MediaBrowser.Controller/Playlists/Playlist.cs | 1 + .../Parsers/BaseItemXmlParser.cs | 16 ++++++++ MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs | 20 ++++++---- MediaBrowser.Model/Entities/IHasShares.cs | 12 ++++++ MediaBrowser.Model/Entities/Share.cs | 17 ++++++++ .../Playlists/PlaylistCreationRequest.cs | 39 ++++++++++++------ 11 files changed, 137 insertions(+), 54 deletions(-) delete mode 100644 MediaBrowser.Controller/Entities/IHasShares.cs delete mode 100644 MediaBrowser.Controller/Entities/Share.cs create mode 100644 MediaBrowser.Model/Entities/IHasShares.cs create mode 100644 MediaBrowser.Model/Entities/Share.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Playlists/PlaylistManager.cs b/Emby.Server.Implementations/Playlists/PlaylistManager.cs index 2717c392b2..8cba652e7b 100644 --- a/Emby.Server.Implementations/Playlists/PlaylistManager.cs +++ b/Emby.Server.Implementations/Playlists/PlaylistManager.cs @@ -135,16 +135,8 @@ namespace Emby.Server.Implementations.Playlists { Name = name, Path = path, - Shares = new[] - { - new Share - { - UserId = options.UserId.Equals(default) - ? null - : options.UserId.ToString("N", CultureInfo.InvariantCulture), - CanEdit = true - } - } + OwnerUserId = options.UserId, + Shares = options.Shares }; playlist.SetMediaType(options.MediaType); @@ -537,5 +529,39 @@ namespace Emby.Server.Implementations.Playlists return _libraryManager.RootFolder.Children.OfType<Folder>().FirstOrDefault(i => string.Equals(i.GetType().Name, TypeName, StringComparison.Ordinal)) ?? _libraryManager.GetUserRootFolder().Children.OfType<Folder>().FirstOrDefault(i => string.Equals(i.GetType().Name, TypeName, StringComparison.Ordinal)); } + + public async Task RemovePlaylists(Guid userId) + { + var playlists = GetPlaylists(userId); + foreach (var playlist in playlists) + { + // Update owner if shared + var rankedShares = playlist.Shares.OrderByDescending(x => x.CanEdit).ToArray(); + if (rankedShares.Length > 0 && Guid.TryParse(rankedShares[0].UserId, out var guid)) + { + playlist.OwnerUserId = guid; + playlist.Shares = rankedShares.Skip(1).ToArray(); + await playlist.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); + + if (playlist.IsFile) + { + SavePlaylistFile(playlist); + } + } + else + { + // Remove playlist if not shared + _libraryManager.DeleteItem( + playlist, + new DeleteOptions + { + DeleteFileLocation = false, + DeleteFromExternalProvider = false + }, + playlist.GetParent(), + false); + } + } + } } } diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs index b0973b8a14..09bf895505 100644 --- a/Jellyfin.Api/Controllers/UserController.cs +++ b/Jellyfin.Api/Controllers/UserController.cs @@ -15,6 +15,7 @@ using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Net; +using MediaBrowser.Controller.Playlists; using MediaBrowser.Controller.QuickConnect; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Configuration; @@ -41,6 +42,7 @@ public class UserController : BaseJellyfinApiController private readonly IServerConfigurationManager _config; private readonly ILogger _logger; private readonly IQuickConnect _quickConnectManager; + private readonly IPlaylistManager _playlistManager; /// <summary> /// Initializes a new instance of the <see cref="UserController"/> class. @@ -53,6 +55,7 @@ public class UserController : BaseJellyfinApiController /// <param name="config">Instance of the <see cref="IServerConfigurationManager"/> interface.</param> /// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param> /// <param name="quickConnectManager">Instance of the <see cref="IQuickConnect"/> interface.</param> + /// <param name="playlistManager">Instance of the <see cref="IPlaylistManager"/> interface.</param> public UserController( IUserManager userManager, ISessionManager sessionManager, @@ -61,7 +64,8 @@ public class UserController : BaseJellyfinApiController IAuthorizationContext authContext, IServerConfigurationManager config, ILogger<UserController> logger, - IQuickConnect quickConnectManager) + IQuickConnect quickConnectManager, + IPlaylistManager playlistManager) { _userManager = userManager; _sessionManager = sessionManager; @@ -71,6 +75,7 @@ public class UserController : BaseJellyfinApiController _config = config; _logger = logger; _quickConnectManager = quickConnectManager; + _playlistManager = playlistManager; } /// <summary> @@ -153,6 +158,7 @@ public class UserController : BaseJellyfinApiController } await _sessionManager.RevokeUserTokens(user.Id, null).ConfigureAwait(false); + await _playlistManager.RemovePlaylists(userId).ConfigureAwait(false); await _userManager.DeleteUserAsync(userId).ConfigureAwait(false); return NoContent(); } diff --git a/MediaBrowser.Controller/Entities/IHasShares.cs b/MediaBrowser.Controller/Entities/IHasShares.cs deleted file mode 100644 index e6fa27703b..0000000000 --- a/MediaBrowser.Controller/Entities/IHasShares.cs +++ /dev/null @@ -1,11 +0,0 @@ -#nullable disable - -#pragma warning disable CA1819, CS1591 - -namespace MediaBrowser.Controller.Entities -{ - public interface IHasShares - { - Share[] Shares { get; set; } - } -} diff --git a/MediaBrowser.Controller/Entities/Share.cs b/MediaBrowser.Controller/Entities/Share.cs deleted file mode 100644 index 64f446eef2..0000000000 --- a/MediaBrowser.Controller/Entities/Share.cs +++ /dev/null @@ -1,13 +0,0 @@ -#nullable disable - -#pragma warning disable CS1591 - -namespace MediaBrowser.Controller.Entities -{ - public class Share - { - public string UserId { get; set; } - - public bool CanEdit { get; set; } - } -} diff --git a/MediaBrowser.Controller/Playlists/IPlaylistManager.cs b/MediaBrowser.Controller/Playlists/IPlaylistManager.cs index f6c5920709..201dadb3d2 100644 --- a/MediaBrowser.Controller/Playlists/IPlaylistManager.cs +++ b/MediaBrowser.Controller/Playlists/IPlaylistManager.cs @@ -56,5 +56,13 @@ namespace MediaBrowser.Controller.Playlists /// <param name="newIndex">The new index.</param> /// <returns>Task.</returns> Task MoveItemAsync(string playlistId, string entryId, int newIndex); + + /// <summary> + /// Removed all playlists of a user. + /// If the playlist is shared, ownership is transferred. + /// </summary> + /// <param name="userId">The user id.</param> + /// <returns>Task.</returns> + Task RemovePlaylists(Guid userId); } } diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs index e6bcc9ea85..2ed05a32c0 100644 --- a/MediaBrowser.Controller/Playlists/Playlist.cs +++ b/MediaBrowser.Controller/Playlists/Playlist.cs @@ -15,6 +15,7 @@ using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; using MediaBrowser.Model.Querying; namespace MediaBrowser.Controller.Playlists diff --git a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs index c8912807ea..a01490c96c 100644 --- a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs +++ b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs @@ -8,6 +8,7 @@ using System.Threading; using System.Xml; using Jellyfin.Extensions; using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Playlists; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using Microsoft.Extensions.Logging; @@ -636,6 +637,21 @@ namespace MediaBrowser.LocalMetadata.Parsers break; } + case "OwnerUserId": + { + var val = reader.ReadElementContentAsString(); + + if (Guid.TryParse(val, out var guid) && !guid.Equals(Guid.Empty)) + { + if (item is Playlist playlist) + { + playlist.OwnerUserId = guid; + } + } + + break; + } + case "Format3D": { var val = reader.ReadElementContentAsString(); diff --git a/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs index d92b504740..1f9c562bac 100644 --- a/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs +++ b/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs @@ -395,6 +395,7 @@ namespace MediaBrowser.LocalMetadata.Savers if (item is Playlist playlist && !Playlist.IsPlaylistFile(playlist.Path)) { + await writer.WriteElementStringAsync(null, "OwnerUserId", null, playlist.OwnerUserId.ToString("N")).ConfigureAwait(false); await AddLinkedChildren(playlist, writer, "PlaylistItems", "PlaylistItem").ConfigureAwait(false); } @@ -418,16 +419,19 @@ namespace MediaBrowser.LocalMetadata.Savers foreach (var share in item.Shares) { - await writer.WriteStartElementAsync(null, "Share", null).ConfigureAwait(false); + if (share.UserId is not null) + { + await writer.WriteStartElementAsync(null, "Share", null).ConfigureAwait(false); - await writer.WriteElementStringAsync(null, "UserId", null, share.UserId).ConfigureAwait(false); - await writer.WriteElementStringAsync( - null, - "CanEdit", - null, - share.CanEdit.ToString(CultureInfo.InvariantCulture).ToLowerInvariant()).ConfigureAwait(false); + await writer.WriteElementStringAsync(null, "UserId", null, share.UserId).ConfigureAwait(false); + await writer.WriteElementStringAsync( + null, + "CanEdit", + null, + share.CanEdit.ToString(CultureInfo.InvariantCulture).ToLowerInvariant()).ConfigureAwait(false); - await writer.WriteEndElementAsync().ConfigureAwait(false); + await writer.WriteEndElementAsync().ConfigureAwait(false); + } } await writer.WriteEndElementAsync().ConfigureAwait(false); diff --git a/MediaBrowser.Model/Entities/IHasShares.cs b/MediaBrowser.Model/Entities/IHasShares.cs new file mode 100644 index 0000000000..b34d1a0376 --- /dev/null +++ b/MediaBrowser.Model/Entities/IHasShares.cs @@ -0,0 +1,12 @@ +namespace MediaBrowser.Model.Entities; + +/// <summary> +/// Interface for access to shares. +/// </summary> +public interface IHasShares +{ + /// <summary> + /// Gets or sets the shares. + /// </summary> + Share[] Shares { get; set; } +} diff --git a/MediaBrowser.Model/Entities/Share.cs b/MediaBrowser.Model/Entities/Share.cs new file mode 100644 index 0000000000..186aad1892 --- /dev/null +++ b/MediaBrowser.Model/Entities/Share.cs @@ -0,0 +1,17 @@ +namespace MediaBrowser.Model.Entities; + +/// <summary> +/// Class to hold data on sharing permissions. +/// </summary> +public class Share +{ + /// <summary> + /// Gets or sets the user id. + /// </summary> + public string? UserId { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether the user has edit permissions. + /// </summary> + public bool CanEdit { get; set; } +} diff --git a/MediaBrowser.Model/Playlists/PlaylistCreationRequest.cs b/MediaBrowser.Model/Playlists/PlaylistCreationRequest.cs index e8ee494034..8472697164 100644 --- a/MediaBrowser.Model/Playlists/PlaylistCreationRequest.cs +++ b/MediaBrowser.Model/Playlists/PlaylistCreationRequest.cs @@ -1,19 +1,36 @@ -#nullable disable -#pragma warning disable CS1591 - using System; using System.Collections.Generic; +using MediaBrowser.Model.Entities; + +namespace MediaBrowser.Model.Playlists; -namespace MediaBrowser.Model.Playlists +/// <summary> +/// A playlist creation request. +/// </summary> +public class PlaylistCreationRequest { - public class PlaylistCreationRequest - { - public string Name { get; set; } + /// <summary> + /// Gets or sets the name. + /// </summary> + public string? Name { get; set; } + + /// <summary> + /// Gets or sets the list of items. + /// </summary> + public IReadOnlyList<Guid> ItemIdList { get; set; } = Array.Empty<Guid>(); - public IReadOnlyList<Guid> ItemIdList { get; set; } = Array.Empty<Guid>(); + /// <summary> + /// Gets or sets the media type. + /// </summary> + public string? MediaType { get; set; } - public string MediaType { get; set; } + /// <summary> + /// Gets or sets the user id. + /// </summary> + public Guid UserId { get; set; } - public Guid UserId { get; set; } - } + /// <summary> + /// Gets or sets the shares. + /// </summary> + public Share[]? Shares { get; set; } } -- cgit v1.2.3 From 8d158df67856bbb9de696f5caa0ec475b172ced2 Mon Sep 17 00:00:00 2001 From: Shadowghost <Ghost_of_Stone@web.de> Date: Fri, 10 Mar 2023 19:16:57 +0100 Subject: Add migration to properly set playlist owner --- .../Playlists/PlaylistManager.cs | 14 +++++ Jellyfin.Server/Migrations/MigrationRunner.cs | 3 +- .../Migrations/Routines/FixPlaylistOwner.cs | 67 ++++++++++++++++++++++ .../Playlists/IPlaylistManager.cs | 7 +++ 4 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 Jellyfin.Server/Migrations/Routines/FixPlaylistOwner.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Playlists/PlaylistManager.cs b/Emby.Server.Implementations/Playlists/PlaylistManager.cs index 8cba652e7b..c15c2c4008 100644 --- a/Emby.Server.Implementations/Playlists/PlaylistManager.cs +++ b/Emby.Server.Implementations/Playlists/PlaylistManager.cs @@ -563,5 +563,19 @@ namespace Emby.Server.Implementations.Playlists } } } + + public async Task UpdatePlaylist(Playlist playlist) + { + var currentPlaylist = (Playlist)_libraryManager.GetItemById(playlist.Id); + currentPlaylist.OwnerUserId = playlist.OwnerUserId; + currentPlaylist.Shares = playlist.Shares; + + await playlist.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); + + if (playlist.IsFile) + { + SavePlaylistFile(currentPlaylist); + } + } } } diff --git a/Jellyfin.Server/Migrations/MigrationRunner.cs b/Jellyfin.Server/Migrations/MigrationRunner.cs index d4bf81f10b..866262d22f 100644 --- a/Jellyfin.Server/Migrations/MigrationRunner.cs +++ b/Jellyfin.Server/Migrations/MigrationRunner.cs @@ -40,7 +40,8 @@ namespace Jellyfin.Server.Migrations typeof(Routines.ReaddDefaultPluginRepository), typeof(Routines.MigrateDisplayPreferencesDb), typeof(Routines.RemoveDownloadImagesInAdvance), - typeof(Routines.MigrateAuthenticationDb) + typeof(Routines.MigrateAuthenticationDb), + typeof(Routines.FixPlaylistOwner) }; /// <summary> diff --git a/Jellyfin.Server/Migrations/Routines/FixPlaylistOwner.cs b/Jellyfin.Server/Migrations/Routines/FixPlaylistOwner.cs new file mode 100644 index 0000000000..d5736fd3c3 --- /dev/null +++ b/Jellyfin.Server/Migrations/Routines/FixPlaylistOwner.cs @@ -0,0 +1,67 @@ +using System; +using System.Linq; + +using Jellyfin.Data.Enums; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Playlists; +using Microsoft.Extensions.Logging; + +namespace Jellyfin.Server.Migrations.Routines; + +/// <summary> +/// Properly set playlist owner. +/// </summary> +internal class FixPlaylistOwner : IMigrationRoutine +{ + private readonly ILogger<RemoveDuplicateExtras> _logger; + private readonly ILibraryManager _libraryManager; + private readonly IPlaylistManager _playlistManager; + + public FixPlaylistOwner( + ILogger<RemoveDuplicateExtras> logger, + ILibraryManager libraryManager, + IPlaylistManager playlistManager) + { + _logger = logger; + _libraryManager = libraryManager; + _playlistManager = playlistManager; + } + + /// <inheritdoc/> + public Guid Id => Guid.Parse("{615DFA9E-2497-4DBB-A472-61938B752C5B}"); + + /// <inheritdoc/> + public string Name => "FixPlaylistOwner"; + + /// <inheritdoc/> + public bool PerformOnNewInstall => false; + + /// <inheritdoc/> + public void Perform() + { + var playlists = _libraryManager.GetItemList(new InternalItemsQuery + { + IncludeItemTypes = new[] { BaseItemKind.Playlist } + }) + .Cast<Playlist>() + .Where(x => x.OwnerUserId.Equals(Guid.Empty)) + .ToArray(); + + if (playlists.Length > 0) + { + foreach (var playlist in playlists) + { + var shares = playlist.Shares; + var firstEditShare = shares.First(x => x.CanEdit); + if (firstEditShare is not null && Guid.TryParse(firstEditShare.UserId, out var guid)) + { + playlist.OwnerUserId = guid; + playlist.Shares = shares.Where(x => x != firstEditShare).ToArray(); + + _playlistManager.UpdatePlaylist(playlist).GetAwaiter().GetResult(); + } + } + } + } +} diff --git a/MediaBrowser.Controller/Playlists/IPlaylistManager.cs b/MediaBrowser.Controller/Playlists/IPlaylistManager.cs index 201dadb3d2..22f974420f 100644 --- a/MediaBrowser.Controller/Playlists/IPlaylistManager.cs +++ b/MediaBrowser.Controller/Playlists/IPlaylistManager.cs @@ -64,5 +64,12 @@ namespace MediaBrowser.Controller.Playlists /// <param name="userId">The user id.</param> /// <returns>Task.</returns> Task RemovePlaylists(Guid userId); + + /// <summary> + /// Updates a playlist. + /// </summary> + /// <param name="playlist">The updated playlist.</param> + /// <returns>Task.</returns> + Task UpdatePlaylist(Playlist playlist); } } -- cgit v1.2.3 From af611367c14e2d56e6346b03bda7d75ab6f04b96 Mon Sep 17 00:00:00 2001 From: gitteric <eschewe@gmx.net> Date: Sun, 12 Mar 2023 16:45:48 +0100 Subject: Fall back to using "logo" attrib if "tvg-logo" is mssing in M3U-tuner (#9475) Co-authored-by: gitteric <you@example.com> --- Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs index 046be7c5c7..f2020e05fe 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs @@ -122,9 +122,13 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts var attributes = ParseExtInf(extInf, out string remaining); extInf = remaining; - if (attributes.TryGetValue("tvg-logo", out string value)) + if (attributes.TryGetValue("tvg-logo", out string tvgLogo)) { - channel.ImageUrl = value; + channel.ImageUrl = tvgLogo; + } + else if (attributes.TryGetValue("logo", out string logo)) + { + channel.ImageUrl = logo; } if (attributes.TryGetValue("group-title", out string groupTitle)) -- cgit v1.2.3 From d8ec3a5470fe602fab356c37720d38190aa713ef Mon Sep 17 00:00:00 2001 From: Bond_009 <bond.009@outlook.com> Date: Wed, 1 Mar 2023 18:57:23 +0100 Subject: Reduce usage of GetAwaiter().GetResult() --- .../Channels/ChannelManager.cs | 24 +++++++++------- .../EntryPoints/LibraryChangedNotifier.cs | 18 ++++++++---- .../EntryPoints/UserDataChangeNotifier.cs | 13 +++++---- .../Library/UserViewManager.cs | 4 +-- .../LiveTv/LiveTvManager.cs | 32 +++++++++++----------- Jellyfin.Api/Controllers/ChannelsController.cs | 6 ++-- Jellyfin.Api/Controllers/LiveTvController.cs | 10 +++---- .../ActivityLogWebSocketListener.cs | 4 +-- .../Channels/IChannelManager.cs | 4 +-- MediaBrowser.Controller/LiveTv/ILiveTvManager.cs | 4 +-- MediaBrowser.Model/Dlna/StreamBuilder.cs | 23 +++++----------- .../Plugins/StudioImages/StudiosImageProvider.cs | 2 +- 12 files changed, 74 insertions(+), 70 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs index 1e3c4dea14..961e225e9e 100644 --- a/Emby.Server.Implementations/Channels/ChannelManager.cs +++ b/Emby.Server.Implementations/Channels/ChannelManager.cs @@ -157,16 +157,16 @@ namespace Emby.Server.Implementations.Channels } /// <inheritdoc /> - public QueryResult<Channel> GetChannelsInternal(ChannelQuery query) + public async Task<QueryResult<Channel>> GetChannelsInternalAsync(ChannelQuery query) { var user = query.UserId.Equals(default) ? null : _userManager.GetUserById(query.UserId); - var channels = GetAllChannels() - .Select(GetChannelEntity) + var channels = await GetAllChannelEntitiesAsync() .OrderBy(i => i.SortName) - .ToList(); + .ToListAsync() + .ConfigureAwait(false); if (query.IsRecordingsFolder.HasValue) { @@ -226,6 +226,7 @@ namespace Emby.Server.Implementations.Channels if (user is not null) { + var userId = user.Id.ToString("N", CultureInfo.InvariantCulture); channels = channels.Where(i => { if (!i.IsVisible(user)) @@ -235,7 +236,7 @@ namespace Emby.Server.Implementations.Channels try { - return GetChannelProvider(i).IsEnabledFor(user.Id.ToString("N", CultureInfo.InvariantCulture)); + return GetChannelProvider(i).IsEnabledFor(userId); } catch { @@ -258,7 +259,7 @@ namespace Emby.Server.Implementations.Channels { foreach (var item in all) { - RefreshLatestChannelItems(GetChannelProvider(item), CancellationToken.None).GetAwaiter().GetResult(); + await RefreshLatestChannelItems(GetChannelProvider(item), CancellationToken.None).ConfigureAwait(false); } } @@ -269,13 +270,13 @@ namespace Emby.Server.Implementations.Channels } /// <inheritdoc /> - public QueryResult<BaseItemDto> GetChannels(ChannelQuery query) + public async Task<QueryResult<BaseItemDto>> GetChannelsAsync(ChannelQuery query) { var user = query.UserId.Equals(default) ? null : _userManager.GetUserById(query.UserId); - var internalResult = GetChannelsInternal(query); + var internalResult = await GetChannelsInternalAsync(query).ConfigureAwait(false); var dtoOptions = new DtoOptions(); @@ -327,9 +328,12 @@ namespace Emby.Server.Implementations.Channels progress.Report(100); } - private Channel GetChannelEntity(IChannel channel) + private async IAsyncEnumerable<Channel> GetAllChannelEntitiesAsync() { - return GetChannel(GetInternalChannelId(channel.Name)) ?? GetChannel(channel, CancellationToken.None).GetAwaiter().GetResult(); + foreach (IChannel channel in GetAllChannels()) + { + yield return GetChannel(GetInternalChannelId(channel.Name)) ?? await GetChannel(channel, CancellationToken.None).ConfigureAwait(false); + } } private MediaSourceInfo[] GetSavedMediaSources(BaseItem item) diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs index 05d0a9b794..2e3988f9eb 100644 --- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs @@ -276,25 +276,31 @@ namespace Emby.Server.Implementations.EntryPoints /// Libraries the update timer callback. /// </summary> /// <param name="state">The state.</param> - private void LibraryUpdateTimerCallback(object state) + private async void LibraryUpdateTimerCallback(object state) { + List<Folder> foldersAddedTo; + List<Folder> foldersRemovedFrom; + List<BaseItem> itemsUpdated; + List<BaseItem> itemsAdded; + List<BaseItem> itemsRemoved; lock (_libraryChangedSyncLock) { // Remove dupes in case some were saved multiple times - var foldersAddedTo = _foldersAddedTo + foldersAddedTo = _foldersAddedTo .DistinctBy(x => x.Id) .ToList(); - var foldersRemovedFrom = _foldersRemovedFrom + foldersRemovedFrom = _foldersRemovedFrom .DistinctBy(x => x.Id) .ToList(); - var itemsUpdated = _itemsUpdated + itemsUpdated = _itemsUpdated .Where(i => !_itemsAdded.Contains(i)) .DistinctBy(x => x.Id) .ToList(); - SendChangeNotifications(_itemsAdded.ToList(), itemsUpdated, _itemsRemoved.ToList(), foldersAddedTo, foldersRemovedFrom, CancellationToken.None).GetAwaiter().GetResult(); + itemsAdded = _itemsAdded.ToList(); + itemsRemoved = _itemsRemoved.ToList(); if (LibraryUpdateTimer is not null) { @@ -308,6 +314,8 @@ namespace Emby.Server.Implementations.EntryPoints _foldersAddedTo.Clear(); _foldersRemovedFrom.Clear(); } + + await SendChangeNotifications(itemsAdded, itemsUpdated, itemsRemoved, foldersAddedTo, foldersRemovedFrom, CancellationToken.None).ConfigureAwait(false); } /// <summary> diff --git a/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs b/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs index e724618b3a..d32759017d 100644 --- a/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs @@ -87,29 +87,30 @@ namespace Emby.Server.Implementations.EntryPoints } } - private void UpdateTimerCallback(object? state) + private async void UpdateTimerCallback(object? state) { + List<KeyValuePair<Guid, List<BaseItem>>> changes; lock (_syncLock) { // Remove dupes in case some were saved multiple times - var changes = _changedItems.ToList(); + changes = _changedItems.ToList(); _changedItems.Clear(); - SendNotifications(changes, CancellationToken.None).GetAwaiter().GetResult(); - if (_updateTimer is not null) { _updateTimer.Dispose(); _updateTimer = null; } } + + await SendNotifications(changes, CancellationToken.None).ConfigureAwait(false); } private async Task SendNotifications(List<KeyValuePair<Guid, List<BaseItem>>> changes, CancellationToken cancellationToken) { - foreach (var pair in changes) + foreach ((var key, var value) in changes) { - await SendNotifications(pair.Key, pair.Value, cancellationToken).ConfigureAwait(false); + await SendNotifications(key, value, cancellationToken).ConfigureAwait(false); } } diff --git a/Emby.Server.Implementations/Library/UserViewManager.cs b/Emby.Server.Implementations/Library/UserViewManager.cs index 0e2d34d39c..17f1d1905f 100644 --- a/Emby.Server.Implementations/Library/UserViewManager.cs +++ b/Emby.Server.Implementations/Library/UserViewManager.cs @@ -111,10 +111,10 @@ namespace Emby.Server.Implementations.Library if (query.IncludeExternalContent) { - var channelResult = _channelManager.GetChannelsInternal(new ChannelQuery + var channelResult = _channelManager.GetChannelsInternalAsync(new ChannelQuery { UserId = query.UserId - }); + }).GetAwaiter().GetResult(); var channels = channelResult.Items; diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs index 4003468d0d..ee039ff0f7 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs @@ -1312,20 +1312,19 @@ namespace Emby.Server.Implementations.LiveTv return 7; } - private QueryResult<BaseItem> GetEmbyRecordings(RecordingQuery query, DtoOptions dtoOptions, User user) + private async Task<QueryResult<BaseItem>> GetEmbyRecordingsAsync(RecordingQuery query, DtoOptions dtoOptions, User user) { if (user is null) { return new QueryResult<BaseItem>(); } - var folderIds = GetRecordingFolders(user, true) - .Select(i => i.Id) - .ToList(); + var folders = await GetRecordingFoldersAsync(user, true).ConfigureAwait(false); + var folderIds = Array.ConvertAll(folders, x => x.Id); var excludeItemTypes = new List<BaseItemKind>(); - if (folderIds.Count == 0) + if (folderIds.Length == 0) { return new QueryResult<BaseItem>(); } @@ -1392,7 +1391,7 @@ namespace Emby.Server.Implementations.LiveTv { MediaTypes = new[] { MediaType.Video }, Recursive = true, - AncestorIds = folderIds.ToArray(), + AncestorIds = folderIds, IsFolder = false, IsVirtualItem = false, Limit = limit, @@ -1528,7 +1527,7 @@ namespace Emby.Server.Implementations.LiveTv } } - public QueryResult<BaseItemDto> GetRecordings(RecordingQuery query, DtoOptions options) + public async Task<QueryResult<BaseItemDto>> GetRecordingsAsync(RecordingQuery query, DtoOptions options) { var user = query.UserId.Equals(default) ? null @@ -1536,7 +1535,7 @@ namespace Emby.Server.Implementations.LiveTv RemoveFields(options); - var internalResult = GetEmbyRecordings(query, options, user); + var internalResult = await GetEmbyRecordingsAsync(query, options, user).ConfigureAwait(false); var returnArray = _dtoService.GetBaseItemDtos(internalResult.Items, options, user); @@ -2379,12 +2378,11 @@ namespace Emby.Server.Implementations.LiveTv return _tvDtoService.GetInternalProgramId(externalId); } - public List<BaseItem> GetRecordingFolders(User user) - { - return GetRecordingFolders(user, false); - } + /// <inheritdoc /> + public Task<BaseItem[]> GetRecordingFoldersAsync(User user) + => GetRecordingFoldersAsync(user, false); - private List<BaseItem> GetRecordingFolders(User user, bool refreshChannels) + private async Task<BaseItem[]> GetRecordingFoldersAsync(User user, bool refreshChannels) { var folders = EmbyTV.EmbyTV.Current.GetRecordingFolders() .SelectMany(i => i.Locations) @@ -2396,14 +2394,16 @@ namespace Emby.Server.Implementations.LiveTv .OrderBy(i => i.SortName) .ToList(); - folders.AddRange(_channelManager.GetChannelsInternal(new MediaBrowser.Model.Channels.ChannelQuery + var channels = await _channelManager.GetChannelsInternalAsync(new MediaBrowser.Model.Channels.ChannelQuery { UserId = user.Id, IsRecordingsFolder = true, RefreshLatestChannelItems = refreshChannels - }).Items); + }).ConfigureAwait(false); + + folders.AddRange(channels.Items); - return folders.Cast<BaseItem>().ToList(); + return folders.Cast<BaseItem>().ToArray(); } } } diff --git a/Jellyfin.Api/Controllers/ChannelsController.cs b/Jellyfin.Api/Controllers/ChannelsController.cs index b5c4d83462..11c4ac3768 100644 --- a/Jellyfin.Api/Controllers/ChannelsController.cs +++ b/Jellyfin.Api/Controllers/ChannelsController.cs @@ -52,7 +52,7 @@ public class ChannelsController : BaseJellyfinApiController /// <returns>An <see cref="OkResult"/> containing the channels.</returns> [HttpGet] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult<QueryResult<BaseItemDto>> GetChannels( + public async Task<ActionResult<QueryResult<BaseItemDto>>> GetChannels( [FromQuery] Guid? userId, [FromQuery] int? startIndex, [FromQuery] int? limit, @@ -61,7 +61,7 @@ public class ChannelsController : BaseJellyfinApiController [FromQuery] bool? isFavorite) { userId = RequestHelpers.GetUserId(User, userId); - return _channelManager.GetChannels(new ChannelQuery + return await _channelManager.GetChannelsAsync(new ChannelQuery { Limit = limit, StartIndex = startIndex, @@ -69,7 +69,7 @@ public class ChannelsController : BaseJellyfinApiController SupportsLatestItems = supportsLatestItems, SupportsMediaDeletion = supportsMediaDeletion, IsFavorite = isFavorite - }); + }).ConfigureAwait(false); } /// <summary> diff --git a/Jellyfin.Api/Controllers/LiveTvController.cs b/Jellyfin.Api/Controllers/LiveTvController.cs index 96fc91f93c..267ba4afb4 100644 --- a/Jellyfin.Api/Controllers/LiveTvController.cs +++ b/Jellyfin.Api/Controllers/LiveTvController.cs @@ -252,7 +252,7 @@ public class LiveTvController : BaseJellyfinApiController [HttpGet("Recordings")] [ProducesResponseType(StatusCodes.Status200OK)] [Authorize(Policy = Policies.LiveTvAccess)] - public ActionResult<QueryResult<BaseItemDto>> GetRecordings( + public async Task<ActionResult<QueryResult<BaseItemDto>>> GetRecordings( [FromQuery] string? channelId, [FromQuery] Guid? userId, [FromQuery] int? startIndex, @@ -278,7 +278,7 @@ public class LiveTvController : BaseJellyfinApiController .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); - return _liveTvManager.GetRecordings( + return await _liveTvManager.GetRecordingsAsync( new RecordingQuery { ChannelId = channelId, @@ -299,7 +299,7 @@ public class LiveTvController : BaseJellyfinApiController ImageTypeLimit = imageTypeLimit, EnableImages = enableImages }, - dtoOptions); + dtoOptions).ConfigureAwait(false); } /// <summary> @@ -383,13 +383,13 @@ public class LiveTvController : BaseJellyfinApiController [HttpGet("Recordings/Folders")] [ProducesResponseType(StatusCodes.Status200OK)] [Authorize(Policy = Policies.LiveTvAccess)] - public ActionResult<QueryResult<BaseItemDto>> GetRecordingFolders([FromQuery] Guid? userId) + public async Task<ActionResult<QueryResult<BaseItemDto>>> GetRecordingFolders([FromQuery] Guid? userId) { userId = RequestHelpers.GetUserId(User, userId); var user = userId.Value.Equals(default) ? null : _userManager.GetUserById(userId.Value); - var folders = _liveTvManager.GetRecordingFolders(user); + var folders = await _liveTvManager.GetRecordingFoldersAsync(user).ConfigureAwait(false); var returnArray = _dtoService.GetBaseItemDtos(folders, new DtoOptions(), user); diff --git a/Jellyfin.Api/WebSocketListeners/ActivityLogWebSocketListener.cs b/Jellyfin.Api/WebSocketListeners/ActivityLogWebSocketListener.cs index 3eac814199..4a5e0ecd4f 100644 --- a/Jellyfin.Api/WebSocketListeners/ActivityLogWebSocketListener.cs +++ b/Jellyfin.Api/WebSocketListeners/ActivityLogWebSocketListener.cs @@ -56,8 +56,8 @@ public class ActivityLogWebSocketListener : BasePeriodicWebSocketListener<Activi base.Dispose(dispose); } - private void OnEntryCreated(object? sender, GenericEventArgs<ActivityLogEntry> e) + private async void OnEntryCreated(object? sender, GenericEventArgs<ActivityLogEntry> e) { - SendData(true).GetAwaiter().GetResult(); + await SendData(true).ConfigureAwait(false); } } diff --git a/MediaBrowser.Controller/Channels/IChannelManager.cs b/MediaBrowser.Controller/Channels/IChannelManager.cs index e392a3493e..8eb27888ab 100644 --- a/MediaBrowser.Controller/Channels/IChannelManager.cs +++ b/MediaBrowser.Controller/Channels/IChannelManager.cs @@ -46,14 +46,14 @@ namespace MediaBrowser.Controller.Channels /// </summary> /// <param name="query">The query.</param> /// <returns>The channels.</returns> - QueryResult<Channel> GetChannelsInternal(ChannelQuery query); + Task<QueryResult<Channel>> GetChannelsInternalAsync(ChannelQuery query); /// <summary> /// Gets the channels. /// </summary> /// <param name="query">The query.</param> /// <returns>The channels.</returns> - QueryResult<BaseItemDto> GetChannels(ChannelQuery query); + Task<QueryResult<BaseItemDto>> GetChannelsAsync(ChannelQuery query); /// <summary> /// Gets the latest channel items. diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs index 46bdca3027..3b6a16dee3 100644 --- a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs +++ b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs @@ -97,7 +97,7 @@ namespace MediaBrowser.Controller.LiveTv /// <param name="query">The query.</param> /// <param name="options">The options.</param> /// <returns>A recording.</returns> - QueryResult<BaseItemDto> GetRecordings(RecordingQuery query, DtoOptions options); + Task<QueryResult<BaseItemDto>> GetRecordingsAsync(RecordingQuery query, DtoOptions options); /// <summary> /// Gets the timers. @@ -308,6 +308,6 @@ namespace MediaBrowser.Controller.LiveTv void AddInfoToRecordingDto(BaseItem item, BaseItemDto dto, ActiveRecordingInfo activeRecordingInfo, User user = null); - List<BaseItem> GetRecordingFolders(User user); + Task<BaseItem[]> GetRecordingFoldersAsync(User user); } } diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index 6f99bbc13e..84c8c012c1 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -44,32 +44,24 @@ namespace MediaBrowser.Model.Dlna { ValidateMediaOptions(options, false); - var mediaSources = new List<MediaSourceInfo>(); + var streams = new List<StreamInfo>(); foreach (var mediaSource in options.MediaSources) { - if (string.IsNullOrEmpty(options.MediaSourceId) - || string.Equals(mediaSource.Id, options.MediaSourceId, StringComparison.OrdinalIgnoreCase)) + if (!(string.IsNullOrEmpty(options.MediaSourceId) + || string.Equals(mediaSource.Id, options.MediaSourceId, StringComparison.OrdinalIgnoreCase))) { - mediaSources.Add(mediaSource); + continue; } - } - var streams = new List<StreamInfo>(); - foreach (var mediaSourceInfo in mediaSources) - { - StreamInfo? streamInfo = GetOptimalAudioStream(mediaSourceInfo, options); + StreamInfo? streamInfo = GetOptimalAudioStream(mediaSource, options); if (streamInfo is not null) { + streamInfo.DeviceId = options.DeviceId; + streamInfo.DeviceProfileId = options.Profile.Id; streams.Add(streamInfo); } } - foreach (var stream in streams) - { - stream.DeviceId = options.DeviceId; - stream.DeviceProfileId = options.Profile.Id; - } - return GetOptimalStream(streams, options.GetMaxBitrate(true) ?? 0); } @@ -399,7 +391,6 @@ namespace MediaBrowser.Model.Dlna return (null, null, GetTranscodeReasonsFromDirectPlayProfile(item, null, audioStream, options.Profile.DirectPlayProfiles)); } - var playMethods = new List<PlayMethod>(); TranscodeReason transcodeReasons = 0; // The profile describes what the device supports diff --git a/MediaBrowser.Providers/Plugins/StudioImages/StudiosImageProvider.cs b/MediaBrowser.Providers/Plugins/StudioImages/StudiosImageProvider.cs index 0fb9d30a62..ae244da19b 100644 --- a/MediaBrowser.Providers/Plugins/StudioImages/StudiosImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/StudioImages/StudiosImageProvider.cs @@ -53,7 +53,7 @@ namespace MediaBrowser.Providers.Plugins.StudioImages /// <inheritdoc /> public IEnumerable<ImageType> GetSupportedImages(BaseItem item) { - return new List<ImageType> + return new ImageType[] { ImageType.Thumb }; -- cgit v1.2.3 From 30556d8fc598779d862de34a45068591b5e62eeb Mon Sep 17 00:00:00 2001 From: Gabriele Bizzon <gbizzon@hotmail.com> Date: Wed, 15 Mar 2023 16:32:44 +0000 Subject: Translated using Weblate (Portuguese) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/pt/ --- Emby.Server.Implementations/Localization/Core/pt.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/pt.json b/Emby.Server.Implementations/Localization/Core/pt.json index 39229f45f9..92e0d34aec 100644 --- a/Emby.Server.Implementations/Localization/Core/pt.json +++ b/Emby.Server.Implementations/Localization/Core/pt.json @@ -121,5 +121,6 @@ "TaskOptimizeDatabase": "Otimizar base de dados", "TaskOptimizeDatabaseDescription": "Base de dados compacta e corta espaço livre. A execução desta tarefa depois de digitalizar a biblioteca ou de fazer outras alterações que impliquem modificações na base de dados pode melhorar o desempenho.", "External": "Externo", - "HearingImpaired": "Problemas auditivos" + "HearingImpaired": "Problemas auditivos", + "TaskKeyframeExtractor": "Extrator de quadro-chave" } -- cgit v1.2.3 From 82080bd1ef056153fd54e05fb9decbf5f5830572 Mon Sep 17 00:00:00 2001 From: Shadowghost <Shadowghost@users.noreply.github.com> Date: Sun, 12 Mar 2023 19:42:18 +0100 Subject: Apply review suggestions --- Emby.Server.Implementations/Playlists/PlaylistManager.cs | 6 +++--- Jellyfin.Api/Controllers/UserController.cs | 2 +- Jellyfin.Server/Migrations/Routines/FixPlaylistOwner.cs | 2 +- MediaBrowser.Controller/Playlists/IPlaylistManager.cs | 4 ++-- MediaBrowser.Controller/Playlists/Playlist.cs | 6 +++--- 5 files changed, 10 insertions(+), 10 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Playlists/PlaylistManager.cs b/Emby.Server.Implementations/Playlists/PlaylistManager.cs index c15c2c4008..853daf7247 100644 --- a/Emby.Server.Implementations/Playlists/PlaylistManager.cs +++ b/Emby.Server.Implementations/Playlists/PlaylistManager.cs @@ -530,7 +530,7 @@ namespace Emby.Server.Implementations.Playlists _libraryManager.GetUserRootFolder().Children.OfType<Folder>().FirstOrDefault(i => string.Equals(i.GetType().Name, TypeName, StringComparison.Ordinal)); } - public async Task RemovePlaylists(Guid userId) + public async Task RemovePlaylistsAsync(Guid userId) { var playlists = GetPlaylists(userId); foreach (var playlist in playlists) @@ -564,7 +564,7 @@ namespace Emby.Server.Implementations.Playlists } } - public async Task UpdatePlaylist(Playlist playlist) + public async Task UpdatePlaylistAsync(Playlist playlist) { var currentPlaylist = (Playlist)_libraryManager.GetItemById(playlist.Id); currentPlaylist.OwnerUserId = playlist.OwnerUserId; @@ -572,7 +572,7 @@ namespace Emby.Server.Implementations.Playlists await playlist.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); - if (playlist.IsFile) + if (currentPlaylist.IsFile) { SavePlaylistFile(currentPlaylist); } diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs index 09bf895505..e495288670 100644 --- a/Jellyfin.Api/Controllers/UserController.cs +++ b/Jellyfin.Api/Controllers/UserController.cs @@ -158,7 +158,7 @@ public class UserController : BaseJellyfinApiController } await _sessionManager.RevokeUserTokens(user.Id, null).ConfigureAwait(false); - await _playlistManager.RemovePlaylists(userId).ConfigureAwait(false); + await _playlistManager.RemovePlaylistsAsync(userId).ConfigureAwait(false); await _userManager.DeleteUserAsync(userId).ConfigureAwait(false); return NoContent(); } diff --git a/Jellyfin.Server/Migrations/Routines/FixPlaylistOwner.cs b/Jellyfin.Server/Migrations/Routines/FixPlaylistOwner.cs index d5736fd3c3..55aadae79a 100644 --- a/Jellyfin.Server/Migrations/Routines/FixPlaylistOwner.cs +++ b/Jellyfin.Server/Migrations/Routines/FixPlaylistOwner.cs @@ -59,7 +59,7 @@ internal class FixPlaylistOwner : IMigrationRoutine playlist.OwnerUserId = guid; playlist.Shares = shares.Where(x => x != firstEditShare).ToArray(); - _playlistManager.UpdatePlaylist(playlist).GetAwaiter().GetResult(); + _playlistManager.UpdatePlaylistAsync(playlist).GetAwaiter().GetResult(); } } } diff --git a/MediaBrowser.Controller/Playlists/IPlaylistManager.cs b/MediaBrowser.Controller/Playlists/IPlaylistManager.cs index 22f974420f..d889436629 100644 --- a/MediaBrowser.Controller/Playlists/IPlaylistManager.cs +++ b/MediaBrowser.Controller/Playlists/IPlaylistManager.cs @@ -63,13 +63,13 @@ namespace MediaBrowser.Controller.Playlists /// </summary> /// <param name="userId">The user id.</param> /// <returns>Task.</returns> - Task RemovePlaylists(Guid userId); + Task RemovePlaylistsAsync(Guid userId); /// <summary> /// Updates a playlist. /// </summary> /// <param name="playlist">The updated playlist.</param> /// <returns>Task.</returns> - Task UpdatePlaylist(Playlist playlist); + Task UpdatePlaylistAsync(Playlist playlist); } } diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs index 2ed05a32c0..344e996ea8 100644 --- a/MediaBrowser.Controller/Playlists/Playlist.cs +++ b/MediaBrowser.Controller/Playlists/Playlist.cs @@ -233,7 +233,8 @@ namespace MediaBrowser.Controller.Playlists return base.IsVisible(user); } - if (user.Id.Equals(OwnerUserId)) + var userId = user.Id; + if (userId.Equals(OwnerUserId)) { return true; } @@ -241,10 +242,9 @@ namespace MediaBrowser.Controller.Playlists var shares = Shares; if (shares.Length == 0) { - return base.IsVisible(user); + return false; } - var userId = user.Id; return shares.Any(share => Guid.TryParse(share.UserId, out var id) && id.Equals(userId)); } -- cgit v1.2.3 From eba24d188d3ff9747e2c39281e3f64d5fdd53899 Mon Sep 17 00:00:00 2001 From: Shadowghost <Shadowghost@users.noreply.github.com> Date: Sat, 18 Mar 2023 08:25:34 +0100 Subject: Update Emby.Server.Implementations/Playlists/PlaylistManager.cs Co-authored-by: Joe Rogers <1337joe@users.noreply.github.com> --- Emby.Server.Implementations/Playlists/PlaylistManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Playlists/PlaylistManager.cs b/Emby.Server.Implementations/Playlists/PlaylistManager.cs index 853daf7247..4d1b454288 100644 --- a/Emby.Server.Implementations/Playlists/PlaylistManager.cs +++ b/Emby.Server.Implementations/Playlists/PlaylistManager.cs @@ -136,7 +136,7 @@ namespace Emby.Server.Implementations.Playlists Name = name, Path = path, OwnerUserId = options.UserId, - Shares = options.Shares + Shares = options.Shares ?? Array.Empty<Share>() }; playlist.SetMediaType(options.MediaType); -- cgit v1.2.3 From 31dfbfdbdf5621823adabbc9f7c3a238f2574068 Mon Sep 17 00:00:00 2001 From: Bas <weblate@hanka.mp> Date: Fri, 17 Mar 2023 10:38:50 +0000 Subject: Translated using Weblate (Dutch) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/nl/ --- Emby.Server.Implementations/Localization/Core/nl.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/nl.json b/Emby.Server.Implementations/Localization/Core/nl.json index 383096f7e2..01a2ab273f 100644 --- a/Emby.Server.Implementations/Localization/Core/nl.json +++ b/Emby.Server.Implementations/Localization/Core/nl.json @@ -114,7 +114,7 @@ "TasksApplicationCategory": "Toepassing", "TasksLibraryCategory": "Bibliotheek", "TasksMaintenanceCategory": "Onderhoud", - "TaskCleanActivityLogDescription": "Verwijdert activiteiten logs ouder dan de ingestelde leeftijd.", + "TaskCleanActivityLogDescription": "Verwijdert activiteitenlogs ouder dan de ingestelde leeftijd.", "TaskCleanActivityLog": "Activiteitenlogboek legen", "Undefined": "Niet gedefinieerd", "Forced": "Geforceerd", -- cgit v1.2.3 From 6a39882a70fed9f1333f7673292583c08f0904fc Mon Sep 17 00:00:00 2001 From: Àlex Bravo <alexbravobosch@gmail.com> Date: Sun, 19 Mar 2023 12:26:45 +0000 Subject: Translated using Weblate (Catalan) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ca/ --- .../Localization/Core/ca.json | 84 +++++++++++----------- 1 file changed, 42 insertions(+), 42 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ca.json b/Emby.Server.Implementations/Localization/Core/ca.json index 1966f69683..26290df4d8 100644 --- a/Emby.Server.Implementations/Localization/Core/ca.json +++ b/Emby.Server.Implementations/Localization/Core/ca.json @@ -5,7 +5,7 @@ "Artists": "Artistes", "AuthenticationSucceededWithUserName": "{0} s'ha autenticat correctament", "Books": "Llibres", - "CameraImageUploadedFrom": "S'ha pujat una nova imatge des de la camera desde {0}", + "CameraImageUploadedFrom": "S'ha pujat una nova imatge de càmera des de {0}", "Channels": "Canals", "ChapterNameValue": "Capítol {0}", "Collections": "Col·leccions", @@ -16,65 +16,65 @@ "Folders": "Carpetes", "Genres": "Gèneres", "HeaderAlbumArtists": "Artistes de l'àlbum", - "HeaderContinueWatching": "Continua Veient", - "HeaderFavoriteAlbums": "Àlbums Preferits", - "HeaderFavoriteArtists": "Artistes Predilectes", - "HeaderFavoriteEpisodes": "Episodis Predilectes", - "HeaderFavoriteShows": "Sèries Predilectes", - "HeaderFavoriteSongs": "Cançons Predilectes", - "HeaderLiveTV": "TV en Directe", + "HeaderContinueWatching": "Continuar veient", + "HeaderFavoriteAlbums": "Àlbums preferits", + "HeaderFavoriteArtists": "Artistes preferits", + "HeaderFavoriteEpisodes": "Episodis preferits", + "HeaderFavoriteShows": "Sèries preferides", + "HeaderFavoriteSongs": "Cançons preferides", + "HeaderLiveTV": "TV en directe", "HeaderNextUp": "A continuació", - "HeaderRecordingGroups": "Grups d'Enregistrament", - "HomeVideos": "Vídeos Domèstics", + "HeaderRecordingGroups": "Grups d'enregistrament", + "HomeVideos": "Vídeos domèstics", "Inherit": "Hereta", - "ItemAddedWithName": "{0} ha estat afegit a la biblioteca", - "ItemRemovedWithName": "{0} ha estat eliminat de la biblioteca", + "ItemAddedWithName": "{0} ha sigut afegit a la biblioteca", + "ItemRemovedWithName": "{0} ha sigut eliminat de la biblioteca", "LabelIpAddressValue": "Adreça IP: {0}", "LabelRunningTimeValue": "Temps en funcionament: {0}", - "Latest": "Darreres", - "MessageApplicationUpdated": "El Servidor de Jellyfin ha estat actualitzat", - "MessageApplicationUpdatedTo": "El Servidor de Jellyfin ha estat actualitzat a {0}", + "Latest": "Darrers", + "MessageApplicationUpdated": "El servidor de Jellyfin ha estat actualitzat", + "MessageApplicationUpdatedTo": "El servidor de Jellyfin ha estat actualitzat a {0}", "MessageNamedServerConfigurationUpdatedWithValue": "La secció {0} de la configuració del servidor ha estat actualitzada", "MessageServerConfigurationUpdated": "S'ha actualitzat la configuració del servidor", "MixedContent": "Contingut barrejat", "Movies": "Pel·lícules", "Music": "Música", - "MusicVideos": "Vídeos Musicals", + "MusicVideos": "Videoclips", "NameInstallFailed": "{0} instal·lació fallida", "NameSeasonNumber": "Temporada {0}", - "NameSeasonUnknown": "Temporada Desconeguda", - "NewVersionIsAvailable": "Una nova versió del Servidor Jellyfin està disponible per descarregar.", - "NotificationOptionApplicationUpdateAvailable": "Actualització d'aplicació disponible", - "NotificationOptionApplicationUpdateInstalled": "Actualització d'aplicació instal·lada", + "NameSeasonUnknown": "Temporada desconeguda", + "NewVersionIsAvailable": "Una nova versió del servidor de Jellyfin està disponible per a descarregar.", + "NotificationOptionApplicationUpdateAvailable": "Actualització de l'aplicació disponible", + "NotificationOptionApplicationUpdateInstalled": "Actualització de l'aplicació instal·lada", "NotificationOptionAudioPlayback": "Reproducció d'àudio iniciada", "NotificationOptionAudioPlaybackStopped": "Reproducció d'àudio aturada", "NotificationOptionCameraImageUploaded": "Imatge de càmera pujada", "NotificationOptionInstallationFailed": "Instal·lació fallida", "NotificationOptionNewLibraryContent": "Nou contingut afegit", - "NotificationOptionPluginError": "Un connector ha fallat", - "NotificationOptionPluginInstalled": "Connector instal·lat", - "NotificationOptionPluginUninstalled": "Connector desinstal·lat", - "NotificationOptionPluginUpdateInstalled": "Actualització de connector instal·lada", + "NotificationOptionPluginError": "Un complement ha fallat", + "NotificationOptionPluginInstalled": "Complement instal·lat", + "NotificationOptionPluginUninstalled": "Complement desinstal·lat", + "NotificationOptionPluginUpdateInstalled": "Actualització de complement instal·lada", "NotificationOptionServerRestartRequired": "Reinici del servidor requerit", "NotificationOptionTaskFailed": "Tasca programada fallida", - "NotificationOptionUserLockedOut": "Usuari tancat", - "NotificationOptionVideoPlayback": "Reproducció de video iniciada", - "NotificationOptionVideoPlaybackStopped": "Reproducció de video aturada", + "NotificationOptionUserLockedOut": "Usuari expulsat", + "NotificationOptionVideoPlayback": "Reproducció de vídeo iniciada", + "NotificationOptionVideoPlaybackStopped": "Reproducció de vídeo aturada", "Photos": "Fotos", "Playlists": "Llistes de reproducció", - "Plugin": "Connector", + "Plugin": "Complement", "PluginInstalledWithName": "{0} ha estat instal·lat", "PluginUninstalledWithName": "{0} ha estat desinstal·lat", "PluginUpdatedWithName": "{0} ha estat actualitzat", "ProviderValue": "Proveïdor: {0}", "ScheduledTaskFailedWithName": "{0} ha fallat", - "ScheduledTaskStartedWithName": "{0} iniciat", + "ScheduledTaskStartedWithName": "{0} s'ha iniciat", "ServerNameNeedsToBeRestarted": "{0} necessita ser reiniciat", "Shows": "Sèries", "Songs": "Cançons", - "StartupEmbyServerIsLoading": "El Servidor de Jellyfin està carregant. Si et plau, prova de nou ben aviat.", + "StartupEmbyServerIsLoading": "El servidor de Jellyfin s'està carregant. Proveu-ho altre cop aviat.", "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", - "SubtitleDownloadFailureFromForItem": "Els subtítols no s'han pogut baixar de {0} per {1}", + "SubtitleDownloadFailureFromForItem": "Els subtítols per a {1} no s'han pogut baixar de {0}", "Sync": "Sincronitzar", "System": "Sistema", "TvShows": "Sèries de TV", @@ -82,11 +82,11 @@ "UserCreatedWithName": "S'ha creat l'usuari {0}", "UserDeletedWithName": "L'usuari {0} ha estat eliminat", "UserDownloadingItemWithValues": "{0} està descarregant {1}", - "UserLockedOutWithName": "L'usuari {0} ha sigut tancat", + "UserLockedOutWithName": "L'usuari {0} ha sigut expulsat", "UserOfflineFromDevice": "{0} s'ha desconnectat de {1}", "UserOnlineFromDevice": "{0} està connectat des de {1}", "UserPasswordChangedWithName": "La contrasenya ha estat canviada per a l'usuari {0}", - "UserPolicyUpdatedWithName": "La política d'usuari s'ha actualitzat per {0}", + "UserPolicyUpdatedWithName": "La política d'usuari s'ha actualitzat per a {0}", "UserStartedPlayingItemWithValues": "{0} ha començat a reproduir {1}", "UserStoppedPlayingItemWithValues": "{0} ha parat de reproduir {1}", "ValueHasBeenAddedToLibrary": "{0} ha sigut afegit a la teva biblioteca", @@ -94,14 +94,14 @@ "VersionNumber": "Versió {0}", "TaskDownloadMissingSubtitlesDescription": "Cerca a internet els subtítols que faltin a partir de la configuració de metadades.", "TaskDownloadMissingSubtitles": "Descarrega els subtítols que faltin", - "TaskRefreshChannelsDescription": "Actualitza la informació dels canals d'Internet.", - "TaskRefreshChannels": "Actualitza Canals", - "TaskCleanTranscodeDescription": "Elimina els arxius temporals de transcodificacions que tinguin més d'un dia.", + "TaskRefreshChannelsDescription": "Actualitza la informació dels canals d'internet.", + "TaskRefreshChannels": "Actualitza els canals", + "TaskCleanTranscodeDescription": "Elimina els arxius de transcodificacions que tinguin més d'un dia.", "TaskCleanTranscode": "Neteja les transcodificacions", - "TaskUpdatePluginsDescription": "Actualitza les extensions que estan configurades per actualitzar-se automàticament.", - "TaskUpdatePlugins": "Actualitza les extensions", + "TaskUpdatePluginsDescription": "Actualitza els connectors que estan configurats per a actualitzar-se automàticament.", + "TaskUpdatePlugins": "Actualitza els connectors", "TaskRefreshPeopleDescription": "Actualitza les metadades dels actors i directors de la teva mediateca.", - "TaskRefreshPeople": "Actualitza Persones", + "TaskRefreshPeople": "Actualitza les persones", "TaskCleanLogsDescription": "Esborra els logs que tinguin més de {0} dies.", "TaskCleanLogs": "Neteja els registres", "TaskRefreshLibraryDescription": "Escaneja la mediateca buscant fitxers nous i refresca les metadades.", @@ -110,12 +110,12 @@ "TaskRefreshChapterImages": "Extreure les imatges dels capítols", "TaskCleanCacheDescription": "Elimina els arxius temporals que ja no són necessaris per al servidor.", "TaskCleanCache": "Elimina arxius temporals", - "TasksChannelsCategory": "Canals d'Internet", + "TasksChannelsCategory": "Canals d'internet", "TasksApplicationCategory": "Aplicació", "TasksLibraryCategory": "Biblioteca", "TasksMaintenanceCategory": "Manteniment", "TaskCleanActivityLogDescription": "Eliminat entrades del registre d'activitats mes antigues que l'antiguitat configurada.", - "TaskCleanActivityLog": "Buidar Registre d'Activitat", + "TaskCleanActivityLog": "Buidar el registre d'activitat", "Undefined": "Indefinit", "Forced": "Forçat", "Default": "Per defecte", @@ -124,5 +124,5 @@ "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.", "TaskKeyframeExtractor": "Extractor de fotogrames clau", "External": "Extern", - "HearingImpaired": "Discapacitat Auditiva" + "HearingImpaired": "Discapacitat auditiva" } -- cgit v1.2.3 From e9821b822083055f75b4661438b29e01a9706e89 Mon Sep 17 00:00:00 2001 From: azam <azamshul@gmail.com> Date: Tue, 21 Mar 2023 20:28:30 +0000 Subject: Translated using Weblate (Malay) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ms/ --- .../Localization/Core/ms.json | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ms.json b/Emby.Server.Implementations/Localization/Core/ms.json index 3d54a5a950..b2293e4b60 100644 --- a/Emby.Server.Implementations/Localization/Core/ms.json +++ b/Emby.Server.Implementations/Localization/Core/ms.json @@ -39,7 +39,7 @@ "MixedContent": "Kandungan campuran", "Movies": "Filem-filem", "Music": "Muzik", - "MusicVideos": "Video muzik", + "MusicVideos": "Video Muzik", "NameInstallFailed": "{0} pemasangan gagal", "NameSeasonNumber": "Musim {0}", "NameSeasonUnknown": "Musim Tidak Diketahui", @@ -55,7 +55,7 @@ "NotificationOptionPluginInstalled": "Plugin telah dipasang", "NotificationOptionPluginUninstalled": "Plugin telah dinyahpasang", "NotificationOptionPluginUpdateInstalled": "Kemaskini plugin telah dipasang", - "NotificationOptionServerRestartRequired": "", + "NotificationOptionServerRestartRequired": "Perlu mulakan semula server", "NotificationOptionTaskFailed": "Kegagalan tugas berjadual", "NotificationOptionUserLockedOut": "Pengguna telah dikunci", "NotificationOptionVideoPlayback": "Ulangmain video bermula", @@ -109,5 +109,20 @@ "TaskRefreshLibrary": "Imbas Perpustakaan Media", "TaskRefreshChapterImagesDescription": "Membuat gambaran kecil untuk video yang mempunyai bab.", "TaskRefreshChapterImages": "Ekstrak Gambar-gambar Bab", - "TaskCleanCacheDescription": "Menghapuskan fail cache yang tidak lagi diperlukan oleh sistem." + "TaskCleanCacheDescription": "Menghapuskan fail cache yang tidak lagi diperlukan oleh sistem.", + "HearingImpaired": "Lemah Pendengaran", + "TaskRefreshPeopleDescription": "Kemas kini metadata untuk pelakon dan pengarah di dalam perpustakaan media.", + "TaskUpdatePluginsDescription": "Muat turun dan kemas kini plugin yang dikonfigurasi secara automatik.", + "TaskDownloadMissingSubtitlesDescription": "Cari sari kata yang hilang di internet, berdasarkan konfigurasi metadata.", + "TaskOptimizeDatabaseDescription": "Mampatkan pangkalan data dan potong ruang kosong. Pelaksanaan tugas ini selepas pengimbasan perpustakaan boleh membantu membaiki prestasi.", + "TaskRefreshChannels": "Segarkan Saluran-saluran", + "TaskUpdatePlugins": "Kemas kini plugin", + "TaskDownloadMissingSubtitles": "Muat turn sari kata yang tiada", + "TaskCleanTranscodeDescription": "Padam fail transkod yang lebih lama dari satu hari.", + "TaskRefreshChannelsDescription": "Segarkan maklumat saluran internet.", + "TaskCleanTranscode": "Bersihkan direktori transkod", + "External": "Luaran", + "TaskOptimizeDatabase": "Optimumkan pangkalan data", + "TaskKeyframeExtractor": "Ekstrak bingkai kunci", + "TaskKeyframeExtractorDescription": "Ekstrak bingkai kunci dari fail video untuk membina HLS playlist yang lebih tepat. Tugas ini mungkin perlukan masa yang panjang." } -- cgit v1.2.3 From 93d43ffac174a8d9f59d4ae81805a3e0b58f2504 Mon Sep 17 00:00:00 2001 From: azam <azamshul@gmail.com> Date: Tue, 21 Mar 2023 18:10:32 +0000 Subject: Translated using Weblate (Japanese) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ja/ --- Emby.Server.Implementations/Localization/Core/ja.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ja.json b/Emby.Server.Implementations/Localization/Core/ja.json index 7f616c35ad..7b059c68ea 100644 --- a/Emby.Server.Implementations/Localization/Core/ja.json +++ b/Emby.Server.Implementations/Localization/Core/ja.json @@ -37,8 +37,8 @@ "MessageNamedServerConfigurationUpdatedWithValue": "サーバー設定項目の {0} が更新されました", "MessageServerConfigurationUpdated": "サーバー設定が更新されました", "MixedContent": "ミックスコンテンツ", - "Movies": "ムービー", - "Music": "ミュージック", + "Movies": "映画", + "Music": "音楽", "MusicVideos": "ミュージックビデオ", "NameInstallFailed": "{0}のインストールに失敗しました", "NameSeasonNumber": "シーズン {0}", -- cgit v1.2.3 From 7ffe44d70512ab51414a0207afe892d353f4ebf9 Mon Sep 17 00:00:00 2001 From: Calvin Ng <calvin.alexander.ng@gmail.com> Date: Thu, 23 Mar 2023 15:59:02 +0000 Subject: Translated using Weblate (Filipino) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/fil/ --- Emby.Server.Implementations/Localization/Core/fil.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/fil.json b/Emby.Server.Implementations/Localization/Core/fil.json index 99839ae6e8..01b3e95fc3 100644 --- a/Emby.Server.Implementations/Localization/Core/fil.json +++ b/Emby.Server.Implementations/Localization/Core/fil.json @@ -119,5 +119,9 @@ "Undefined": "Hindi tiyak", "Forced": "Sapilitan", "TaskOptimizeDatabaseDescription": "Iko-compact ang database at ita-truncate ang free space. Ang pagpapatakbo ng gawaing ito pagkatapos ng pag-scan sa library o paggawa ng iba pang mga pagbabago na nagpapahiwatig ng mga pagbabago sa database ay maaaring magpa-improve ng performance.", - "TaskOptimizeDatabase": "I-optimize ang database" + "TaskOptimizeDatabase": "I-optimize ang database", + "HearingImpaired": "Bingi", + "TaskKeyframeExtractor": "Tagabunot ng Keyframe", + "TaskKeyframeExtractorDescription": "Nagbubunot ng keyframe mula sa mga bidyo upang makabuo ng mas tumpak na HLS playlist. Maaaring matagal ito gawin.", + "External": "External" } -- cgit v1.2.3 From 9211a73e4011c0c610fdbcf24e0723a3552f22fa Mon Sep 17 00:00:00 2001 From: Shadowghost <Shadowghost@users.noreply.github.com> Date: Sat, 25 Mar 2023 18:41:09 +0100 Subject: Apply suggestions from code review Co-authored-by: Cody Robibero <cody@robibe.ro> --- Emby.Server.Implementations/Playlists/PlaylistManager.cs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Playlists/PlaylistManager.cs b/Emby.Server.Implementations/Playlists/PlaylistManager.cs index 4d1b454288..6176879b66 100644 --- a/Emby.Server.Implementations/Playlists/PlaylistManager.cs +++ b/Emby.Server.Implementations/Playlists/PlaylistManager.cs @@ -530,6 +530,7 @@ namespace Emby.Server.Implementations.Playlists _libraryManager.GetUserRootFolder().Children.OfType<Folder>().FirstOrDefault(i => string.Equals(i.GetType().Name, TypeName, StringComparison.Ordinal)); } + /// <inheritdoc /> public async Task RemovePlaylistsAsync(Guid userId) { var playlists = GetPlaylists(userId); @@ -564,6 +565,7 @@ namespace Emby.Server.Implementations.Playlists } } + /// <inheritdoc /> public async Task UpdatePlaylistAsync(Playlist playlist) { var currentPlaylist = (Playlist)_libraryManager.GetItemById(playlist.Id); -- cgit v1.2.3 From 89be3aa37f89fcd91c2b68da07b5827510394141 Mon Sep 17 00:00:00 2001 From: Cody Robibero <cody@robibe.ro> Date: Sat, 25 Mar 2023 11:52:02 -0600 Subject: Convert Person.Type to use PersonKind enum (#9487) --- Emby.Dlna/Didl/DidlBuilder.cs | 20 +++-- .../Data/SqliteItemRepository.cs | 7 +- Emby.Server.Implementations/Dto/DtoService.cs | 12 +-- .../LiveTv/EmbyTV/EmbyTV.cs | 8 +- Jellyfin.Data/Enums/PersonKind.cs | 97 ++++++++++++++++++++++ MediaBrowser.Controller/Entities/PeopleHelper.cs | 23 ++--- MediaBrowser.Controller/Entities/PersonInfo.cs | 9 +- .../Parsers/BaseItemXmlParser.cs | 17 ++-- MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs | 4 +- .../Probing/ProbeResultNormalizer.cs | 27 +++--- MediaBrowser.Model/Dto/BaseItemPerson.cs | 3 +- .../MediaInfo/AudioFileProber.cs | 7 +- .../Music/AlbumMetadataService.cs | 5 +- .../Plugins/Omdb/OmdbProvider.cs | 7 +- .../Plugins/Tmdb/Movies/TmdbMovieProvider.cs | 14 +--- .../Plugins/Tmdb/TV/TmdbEpisodeProvider.cs | 7 +- .../Plugins/Tmdb/TV/TmdbSeasonProvider.cs | 5 +- .../Plugins/Tmdb/TV/TmdbSeriesProvider.cs | 7 +- MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs | 21 +++-- MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs | 25 ++---- MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs | 15 ++-- .../Probing/ProbeResultNormalizerTests.cs | 11 +-- .../Parsers/EpisodeNfoProviderTests.cs | 7 +- .../Parsers/MovieNfoParserTests.cs | 9 +- .../Parsers/SeasonNfoProviderTests.cs | 3 +- .../Parsers/SeriesNfoParserTests.cs | 3 +- 26 files changed, 235 insertions(+), 138 deletions(-) create mode 100644 Jellyfin.Data/Enums/PersonKind.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Dlna/Didl/DidlBuilder.cs b/Emby.Dlna/Didl/DidlBuilder.cs index bea7a5a0da..f668dc829a 100644 --- a/Emby.Dlna/Didl/DidlBuilder.cs +++ b/Emby.Dlna/Didl/DidlBuilder.cs @@ -10,6 +10,7 @@ using System.Text; using System.Xml; using Emby.Dlna.ContentDirectory; using Jellyfin.Data.Entities; +using Jellyfin.Data.Enums; using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Entities; @@ -870,11 +871,11 @@ namespace Emby.Dlna.Didl var types = new[] { - PersonType.Director, - PersonType.Writer, - PersonType.Producer, - PersonType.Composer, - "creator" + PersonKind.Director, + PersonKind.Writer, + PersonKind.Producer, + PersonKind.Composer, + PersonKind.Creator }; // Seeing some LG models locking up due content with large lists of people @@ -888,10 +889,13 @@ namespace Emby.Dlna.Didl foreach (var actor in people) { - var type = types.FirstOrDefault(i => string.Equals(i, actor.Type, StringComparison.OrdinalIgnoreCase) || string.Equals(i, actor.Role, StringComparison.OrdinalIgnoreCase)) - ?? PersonType.Actor; + var type = types.FirstOrDefault(i => i == actor.Type || string.Equals(actor.Role, i.ToString(), StringComparison.OrdinalIgnoreCase)); + if (type == PersonKind.Unknown) + { + type = PersonKind.Actor; + } - AddValue(writer, "upnp", type.ToLowerInvariant(), actor.Name, NsUpnp); + AddValue(writer, "upnp", type.ToString().ToLowerInvariant(), actor.Name, NsUpnp); } } diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 3bf4d07c59..fcff5f98c6 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -5540,7 +5540,7 @@ AND Type = @InternalPersonType)"); statement.TryBind("@Name" + index, person.Name); statement.TryBind("@Role" + index, person.Role); - statement.TryBind("@PersonType" + index, person.Type); + statement.TryBind("@PersonType" + index, person.Type.ToString()); statement.TryBind("@SortOrder" + index, person.SortOrder); statement.TryBind("@ListOrder" + index, listIndex); @@ -5569,9 +5569,10 @@ AND Type = @InternalPersonType)"); item.Role = role; } - if (reader.TryGetString(3, out var type)) + if (reader.TryGetString(3, out var type) + && Enum.TryParse(type, true, out PersonKind personKind)) { - item.Type = type; + item.Type = personKind; } if (reader.TryGetInt32(4, out var sortOrder)) diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index 45270de893..8b66829032 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -523,32 +523,32 @@ namespace Emby.Server.Implementations.Dto var people = _libraryManager.GetPeople(item).OrderBy(i => i.SortOrder ?? int.MaxValue) .ThenBy(i => { - if (i.IsType(PersonType.Actor)) + if (i.IsType(PersonKind.Actor)) { return 0; } - if (i.IsType(PersonType.GuestStar)) + if (i.IsType(PersonKind.GuestStar)) { return 1; } - if (i.IsType(PersonType.Director)) + if (i.IsType(PersonKind.Director)) { return 2; } - if (i.IsType(PersonType.Writer)) + if (i.IsType(PersonKind.Writer)) { return 3; } - if (i.IsType(PersonType.Producer)) + if (i.IsType(PersonKind.Producer)) { return 4; } - if (i.IsType(PersonType.Composer)) + if (i.IsType(PersonKind.Composer)) { return 4; } diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 8edd8f66ae..e7f4d2f4eb 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -2032,7 +2032,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV var people = item.Id.Equals(default) ? new List<PersonInfo>() : _libraryManager.GetPeople(item); var directors = people - .Where(i => IsPersonType(i, PersonType.Director)) + .Where(i => i.IsType(PersonKind.Director)) .Select(i => i.Name) .ToList(); @@ -2042,7 +2042,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV } var writers = people - .Where(i => IsPersonType(i, PersonType.Writer)) + .Where(i => i.IsType(PersonKind.Writer)) .Select(i => i.Name) .Distinct(StringComparer.OrdinalIgnoreCase) .ToList(); @@ -2122,10 +2122,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV } } - private static bool IsPersonType(PersonInfo person, string type) - => string.Equals(person.Type, type, StringComparison.OrdinalIgnoreCase) - || string.Equals(person.Role, type, StringComparison.OrdinalIgnoreCase); - private LiveTvProgram GetProgramInfoFromCache(string programId) { var query = new InternalItemsQuery diff --git a/Jellyfin.Data/Enums/PersonKind.cs b/Jellyfin.Data/Enums/PersonKind.cs new file mode 100644 index 0000000000..10a8056669 --- /dev/null +++ b/Jellyfin.Data/Enums/PersonKind.cs @@ -0,0 +1,97 @@ +namespace Jellyfin.Data.Enums; + +/// <summary> +/// The person kind. +/// </summary> +public enum PersonKind +{ + /// <summary> + /// An unknown person kind. + /// </summary> + Unknown, + + /// <summary> + /// A person whose profession is acting on the stage, in films, or on television. + /// </summary> + Actor, + + /// <summary> + /// A person who supervises the actors and other staff in a film, play, or similar production. + /// </summary> + Director, + + /// <summary> + /// A person who writes music, especially as a professional occupation. + /// </summary> + Composer, + + /// <summary> + /// A writer of a book, article, or document. Can also be used as a generic term for music writer if there is a lack of specificity. + /// </summary> + Writer, + + /// <summary> + /// A well-known actor or other performer who appears in a work in which they do not have a regular role. + /// </summary> + GuestStar, + + /// <summary> + /// A person responsible for the financial and managerial aspects of the making of a film or broadcast or for staging a play, opera, etc. + /// </summary> + Producer, + + /// <summary> + /// A person who directs the performance of an orchestra or choir. + /// </summary> + Conductor, + + /// <summary> + /// A person who writes the words to a song or musical. + /// </summary> + Lyricist, + + /// <summary> + /// A person who adapts a musical composition for performance. + /// </summary> + Arranger, + + /// <summary> + /// An audio engineer who performed a general engineering role. + /// </summary> + Engineer, + + /// <summary> + /// An engineer responsible for using a mixing console to mix a recorded track into a single piece of music suitable for release. + /// </summary> + Mixer, + + /// <summary> + /// A person who remixed a recording by taking one or more other tracks, substantially altering them and mixing them together with other material. + /// </summary> + Remixer, + + /// <summary> + /// A person who created the material. + /// </summary> + Creator, + + /// <summary> + /// A person who was the artist. + /// </summary> + Artist, + + /// <summary> + /// A person who was the album artist. + /// </summary> + AlbumArtist, + + /// <summary> + /// A person who was the author. + /// </summary> + Author, + + /// <summary> + /// A person who was the illustrator. + /// </summary> + Illustrator, +} diff --git a/MediaBrowser.Controller/Entities/PeopleHelper.cs b/MediaBrowser.Controller/Entities/PeopleHelper.cs index 7f8dc069cf..5292bd7727 100644 --- a/MediaBrowser.Controller/Entities/PeopleHelper.cs +++ b/MediaBrowser.Controller/Entities/PeopleHelper.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Jellyfin.Data.Enums; using MediaBrowser.Model.Entities; namespace MediaBrowser.Controller.Entities @@ -17,38 +18,38 @@ namespace MediaBrowser.Controller.Entities // Normalize if (string.Equals(person.Role, PersonType.GuestStar, StringComparison.OrdinalIgnoreCase)) { - person.Type = PersonType.GuestStar; + person.Type = PersonKind.GuestStar; } else if (string.Equals(person.Role, PersonType.Director, StringComparison.OrdinalIgnoreCase)) { - person.Type = PersonType.Director; + person.Type = PersonKind.Director; } else if (string.Equals(person.Role, PersonType.Producer, StringComparison.OrdinalIgnoreCase)) { - person.Type = PersonType.Producer; + person.Type = PersonKind.Producer; } else if (string.Equals(person.Role, PersonType.Writer, StringComparison.OrdinalIgnoreCase)) { - person.Type = PersonType.Writer; + person.Type = PersonKind.Writer; } // If the type is GuestStar and there's already an Actor entry, then update it to avoid dupes - if (string.Equals(person.Type, PersonType.GuestStar, StringComparison.OrdinalIgnoreCase)) + if (person.Type == PersonKind.GuestStar) { - var existing = people.FirstOrDefault(p => p.Name.Equals(person.Name, StringComparison.OrdinalIgnoreCase) && p.Type.Equals(PersonType.Actor, StringComparison.OrdinalIgnoreCase)); + var existing = people.FirstOrDefault(p => p.Name.Equals(person.Name, StringComparison.OrdinalIgnoreCase) && p.Type == PersonKind.Actor); if (existing is not null) { - existing.Type = PersonType.GuestStar; + existing.Type = PersonKind.GuestStar; MergeExisting(existing, person); return; } } - if (string.Equals(person.Type, PersonType.Actor, StringComparison.OrdinalIgnoreCase)) + if (person.Type == PersonKind.Actor) { // If the actor already exists without a role and we have one, fill it in - var existing = people.FirstOrDefault(p => p.Name.Equals(person.Name, StringComparison.OrdinalIgnoreCase) && (p.Type.Equals(PersonType.Actor, StringComparison.OrdinalIgnoreCase) || p.Type.Equals(PersonType.GuestStar, StringComparison.OrdinalIgnoreCase))); + var existing = people.FirstOrDefault(p => p.Name.Equals(person.Name, StringComparison.OrdinalIgnoreCase) && (p.Type == PersonKind.Actor || p.Type == PersonKind.GuestStar)); if (existing is null) { // Wasn't there - add it @@ -68,8 +69,8 @@ namespace MediaBrowser.Controller.Entities else { var existing = people.FirstOrDefault(p => - string.Equals(p.Name, person.Name, StringComparison.OrdinalIgnoreCase) && - string.Equals(p.Type, person.Type, StringComparison.OrdinalIgnoreCase)); + string.Equals(p.Name, person.Name, StringComparison.OrdinalIgnoreCase) + && p.Type == person.Type); // Check for dupes based on the combination of Name and Type if (existing is null) diff --git a/MediaBrowser.Controller/Entities/PersonInfo.cs b/MediaBrowser.Controller/Entities/PersonInfo.cs index 2b689ae7e2..3df0b0b785 100644 --- a/MediaBrowser.Controller/Entities/PersonInfo.cs +++ b/MediaBrowser.Controller/Entities/PersonInfo.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using Jellyfin.Data.Enums; using MediaBrowser.Model.Entities; namespace MediaBrowser.Controller.Entities @@ -36,7 +37,7 @@ namespace MediaBrowser.Controller.Entities /// Gets or sets the type. /// </summary> /// <value>The type.</value> - public string Type { get; set; } + public PersonKind Type { get; set; } /// <summary> /// Gets or sets the ascending sort order. @@ -57,10 +58,6 @@ namespace MediaBrowser.Controller.Entities return Name; } - public bool IsType(string type) - { - return string.Equals(Type, type, StringComparison.OrdinalIgnoreCase) - || string.Equals(Role, type, StringComparison.OrdinalIgnoreCase); - } + public bool IsType(PersonKind type) => Type == type || string.Equals(type.ToString(), Role, StringComparison.OrdinalIgnoreCase); } } diff --git a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs index c8912807ea..09abd3c364 100644 --- a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs +++ b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Text; using System.Threading; using System.Xml; +using Jellyfin.Data.Enums; using Jellyfin.Extensions; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; @@ -370,7 +371,7 @@ namespace MediaBrowser.LocalMetadata.Parsers case "Director": { - foreach (var p in SplitNames(reader.ReadElementContentAsString()).Select(v => new PersonInfo { Name = v.Trim(), Type = PersonType.Director })) + foreach (var p in SplitNames(reader.ReadElementContentAsString()).Select(v => new PersonInfo { Name = v.Trim(), Type = PersonKind.Director })) { if (string.IsNullOrWhiteSpace(p.Name)) { @@ -385,7 +386,7 @@ namespace MediaBrowser.LocalMetadata.Parsers case "Writer": { - foreach (var p in SplitNames(reader.ReadElementContentAsString()).Select(v => new PersonInfo { Name = v.Trim(), Type = PersonType.Writer })) + foreach (var p in SplitNames(reader.ReadElementContentAsString()).Select(v => new PersonInfo { Name = v.Trim(), Type = PersonKind.Writer })) { if (string.IsNullOrWhiteSpace(p.Name)) { @@ -412,7 +413,7 @@ namespace MediaBrowser.LocalMetadata.Parsers else { // Old-style piped string - foreach (var p in SplitNames(actors).Select(v => new PersonInfo { Name = v.Trim(), Type = PersonType.Actor })) + foreach (var p in SplitNames(actors).Select(v => new PersonInfo { Name = v.Trim(), Type = PersonKind.Actor })) { if (string.IsNullOrWhiteSpace(p.Name)) { @@ -428,7 +429,7 @@ namespace MediaBrowser.LocalMetadata.Parsers case "GuestStars": { - foreach (var p in SplitNames(reader.ReadElementContentAsString()).Select(v => new PersonInfo { Name = v.Trim(), Type = PersonType.GuestStar })) + foreach (var p in SplitNames(reader.ReadElementContentAsString()).Select(v => new PersonInfo { Name = v.Trim(), Type = PersonKind.GuestStar })) { if (string.IsNullOrWhiteSpace(p.Name)) { @@ -1035,7 +1036,7 @@ namespace MediaBrowser.LocalMetadata.Parsers private IEnumerable<PersonInfo> GetPersonsFromXmlNode(XmlReader reader) { var name = string.Empty; - var type = PersonType.Actor; // If type is not specified assume actor + var type = PersonKind.Actor; // If type is not specified assume actor var role = string.Empty; int? sortOrder = null; @@ -1056,11 +1057,7 @@ namespace MediaBrowser.LocalMetadata.Parsers case "Type": { var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - type = val; - } + _ = Enum.TryParse(val, true, out type); break; } diff --git a/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs index d92b504740..0c016746b4 100644 --- a/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs +++ b/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs @@ -374,8 +374,8 @@ namespace MediaBrowser.LocalMetadata.Savers { await writer.WriteStartElementAsync(null, "Person", null).ConfigureAwait(false); await writer.WriteElementStringAsync(null, "Name", null, person.Name).ConfigureAwait(false); - await writer.WriteElementStringAsync(null, "Type", null, person.Type).ConfigureAwait(false); - await writer.WriteElementStringAsync(null, "Role", null, person.Role).ConfigureAwait(false); + await writer.WriteElementStringAsync(null, "Type", null, person.Type.ToString()).ConfigureAwait(false); + await writer.WriteElementStringAsync(null, "Role", null, person.Role.ToString()).ConfigureAwait(false); if (person.SortOrder.HasValue) { diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index cb482301fe..dce3f0e39d 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -9,6 +9,7 @@ using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Xml; +using Jellyfin.Data.Enums; using Jellyfin.Extensions; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Dto; @@ -507,7 +508,7 @@ namespace MediaBrowser.MediaEncoding.Probing peoples.Add(new BaseItemPerson { Name = pair.Value, - Type = PersonType.Writer + Type = PersonKind.Writer }); } } @@ -518,7 +519,7 @@ namespace MediaBrowser.MediaEncoding.Probing peoples.Add(new BaseItemPerson { Name = pair.Value, - Type = PersonType.Producer + Type = PersonKind.Producer }); } } @@ -529,7 +530,7 @@ namespace MediaBrowser.MediaEncoding.Probing peoples.Add(new BaseItemPerson { Name = pair.Value, - Type = PersonType.Director + Type = PersonKind.Director }); } } @@ -1163,7 +1164,7 @@ namespace MediaBrowser.MediaEncoding.Probing { foreach (var person in Split(composer, false)) { - people.Add(new BaseItemPerson { Name = person, Type = PersonType.Composer }); + people.Add(new BaseItemPerson { Name = person, Type = PersonKind.Composer }); } } @@ -1171,7 +1172,7 @@ namespace MediaBrowser.MediaEncoding.Probing { foreach (var person in Split(conductor, false)) { - people.Add(new BaseItemPerson { Name = person, Type = PersonType.Conductor }); + people.Add(new BaseItemPerson { Name = person, Type = PersonKind.Conductor }); } } @@ -1179,7 +1180,7 @@ namespace MediaBrowser.MediaEncoding.Probing { foreach (var person in Split(lyricist, false)) { - people.Add(new BaseItemPerson { Name = person, Type = PersonType.Lyricist }); + people.Add(new BaseItemPerson { Name = person, Type = PersonKind.Lyricist }); } } @@ -1195,7 +1196,7 @@ namespace MediaBrowser.MediaEncoding.Probing people.Add(new BaseItemPerson { Name = match.Groups["name"].Value, - Type = PersonType.Actor, + Type = PersonKind.Actor, Role = CultureInfo.InvariantCulture.TextInfo.ToTitleCase(match.Groups["instrument"].Value) }); } @@ -1207,7 +1208,7 @@ namespace MediaBrowser.MediaEncoding.Probing { foreach (var person in Split(writer, false)) { - people.Add(new BaseItemPerson { Name = person, Type = PersonType.Writer }); + people.Add(new BaseItemPerson { Name = person, Type = PersonKind.Writer }); } } @@ -1215,7 +1216,7 @@ namespace MediaBrowser.MediaEncoding.Probing { foreach (var person in Split(arranger, false)) { - people.Add(new BaseItemPerson { Name = person, Type = PersonType.Arranger }); + people.Add(new BaseItemPerson { Name = person, Type = PersonKind.Arranger }); } } @@ -1223,7 +1224,7 @@ namespace MediaBrowser.MediaEncoding.Probing { foreach (var person in Split(engineer, false)) { - people.Add(new BaseItemPerson { Name = person, Type = PersonType.Engineer }); + people.Add(new BaseItemPerson { Name = person, Type = PersonKind.Engineer }); } } @@ -1231,7 +1232,7 @@ namespace MediaBrowser.MediaEncoding.Probing { foreach (var person in Split(mixer, false)) { - people.Add(new BaseItemPerson { Name = person, Type = PersonType.Mixer }); + people.Add(new BaseItemPerson { Name = person, Type = PersonKind.Mixer }); } } @@ -1239,7 +1240,7 @@ namespace MediaBrowser.MediaEncoding.Probing { foreach (var person in Split(remixer, false)) { - people.Add(new BaseItemPerson { Name = person, Type = PersonType.Remixer }); + people.Add(new BaseItemPerson { Name = person, Type = PersonKind.Remixer }); } } @@ -1491,7 +1492,7 @@ namespace MediaBrowser.MediaEncoding.Probing { video.People = people.Split(new[] { ';', '/' }, StringSplitOptions.RemoveEmptyEntries) .Where(i => !string.IsNullOrWhiteSpace(i)) - .Select(i => new BaseItemPerson { Name = i.Trim(), Type = PersonType.Actor }) + .Select(i => new BaseItemPerson { Name = i.Trim(), Type = PersonKind.Actor }) .ToArray(); } diff --git a/MediaBrowser.Model/Dto/BaseItemPerson.cs b/MediaBrowser.Model/Dto/BaseItemPerson.cs index 9c65a2308d..d3bcf492d8 100644 --- a/MediaBrowser.Model/Dto/BaseItemPerson.cs +++ b/MediaBrowser.Model/Dto/BaseItemPerson.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Text.Json.Serialization; +using Jellyfin.Data.Enums; using MediaBrowser.Model.Entities; namespace MediaBrowser.Model.Dto @@ -33,7 +34,7 @@ namespace MediaBrowser.Model.Dto /// Gets or sets the type. /// </summary> /// <value>The type.</value> - public string Type { get; set; } + public PersonKind Type { get; set; } /// <summary> /// Gets or sets the primary image tag. diff --git a/MediaBrowser.Providers/MediaInfo/AudioFileProber.cs b/MediaBrowser.Providers/MediaInfo/AudioFileProber.cs index 19b594c1c8..b8578c46f8 100644 --- a/MediaBrowser.Providers/MediaInfo/AudioFileProber.cs +++ b/MediaBrowser.Providers/MediaInfo/AudioFileProber.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; +using Jellyfin.Data.Enums; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Library; @@ -163,7 +164,7 @@ namespace MediaBrowser.Providers.MediaInfo PeopleHelper.AddPerson(people, new PersonInfo { Name = albumArtist, - Type = "AlbumArtist" + Type = PersonKind.AlbumArtist }); } @@ -173,7 +174,7 @@ namespace MediaBrowser.Providers.MediaInfo PeopleHelper.AddPerson(people, new PersonInfo { Name = performer, - Type = "Artist" + Type = PersonKind.Artist }); } @@ -182,7 +183,7 @@ namespace MediaBrowser.Providers.MediaInfo PeopleHelper.AddPerson(people, new PersonInfo { Name = composer, - Type = "Composer" + Type = PersonKind.Composer }); } diff --git a/MediaBrowser.Providers/Music/AlbumMetadataService.cs b/MediaBrowser.Providers/Music/AlbumMetadataService.cs index 3476e70009..0ddb2ad67b 100644 --- a/MediaBrowser.Providers/Music/AlbumMetadataService.cs +++ b/MediaBrowser.Providers/Music/AlbumMetadataService.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Jellyfin.Data.Enums; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; @@ -187,7 +188,7 @@ namespace MediaBrowser.Providers.Music PeopleHelper.AddPerson(people, new PersonInfo { Name = albumArtist, - Type = "AlbumArtist" + Type = PersonKind.AlbumArtist }); } @@ -196,7 +197,7 @@ namespace MediaBrowser.Providers.Music PeopleHelper.AddPerson(people, new PersonInfo { Name = artist, - Type = "Artist" + Type = PersonKind.Artist }); } diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs index dfaba6423b..3fd4ae1fc0 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs @@ -13,6 +13,7 @@ using System.Net.Http.Json; using System.Text.Json; using System.Threading; using System.Threading.Tasks; +using Jellyfin.Data.Enums; using Jellyfin.Extensions.Json; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; @@ -424,7 +425,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb var person = new PersonInfo { Name = result.Director, - Type = PersonType.Director + Type = PersonKind.Director }; itemResult.AddPerson(person); @@ -435,7 +436,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb var person = new PersonInfo { Name = result.Writer, - Type = PersonType.Writer + Type = PersonKind.Writer }; itemResult.AddPerson(person); @@ -454,7 +455,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb var person = new PersonInfo { Name = actor, - Type = PersonType.Actor + Type = PersonKind.Actor }; itemResult.AddPerson(person); diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs index fc72023666..2f62e117eb 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using Jellyfin.Data.Enums; using Jellyfin.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Entities; @@ -258,7 +259,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies { Name = actor.Name.Trim(), Role = actor.Character, - Type = PersonType.Actor, + Type = PersonKind.Actor, SortOrder = actor.Order }; @@ -278,20 +279,13 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies if (movieResult.Credits?.Crew is not null) { - var keepTypes = new[] - { - PersonType.Director, - PersonType.Writer, - PersonType.Producer - }; - foreach (var person in movieResult.Credits.Crew) { // Normalize this var type = TmdbUtils.MapCrewToPersonType(person); - if (!keepTypes.Contains(type, StringComparison.OrdinalIgnoreCase) && - !keepTypes.Contains(person.Job ?? string.Empty, StringComparison.OrdinalIgnoreCase)) + if (!TmdbUtils.WantedCrewKinds.Contains(type) + && !TmdbUtils.WantedCrewTypes.Contains(person.Job ?? string.Empty, StringComparison.OrdinalIgnoreCase)) { continue; } diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs index 66decde842..f18575aa98 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using Jellyfin.Data.Enums; using Jellyfin.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Entities; @@ -168,7 +169,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV { Name = actor.Name.Trim(), Role = actor.Character, - Type = PersonType.Actor, + Type = PersonKind.Actor, SortOrder = actor.Order }); } @@ -182,7 +183,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV { Name = guest.Name.Trim(), Role = guest.Character, - Type = PersonType.GuestStar, + Type = PersonKind.GuestStar, SortOrder = guest.Order }); } @@ -196,7 +197,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV // Normalize this var type = TmdbUtils.MapCrewToPersonType(person); - if (!TmdbUtils.WantedCrewTypes.Contains(type, StringComparison.OrdinalIgnoreCase) + if (!TmdbUtils.WantedCrewKinds.Contains(type) && !TmdbUtils.WantedCrewTypes.Contains(person.Job ?? string.Empty, StringComparison.OrdinalIgnoreCase)) { continue; diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs index 3cb72b89b5..10efb68b94 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using Jellyfin.Data.Enums; using Jellyfin.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Entities; @@ -88,7 +89,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV { Name = cast[i].Name.Trim(), Role = cast[i].Character, - Type = PersonType.Actor, + Type = PersonKind.Actor, SortOrder = cast[i].Order }); } @@ -101,7 +102,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV // Normalize this var type = TmdbUtils.MapCrewToPersonType(person); - if (!TmdbUtils.WantedCrewTypes.Contains(type, StringComparison.OrdinalIgnoreCase) + if (!TmdbUtils.WantedCrewKinds.Contains(type) && !TmdbUtils.WantedCrewTypes.Contains(person.Job ?? string.Empty, StringComparison.OrdinalIgnoreCase)) { continue; diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs index 09d1a739d2..8dc2d69385 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using Jellyfin.Data.Enums; using Jellyfin.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Entities; @@ -352,7 +353,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV { Name = actor.Name.Trim(), Role = actor.Character, - Type = PersonType.Actor, + Type = PersonKind.Actor, SortOrder = actor.Order, ImageUrl = _tmdbClientManager.GetPosterUrl(actor.ProfilePath) }; @@ -380,8 +381,8 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV // Normalize this var type = TmdbUtils.MapCrewToPersonType(person); - if (!keepTypes.Contains(type, StringComparison.OrdinalIgnoreCase) - && !keepTypes.Contains(person.Job ?? string.Empty, StringComparison.OrdinalIgnoreCase)) + if (!TmdbUtils.WantedCrewKinds.Contains(type) + && !TmdbUtils.WantedCrewTypes.Contains(person.Job ?? string.Empty, StringComparison.OrdinalIgnoreCase)) { continue; } diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs b/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs index b326d22c87..516eee758c 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Text.RegularExpressions; +using Jellyfin.Data.Enums; using MediaBrowser.Model.Entities; using TMDbLib.Objects.General; @@ -39,6 +40,16 @@ namespace MediaBrowser.Providers.Plugins.Tmdb PersonType.Producer }; + /// <summary> + /// The crew kinds to keep. + /// </summary> + public static readonly PersonKind[] WantedCrewKinds = + { + PersonKind.Director, + PersonKind.Writer, + PersonKind.Producer + }; + /// <summary> /// Cleans the name according to TMDb requirements. /// </summary> @@ -55,26 +66,26 @@ namespace MediaBrowser.Providers.Plugins.Tmdb /// </summary> /// <param name="crew">Crew member to map against the Jellyfin person types.</param> /// <returns>The Jellyfin person type.</returns> - public static string MapCrewToPersonType(Crew crew) + public static PersonKind MapCrewToPersonType(Crew crew) { if (crew.Department.Equals("production", StringComparison.OrdinalIgnoreCase) && crew.Job.Contains("director", StringComparison.OrdinalIgnoreCase)) { - return PersonType.Director; + return PersonKind.Director; } if (crew.Department.Equals("production", StringComparison.OrdinalIgnoreCase) && crew.Job.Contains("producer", StringComparison.OrdinalIgnoreCase)) { - return PersonType.Producer; + return PersonKind.Producer; } if (crew.Department.Equals("writing", StringComparison.OrdinalIgnoreCase)) { - return PersonType.Writer; + return PersonKind.Writer; } - return string.Empty; + return PersonKind.Unknown; } /// <summary> diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs index 8bd30447a2..111d0c5cbc 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Text; using System.Threading; using System.Xml; +using Jellyfin.Data.Enums; using Jellyfin.Extensions; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Providers; @@ -530,7 +531,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers case "director": { var val = reader.ReadElementContentAsString(); - foreach (var p in SplitNames(val).Select(v => new PersonInfo { Name = v.Trim(), Type = PersonType.Director })) + foreach (var p in SplitNames(val).Select(v => new PersonInfo { Name = v.Trim(), Type = PersonKind.Director })) { if (string.IsNullOrWhiteSpace(p.Name)) { @@ -552,7 +553,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers var parts = val.Split('/').Select(i => i.Trim()) .Where(i => !string.IsNullOrEmpty(i)); - foreach (var p in parts.Select(v => new PersonInfo { Name = v.Trim(), Type = PersonType.Writer })) + foreach (var p in parts.Select(v => new PersonInfo { Name = v.Trim(), Type = PersonKind.Writer })) { if (string.IsNullOrWhiteSpace(p.Name)) { @@ -569,7 +570,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers case "writer": { var val = reader.ReadElementContentAsString(); - foreach (var p in SplitNames(val).Select(v => new PersonInfo { Name = v.Trim(), Type = PersonType.Writer })) + foreach (var p in SplitNames(val).Select(v => new PersonInfo { Name = v.Trim(), Type = PersonKind.Writer })) { if (string.IsNullOrWhiteSpace(p.Name)) { @@ -1206,7 +1207,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers private PersonInfo GetPersonFromXmlNode(XmlReader reader) { var name = string.Empty; - var type = PersonType.Actor; // If type is not specified assume actor + var type = PersonKind.Actor; // If type is not specified assume actor var role = string.Empty; int? sortOrder = null; string? imageUrl = null; @@ -1240,21 +1241,9 @@ namespace MediaBrowser.XbmcMetadata.Parsers case "type": { var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) + if (!Enum.TryParse(val, true, out type)) { - type = val switch - { - PersonType.Composer => PersonType.Composer, - PersonType.Conductor => PersonType.Conductor, - PersonType.Director => PersonType.Director, - PersonType.Lyricist => PersonType.Lyricist, - PersonType.Producer => PersonType.Producer, - PersonType.Writer => PersonType.Writer, - PersonType.GuestStar => PersonType.GuestStar, - // unknown type --> actor - _ => PersonType.Actor - }; + type = PersonKind.Actor; } break; diff --git a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs index 130d0bfe40..4f8f869ace 100644 --- a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs +++ b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs @@ -10,6 +10,7 @@ using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using System.Xml; +using Jellyfin.Data.Enums; using Jellyfin.Extensions; using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Configuration; @@ -485,7 +486,7 @@ namespace MediaBrowser.XbmcMetadata.Savers var people = libraryManager.GetPeople(item); var directors = people - .Where(i => IsPersonType(i, PersonType.Director)) + .Where(i => i.IsType(PersonKind.Director)) .Select(i => i.Name) .ToList(); @@ -495,7 +496,7 @@ namespace MediaBrowser.XbmcMetadata.Savers } var writers = people - .Where(i => IsPersonType(i, PersonType.Writer)) + .Where(i => i.IsType(PersonKind.Writer)) .Select(i => i.Name) .Distinct(StringComparer.OrdinalIgnoreCase) .ToList(); @@ -913,7 +914,7 @@ namespace MediaBrowser.XbmcMetadata.Savers { foreach (var person in people) { - if (IsPersonType(person, PersonType.Director) || IsPersonType(person, PersonType.Writer)) + if (person.IsType(PersonKind.Director) || person.IsType(PersonKind.Writer)) { continue; } @@ -930,9 +931,9 @@ namespace MediaBrowser.XbmcMetadata.Savers writer.WriteElementString("role", person.Role); } - if (!string.IsNullOrWhiteSpace(person.Type)) + if (person.Type != PersonKind.Unknown) { - writer.WriteElementString("type", person.Type); + writer.WriteElementString("type", person.Type.ToString()); } if (person.SortOrder.HasValue) @@ -969,10 +970,6 @@ namespace MediaBrowser.XbmcMetadata.Savers return libraryManager.GetPathAfterNetworkSubstitution(image.Path); } - private bool IsPersonType(PersonInfo person, string type) - => string.Equals(person.Type, type, StringComparison.OrdinalIgnoreCase) - || string.Equals(person.Role, type, StringComparison.OrdinalIgnoreCase); - private void AddCustomTags(string path, IReadOnlyCollection<string> xmlTagsUsed, XmlWriter writer, ILogger<BaseNfoSaver> logger) { var settings = new XmlReaderSettings() diff --git a/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs b/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs index 6cb98b2b86..198dc63efe 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs @@ -2,6 +2,7 @@ using System; using System.Globalization; using System.IO; using System.Text.Json; +using Jellyfin.Data.Enums; using Jellyfin.Extensions.Json; using Jellyfin.Extensions.Json.Converters; using MediaBrowser.MediaEncoding.Probing; @@ -314,15 +315,15 @@ namespace Jellyfin.MediaEncoding.Tests.Probing Assert.Equal(DateTime.Parse("2020-10-26T00:00Z", DateTimeFormatInfo.CurrentInfo, DateTimeStyles.AdjustToUniversal), res.PremiereDate); Assert.Equal(22, res.People.Length); Assert.Equal("Krysta Youngs", res.People[0].Name); - Assert.Equal(PersonType.Composer, res.People[0].Type); + Assert.Equal(PersonKind.Composer, res.People[0].Type); Assert.Equal("Julia Ross", res.People[1].Name); - Assert.Equal(PersonType.Composer, res.People[1].Type); + Assert.Equal(PersonKind.Composer, res.People[1].Type); Assert.Equal("Yiwoomin", res.People[2].Name); - Assert.Equal(PersonType.Composer, res.People[2].Type); + Assert.Equal(PersonKind.Composer, res.People[2].Type); Assert.Equal("Ji-hyo Park", res.People[3].Name); - Assert.Equal(PersonType.Lyricist, res.People[3].Type); + Assert.Equal(PersonKind.Lyricist, res.People[3].Type); Assert.Equal("Yiwoomin", res.People[4].Name); - Assert.Equal(PersonType.Actor, res.People[4].Type); + Assert.Equal(PersonKind.Actor, res.People[4].Type); Assert.Equal("Electric Piano", res.People[4].Role); Assert.Equal(4, res.Genres.Length); Assert.Contains("Electronic", res.Genres); diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/EpisodeNfoProviderTests.cs b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/EpisodeNfoProviderTests.cs index 4f4ae5afb9..f63bc0e1bc 100644 --- a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/EpisodeNfoProviderTests.cs +++ b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/EpisodeNfoProviderTests.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using System.Threading; +using Jellyfin.Data.Enums; using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; @@ -79,18 +80,18 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers Assert.Equal("1276153", item.ProviderIds[MetadataProvider.Tmdb.ToString()]); // Credits - var writers = result.People.Where(x => x.Type == PersonType.Writer).ToArray(); + var writers = result.People.Where(x => x.Type == PersonKind.Writer).ToArray(); Assert.Equal(2, writers.Length); Assert.Contains("Bryan Fuller", writers.Select(x => x.Name)); Assert.Contains("Michael Green", writers.Select(x => x.Name)); // Direcotrs - var directors = result.People.Where(x => x.Type == PersonType.Director).ToArray(); + var directors = result.People.Where(x => x.Type == PersonKind.Director).ToArray(); Assert.Single(directors); Assert.Contains("David Slade", directors.Select(x => x.Name)); // Actors - var actors = result.People.Where(x => x.Type == PersonType.Actor).ToArray(); + var actors = result.People.Where(x => x.Type == PersonKind.Actor).ToArray(); Assert.Equal(11, actors.Length); // Only test one actor var shadow = actors.FirstOrDefault(x => x.Role.Equals("Shadow Moon", StringComparison.Ordinal)); diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs index 988abce812..f56f58c6fe 100644 --- a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs +++ b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs @@ -2,6 +2,7 @@ using System; using System.Linq; using System.Threading; using Jellyfin.Data.Entities; +using Jellyfin.Data.Enums; using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; @@ -117,18 +118,18 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers Assert.Equal(20, result.People.Count); - var writers = result.People.Where(x => x.Type == PersonType.Writer).ToArray(); + var writers = result.People.Where(x => x.Type == PersonKind.Writer).ToArray(); Assert.Equal(3, writers.Length); var writerNames = writers.Select(x => x.Name); Assert.Contains("Jerry Siegel", writerNames); Assert.Contains("Joe Shuster", writerNames); Assert.Contains("Test", writerNames); - var directors = result.People.Where(x => x.Type == PersonType.Director).ToArray(); + var directors = result.People.Where(x => x.Type == PersonKind.Director).ToArray(); Assert.Single(directors); Assert.Equal("Zack Snyder", directors[0].Name); - var actors = result.People.Where(x => x.Type == PersonType.Actor).ToArray(); + var actors = result.People.Where(x => x.Type == PersonKind.Actor).ToArray(); Assert.Equal(15, actors.Length); // Only test one actor @@ -138,7 +139,7 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers Assert.Equal(5, aquaman!.SortOrder); Assert.Equal("https://m.media-amazon.com/images/M/MV5BMTI5MTU5NjM1MV5BMl5BanBnXkFtZTcwODc4MDk0Mw@@._V1_SX1024_SY1024_.jpg", aquaman!.ImageUrl); - var lyricist = result.People.FirstOrDefault(x => x.Type == PersonType.Lyricist); + var lyricist = result.People.FirstOrDefault(x => x.Type == PersonKind.Lyricist); Assert.NotNull(lyricist); Assert.Equal("Test Lyricist", lyricist!.Name); diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/SeasonNfoProviderTests.cs b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/SeasonNfoProviderTests.cs index 31110dbd7d..e69ca996cc 100644 --- a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/SeasonNfoProviderTests.cs +++ b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/SeasonNfoProviderTests.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using System.Threading; +using Jellyfin.Data.Enums; using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; @@ -60,7 +61,7 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers Assert.Equal(10, result.People.Count); - Assert.True(result.People.All(x => x.Type == PersonType.Actor)); + Assert.True(result.People.All(x => x.Type == PersonKind.Actor)); // Only test one actor var nini = result.People.FirstOrDefault(x => x.Role.Equals("Nini", StringComparison.Ordinal)); diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/SeriesNfoParserTests.cs b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/SeriesNfoParserTests.cs index bdedae205b..542324e78b 100644 --- a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/SeriesNfoParserTests.cs +++ b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/SeriesNfoParserTests.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using System.Threading; +using Jellyfin.Data.Enums; using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; @@ -67,7 +68,7 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers Assert.Equal(6, result.People.Count); - Assert.True(result.People.All(x => x.Type == PersonType.Actor)); + Assert.True(result.People.All(x => x.Type == PersonKind.Actor)); // Only test one actor var sweeney = result.People.FirstOrDefault(x => x.Role.Equals("Mad Sweeney", StringComparison.Ordinal)); -- cgit v1.2.3 From 16bae527e56432fdc549882ae6f68505518f233c Mon Sep 17 00:00:00 2001 From: Bas <weblate@hanka.mp> Date: Sat, 25 Mar 2023 16:32:35 +0000 Subject: Translated using Weblate (Dutch) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/nl/ --- Emby.Server.Implementations/Localization/Core/nl.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/nl.json b/Emby.Server.Implementations/Localization/Core/nl.json index 01a2ab273f..4eb00d2896 100644 --- a/Emby.Server.Implementations/Localization/Core/nl.json +++ b/Emby.Server.Implementations/Localization/Core/nl.json @@ -8,7 +8,7 @@ "CameraImageUploadedFrom": "Nieuwe camera-afbeelding toegevoegd vanaf {0}", "Channels": "Kanalen", "ChapterNameValue": "Hoofdstuk {0}", - "Collections": "Verzamelingen", + "Collections": "Collecties", "DeviceOfflineWithName": "Verbinding met {0} is verbroken", "DeviceOnlineWithName": "{0} is verbonden", "FailedLoginAttemptWithUserName": "Mislukte inlogpoging van {0}", -- cgit v1.2.3 From 891b9f7a997ce5e5892c1b0f166a921ff07abf68 Mon Sep 17 00:00:00 2001 From: AmbulantRex <21176662+AmbulantRex@users.noreply.github.com> Date: Thu, 30 Mar 2023 08:59:21 -0600 Subject: Add DLL whitelist support for plugins --- .../Library/PathExtensions.cs | 99 ++++++++++++--- .../Plugins/PluginManager.cs | 83 ++++++++++++- MediaBrowser.Common/Plugins/PluginManifest.cs | 9 ++ MediaBrowser.Model/Updates/VersionInfo.cs | 8 ++ .../Library/PathExtensionsTests.cs | 43 +++++++ .../Plugins/PluginManagerTests.cs | 135 +++++++++++++++++++++ .../Test Data/Updates/manifest-stable.json | 10 +- .../Updates/InstallationManagerTests.cs | 14 +++ 8 files changed, 375 insertions(+), 26 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/PathExtensions.cs b/Emby.Server.Implementations/Library/PathExtensions.cs index 64e7d54466..7e0d0b78d5 100644 --- a/Emby.Server.Implementations/Library/PathExtensions.cs +++ b/Emby.Server.Implementations/Library/PathExtensions.cs @@ -1,6 +1,9 @@ using System; using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; using MediaBrowser.Common.Providers; +using Nikse.SubtitleEdit.Core.Common; namespace Emby.Server.Implementations.Library { @@ -86,24 +89,8 @@ namespace Emby.Server.Implementations.Library return false; } - char oldDirectorySeparatorChar; - char newDirectorySeparatorChar; - // True normalization is still not possible https://github.com/dotnet/runtime/issues/2162 - // The reasoning behind this is that a forward slash likely means it's a Linux path and - // so the whole path should be normalized to use / and vice versa for Windows (although Windows doesn't care much). - if (newSubPath.Contains('/', StringComparison.Ordinal)) - { - oldDirectorySeparatorChar = '\\'; - newDirectorySeparatorChar = '/'; - } - else - { - oldDirectorySeparatorChar = '/'; - newDirectorySeparatorChar = '\\'; - } - - path = path.Replace(oldDirectorySeparatorChar, newDirectorySeparatorChar); - subPath = subPath.Replace(oldDirectorySeparatorChar, newDirectorySeparatorChar); + subPath = subPath.NormalizePath(out var newDirectorySeparatorChar)!; + path = path.NormalizePath(newDirectorySeparatorChar)!; // We have to ensure that the sub path ends with a directory separator otherwise we'll get weird results // when the sub path matches a similar but in-complete subpath @@ -120,12 +107,86 @@ namespace Emby.Server.Implementations.Library return false; } - var newSubPathTrimmed = newSubPath.AsSpan().TrimEnd(newDirectorySeparatorChar); + var newSubPathTrimmed = newSubPath.AsSpan().TrimEnd((char)newDirectorySeparatorChar!); // Ensure that the path with the old subpath removed starts with a leading dir separator int idx = oldSubPathEndsWithSeparator ? subPath.Length - 1 : subPath.Length; newPath = string.Concat(newSubPathTrimmed, path.AsSpan(idx)); return true; } + + /// <summary> + /// Retrieves the full resolved path and normalizes path separators to the <see cref="Path.DirectorySeparatorChar"/>. + /// </summary> + /// <param name="path">The path to canonicalize.</param> + /// <returns>The fully expanded, normalized path.</returns> + public static string Canonicalize(this string path) + { + return Path.GetFullPath(path).NormalizePath()!; + } + + /// <summary> + /// Normalizes the path's directory separator character to the currently defined <see cref="Path.DirectorySeparatorChar"/>. + /// </summary> + /// <param name="path">The path to normalize.</param> + /// <returns>The normalized path string or <see langword="null"/> if the input path is null or empty.</returns> + public static string? NormalizePath(this string? path) + { + return path.NormalizePath(Path.DirectorySeparatorChar); + } + + /// <summary> + /// Normalizes the path's directory separator character. + /// </summary> + /// <param name="path">The path to normalize.</param> + /// <param name="separator">The separator character the path now uses or <see langword="null"/>.</param> + /// <returns>The normalized path string or <see langword="null"/> if the input path is null or empty.</returns> + public static string? NormalizePath(this string? path, out char separator) + { + if (string.IsNullOrEmpty(path)) + { + separator = default; + return path; + } + + var newSeparator = '\\'; + + // True normalization is still not possible https://github.com/dotnet/runtime/issues/2162 + // The reasoning behind this is that a forward slash likely means it's a Linux path and + // so the whole path should be normalized to use / and vice versa for Windows (although Windows doesn't care much). + if (path.Contains('/', StringComparison.Ordinal)) + { + newSeparator = '/'; + } + + separator = newSeparator; + + return path?.NormalizePath(newSeparator); + } + + /// <summary> + /// Normalizes the path's directory separator character to the specified character. + /// </summary> + /// <param name="path">The path to normalize.</param> + /// <param name="newSeparator">The replacement directory separator character. Must be a valid directory separator.</param> + /// <returns>The normalized path.</returns> + /// <exception cref="ArgumentException">Thrown if the new separator character is not a directory separator.</exception> + public static string? NormalizePath(this string? path, char newSeparator) + { + const char Bs = '\\'; + const char Fs = '/'; + + if (!(newSeparator == Bs || newSeparator == Fs)) + { + throw new ArgumentException("The character must be a directory separator."); + } + + if (string.IsNullOrEmpty(path)) + { + return path; + } + + return newSeparator == Bs ? path?.Replace(Fs, newSeparator) : path?.Replace(Bs, newSeparator); + } } } diff --git a/Emby.Server.Implementations/Plugins/PluginManager.cs b/Emby.Server.Implementations/Plugins/PluginManager.cs index 7c23254a12..a5c55c8a09 100644 --- a/Emby.Server.Implementations/Plugins/PluginManager.cs +++ b/Emby.Server.Implementations/Plugins/PluginManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Data; using System.Globalization; using System.IO; using System.Linq; @@ -9,6 +10,8 @@ using System.Runtime.Loader; using System.Text; using System.Text.Json; using System.Threading.Tasks; +using Emby.Server.Implementations.Library; +using Jellyfin.Extensions; using Jellyfin.Extensions.Json; using Jellyfin.Extensions.Json.Converters; using MediaBrowser.Common; @@ -19,8 +22,11 @@ using MediaBrowser.Model.Configuration; using MediaBrowser.Model.IO; using MediaBrowser.Model.Plugins; using MediaBrowser.Model.Updates; +using Microsoft.AspNetCore.Mvc.ApplicationParts; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Nikse.SubtitleEdit.Core.Common; +using SQLitePCL.pretty; namespace Emby.Server.Implementations.Plugins { @@ -44,7 +50,7 @@ namespace Emby.Server.Implementations.Plugins /// <summary> /// Initializes a new instance of the <see cref="PluginManager"/> class. /// </summary> - /// <param name="logger">The <see cref="ILogger"/>.</param> + /// <param name="logger">The <see cref="ILogger{PluginManager}"/>.</param> /// <param name="appHost">The <see cref="IApplicationHost"/>.</param> /// <param name="config">The <see cref="ServerConfiguration"/>.</param> /// <param name="pluginsPath">The plugin path.</param> @@ -424,7 +430,8 @@ namespace Emby.Server.Implementations.Plugins Version = versionInfo.Version, Status = status == PluginStatus.Disabled ? PluginStatus.Disabled : PluginStatus.Active, // Keep disabled state. AutoUpdate = true, - ImagePath = imagePath + ImagePath = imagePath, + Assemblies = versionInfo.Assemblies }; return SaveManifest(manifest, path); @@ -688,7 +695,15 @@ namespace Emby.Server.Implementations.Plugins var entry = versions[x]; if (!string.Equals(lastName, entry.Name, StringComparison.OrdinalIgnoreCase)) { - entry.DllFiles = Directory.GetFiles(entry.Path, "*.dll", SearchOption.AllDirectories); + if (!TryGetPluginDlls(entry, out var allowedDlls)) + { + _logger.LogError("One or more assembly paths was invalid. Marking plugin {Plugin} as \"Malfunctioned\".", entry.Name); + ChangePluginState(entry, PluginStatus.Malfunctioned); + continue; + } + + entry.DllFiles = allowedDlls; + if (entry.IsEnabledAndSupported) { lastName = entry.Name; @@ -734,6 +749,68 @@ namespace Emby.Server.Implementations.Plugins return versions.Where(p => p.DllFiles.Count != 0); } + /// <summary> + /// Attempts to retrieve valid DLLs from the plugin path. This method will consider the assembly whitelist + /// from the manifest. + /// </summary> + /// <remarks> + /// Loading DLLs from externally supplied paths introduces a path traversal risk. This method + /// uses a safelisting tactic of considering DLLs from the plugin directory and only using + /// the plugin's canonicalized assembly whitelist for comparison. See + /// <see href="https://owasp.org/www-community/attacks/Path_Traversal"/> for more details. + /// </remarks> + /// <param name="plugin">The plugin.</param> + /// <param name="whitelistedDlls">The whitelisted DLLs. If the method returns <see langword="false"/>, this will be empty.</param> + /// <returns> + /// <see langword="true"/> if all assemblies listed in the manifest were available in the plugin directory. + /// <see langword="false"/> if any assemblies were invalid or missing from the plugin directory. + /// </returns> + /// <exception cref="ArgumentNullException">If the <see cref="LocalPlugin"/> is null.</exception> + private bool TryGetPluginDlls(LocalPlugin plugin, out IReadOnlyList<string> whitelistedDlls) + { + _ = plugin ?? throw new ArgumentNullException(nameof(plugin)); + + IReadOnlyList<string> pluginDlls = Directory.GetFiles(plugin.Path, "*.dll", SearchOption.AllDirectories); + + whitelistedDlls = Array.Empty<string>(); + if (pluginDlls.Count > 0 && plugin.Manifest.Assemblies.Count > 0) + { + _logger.LogInformation("Registering whitelisted assemblies for plugin \"{Plugin}\"...", plugin.Name); + + var canonicalizedPaths = new List<string>(); + foreach (var path in plugin.Manifest.Assemblies) + { + var canonicalized = Path.Combine(plugin.Path, path).Canonicalize(); + + // Ensure we stay in the plugin directory. + if (!canonicalized.StartsWith(plugin.Path.NormalizePath()!, StringComparison.Ordinal)) + { + _logger.LogError("Assembly path {Path} is not inside the plugin directory.", path); + return false; + } + + canonicalizedPaths.Add(canonicalized); + } + + var intersected = pluginDlls.Intersect(canonicalizedPaths).ToList(); + + if (intersected.Count != canonicalizedPaths.Count) + { + _logger.LogError("Plugin {Plugin} contained assembly paths that were not found in the directory.", plugin.Name); + return false; + } + + whitelistedDlls = intersected; + } + else + { + // No whitelist, default to loading all DLLs in plugin directory. + whitelistedDlls = pluginDlls; + } + + return true; + } + /// <summary> /// Changes the status of the other versions of the plugin to "Superceded". /// </summary> diff --git a/MediaBrowser.Common/Plugins/PluginManifest.cs b/MediaBrowser.Common/Plugins/PluginManifest.cs index 2910dbe144..2bad3454d1 100644 --- a/MediaBrowser.Common/Plugins/PluginManifest.cs +++ b/MediaBrowser.Common/Plugins/PluginManifest.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Text.Json.Serialization; using MediaBrowser.Model.Plugins; @@ -23,6 +24,7 @@ namespace MediaBrowser.Common.Plugins Overview = string.Empty; TargetAbi = string.Empty; Version = string.Empty; + Assemblies = new List<string>(); } /// <summary> @@ -104,5 +106,12 @@ namespace MediaBrowser.Common.Plugins /// </summary> [JsonPropertyName("imagePath")] public string? ImagePath { get; set; } + + /// <summary> + /// Gets or sets the collection of assemblies that should be loaded. + /// Paths are considered relative to the plugin folder. + /// </summary> + [JsonPropertyName("assemblies")] + public IList<string> Assemblies { get; set; } } } diff --git a/MediaBrowser.Model/Updates/VersionInfo.cs b/MediaBrowser.Model/Updates/VersionInfo.cs index 320199f984..1e24bde84e 100644 --- a/MediaBrowser.Model/Updates/VersionInfo.cs +++ b/MediaBrowser.Model/Updates/VersionInfo.cs @@ -1,3 +1,5 @@ +using System; +using System.Collections.Generic; using System.Text.Json.Serialization; using SysVersion = System.Version; @@ -73,5 +75,11 @@ namespace MediaBrowser.Model.Updates /// </summary> [JsonPropertyName("repositoryUrl")] public string RepositoryUrl { get; set; } = string.Empty; + + /// <summary> + /// Gets or sets the assemblies whitelist for this version. + /// </summary> + [JsonPropertyName("assemblies")] + public IList<string> Assemblies { get; set; } = Array.Empty<string>(); } } diff --git a/tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs index be2dfe0a86..c33a957e69 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Library/PathExtensionsTests.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using Emby.Server.Implementations.Library; using Xunit; @@ -73,5 +74,47 @@ namespace Jellyfin.Server.Implementations.Tests.Library Assert.False(PathExtensions.TryReplaceSubPath(path, subPath, newSubPath, out var result)); Assert.Null(result); } + + [Theory] + [InlineData(null, '/', null)] + [InlineData(null, '\\', null)] + [InlineData("/home/jeff/myfile.mkv", '\\', "\\home\\jeff\\myfile.mkv")] + [InlineData("C:\\Users\\Jeff\\myfile.mkv", '/', "C:/Users/Jeff/myfile.mkv")] + [InlineData("\\home/jeff\\myfile.mkv", '\\', "\\home\\jeff\\myfile.mkv")] + [InlineData("\\home/jeff\\myfile.mkv", '/', "/home/jeff/myfile.mkv")] + [InlineData("", '/', "")] + public void NormalizePath_SpecifyingSeparator_Normalizes(string path, char separator, string expectedPath) + { + Assert.Equal(expectedPath, path.NormalizePath(separator)); + } + + [Theory] + [InlineData("/home/jeff/myfile.mkv")] + [InlineData("C:\\Users\\Jeff\\myfile.mkv")] + [InlineData("\\home/jeff\\myfile.mkv")] + public void NormalizePath_NoArgs_UsesDirectorySeparatorChar(string path) + { + var separator = Path.DirectorySeparatorChar; + + Assert.Equal(path.Replace('\\', separator).Replace('/', separator), path.NormalizePath()); + } + + [Theory] + [InlineData("/home/jeff/myfile.mkv", '/')] + [InlineData("C:\\Users\\Jeff\\myfile.mkv", '\\')] + [InlineData("\\home/jeff\\myfile.mkv", '/')] + public void NormalizePath_OutVar_Correct(string path, char expectedSeparator) + { + var result = path.NormalizePath(out var separator); + + Assert.Equal(expectedSeparator, separator); + Assert.Equal(path.Replace('\\', separator).Replace('/', separator), result); + } + + [Fact] + public void NormalizePath_SpecifyInvalidSeparator_ThrowsException() + { + Assert.Throws<ArgumentException>(() => string.Empty.NormalizePath('a')); + } } } diff --git a/tests/Jellyfin.Server.Implementations.Tests/Plugins/PluginManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Plugins/PluginManagerTests.cs index bc6a447410..d9fdc96f55 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Plugins/PluginManagerTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Plugins/PluginManagerTests.cs @@ -1,7 +1,12 @@ using System; using System.IO; +using System.Text.Json; +using Emby.Server.Implementations.Library; using Emby.Server.Implementations.Plugins; +using Jellyfin.Extensions.Json; +using Jellyfin.Extensions.Json.Converters; using MediaBrowser.Common.Plugins; +using MediaBrowser.Model.Plugins; using Microsoft.Extensions.Logging.Abstractions; using Xunit; @@ -40,6 +45,136 @@ namespace Jellyfin.Server.Implementations.Tests.Plugins Assert.Equal(manifest.Status, res.Manifest.Status); Assert.Equal(manifest.AutoUpdate, res.Manifest.AutoUpdate); Assert.Equal(manifest.ImagePath, res.Manifest.ImagePath); + Assert.Equal(manifest.Assemblies, res.Manifest.Assemblies); + } + + /// <summary> + /// Tests safe traversal within the plugin directory. + /// </summary> + /// <param name="dllFile">The safe path to evaluate.</param> + [Theory] + [InlineData("./some.dll")] + [InlineData("some.dll")] + [InlineData("sub/path/some.dll")] + public void Constructor_DiscoversSafePluginAssembly_Status_Active(string dllFile) + { + var manifest = new PluginManifest + { + Id = Guid.NewGuid(), + Name = "Safe Assembly", + Assemblies = new string[] { dllFile } + }; + + var filename = Path.GetFileName(dllFile)!; + var (tempPath, pluginPath) = GetTestPaths("safe"); + + Directory.CreateDirectory(Path.Combine(pluginPath, dllFile.Replace(filename, string.Empty, StringComparison.OrdinalIgnoreCase))); + File.Create(Path.Combine(pluginPath, dllFile)); + + var options = GetTestSerializerOptions(); + var data = JsonSerializer.Serialize(manifest, options); + var metafilePath = Path.Combine(tempPath, "safe", "meta.json"); + + File.WriteAllText(metafilePath, data); + + var pluginManager = new PluginManager(new NullLogger<PluginManager>(), null!, null!, tempPath, new Version(1, 0)); + + var res = JsonSerializer.Deserialize<PluginManifest>(File.ReadAllText(metafilePath), options); + + var expectedFullPath = Path.Combine(pluginPath, dllFile).Canonicalize(); + + Assert.NotNull(res); + Assert.NotEmpty(pluginManager.Plugins); + Assert.Equal(PluginStatus.Active, res!.Status); + Assert.Equal(expectedFullPath, pluginManager.Plugins[0].DllFiles[0]); + Assert.StartsWith(Path.Combine(tempPath, "safe"), expectedFullPath, StringComparison.InvariantCulture); + } + + /// <summary> + /// Tests unsafe attempts to traverse to higher directories. + /// </summary> + /// <remarks> + /// Attempts to load directories outside of the plugin should be + /// constrained. Path traversal, shell expansion, and double encoding + /// can be used to load unintended files. + /// See <see href="https://owasp.org/www-community/attacks/Path_Traversal"/> for more. + /// </remarks> + /// <param name="unsafePath">The unsafe path to evaluate.</param> + [Theory] + [InlineData("/some.dll")] // Root path. + [InlineData("../some.dll")] // Simple traversal. + [InlineData("C:\\some.dll")] // Windows root path. + [InlineData("test.txt")] // Not a DLL + [InlineData(".././.././../some.dll")] // Traversal with current and parent + [InlineData("..\\.\\..\\.\\..\\some.dll")] // Windows traversal with current and parent + [InlineData("\\\\network\\resource.dll")] // UNC Path + [InlineData("https://jellyfin.org/some.dll")] // URL + [InlineData("....//....//some.dll")] // Path replacement risk if a single "../" replacement occurs. + [InlineData("~/some.dll")] // Tilde poses a shell expansion risk, but is a valid path character. + public void Constructor_DiscoversUnsafePluginAssembly_Status_Malfunctioned(string unsafePath) + { + var manifest = new PluginManifest + { + Id = Guid.NewGuid(), + Name = "Unsafe Assembly", + Assemblies = new string[] { unsafePath } + }; + + var (tempPath, pluginPath) = GetTestPaths("unsafe"); + + Directory.CreateDirectory(pluginPath); + + var files = new string[] + { + "../other.dll", + "some.dll" + }; + + foreach (var file in files) + { + File.Create(Path.Combine(pluginPath, file)); + } + + var options = GetTestSerializerOptions(); + var data = JsonSerializer.Serialize(manifest, options); + var metafilePath = Path.Combine(tempPath, "unsafe", "meta.json"); + + File.WriteAllText(metafilePath, data); + + var pluginManager = new PluginManager(new NullLogger<PluginManager>(), null!, null!, tempPath, new Version(1, 0)); + + var res = JsonSerializer.Deserialize<PluginManifest>(File.ReadAllText(metafilePath), options); + + Assert.NotNull(res); + Assert.Empty(pluginManager.Plugins); + Assert.Equal(PluginStatus.Malfunctioned, res!.Status); + } + + private JsonSerializerOptions GetTestSerializerOptions() + { + var options = new JsonSerializerOptions(JsonDefaults.Options) + { + WriteIndented = true + }; + + for (var i = 0; i < options.Converters.Count; i++) + { + // Remove the Guid converter for parity with plugin manager. + if (options.Converters[i] is JsonGuidConverter converter) + { + options.Converters.Remove(converter); + } + } + + return options; + } + + private (string TempPath, string PluginPath) GetTestPaths(string pluginFolderName) + { + var tempPath = Path.Combine(_testPathRoot, "plugins-" + Path.GetRandomFileName()); + var pluginPath = Path.Combine(tempPath, pluginFolderName); + + return (tempPath, pluginPath); } } } diff --git a/tests/Jellyfin.Server.Implementations.Tests/Test Data/Updates/manifest-stable.json b/tests/Jellyfin.Server.Implementations.Tests/Test Data/Updates/manifest-stable.json index fa8fbd8d2c..d69a52d6d0 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Test Data/Updates/manifest-stable.json +++ b/tests/Jellyfin.Server.Implementations.Tests/Test Data/Updates/manifest-stable.json @@ -13,7 +13,8 @@ "targetAbi": "10.6.0.0", "sourceUrl": "https://repo.jellyfin.org/releases/plugin/anime/anime_10.0.0.0.zip", "checksum": "93e969adeba1050423fc8817ed3c36f8", - "timestamp": "2020-08-17T01:41:13Z" + "timestamp": "2020-08-17T01:41:13Z", + "assemblies": [ "Jellyfin.Plugin.Anime.dll" ] }, { "version": "9.0.0.0", @@ -21,9 +22,10 @@ "targetAbi": "10.6.0.0", "sourceUrl": "https://repo.jellyfin.org/releases/plugin/anime/anime_9.0.0.0.zip", "checksum": "9b1cebff835813e15f414f44b40c41c8", - "timestamp": "2020-07-20T01:30:16Z" + "timestamp": "2020-07-20T01:30:16Z", + "assemblies": [ "Jellyfin.Plugin.Anime.dll" ] } - ] + ] }, { "guid": "70b7b43b-471b-4159-b4be-56750c795499", @@ -681,4 +683,4 @@ } ] } -] \ No newline at end of file +] diff --git a/tests/Jellyfin.Server.Implementations.Tests/Updates/InstallationManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Updates/InstallationManagerTests.cs index 7abd2e685f..70d03f8c4a 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Updates/InstallationManagerTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Updates/InstallationManagerTests.cs @@ -106,5 +106,19 @@ namespace Jellyfin.Server.Implementations.Tests.Updates var ex = await Record.ExceptionAsync(() => _installationManager.InstallPackage(packageInfo, CancellationToken.None)).ConfigureAwait(false); Assert.Null(ex); } + + [Fact] + public async Task InstallPackage_WithAssemblies_Success() + { + PackageInfo[] packages = await _installationManager.GetPackages( + "Jellyfin Stable", + "https://repo.jellyfin.org/releases/plugin/manifest-stable.json", + false); + + packages = _installationManager.FilterPackages(packages, "Anime").ToArray(); + Assert.Single(packages); + Assert.NotEmpty(packages[0].Versions[0].Assemblies); + Assert.Equal("Jellyfin.Plugin.Anime.dll", packages[0].Versions[0].Assemblies[0]); + } } } -- cgit v1.2.3 From 677b1f8e34799d26ffa9ef792aabcc79ad9073ca Mon Sep 17 00:00:00 2001 From: AmbulantRex <21176662+AmbulantRex@users.noreply.github.com> Date: Thu, 30 Mar 2023 12:56:57 -0600 Subject: Remove unnecessary using statements in PluginManager --- Emby.Server.Implementations/Library/PathExtensions.cs | 2 -- Emby.Server.Implementations/Plugins/PluginManager.cs | 3 --- 2 files changed, 5 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/PathExtensions.cs b/Emby.Server.Implementations/Library/PathExtensions.cs index 7e0d0b78d5..a3d748a138 100644 --- a/Emby.Server.Implementations/Library/PathExtensions.cs +++ b/Emby.Server.Implementations/Library/PathExtensions.cs @@ -1,9 +1,7 @@ using System; using System.Diagnostics.CodeAnalysis; using System.IO; -using System.Linq; using MediaBrowser.Common.Providers; -using Nikse.SubtitleEdit.Core.Common; namespace Emby.Server.Implementations.Library { diff --git a/Emby.Server.Implementations/Plugins/PluginManager.cs b/Emby.Server.Implementations/Plugins/PluginManager.cs index a5c55c8a09..d253a0ab99 100644 --- a/Emby.Server.Implementations/Plugins/PluginManager.cs +++ b/Emby.Server.Implementations/Plugins/PluginManager.cs @@ -22,11 +22,8 @@ using MediaBrowser.Model.Configuration; using MediaBrowser.Model.IO; using MediaBrowser.Model.Plugins; using MediaBrowser.Model.Updates; -using Microsoft.AspNetCore.Mvc.ApplicationParts; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using Nikse.SubtitleEdit.Core.Common; -using SQLitePCL.pretty; namespace Emby.Server.Implementations.Plugins { -- cgit v1.2.3 From 945f2761d29f8709ac928ab8a2d94506579d2220 Mon Sep 17 00:00:00 2001 From: heim3x <heim3x@proton.me> Date: Mon, 27 Mar 2023 20:23:27 +0000 Subject: Translated using Weblate (Danish) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/da/ --- .../Localization/Core/da.json | 104 ++++++++++----------- 1 file changed, 52 insertions(+), 52 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/da.json b/Emby.Server.Implementations/Localization/Core/da.json index 0d0d0c8136..1b6eecdcfe 100644 --- a/Emby.Server.Implementations/Localization/Core/da.json +++ b/Emby.Server.Implementations/Localization/Core/da.json @@ -1,9 +1,9 @@ { - "Albums": "Albummer", + "Albums": "Album", "AppDeviceValues": "App: {0}, Enhed: {1}", "Application": "Applikation", "Artists": "Kunstnere", - "AuthenticationSucceededWithUserName": "{0} succesfuldt autentificeret", + "AuthenticationSucceededWithUserName": "{0} er logget ind", "Books": "Bøger", "CameraImageUploadedFrom": "Et nyt kamerabillede er blevet uploadet fra {0}", "Channels": "Kanaler", @@ -11,17 +11,17 @@ "Collections": "Samlinger", "DeviceOfflineWithName": "{0} har afbrudt forbindelsen", "DeviceOnlineWithName": "{0} er forbundet", - "FailedLoginAttemptWithUserName": "Fejlet loginforsøg fra {0}", + "FailedLoginAttemptWithUserName": "Mislykket loginforsøg fra {0}", "Favorites": "Favoritter", "Folders": "Mapper", "Genres": "Genrer", - "HeaderAlbumArtists": "Albumkunstner", + "HeaderAlbumArtists": "Albums kunstnere", "HeaderContinueWatching": "Fortsæt afspilning", - "HeaderFavoriteAlbums": "Favoritalbummer", - "HeaderFavoriteArtists": "Favoritkunstnere", - "HeaderFavoriteEpisodes": "Favoritepisoder", - "HeaderFavoriteShows": "Favoritserier", - "HeaderFavoriteSongs": "Favoritsange", + "HeaderFavoriteAlbums": "Favorit albummer", + "HeaderFavoriteArtists": "Favorit kunstnere", + "HeaderFavoriteEpisodes": "Favorit afsnit", + "HeaderFavoriteShows": "Favorit serier", + "HeaderFavoriteSongs": "Favorit sange", "HeaderLiveTV": "Live-TV", "HeaderNextUp": "Næste", "HeaderRecordingGroups": "Optagelsesgrupper", @@ -39,90 +39,90 @@ "MixedContent": "Blandet indhold", "Movies": "Film", "Music": "Musik", - "MusicVideos": "Musik videoer", + "MusicVideos": "Musikvideoer", "NameInstallFailed": "{0} installationen mislykkedes", "NameSeasonNumber": "Sæson {0}", "NameSeasonUnknown": "Ukendt sæson", - "NewVersionIsAvailable": "En ny version af Jellyfin Server er tilgængelig til download.", - "NotificationOptionApplicationUpdateAvailable": "Opdatering til applikation tilgængelig", - "NotificationOptionApplicationUpdateInstalled": "Opdatering til applikation installeret", + "NewVersionIsAvailable": "En ny version af Jellyfin Server er tilgængelig.", + "NotificationOptionApplicationUpdateAvailable": "Opdatering til applikationen er tilgængelig", + "NotificationOptionApplicationUpdateInstalled": "Opdatering til applikationen blev installeret", "NotificationOptionAudioPlayback": "Lydafspilning påbegyndt", "NotificationOptionAudioPlaybackStopped": "Lydafspilning stoppet", "NotificationOptionCameraImageUploaded": "Kamerabillede uploadet", - "NotificationOptionInstallationFailed": "Installationen fejlede", + "NotificationOptionInstallationFailed": "Installationen mislykkedes", "NotificationOptionNewLibraryContent": "Nyt indhold tilføjet", - "NotificationOptionPluginError": "Pluginfejl", - "NotificationOptionPluginInstalled": "Plugin installeret", - "NotificationOptionPluginUninstalled": "Plugin afinstalleret", - "NotificationOptionPluginUpdateInstalled": "Opdatering til plugin installeret", - "NotificationOptionServerRestartRequired": "Genstart af server påkrævet", - "NotificationOptionTaskFailed": "Planlagt opgave fejlet", - "NotificationOptionUserLockedOut": "Bruger låst ude", + "NotificationOptionPluginError": "Plugin fejl", + "NotificationOptionPluginInstalled": "Plugin blev installeret", + "NotificationOptionPluginUninstalled": "Plugin blev afinstalleret", + "NotificationOptionPluginUpdateInstalled": "Opdatering til plugin blev installeret", + "NotificationOptionServerRestartRequired": "Genstart af serveren er påkrævet", + "NotificationOptionTaskFailed": "Planlagt opgave er fejlet", + "NotificationOptionUserLockedOut": "Bruger er låst ude", "NotificationOptionVideoPlayback": "Videoafspilning påbegyndt", - "NotificationOptionVideoPlaybackStopped": "Videoafspilning stoppet", - "Photos": "Fotoer", + "NotificationOptionVideoPlaybackStopped": "Videoafspilning blev stoppet", + "Photos": "Fotos", "Playlists": "Afspilningslister", "Plugin": "Plugin", "PluginInstalledWithName": "{0} blev installeret", "PluginUninstalledWithName": "{0} blev afinstalleret", "PluginUpdatedWithName": "{0} blev opdateret", "ProviderValue": "Udbyder: {0}", - "ScheduledTaskFailedWithName": "{0} fejlet", - "ScheduledTaskStartedWithName": "{0} påbegyndt", + "ScheduledTaskFailedWithName": "{0} mislykkedes", + "ScheduledTaskStartedWithName": "{0} påbegyndte", "ServerNameNeedsToBeRestarted": "{0} skal genstartes", "Shows": "Serier", "Songs": "Sange", - "StartupEmbyServerIsLoading": "Jellyfin Server er i gang med at starte op. Prøv venligst igen om lidt.", + "StartupEmbyServerIsLoading": "Jellyfin Server er i gang med at starte. Forsøg igen om et øjeblik.", "SubtitleDownloadFailureForItem": "Fejlet i download af undertekster for {0}", - "SubtitleDownloadFailureFromForItem": "Undertekster kunne ikke downloades fra {0} til {1}", - "Sync": "Synk", + "SubtitleDownloadFailureFromForItem": "Undertekster kunne ikke hentes fra {0} til {1}", + "Sync": "Synkroniser", "System": "System", - "TvShows": "Tv-serier", + "TvShows": "TV-serier", "User": "Bruger", "UserCreatedWithName": "Bruger {0} er blevet oprettet", - "UserDeletedWithName": "Brugeren {0} er blevet slettet", - "UserDownloadingItemWithValues": "{0} downloader {1}", + "UserDeletedWithName": "Brugeren {0} er nu slettet", + "UserDownloadingItemWithValues": "{0} henter {1}", "UserLockedOutWithName": "Brugeren {0} er blevet låst ude", "UserOfflineFromDevice": "{0} har afbrudt fra {1}", "UserOnlineFromDevice": "{0} er online fra {1}", - "UserPasswordChangedWithName": "Adgangskode er ændret for bruger {0}", - "UserPolicyUpdatedWithName": "Brugerpolitik er blevet opdateret for {0}", + "UserPasswordChangedWithName": "Adgangskode er ændret for brugeren {0}", + "UserPolicyUpdatedWithName": "Brugerpolitikken er blevet opdateret for {0}", "UserStartedPlayingItemWithValues": "{0} har påbegyndt afspilning af {1}", "UserStoppedPlayingItemWithValues": "{0} har afsluttet afspilning af {1} på {2}", "ValueHasBeenAddedToLibrary": "{0} er blevet tilføjet til dit mediebibliotek", "ValueSpecialEpisodeName": "Special - {0}", "VersionNumber": "Version {0}", - "TaskDownloadMissingSubtitlesDescription": "Søger på internettet efter manglende undertekster baseret på metadata konfiguration.", - "TaskDownloadMissingSubtitles": "Download manglende undertekster", - "TaskUpdatePluginsDescription": "Downloader og installere opdateringer for plugins som er konfigureret til at opdatere automatisk.", + "TaskDownloadMissingSubtitlesDescription": "Søger på internettet efter manglende undertekster baseret på metadata konfigurationen.", + "TaskDownloadMissingSubtitles": "Hent manglende undertekster", + "TaskUpdatePluginsDescription": "Henter og installerer opdateringer for plugins, som er indstillet til at blive opdateret automatisk.", "TaskUpdatePlugins": "Opdater Plugins", - "TaskCleanLogsDescription": "Sletter log filer som er mere end {0} dage gammle.", - "TaskCleanLogs": "Ryd Log Mappe", - "TaskRefreshLibraryDescription": "Scanner dit medie bibliotek for nye filer og opdaterer metadata.", + "TaskCleanLogsDescription": "Sletter log filer som er mere end {0} dage gamle.", + "TaskCleanLogs": "Ryd Log mappe", + "TaskRefreshLibraryDescription": "Scanner dit medie bibliotek for nye filer og opdateret metadata.", "TaskRefreshLibrary": "Scan Medie Bibliotek", - "TaskCleanCacheDescription": "Sletter cache filer som systemet ikke har brug for længere.", - "TaskCleanCache": "Ryd Cache Mappe", + "TaskCleanCacheDescription": "Sletter cache filer som systemet ikke længere bruger.", + "TaskCleanCache": "Ryd Cache mappe", "TasksChannelsCategory": "Internet Kanaler", "TasksApplicationCategory": "Applikation", "TasksLibraryCategory": "Bibliotek", "TasksMaintenanceCategory": "Vedligeholdelse", - "TaskRefreshChapterImages": "Udtræk Kapitel billeder", + "TaskRefreshChapterImages": "Udtræk kapitel billeder", "TaskRefreshChapterImagesDescription": "Lav miniaturebilleder for videoer der har kapitler.", - "TaskRefreshChannelsDescription": "Genopfrisker internet kanal information.", - "TaskRefreshChannels": "Genopfrisk Kanaler", - "TaskCleanTranscodeDescription": "Fjern transcode filer som er mere end en dag gammel.", - "TaskCleanTranscode": "Rengør Transcode Mappen", - "TaskRefreshPeople": "Genopfrisk Personer", - "TaskRefreshPeopleDescription": "Opdatere metadata for skuespillere og instruktører i dit bibliotek.", - "TaskCleanActivityLogDescription": "Sletter linjer i aktivitetsloggen ældre end den konfigureret alder.", + "TaskRefreshChannelsDescription": "Opdater internet kanal information.", + "TaskRefreshChannels": "Opdater Kanaler", + "TaskCleanTranscodeDescription": "Fjern transcode filer som er mere end 1 dag gammel.", + "TaskCleanTranscode": "Tøm Transcode mappen", + "TaskRefreshPeople": "Opdater Personer", + "TaskRefreshPeopleDescription": "Opdaterer metadata for skuespillere og instruktører i dit mediebibliotek.", + "TaskCleanActivityLogDescription": "Sletter linjer i aktivitetsloggen ældre end den konfigurerede alder.", "TaskCleanActivityLog": "Ryd Aktivitetslog", "Undefined": "Udefineret", "Forced": "Tvunget", "Default": "Standard", - "TaskOptimizeDatabaseDescription": "Kompakter database og forkorter fri plads. Ved at køre denne proces efter at scanne biblioteket eller efter at ændre noget som kunne have indflydelse på databasen, kan forbedre ydeevne.", + "TaskOptimizeDatabaseDescription": "Komprimerer databasen og frigør plads. Denne handling køres efter at have scannet mediebiblioteket, eller efter at have lavet ændringer til databasen, for at højne ydeevnen.", "TaskOptimizeDatabase": "Optimér database", - "TaskKeyframeExtractorDescription": "Udtrækker billeder fra videofiler for at lave mere præcise HLS playlister. Denne opgave kan godt tage lang tid.", - "TaskKeyframeExtractor": "Billedramme udtrækker", + "TaskKeyframeExtractorDescription": "Udtrækker billeder fra videofiler for at lave mere præcise HLS playlister. Denne opgave kan tage lang tid.", + "TaskKeyframeExtractor": "Nøglebillede udtræk", "External": "Ekstern", "HearingImpaired": "Hørehæmmet" } -- cgit v1.2.3 From d1646e9b8d7f29a4734593b6686691c3256be4b0 Mon Sep 17 00:00:00 2001 From: Sunip Mukherjee <sunipkmukherjee@gmail.com> Date: Wed, 29 Mar 2023 22:54:32 +0000 Subject: Translated using Weblate (Bengali) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/bn/ --- .../Localization/Core/bn.json | 38 +++++++++++++--------- 1 file changed, 22 insertions(+), 16 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/bn.json b/Emby.Server.Implementations/Localization/Core/bn.json index c3fbe24082..355fb3b21e 100644 --- a/Emby.Server.Implementations/Localization/Core/bn.json +++ b/Emby.Server.Implementations/Localization/Core/bn.json @@ -1,27 +1,27 @@ { "DeviceOnlineWithName": "{0}-এর সাথে সংযুক্ত হয়েছে", "DeviceOfflineWithName": "{0}-এর সাথে সংযোগ বিচ্ছিন্ন হয়েছে", - "Collections": "সংগ্রহ", + "Collections": "সংগ্রহশালা", "ChapterNameValue": "অধ্যায় {0}", - "Channels": "চ্যানেল", + "Channels": "চ্যানেলসমূহ", "CameraImageUploadedFrom": "{0} থেকে একটি নতুন ক্যামেরার চিত্র আপলোড করা হয়েছে", - "Books": "বই", + "Books": "পুস্তকসমূহ", "AuthenticationSucceededWithUserName": "{0} অনুমোদন সফল", - "Artists": "শিল্পীরা", + "Artists": "শিল্পীগণ", "Application": "অ্যাপ্লিকেশন", - "Albums": "অ্যালবামগুলো", + "Albums": "অ্যালবামসমূহ", "HeaderFavoriteEpisodes": "প্রিব পর্বগুলো", "HeaderFavoriteArtists": "প্রিয় শিল্পীরা", "HeaderFavoriteAlbums": "প্রিয় এলবামগুলো", "HeaderContinueWatching": "দেখতে থাকুন", - "HeaderAlbumArtists": "এলবাম শিল্পীবৃন্দ", - "Genres": "শৈলী", - "Folders": "ফোল্ডারগুলো", + "HeaderAlbumArtists": "অ্যালবাম শিল্পীবৃন্দ", + "Genres": "শৈলীধারাসমূহ", + "Folders": "ফোল্ডারসমূহ", "Favorites": "পছন্দসমূহ", "FailedLoginAttemptWithUserName": "{0} লগিন করতে ব্যর্থ হয়েছে", "AppDeviceValues": "অ্যাপ: {0}, ডিভাইস: {0}", "VersionNumber": "সংস্করণ {0}", - "ValueSpecialEpisodeName": "বিশেষ - {0}", + "ValueSpecialEpisodeName": "বিশেষ পর্ব - {0}", "ValueHasBeenAddedToLibrary": "আপনার লাইব্রেরিতে {0} যোগ করা হয়েছে", "UserStoppedPlayingItemWithValues": "{2}তে {1} বাজানো শেষ করেছেন {0}", "UserStartedPlayingItemWithValues": "{2}তে {1} বাজাচ্ছেন {0}", @@ -36,10 +36,10 @@ "User": "ব্যবহারকারী", "TvShows": "টিভি শোগুলো", "System": "সিস্টেম", - "Sync": "সিংক", + "Sync": "সমলয় স্থাপন", "SubtitleDownloadFailureFromForItem": "{2} থেকে {1} এর জন্য সাবটাইটেল ডাউনলোড ব্যর্থ", "StartupEmbyServerIsLoading": "জেলিফিন সার্ভার লোড হচ্ছে। দয়া করে একটু পরে আবার চেষ্টা করুন।", - "Songs": "গানগুলো", + "Songs": "সঙ্গীতসমূহ", "Shows": "টিভি পর্ব", "ServerNameNeedsToBeRestarted": "{0} রিস্টার্ট করা প্রয়োজন", "ScheduledTaskStartedWithName": "{0} শুরু হয়েছে", @@ -49,8 +49,8 @@ "PluginUninstalledWithName": "{0} বাদ দেয়া হয়েছে", "PluginInstalledWithName": "{0} ইন্সটল করা হয়েছে", "Plugin": "প্লাগিন", - "Playlists": "প্লেলিস্ট", - "Photos": "ছবিগুলো", + "Playlists": "প্লে লিস্ট সমূহ", + "Photos": "চিত্রসমূহ", "NotificationOptionVideoPlaybackStopped": "ভিডিও চলা বন্ধ", "NotificationOptionVideoPlayback": "ভিডিও চলা শুরু হয়েছে", "NotificationOptionUserLockedOut": "ব্যবহারকারী ঢুকতে পারছে না", @@ -71,9 +71,9 @@ "NameSeasonUnknown": "সিজন অজানা", "NameSeasonNumber": "সিজন {0}", "NameInstallFailed": "{0} ইন্সটল ব্যর্থ", - "MusicVideos": "গানের ভিডিও", + "MusicVideos": "সঙ্গীত ভিডিয়ো সমূহ", "Music": "গান", - "Movies": "চলচ্চিত্র", + "Movies": "চলচ্চিত্রসমূহ", "MixedContent": "মিশ্র কন্টেন্ট", "MessageServerConfigurationUpdated": "সার্ভারের কনফিগারেশন আপডেট করা হয়েছে", "HeaderRecordingGroups": "রেকর্ডিং দল", @@ -117,5 +117,11 @@ "Forced": "জোরকরে", "TaskCleanActivityLogDescription": "নির্ধারিত সময়ের আগের কাজের হিসাব মুছে দিন খালি করুন.", "TaskCleanActivityLog": "কাজের ফাইল খালি করুন", - "Default": "প্রাথমিক" + "Default": "প্রাথমিক", + "HearingImpaired": "দুর্বল শ্রবণক্ষমতাধরদের জন্য", + "TaskOptimizeDatabaseDescription": "তথ্যভাণ্ডার সুবিন্যস্ত করে ও অব্যবহৃত জায়গা ছেড়ে দেয়। লাইব্রেরী স্ক্যান অথবা যেকোনো তথ্যভাণ্ডার পরিবর্তনের পর এই প্রক্রিয়া চালালে তথ্যভাণ্ডারের তথ্য প্রদান দ্রুততর হতে পারে।", + "External": "বাহ্যিক", + "TaskOptimizeDatabase": "তথ্যভাণ্ডার সুবিন্যাস", + "TaskKeyframeExtractor": "কি-ফ্রেম নিষ্কাশক", + "TaskKeyframeExtractorDescription": "ভিডিয়ো থেকে কি-ফ্রেম নিষ্কাশনের মাধ্যমে অধিকতর সঠিক HLS প্লে লিস্ট তৈরী করে। এই প্রক্রিয়া দীর্ঘ সময় ধরে চলতে পারে।" } -- cgit v1.2.3 From 56f5b37fbf8b081a477f936dea5194850223494a Mon Sep 17 00:00:00 2001 From: pankajabhyam <pkkumar391@gmail.com> Date: Mon, 27 Mar 2023 08:36:58 +0000 Subject: Translated using Weblate (Hindi) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/hi/ --- .../Localization/Core/hi.json | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/hi.json b/Emby.Server.Implementations/Localization/Core/hi.json index a0e2f04a16..b62e64f4da 100644 --- a/Emby.Server.Implementations/Localization/Core/hi.json +++ b/Emby.Server.Implementations/Localization/Core/hi.json @@ -73,5 +73,23 @@ "Songs": "गाने", "UserStartedPlayingItemWithValues": "{0} {2} पर {1} खेल रहे हैं", "UserStoppedPlayingItemWithValues": "{0} ने {2} पर {1} खेलना खत्म किया", - "StartupEmbyServerIsLoading": "जेलीफ़िन सर्वर लोड हो रहा है। कृपया शीघ्र ही पुन: प्रयास करें।" + "StartupEmbyServerIsLoading": "जेलीफ़िन सर्वर लोड हो रहा है। कृपया शीघ्र ही पुन: प्रयास करें।", + "ServerNameNeedsToBeRestarted": "{0} रीस्टार्ट करने की आवश्यकता है", + "UserCreatedWithName": "उपयोगकर्ता {0} बनाया गया", + "UserDownloadingItemWithValues": "{0} डाउनलोड हो रहा है", + "UserOfflineFromDevice": "{0} {1} से डिस्कनेक्ट हो गया है", + "Undefined": "अनिर्धारित", + "UserOnlineFromDevice": "{0} {1} से ऑनलाइन है", + "Shows": "शो", + "UserPasswordChangedWithName": "उपयोगकर्ता {0} के लिए पासवर्ड बदल दिया गया है", + "UserDeletedWithName": "उपयोगकर्ता {0} हटा दिया गया", + "UserPolicyUpdatedWithName": "{0} के लिए उपयोगकर्ता नीति अपडेट कर दी गई है", + "User": "उपयोगकर्ता", + "SubtitleDownloadFailureFromForItem": "{1} के लिए {0} से उपशीर्षक डाउनलोड करने में विफल", + "ProviderValue": "प्रदाता: {0}", + "ScheduledTaskFailedWithName": "{0}असफल", + "UserLockedOutWithName": "उपयोगकर्ता {0} को लॉक आउट कर दिया गया है", + "System": "प्रणाली", + "TvShows": "टीवी शो", + "HearingImpaired": "मूक बधिर" } -- cgit v1.2.3 From eb59456e78b11379ba657503f08b1f7c9208f2ae Mon Sep 17 00:00:00 2001 From: Sahil Ahluwalia <satrader2000@gmail.com> Date: Wed, 29 Mar 2023 05:47:31 +0000 Subject: Translated using Weblate (Hindi) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/hi/ --- Emby.Server.Implementations/Localization/Core/hi.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/hi.json b/Emby.Server.Implementations/Localization/Core/hi.json index b62e64f4da..cc0a926d03 100644 --- a/Emby.Server.Implementations/Localization/Core/hi.json +++ b/Emby.Server.Implementations/Localization/Core/hi.json @@ -91,5 +91,7 @@ "UserLockedOutWithName": "उपयोगकर्ता {0} को लॉक आउट कर दिया गया है", "System": "प्रणाली", "TvShows": "टीवी शो", - "HearingImpaired": "मूक बधिर" + "HearingImpaired": "मूक बधिर", + "ValueSpecialEpisodeName": "विशेष", + "TasksMaintenanceCategory": "रखरखाव" } -- cgit v1.2.3 From a944352aa8b961ab723fed2d66947c19129a11cc Mon Sep 17 00:00:00 2001 From: AmbulantRex <21176662+AmbulantRex@users.noreply.github.com> Date: Sat, 1 Apr 2023 04:59:07 -0600 Subject: Correct style inconsistencies --- Emby.Server.Implementations/Library/PathExtensions.cs | 9 ++++++--- Emby.Server.Implementations/Plugins/PluginManager.cs | 2 +- MediaBrowser.Common/Plugins/PluginManifest.cs | 4 ++-- MediaBrowser.Model/Updates/VersionInfo.cs | 2 +- .../Test Data/Updates/manifest-stable.json | 2 +- 5 files changed, 11 insertions(+), 8 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/PathExtensions.cs b/Emby.Server.Implementations/Library/PathExtensions.cs index a3d748a138..62a9e6419e 100644 --- a/Emby.Server.Implementations/Library/PathExtensions.cs +++ b/Emby.Server.Implementations/Library/PathExtensions.cs @@ -87,8 +87,8 @@ namespace Emby.Server.Implementations.Library return false; } - subPath = subPath.NormalizePath(out var newDirectorySeparatorChar)!; - path = path.NormalizePath(newDirectorySeparatorChar)!; + subPath = subPath.NormalizePath(out var newDirectorySeparatorChar); + path = path.NormalizePath(newDirectorySeparatorChar); // We have to ensure that the sub path ends with a directory separator otherwise we'll get weird results // when the sub path matches a similar but in-complete subpath @@ -128,6 +128,7 @@ namespace Emby.Server.Implementations.Library /// </summary> /// <param name="path">The path to normalize.</param> /// <returns>The normalized path string or <see langword="null"/> if the input path is null or empty.</returns> + [return: NotNullIfNotNull(nameof(path))] public static string? NormalizePath(this string? path) { return path.NormalizePath(Path.DirectorySeparatorChar); @@ -139,6 +140,7 @@ namespace Emby.Server.Implementations.Library /// <param name="path">The path to normalize.</param> /// <param name="separator">The separator character the path now uses or <see langword="null"/>.</param> /// <returns>The normalized path string or <see langword="null"/> if the input path is null or empty.</returns> + [return: NotNullIfNotNull(nameof(path))] public static string? NormalizePath(this string? path, out char separator) { if (string.IsNullOrEmpty(path)) @@ -169,6 +171,7 @@ namespace Emby.Server.Implementations.Library /// <param name="newSeparator">The replacement directory separator character. Must be a valid directory separator.</param> /// <returns>The normalized path.</returns> /// <exception cref="ArgumentException">Thrown if the new separator character is not a directory separator.</exception> + [return: NotNullIfNotNull(nameof(path))] public static string? NormalizePath(this string? path, char newSeparator) { const char Bs = '\\'; @@ -184,7 +187,7 @@ namespace Emby.Server.Implementations.Library return path; } - return newSeparator == Bs ? path?.Replace(Fs, newSeparator) : path?.Replace(Bs, newSeparator); + return newSeparator == Bs ? path.Replace(Fs, newSeparator) : path.Replace(Bs, newSeparator); } } } diff --git a/Emby.Server.Implementations/Plugins/PluginManager.cs b/Emby.Server.Implementations/Plugins/PluginManager.cs index d253a0ab99..0a7c144ed7 100644 --- a/Emby.Server.Implementations/Plugins/PluginManager.cs +++ b/Emby.Server.Implementations/Plugins/PluginManager.cs @@ -765,7 +765,7 @@ namespace Emby.Server.Implementations.Plugins /// <exception cref="ArgumentNullException">If the <see cref="LocalPlugin"/> is null.</exception> private bool TryGetPluginDlls(LocalPlugin plugin, out IReadOnlyList<string> whitelistedDlls) { - _ = plugin ?? throw new ArgumentNullException(nameof(plugin)); + ArgumentNullException.ThrowIfNull(nameof(plugin)); IReadOnlyList<string> pluginDlls = Directory.GetFiles(plugin.Path, "*.dll", SearchOption.AllDirectories); diff --git a/MediaBrowser.Common/Plugins/PluginManifest.cs b/MediaBrowser.Common/Plugins/PluginManifest.cs index 2bad3454d1..e0847ccea4 100644 --- a/MediaBrowser.Common/Plugins/PluginManifest.cs +++ b/MediaBrowser.Common/Plugins/PluginManifest.cs @@ -24,7 +24,7 @@ namespace MediaBrowser.Common.Plugins Overview = string.Empty; TargetAbi = string.Empty; Version = string.Empty; - Assemblies = new List<string>(); + Assemblies = Array.Empty<string>(); } /// <summary> @@ -112,6 +112,6 @@ namespace MediaBrowser.Common.Plugins /// Paths are considered relative to the plugin folder. /// </summary> [JsonPropertyName("assemblies")] - public IList<string> Assemblies { get; set; } + public IReadOnlyList<string> Assemblies { get; set; } } } diff --git a/MediaBrowser.Model/Updates/VersionInfo.cs b/MediaBrowser.Model/Updates/VersionInfo.cs index 1e24bde84e..8f76806450 100644 --- a/MediaBrowser.Model/Updates/VersionInfo.cs +++ b/MediaBrowser.Model/Updates/VersionInfo.cs @@ -80,6 +80,6 @@ namespace MediaBrowser.Model.Updates /// Gets or sets the assemblies whitelist for this version. /// </summary> [JsonPropertyName("assemblies")] - public IList<string> Assemblies { get; set; } = Array.Empty<string>(); + public IReadOnlyList<string> Assemblies { get; set; } = Array.Empty<string>(); } } diff --git a/tests/Jellyfin.Server.Implementations.Tests/Test Data/Updates/manifest-stable.json b/tests/Jellyfin.Server.Implementations.Tests/Test Data/Updates/manifest-stable.json index d69a52d6d0..3aec299587 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Test Data/Updates/manifest-stable.json +++ b/tests/Jellyfin.Server.Implementations.Tests/Test Data/Updates/manifest-stable.json @@ -25,7 +25,7 @@ "timestamp": "2020-07-20T01:30:16Z", "assemblies": [ "Jellyfin.Plugin.Anime.dll" ] } - ] + ] }, { "guid": "70b7b43b-471b-4159-b4be-56750c795499", -- cgit v1.2.3 From 3a731051adf6d636517e5a9babbbe9f9da7d520b Mon Sep 17 00:00:00 2001 From: AmbulantRex <21176662+AmbulantRex@users.noreply.github.com> Date: Sat, 1 Apr 2023 05:03:55 -0600 Subject: Correct styling inconsistencies --- Emby.Server.Implementations/Library/PathExtensions.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/PathExtensions.cs b/Emby.Server.Implementations/Library/PathExtensions.cs index 62a9e6419e..c4b6b37561 100644 --- a/Emby.Server.Implementations/Library/PathExtensions.cs +++ b/Emby.Server.Implementations/Library/PathExtensions.cs @@ -105,7 +105,7 @@ namespace Emby.Server.Implementations.Library return false; } - var newSubPathTrimmed = newSubPath.AsSpan().TrimEnd((char)newDirectorySeparatorChar!); + var newSubPathTrimmed = newSubPath.AsSpan().TrimEnd(newDirectorySeparatorChar); // Ensure that the path with the old subpath removed starts with a leading dir separator int idx = oldSubPathEndsWithSeparator ? subPath.Length - 1 : subPath.Length; newPath = string.Concat(newSubPathTrimmed, path.AsSpan(idx)); @@ -120,7 +120,7 @@ namespace Emby.Server.Implementations.Library /// <returns>The fully expanded, normalized path.</returns> public static string Canonicalize(this string path) { - return Path.GetFullPath(path).NormalizePath()!; + return Path.GetFullPath(path).NormalizePath(); } /// <summary> @@ -161,7 +161,7 @@ namespace Emby.Server.Implementations.Library separator = newSeparator; - return path?.NormalizePath(newSeparator); + return path.NormalizePath(newSeparator); } /// <summary> -- cgit v1.2.3 From e74630a6137b77e76b7fc311859e6c81bcd776b5 Mon Sep 17 00:00:00 2001 From: Stepan Goremykin <goremukin@gmail.com> Date: Sat, 1 Apr 2023 23:00:51 +0200 Subject: Use MinBy and MaxBy --- Emby.Server.Implementations/Plugins/PluginManager.cs | 2 +- Emby.Server.Implementations/Session/WebSocketController.cs | 4 +--- Jellyfin.Api/Controllers/DynamicHlsController.cs | 4 +--- Jellyfin.Api/Controllers/PluginsController.cs | 2 +- Jellyfin.Networking/Manager/NetworkManager.cs | 3 +-- 5 files changed, 5 insertions(+), 10 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Plugins/PluginManager.cs b/Emby.Server.Implementations/Plugins/PluginManager.cs index 7c23254a12..f14f87ccda 100644 --- a/Emby.Server.Implementations/Plugins/PluginManager.cs +++ b/Emby.Server.Implementations/Plugins/PluginManager.cs @@ -304,7 +304,7 @@ namespace Emby.Server.Implementations.Plugins // If no version is given, return the current instance. var plugins = _plugins.Where(p => p.Id.Equals(id)).ToList(); - plugin = plugins.FirstOrDefault(p => p.Instance is not null) ?? plugins.OrderByDescending(p => p.Version).FirstOrDefault(); + plugin = plugins.FirstOrDefault(p => p.Instance is not null) ?? plugins.MaxBy(p => p.Version); } else { diff --git a/Emby.Server.Implementations/Session/WebSocketController.cs b/Emby.Server.Implementations/Session/WebSocketController.cs index 051fa5b3cf..cdc736950e 100644 --- a/Emby.Server.Implementations/Session/WebSocketController.cs +++ b/Emby.Server.Implementations/Session/WebSocketController.cs @@ -69,9 +69,7 @@ namespace Emby.Server.Implementations.Session T data, CancellationToken cancellationToken) { - var socket = GetActiveSockets() - .OrderByDescending(i => i.LastActivityDate) - .FirstOrDefault(); + var socket = GetActiveSockets().MaxBy(i => i.LastActivityDate); if (socket is null) { diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index 42a7ac2b16..09cf7303e7 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -20,7 +20,6 @@ using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; -using MediaBrowser.Controller.Net; using MediaBrowser.MediaEncoding.Encoder; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Dlna; @@ -2030,8 +2029,7 @@ public class DynamicHlsController : BaseJellyfinApiController { return fileSystem.GetFiles(folder, new[] { segmentExtension }, true, false) .Where(i => Path.GetFileNameWithoutExtension(i.Name).StartsWith(filePrefix, StringComparison.OrdinalIgnoreCase)) - .OrderByDescending(fileSystem.GetLastWriteTimeUtc) - .FirstOrDefault(); + .MaxBy(fileSystem.GetLastWriteTimeUtc); } catch (IOException) { diff --git a/Jellyfin.Api/Controllers/PluginsController.cs b/Jellyfin.Api/Controllers/PluginsController.cs index 4726cf0663..72ad14a281 100644 --- a/Jellyfin.Api/Controllers/PluginsController.cs +++ b/Jellyfin.Api/Controllers/PluginsController.cs @@ -146,7 +146,7 @@ public class PluginsController : BaseJellyfinApiController var plugins = _pluginManager.Plugins.Where(p => p.Id.Equals(pluginId)).ToList(); // Select the un-instanced one first. - var plugin = plugins.FirstOrDefault(p => p.Instance is null) ?? plugins.OrderBy(p => p.Manifest.Status).FirstOrDefault(); + var plugin = plugins.FirstOrDefault(p => p.Instance is null) ?? plugins.MinBy(p => p.Manifest.Status); if (plugin is not null) { diff --git a/Jellyfin.Networking/Manager/NetworkManager.cs b/Jellyfin.Networking/Manager/NetworkManager.cs index f406e27a6d..ac9258ad13 100644 --- a/Jellyfin.Networking/Manager/NetworkManager.cs +++ b/Jellyfin.Networking/Manager/NetworkManager.cs @@ -1256,8 +1256,7 @@ namespace Jellyfin.Networking.Manager // Look for the best internal address. bindAddress = addresses .Where(p => IsInLocalNetwork(p) && (p.Contains(source) || p.Equals(IPAddress.None))) - .OrderBy(p => p.Tag) - .FirstOrDefault()?.Address; + .MinBy(p => p.Tag)?.Address; } if (bindAddress is not null) -- cgit v1.2.3 From bb5bf0277ae33fd4668e00bd2f1a85e89aef466a Mon Sep 17 00:00:00 2001 From: elmuffo <elmuffo@pm.me> Date: Sun, 2 Apr 2023 09:01:05 +1200 Subject: Implement check to hide all libraries when user has no access (#9536) --- Emby.Server.Implementations/Library/LibraryManager.cs | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index e5c520ca2b..36a300a572 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -1503,6 +1503,12 @@ namespace Emby.Server.Implementations.Library }); query.TopParentIds = userViews.SelectMany(i => GetTopParentIdsForQuery(i, user)).ToArray(); + + // Prevent searching in all libraries due to empty filter + if (query.TopParentIds.Length == 0) + { + query.TopParentIds = new[] { Guid.NewGuid() }; + } } } -- cgit v1.2.3 From 078d7da3dab3e72feca1d18bc21669d04c45b70a Mon Sep 17 00:00:00 2001 From: SuperDumbTM <eltonlochunkit@gmail.com> Date: Tue, 4 Apr 2023 03:32:35 +0000 Subject: Translated using Weblate (Chinese (Traditional, Hong Kong)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/zh_Hant_HK/ --- .../Localization/Core/zh-HK.json | 92 +++++++++++----------- 1 file changed, 46 insertions(+), 46 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/zh-HK.json b/Emby.Server.Implementations/Localization/Core/zh-HK.json index cdc25ec7c7..b08119d0a6 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-HK.json +++ b/Emby.Server.Implementations/Localization/Core/zh-HK.json @@ -5,12 +5,12 @@ "Artists": "藝人", "AuthenticationSucceededWithUserName": "{0} 授權成功", "Books": "圖書", - "CameraImageUploadedFrom": "{0} 成功上傳一張新相片", + "CameraImageUploadedFrom": "{0} 成功上傳一張新照片", "Channels": "頻道", - "ChapterNameValue": "章節 {0}", + "ChapterNameValue": "第 {0} 章", "Collections": "合輯", - "DeviceOfflineWithName": "{0} 已經斷開連接", - "DeviceOnlineWithName": "{0} 已經連接", + "DeviceOfflineWithName": "{0} 已斷開連接", + "DeviceOnlineWithName": "{0} 已連接", "FailedLoginAttemptWithUserName": "{0} 登入失敗", "Favorites": "我的最愛", "Folders": "資料夾", @@ -23,43 +23,43 @@ "HeaderFavoriteShows": "最愛的節目", "HeaderFavoriteSongs": "最愛的歌曲", "HeaderLiveTV": "電視直播", - "HeaderNextUp": "接下來", + "HeaderNextUp": "接著播放", "HeaderRecordingGroups": "錄製組", "HomeVideos": "家庭影片", "Inherit": "繼承", - "ItemAddedWithName": "{0} 已添加至媒體庫", + "ItemAddedWithName": "{0} 已被添加至媒體庫", "ItemRemovedWithName": "{0} 已從媒體庫移除", "LabelIpAddressValue": "IP 地址: {0}", "LabelRunningTimeValue": "運行時間: {0}", "Latest": "最新", - "MessageApplicationUpdated": "Jellyfin 伺服器已更新", - "MessageApplicationUpdatedTo": "Jellyfin 伺服器已更新至 {0}", - "MessageNamedServerConfigurationUpdatedWithValue": "伺服器設定 {0} 已更新", - "MessageServerConfigurationUpdated": "伺服器設定已經更新", + "MessageApplicationUpdated": "Jellyfin 已被更新", + "MessageApplicationUpdatedTo": "Jellyfin 已被更新至 {0}", + "MessageNamedServerConfigurationUpdatedWithValue": "伺服器設定 {0} 已被更新", + "MessageServerConfigurationUpdated": "伺服器設定已經被更新", "MixedContent": "混合內容", "Movies": "電影", "Music": "音樂", - "MusicVideos": "音樂影片", + "MusicVideos": "MV", "NameInstallFailed": "{0} 安裝失敗", "NameSeasonNumber": "第 {0} 季", - "NameSeasonUnknown": "未知季數", - "NewVersionIsAvailable": "新版本的 Jellyfin 伺服器可供下載。", + "NameSeasonUnknown": "未知的季度", + "NewVersionIsAvailable": "有較新版本的 Jellyfin 可供下載。", "NotificationOptionApplicationUpdateAvailable": "有可用的更新", - "NotificationOptionApplicationUpdateInstalled": "應用程式已更新", + "NotificationOptionApplicationUpdateInstalled": "應用程式已被更新", "NotificationOptionAudioPlayback": "開始播放音訊", - "NotificationOptionAudioPlaybackStopped": "已停止播放音訊", - "NotificationOptionCameraImageUploaded": "相片已上傳", + "NotificationOptionAudioPlaybackStopped": "停止播放音訊", + "NotificationOptionCameraImageUploaded": "相片已被上傳", "NotificationOptionInstallationFailed": "安裝失敗", "NotificationOptionNewLibraryContent": "已添加新内容", - "NotificationOptionPluginError": "擴充元件錯誤", - "NotificationOptionPluginInstalled": "擴充元件已安裝", - "NotificationOptionPluginUninstalled": "擴充元件已移除", - "NotificationOptionPluginUpdateInstalled": "擴充元件更新已安裝", - "NotificationOptionServerRestartRequired": "伺服器需要重啓", - "NotificationOptionTaskFailed": "計劃任務失敗", - "NotificationOptionUserLockedOut": "用家已鎖定", - "NotificationOptionVideoPlayback": "開始播放視頻", - "NotificationOptionVideoPlaybackStopped": "已停止播放視頻", + "NotificationOptionPluginError": "插件出現錯誤", + "NotificationOptionPluginInstalled": "插件已被安裝", + "NotificationOptionPluginUninstalled": "插件已被移除", + "NotificationOptionPluginUpdateInstalled": "插件已被更新", + "NotificationOptionServerRestartRequired": "伺服器需要重啟", + "NotificationOptionTaskFailed": "排程任務執行失敗", + "NotificationOptionUserLockedOut": "用戶已被鎖定", + "NotificationOptionVideoPlayback": "開始播放影片", + "NotificationOptionVideoPlaybackStopped": "已停止播放影片", "Photos": "相片", "Playlists": "播放清單", "Plugin": "插件", @@ -69,51 +69,51 @@ "ProviderValue": "提供者: {0}", "ScheduledTaskFailedWithName": "{0} 任務失敗", "ScheduledTaskStartedWithName": "{0} 任務開始", - "ServerNameNeedsToBeRestarted": "{0} 需要重啓", + "ServerNameNeedsToBeRestarted": "{0} 需要重啟", "Shows": "節目", "Songs": "歌曲", - "StartupEmbyServerIsLoading": "Jellyfin 伺服器載入中,請稍後再試。", + "StartupEmbyServerIsLoading": "Jellyfin 載入中,請稍後再試。", "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", "SubtitleDownloadFailureFromForItem": "無法從 {0} 下載 {1} 的字幕", "Sync": "同步", "System": "系統", "TvShows": "電視節目", - "User": "使用者", - "UserCreatedWithName": "使用者 {0} 已創建", - "UserDeletedWithName": "使用者 {0} 已移除", + "User": "用戶", + "UserCreatedWithName": "用戶 {0} 已被建立", + "UserDeletedWithName": "用戶 {0} 已被移除", "UserDownloadingItemWithValues": "{0} 正在下載 {1}", "UserLockedOutWithName": "使用者 {0} 已被鎖定", - "UserOfflineFromDevice": "{0} 已從 {1} 斷開", - "UserOnlineFromDevice": "{0} 已連綫,來自 {1}", - "UserPasswordChangedWithName": "使用者 {0} 的密碼已變更", + "UserOfflineFromDevice": "{0} 從 {1} 斷開連接", + "UserOnlineFromDevice": "{0} 從 {1} 連線", + "UserPasswordChangedWithName": "{0} 的密碼已被變改", "UserPolicyUpdatedWithName": "使用者協議已更新為 {0}", "UserStartedPlayingItemWithValues": "{0} 正在 {2} 上播放 {1}", - "UserStoppedPlayingItemWithValues": "{0} 已在 {2} 上停止播放 {1}", - "ValueHasBeenAddedToLibrary": "{0} 已添加到你的媒體庫", + "UserStoppedPlayingItemWithValues": "{0} 已停止在 {2} 上播放 {1}", + "ValueHasBeenAddedToLibrary": "已添加 {0} 到你的媒體庫", "ValueSpecialEpisodeName": "特典 - {0}", - "VersionNumber": "版本{0}", - "TaskDownloadMissingSubtitles": "下載遺失的字幕", + "VersionNumber": "版本 {0}", + "TaskDownloadMissingSubtitles": "下載缺少的字幕", "TaskUpdatePlugins": "更新插件", "TasksApplicationCategory": "應用程式", - "TaskRefreshLibraryDescription": "掃描媒體庫以查找新文件並刷新metadata。", + "TaskRefreshLibraryDescription": "掃描媒體庫以加入新增檔案及重新載入 metadata。", "TasksMaintenanceCategory": "維護", - "TaskDownloadMissingSubtitlesDescription": "根據metadata配置在互聯網上搜索缺少的字幕。", - "TaskRefreshChannelsDescription": "刷新互聯網頻道信息。", - "TaskRefreshChannels": "刷新頻道", + "TaskDownloadMissingSubtitlesDescription": "根據元數據中的設定,在互聯網上搜索缺少的字幕。", + "TaskRefreshChannelsDescription": "重新載入網絡頻道的資訊。", + "TaskRefreshChannels": "重新載入頻道", "TaskCleanTranscodeDescription": "刪除超過一天的轉碼文件。", "TaskCleanTranscode": "清理轉碼目錄", - "TaskUpdatePluginsDescription": "下載並安裝配置為自動更新的插件的更新。", + "TaskUpdatePluginsDescription": "下載並更新能夠被自動更新的插件。", "TaskRefreshPeopleDescription": "更新媒體庫中演員和導演的元數據。", "TaskCleanLogsDescription": "刪除超過{0}天的日誌文件。", "TaskCleanLogs": "清理日誌目錄", "TaskRefreshLibrary": "掃描媒體庫", - "TaskRefreshChapterImagesDescription": "為帶有章節的視頻創建縮略圖。", + "TaskRefreshChapterImagesDescription": "為帶有章節的影片建立縮圖。", "TaskRefreshChapterImages": "提取章節圖像", "TaskCleanCacheDescription": "刪除系統不再需要的緩存文件。", "TaskCleanCache": "清理緩存目錄", - "TasksChannelsCategory": "互聯網頻道", + "TasksChannelsCategory": "網絡頻道", "TasksLibraryCategory": "庫", - "TaskRefreshPeople": "刷新人物", + "TaskRefreshPeople": "重新載入人物", "TaskCleanActivityLog": "清理活動記錄", "Undefined": "未定義", "Forced": "強制", @@ -121,7 +121,7 @@ "TaskOptimizeDatabaseDescription": "壓縮數據庫並截斷可用空間。在掃描媒體庫或執行其他數據庫的修改後運行此任務可能會提高性能。", "TaskOptimizeDatabase": "最佳化數據庫", "TaskCleanActivityLogDescription": "刪除早於設定時間的日誌記錄。", - "TaskKeyframeExtractorDescription": "提取關鍵格以創建更準確的HLS播放列表。次指示可能用時很長。", + "TaskKeyframeExtractorDescription": "提取關鍵幀以建立更準確的 HLS 播放列表。此工作或需要使用較長時間來完成。", "TaskKeyframeExtractor": "關鍵幀提取器", "External": "外部", "HearingImpaired": "聽力障礙" -- cgit v1.2.3 From 26958162d01ea9557d80f384face93012f70b821 Mon Sep 17 00:00:00 2001 From: Stepan Goremykin <goremukin@gmail.com> Date: Thu, 6 Apr 2023 18:41:25 +0200 Subject: Remove unused using directives --- Emby.Dlna/Main/DlnaEntryPoint.cs | 1 - Emby.Server.Implementations/Dto/DtoService.cs | 1 - Jellyfin.Api/Controllers/FilterController.cs | 1 - Jellyfin.Api/Controllers/QuickConnectController.cs | 2 -- Jellyfin.Api/Controllers/SearchController.cs | 1 - Jellyfin.Api/Controllers/UniversalAudioController.cs | 1 - Jellyfin.Server.Implementations/Security/AuthorizationContext.cs | 1 - Jellyfin.Server/CoreAppHost.cs | 1 - Jellyfin.Server/Startup.cs | 1 - MediaBrowser.Controller/Lyrics/LyricMetadata.cs | 2 -- MediaBrowser.Controller/Session/ISessionManager.cs | 1 - MediaBrowser.Controller/Subtitles/ISubtitleManager.cs | 1 - MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs | 1 - MediaBrowser.Model/Configuration/ServerConfiguration.cs | 1 - MediaBrowser.Providers/Lyric/TxtLyricProvider.cs | 1 - src/Jellyfin.Extensions/Json/Converters/JsonBoolStringConverter.cs | 1 - src/Jellyfin.Extensions/StringExtensions.cs | 2 -- .../Auth/FirstTimeSetupPolicy/FirstTimeSetupHandlerTests.cs | 1 - tests/Jellyfin.Api.Tests/Controllers/ImageControllerTests.cs | 1 - 19 files changed, 22 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs index aab475153b..39cfc2d1d4 100644 --- a/Emby.Dlna/Main/DlnaEntryPoint.cs +++ b/Emby.Dlna/Main/DlnaEntryPoint.cs @@ -7,7 +7,6 @@ using System.Globalization; using System.Linq; using System.Net.Http; using System.Net.Sockets; -using System.Runtime.InteropServices; using System.Threading.Tasks; using Emby.Dlna.PlayTo; using Emby.Dlna.Ssdp; diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index 8b66829032..c725839fba 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -7,7 +7,6 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; -using Jellyfin.Api.Helpers; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; using Jellyfin.Extensions; diff --git a/Jellyfin.Api/Controllers/FilterController.cs b/Jellyfin.Api/Controllers/FilterController.cs index dac07429ff..d51a5325f5 100644 --- a/Jellyfin.Api/Controllers/FilterController.cs +++ b/Jellyfin.Api/Controllers/FilterController.cs @@ -1,6 +1,5 @@ using System; using System.Linq; -using Jellyfin.Api.Constants; using Jellyfin.Api.Helpers; using Jellyfin.Api.ModelBinders; using Jellyfin.Data.Enums; diff --git a/Jellyfin.Api/Controllers/QuickConnectController.cs b/Jellyfin.Api/Controllers/QuickConnectController.cs index d7e54b5b6a..14f5265aa7 100644 --- a/Jellyfin.Api/Controllers/QuickConnectController.cs +++ b/Jellyfin.Api/Controllers/QuickConnectController.cs @@ -1,8 +1,6 @@ using System; using System.ComponentModel.DataAnnotations; using System.Threading.Tasks; -using Jellyfin.Api.Constants; -using Jellyfin.Api.Extensions; using Jellyfin.Api.Helpers; using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Authentication; diff --git a/Jellyfin.Api/Controllers/SearchController.cs b/Jellyfin.Api/Controllers/SearchController.cs index f638c31c39..387b3ea5a6 100644 --- a/Jellyfin.Api/Controllers/SearchController.cs +++ b/Jellyfin.Api/Controllers/SearchController.cs @@ -3,7 +3,6 @@ using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Globalization; using System.Linq; -using Jellyfin.Api.Constants; using Jellyfin.Api.Helpers; using Jellyfin.Api.ModelBinders; using Jellyfin.Data.Enums; diff --git a/Jellyfin.Api/Controllers/UniversalAudioController.cs b/Jellyfin.Api/Controllers/UniversalAudioController.cs index 12d033ae63..2e9035d24f 100644 --- a/Jellyfin.Api/Controllers/UniversalAudioController.cs +++ b/Jellyfin.Api/Controllers/UniversalAudioController.cs @@ -5,7 +5,6 @@ using System.Globalization; using System.Linq; using System.Threading.Tasks; using Jellyfin.Api.Attributes; -using Jellyfin.Api.Extensions; using Jellyfin.Api.Helpers; using Jellyfin.Api.ModelBinders; using Jellyfin.Api.Models.StreamingDtos; diff --git a/Jellyfin.Server.Implementations/Security/AuthorizationContext.cs b/Jellyfin.Server.Implementations/Security/AuthorizationContext.cs index 63d3e8a04c..700e639700 100644 --- a/Jellyfin.Server.Implementations/Security/AuthorizationContext.cs +++ b/Jellyfin.Server.Implementations/Security/AuthorizationContext.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Net; using System.Threading.Tasks; -using EFCoreSecondLevelCacheInterceptor; using MediaBrowser.Controller; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Net; diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs index 40cd5a0446..939376dd8d 100644 --- a/Jellyfin.Server/CoreAppHost.cs +++ b/Jellyfin.Server/CoreAppHost.cs @@ -22,7 +22,6 @@ using MediaBrowser.Controller.Lyrics; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Security; using MediaBrowser.Model.Activity; -using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; diff --git a/Jellyfin.Server/Startup.cs b/Jellyfin.Server/Startup.cs index 155f9fc8c1..56f5c4796e 100644 --- a/Jellyfin.Server/Startup.cs +++ b/Jellyfin.Server/Startup.cs @@ -4,7 +4,6 @@ using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Net.Mime; -using System.Runtime.InteropServices; using System.Text; using Jellyfin.Api.Middleware; using Jellyfin.MediaEncoding.Hls.Extensions; diff --git a/MediaBrowser.Controller/Lyrics/LyricMetadata.cs b/MediaBrowser.Controller/Lyrics/LyricMetadata.cs index 6091ede52a..c4f0334892 100644 --- a/MediaBrowser.Controller/Lyrics/LyricMetadata.cs +++ b/MediaBrowser.Controller/Lyrics/LyricMetadata.cs @@ -1,5 +1,3 @@ -using System; - namespace MediaBrowser.Controller.Lyrics; /// <summary> diff --git a/MediaBrowser.Controller/Session/ISessionManager.cs b/MediaBrowser.Controller/Session/ISessionManager.cs index eefc5d222e..0c4719a0e5 100644 --- a/MediaBrowser.Controller/Session/ISessionManager.cs +++ b/MediaBrowser.Controller/Session/ISessionManager.cs @@ -7,7 +7,6 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Jellyfin.Data.Entities.Security; -using Jellyfin.Data.Events; using MediaBrowser.Controller.Authentication; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Session; diff --git a/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs b/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs index b86e482435..fcfc18a644 100644 --- a/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs +++ b/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs @@ -1,7 +1,6 @@ #pragma warning disable CS1591 using System; -using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Controller.Entities; diff --git a/MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs b/MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs index ea520b1d6b..55480253dd 100644 --- a/MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs +++ b/MediaBrowser.MediaEncoding/BdInfo/BdInfoDirectoryInfo.cs @@ -1,4 +1,3 @@ -using System; using System.IO; using System.Linq; using BDInfo.IO; diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index 7cb07a2f7a..07f02d1879 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -2,7 +2,6 @@ #pragma warning disable CA1819 using System; -using System.Collections.Generic; using MediaBrowser.Model.Drawing; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Updates; diff --git a/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs b/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs index 96a9e9dcf3..a9099d1927 100644 --- a/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.IO; -using System.Linq; using System.Threading.Tasks; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Lyrics; diff --git a/src/Jellyfin.Extensions/Json/Converters/JsonBoolStringConverter.cs b/src/Jellyfin.Extensions/Json/Converters/JsonBoolStringConverter.cs index 2936fe4d6d..6895eadb86 100644 --- a/src/Jellyfin.Extensions/Json/Converters/JsonBoolStringConverter.cs +++ b/src/Jellyfin.Extensions/Json/Converters/JsonBoolStringConverter.cs @@ -1,7 +1,6 @@ using System; using System.Buffers; using System.Buffers.Text; -using System.Linq; using System.Text.Json; using System.Text.Json.Serialization; diff --git a/src/Jellyfin.Extensions/StringExtensions.cs b/src/Jellyfin.Extensions/StringExtensions.cs index 7c61248757..b22eb7c4ea 100644 --- a/src/Jellyfin.Extensions/StringExtensions.cs +++ b/src/Jellyfin.Extensions/StringExtensions.cs @@ -1,6 +1,4 @@ using System; -using System.Globalization; -using System.Text; using System.Text.RegularExpressions; namespace Jellyfin.Extensions diff --git a/tests/Jellyfin.Api.Tests/Auth/FirstTimeSetupPolicy/FirstTimeSetupHandlerTests.cs b/tests/Jellyfin.Api.Tests/Auth/FirstTimeSetupPolicy/FirstTimeSetupHandlerTests.cs index 6669a6689c..1ea1797ba1 100644 --- a/tests/Jellyfin.Api.Tests/Auth/FirstTimeSetupPolicy/FirstTimeSetupHandlerTests.cs +++ b/tests/Jellyfin.Api.Tests/Auth/FirstTimeSetupPolicy/FirstTimeSetupHandlerTests.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Threading.Tasks; using AutoFixture; using AutoFixture.AutoMoq; -using Jellyfin.Api.Auth.DefaultAuthorizationPolicy; using Jellyfin.Api.Auth.FirstTimeSetupPolicy; using Jellyfin.Api.Constants; using MediaBrowser.Common.Configuration; diff --git a/tests/Jellyfin.Api.Tests/Controllers/ImageControllerTests.cs b/tests/Jellyfin.Api.Tests/Controllers/ImageControllerTests.cs index d6428fb2cd..0254a1ec6e 100644 --- a/tests/Jellyfin.Api.Tests/Controllers/ImageControllerTests.cs +++ b/tests/Jellyfin.Api.Tests/Controllers/ImageControllerTests.cs @@ -1,4 +1,3 @@ -using System; using Jellyfin.Api.Controllers; using Xunit; -- cgit v1.2.3 From 7d7e177265a6725b58669a2717af85c031f67996 Mon Sep 17 00:00:00 2001 From: Stepan Goremykin <goremukin@gmail.com> Date: Thu, 6 Apr 2023 18:46:28 +0200 Subject: Remove redundant casts --- Emby.Server.Implementations/Data/BaseSqliteRepository.cs | 2 +- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 4 ++-- MediaBrowser.Model/Dlna/StreamInfo.cs | 2 +- tests/Jellyfin.Model.Tests/Dlna/StreamBuilderTests.cs | 6 +++--- tests/Jellyfin.XbmcMetadata.Tests/Parsers/SeriesNfoParserTests.cs | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs index 1d61667f86..47706a4401 100644 --- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs +++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs @@ -139,7 +139,7 @@ namespace Emby.Server.Implementations.Data if (JournalSizeLimit.HasValue) { - WriteConnection.Execute("PRAGMA journal_size_limit=" + (int)JournalSizeLimit.Value); + WriteConnection.Execute("PRAGMA journal_size_limit=" + JournalSizeLimit.Value); } if (Synchronous.HasValue) diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 0124c66c74..5430ea2043 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -2614,8 +2614,8 @@ namespace MediaBrowser.Controller.MediaEncoding if (outputWidth > maximumWidth || outputHeight > maximumHeight) { - var scaleW = (double)maximumWidth / (double)outputWidth; - var scaleH = (double)maximumHeight / (double)outputHeight; + var scaleW = (double)maximumWidth / outputWidth; + var scaleH = (double)maximumHeight / outputHeight; var scale = Math.Min(scaleW, scaleH); outputWidth = Math.Min(maximumWidth, (int)(outputWidth * scale)); outputHeight = Math.Min(maximumHeight, (int)(outputHeight * scale)); diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs index 886b64a248..a78a28e13a 100644 --- a/MediaBrowser.Model/Dlna/StreamInfo.cs +++ b/MediaBrowser.Model/Dlna/StreamInfo.cs @@ -430,7 +430,7 @@ namespace MediaBrowser.Model.Dlna return totalBitrate.HasValue ? Convert.ToInt64(totalBitrate.Value * totalSeconds) : - (long?)null; + null; } return null; diff --git a/tests/Jellyfin.Model.Tests/Dlna/StreamBuilderTests.cs b/tests/Jellyfin.Model.Tests/Dlna/StreamBuilderTests.cs index f05a0152e0..a1c73d57e0 100644 --- a/tests/Jellyfin.Model.Tests/Dlna/StreamBuilderTests.cs +++ b/tests/Jellyfin.Model.Tests/Dlna/StreamBuilderTests.cs @@ -162,7 +162,7 @@ namespace Jellyfin.Model.Tests [InlineData("Tizen4-4K-5.1", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectPlay)] [InlineData("Tizen4-4K-5.1", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectPlay)] [InlineData("Tizen4-4K-5.1", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectPlay)] - public async Task BuildVideoItemSimple(string deviceName, string mediaSource, PlayMethod? playMethod, TranscodeReason why = (TranscodeReason)0, string transcodeMode = "DirectStream", string transcodeProtocol = "") + public async Task BuildVideoItemSimple(string deviceName, string mediaSource, PlayMethod? playMethod, TranscodeReason why = 0, string transcodeMode = "DirectStream", string transcodeProtocol = "") { var options = await GetMediaOptions(deviceName, mediaSource); BuildVideoItemSimpleTest(options, playMethod, why, transcodeMode, transcodeProtocol); @@ -260,7 +260,7 @@ namespace Jellyfin.Model.Tests [InlineData("Tizen4-4K-5.1", "mkv-vp9-aac-srt-2600k", PlayMethod.DirectPlay)] [InlineData("Tizen4-4K-5.1", "mkv-vp9-ac3-srt-2600k", PlayMethod.DirectPlay)] [InlineData("Tizen4-4K-5.1", "mkv-vp9-vorbis-vtt-2600k", PlayMethod.DirectPlay)] - public async Task BuildVideoItemWithFirstExplicitStream(string deviceName, string mediaSource, PlayMethod? playMethod, TranscodeReason why = (TranscodeReason)0, string transcodeMode = "DirectStream", string transcodeProtocol = "") + public async Task BuildVideoItemWithFirstExplicitStream(string deviceName, string mediaSource, PlayMethod? playMethod, TranscodeReason why = 0, string transcodeMode = "DirectStream", string transcodeProtocol = "") { var options = await GetMediaOptions(deviceName, mediaSource); options.AudioStreamIndex = 1; @@ -296,7 +296,7 @@ namespace Jellyfin.Model.Tests // Tizen 4 4K 5.1 [InlineData("Tizen4-4K-5.1", "mp4-h264-ac3-aac-srt-2600k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] [InlineData("Tizen4-4K-5.1", "mp4-hevc-ac3-aac-srt-15200k", PlayMethod.DirectPlay, (TranscodeReason)0, "Remux")] - public async Task BuildVideoItemWithDirectPlayExplicitStreams(string deviceName, string mediaSource, PlayMethod? playMethod, TranscodeReason why = (TranscodeReason)0, string transcodeMode = "DirectStream", string transcodeProtocol = "") + public async Task BuildVideoItemWithDirectPlayExplicitStreams(string deviceName, string mediaSource, PlayMethod? playMethod, TranscodeReason why = 0, string transcodeMode = "DirectStream", string transcodeProtocol = "") { var options = await GetMediaOptions(deviceName, mediaSource); var streamCount = options.MediaSources[0].MediaStreams.Count; diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/SeriesNfoParserTests.cs b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/SeriesNfoParserTests.cs index 542324e78b..f680d2dcc5 100644 --- a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/SeriesNfoParserTests.cs +++ b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/SeriesNfoParserTests.cs @@ -90,7 +90,7 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers }; _parser.Fetch(result, path, CancellationToken.None); - var item = (Series)result.Item; + var item = result.Item; Assert.Equal(id, item.ProviderIds[provider]); } -- cgit v1.2.3 From 1c0bb828d23859df363e46d18462c425ae1680fa Mon Sep 17 00:00:00 2001 From: Stepan Goremykin <goremukin@gmail.com> Date: Thu, 6 Apr 2023 19:05:05 +0200 Subject: Fix argument is not used in message template warning --- Emby.Server.Implementations/Session/SessionManager.cs | 4 ++-- MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index afa3721b88..5f6dc93fb3 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -606,7 +606,7 @@ namespace Emby.Server.Implementations.Session } catch (Exception ex) { - _logger.LogDebug("Error calling OnPlaybackStopped", ex); + _logger.LogDebug(ex, "Error calling OnPlaybackStopped"); } } @@ -953,7 +953,7 @@ namespace Emby.Server.Implementations.Session } catch (Exception ex) { - _logger.LogError("Error closing live stream", ex); + _logger.LogError(ex, "Error closing live stream"); } } diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs index 90bc491322..3b85988715 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs @@ -449,7 +449,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles { try { - _logger.LogInformation("Deleting converted subtitle due to failure: ", outputPath); + _logger.LogInformation("Deleting converted subtitle due to failure: {Path}", outputPath); _fileSystem.DeleteFile(outputPath); } catch (IOException ex) -- cgit v1.2.3 From c051736c800c56767e238ccf2b93054cf5a719f1 Mon Sep 17 00:00:00 2001 From: Stepan Goremykin <goremukin@gmail.com> Date: Thu, 6 Apr 2023 19:21:29 +0200 Subject: Inline out variable declaration --- Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs | 3 +-- Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs | 3 +-- RSSDP/HttpRequestParser.cs | 3 +-- RSSDP/HttpResponseParser.cs | 3 +-- RSSDP/SsdpDeviceLocator.cs | 6 ++---- RSSDP/SsdpDevicePublisher.cs | 6 ++---- tests/Jellyfin.Providers.Tests/Manager/MetadataServiceTests.cs | 7 ++----- 7 files changed, 10 insertions(+), 21 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index e7f4d2f4eb..477fd9df1e 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -1866,8 +1866,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { await writer.WriteStartDocumentAsync(true).ConfigureAwait(false); await writer.WriteStartElementAsync(null, "tvshow", null).ConfigureAwait(false); - string id; - if (timer.SeriesProviderIds.TryGetValue(MetadataProvider.Tvdb.ToString(), out id)) + if (timer.SeriesProviderIds.TryGetValue(MetadataProvider.Tvdb.ToString(), out var id)) { await writer.WriteElementStringAsync(null, "id", null, id).ConfigureAwait(false); } diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs index f2020e05fe..b418162304 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs @@ -170,9 +170,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts var nameInExtInf = nameParts.Length > 1 ? nameParts[^1].AsSpan().Trim() : ReadOnlySpan<char>.Empty; string numberString = null; - string attributeValue; - if (attributes.TryGetValue("tvg-chno", out attributeValue) + if (attributes.TryGetValue("tvg-chno", out var attributeValue) && double.TryParse(attributeValue, CultureInfo.InvariantCulture, out _)) { numberString = attributeValue; diff --git a/RSSDP/HttpRequestParser.cs b/RSSDP/HttpRequestParser.cs index a3e100796d..a1b4627a93 100644 --- a/RSSDP/HttpRequestParser.cs +++ b/RSSDP/HttpRequestParser.cs @@ -64,8 +64,7 @@ namespace Rssdp.Infrastructure } message.Method = new HttpMethod(parts[0].Trim()); - Uri requestUri; - if (Uri.TryCreate(parts[1].Trim(), UriKind.RelativeOrAbsolute, out requestUri)) + if (Uri.TryCreate(parts[1].Trim(), UriKind.RelativeOrAbsolute, out var requestUri)) { message.RequestUri = requestUri; } diff --git a/RSSDP/HttpResponseParser.cs b/RSSDP/HttpResponseParser.cs index 3e361465d7..71b7a7b990 100644 --- a/RSSDP/HttpResponseParser.cs +++ b/RSSDP/HttpResponseParser.cs @@ -77,8 +77,7 @@ namespace Rssdp.Infrastructure message.Version = ParseHttpVersion(parts[0].Trim()); - int statusCode = -1; - if (!Int32.TryParse(parts[1].Trim(), out statusCode)) + if (!Int32.TryParse(parts[1].Trim(), out var statusCode)) { throw new ArgumentException("data status line is invalid. Status code is not a valid integer.", nameof(data)); } diff --git a/RSSDP/SsdpDeviceLocator.cs b/RSSDP/SsdpDeviceLocator.cs index 681ef0a5c1..de48bd8798 100644 --- a/RSSDP/SsdpDeviceLocator.cs +++ b/RSSDP/SsdpDeviceLocator.cs @@ -483,8 +483,7 @@ namespace Rssdp.Infrastructure } } - Uri retVal; - Uri.TryCreate(value, UriKind.RelativeOrAbsolute, out retVal); + Uri.TryCreate(value, UriKind.RelativeOrAbsolute, out var retVal); return retVal; } @@ -501,8 +500,7 @@ namespace Rssdp.Infrastructure } } - Uri retVal; - Uri.TryCreate(value, UriKind.RelativeOrAbsolute, out retVal); + Uri.TryCreate(value, UriKind.RelativeOrAbsolute, out var retVal); return retVal; } diff --git a/RSSDP/SsdpDevicePublisher.cs b/RSSDP/SsdpDevicePublisher.cs index e073ffa96c..0225d4f7db 100644 --- a/RSSDP/SsdpDevicePublisher.cs +++ b/RSSDP/SsdpDevicePublisher.cs @@ -244,7 +244,6 @@ namespace Rssdp.Infrastructure // Wait on random interval up to MX, as per SSDP spec. // Also, as per UPnP 1.1/SSDP spec ignore missing/bank MX header. If over 120, assume random value between 0 and 120. // Using 16 as minimum as that's often the minimum system clock frequency anyway. - int maxWaitInterval = 0; if (String.IsNullOrEmpty(mx)) { // Windows Explorer is poorly behaved and doesn't supply an MX header value. @@ -254,7 +253,7 @@ namespace Rssdp.Infrastructure // return; } - if (!Int32.TryParse(mx, out maxWaitInterval) || maxWaitInterval <= 0) + if (!Int32.TryParse(mx, out var maxWaitInterval) || maxWaitInterval <= 0) { return; } @@ -581,8 +580,7 @@ namespace Rssdp.Infrastructure private string GetFirstHeaderValue(System.Net.Http.Headers.HttpRequestHeaders httpRequestHeaders, string headerName) { string retVal = null; - IEnumerable<String> values = null; - if (httpRequestHeaders.TryGetValues(headerName, out values) && values != null) + if (httpRequestHeaders.TryGetValues(headerName, out var values) && values != null) { retVal = values.FirstOrDefault(); } diff --git a/tests/Jellyfin.Providers.Tests/Manager/MetadataServiceTests.cs b/tests/Jellyfin.Providers.Tests/Manager/MetadataServiceTests.cs index e18faa422d..ec4df9981c 100644 --- a/tests/Jellyfin.Providers.Tests/Manager/MetadataServiceTests.cs +++ b/tests/Jellyfin.Providers.Tests/Manager/MetadataServiceTests.cs @@ -238,9 +238,6 @@ namespace Jellyfin.Providers.Tests.Manager } }; - object? result; - List<PersonInfo> actual; - // overwrite provider id var overwriteNewValue = new List<PersonInfo> { @@ -249,9 +246,9 @@ namespace Jellyfin.Providers.Tests.Manager Name = "Name 2" } }; - Assert.False(TestMergeBaseItemDataPerson(GetOldValue(), overwriteNewValue, null, false, out result)); + Assert.False(TestMergeBaseItemDataPerson(GetOldValue(), overwriteNewValue, null, false, out var result)); // People not already in target are not merged into it from source - actual = (List<PersonInfo>)result!; + List<PersonInfo> actual = (List<PersonInfo>)result!; Assert.Single(actual); Assert.Equal("Name 1", actual[0].Name); -- cgit v1.2.3 From dabdc86a1bacfd55de3c38b04adbe9fe3643e27e Mon Sep 17 00:00:00 2001 From: Stepan Goremykin <goremukin@gmail.com> Date: Thu, 6 Apr 2023 19:25:25 +0200 Subject: Pass cancellation token --- .../LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs index 81eb083f6f..7bc209d6bd 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs @@ -51,7 +51,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun public async Task<bool> CheckTunerAvailability(IPAddress remoteIp, int tuner, CancellationToken cancellationToken) { using var client = new TcpClient(); - await client.ConnectAsync(remoteIp, HdHomeRunPort).ConfigureAwait(false); + await client.ConnectAsync(remoteIp, HdHomeRunPort, cancellationToken).ConfigureAwait(false); using var stream = client.GetStream(); return await CheckTunerAvailability(stream, tuner, cancellationToken).ConfigureAwait(false); -- cgit v1.2.3 From 19e65269a24714469bb6a2b3e15602b73327e5d3 Mon Sep 17 00:00:00 2001 From: Stepan Goremykin <goremukin@gmail.com> Date: Thu, 6 Apr 2023 19:27:57 +0200 Subject: Simplify linq expressions (use All) --- Emby.Server.Implementations/Library/MediaSourceManager.cs | 4 ++-- .../Localization/LocalizationManager.cs | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs index eadfa5dfe9..c9a26a30f5 100644 --- a/Emby.Server.Implementations/Library/MediaSourceManager.cs +++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs @@ -154,8 +154,8 @@ namespace Emby.Server.Implementations.Library // If file is strm or main media stream is missing, force a metadata refresh with remote probing if (allowMediaProbe && mediaSources[0].Type != MediaSourceType.Placeholder && (item.Path.EndsWith(".strm", StringComparison.OrdinalIgnoreCase) - || (item.MediaType == MediaType.Video && !mediaSources[0].MediaStreams.Any(i => i.Type == MediaStreamType.Video)) - || (item.MediaType == MediaType.Audio && !mediaSources[0].MediaStreams.Any(i => i.Type == MediaStreamType.Audio)))) + || (item.MediaType == MediaType.Video && mediaSources[0].MediaStreams.All(i => i.Type != MediaStreamType.Video)) + || (item.MediaType == MediaType.Audio && mediaSources[0].MediaStreams.All(i => i.Type != MediaStreamType.Audio)))) { await item.RefreshMetadata( new MetadataRefreshOptions(_directoryService) diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs index 166b71b4a3..96f4353998 100644 --- a/Emby.Server.Implementations/Localization/LocalizationManager.cs +++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs @@ -198,25 +198,25 @@ namespace Emby.Server.Implementations.Localization } // Minimum rating possible - if (!ratings.Any(x => x.Value == 0)) + if (ratings.All(x => x.Value != 0)) { ratings.Add(new ParentalRating("Approved", 0)); } // Matches PG (this has different age restrictions depending on country) - if (!ratings.Any(x => x.Value == 10)) + if (ratings.All(x => x.Value != 10)) { ratings.Add(new ParentalRating("10", 10)); } // Matches PG-13 - if (!ratings.Any(x => x.Value == 13)) + if (ratings.All(x => x.Value != 13)) { ratings.Add(new ParentalRating("13", 13)); } // Matches TV-14 - if (!ratings.Any(x => x.Value == 14)) + if (ratings.All(x => x.Value != 14)) { ratings.Add(new ParentalRating("14", 14)); } @@ -229,13 +229,13 @@ namespace Emby.Server.Implementations.Localization } // A lot of countries don't excplicitly have a seperate rating for adult content - if (!ratings.Any(x => x.Value == 1000)) + if (ratings.All(x => x.Value != 1000)) { ratings.Add(new ParentalRating("XXX", 1000)); } // A lot of countries don't excplicitly have a seperate rating for banned content - if (!ratings.Any(x => x.Value == 1001)) + if (ratings.All(x => x.Value != 1001)) { ratings.Add(new ParentalRating("Banned", 1001)); } -- cgit v1.2.3 From b6cfdb8b92650aa4f5c33b9523700ffeaa7694d0 Mon Sep 17 00:00:00 2001 From: Stepan Goremykin <goremukin@gmail.com> Date: Thu, 6 Apr 2023 19:30:17 +0200 Subject: Simplify conditional expression --- Emby.Server.Implementations/Dto/DtoService.cs | 4 +--- Emby.Server.Implementations/Library/LibraryManager.cs | 4 +--- Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs | 4 +--- 3 files changed, 3 insertions(+), 9 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index c725839fba..8fa2f05662 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -571,9 +571,7 @@ namespace Emby.Server.Implementations.Dto return null; } }).Where(i => i is not null) - .Where(i => user is null ? - true : - i.IsVisible(user)) + .Where(i => user is null || i.IsVisible(user)) .DistinctBy(x => x.Name, StringComparer.OrdinalIgnoreCase) .ToDictionary(i => i.Name, StringComparer.OrdinalIgnoreCase); diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 36a300a572..8883aff0b0 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -2749,9 +2749,7 @@ namespace Emby.Server.Implementations.Library } }) .Where(i => i is not null) - .Where(i => query.User is null ? - true : - i.IsVisible(query.User)) + .Where(i => query.User is null || i.IsVisible(query.User)) .ToList(); } diff --git a/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs index 7c4ffdbc00..8fe2b087d9 100644 --- a/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs +++ b/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs @@ -133,9 +133,7 @@ namespace Jellyfin.Server.Migrations.Routines SkipBackwardLength = dto.CustomPrefs.TryGetValue("skipBackLength", out length) && int.TryParse(length, out var skipBackwardLength) ? skipBackwardLength : 10000, - EnableNextVideoInfoOverlay = dto.CustomPrefs.TryGetValue("enableNextVideoInfoOverlay", out var enabled) && !string.IsNullOrEmpty(enabled) - ? bool.Parse(enabled) - : true, + EnableNextVideoInfoOverlay = !dto.CustomPrefs.TryGetValue("enableNextVideoInfoOverlay", out var enabled) || string.IsNullOrEmpty(enabled) || bool.Parse(enabled), DashboardTheme = dto.CustomPrefs.TryGetValue("dashboardtheme", out var theme) ? theme : string.Empty, TvHome = dto.CustomPrefs.TryGetValue("tvhome", out var home) ? home : string.Empty }; -- cgit v1.2.3 From 910617bbc3b960ff6a1e1c18c27a0b42a4be9ee0 Mon Sep 17 00:00:00 2001 From: Stepan Goremykin <goremukin@gmail.com> Date: Thu, 6 Apr 2023 19:38:34 +0200 Subject: Remove redundant 'else' keywords --- .../Data/SqliteItemRepository.cs | 42 ++++--- .../Library/Resolvers/BaseVideoResolver.cs | 3 +- .../LiveTv/EmbyTV/EmbyTV.cs | 6 +- .../LiveTv/Listings/SchedulesDirect.cs | 9 +- Emby.Server.Implementations/SyncPlay/Group.cs | 12 +- .../SyncPlay/SyncPlayManager.cs | 6 +- Jellyfin.Api/Controllers/SubtitleController.cs | 6 +- Jellyfin.Networking/Manager/NetworkManager.cs | 12 +- MediaBrowser.Controller/Entities/BaseItem.cs | 14 +-- MediaBrowser.Controller/LiveTv/LiveTvProgram.cs | 6 +- .../MediaEncoding/EncodingHelper.cs | 132 ++++++++++----------- .../SyncPlay/GroupStates/WaitingGroupState.cs | 8 +- .../SyncPlay/Queue/PlayQueueManager.cs | 29 ++--- .../Attachments/AttachmentExtractor.cs | 12 +- .../Encoder/EncoderValidator.cs | 6 +- .../Subtitles/SubtitleEncoder.cs | 6 +- MediaBrowser.Model/Cryptography/PasswordHash.cs | 3 +- MediaBrowser.Model/Dlna/StreamBuilder.cs | 24 ++-- MediaBrowser.Model/MediaInfo/AudioCodec.cs | 6 +- RSSDP/HttpParserBase.cs | 6 +- RSSDP/SsdpDevice.cs | 6 +- RSSDP/SsdpDeviceLocator.cs | 6 +- RSSDP/SsdpDevicePublisher.cs | 6 +- src/Jellyfin.Extensions/AlphanumericComparator.cs | 15 ++- 24 files changed, 188 insertions(+), 193 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index fcff5f98c6..580fcee86f 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -1309,7 +1309,8 @@ namespace Emby.Server.Implementations.Data { return false; } - else if (type == typeof(UserRootFolder)) + + if (type == typeof(UserRootFolder)) { return false; } @@ -1319,55 +1320,68 @@ namespace Emby.Server.Implementations.Data { return false; } - else if (type == typeof(MusicArtist)) + + if (type == typeof(MusicArtist)) { return false; } - else if (type == typeof(Person)) + + if (type == typeof(Person)) { return false; } - else if (type == typeof(MusicGenre)) + + if (type == typeof(MusicGenre)) { return false; } - else if (type == typeof(Genre)) + + if (type == typeof(Genre)) { return false; } - else if (type == typeof(Studio)) + + if (type == typeof(Studio)) { return false; } - else if (type == typeof(PlaylistsFolder)) + + if (type == typeof(PlaylistsFolder)) { return false; } - else if (type == typeof(PhotoAlbum)) + + if (type == typeof(PhotoAlbum)) { return false; } - else if (type == typeof(Year)) + + if (type == typeof(Year)) { return false; } - else if (type == typeof(Book)) + + if (type == typeof(Book)) { return false; } - else if (type == typeof(LiveTvProgram)) + + if (type == typeof(LiveTvProgram)) { return false; } - else if (type == typeof(AudioBook)) + + if (type == typeof(AudioBook)) { return false; } - else if (type == typeof(Audio)) + + if (type == typeof(Audio)) { return false; } - else if (type == typeof(MusicAlbum)) + + if (type == typeof(MusicAlbum)) { return false; } diff --git a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs index 4fac91bf1e..381796d0e3 100644 --- a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs @@ -78,7 +78,8 @@ namespace Emby.Server.Implementations.Library.Resolvers Set3DFormat(videoTmp); return videoTmp; } - else if (IsBluRayDirectory(filename)) + + if (IsBluRayDirectory(filename)) { var videoTmp = new TVideoType { diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 477fd9df1e..b9d0f170ac 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -627,10 +627,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV _timerProvider.Update(existingTimer); return Task.FromResult(existingTimer.Id); } - else - { - throw new ArgumentException("A scheduled recording already exists for this program."); - } + + throw new ArgumentException("A scheduled recording already exists for this program."); } info.Id = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture); diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs index b5e742f98f..ca3e45707b 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs @@ -415,14 +415,13 @@ namespace Emby.Server.Implementations.LiveTv.Listings { return null; } - else if (uri.IndexOf("http", StringComparison.OrdinalIgnoreCase) != -1) + + if (uri.IndexOf("http", StringComparison.OrdinalIgnoreCase) != -1) { return uri; } - else - { - return apiUrl + "/image/" + uri + "?token=" + token; - } + + return apiUrl + "/image/" + uri + "?token=" + token; } private static double GetAspectRatio(ImageDataDto i) diff --git a/Emby.Server.Implementations/SyncPlay/Group.cs b/Emby.Server.Implementations/SyncPlay/Group.cs index 7d7ea58106..da8f949326 100644 --- a/Emby.Server.Implementations/SyncPlay/Group.cs +++ b/Emby.Server.Implementations/SyncPlay/Group.cs @@ -620,10 +620,8 @@ namespace Emby.Server.Implementations.SyncPlay RestartCurrentItem(); return true; } - else - { - return false; - } + + return false; } /// <inheritdoc /> @@ -637,10 +635,8 @@ namespace Emby.Server.Implementations.SyncPlay RestartCurrentItem(); return true; } - else - { - return false; - } + + return false; } /// <inheritdoc /> diff --git a/Emby.Server.Implementations/SyncPlay/SyncPlayManager.cs b/Emby.Server.Implementations/SyncPlay/SyncPlayManager.cs index 63c4a15564..00c655634a 100644 --- a/Emby.Server.Implementations/SyncPlay/SyncPlayManager.cs +++ b/Emby.Server.Implementations/SyncPlay/SyncPlayManager.cs @@ -339,10 +339,8 @@ namespace Emby.Server.Implementations.SyncPlay { return sessionsCounter > 0; } - else - { - return false; - } + + return false; } /// <summary> diff --git a/Jellyfin.Api/Controllers/SubtitleController.cs b/Jellyfin.Api/Controllers/SubtitleController.cs index e384213380..b3e9d62972 100644 --- a/Jellyfin.Api/Controllers/SubtitleController.cs +++ b/Jellyfin.Api/Controllers/SubtitleController.cs @@ -533,10 +533,8 @@ public class SubtitleController : BaseJellyfinApiController _logger.LogDebug("Fallback font size is {FileSize} Bytes", fileSize); return PhysicalFile(fontFile.FullName, MimeTypes.GetMimeType(fontFile.FullName)); } - else - { - _logger.LogWarning("The selected font is null or empty"); - } + + _logger.LogWarning("The selected font is null or empty"); } else { diff --git a/Jellyfin.Networking/Manager/NetworkManager.cs b/Jellyfin.Networking/Manager/NetworkManager.cs index ac9258ad13..a6d5252ffc 100644 --- a/Jellyfin.Networking/Manager/NetworkManager.cs +++ b/Jellyfin.Networking/Manager/NetworkManager.cs @@ -500,10 +500,8 @@ namespace Jellyfin.Networking.Manager { return true; } - else - { - return address.IsPrivateAddressRange(); - } + + return address.IsPrivateAddressRange(); } /// <inheritdoc/> @@ -1171,13 +1169,15 @@ namespace Jellyfin.Networking.Manager bindPreference = addr.Value; break; } - else if ((addr.Key.Address.Equals(IPAddress.Any) || addr.Key.Address.Equals(IPAddress.IPv6Any)) && isInExternalSubnet) + + if ((addr.Key.Address.Equals(IPAddress.Any) || addr.Key.Address.Equals(IPAddress.IPv6Any)) && isInExternalSubnet) { // External. bindPreference = addr.Value; break; } - else if (addr.Key.Contains(source)) + + if (addr.Key.Contains(source)) { // Match ip address. bindPreference = addr.Value; diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index a04f02bf91..adc7b2f953 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -801,16 +801,14 @@ namespace MediaBrowser.Controller.Entities { return allowed.Contains(ChannelId); } - else - { - var collectionFolders = LibraryManager.GetCollectionFolders(this, allCollectionFolders); - foreach (var folder in collectionFolders) + var collectionFolders = LibraryManager.GetCollectionFolders(this, allCollectionFolders); + + foreach (var folder in collectionFolders) + { + if (allowed.Contains(folder.Id)) { - if (allowed.Contains(folder.Id)) - { - return true; - } + return true; } } diff --git a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs index 514323238e..c721fb7785 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs @@ -197,10 +197,8 @@ namespace MediaBrowser.Controller.LiveTv { return 2.0 / 3; } - else - { - return 16.0 / 9; - } + + return 16.0 / 9; } public override string GetClientTypeName() diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 5430ea2043..53f48d9598 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -557,7 +557,8 @@ namespace MediaBrowser.Controller.MediaEncoding { return Array.FindIndex(_videoProfilesH264, x => string.Equals(x, profile, StringComparison.OrdinalIgnoreCase)); } - else if (string.Equals("hevc", videoCodec, StringComparison.OrdinalIgnoreCase)) + + if (string.Equals("hevc", videoCodec, StringComparison.OrdinalIgnoreCase)) { return Array.FindIndex(_videoProfilesH265, x => string.Equals(x, profile, StringComparison.OrdinalIgnoreCase)); } @@ -1109,19 +1110,19 @@ namespace MediaBrowser.Controller.MediaEncoding { return "-bsf:v h264_mp4toannexb"; } - else if (IsH265(stream)) + + if (IsH265(stream)) { return "-bsf:v hevc_mp4toannexb"; } - else if (IsAAC(stream)) + + if (IsAAC(stream)) { // Convert adts header(mpegts) to asc header(mp4). return "-bsf:a aac_adtstoasc"; } - else - { - return null; - } + + return null; } public static string GetAudioBitStreamArguments(EncodingJobInfo state, string segmentContainer, string mediaSourceContainer) @@ -1199,10 +1200,8 @@ namespace MediaBrowser.Controller.MediaEncoding { return FormattableString.Invariant($" -rc_mode CBR -b:v {bitrate} -maxrate {bitrate} -bufsize {bufsize}"); } - else - { - return FormattableString.Invariant($" -rc_mode VBR -b:v {bitrate} -maxrate {bitrate} -bufsize {bufsize}"); - } + + return FormattableString.Invariant($" -rc_mode VBR -b:v {bitrate} -maxrate {bitrate} -bufsize {bufsize}"); } return FormattableString.Invariant($" -b:v {bitrate} -maxrate {bitrate} -bufsize {bufsize}"); @@ -2762,79 +2761,76 @@ namespace MediaBrowser.Controller.MediaEncoding widthParam, heightParam); } - else - { - return GetFixedSwScaleFilter(threedFormat, requestedWidth.Value, requestedHeight.Value); - } + + return GetFixedSwScaleFilter(threedFormat, requestedWidth.Value, requestedHeight.Value); } // If Max dimensions were supplied, for width selects lowest even number between input width and width req size and selects lowest even number from in width*display aspect and requested size - else if (requestedMaxWidth.HasValue && requestedMaxHeight.HasValue) + + if (requestedMaxWidth.HasValue && requestedMaxHeight.HasValue) { var maxWidthParam = requestedMaxWidth.Value.ToString(CultureInfo.InvariantCulture); var maxHeightParam = requestedMaxHeight.Value.ToString(CultureInfo.InvariantCulture); return string.Format( - CultureInfo.InvariantCulture, - "scale=trunc(min(max(iw\\,ih*a)\\,min({0}\\,{1}*a))/{2})*{2}:trunc(min(max(iw/a\\,ih)\\,min({0}/a\\,{1}))/2)*2", - maxWidthParam, - maxHeightParam, - scaleVal); + CultureInfo.InvariantCulture, + "scale=trunc(min(max(iw\\,ih*a)\\,min({0}\\,{1}*a))/{2})*{2}:trunc(min(max(iw/a\\,ih)\\,min({0}/a\\,{1}))/2)*2", + maxWidthParam, + maxHeightParam, + scaleVal); } // If a fixed width was requested - else if (requestedWidth.HasValue) + if (requestedWidth.HasValue) { if (threedFormat.HasValue) { // This method can handle 0 being passed in for the requested height return GetFixedSwScaleFilter(threedFormat, requestedWidth.Value, 0); } - else - { - var widthParam = requestedWidth.Value.ToString(CultureInfo.InvariantCulture); - return string.Format( - CultureInfo.InvariantCulture, - "scale={0}:trunc(ow/a/2)*2", - widthParam); - } + var widthParam = requestedWidth.Value.ToString(CultureInfo.InvariantCulture); + + return string.Format( + CultureInfo.InvariantCulture, + "scale={0}:trunc(ow/a/2)*2", + widthParam); } // If a fixed height was requested - else if (requestedHeight.HasValue) + if (requestedHeight.HasValue) { var heightParam = requestedHeight.Value.ToString(CultureInfo.InvariantCulture); return string.Format( - CultureInfo.InvariantCulture, - "scale=trunc(oh*a/{1})*{1}:{0}", - heightParam, - scaleVal); + CultureInfo.InvariantCulture, + "scale=trunc(oh*a/{1})*{1}:{0}", + heightParam, + scaleVal); } // If a max width was requested - else if (requestedMaxWidth.HasValue) + if (requestedMaxWidth.HasValue) { var maxWidthParam = requestedMaxWidth.Value.ToString(CultureInfo.InvariantCulture); return string.Format( - CultureInfo.InvariantCulture, - "scale=trunc(min(max(iw\\,ih*a)\\,{0})/{1})*{1}:trunc(ow/a/2)*2", - maxWidthParam, - scaleVal); + CultureInfo.InvariantCulture, + "scale=trunc(min(max(iw\\,ih*a)\\,{0})/{1})*{1}:trunc(ow/a/2)*2", + maxWidthParam, + scaleVal); } // If a max height was requested - else if (requestedMaxHeight.HasValue) + if (requestedMaxHeight.HasValue) { var maxHeightParam = requestedMaxHeight.Value.ToString(CultureInfo.InvariantCulture); return string.Format( - CultureInfo.InvariantCulture, - "scale=trunc(oh*a/{1})*{1}:min(max(iw/a\\,ih)\\,{0})", - maxHeightParam, - scaleVal); + CultureInfo.InvariantCulture, + "scale=trunc(oh*a/{1})*{1}:min(max(iw/a\\,ih)\\,{0})", + maxHeightParam, + scaleVal); } return string.Empty; @@ -2908,18 +2904,21 @@ namespace MediaBrowser.Controller.MediaEncoding "yadif_cuda={0}:-1:0", doubleRateDeint ? "1" : "0"); } - else if (hwDeintSuffix.Contains("vaapi", StringComparison.OrdinalIgnoreCase)) + + if (hwDeintSuffix.Contains("vaapi", StringComparison.OrdinalIgnoreCase)) { return string.Format( CultureInfo.InvariantCulture, "deinterlace_vaapi=rate={0}", doubleRateDeint ? "field" : "frame"); } - else if (hwDeintSuffix.Contains("qsv", StringComparison.OrdinalIgnoreCase)) + + if (hwDeintSuffix.Contains("qsv", StringComparison.OrdinalIgnoreCase)) { return "deinterlace_qsv=mode=2"; } - else if (hwDeintSuffix.Contains("videotoolbox", StringComparison.OrdinalIgnoreCase)) + + if (hwDeintSuffix.Contains("videotoolbox", StringComparison.OrdinalIgnoreCase)) { return string.Format( CultureInfo.InvariantCulture, @@ -2950,7 +2949,8 @@ namespace MediaBrowser.Controller.MediaEncoding options.VppTonemappingBrightness, options.VppTonemappingContrast); } - else if (string.Equals(hwTonemapSuffix, "vulkan", StringComparison.OrdinalIgnoreCase)) + + if (string.Equals(hwTonemapSuffix, "vulkan", StringComparison.OrdinalIgnoreCase)) { args = "libplacebo=format={1}:tonemapping={2}:color_primaries=bt709:color_trc=bt709:colorspace=bt709:peak_detect=0:upscaler=none:downscaler=none"; @@ -4826,26 +4826,27 @@ namespace MediaBrowser.Controller.MediaEncoding { return videoStream.BitDepth.Value; } - else if (string.Equals(videoStream.PixelFormat, "yuv420p", StringComparison.OrdinalIgnoreCase) - || string.Equals(videoStream.PixelFormat, "yuvj420p", StringComparison.OrdinalIgnoreCase) - || string.Equals(videoStream.PixelFormat, "yuv444p", StringComparison.OrdinalIgnoreCase)) + + if (string.Equals(videoStream.PixelFormat, "yuv420p", StringComparison.OrdinalIgnoreCase) + || string.Equals(videoStream.PixelFormat, "yuvj420p", StringComparison.OrdinalIgnoreCase) + || string.Equals(videoStream.PixelFormat, "yuv444p", StringComparison.OrdinalIgnoreCase)) { return 8; } - else if (string.Equals(videoStream.PixelFormat, "yuv420p10le", StringComparison.OrdinalIgnoreCase) - || string.Equals(videoStream.PixelFormat, "yuv444p10le", StringComparison.OrdinalIgnoreCase)) + + if (string.Equals(videoStream.PixelFormat, "yuv420p10le", StringComparison.OrdinalIgnoreCase) + || string.Equals(videoStream.PixelFormat, "yuv444p10le", StringComparison.OrdinalIgnoreCase)) { return 10; } - else if (string.Equals(videoStream.PixelFormat, "yuv420p12le", StringComparison.OrdinalIgnoreCase) - || string.Equals(videoStream.PixelFormat, "yuv444p12le", StringComparison.OrdinalIgnoreCase)) + + if (string.Equals(videoStream.PixelFormat, "yuv420p12le", StringComparison.OrdinalIgnoreCase) + || string.Equals(videoStream.PixelFormat, "yuv444p12le", StringComparison.OrdinalIgnoreCase)) { return 12; } - else - { - return 8; - } + + return 8; } return 0; @@ -5077,11 +5078,9 @@ namespace MediaBrowser.Controller.MediaEncoding return " -hwaccel cuda" + (outputHwSurface ? " -hwaccel_output_format cuda" : string.Empty) + (nvdecNoInternalCopy ? " -hwaccel_flags +unsafe_output" : string.Empty) + " -threads 1" + (isAv1 ? " -c:v av1" : string.Empty); } - else - { - // cuvid decoder doesn't have threading issue. - return " -hwaccel cuda" + (outputHwSurface ? " -hwaccel_output_format cuda" : string.Empty); - } + + // cuvid decoder doesn't have threading issue. + return " -hwaccel cuda" + (outputHwSurface ? " -hwaccel_output_format cuda" : string.Empty); } } @@ -5439,7 +5438,8 @@ namespace MediaBrowser.Controller.MediaEncoding // Automatically set thread count return mustSetThreadCount ? Math.Max(Environment.ProcessorCount - 1, 1) : 0; } - else if (threads >= Environment.ProcessorCount) + + if (threads >= Environment.ProcessorCount) { return Environment.ProcessorCount; } diff --git a/MediaBrowser.Controller/SyncPlay/GroupStates/WaitingGroupState.cs b/MediaBrowser.Controller/SyncPlay/GroupStates/WaitingGroupState.cs index 2164945560..dcc06db1ed 100644 --- a/MediaBrowser.Controller/SyncPlay/GroupStates/WaitingGroupState.cs +++ b/MediaBrowser.Controller/SyncPlay/GroupStates/WaitingGroupState.cs @@ -533,11 +533,9 @@ namespace MediaBrowser.Controller.SyncPlay.GroupStates _logger.LogWarning("Session {SessionId} is seeking to wrong position, correcting.", session.Id); return; } - else - { - // Session is ready. - context.SetBuffering(session, false); - } + + // Session is ready. + context.SetBuffering(session, false); if (!context.IsBuffering()) { diff --git a/MediaBrowser.Controller/SyncPlay/Queue/PlayQueueManager.cs b/MediaBrowser.Controller/SyncPlay/Queue/PlayQueueManager.cs index ddbfeb8de8..bdebbbfd49 100644 --- a/MediaBrowser.Controller/SyncPlay/Queue/PlayQueueManager.cs +++ b/MediaBrowser.Controller/SyncPlay/Queue/PlayQueueManager.cs @@ -313,17 +313,13 @@ namespace MediaBrowser.Controller.SyncPlay.Queue return true; } - else - { - // Restoring playing item. - SetPlayingItemByPlaylistId(playingItem.PlaylistItemId); - return false; - } - } - else - { + + // Restoring playing item. + SetPlayingItemByPlaylistId(playingItem.PlaylistItemId); return false; } + + return false; } /// <summary> @@ -528,10 +524,8 @@ namespace MediaBrowser.Controller.SyncPlay.Queue { return _shuffledPlaylist; } - else - { - return _sortedPlaylist; - } + + return _sortedPlaylist; } /// <summary> @@ -544,14 +538,13 @@ namespace MediaBrowser.Controller.SyncPlay.Queue { return null; } - else if (ShuffleMode.Equals(GroupShuffleMode.Shuffle)) + + if (ShuffleMode.Equals(GroupShuffleMode.Shuffle)) { return _shuffledPlaylist[PlayingItemIndex]; } - else - { - return _sortedPlaylist[PlayingItemIndex]; - } + + return _sortedPlaylist[PlayingItemIndex]; } } } diff --git a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs index ec65dd80f4..989e386a51 100644 --- a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs +++ b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs @@ -231,10 +231,8 @@ namespace MediaBrowser.MediaEncoding.Attachments throw new InvalidOperationException( string.Format(CultureInfo.InvariantCulture, "ffmpeg attachment extraction failed for {0} to {1}", inputPath, outputPath)); } - else - { - _logger.LogInformation("ffmpeg attachment extraction completed for {InputPath} to {OutputPath}", inputPath, outputPath); - } + + _logger.LogInformation("ffmpeg attachment extraction completed for {InputPath} to {OutputPath}", inputPath, outputPath); } private async Task<Stream> GetAttachmentStream( @@ -376,10 +374,8 @@ namespace MediaBrowser.MediaEncoding.Attachments throw new InvalidOperationException( string.Format(CultureInfo.InvariantCulture, "ffmpeg attachment extraction failed for {0} to {1}", inputPath, outputPath)); } - else - { - _logger.LogInformation("ffmpeg attachment extraction completed for {InputPath} to {OutputPath}", inputPath, outputPath); - } + + _logger.LogInformation("ffmpeg attachment extraction completed for {InputPath} to {OutputPath}", inputPath, outputPath); } private string GetAttachmentCachePath(string mediaPath, MediaSourceInfo mediaSource, int attachmentStreamIndex) diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index 53b8a6e678..d3843796f7 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -217,12 +217,14 @@ namespace MediaBrowser.MediaEncoding.Encoder return false; } - else if (version < MinVersion) // Version is below what we recommend + + if (version < MinVersion) // Version is below what we recommend { _logger.LogWarning("FFmpeg validation: The minimum recommended version is {MinVersion}", MinVersion); return false; } - else if (MaxVersion is not null && version > MaxVersion) // Version is above what we recommend + + if (MaxVersion is not null && version > MaxVersion) // Version is above what we recommend { _logger.LogWarning("FFmpeg validation: The maximum recommended version is {MaxVersion}", MaxVersion); return false; diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs index 3b85988715..794906c3b4 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs @@ -624,10 +624,8 @@ namespace MediaBrowser.MediaEncoding.Subtitles throw new FfmpegException( string.Format(CultureInfo.InvariantCulture, "ffmpeg subtitle extraction failed for {0} to {1}", inputPath, outputPath)); } - else - { - _logger.LogInformation("ffmpeg subtitle extraction completed for {InputPath} to {OutputPath}", inputPath, outputPath); - } + + _logger.LogInformation("ffmpeg subtitle extraction completed for {InputPath} to {OutputPath}", inputPath, outputPath); if (string.Equals(outputCodec, "ass", StringComparison.OrdinalIgnoreCase)) { diff --git a/MediaBrowser.Model/Cryptography/PasswordHash.cs b/MediaBrowser.Model/Cryptography/PasswordHash.cs index 80a30684ab..ccb361c132 100644 --- a/MediaBrowser.Model/Cryptography/PasswordHash.cs +++ b/MediaBrowser.Model/Cryptography/PasswordHash.cs @@ -80,7 +80,8 @@ namespace MediaBrowser.Model.Cryptography { throw new FormatException("Hash string must contain a valid id"); } - else if (nextSegment == -1) + + if (nextSegment == -1) { return new PasswordHash(hashString.ToString(), Array.Empty<byte>()); } diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index db892a22c6..df185e40c5 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -1075,31 +1075,38 @@ namespace MediaBrowser.Model.Dlna { return 128000; } - else if (totalBitrate <= 2000000) + + if (totalBitrate <= 2000000) { return 384000; } - else if (totalBitrate <= 3000000) + + if (totalBitrate <= 3000000) { return 448000; } - else if (totalBitrate <= 4000000) + + if (totalBitrate <= 4000000) { return 640000; } - else if (totalBitrate <= 5000000) + + if (totalBitrate <= 5000000) { return 768000; } - else if (totalBitrate <= 10000000) + + if (totalBitrate <= 10000000) { return 1536000; } - else if (totalBitrate <= 15000000) + + if (totalBitrate <= 15000000) { return 2304000; } - else if (totalBitrate <= 20000000) + + if (totalBitrate <= 20000000) { return 3584000; } @@ -1443,7 +1450,8 @@ namespace MediaBrowser.Model.Dlna { return false; } - else if (ContainerProfile.ContainsContainer(normalizedContainers, "mkv") + + if (ContainerProfile.ContainsContainer(normalizedContainers, "mkv") || ContainerProfile.ContainsContainer(normalizedContainers, "matroska")) { return true; diff --git a/MediaBrowser.Model/MediaInfo/AudioCodec.cs b/MediaBrowser.Model/MediaInfo/AudioCodec.cs index 7b83b1b9df..4c22af4498 100644 --- a/MediaBrowser.Model/MediaInfo/AudioCodec.cs +++ b/MediaBrowser.Model/MediaInfo/AudioCodec.cs @@ -17,11 +17,13 @@ namespace MediaBrowser.Model.MediaInfo { return "Dolby Digital"; } - else if (string.Equals(codec, "eac3", StringComparison.OrdinalIgnoreCase)) + + if (string.Equals(codec, "eac3", StringComparison.OrdinalIgnoreCase)) { return "Dolby Digital+"; } - else if (string.Equals(codec, "dca", StringComparison.OrdinalIgnoreCase)) + + if (string.Equals(codec, "dca", StringComparison.OrdinalIgnoreCase)) { return "DTS"; } diff --git a/RSSDP/HttpParserBase.cs b/RSSDP/HttpParserBase.cs index 6b6c13d996..1949a9df33 100644 --- a/RSSDP/HttpParserBase.cs +++ b/RSSDP/HttpParserBase.cs @@ -221,10 +221,8 @@ namespace Rssdp.Infrastructure { return trimmedSegment.Substring(1, trimmedSegment.Length - 2); } - else - { - return trimmedSegment; - } + + return trimmedSegment; } } } diff --git a/RSSDP/SsdpDevice.cs b/RSSDP/SsdpDevice.cs index c826830f1d..3e4261b6a9 100644 --- a/RSSDP/SsdpDevice.cs +++ b/RSSDP/SsdpDevice.cs @@ -171,10 +171,8 @@ namespace Rssdp { return "uuid:" + this.Uuid; } - else - { - return _Udn; - } + + return _Udn; } set diff --git a/RSSDP/SsdpDeviceLocator.cs b/RSSDP/SsdpDeviceLocator.cs index de48bd8798..7afd325819 100644 --- a/RSSDP/SsdpDeviceLocator.cs +++ b/RSSDP/SsdpDeviceLocator.cs @@ -585,10 +585,8 @@ namespace Rssdp.Infrastructure { return OneSecond; } - else - { - return searchWaitTime.Subtract(OneSecond); - } + + return searchWaitTime.Subtract(OneSecond); } private DiscoveredSsdpDevice FindExistingDeviceNotification(IEnumerable<DiscoveredSsdpDevice> devices, string notificationType, string usn) diff --git a/RSSDP/SsdpDevicePublisher.cs b/RSSDP/SsdpDevicePublisher.cs index 0225d4f7db..be66f5947d 100644 --- a/RSSDP/SsdpDevicePublisher.cs +++ b/RSSDP/SsdpDevicePublisher.cs @@ -571,10 +571,8 @@ namespace Rssdp.Infrastructure { return nonzeroCacheLifetimesQuery.Min(); } - else - { - return TimeSpan.Zero; - } + + return TimeSpan.Zero; } private string GetFirstHeaderValue(System.Net.Http.Headers.HttpRequestHeaders httpRequestHeaders, string headerName) diff --git a/src/Jellyfin.Extensions/AlphanumericComparator.cs b/src/Jellyfin.Extensions/AlphanumericComparator.cs index 6e451d40e9..299e2f94ae 100644 --- a/src/Jellyfin.Extensions/AlphanumericComparator.cs +++ b/src/Jellyfin.Extensions/AlphanumericComparator.cs @@ -20,11 +20,13 @@ namespace Jellyfin.Extensions { return 0; } - else if (s1 is null) + + if (s1 is null) { return -1; } - else if (s2 is null) + + if (s2 is null) { return 1; } @@ -37,11 +39,13 @@ namespace Jellyfin.Extensions { return 0; } - else if (len1 == 0) + + if (len1 == 0) { return -1; } - else if (len2 == 0) + + if (len2 == 0) { return 1; } @@ -82,7 +86,8 @@ namespace Jellyfin.Extensions { return -1; } - else if (span1Len > span2Len) + + if (span1Len > span2Len) { return 1; } -- cgit v1.2.3 From d8c5d5522150fbbc0368626b5b52697d40a232b2 Mon Sep 17 00:00:00 2001 From: argalion79 <lotms@mail.ru> Date: Thu, 6 Apr 2023 17:41:39 +0000 Subject: Translated using Weblate (Russian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ru/ --- Emby.Server.Implementations/Localization/Core/ru.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ru.json b/Emby.Server.Implementations/Localization/Core/ru.json index 839bbcb6d3..421513341a 100644 --- a/Emby.Server.Implementations/Localization/Core/ru.json +++ b/Emby.Server.Implementations/Localization/Core/ru.json @@ -42,7 +42,7 @@ "MusicVideos": "Муз. видео", "NameInstallFailed": "Установка {0} неудачна", "NameSeasonNumber": "Сезон {0}", - "NameSeasonUnknown": "Сезон неопознан", + "NameSeasonUnknown": "Сезон не опознан", "NewVersionIsAvailable": "Новая версия Jellyfin Server доступна для загрузки.", "NotificationOptionApplicationUpdateAvailable": "Имеется обновление приложения", "NotificationOptionApplicationUpdateInstalled": "Обновление приложения установлено", @@ -96,7 +96,7 @@ "TaskRefreshChannels": "Обновление каналов", "TaskCleanTranscode": "Очистка каталога перекодировки", "TaskUpdatePlugins": "Обновление плагинов", - "TaskRefreshPeople": "Подновление людей", + "TaskRefreshPeople": "Обновление информации о персонах", "TaskCleanLogs": "Очистка каталога журналов", "TaskRefreshLibrary": "Сканирование медиатеки", "TaskRefreshChapterImages": "Извлечение изображений сцен", -- cgit v1.2.3 From 9d37c0feec9187145af8759e83175da0467fa96c Mon Sep 17 00:00:00 2001 From: Brad Beattie <bradbeattie@gmail.com> Date: Fri, 7 Apr 2023 11:55:02 -0700 Subject: Augment similarity with person matches The code comment says "genres, tags, studios, _person_, year?", but does no matching on common people between films. This PR augments similarity score treating people similar to tags. --- Emby.Server.Implementations/Data/SqliteItemRepository.cs | 1 + 1 file changed, 1 insertion(+) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index fcff5f98c6..f3fb0e285b 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -2392,6 +2392,7 @@ namespace Emby.Server.Implementations.Data // genres, tags, studios, person, year? builder.Append("+ (Select count(1) * 10 from ItemValues where ItemId=Guid and CleanValue in (select CleanValue from ItemValues where ItemId=@SimilarItemId))"); + builder.Append("+ (Select count(1) * 10 from People where ItemId=Guid and Name in (select Name from People where ItemId=@SimilarItemId))"); if (item is MusicArtist) { -- cgit v1.2.3 From 7dd4201971f1bb19ea380f2ca83aed11206cfe97 Mon Sep 17 00:00:00 2001 From: AmbulantRex <21176662+AmbulantRex@users.noreply.github.com> Date: Sun, 9 Apr 2023 10:53:09 -0600 Subject: Reconcile pre-packaged meta.json against manifest on install --- .../Plugins/PluginManager.cs | 74 ++++++++- .../Updates/InstallationManager.cs | 7 +- MediaBrowser.Common/Plugins/IPluginManager.cs | 2 +- MediaBrowser.Model/Updates/VersionInfo.cs | 6 - src/Jellyfin.Extensions/TypeExtensions.cs | 45 +++++ .../TypeExtensionsTests.cs | 68 ++++++++ .../Plugins/PluginManagerTests.cs | 181 +++++++++++++++++---- .../Test Data/Updates/manifest-stable.json | 6 +- .../Updates/InstallationManagerTests.cs | 14 -- 9 files changed, 341 insertions(+), 62 deletions(-) create mode 100644 src/Jellyfin.Extensions/TypeExtensions.cs create mode 100644 tests/Jellyfin.Extensions.Tests/TypeExtensionsTests.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Plugins/PluginManager.cs b/Emby.Server.Implementations/Plugins/PluginManager.cs index 0a7c144ed7..c6a7f45464 100644 --- a/Emby.Server.Implementations/Plugins/PluginManager.cs +++ b/Emby.Server.Implementations/Plugins/PluginManager.cs @@ -32,6 +32,8 @@ namespace Emby.Server.Implementations.Plugins /// </summary> public class PluginManager : IPluginManager { + private const string MetafileName = "meta.json"; + private readonly string _pluginsPath; private readonly Version _appVersion; private readonly List<AssemblyLoadContext> _assemblyLoadContexts; @@ -374,7 +376,7 @@ namespace Emby.Server.Implementations.Plugins try { var data = JsonSerializer.Serialize(manifest, _jsonOptions); - File.WriteAllText(Path.Combine(path, "meta.json"), data); + File.WriteAllText(Path.Combine(path, MetafileName), data); return true; } catch (ArgumentException e) @@ -385,7 +387,7 @@ namespace Emby.Server.Implementations.Plugins } /// <inheritdoc/> - public async Task<bool> GenerateManifest(PackageInfo packageInfo, Version version, string path, PluginStatus status) + public async Task<bool> PopulateManifest(PackageInfo packageInfo, Version version, string path, PluginStatus status) { var versionInfo = packageInfo.Versions.First(v => v.Version == version.ToString()); var imagePath = string.Empty; @@ -427,13 +429,75 @@ namespace Emby.Server.Implementations.Plugins Version = versionInfo.Version, Status = status == PluginStatus.Disabled ? PluginStatus.Disabled : PluginStatus.Active, // Keep disabled state. AutoUpdate = true, - ImagePath = imagePath, - Assemblies = versionInfo.Assemblies + ImagePath = imagePath }; + var metafile = Path.Combine(Path.Combine(path, MetafileName)); + if (File.Exists(metafile)) + { + var data = File.ReadAllBytes(metafile); + var localManifest = JsonSerializer.Deserialize<PluginManifest>(data, _jsonOptions) ?? new PluginManifest(); + + // Plugin installation is the typical cause for populating a manifest. Activate. + localManifest.Status = status == PluginStatus.Disabled ? PluginStatus.Disabled : PluginStatus.Active; + + if (!Equals(localManifest.Id, manifest.Id)) + { + _logger.LogError("The manifest ID {LocalUUID} did not match the package info ID {PackageUUID}.", localManifest.Id, manifest.Id); + localManifest.Status = PluginStatus.Malfunctioned; + } + + if (localManifest.Version != manifest.Version) + { + _logger.LogWarning("The version of the local manifest was {LocalVersion}, but {PackageVersion} was expected. The value will be replaced.", localManifest.Version, manifest.Version); + + // Correct the local version. + localManifest.Version = manifest.Version; + } + + // Reconcile missing data against repository manifest. + ReconcileManifest(localManifest, manifest); + + manifest = localManifest; + } + else + { + _logger.LogInformation("No local manifest exists for plugin {Plugin}. Populating from repository manifest.", manifest.Name); + } + return SaveManifest(manifest, path); } + /// <summary> + /// Resolve the target plugin manifest against the source. Values are mapped onto the + /// target only if they are default values or empty strings. ID and status fields are ignored. + /// </summary> + /// <param name="baseManifest">The base <see cref="PluginManifest"/> to be reconciled.</param> + /// <param name="projector">The <see cref="PluginManifest"/> to reconcile against.</param> + private void ReconcileManifest(PluginManifest baseManifest, PluginManifest projector) + { + var ignoredFields = new string[] + { + nameof(baseManifest.Id), + nameof(baseManifest.Status) + }; + + foreach (var property in baseManifest.GetType().GetProperties()) + { + var localValue = property.GetValue(baseManifest); + + if (property.PropertyType == typeof(bool) || ignoredFields.Any(s => Equals(s, property.Name))) + { + continue; + } + + if (property.PropertyType.IsNullOrDefault(localValue) || (property.PropertyType == typeof(string) && (string)localValue! == string.Empty)) + { + property.SetValue(baseManifest, property.GetValue(projector)); + } + } + } + /// <summary> /// Changes a plugin's load status. /// </summary> @@ -598,7 +662,7 @@ namespace Emby.Server.Implementations.Plugins { Version? version; PluginManifest? manifest = null; - var metafile = Path.Combine(dir, "meta.json"); + var metafile = Path.Combine(dir, MetafileName); if (File.Exists(metafile)) { // Only path where this stays null is when File.ReadAllBytes throws an IOException diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs index 5e897833e0..6c198b6f99 100644 --- a/Emby.Server.Implementations/Updates/InstallationManager.cs +++ b/Emby.Server.Implementations/Updates/InstallationManager.cs @@ -183,7 +183,7 @@ namespace Emby.Server.Implementations.Updates var plugin = _pluginManager.GetPlugin(package.Id, version.VersionNumber); if (plugin is not null) { - await _pluginManager.GenerateManifest(package, version.VersionNumber, plugin.Path, plugin.Manifest.Status).ConfigureAwait(false); + await _pluginManager.PopulateManifest(package, version.VersionNumber, plugin.Path, plugin.Manifest.Status).ConfigureAwait(false); } // Remove versions with a target ABI greater then the current application version. @@ -555,7 +555,10 @@ namespace Emby.Server.Implementations.Updates stream.Position = 0; using var reader = new ZipArchive(stream); reader.ExtractToDirectory(targetDir, true); - await _pluginManager.GenerateManifest(package.PackageInfo, package.Version, targetDir, status).ConfigureAwait(false); + + // Ensure we create one or populate existing ones with missing data. + await _pluginManager.PopulateManifest(package.PackageInfo, package.Version, targetDir, status); + _pluginManager.ImportPluginFrom(targetDir); } diff --git a/MediaBrowser.Common/Plugins/IPluginManager.cs b/MediaBrowser.Common/Plugins/IPluginManager.cs index fa92d383a2..1d73de3c95 100644 --- a/MediaBrowser.Common/Plugins/IPluginManager.cs +++ b/MediaBrowser.Common/Plugins/IPluginManager.cs @@ -57,7 +57,7 @@ namespace MediaBrowser.Common.Plugins /// <param name="path">The path where to save the manifest.</param> /// <param name="status">Initial status of the plugin.</param> /// <returns>True if successful.</returns> - Task<bool> GenerateManifest(PackageInfo packageInfo, Version version, string path, PluginStatus status); + Task<bool> PopulateManifest(PackageInfo packageInfo, Version version, string path, PluginStatus status); /// <summary> /// Imports plugin details from a folder. diff --git a/MediaBrowser.Model/Updates/VersionInfo.cs b/MediaBrowser.Model/Updates/VersionInfo.cs index 8f76806450..53e1d29b01 100644 --- a/MediaBrowser.Model/Updates/VersionInfo.cs +++ b/MediaBrowser.Model/Updates/VersionInfo.cs @@ -75,11 +75,5 @@ namespace MediaBrowser.Model.Updates /// </summary> [JsonPropertyName("repositoryUrl")] public string RepositoryUrl { get; set; } = string.Empty; - - /// <summary> - /// Gets or sets the assemblies whitelist for this version. - /// </summary> - [JsonPropertyName("assemblies")] - public IReadOnlyList<string> Assemblies { get; set; } = Array.Empty<string>(); } } diff --git a/src/Jellyfin.Extensions/TypeExtensions.cs b/src/Jellyfin.Extensions/TypeExtensions.cs new file mode 100644 index 0000000000..5b1111d594 --- /dev/null +++ b/src/Jellyfin.Extensions/TypeExtensions.cs @@ -0,0 +1,45 @@ +using System; +using System.Globalization; + +namespace Jellyfin.Extensions; + +/// <summary> +/// Provides extensions methods for <see cref="Type" />. +/// </summary> +public static class TypeExtensions +{ + /// <summary> + /// Checks if the supplied value is the default or null value for that type. + /// </summary> + /// <typeparam name="T">The type of the value to compare.</typeparam> + /// <param name="type">The type.</param> + /// <param name="value">The value to check.</param> + /// <returns><see langword="true"/> if the value is the default for the type. Otherwise, <see langword="false"/>.</returns> + public static bool IsNullOrDefault<T>(this Type type, T value) + { + if (value is null) + { + return true; + } + + object? tmp = value; + object? defaultValue = type.IsValueType ? Activator.CreateInstance(type) : null; + if (type.IsAssignableTo(typeof(IConvertible))) + { + tmp = Convert.ChangeType(value, type, CultureInfo.InvariantCulture); + } + + return Equals(tmp, defaultValue); + } + + /// <summary> + /// Checks if the object is currently a default or null value. Boxed types will be unboxed prior to comparison. + /// </summary> + /// <param name="obj">The object to check.</param> + /// <returns><see langword="true"/> if the value is the default for the type. Otherwise, <see langword="false"/>.</returns> + public static bool IsNullOrDefault(this object? obj) + { + // Unbox the type and check. + return obj?.GetType().IsNullOrDefault(obj) ?? true; + } +} diff --git a/tests/Jellyfin.Extensions.Tests/TypeExtensionsTests.cs b/tests/Jellyfin.Extensions.Tests/TypeExtensionsTests.cs new file mode 100644 index 0000000000..747913fa1a --- /dev/null +++ b/tests/Jellyfin.Extensions.Tests/TypeExtensionsTests.cs @@ -0,0 +1,68 @@ +using System; +using Xunit; + +namespace Jellyfin.Extensions.Tests +{ + public class TypeExtensionsTests + { + [Theory] + [InlineData(typeof(byte), byte.MaxValue, false)] + [InlineData(typeof(short), short.MinValue, false)] + [InlineData(typeof(ushort), ushort.MaxValue, false)] + [InlineData(typeof(int), int.MinValue, false)] + [InlineData(typeof(uint), uint.MaxValue, false)] + [InlineData(typeof(long), long.MinValue, false)] + [InlineData(typeof(ulong), ulong.MaxValue, false)] + [InlineData(typeof(decimal), -1.0, false)] + [InlineData(typeof(bool), true, false)] + [InlineData(typeof(char), 'a', false)] + [InlineData(typeof(string), "", false)] + [InlineData(typeof(object), 1, false)] + [InlineData(typeof(byte), 0, true)] + [InlineData(typeof(short), 0, true)] + [InlineData(typeof(ushort), 0, true)] + [InlineData(typeof(int), 0, true)] + [InlineData(typeof(uint), 0, true)] + [InlineData(typeof(long), 0, true)] + [InlineData(typeof(ulong), 0, true)] + [InlineData(typeof(decimal), 0, true)] + [InlineData(typeof(bool), false, true)] + [InlineData(typeof(char), '\x0000', true)] + [InlineData(typeof(string), null, true)] + [InlineData(typeof(object), null, true)] + [InlineData(typeof(PhonyClass), null, true)] + [InlineData(typeof(DateTime), null, true)] // Special case handled within the test. + [InlineData(typeof(DateTime), null, false)] // Special case handled within the test. + [InlineData(typeof(byte?), null, true)] + [InlineData(typeof(short?), null, true)] + [InlineData(typeof(ushort?), null, true)] + [InlineData(typeof(int?), null, true)] + [InlineData(typeof(uint?), null, true)] + [InlineData(typeof(long?), null, true)] + [InlineData(typeof(ulong?), null, true)] + [InlineData(typeof(decimal?), null, true)] + [InlineData(typeof(bool?), null, true)] + [InlineData(typeof(char?), null, true)] + public void IsNullOrDefault_Matches_Expected(Type type, object? value, bool expectedResult) + { + if (type == typeof(DateTime)) + { + if (expectedResult) + { + value = default(DateTime); + } + else + { + value = DateTime.Now; + } + } + + Assert.Equal(expectedResult, type.IsNullOrDefault(value)); + Assert.Equal(expectedResult, value.IsNullOrDefault()); + } + + private class PhonyClass + { + } + } +} diff --git a/tests/Jellyfin.Server.Implementations.Tests/Plugins/PluginManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Plugins/PluginManagerTests.cs index d9fdc96f55..204d144214 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Plugins/PluginManagerTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Plugins/PluginManagerTests.cs @@ -1,12 +1,16 @@ using System; +using System.Globalization; using System.IO; using System.Text.Json; +using System.Threading.Tasks; +using AutoFixture; using Emby.Server.Implementations.Library; using Emby.Server.Implementations.Plugins; using Jellyfin.Extensions.Json; using Jellyfin.Extensions.Json.Converters; using MediaBrowser.Common.Plugins; using MediaBrowser.Model.Plugins; +using MediaBrowser.Model.Updates; using Microsoft.Extensions.Logging.Abstractions; using Xunit; @@ -16,6 +20,21 @@ namespace Jellyfin.Server.Implementations.Tests.Plugins { private static readonly string _testPathRoot = Path.Combine(Path.GetTempPath(), "jellyfin-test-data"); + private string _tempPath = string.Empty; + + private string _pluginPath = string.Empty; + + private JsonSerializerOptions _options; + + public PluginManagerTests() + { + (_tempPath, _pluginPath) = GetTestPaths("plugin-" + Path.GetRandomFileName()); + + Directory.CreateDirectory(_pluginPath); + + _options = GetTestSerializerOptions(); + } + [Fact] public void SaveManifest_RoundTrip_Success() { @@ -25,12 +44,9 @@ namespace Jellyfin.Server.Implementations.Tests.Plugins Version = "1.0" }; - var tempPath = Path.Combine(_testPathRoot, "manifest-" + Path.GetRandomFileName()); - Directory.CreateDirectory(tempPath); - - Assert.True(pluginManager.SaveManifest(manifest, tempPath)); + Assert.True(pluginManager.SaveManifest(manifest, _pluginPath)); - var res = pluginManager.LoadManifest(tempPath); + var res = pluginManager.LoadManifest(_pluginPath); Assert.Equal(manifest.Category, res.Manifest.Category); Assert.Equal(manifest.Changelog, res.Manifest.Changelog); @@ -66,28 +82,25 @@ namespace Jellyfin.Server.Implementations.Tests.Plugins }; var filename = Path.GetFileName(dllFile)!; - var (tempPath, pluginPath) = GetTestPaths("safe"); + var dllPath = Path.GetDirectoryName(Path.Combine(_pluginPath, dllFile))!; - Directory.CreateDirectory(Path.Combine(pluginPath, dllFile.Replace(filename, string.Empty, StringComparison.OrdinalIgnoreCase))); - File.Create(Path.Combine(pluginPath, dllFile)); + Directory.CreateDirectory(dllPath); + File.Create(Path.Combine(dllPath, filename)); + var metafilePath = Path.Combine(_pluginPath, "meta.json"); - var options = GetTestSerializerOptions(); - var data = JsonSerializer.Serialize(manifest, options); - var metafilePath = Path.Combine(tempPath, "safe", "meta.json"); + File.WriteAllText(metafilePath, JsonSerializer.Serialize(manifest, _options)); - File.WriteAllText(metafilePath, data); + var pluginManager = new PluginManager(new NullLogger<PluginManager>(), null!, null!, _tempPath, new Version(1, 0)); - var pluginManager = new PluginManager(new NullLogger<PluginManager>(), null!, null!, tempPath, new Version(1, 0)); + var res = JsonSerializer.Deserialize<PluginManifest>(File.ReadAllText(metafilePath), _options); - var res = JsonSerializer.Deserialize<PluginManifest>(File.ReadAllText(metafilePath), options); - - var expectedFullPath = Path.Combine(pluginPath, dllFile).Canonicalize(); + var expectedFullPath = Path.Combine(_pluginPath, dllFile).Canonicalize(); Assert.NotNull(res); Assert.NotEmpty(pluginManager.Plugins); Assert.Equal(PluginStatus.Active, res!.Status); Assert.Equal(expectedFullPath, pluginManager.Plugins[0].DllFiles[0]); - Assert.StartsWith(Path.Combine(tempPath, "safe"), expectedFullPath, StringComparison.InvariantCulture); + Assert.StartsWith(_pluginPath, expectedFullPath, StringComparison.InvariantCulture); } /// <summary> @@ -109,7 +122,6 @@ namespace Jellyfin.Server.Implementations.Tests.Plugins [InlineData("..\\.\\..\\.\\..\\some.dll")] // Windows traversal with current and parent [InlineData("\\\\network\\resource.dll")] // UNC Path [InlineData("https://jellyfin.org/some.dll")] // URL - [InlineData("....//....//some.dll")] // Path replacement risk if a single "../" replacement occurs. [InlineData("~/some.dll")] // Tilde poses a shell expansion risk, but is a valid path character. public void Constructor_DiscoversUnsafePluginAssembly_Status_Malfunctioned(string unsafePath) { @@ -120,10 +132,7 @@ namespace Jellyfin.Server.Implementations.Tests.Plugins Assemblies = new string[] { unsafePath } }; - var (tempPath, pluginPath) = GetTestPaths("unsafe"); - - Directory.CreateDirectory(pluginPath); - + // Only create very specific files. Otherwise the test will be exploiting path traversal. var files = new string[] { "../other.dll", @@ -132,24 +141,136 @@ namespace Jellyfin.Server.Implementations.Tests.Plugins foreach (var file in files) { - File.Create(Path.Combine(pluginPath, file)); + File.Create(Path.Combine(_pluginPath, file)); } - var options = GetTestSerializerOptions(); - var data = JsonSerializer.Serialize(manifest, options); - var metafilePath = Path.Combine(tempPath, "unsafe", "meta.json"); + var metafilePath = Path.Combine(_pluginPath, "meta.json"); - File.WriteAllText(metafilePath, data); + File.WriteAllText(metafilePath, JsonSerializer.Serialize(manifest, _options)); - var pluginManager = new PluginManager(new NullLogger<PluginManager>(), null!, null!, tempPath, new Version(1, 0)); + var pluginManager = new PluginManager(new NullLogger<PluginManager>(), null!, null!, _tempPath, new Version(1, 0)); - var res = JsonSerializer.Deserialize<PluginManifest>(File.ReadAllText(metafilePath), options); + var res = JsonSerializer.Deserialize<PluginManifest>(File.ReadAllText(metafilePath), _options); Assert.NotNull(res); Assert.Empty(pluginManager.Plugins); Assert.Equal(PluginStatus.Malfunctioned, res!.Status); } + [Fact] + public async Task PopulateManifest_ExistingMetafilePlugin_PopulatesMissingFields() + { + var packageInfo = GenerateTestPackage(); + + // Partial plugin without a name, but matching version and package ID + var partial = new PluginManifest + { + Id = packageInfo.Id, + AutoUpdate = false, // Turn off AutoUpdate + Status = PluginStatus.Restart, + Version = new Version(1, 0, 0).ToString(), + Assemblies = new[] { "Jellyfin.Test.dll" } + }; + + var metafilePath = Path.Combine(_pluginPath, "meta.json"); + File.WriteAllText(metafilePath, JsonSerializer.Serialize(partial, _options)); + + var pluginManager = new PluginManager(new NullLogger<PluginManager>(), null!, null!, _tempPath, new Version(1, 0)); + + await pluginManager.PopulateManifest(packageInfo, new Version(1, 0), _pluginPath, PluginStatus.Active); + + var resultBytes = File.ReadAllBytes(metafilePath); + var result = JsonSerializer.Deserialize<PluginManifest>(resultBytes, _options); + + Assert.NotNull(result); + Assert.Equal(packageInfo.Name, result.Name); + Assert.Equal(packageInfo.Owner, result.Owner); + Assert.Equal(PluginStatus.Active, result.Status); + Assert.NotEmpty(result.Assemblies); + Assert.False(result.AutoUpdate); + + // Preserved + Assert.Equal(packageInfo.Category, result.Category); + Assert.Equal(packageInfo.Description, result.Description); + Assert.Equal(packageInfo.Id, result.Id); + Assert.Equal(packageInfo.Overview, result.Overview); + Assert.Equal(partial.Assemblies[0], result.Assemblies[0]); + Assert.Equal(packageInfo.Versions[0].TargetAbi, result.TargetAbi); + Assert.Equal(DateTime.Parse(packageInfo.Versions[0].Timestamp!, CultureInfo.InvariantCulture), result.Timestamp); + Assert.Equal(packageInfo.Versions[0].Changelog, result.Changelog); + Assert.Equal(packageInfo.Versions[0].Version, result.Version); + } + + [Fact] + public async Task PopulateManifest_ExistingMetafileMismatchedIds_Status_Malfunctioned() + { + var packageInfo = GenerateTestPackage(); + + // Partial plugin without a name, but matching version and package ID + var partial = new PluginManifest + { + Id = Guid.NewGuid(), + Version = new Version(1, 0, 0).ToString() + }; + + var metafilePath = Path.Combine(_pluginPath, "meta.json"); + File.WriteAllText(metafilePath, JsonSerializer.Serialize(partial, _options)); + + var pluginManager = new PluginManager(new NullLogger<PluginManager>(), null!, null!, _tempPath, new Version(1, 0)); + + await pluginManager.PopulateManifest(packageInfo, new Version(1, 0), _pluginPath, PluginStatus.Active); + + var resultBytes = File.ReadAllBytes(metafilePath); + var result = JsonSerializer.Deserialize<PluginManifest>(resultBytes, _options); + + Assert.NotNull(result); + Assert.Equal(packageInfo.Name, result.Name); + Assert.Equal(PluginStatus.Malfunctioned, result.Status); + } + + [Fact] + public async Task PopulateManifest_ExistingMetafileMismatchedVersions_Updates_Version() + { + var packageInfo = GenerateTestPackage(); + + var partial = new PluginManifest + { + Id = packageInfo.Id, + Version = new Version(2, 0, 0).ToString() + }; + + var metafilePath = Path.Combine(_pluginPath, "meta.json"); + File.WriteAllText(metafilePath, JsonSerializer.Serialize(partial, _options)); + + var pluginManager = new PluginManager(new NullLogger<PluginManager>(), null!, null!, _tempPath, new Version(1, 0)); + + await pluginManager.PopulateManifest(packageInfo, new Version(1, 0), _pluginPath, PluginStatus.Active); + + var resultBytes = File.ReadAllBytes(metafilePath); + var result = JsonSerializer.Deserialize<PluginManifest>(resultBytes, _options); + + Assert.NotNull(result); + Assert.Equal(packageInfo.Name, result.Name); + Assert.Equal(PluginStatus.Active, result.Status); + Assert.Equal(packageInfo.Versions[0].Version, result.Version); + } + + private PackageInfo GenerateTestPackage() + { + var fixture = new Fixture(); + fixture.Customize<PackageInfo>(c => c.Without(x => x.Versions).Without(x => x.ImageUrl)); + fixture.Customize<VersionInfo>(c => c.Without(x => x.Version).Without(x => x.Timestamp)); + + var versionInfo = fixture.Create<VersionInfo>(); + versionInfo.Version = new Version(1, 0).ToString(); + versionInfo.Timestamp = DateTime.UtcNow.ToString(CultureInfo.InvariantCulture); + + var packageInfo = fixture.Create<PackageInfo>(); + packageInfo.Versions = new[] { versionInfo }; + + return packageInfo; + } + private JsonSerializerOptions GetTestSerializerOptions() { var options = new JsonSerializerOptions(JsonDefaults.Options) @@ -171,7 +292,7 @@ namespace Jellyfin.Server.Implementations.Tests.Plugins private (string TempPath, string PluginPath) GetTestPaths(string pluginFolderName) { - var tempPath = Path.Combine(_testPathRoot, "plugins-" + Path.GetRandomFileName()); + var tempPath = Path.Combine(_testPathRoot, "plugin-manager" + Path.GetRandomFileName()); var pluginPath = Path.Combine(tempPath, pluginFolderName); return (tempPath, pluginPath); diff --git a/tests/Jellyfin.Server.Implementations.Tests/Test Data/Updates/manifest-stable.json b/tests/Jellyfin.Server.Implementations.Tests/Test Data/Updates/manifest-stable.json index 3aec299587..57367ce88c 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Test Data/Updates/manifest-stable.json +++ b/tests/Jellyfin.Server.Implementations.Tests/Test Data/Updates/manifest-stable.json @@ -13,8 +13,7 @@ "targetAbi": "10.6.0.0", "sourceUrl": "https://repo.jellyfin.org/releases/plugin/anime/anime_10.0.0.0.zip", "checksum": "93e969adeba1050423fc8817ed3c36f8", - "timestamp": "2020-08-17T01:41:13Z", - "assemblies": [ "Jellyfin.Plugin.Anime.dll" ] + "timestamp": "2020-08-17T01:41:13Z" }, { "version": "9.0.0.0", @@ -22,8 +21,7 @@ "targetAbi": "10.6.0.0", "sourceUrl": "https://repo.jellyfin.org/releases/plugin/anime/anime_9.0.0.0.zip", "checksum": "9b1cebff835813e15f414f44b40c41c8", - "timestamp": "2020-07-20T01:30:16Z", - "assemblies": [ "Jellyfin.Plugin.Anime.dll" ] + "timestamp": "2020-07-20T01:30:16Z" } ] }, diff --git a/tests/Jellyfin.Server.Implementations.Tests/Updates/InstallationManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Updates/InstallationManagerTests.cs index 70d03f8c4a..7abd2e685f 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Updates/InstallationManagerTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Updates/InstallationManagerTests.cs @@ -106,19 +106,5 @@ namespace Jellyfin.Server.Implementations.Tests.Updates var ex = await Record.ExceptionAsync(() => _installationManager.InstallPackage(packageInfo, CancellationToken.None)).ConfigureAwait(false); Assert.Null(ex); } - - [Fact] - public async Task InstallPackage_WithAssemblies_Success() - { - PackageInfo[] packages = await _installationManager.GetPackages( - "Jellyfin Stable", - "https://repo.jellyfin.org/releases/plugin/manifest-stable.json", - false); - - packages = _installationManager.FilterPackages(packages, "Anime").ToArray(); - Assert.Single(packages); - Assert.NotEmpty(packages[0].Versions[0].Assemblies); - Assert.Equal("Jellyfin.Plugin.Anime.dll", packages[0].Versions[0].Assemblies[0]); - } } } -- cgit v1.2.3 From c92ad0dc4bf3da2d6eeb87a47d3322bba2de1503 Mon Sep 17 00:00:00 2001 From: felix920506 <felix920506@gmail.com> Date: Sun, 9 Apr 2023 02:07:39 +0000 Subject: Translated using Weblate (Chinese (Traditional)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/zh_Hant/ --- Emby.Server.Implementations/Localization/Core/zh-TW.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/zh-TW.json b/Emby.Server.Implementations/Localization/Core/zh-TW.json index 4949c5ab6d..36f4df93d1 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-TW.json +++ b/Emby.Server.Implementations/Localization/Core/zh-TW.json @@ -91,14 +91,14 @@ "HeaderRecordingGroups": "錄製組", "Inherit": "繼承", "SubtitleDownloadFailureFromForItem": "無法為 {1} 從 {0} 下載字幕", - "TaskDownloadMissingSubtitlesDescription": "透過中繼資料從網路上搜尋遺失的字幕。", + "TaskDownloadMissingSubtitlesDescription": "透過媒體資訊從網路上搜尋遺失的字幕。", "TaskDownloadMissingSubtitles": "下載遺失的字幕", "TaskRefreshChannels": "重新整理頻道", "TaskUpdatePlugins": "更新附加元件", "TaskRefreshPeople": "更新人物", "TaskCleanLogsDescription": "刪除超過 {0} 天的日誌文件。", "TaskCleanLogs": "清空日誌資料夾", - "TaskRefreshLibraryDescription": "重新掃描媒體庫的新檔案並更新中繼資料。", + "TaskRefreshLibraryDescription": "重新掃描媒體庫的新檔案並更新媒體資訊。", "TaskRefreshLibrary": "重新掃描媒體庫", "TaskRefreshChapterImages": "擷取章節圖片", "TaskCleanCacheDescription": "刪除系統已不需要的快取。", @@ -108,7 +108,7 @@ "TaskCleanTranscodeDescription": "刪除超過一天的轉碼檔案。", "TaskCleanTranscode": "清除轉碼資料夾", "TaskUpdatePluginsDescription": "為已設置為自動更新的附加元件下載並安裝更新。", - "TaskRefreshPeopleDescription": "更新媒體庫中演員和導演的中繼資料。", + "TaskRefreshPeopleDescription": "更新媒體庫中演員和導演的資訊。", "TaskRefreshChapterImagesDescription": "為有章節的影片建立縮圖。", "TasksChannelsCategory": "網路頻道", "TasksApplicationCategory": "應用程式", -- cgit v1.2.3 From 3c22d5c9705921672a932192d016933ef5900001 Mon Sep 17 00:00:00 2001 From: JPVenson <JPVenson@users.noreply.github.com> Date: Mon, 10 Apr 2023 22:38:07 +0300 Subject: #7626 Added handling for common FormatExceptions with Skia loading sv… (#9581) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Shadowghost <Shadowghost@users.noreply.github.com> --- Emby.Server.Implementations/Library/LibraryManager.cs | 2 +- src/Jellyfin.Drawing.Skia/SkiaEncoder.cs | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 8883aff0b0..75a1a5a4d8 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -1885,7 +1885,7 @@ namespace Emby.Server.Implementations.Library catch (Exception ex) { _logger.LogError(ex, "Cannot get image dimensions for {ImagePath}", image.Path); - size = new ImageDimensions(0, 0); + size = default; image.Width = 0; image.Height = 0; } diff --git a/src/Jellyfin.Drawing.Skia/SkiaEncoder.cs b/src/Jellyfin.Drawing.Skia/SkiaEncoder.cs index 6da77ad959..2d980db181 100644 --- a/src/Jellyfin.Drawing.Skia/SkiaEncoder.cs +++ b/src/Jellyfin.Drawing.Skia/SkiaEncoder.cs @@ -120,8 +120,18 @@ public class SkiaEncoder : IImageEncoder if (extension.Equals(".svg", StringComparison.OrdinalIgnoreCase)) { var svg = new SKSvg(); - svg.Load(path); - return new ImageDimensions(Convert.ToInt32(svg.Picture.CullRect.Width), Convert.ToInt32(svg.Picture.CullRect.Height)); + try + { + svg.Load(path); + return new ImageDimensions(Convert.ToInt32(svg.Picture.CullRect.Width), Convert.ToInt32(svg.Picture.CullRect.Height)); + } + catch (FormatException skiaColorException) + { + // This exception is known to be thrown on vector images that define custom styles + // Skia SVG is not able to handle that and as the repository is quite stale and has not received updates we just catch them + _logger.LogDebug(skiaColorException, "There was a issue loading the requested svg file"); + return default; + } } using var codec = SKCodec.Create(path, out SKCodecResult result); @@ -132,10 +142,10 @@ public class SkiaEncoder : IImageEncoder return new ImageDimensions(info.Width, info.Height); case SKCodecResult.Unimplemented: _logger.LogDebug("Image format not supported: {FilePath}", path); - return new ImageDimensions(0, 0); + return default; default: _logger.LogError("Unable to determine image dimensions for {FilePath}: {SkCodecResult}", path, result); - return new ImageDimensions(0, 0); + return default; } } -- cgit v1.2.3 From b69abd4d7dfdbdaa62b5c7e12aa36bfdc9e15774 Mon Sep 17 00:00:00 2001 From: Bond_009 <bond.009@outlook.com> Date: Thu, 13 Apr 2023 01:50:57 +0200 Subject: Properly dispose prepared statements --- .../Data/BaseSqliteRepository.cs | 12 --- .../Data/SqliteItemRepository.cs | 99 +++++++++------------- 2 files changed, 39 insertions(+), 72 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs index 47706a4401..bc520b86e7 100644 --- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs +++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs @@ -166,18 +166,6 @@ namespace Emby.Server.Implementations.Data public IStatement PrepareStatement(IDatabaseConnection connection, string sql) => connection.PrepareStatement(sql); - public IStatement[] PrepareAll(IDatabaseConnection connection, IReadOnlyList<string> sql) - { - int len = sql.Count; - IStatement[] statements = new IStatement[len]; - for (int i = 0; i < len; i++) - { - statements[i] = connection.PrepareStatement(sql[i]); - } - - return statements; - } - protected bool TableExists(ManagedConnection connection, string name) { return connection.RunInTransaction( diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 80c4e5b103..71788a3e43 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -624,14 +624,8 @@ namespace Emby.Server.Implementations.Data private void SaveItemsInTransaction(IDatabaseConnection db, IEnumerable<(BaseItem Item, List<Guid> AncestorIds, BaseItem TopParent, string UserDataKey, List<string> InheritedTags)> tuples) { - var statements = PrepareAll(db, new string[] - { - SaveItemCommandText, - "delete from AncestorIds where ItemId=@ItemId" - }); - - using (var saveItemStatement = statements[0]) - using (var deleteAncestorsStatement = statements[1]) + using (var saveItemStatement = PrepareStatement(db, SaveItemCommandText)) + using (var deleteAncestorsStatement = PrepareStatement(db, "delete from AncestorIds where ItemId=@ItemId")) { var requiresReset = false; foreach (var tuple in tuples) @@ -1286,15 +1280,13 @@ namespace Emby.Server.Implementations.Data CheckDisposed(); using (var connection = GetConnection(true)) + using (var statement = PrepareStatement(connection, _retrieveItemColumnsSelectQuery)) { - using (var statement = PrepareStatement(connection, _retrieveItemColumnsSelectQuery)) - { - statement.TryBind("@guid", id); + statement.TryBind("@guid", id); - foreach (var row in statement.ExecuteQuery()) - { - return GetItem(row, new InternalItemsQuery()); - } + foreach (var row in statement.ExecuteQuery()) + { + return GetItem(row, new InternalItemsQuery()); } } @@ -1972,22 +1964,19 @@ namespace Emby.Server.Implementations.Data { CheckDisposed(); + var chapters = new List<ChapterInfo>(); using (var connection = GetConnection(true)) + using (var statement = PrepareStatement(connection, "select StartPositionTicks,Name,ImagePath,ImageDateModified from " + ChaptersTableName + " where ItemId = @ItemId order by ChapterIndex asc")) { - var chapters = new List<ChapterInfo>(); + statement.TryBind("@ItemId", item.Id); - using (var statement = PrepareStatement(connection, "select StartPositionTicks,Name,ImagePath,ImageDateModified from " + ChaptersTableName + " where ItemId = @ItemId order by ChapterIndex asc")) + foreach (var row in statement.ExecuteQuery()) { - statement.TryBind("@ItemId", item.Id); - - foreach (var row in statement.ExecuteQuery()) - { - chapters.Add(GetChapter(row, item)); - } + chapters.Add(GetChapter(row, item)); } - - return chapters; } + + return chapters; } /// <inheritdoc /> @@ -1996,16 +1985,14 @@ namespace Emby.Server.Implementations.Data CheckDisposed(); using (var connection = GetConnection(true)) + using (var statement = PrepareStatement(connection, "select StartPositionTicks,Name,ImagePath,ImageDateModified from " + ChaptersTableName + " where ItemId = @ItemId and ChapterIndex=@ChapterIndex")) { - using (var statement = PrepareStatement(connection, "select StartPositionTicks,Name,ImagePath,ImageDateModified from " + ChaptersTableName + " where ItemId = @ItemId and ChapterIndex=@ChapterIndex")) - { - statement.TryBind("@ItemId", item.Id); - statement.TryBind("@ChapterIndex", index); + statement.TryBind("@ItemId", item.Id); + statement.TryBind("@ChapterIndex", index); - foreach (var row in statement.ExecuteQuery()) - { - return GetChapter(row, item); - } + foreach (var row in statement.ExecuteQuery()) + { + return GetChapter(row, item); } } @@ -2858,13 +2845,10 @@ namespace Emby.Server.Implementations.Data connection.RunInTransaction( db => { - var itemQueryStatement = PrepareStatement(db, itemQuery); - var totalRecordCountQueryStatement = PrepareStatement(db, totalRecordCountQuery); - if (!isReturningZeroItems) { using (new QueryTimeLogger(Logger, itemQuery, "GetItems.ItemQuery")) - using (var statement = itemQueryStatement) + using (var statement = PrepareStatement(db, itemQuery)) { if (EnableJoinUserData(query)) { @@ -2899,7 +2883,7 @@ namespace Emby.Server.Implementations.Data if (query.EnableTotalRecordCount) { using (new QueryTimeLogger(Logger, totalRecordCountQuery, "GetItems.TotalRecordCount")) - using (var statement = totalRecordCountQueryStatement) + using (var statement = PrepareStatement(db, totalRecordCountQuery)) { if (EnableJoinUserData(query)) { @@ -4768,22 +4752,20 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type commandText.Append(" LIMIT ").Append(query.Limit); } + var list = new List<string>(); using (var connection = GetConnection(true)) + using (var statement = PrepareStatement(connection, commandText.ToString())) { - var list = new List<string>(); - using (var statement = PrepareStatement(connection, commandText.ToString())) - { - // Run this again to bind the params - GetPeopleWhereClauses(query, statement); + // Run this again to bind the params + GetPeopleWhereClauses(query, statement); - foreach (var row in statement.ExecuteQuery()) - { - list.Add(row.GetString(0)); - } + foreach (var row in statement.ExecuteQuery()) + { + list.Add(row.GetString(0)); } - - return list; } + + return list; } public List<PersonInfo> GetPeople(InternalPeopleQuery query) @@ -4808,23 +4790,20 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type commandText += " LIMIT " + query.Limit; } + var list = new List<PersonInfo>(); using (var connection = GetConnection(true)) + using (var statement = PrepareStatement(connection, commandText)) { - var list = new List<PersonInfo>(); + // Run this again to bind the params + GetPeopleWhereClauses(query, statement); - using (var statement = PrepareStatement(connection, commandText)) + foreach (var row in statement.ExecuteQuery()) { - // Run this again to bind the params - GetPeopleWhereClauses(query, statement); - - foreach (var row in statement.ExecuteQuery()) - { - list.Add(GetPerson(row)); - } + list.Add(GetPerson(row)); } - - return list; } + + return list; } private List<string> GetPeopleWhereClauses(InternalPeopleQuery query, IStatement statement) -- cgit v1.2.3 From 643755cfa37352926fe4538d8598a0c4513f7412 Mon Sep 17 00:00:00 2001 From: David Pereira Cruz <david.cruz@gmail.com> Date: Tue, 11 Apr 2023 16:24:11 +0000 Subject: Translated using Weblate (Portuguese) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/pt/ --- Emby.Server.Implementations/Localization/Core/pt.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/pt.json b/Emby.Server.Implementations/Localization/Core/pt.json index 92e0d34aec..2281e80c8a 100644 --- a/Emby.Server.Implementations/Localization/Core/pt.json +++ b/Emby.Server.Implementations/Localization/Core/pt.json @@ -122,5 +122,6 @@ "TaskOptimizeDatabaseDescription": "Base de dados compacta e corta espaço livre. A execução desta tarefa depois de digitalizar a biblioteca ou de fazer outras alterações que impliquem modificações na base de dados pode melhorar o desempenho.", "External": "Externo", "HearingImpaired": "Problemas auditivos", - "TaskKeyframeExtractor": "Extrator de quadro-chave" + "TaskKeyframeExtractor": "Extrator de quadro-chave", + "TaskKeyframeExtractorDescription": "Retira frames chave do video para criar listas HLS precisas. Esta tarefa pode correr durante algum tempo." } -- cgit v1.2.3 From 39af7f668c4ec2c283b063f71550baebb5454d3f Mon Sep 17 00:00:00 2001 From: Aman Alam <amanpreet.alam@gmail.com> Date: Wed, 12 Apr 2023 02:44:20 +0000 Subject: Translated using Weblate (Punjabi) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/pa/ --- .../Localization/Core/pa.json | 42 +++++++++++----------- 1 file changed, 22 insertions(+), 20 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/pa.json b/Emby.Server.Implementations/Localization/Core/pa.json index 4ac57b630d..1f982feaf4 100644 --- a/Emby.Server.Implementations/Localization/Core/pa.json +++ b/Emby.Server.Implementations/Localization/Core/pa.json @@ -28,22 +28,22 @@ "ValueHasBeenAddedToLibrary": "{0} ਤੁਹਾਡੀ ਮੀਡੀਆ ਲਾਇਬ੍ਰੇਰੀ ਵਿੱਚ ਸ਼ਾਮਲ ਕੀਤਾ ਗਿਆ ਹੈ", "UserStoppedPlayingItemWithValues": "{0} ਨੇ {2} 'ਤੇ {1} ਖੇਡਣਾ ਪੂਰਾ ਕਰ ਲਿਆ ਹੈ", "UserStartedPlayingItemWithValues": "{0} {2} 'ਤੇ {1} ਖੇਡ ਰਿਹਾ ਹੈ", - "UserPolicyUpdatedWithName": "ਉਪਭੋਗਤਾ ਨੀਤੀ ਨੂੰ {0} ਲਈ ਅਪਡੇਟ ਕੀਤਾ ਗਿਆ ਹੈ", - "UserPasswordChangedWithName": "ਪਾਸਵਰਡ ਯੂਜ਼ਰ ਲਈ ਬਦਲਿਆ ਗਿਆ ਹੈ {0}", - "UserOnlineFromDevice": "{0} ਤੋਂ isਨਲਾਈਨ ਹੈ {1}", + "UserPolicyUpdatedWithName": "ਵਰਤੋਂਕਾਰ ਨੀਤੀ ਨੂੰ {0} ਲਈ ਅਪਡੇਟ ਕੀਤਾ ਗਿਆ ਹੈ", + "UserPasswordChangedWithName": "{0} ਵਰਤੋਂਕਾਰ ਲਈ ਪਾਸਵਰਡ ਬਦਲਿਆ ਗਿਆ ਸੀ", + "UserOnlineFromDevice": "{0} ਨੂੰ {1} ਤੋਂ ਆਨਲਾਈਨ ਹੈ", "UserOfflineFromDevice": "{0} ਤੋਂ ਡਿਸਕਨੈਕਟ ਹੋ ਗਿਆ ਹੈ {1}", - "UserLockedOutWithName": "ਯੂਜ਼ਰ {0} ਨੂੰ ਲਾਕ ਆਉਟ ਕਰ ਦਿੱਤਾ ਗਿਆ ਹੈ", - "UserDownloadingItemWithValues": "{0} ਡਾ{ਨਲੋਡ ਕਰ ਰਿਹਾ ਹੈ {1}", - "UserDeletedWithName": "ਯੂਜ਼ਰ {0} ਨੂੰ ਮਿਟਾ ਦਿੱਤਾ ਗਿਆ ਹੈ", - "UserCreatedWithName": "ਯੂਜ਼ਰ {0} ਬਣਾਇਆ ਗਿਆ ਹੈ", - "User": "ਯੂਜ਼ਰ", + "UserLockedOutWithName": "ਵਰਤੋਂਕਾਰ {0} ਨੂੰ ਲਾਕ ਕੀਤਾ ਗਿਆ ਹੈ", + "UserDownloadingItemWithValues": "{0} {1} ਨੂੰ ਡਾਊਨਲੋਡ ਕਰ ਰਿਹਾ ਹੈ", + "UserDeletedWithName": "ਵਰਤੋਂਕਾਰ {0} ਨੂੰ ਹਟਾਇਆ ਗਿਆ", + "UserCreatedWithName": "ਵਰਤੋਂਕਾਰ {0} ਬਣਾਇਆ ਗਿਆ ਹੈ", + "User": "ਵਰਤੋਂਕਾਰ", "Undefined": "ਪਰਿਭਾਸ਼ਤ", - "TvShows": "ਟੀਵੀ ਸ਼ੋਅਜ਼", + "TvShows": "ਟੀਵੀ ਸ਼ੋਅ", "System": "ਸਿਸਟਮ", "Sync": "ਸਿੰਕ", - "SubtitleDownloadFailureFromForItem": "ਉਪਸਿਰਲੇਖ {1} ਲਈ {0} ਤੋਂ ਡਾ toਨਲੋਡ ਕਰਨ ਵਿੱਚ ਅਸਫਲ ਰਹੇ", - "StartupEmbyServerIsLoading": "ਜੈਲੀਫਿਨ ਸਰਵਰ ਲੋਡ ਹੋ ਰਿਹਾ ਹੈ. ਕਿਰਪਾ ਕਰਕੇ ਜਲਦੀ ਹੀ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ.", - "Songs": "ਗਾਣੇਂ", + "SubtitleDownloadFailureFromForItem": "ਉਪਸਿਰਲੇਖ {1} ਲਈ {0} ਤੋਂ ਡਾਊਨਲੋਡ ਕਰਨ ਵਿੱਚ ਅਸਫਲ ਰਹੇ", + "StartupEmbyServerIsLoading": "Jellyfin ਸਰਵਰ ਲੋਡ ਹੋ ਰਿਹਾ ਹੈ। ਛੇਤੀ ਹੀ ਫ਼ੇਰ ਕੋਸ਼ਿਸ਼ ਕਰੋ।", + "Songs": "ਗਾਣੇ", "Shows": "ਸ਼ੋਅ", "ServerNameNeedsToBeRestarted": "{0} ਮੁੜ ਚਾਲੂ ਕਰਨ ਦੀ ਲੋੜ ਹੈ", "ScheduledTaskStartedWithName": "{0} ਸ਼ੁਰੂ ਹੋਇਆ", @@ -57,12 +57,12 @@ "Photos": "ਫੋਟੋਆਂ", "NotificationOptionVideoPlaybackStopped": "ਵੀਡੀਓ ਪਲੇਬੈਕ ਰੋਕਿਆ ਗਿਆ", "NotificationOptionVideoPlayback": "ਵੀਡੀਓ ਪਲੇਬੈਕ ਸ਼ੁਰੂ ਹੋਇਆ", - "NotificationOptionUserLockedOut": "ਉਪਭੋਗਤਾ ਨੂੰ ਲਾਕ ਆਉਟ ਕੀਤਾ ਗਿਆ", + "NotificationOptionUserLockedOut": "ਵਰਤੋਂਕਾਰ ਨੂੰ ਲਾਕ ਕੀਤਾ", "NotificationOptionTaskFailed": "ਨਿਰਧਾਰਤ ਕਾਰਜ ਅਸਫਲਤਾ", "NotificationOptionServerRestartRequired": "ਸਰਵਰ ਨੂੰ ਮੁੜ ਚਾਲੂ ਕਰਨ ਦੀ ਲੋੜ ਹੈ", "NotificationOptionPluginUpdateInstalled": "ਪਲੱਗਇਨ ਅਪਡੇਟ ਇੰਸਟੌਲ ਕੀਤਾ ਗਿਆ", "NotificationOptionPluginUninstalled": "ਪਲੱਗਇਨ ਅਣਇੰਸਟੌਲ ਕੀਤਾ", - "NotificationOptionPluginInstalled": "ਪਲੱਗਇਨ ਸਥਾਪਿਤ ਕੀਤਾ", + "NotificationOptionPluginInstalled": "ਪਲੱਗਇਨ ਇੰਸਟਾਲ ਕੀਤੀ", "NotificationOptionPluginError": "ਪਲੱਗਇਨ ਅਸਫਲ", "NotificationOptionNewLibraryContent": "ਨਵੀਂ ਸਮੱਗਰੀ ਸ਼ਾਮਲ ਕੀਤੀ ਗਈ", "NotificationOptionInstallationFailed": "ਇੰਸਟਾਲੇਸ਼ਨ ਅਸਫਲ", @@ -92,7 +92,7 @@ "HomeVideos": "ਘਰੇਲੂ ਵੀਡੀਓ", "HeaderRecordingGroups": "ਰਿਕਾਰਡਿੰਗ ਸਮੂਹ", "HeaderNextUp": "ਅੱਗੇ", - "HeaderLiveTV": "ਲਾਈਵ ਟੀ", + "HeaderLiveTV": "ਲਾਈਵ ਟੀਵੀ", "HeaderFavoriteSongs": "ਮਨਪਸੰਦ ਗਾਣੇ", "HeaderFavoriteShows": "ਮਨਪਸੰਦ ਸ਼ੋਅ", "HeaderFavoriteEpisodes": "ਮਨਪਸੰਦ ਐਪੀਸੋਡ", @@ -102,20 +102,22 @@ "HeaderAlbumArtists": "ਐਲਬਮ ਕਲਾਕਾਰ", "Genres": "ਸ਼ੈਲੀਆਂ", "Forced": "ਮਜਬੂਰ", - "Folders": "ਫੋਲਡਰਸ", + "Folders": "ਫੋਲਡਰ", "Favorites": "ਮਨਪਸੰਦ", - "FailedLoginAttemptWithUserName": "ਤੋਂ ਲਾਗਇਨ ਕੋਸ਼ਿਸ਼ ਫੇਲ ਹੋਈ {0}", + "FailedLoginAttemptWithUserName": "{0} ਤੋਂ ਲਾਗਇਨ ਕੋਸ਼ਿਸ਼ ਫੇਲ ਹੋਈ", "DeviceOnlineWithName": "{0} ਜੁੜਿਆ ਹੋਇਆ ਹੈ", "DeviceOfflineWithName": "{0} ਡਿਸਕਨੈਕਟ ਹੋ ਗਿਆ ਹੈ", "Default": "ਡਿਫੌਲਟ", "Collections": "ਸੰਗ੍ਰਹਿਣ", - "ChapterNameValue": "ਅਧਿਆਇ {0}", + "ChapterNameValue": "ਚੈਪਟਰ {0}", "Channels": "ਚੈਨਲ", - "CameraImageUploadedFrom": "ਤੋਂ ਇੱਕ ਨਵਾਂ ਕੈਮਰਾ ਚਿੱਤਰ ਅਪਲੋਡ ਕੀਤਾ ਗਿਆ ਹੈ {0}", + "CameraImageUploadedFrom": "{0} ਤੋਂ ਇੱਕ ਨਵਾਂ ਕੈਮਰਾ ਚਿੱਤਰ ਅਪਲੋਡ ਕੀਤਾ ਗਿਆ ਹੈ", "Books": "ਕਿਤਾਬਾਂ", "AuthenticationSucceededWithUserName": "{0} ਸਫਲਤਾਪੂਰਕ ਪ੍ਰਮਾਣਿਤ", "Artists": "ਕਲਾਕਾਰ", "Application": "ਐਪਲੀਕੇਸ਼ਨ", "AppDeviceValues": "ਐਪ: {0}, ਜੰਤਰ: {1}", - "Albums": "ਐਲਬਮਾਂ" + "Albums": "ਐਲਬਮਾਂ", + "TaskOptimizeDatabase": "ਡਾਟਾਬੇਸ ਅਨੁਕੂਲ ਬਣਾਓ", + "External": "ਬਾਹਰੀ" } -- cgit v1.2.3 From 648240dad0adf91dd53acd2e0edc3e23c5901a1c Mon Sep 17 00:00:00 2001 From: SuperDumbTM <eltonlochunkit@gmail.com> Date: Fri, 14 Apr 2023 12:31:03 +0000 Subject: Translated using Weblate (Chinese (Traditional, Hong Kong)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/zh_Hant_HK/ --- Emby.Server.Implementations/Localization/Core/zh-HK.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/zh-HK.json b/Emby.Server.Implementations/Localization/Core/zh-HK.json index b08119d0a6..c65568382a 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-HK.json +++ b/Emby.Server.Implementations/Localization/Core/zh-HK.json @@ -66,13 +66,13 @@ "PluginInstalledWithName": "已安裝 {0}", "PluginUninstalledWithName": "已移除 {0}", "PluginUpdatedWithName": "已更新 {0}", - "ProviderValue": "提供者: {0}", - "ScheduledTaskFailedWithName": "{0} 任務失敗", - "ScheduledTaskStartedWithName": "{0} 任務開始", + "ProviderValue": "提供者:{0}", + "ScheduledTaskFailedWithName": "{0} 執行失敗", + "ScheduledTaskStartedWithName": "{0} 開始執行", "ServerNameNeedsToBeRestarted": "{0} 需要重啟", "Shows": "節目", "Songs": "歌曲", - "StartupEmbyServerIsLoading": "Jellyfin 載入中,請稍後再試。", + "StartupEmbyServerIsLoading": "正在載入 Jellyfin,請稍後再試。", "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", "SubtitleDownloadFailureFromForItem": "無法從 {0} 下載 {1} 的字幕", "Sync": "同步", @@ -104,8 +104,8 @@ "TaskCleanTranscode": "清理轉碼目錄", "TaskUpdatePluginsDescription": "下載並更新能夠被自動更新的插件。", "TaskRefreshPeopleDescription": "更新媒體庫中演員和導演的元數據。", - "TaskCleanLogsDescription": "刪除超過{0}天的日誌文件。", - "TaskCleanLogs": "清理日誌目錄", + "TaskCleanLogsDescription": "刪除超過{0}天的紀錄檔。", + "TaskCleanLogs": "清理紀錄檔目錄", "TaskRefreshLibrary": "掃描媒體庫", "TaskRefreshChapterImagesDescription": "為帶有章節的影片建立縮圖。", "TaskRefreshChapterImages": "提取章節圖像", @@ -120,7 +120,7 @@ "Default": "預設", "TaskOptimizeDatabaseDescription": "壓縮數據庫並截斷可用空間。在掃描媒體庫或執行其他數據庫的修改後運行此任務可能會提高性能。", "TaskOptimizeDatabase": "最佳化數據庫", - "TaskCleanActivityLogDescription": "刪除早於設定時間的日誌記錄。", + "TaskCleanActivityLogDescription": "刪除早於設定時間的活動記錄。", "TaskKeyframeExtractorDescription": "提取關鍵幀以建立更準確的 HLS 播放列表。此工作或需要使用較長時間來完成。", "TaskKeyframeExtractor": "關鍵幀提取器", "External": "外部", -- cgit v1.2.3 From 5c370ff323028d093cb4a9c6c0d79d04a6c9ac0b Mon Sep 17 00:00:00 2001 From: 同福客栈跑堂蘸糖 <2316804089@qq.com> Date: Sat, 15 Apr 2023 11:15:21 -0400 Subject: Added translation using Weblate (Chinese (Literary)) --- Emby.Server.Implementations/Localization/Core/lzh.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 Emby.Server.Implementations/Localization/Core/lzh.json (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/lzh.json b/Emby.Server.Implementations/Localization/Core/lzh.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/Emby.Server.Implementations/Localization/Core/lzh.json @@ -0,0 +1 @@ +{} -- cgit v1.2.3 From 922e04dbf347611ac813a6ca823c0c4fde622c54 Mon Sep 17 00:00:00 2001 From: 同福客栈跑堂蘸糖 <2316804089@qq.com> Date: Sat, 15 Apr 2023 15:18:57 +0000 Subject: Translated using Weblate (Chinese (Literary)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/lzh/ --- Emby.Server.Implementations/Localization/Core/lzh.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/lzh.json b/Emby.Server.Implementations/Localization/Core/lzh.json index 0967ef424b..031a4dac76 100644 --- a/Emby.Server.Implementations/Localization/Core/lzh.json +++ b/Emby.Server.Implementations/Localization/Core/lzh.json @@ -1 +1,6 @@ -{} +{ + "Albums": "辑册", + "Artists": "艺人", + "AuthenticationSucceededWithUserName": "{0} 授之权矣", + "Books": "册" +} -- cgit v1.2.3 From 92f50054b28c85afbee0dfa99016c4b71548de6f Mon Sep 17 00:00:00 2001 From: AmbulantRex <21176662+AmbulantRex@users.noreply.github.com> Date: Sun, 16 Apr 2023 07:46:12 -0600 Subject: Add explicit mapping instead of reflection to manifest reconciliation. --- .../Plugins/PluginManager.cs | 90 +++++++++++----------- MediaBrowser.Model/Updates/VersionInfo.cs | 2 - .../Plugins/PluginManagerTests.cs | 69 +++++++++++++---- 3 files changed, 97 insertions(+), 64 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Plugins/PluginManager.cs b/Emby.Server.Implementations/Plugins/PluginManager.cs index 2d2ad26d29..10d5ea906e 100644 --- a/Emby.Server.Implementations/Plugins/PluginManager.cs +++ b/Emby.Server.Implementations/Plugins/PluginManager.cs @@ -432,69 +432,67 @@ namespace Emby.Server.Implementations.Plugins ImagePath = imagePath }; - var metafile = Path.Combine(Path.Combine(path, MetafileName)); - if (File.Exists(metafile)) - { - var data = File.ReadAllBytes(metafile); - var localManifest = JsonSerializer.Deserialize<PluginManifest>(data, _jsonOptions) ?? new PluginManifest(); - - // Plugin installation is the typical cause for populating a manifest. Activate. - localManifest.Status = status == PluginStatus.Disabled ? PluginStatus.Disabled : PluginStatus.Active; - - if (!Equals(localManifest.Id, manifest.Id)) - { - _logger.LogError("The manifest ID {LocalUUID} did not match the package info ID {PackageUUID}.", localManifest.Id, manifest.Id); - localManifest.Status = PluginStatus.Malfunctioned; - } - - if (localManifest.Version != manifest.Version) - { - _logger.LogWarning("The version of the local manifest was {LocalVersion}, but {PackageVersion} was expected. The value will be replaced.", localManifest.Version, manifest.Version); - - // Correct the local version. - localManifest.Version = manifest.Version; - } - - // Reconcile missing data against repository manifest. - ReconcileManifest(localManifest, manifest); - - manifest = localManifest; - } - else + if (!ReconcileManifest(manifest, path)) { - _logger.LogInformation("No local manifest exists for plugin {Plugin}. Populating from repository manifest.", manifest.Name); + // An error occurred during reconciliation and saving could be undesirable. + return false; } return SaveManifest(manifest, path); } /// <summary> - /// Resolve the target plugin manifest against the source. Values are mapped onto the - /// target only if they are default values or empty strings. ID and status fields are ignored. + /// Reconciles the manifest against any properties that exist locally in a pre-packaged meta.json found at the path. + /// If no file is found, no reconciliation occurs. /// </summary> - /// <param name="baseManifest">The base <see cref="PluginManifest"/> to be reconciled.</param> - /// <param name="projector">The <see cref="PluginManifest"/> to reconcile against.</param> - private void ReconcileManifest(PluginManifest baseManifest, PluginManifest projector) + /// <param name="manifest">The <see cref="PluginManifest"/> to reconcile against.</param> + /// <param name="path">The plugin path.</param> + /// <returns>The reconciled <see cref="PluginManifest"/>.</returns> + private bool ReconcileManifest(PluginManifest manifest, string path) { - var ignoredFields = new string[] + try { - nameof(baseManifest.Id), - nameof(baseManifest.Status) - }; + var metafile = Path.Combine(path, MetafileName); + if (!File.Exists(metafile)) + { + _logger.LogInformation("No local manifest exists for plugin {Plugin}. Skipping manifest reconciliation.", manifest.Name); + return true; + } - foreach (var property in baseManifest.GetType().GetProperties()) - { - var localValue = property.GetValue(baseManifest); + var data = File.ReadAllBytes(metafile); + var localManifest = JsonSerializer.Deserialize<PluginManifest>(data, _jsonOptions) ?? new PluginManifest(); - if (property.PropertyType == typeof(bool) || ignoredFields.Any(s => Equals(s, property.Name))) + if (!Equals(localManifest.Id, manifest.Id)) { - continue; + _logger.LogError("The manifest ID {LocalUUID} did not match the package info ID {PackageUUID}.", localManifest.Id, manifest.Id); + manifest.Status = PluginStatus.Malfunctioned; } - if (property.PropertyType.IsNullOrDefault(localValue) || (property.PropertyType == typeof(string) && (string)localValue! == string.Empty)) + if (localManifest.Version != manifest.Version) { - property.SetValue(baseManifest, property.GetValue(projector)); + // Package information provides the version and is the source of truth. Pre-packages meta.json is assumed to be a mistake in this regard. + _logger.LogWarning("The version of the local manifest was {LocalVersion}, but {PackageVersion} was expected. The value will be replaced.", localManifest.Version, manifest.Version); } + + // Explicitly mapping properties instead of using reflection is preferred here. + manifest.Category = string.IsNullOrEmpty(localManifest.Category) ? manifest.Category : localManifest.Category; + manifest.AutoUpdate = localManifest.AutoUpdate; // Preserve whatever is local. Package info does not have this property. + manifest.Changelog = string.IsNullOrEmpty(localManifest.Changelog) ? manifest.Changelog : localManifest.Changelog; + manifest.Description = string.IsNullOrEmpty(localManifest.Description) ? manifest.Description : localManifest.Description; + manifest.Name = string.IsNullOrEmpty(localManifest.Name) ? manifest.Name : localManifest.Name; + manifest.Overview = string.IsNullOrEmpty(localManifest.Overview) ? manifest.Overview : localManifest.Overview; + manifest.Owner = string.IsNullOrEmpty(localManifest.Owner) ? manifest.Owner : localManifest.Owner; + manifest.TargetAbi = string.IsNullOrEmpty(localManifest.TargetAbi) ? manifest.TargetAbi : localManifest.TargetAbi; + manifest.Timestamp = localManifest.Timestamp.IsNullOrDefault() ? manifest.Timestamp : localManifest.Timestamp; + manifest.ImagePath = string.IsNullOrEmpty(localManifest.ImagePath) ? manifest.ImagePath : localManifest.ImagePath; + manifest.Assemblies = localManifest.Assemblies; + + return true; + } + catch (Exception e) + { + _logger.LogWarning(e, "Unable to reconcile plugin manifest due to an error. {Path}", path); + return false; } } diff --git a/MediaBrowser.Model/Updates/VersionInfo.cs b/MediaBrowser.Model/Updates/VersionInfo.cs index 53e1d29b01..320199f984 100644 --- a/MediaBrowser.Model/Updates/VersionInfo.cs +++ b/MediaBrowser.Model/Updates/VersionInfo.cs @@ -1,5 +1,3 @@ -using System; -using System.Collections.Generic; using System.Text.Json.Serialization; using SysVersion = System.Version; diff --git a/tests/Jellyfin.Server.Implementations.Tests/Plugins/PluginManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Plugins/PluginManagerTests.cs index 204d144214..d4b90dac02 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Plugins/PluginManagerTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Plugins/PluginManagerTests.cs @@ -172,6 +172,24 @@ namespace Jellyfin.Server.Implementations.Tests.Plugins Assemblies = new[] { "Jellyfin.Test.dll" } }; + var expectedManifest = new PluginManifest + { + Id = partial.Id, + Name = packageInfo.Name, + AutoUpdate = partial.AutoUpdate, + Status = PluginStatus.Active, + Owner = packageInfo.Owner, + Assemblies = partial.Assemblies, + Category = packageInfo.Category, + Description = packageInfo.Description, + Overview = packageInfo.Overview, + TargetAbi = packageInfo.Versions[0].TargetAbi!, + Timestamp = DateTime.Parse(packageInfo.Versions[0].Timestamp!, CultureInfo.InvariantCulture), + Changelog = packageInfo.Versions[0].Changelog!, + Version = new Version(1, 0).ToString(), + ImagePath = string.Empty + }; + var metafilePath = Path.Combine(_pluginPath, "meta.json"); File.WriteAllText(metafilePath, JsonSerializer.Serialize(partial, _options)); @@ -183,22 +201,41 @@ namespace Jellyfin.Server.Implementations.Tests.Plugins var result = JsonSerializer.Deserialize<PluginManifest>(resultBytes, _options); Assert.NotNull(result); - Assert.Equal(packageInfo.Name, result.Name); - Assert.Equal(packageInfo.Owner, result.Owner); - Assert.Equal(PluginStatus.Active, result.Status); - Assert.NotEmpty(result.Assemblies); - Assert.False(result.AutoUpdate); - - // Preserved - Assert.Equal(packageInfo.Category, result.Category); - Assert.Equal(packageInfo.Description, result.Description); - Assert.Equal(packageInfo.Id, result.Id); - Assert.Equal(packageInfo.Overview, result.Overview); - Assert.Equal(partial.Assemblies[0], result.Assemblies[0]); - Assert.Equal(packageInfo.Versions[0].TargetAbi, result.TargetAbi); - Assert.Equal(DateTime.Parse(packageInfo.Versions[0].Timestamp!, CultureInfo.InvariantCulture), result.Timestamp); - Assert.Equal(packageInfo.Versions[0].Changelog, result.Changelog); - Assert.Equal(packageInfo.Versions[0].Version, result.Version); + Assert.Equivalent(expectedManifest, result); + } + + [Fact] + public async Task PopulateManifest_NoMetafile_PreservesManifest() + { + var packageInfo = GenerateTestPackage(); + var expectedManifest = new PluginManifest + { + Id = packageInfo.Id, + Name = packageInfo.Name, + AutoUpdate = true, + Status = PluginStatus.Active, + Owner = packageInfo.Owner, + Assemblies = Array.Empty<string>(), + Category = packageInfo.Category, + Description = packageInfo.Description, + Overview = packageInfo.Overview, + TargetAbi = packageInfo.Versions[0].TargetAbi!, + Timestamp = DateTime.Parse(packageInfo.Versions[0].Timestamp!, CultureInfo.InvariantCulture), + Changelog = packageInfo.Versions[0].Changelog!, + Version = packageInfo.Versions[0].Version, + ImagePath = string.Empty + }; + + var pluginManager = new PluginManager(new NullLogger<PluginManager>(), null!, null!, null!, new Version(1, 0)); + + await pluginManager.PopulateManifest(packageInfo, new Version(1, 0), _pluginPath, PluginStatus.Active); + + var metafilePath = Path.Combine(_pluginPath, "meta.json"); + var resultBytes = File.ReadAllBytes(metafilePath); + var result = JsonSerializer.Deserialize<PluginManifest>(resultBytes, _options); + + Assert.NotNull(result); + Assert.Equivalent(expectedManifest, result); } [Fact] -- cgit v1.2.3 From ba5e51700e753f20d568418b8f0f7b48f4ca271c Mon Sep 17 00:00:00 2001 From: Brad Beattie <bradbeattie@gmail.com> Date: Sun, 16 Apr 2023 10:09:54 -0700 Subject: Fix null parental rating comparison (#9618) --- Emby.Server.Implementations/Data/SqliteItemRepository.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 71788a3e43..e59f2a198e 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -2379,7 +2379,7 @@ namespace Emby.Server.Implementations.Data else { builder.Append( - @"(SELECT CASE WHEN InheritedParentalRatingValue=0 + @"(SELECT CASE WHEN COALESCE(InheritedParentalRatingValue, 0)=0 THEN 0 ELSE 10.0 / (1.0 + ABS(InheritedParentalRatingValue - @InheritedParentalRatingValue)) END)"); -- cgit v1.2.3 From c7174255498e28272fbe6d4d6867a774a3327eff Mon Sep 17 00:00:00 2001 From: AmbulantRex <21176662+AmbulantRex@users.noreply.github.com> Date: Sun, 16 Apr 2023 18:47:57 -0600 Subject: Remove unnecessary type extension and handle feedback. --- .../Plugins/PluginManager.cs | 13 +++-- src/Jellyfin.Extensions/TypeExtensions.cs | 45 -------------- .../TypeExtensionsTests.cs | 68 ---------------------- 3 files changed, 7 insertions(+), 119 deletions(-) delete mode 100644 src/Jellyfin.Extensions/TypeExtensions.cs delete mode 100644 tests/Jellyfin.Extensions.Tests/TypeExtensionsTests.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Plugins/PluginManager.cs b/Emby.Server.Implementations/Plugins/PluginManager.cs index 10d5ea906e..48584ae0cb 100644 --- a/Emby.Server.Implementations/Plugins/PluginManager.cs +++ b/Emby.Server.Implementations/Plugins/PluginManager.cs @@ -432,7 +432,7 @@ namespace Emby.Server.Implementations.Plugins ImagePath = imagePath }; - if (!ReconcileManifest(manifest, path)) + if (!await ReconcileManifest(manifest, path)) { // An error occurred during reconciliation and saving could be undesirable. return false; @@ -448,7 +448,7 @@ namespace Emby.Server.Implementations.Plugins /// <param name="manifest">The <see cref="PluginManifest"/> to reconcile against.</param> /// <param name="path">The plugin path.</param> /// <returns>The reconciled <see cref="PluginManifest"/>.</returns> - private bool ReconcileManifest(PluginManifest manifest, string path) + private async Task<bool> ReconcileManifest(PluginManifest manifest, string path) { try { @@ -459,8 +459,9 @@ namespace Emby.Server.Implementations.Plugins return true; } - var data = File.ReadAllBytes(metafile); - var localManifest = JsonSerializer.Deserialize<PluginManifest>(data, _jsonOptions) ?? new PluginManifest(); + using var metaStream = File.OpenRead(metafile); + var localManifest = await JsonSerializer.DeserializeAsync<PluginManifest>(metaStream, _jsonOptions); + localManifest ??= new PluginManifest(); if (!Equals(localManifest.Id, manifest.Id)) { @@ -483,7 +484,7 @@ namespace Emby.Server.Implementations.Plugins manifest.Overview = string.IsNullOrEmpty(localManifest.Overview) ? manifest.Overview : localManifest.Overview; manifest.Owner = string.IsNullOrEmpty(localManifest.Owner) ? manifest.Owner : localManifest.Owner; manifest.TargetAbi = string.IsNullOrEmpty(localManifest.TargetAbi) ? manifest.TargetAbi : localManifest.TargetAbi; - manifest.Timestamp = localManifest.Timestamp.IsNullOrDefault() ? manifest.Timestamp : localManifest.Timestamp; + manifest.Timestamp = localManifest.Timestamp.Equals(default) ? manifest.Timestamp : localManifest.Timestamp; manifest.ImagePath = string.IsNullOrEmpty(localManifest.ImagePath) ? manifest.ImagePath : localManifest.ImagePath; manifest.Assemblies = localManifest.Assemblies; @@ -842,7 +843,7 @@ namespace Emby.Server.Implementations.Plugins var canonicalized = Path.Combine(plugin.Path, path).Canonicalize(); // Ensure we stay in the plugin directory. - if (!canonicalized.StartsWith(plugin.Path.NormalizePath()!, StringComparison.Ordinal)) + if (!canonicalized.StartsWith(plugin.Path.NormalizePath(), StringComparison.Ordinal)) { _logger.LogError("Assembly path {Path} is not inside the plugin directory.", path); return false; diff --git a/src/Jellyfin.Extensions/TypeExtensions.cs b/src/Jellyfin.Extensions/TypeExtensions.cs deleted file mode 100644 index 5b1111d594..0000000000 --- a/src/Jellyfin.Extensions/TypeExtensions.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using System.Globalization; - -namespace Jellyfin.Extensions; - -/// <summary> -/// Provides extensions methods for <see cref="Type" />. -/// </summary> -public static class TypeExtensions -{ - /// <summary> - /// Checks if the supplied value is the default or null value for that type. - /// </summary> - /// <typeparam name="T">The type of the value to compare.</typeparam> - /// <param name="type">The type.</param> - /// <param name="value">The value to check.</param> - /// <returns><see langword="true"/> if the value is the default for the type. Otherwise, <see langword="false"/>.</returns> - public static bool IsNullOrDefault<T>(this Type type, T value) - { - if (value is null) - { - return true; - } - - object? tmp = value; - object? defaultValue = type.IsValueType ? Activator.CreateInstance(type) : null; - if (type.IsAssignableTo(typeof(IConvertible))) - { - tmp = Convert.ChangeType(value, type, CultureInfo.InvariantCulture); - } - - return Equals(tmp, defaultValue); - } - - /// <summary> - /// Checks if the object is currently a default or null value. Boxed types will be unboxed prior to comparison. - /// </summary> - /// <param name="obj">The object to check.</param> - /// <returns><see langword="true"/> if the value is the default for the type. Otherwise, <see langword="false"/>.</returns> - public static bool IsNullOrDefault(this object? obj) - { - // Unbox the type and check. - return obj?.GetType().IsNullOrDefault(obj) ?? true; - } -} diff --git a/tests/Jellyfin.Extensions.Tests/TypeExtensionsTests.cs b/tests/Jellyfin.Extensions.Tests/TypeExtensionsTests.cs deleted file mode 100644 index 747913fa1a..0000000000 --- a/tests/Jellyfin.Extensions.Tests/TypeExtensionsTests.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System; -using Xunit; - -namespace Jellyfin.Extensions.Tests -{ - public class TypeExtensionsTests - { - [Theory] - [InlineData(typeof(byte), byte.MaxValue, false)] - [InlineData(typeof(short), short.MinValue, false)] - [InlineData(typeof(ushort), ushort.MaxValue, false)] - [InlineData(typeof(int), int.MinValue, false)] - [InlineData(typeof(uint), uint.MaxValue, false)] - [InlineData(typeof(long), long.MinValue, false)] - [InlineData(typeof(ulong), ulong.MaxValue, false)] - [InlineData(typeof(decimal), -1.0, false)] - [InlineData(typeof(bool), true, false)] - [InlineData(typeof(char), 'a', false)] - [InlineData(typeof(string), "", false)] - [InlineData(typeof(object), 1, false)] - [InlineData(typeof(byte), 0, true)] - [InlineData(typeof(short), 0, true)] - [InlineData(typeof(ushort), 0, true)] - [InlineData(typeof(int), 0, true)] - [InlineData(typeof(uint), 0, true)] - [InlineData(typeof(long), 0, true)] - [InlineData(typeof(ulong), 0, true)] - [InlineData(typeof(decimal), 0, true)] - [InlineData(typeof(bool), false, true)] - [InlineData(typeof(char), '\x0000', true)] - [InlineData(typeof(string), null, true)] - [InlineData(typeof(object), null, true)] - [InlineData(typeof(PhonyClass), null, true)] - [InlineData(typeof(DateTime), null, true)] // Special case handled within the test. - [InlineData(typeof(DateTime), null, false)] // Special case handled within the test. - [InlineData(typeof(byte?), null, true)] - [InlineData(typeof(short?), null, true)] - [InlineData(typeof(ushort?), null, true)] - [InlineData(typeof(int?), null, true)] - [InlineData(typeof(uint?), null, true)] - [InlineData(typeof(long?), null, true)] - [InlineData(typeof(ulong?), null, true)] - [InlineData(typeof(decimal?), null, true)] - [InlineData(typeof(bool?), null, true)] - [InlineData(typeof(char?), null, true)] - public void IsNullOrDefault_Matches_Expected(Type type, object? value, bool expectedResult) - { - if (type == typeof(DateTime)) - { - if (expectedResult) - { - value = default(DateTime); - } - else - { - value = DateTime.Now; - } - } - - Assert.Equal(expectedResult, type.IsNullOrDefault(value)); - Assert.Equal(expectedResult, value.IsNullOrDefault()); - } - - private class PhonyClass - { - } - } -} -- cgit v1.2.3 From 9c88752f227e4f3905505c070f8e14818ad4654f Mon Sep 17 00:00:00 2001 From: Juan B <benitesjn@gmail.com> Date: Mon, 17 Apr 2023 01:53:11 +0000 Subject: Translated using Weblate (Spanish) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/es/ --- Emby.Server.Implementations/Localization/Core/es.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/es.json b/Emby.Server.Implementations/Localization/Core/es.json index 5e41462db1..f5636a0af0 100644 --- a/Emby.Server.Implementations/Localization/Core/es.json +++ b/Emby.Server.Implementations/Localization/Core/es.json @@ -31,7 +31,7 @@ "ItemRemovedWithName": "{0} ha sido eliminado de la biblioteca", "LabelIpAddressValue": "Dirección IP: {0}", "LabelRunningTimeValue": "Tiempo de funcionamiento: {0}", - "Latest": "Último contenido en", + "Latest": "Últimas", "MessageApplicationUpdated": "Se ha actualizado el servidor Jellyfin", "MessageApplicationUpdatedTo": "Se ha actualizado el servidor Jellyfin a la versión {0}", "MessageNamedServerConfigurationUpdatedWithValue": "La sección {0} de configuración del servidor ha sido actualizada", -- cgit v1.2.3 From 4b2b46c8f3d98227b01d57cd3f4805e65ddac727 Mon Sep 17 00:00:00 2001 From: SuperDumbTM <eltonlochunkit@gmail.com> Date: Sun, 16 Apr 2023 11:18:00 +0000 Subject: Translated using Weblate (Chinese (Traditional, Hong Kong)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/zh_Hant_HK/ --- Emby.Server.Implementations/Localization/Core/zh-HK.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/zh-HK.json b/Emby.Server.Implementations/Localization/Core/zh-HK.json index c65568382a..610f503ddb 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-HK.json +++ b/Emby.Server.Implementations/Localization/Core/zh-HK.json @@ -4,11 +4,11 @@ "Application": "應用程式", "Artists": "藝人", "AuthenticationSucceededWithUserName": "{0} 授權成功", - "Books": "圖書", + "Books": "書籍", "CameraImageUploadedFrom": "{0} 成功上傳一張新照片", "Channels": "頻道", "ChapterNameValue": "第 {0} 章", - "Collections": "合輯", + "Collections": "系列", "DeviceOfflineWithName": "{0} 已斷開連接", "DeviceOnlineWithName": "{0} 已連接", "FailedLoginAttemptWithUserName": "{0} 登入失敗", -- cgit v1.2.3 From 858dadcdd1caadb5fa8cc13a02eb227098f39c3c Mon Sep 17 00:00:00 2001 From: Bond_009 <bond.009@outlook.com> Date: Fri, 14 Apr 2023 13:43:56 +0200 Subject: POC sql connection pool --- Emby.Server.Implementations/ApplicationHost.cs | 6 +- .../Data/BaseSqliteRepository.cs | 115 +++++++++++++++------ Emby.Server.Implementations/Data/ConnectionPool.cs | 68 ++++++++++++ .../Data/ManagedConnection.cs | 13 ++- .../Data/SqliteItemRepository.cs | 9 +- .../Data/SqliteUserDataRepository.cs | 38 ++----- 6 files changed, 174 insertions(+), 75 deletions(-) create mode 100644 Emby.Server.Implementations/Data/ConnectionPool.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 080c448299..7969577bc0 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -627,6 +627,9 @@ namespace Emby.Server.Implementations } } + ((SqliteItemRepository)Resolve<IItemRepository>()).Initialize(); + ((SqliteUserDataRepository)Resolve<IUserDataRepository>()).Initialize(); + var localizationManager = (LocalizationManager)Resolve<ILocalizationManager>(); await localizationManager.LoadAll().ConfigureAwait(false); @@ -634,9 +637,6 @@ namespace Emby.Server.Implementations SetStaticProperties(); - var userDataRepo = (SqliteUserDataRepository)Resolve<IUserDataRepository>(); - ((SqliteItemRepository)Resolve<IItemRepository>()).Initialize(userDataRepo, Resolve<IUserManager>()); - FindParts(); } diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs index bc520b86e7..859a3c7464 100644 --- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs +++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; -using System.Threading; using Jellyfin.Extensions; using Microsoft.Extensions.Logging; using SQLitePCL.pretty; @@ -27,9 +26,19 @@ namespace Emby.Server.Implementations.Data /// <summary> /// Gets or sets the path to the DB file. /// </summary> - /// <value>Path to the DB file.</value> protected string DbFilePath { get; set; } + /// <summary> + /// Gets or sets the number of write connections to create. + /// </summary> + /// <value>Path to the DB file.</value> + protected int WriteConnectionsCount { get; set; } = 1; + + /// <summary> + /// Gets or sets the number of read connections to create. + /// </summary> + protected int ReadConnectionsCount { get; set; } = 1; + /// <summary> /// Gets the logger. /// </summary> @@ -63,7 +72,7 @@ namespace Emby.Server.Implementations.Data /// <summary> /// Gets the locking mode. <see href="https://www.sqlite.org/pragma.html#pragma_locking_mode" />. /// </summary> - protected virtual string LockingMode => "EXCLUSIVE"; + protected virtual string LockingMode => "NORMAL"; /// <summary> /// Gets the journal mode. <see href="https://www.sqlite.org/pragma.html#pragma_journal_mode" />. @@ -88,7 +97,7 @@ namespace Emby.Server.Implementations.Data /// </summary> /// <value>The temp store mode.</value> /// <see cref="TempStoreMode"/> - protected virtual TempStoreMode TempStore => TempStoreMode.Default; + protected virtual TempStoreMode TempStore => TempStoreMode.Memory; /// <summary> /// Gets the synchronous mode. @@ -101,63 +110,115 @@ namespace Emby.Server.Implementations.Data /// Gets or sets the write lock. /// </summary> /// <value>The write lock.</value> - protected SemaphoreSlim WriteLock { get; set; } = new SemaphoreSlim(1, 1); + protected ConnectionPool WriteConnections { get; set; } /// <summary> /// Gets or sets the write connection. /// </summary> /// <value>The write connection.</value> - protected SQLiteDatabaseConnection WriteConnection { get; set; } + protected ConnectionPool ReadConnections { get; set; } + + public virtual void Initialize() + { + WriteConnections = new ConnectionPool(WriteConnectionsCount, CreateWriteConnection); + ReadConnections = new ConnectionPool(ReadConnectionsCount, CreateReadConnection); + } protected ManagedConnection GetConnection(bool readOnly = false) { - WriteLock.Wait(); - if (WriteConnection is not null) + if (readOnly) { - return new ManagedConnection(WriteConnection, WriteLock); + return ReadConnections.GetConnection(); } - WriteConnection = SQLite3.Open( + return WriteConnections.GetConnection(); + } + + protected SQLiteDatabaseConnection CreateWriteConnection() + { + var writeConnection = SQLite3.Open( DbFilePath, DefaultConnectionFlags | ConnectionFlags.Create | ConnectionFlags.ReadWrite, null); if (CacheSize.HasValue) { - WriteConnection.Execute("PRAGMA cache_size=" + CacheSize.Value); + writeConnection.Execute("PRAGMA cache_size=" + CacheSize.Value); } if (!string.IsNullOrWhiteSpace(LockingMode)) { - WriteConnection.Execute("PRAGMA locking_mode=" + LockingMode); + writeConnection.Execute("PRAGMA locking_mode=" + LockingMode); } if (!string.IsNullOrWhiteSpace(JournalMode)) { - WriteConnection.Execute("PRAGMA journal_mode=" + JournalMode); + writeConnection.Execute("PRAGMA journal_mode=" + JournalMode); } if (JournalSizeLimit.HasValue) { - WriteConnection.Execute("PRAGMA journal_size_limit=" + JournalSizeLimit.Value); + writeConnection.Execute("PRAGMA journal_size_limit=" + JournalSizeLimit.Value); } if (Synchronous.HasValue) { - WriteConnection.Execute("PRAGMA synchronous=" + (int)Synchronous.Value); + writeConnection.Execute("PRAGMA synchronous=" + (int)Synchronous.Value); } if (PageSize.HasValue) { - WriteConnection.Execute("PRAGMA page_size=" + PageSize.Value); + writeConnection.Execute("PRAGMA page_size=" + PageSize.Value); } - WriteConnection.Execute("PRAGMA temp_store=" + (int)TempStore); + writeConnection.Execute("PRAGMA temp_store=" + (int)TempStore); // Configuration and pragmas can affect VACUUM so it needs to be last. - WriteConnection.Execute("VACUUM"); + writeConnection.Execute("VACUUM"); - return new ManagedConnection(WriteConnection, WriteLock); + return writeConnection; + } + + protected SQLiteDatabaseConnection CreateReadConnection() + { + var writeConnection = SQLite3.Open( + DbFilePath, + DefaultConnectionFlags | ConnectionFlags.ReadOnly, + null); + + if (CacheSize.HasValue) + { + 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=" + JournalSizeLimit.Value); + } + + if (Synchronous.HasValue) + { + writeConnection.Execute("PRAGMA synchronous=" + (int)Synchronous.Value); + } + + if (PageSize.HasValue) + { + writeConnection.Execute("PRAGMA page_size=" + PageSize.Value); + } + + writeConnection.Execute("PRAGMA temp_store=" + (int)TempStore); + + return writeConnection; } public IStatement PrepareStatement(ManagedConnection connection, string sql) @@ -240,22 +301,10 @@ namespace Emby.Server.Implementations.Data if (dispose) { - WriteLock.Wait(); - try - { - WriteConnection?.Dispose(); - } - finally - { - WriteLock.Release(); - } - - WriteLock.Dispose(); + WriteConnections.Dispose(); + ReadConnections.Dispose(); } - WriteConnection = null; - WriteLock = null; - _disposed = true; } } diff --git a/Emby.Server.Implementations/Data/ConnectionPool.cs b/Emby.Server.Implementations/Data/ConnectionPool.cs new file mode 100644 index 0000000000..86a125ba51 --- /dev/null +++ b/Emby.Server.Implementations/Data/ConnectionPool.cs @@ -0,0 +1,68 @@ +#pragma warning disable CS1591 + +using System; +using System.Collections.Concurrent; +using System.Threading; +using SQLitePCL.pretty; + +namespace Emby.Server.Implementations.Data; + +public sealed class ConnectionPool : IDisposable +{ + private readonly int _count; + private readonly SemaphoreSlim _lock; + private readonly ConcurrentQueue<SQLiteDatabaseConnection> _connections = new ConcurrentQueue<SQLiteDatabaseConnection>(); + private bool _disposed; + + public ConnectionPool(int count, Func<SQLiteDatabaseConnection> factory) + { + _count = count; + _lock = new SemaphoreSlim(count, count); + for (int i = 0; i < count; i++) + { + _connections.Enqueue(factory.Invoke()); + } + } + + public ManagedConnection GetConnection() + { + _lock.Wait(); + if (!_connections.TryDequeue(out var connection)) + { + _lock.Release(); + throw new InvalidOperationException(); + } + + return new ManagedConnection(connection, this); + } + + public void Return(SQLiteDatabaseConnection connection) + { + _connections.Enqueue(connection); + _lock.Release(); + } + + public void Dispose() + { + if (_disposed) + { + return; + } + + for (int i = 0; i < _count; i++) + { + _lock.Wait(); + if (!_connections.TryDequeue(out var connection)) + { + _lock.Release(); + throw new InvalidOperationException(); + } + + connection.Dispose(); + } + + _lock.Dispose(); + + _disposed = true; + } +} diff --git a/Emby.Server.Implementations/Data/ManagedConnection.cs b/Emby.Server.Implementations/Data/ManagedConnection.cs index 11e33278d4..e84ed8f918 100644 --- a/Emby.Server.Implementations/Data/ManagedConnection.cs +++ b/Emby.Server.Implementations/Data/ManagedConnection.cs @@ -2,23 +2,22 @@ using System; using System.Collections.Generic; -using System.Threading; using SQLitePCL.pretty; namespace Emby.Server.Implementations.Data { public sealed class ManagedConnection : IDisposable { - private readonly SemaphoreSlim _writeLock; + private readonly ConnectionPool _pool; - private SQLiteDatabaseConnection? _db; + private SQLiteDatabaseConnection _db; private bool _disposed = false; - public ManagedConnection(SQLiteDatabaseConnection db, SemaphoreSlim writeLock) + public ManagedConnection(SQLiteDatabaseConnection db, ConnectionPool pool) { _db = db; - _writeLock = writeLock; + _pool = pool; } public IStatement PrepareStatement(string sql) @@ -73,9 +72,9 @@ namespace Emby.Server.Implementations.Data return; } - _writeLock.Release(); + _pool.Return(_db); - _db = null; // Don't dispose it + _db = null!; // Don't dispose it _disposed = true; } } diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index e59f2a198e..33466c34b0 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -336,6 +336,7 @@ namespace Emby.Server.Implementations.Data _jsonOptions = JsonDefaults.Options; DbFilePath = Path.Combine(_config.ApplicationPaths.DataPath, "library.db"); + ReadConnectionsCount = 5; } /// <inheritdoc /> @@ -347,10 +348,10 @@ namespace Emby.Server.Implementations.Data /// <summary> /// Opens the connection to the database. /// </summary> - /// <param name="userDataRepo">The user data repository.</param> - /// <param name="userManager">The user manager.</param> - public void Initialize(SqliteUserDataRepository userDataRepo, IUserManager userManager) + public override void Initialize() { + base.Initialize(); + const string CreateMediaStreamsTableCommand = "create table if not exists mediastreams (ItemId GUID, StreamIndex INT, StreamType TEXT, Codec TEXT, Language TEXT, ChannelLayout TEXT, Profile TEXT, AspectRatio TEXT, Path TEXT, IsInterlaced BIT, BitRate INT NULL, Channels INT NULL, SampleRate INT NULL, IsDefault BIT, IsForced BIT, IsExternal BIT, Height INT NULL, Width INT NULL, AverageFrameRate FLOAT NULL, RealFrameRate FLOAT NULL, Level FLOAT NULL, PixelFormat TEXT, BitDepth INT NULL, IsAnamorphic BIT NULL, RefFrames INT NULL, CodecTag TEXT NULL, Comment TEXT NULL, NalLengthSize TEXT NULL, IsAvc BIT NULL, Title TEXT NULL, TimeBase TEXT NULL, CodecTimeBase TEXT NULL, ColorPrimaries TEXT NULL, ColorSpace TEXT NULL, ColorTransfer TEXT NULL, DvVersionMajor INT NULL, DvVersionMinor INT NULL, DvProfile INT NULL, DvLevel INT NULL, RpuPresentFlag INT NULL, ElPresentFlag INT NULL, BlPresentFlag INT NULL, DvBlSignalCompatibilityId INT NULL, IsHearingImpaired BIT NULL, PRIMARY KEY (ItemId, StreamIndex))"; @@ -551,8 +552,6 @@ namespace Emby.Server.Implementations.Data connection.RunQueries(postQueries); } - - userDataRepo.Initialize(userManager, WriteLock, WriteConnection); } public void SaveImages(BaseItem item) diff --git a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs index 5f2c3c9dcc..a1e217ad14 100644 --- a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs @@ -7,7 +7,7 @@ using System.Collections.Generic; using System.IO; using System.Threading; using Jellyfin.Data.Entities; -using MediaBrowser.Common.Configuration; +using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Persistence; @@ -18,33 +18,32 @@ namespace Emby.Server.Implementations.Data { public class SqliteUserDataRepository : BaseSqliteRepository, IUserDataRepository { + private readonly IUserManager _userManager; + public SqliteUserDataRepository( ILogger<SqliteUserDataRepository> logger, - IApplicationPaths appPaths) + IServerConfigurationManager config, + IUserManager userManager) : base(logger) { - DbFilePath = Path.Combine(appPaths.DataPath, "library.db"); + _userManager = userManager; + + DbFilePath = Path.Combine(config.ApplicationPaths.DataPath, "library.db"); } /// <summary> /// Opens the connection to the database. /// </summary> - /// <param name="userManager">The user manager.</param> - /// <param name="dbLock">The lock to use for database IO.</param> - /// <param name="dbConnection">The connection to use for database IO.</param> - public void Initialize(IUserManager userManager, SemaphoreSlim dbLock, SQLiteDatabaseConnection dbConnection) + public override void Initialize() { - WriteLock.Dispose(); - WriteLock = dbLock; - WriteConnection?.Dispose(); - WriteConnection = dbConnection; + base.Initialize(); using (var connection = GetConnection()) { var userDatasTableExists = TableExists(connection, "UserDatas"); var userDataTableExists = TableExists(connection, "userdata"); - var users = userDatasTableExists ? null : userManager.Users; + var users = userDatasTableExists ? null : _userManager.Users; connection.RunInTransaction( db => @@ -371,20 +370,5 @@ namespace Emby.Server.Implementations.Data return userData; } - -#pragma warning disable CA2215 - /// <inheritdoc/> - /// <remarks> - /// There is nothing to dispose here since <see cref="BaseSqliteRepository.WriteLock"/> and - /// <see cref="BaseSqliteRepository.WriteConnection"/> are managed by <see cref="SqliteItemRepository"/>. - /// See <see cref="Initialize(IUserManager, SemaphoreSlim, SQLiteDatabaseConnection)"/>. - /// </remarks> - protected override void Dispose(bool dispose) - { - // The write lock and connection for the item repository are shared with the user data repository - // since they point to the same database. The item repo has responsibility for disposing these two objects, - // so the user data repo should not attempt to dispose them as well - } -#pragma warning restore CA2215 } } -- cgit v1.2.3 From 33f97045f957179c907a22af96dac0261b3651d8 Mon Sep 17 00:00:00 2001 From: Bond_009 <bond.009@outlook.com> Date: Fri, 14 Apr 2023 21:38:12 +0200 Subject: Use BlockingCollection --- .../Data/BaseSqliteRepository.cs | 37 ++++++++----------- Emby.Server.Implementations/Data/ConnectionPool.cs | 42 ++++++++++------------ 2 files changed, 32 insertions(+), 47 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs index 859a3c7464..ce0d03b2b5 100644 --- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs +++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs @@ -122,18 +122,17 @@ namespace Emby.Server.Implementations.Data { WriteConnections = new ConnectionPool(WriteConnectionsCount, CreateWriteConnection); ReadConnections = new ConnectionPool(ReadConnectionsCount, CreateReadConnection); - } - protected ManagedConnection GetConnection(bool readOnly = false) - { - if (readOnly) + // Configuration and pragmas can affect VACUUM so it needs to be last. + using (var connection = GetConnection(true)) { - return ReadConnections.GetConnection(); + connection.Execute("VACUUM"); } - - return WriteConnections.GetConnection(); } + protected ManagedConnection GetConnection(bool readOnly = false) + => readOnly ? ReadConnections.GetConnection() : WriteConnections.GetConnection(); + protected SQLiteDatabaseConnection CreateWriteConnection() { var writeConnection = SQLite3.Open( @@ -173,52 +172,44 @@ namespace Emby.Server.Implementations.Data writeConnection.Execute("PRAGMA temp_store=" + (int)TempStore); - // Configuration and pragmas can affect VACUUM so it needs to be last. - writeConnection.Execute("VACUUM"); - return writeConnection; } protected SQLiteDatabaseConnection CreateReadConnection() { - var writeConnection = SQLite3.Open( + var connection = SQLite3.Open( DbFilePath, DefaultConnectionFlags | ConnectionFlags.ReadOnly, null); if (CacheSize.HasValue) { - writeConnection.Execute("PRAGMA cache_size=" + CacheSize.Value); + connection.Execute("PRAGMA cache_size=" + CacheSize.Value); } if (!string.IsNullOrWhiteSpace(LockingMode)) { - writeConnection.Execute("PRAGMA locking_mode=" + LockingMode); + connection.Execute("PRAGMA locking_mode=" + LockingMode); } if (!string.IsNullOrWhiteSpace(JournalMode)) { - writeConnection.Execute("PRAGMA journal_mode=" + JournalMode); + connection.Execute("PRAGMA journal_mode=" + JournalMode); } if (JournalSizeLimit.HasValue) { - writeConnection.Execute("PRAGMA journal_size_limit=" + JournalSizeLimit.Value); + connection.Execute("PRAGMA journal_size_limit=" + JournalSizeLimit.Value); } if (Synchronous.HasValue) { - writeConnection.Execute("PRAGMA synchronous=" + (int)Synchronous.Value); + connection.Execute("PRAGMA synchronous=" + (int)Synchronous.Value); } - if (PageSize.HasValue) - { - writeConnection.Execute("PRAGMA page_size=" + PageSize.Value); - } + connection.Execute("PRAGMA temp_store=" + (int)TempStore); - writeConnection.Execute("PRAGMA temp_store=" + (int)TempStore); - - return writeConnection; + return connection; } public IStatement PrepareStatement(ManagedConnection connection, string sql) diff --git a/Emby.Server.Implementations/Data/ConnectionPool.cs b/Emby.Server.Implementations/Data/ConnectionPool.cs index 86a125ba51..091a1b74f3 100644 --- a/Emby.Server.Implementations/Data/ConnectionPool.cs +++ b/Emby.Server.Implementations/Data/ConnectionPool.cs @@ -2,44 +2,47 @@ using System; using System.Collections.Concurrent; -using System.Threading; using SQLitePCL.pretty; namespace Emby.Server.Implementations.Data; public sealed class ConnectionPool : IDisposable { - private readonly int _count; - private readonly SemaphoreSlim _lock; - private readonly ConcurrentQueue<SQLiteDatabaseConnection> _connections = new ConcurrentQueue<SQLiteDatabaseConnection>(); + private readonly BlockingCollection<SQLiteDatabaseConnection> _connections = new(); private bool _disposed; public ConnectionPool(int count, Func<SQLiteDatabaseConnection> factory) { - _count = count; - _lock = new SemaphoreSlim(count, count); for (int i = 0; i < count; i++) { - _connections.Enqueue(factory.Invoke()); + _connections.Add(factory.Invoke()); } } public ManagedConnection GetConnection() { - _lock.Wait(); - if (!_connections.TryDequeue(out var connection)) + if (_disposed) { - _lock.Release(); - throw new InvalidOperationException(); + ThrowObjectDisposedException(); } - return new ManagedConnection(connection, this); + return new ManagedConnection(_connections.Take(), this); + + void ThrowObjectDisposedException() + { + throw new ObjectDisposedException(GetType().Name); + } } public void Return(SQLiteDatabaseConnection connection) { - _connections.Enqueue(connection); - _lock.Release(); + if (_disposed) + { + connection.Dispose(); + return; + } + + _connections.Add(connection); } public void Dispose() @@ -49,20 +52,11 @@ public sealed class ConnectionPool : IDisposable return; } - for (int i = 0; i < _count; i++) + foreach (var connection in _connections) { - _lock.Wait(); - if (!_connections.TryDequeue(out var connection)) - { - _lock.Release(); - throw new InvalidOperationException(); - } - connection.Dispose(); } - _lock.Dispose(); - _disposed = true; } } -- cgit v1.2.3 From 8dba3a44fd3f4771acb0cade2c91696179ad21d9 Mon Sep 17 00:00:00 2001 From: Bond_009 <bond.009@outlook.com> Date: Fri, 14 Apr 2023 22:43:14 +0200 Subject: Get write connection for vacuum --- Emby.Server.Implementations/Data/BaseSqliteRepository.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs index ce0d03b2b5..4b9ab53ae4 100644 --- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs +++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs @@ -124,7 +124,7 @@ namespace Emby.Server.Implementations.Data ReadConnections = new ConnectionPool(ReadConnectionsCount, CreateReadConnection); // Configuration and pragmas can affect VACUUM so it needs to be last. - using (var connection = GetConnection(true)) + using (var connection = GetConnection()) { connection.Execute("VACUUM"); } -- cgit v1.2.3 From 13152bf09dcadf8807e1240be7ca84662d2ed397 Mon Sep 17 00:00:00 2001 From: Bond_009 <bond.009@outlook.com> Date: Fri, 21 Apr 2023 14:05:27 +0200 Subject: Change number of read connections based on # of threads and add comments --- Emby.Server.Implementations/Data/ConnectionPool.cs | 19 +++++++++++++++++-- .../Data/SqliteItemRepository.cs | 2 +- 2 files changed, 18 insertions(+), 3 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/ConnectionPool.cs b/Emby.Server.Implementations/Data/ConnectionPool.cs index 091a1b74f3..6d28a5e43d 100644 --- a/Emby.Server.Implementations/Data/ConnectionPool.cs +++ b/Emby.Server.Implementations/Data/ConnectionPool.cs @@ -1,16 +1,22 @@ -#pragma warning disable CS1591 - using System; using System.Collections.Concurrent; using SQLitePCL.pretty; namespace Emby.Server.Implementations.Data; +/// <summary> +/// A pool of SQLite Database connections. +/// </summary> public sealed class ConnectionPool : IDisposable { private readonly BlockingCollection<SQLiteDatabaseConnection> _connections = new(); private bool _disposed; + /// <summary> + /// Initializes a new instance of the <see cref="ConnectionPool" /> class. + /// </summary> + /// <param name="count">The number of database connection to create.</param> + /// <param name="factory">Factory function to create the database connections.</param> public ConnectionPool(int count, Func<SQLiteDatabaseConnection> factory) { for (int i = 0; i < count; i++) @@ -19,6 +25,10 @@ public sealed class ConnectionPool : IDisposable } } + /// <summary> + /// Gets a database connection from the pool if one is available, otherwise blocks. + /// </summary> + /// <returns>A database connection.</returns> public ManagedConnection GetConnection() { if (_disposed) @@ -34,6 +44,10 @@ public sealed class ConnectionPool : IDisposable } } + /// <summary> + /// Return a database connection to the pool. + /// </summary> + /// <param name="connection">The database connection to return.</param> public void Return(SQLiteDatabaseConnection connection) { if (_disposed) @@ -45,6 +59,7 @@ public sealed class ConnectionPool : IDisposable _connections.Add(connection); } + /// <inheritdoc /> public void Dispose() { if (_disposed) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 33466c34b0..22d485d335 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -336,7 +336,7 @@ namespace Emby.Server.Implementations.Data _jsonOptions = JsonDefaults.Options; DbFilePath = Path.Combine(_config.ApplicationPaths.DataPath, "library.db"); - ReadConnectionsCount = 5; + ReadConnectionsCount = Environment.ProcessorCount * 2; } /// <inheritdoc /> -- cgit v1.2.3 From 0d67901e373ff8a2ab279d51c3c1f88adf68bfcf Mon Sep 17 00:00:00 2001 From: Bond_009 <bond.009@outlook.com> Date: Mon, 24 Apr 2023 13:08:46 +0200 Subject: Dispose BlockingCollection --- Emby.Server.Implementations/Data/ConnectionPool.cs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/ConnectionPool.cs b/Emby.Server.Implementations/Data/ConnectionPool.cs index 6d28a5e43d..a671eb165d 100644 --- a/Emby.Server.Implementations/Data/ConnectionPool.cs +++ b/Emby.Server.Implementations/Data/ConnectionPool.cs @@ -72,6 +72,8 @@ public sealed class ConnectionPool : IDisposable connection.Dispose(); } + _connections.Dispose(); + _disposed = true; } } -- cgit v1.2.3 From 29889159e80f66bcecd5403a756c5c853cae1a3b Mon Sep 17 00:00:00 2001 From: Mark Lopez <m@silvenga.com> Date: Sat, 22 Apr 2023 09:18:12 -0500 Subject: Increased the max journal_size_limit to reduce the number of truncation operations. --- Emby.Server.Implementations/Data/BaseSqliteRepository.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs index bc520b86e7..fa58249ec2 100644 --- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs +++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs @@ -73,9 +73,10 @@ namespace Emby.Server.Implementations.Data /// <summary> /// Gets the journal size limit. <see href="https://www.sqlite.org/pragma.html#pragma_journal_size_limit" />. + /// The default (-1) is overriden to prevent unconstrained WAL size, as reported by users. /// </summary> /// <value>The journal size limit.</value> - protected virtual int? JournalSizeLimit => 0; + protected virtual int? JournalSizeLimit => 134_217_728; // 128MiB /// <summary> /// Gets the page size. -- cgit v1.2.3 From 2c4f7ec52c3f99143f0d316e791b03bded17d714 Mon Sep 17 00:00:00 2001 From: Oskari Lavinto <olavinto@protonmail.com> Date: Sun, 30 Apr 2023 08:37:24 +0000 Subject: Translated using Weblate (Finnish) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/fi/ --- Emby.Server.Implementations/Localization/Core/fi.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/fi.json b/Emby.Server.Implementations/Localization/Core/fi.json index ec72d58dd6..8672cfb9ff 100644 --- a/Emby.Server.Implementations/Localization/Core/fi.json +++ b/Emby.Server.Implementations/Localization/Core/fi.json @@ -118,7 +118,7 @@ "TaskCleanActivityLogDescription": "Poistaa määritettyä ikää vanhemmat tapahtumat toimintahistoriasta.", "TaskCleanActivityLog": "Tyhjennä toimintahistoria", "Undefined": "Määrittelemätön", - "TaskOptimizeDatabaseDescription": "Tiivistää ja puhdistaa tietokannan. Tämän toiminnon suorittaminen kirjastojen skannauksen tai muiden tietokantaan liittyvien muutoksien jälkeen voi parantaa suorituskykyä.", + "TaskOptimizeDatabaseDescription": "Tiivistää ja puhdistaa tietokannan. Tämän toiminnon suorittaminen kirjastopäivityksen tai muiden mahdollisten tietokantamuutosten jälkeen voi parantaa suorituskykyä.", "TaskOptimizeDatabase": "Optimoi tietokanta", "TaskKeyframeExtractorDescription": "Purkaa videotiedostojen avainkuvat tarkempien HLS-toistolistojen luomiseksi. Tehtävä saattaa kestää huomattavan pitkään.", "TaskKeyframeExtractor": "Avainkuvien purkain", -- cgit v1.2.3 From 7b109572230e664b40b6271ee3a0fa7e760f1b4b Mon Sep 17 00:00:00 2001 From: RJS <zibenscilveeks@aicis.id.lv> Date: Tue, 2 May 2023 21:53:30 +0000 Subject: Translated using Weblate (Latvian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/lv/ --- Emby.Server.Implementations/Localization/Core/lv.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/lv.json b/Emby.Server.Implementations/Localization/Core/lv.json index e460fd7197..f753a369ab 100644 --- a/Emby.Server.Implementations/Localization/Core/lv.json +++ b/Emby.Server.Implementations/Localization/Core/lv.json @@ -120,5 +120,6 @@ "Default": "Noklusējuma", "TaskOptimizeDatabaseDescription": "Saspiež datubāzi un atbrīvo atmiņu. Uzdevum palaišana pēc bibliotēku skenēšanas vai citām, ar datubāzi saistītām, izmaiņām iespējams uzlabos ātrdarbību.", "TaskOptimizeDatabase": "Optimizēt datubāzi", - "External": "Ārējais" + "External": "Ārējais", + "HearingImpaired": "Ar dzirdes traucējumiem" } -- cgit v1.2.3 From 5e2a1fb4478d5324961a3f822a1057628e75d2a8 Mon Sep 17 00:00:00 2001 From: Meyu-Sys <sarveshkate0@gmail.com> Date: Tue, 2 May 2023 13:07:48 +0000 Subject: Translated using Weblate (Marathi) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/mr/ --- Emby.Server.Implementations/Localization/Core/mr.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/mr.json b/Emby.Server.Implementations/Localization/Core/mr.json index b2227e4543..a8fb26b91a 100644 --- a/Emby.Server.Implementations/Localization/Core/mr.json +++ b/Emby.Server.Implementations/Localization/Core/mr.json @@ -122,5 +122,6 @@ "External": "बाहेरचा", "DeviceOnlineWithName": "{0} कनेक्ट झाले", "DeviceOfflineWithName": "{0} डिस्कनेक्ट झाला आहे", - "AuthenticationSucceededWithUserName": "{0} यशस्वीरित्या प्रमाणीकृत" + "AuthenticationSucceededWithUserName": "{0} यशस्वीरित्या प्रमाणीकृत", + "HearingImpaired": "कर्णबधीर" } -- cgit v1.2.3 From 8e1f0d53c1efe286628a4119b3c595f219513d23 Mon Sep 17 00:00:00 2001 From: Bond_009 <bond.009@outlook.com> Date: Thu, 4 May 2023 14:42:39 +0200 Subject: nameof instead of GetType().Name --- Emby.Server.Implementations/Data/ConnectionPool.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/ConnectionPool.cs b/Emby.Server.Implementations/Data/ConnectionPool.cs index a671eb165d..5ea7e934ff 100644 --- a/Emby.Server.Implementations/Data/ConnectionPool.cs +++ b/Emby.Server.Implementations/Data/ConnectionPool.cs @@ -38,9 +38,9 @@ public sealed class ConnectionPool : IDisposable return new ManagedConnection(_connections.Take(), this); - void ThrowObjectDisposedException() + static void ThrowObjectDisposedException() { - throw new ObjectDisposedException(GetType().Name); + throw new ObjectDisposedException(nameof(ConnectionPool)); } } -- cgit v1.2.3 From f2a41fe79fd6e6e42a43eebb8f2ea978c6b7e023 Mon Sep 17 00:00:00 2001 From: Alan Azar <alanazar304@hotmail.com> Date: Fri, 5 May 2023 11:10:03 +0000 Subject: Translated using Weblate (Urdu (Pakistan)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ur_PK/ --- Emby.Server.Implementations/Localization/Core/ur_PK.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ur_PK.json b/Emby.Server.Implementations/Localization/Core/ur_PK.json index 7fe0c4c4be..5d3f194329 100644 --- a/Emby.Server.Implementations/Localization/Core/ur_PK.json +++ b/Emby.Server.Implementations/Localization/Core/ur_PK.json @@ -12,7 +12,7 @@ "HeaderContinueWatching": "دیکھنا جاری رکھیں", "Playlists": "پلے لسٹس", "ValueSpecialEpisodeName": "خصوصی - {0}", - "Shows": "دکھاتا ہے۔", + "Shows": "دکھاتا ہے", "Genres": "انواع", "Artists": "فنکار", "Sync": "مطابقت پذیری", @@ -123,5 +123,5 @@ "TaskCleanActivityLogDescription": "تشکیل شدہ عمر سے زیادہ پرانی سرگرمی لاگ اندراجات کو حذف کرتا ہے۔", "External": "بیرونی", "HearingImpaired": "قوت سماعت سے محروم", - "TaskCleanActivityLog": "سرگرمی لاگ کو صاف کریں۔" + "TaskCleanActivityLog": "سرگرمی لاگ کو صاف کریں" } -- cgit v1.2.3 From fa0a48b54eab58d77fce4b0e3653f88633944342 Mon Sep 17 00:00:00 2001 From: Subham Jena <subhamjena8465@gmail.com> Date: Sat, 6 May 2023 01:42:35 -0400 Subject: Added translation using Weblate (Odia) --- Emby.Server.Implementations/Localization/Core/or.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 Emby.Server.Implementations/Localization/Core/or.json (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/or.json b/Emby.Server.Implementations/Localization/Core/or.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/Emby.Server.Implementations/Localization/Core/or.json @@ -0,0 +1 @@ +{} -- cgit v1.2.3 From 9ce152ecf6159a77e39c7231f8a6b4f77bacd65e Mon Sep 17 00:00:00 2001 From: TheSharingBrother <thesharingbrother@proton.me> Date: Sat, 6 May 2023 15:53:18 +0000 Subject: Translated using Weblate (Hindi) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/hi/ --- Emby.Server.Implementations/Localization/Core/hi.json | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/hi.json b/Emby.Server.Implementations/Localization/Core/hi.json index cc0a926d03..4bbb1dd352 100644 --- a/Emby.Server.Implementations/Localization/Core/hi.json +++ b/Emby.Server.Implementations/Localization/Core/hi.json @@ -92,6 +92,17 @@ "System": "प्रणाली", "TvShows": "टीवी शो", "HearingImpaired": "मूक बधिर", - "ValueSpecialEpisodeName": "विशेष", - "TasksMaintenanceCategory": "रखरखाव" + "ValueSpecialEpisodeName": "विशेष - {0}", + "TasksMaintenanceCategory": "रखरखाव", + "Sync": "समाकलयति", + "VersionNumber": "{0} पाठान्तर", + "ValueHasBeenAddedToLibrary": "{0} आपके माध्यम ग्रन्थालय में उपजात हो गया हैं", + "TasksLibraryCategory": "संग्रहालय", + "TaskOptimizeDatabase": "जानकारी प्रवृद्धि", + "TaskDownloadMissingSubtitles": "असमेत अनुलेख को अवाहरति करें", + "TaskRefreshLibrary": "माध्यम संग्राहत को छाने", + "TaskCleanActivityLog": "क्रियाकलाप लॉग साफ करें", + "TasksChannelsCategory": "इंटरनेट प्रणाली", + "TasksApplicationCategory": "अनुप्रयोग", + "TaskRefreshPeople": "लोगोकी जानकारी ताज़ी करें" } -- cgit v1.2.3 From 898a04c9e31a77f74bd69d78896887f4f11ba1a5 Mon Sep 17 00:00:00 2001 From: Brett Healey <Bearach.goll@gmail.com> Date: Sat, 6 May 2023 18:29:05 +0000 Subject: Translated using Weblate (Welsh) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/cy/ --- Emby.Server.Implementations/Localization/Core/cy.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/cy.json b/Emby.Server.Implementations/Localization/Core/cy.json index 331c3d678f..794a8e4ce4 100644 --- a/Emby.Server.Implementations/Localization/Core/cy.json +++ b/Emby.Server.Implementations/Localization/Core/cy.json @@ -28,7 +28,7 @@ "NameSeasonNumber": "Tymor {0}", "MusicVideos": "Fideos Cerddoriaeth", "MixedContent": "Cynnwys amrywiol", - "HomeVideos": "Fideos Cartref", + "HomeVideos": "Genres", "HeaderNextUp": "Nesaf i Fyny", "HeaderFavoriteArtists": "Ffefryn Artistiaid", "HeaderFavoriteAlbums": "Ffefryn Albwmau", @@ -122,5 +122,6 @@ "TaskRefreshChapterImagesDescription": "Creu mân-luniau ar gyfer fideos sydd â phenodau.", "TaskRefreshChapterImages": "Echdynnu Lluniau Pennod", "TaskCleanCacheDescription": "Dileu ffeiliau cache nad oes eu hangen ar y system mwyach.", - "TaskCleanCache": "Gwaghau Ffolder Cache" + "TaskCleanCache": "Gwaghau Ffolder Cache", + "HearingImpaired": "Nam ar y clyw" } -- cgit v1.2.3 From bceca0ae45012fa6fbb13b793559779a4a32910a Mon Sep 17 00:00:00 2001 From: Subham Jena <subhamjena8465@gmail.com> Date: Sat, 6 May 2023 05:43:29 +0000 Subject: Translated using Weblate (Odia) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/or/ --- Emby.Server.Implementations/Localization/Core/or.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/or.json b/Emby.Server.Implementations/Localization/Core/or.json index 0967ef424b..0e9d81ee87 100644 --- a/Emby.Server.Implementations/Localization/Core/or.json +++ b/Emby.Server.Implementations/Localization/Core/or.json @@ -1 +1,4 @@ -{} +{ + "External": "ବହିଃସ୍ଥ", + "Genres": "ଧରଣ" +} -- cgit v1.2.3 From 92a0d26f31743ca0015fcc3e0a4fe094792ac63c Mon Sep 17 00:00:00 2001 From: 無情天 <kofzhanganguo@126.com> Date: Mon, 8 May 2023 19:19:12 +0000 Subject: Translated using Weblate (Chinese (Simplified)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/zh_Hans/ --- Emby.Server.Implementations/Localization/Core/zh-CN.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/zh-CN.json b/Emby.Server.Implementations/Localization/Core/zh-CN.json index ccfbeef0ce..03265d3fb0 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-CN.json +++ b/Emby.Server.Implementations/Localization/Core/zh-CN.json @@ -14,7 +14,7 @@ "FailedLoginAttemptWithUserName": "从 {0} 尝试登录失败", "Favorites": "我的最爱", "Folders": "文件夹", - "Genres": "风格", + "Genres": "类型", "HeaderAlbumArtists": "专辑艺术家", "HeaderContinueWatching": "继续观看", "HeaderFavoriteAlbums": "收藏的专辑", -- cgit v1.2.3 From b42a6119f56d3201ac402c834faeae73f52cf992 Mon Sep 17 00:00:00 2001 From: SuperDumbTM <eltonlochunkit@gmail.com> Date: Tue, 9 May 2023 05:24:04 +0000 Subject: Translated using Weblate (Chinese (Traditional, Hong Kong)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/zh_Hant_HK/ --- Emby.Server.Implementations/Localization/Core/zh-HK.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/zh-HK.json b/Emby.Server.Implementations/Localization/Core/zh-HK.json index 610f503ddb..e8b8c2c5fa 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-HK.json +++ b/Emby.Server.Implementations/Localization/Core/zh-HK.json @@ -15,7 +15,7 @@ "Favorites": "我的最愛", "Folders": "資料夾", "Genres": "風格", - "HeaderAlbumArtists": "專輯藝人", + "HeaderAlbumArtists": "專輯歌手", "HeaderContinueWatching": "繼續觀看", "HeaderFavoriteAlbums": "最愛的專輯", "HeaderFavoriteArtists": "最愛的藝人", -- cgit v1.2.3 From b3b75d5bc232a0e912f4ded8e3588e2c347a51de Mon Sep 17 00:00:00 2001 From: garikaib <garikaib@gmail.com> Date: Wed, 10 May 2023 01:01:16 -0400 Subject: Added translation using Weblate (Shona) --- Emby.Server.Implementations/Localization/Core/sn.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 Emby.Server.Implementations/Localization/Core/sn.json (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/sn.json b/Emby.Server.Implementations/Localization/Core/sn.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/Emby.Server.Implementations/Localization/Core/sn.json @@ -0,0 +1 @@ +{} -- cgit v1.2.3 From f0bf5c4998ca2f8bedd61a750dc5228e2787f21e Mon Sep 17 00:00:00 2001 From: garikaib <garikaib@gmail.com> Date: Wed, 10 May 2023 05:10:35 +0000 Subject: Translated using Weblate (Shona) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/sn/ --- .../Localization/Core/sn.json | 29 +++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/sn.json b/Emby.Server.Implementations/Localization/Core/sn.json index 0967ef424b..74720e7646 100644 --- a/Emby.Server.Implementations/Localization/Core/sn.json +++ b/Emby.Server.Implementations/Localization/Core/sn.json @@ -1 +1,28 @@ -{} +{ + "HeaderAlbumArtists": "Vaimbi vemadambarefu", + "HeaderContinueWatching": "Simudzira kuona", + "HeaderFavoriteSongs": "Nziyo dzaunofarira", + "Albums": "Dambarefu", + "AppDeviceValues": "Apu: {0}, Dhivhaisi: {1}", + "Application": "Purogiramu", + "Artists": "Vaimbi", + "AuthenticationSucceededWithUserName": "apinda", + "Books": "Mabhuku", + "CameraImageUploadedFrom": "Mufananidzo mutsva vabva pakamera {0}", + "Channels": "Machanewo", + "ChapterNameValue": "Chikamu {0}", + "Collections": "Akafanana", + "Default": "Zvakasarudzwa Kare", + "DeviceOfflineWithName": "{0} haasisipo", + "DeviceOnlineWithName": "{0} aripo", + "External": "Zvekunze", + "FailedLoginAttemptWithUserName": "Vatadza kuloga chimboedza kushandisa {0}", + "Favorites": "Zvaunofarira", + "Folders": "Mafoodha", + "Forced": "Zvekumanikidzira", + "Genres": "Mhando", + "HeaderFavoriteAlbums": "Madambarefu aunofarira", + "HeaderFavoriteArtists": "Vaimbi vaunofarira", + "HeaderFavoriteEpisodes": "Maepisodhi aunofarira", + "HeaderFavoriteShows": "Masirisi aunofarira" +} -- cgit v1.2.3 From 756ee38d01ae66f1d6abe920d96eabce468a2e2e Mon Sep 17 00:00:00 2001 From: Bond-009 <bond.009@outlook.com> Date: Thu, 11 May 2023 01:39:57 +0200 Subject: Remove ExtendedFileSystemInfo (#9749) --- .../IO/ExtendedFileSystemInfo.cs | 13 ------ .../IO/ManagedFileSystem.cs | 46 ++++++---------------- 2 files changed, 11 insertions(+), 48 deletions(-) delete mode 100644 Emby.Server.Implementations/IO/ExtendedFileSystemInfo.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/IO/ExtendedFileSystemInfo.cs b/Emby.Server.Implementations/IO/ExtendedFileSystemInfo.cs deleted file mode 100644 index 545d73e05f..0000000000 --- a/Emby.Server.Implementations/IO/ExtendedFileSystemInfo.cs +++ /dev/null @@ -1,13 +0,0 @@ -#pragma warning disable CS1591 - -namespace Emby.Server.Implementations.IO -{ - public class ExtendedFileSystemInfo - { - public bool IsHidden { get; set; } - - public bool IsReadOnly { get; set; } - - public bool Exists { get; set; } - } -} diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs index 55f384ae8f..1fffdfbfa0 100644 --- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs +++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs @@ -267,25 +267,6 @@ namespace Emby.Server.Implementations.IO return result; } - private static ExtendedFileSystemInfo GetExtendedFileSystemInfo(string path) - { - var result = new ExtendedFileSystemInfo(); - - var info = new FileInfo(path); - - if (info.Exists) - { - result.Exists = true; - - var attributes = info.Attributes; - - result.IsHidden = (attributes & FileAttributes.Hidden) == FileAttributes.Hidden; - result.IsReadOnly = (attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly; - } - - return result; - } - /// <summary> /// Takes a filename and removes invalid characters. /// </summary> @@ -403,19 +384,18 @@ namespace Emby.Server.Implementations.IO return; } - var info = GetExtendedFileSystemInfo(path); + var info = new FileInfo(path); - if (info.Exists && info.IsHidden != isHidden) + if (info.Exists && + ((info.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden) != isHidden) { if (isHidden) { - File.SetAttributes(path, File.GetAttributes(path) | FileAttributes.Hidden); + File.SetAttributes(path, info.Attributes | FileAttributes.Hidden); } else { - var attributes = File.GetAttributes(path); - attributes = RemoveAttribute(attributes, FileAttributes.Hidden); - File.SetAttributes(path, attributes); + File.SetAttributes(path, info.Attributes & ~FileAttributes.Hidden); } } } @@ -428,19 +408,20 @@ namespace Emby.Server.Implementations.IO return; } - var info = GetExtendedFileSystemInfo(path); + var info = new FileInfo(path); if (!info.Exists) { return; } - if (info.IsReadOnly == readOnly && info.IsHidden == isHidden) + if (((info.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly) == readOnly + && ((info.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden) == isHidden) { return; } - var attributes = File.GetAttributes(path); + var attributes = info.Attributes; if (readOnly) { @@ -448,7 +429,7 @@ namespace Emby.Server.Implementations.IO } else { - attributes = RemoveAttribute(attributes, FileAttributes.ReadOnly); + attributes &= ~FileAttributes.ReadOnly; } if (isHidden) @@ -457,17 +438,12 @@ namespace Emby.Server.Implementations.IO } else { - attributes = RemoveAttribute(attributes, FileAttributes.Hidden); + attributes &= ~FileAttributes.Hidden; } File.SetAttributes(path, attributes); } - private static FileAttributes RemoveAttribute(FileAttributes attributes, FileAttributes attributesToRemove) - { - return attributes & ~attributesToRemove; - } - /// <summary> /// Swaps the files. /// </summary> -- cgit v1.2.3 From fdda72139411a89a64a1a1bb7282f273dc720e42 Mon Sep 17 00:00:00 2001 From: Gustavs <tausgus@tuta.io> Date: Thu, 11 May 2023 13:28:25 +0000 Subject: Translated using Weblate (Latvian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/lv/ --- Emby.Server.Implementations/Localization/Core/lv.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/lv.json b/Emby.Server.Implementations/Localization/Core/lv.json index f753a369ab..f7b24412af 100644 --- a/Emby.Server.Implementations/Localization/Core/lv.json +++ b/Emby.Server.Implementations/Localization/Core/lv.json @@ -84,7 +84,7 @@ "CameraImageUploadedFrom": "Jauns kameras attēls ir ticis augšupielādēts no {0}", "Books": "Grāmatas", "Artists": "Izpildītāji", - "Albums": "Albūmi", + "Albums": "Albumi", "ProviderValue": "Provider: {0}", "HeaderFavoriteSongs": "Dziesmu Favorīti", "HeaderFavoriteShows": "Raidījumu Favorīti", @@ -121,5 +121,7 @@ "TaskOptimizeDatabaseDescription": "Saspiež datubāzi un atbrīvo atmiņu. Uzdevum palaišana pēc bibliotēku skenēšanas vai citām, ar datubāzi saistītām, izmaiņām iespējams uzlabos ātrdarbību.", "TaskOptimizeDatabase": "Optimizēt datubāzi", "External": "Ārējais", - "HearingImpaired": "Ar dzirdes traucējumiem" + "HearingImpaired": "Ar dzirdes traucējumiem", + "TaskKeyframeExtractor": "Atslēgkadru Ekstraktors", + "TaskKeyframeExtractorDescription": "Ekstraktē atslēgkadrus no video failiem lai izveidotu precīzākus HLS atskaņošanas sarakstus. Šis process var būt ilgs." } -- cgit v1.2.3 From a8cdf4434b10044dbb9ba540d6d137906aa67b54 Mon Sep 17 00:00:00 2001 From: Shadowghost <Shadowghost@users.noreply.github.com> Date: Fri, 12 May 2023 15:11:59 +0200 Subject: Fix access to playlists not created by a user (#9746) --- .../Library/Resolvers/PlaylistResolver.cs | 11 +++++++---- .../Playlists/PlaylistManager.cs | 2 +- Jellyfin.Api/Controllers/PlaylistsController.cs | 16 +++++++++++++--- Jellyfin.Server/Migrations/Routines/FixPlaylistOwner.cs | 17 ++++++++++++----- MediaBrowser.Controller/Playlists/Playlist.cs | 8 ++++++++ 5 files changed, 41 insertions(+), 13 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs b/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs index 7a2b3da3a9..5d569009d3 100644 --- a/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs @@ -30,7 +30,7 @@ namespace Emby.Server.Implementations.Library.Resolvers { if (args.IsDirectory) { - // It's a boxset if the path is a directory with [playlist] in it's the name + // It's a boxset if the path is a directory with [playlist] in its name var filename = Path.GetFileName(Path.TrimEndingDirectorySeparator(args.Path)); if (string.IsNullOrEmpty(filename)) { @@ -42,7 +42,8 @@ namespace Emby.Server.Implementations.Library.Resolvers return new Playlist { Path = args.Path, - Name = filename.Replace("[playlist]", string.Empty, StringComparison.OrdinalIgnoreCase).Trim() + Name = filename.Replace("[playlist]", string.Empty, StringComparison.OrdinalIgnoreCase).Trim(), + OpenAccess = true }; } @@ -53,7 +54,8 @@ namespace Emby.Server.Implementations.Library.Resolvers return new Playlist { Path = args.Path, - Name = filename + Name = filename, + OpenAccess = true }; } } @@ -70,7 +72,8 @@ namespace Emby.Server.Implementations.Library.Resolvers Path = args.Path, Name = Path.GetFileNameWithoutExtension(args.Path), IsInMixedFolder = true, - PlaylistMediaType = MediaType.Audio + PlaylistMediaType = MediaType.Audio, + OpenAccess = true }; } } diff --git a/Emby.Server.Implementations/Playlists/PlaylistManager.cs b/Emby.Server.Implementations/Playlists/PlaylistManager.cs index 6176879b66..adb8ac7327 100644 --- a/Emby.Server.Implementations/Playlists/PlaylistManager.cs +++ b/Emby.Server.Implementations/Playlists/PlaylistManager.cs @@ -549,7 +549,7 @@ namespace Emby.Server.Implementations.Playlists SavePlaylistFile(playlist); } } - else + else if (!playlist.OpenAccess) { // Remove playlist if not shared _libraryManager.DeleteItem( diff --git a/Jellyfin.Api/Controllers/PlaylistsController.cs b/Jellyfin.Api/Controllers/PlaylistsController.cs index c6dbea5e22..20995bf1b4 100644 --- a/Jellyfin.Api/Controllers/PlaylistsController.cs +++ b/Jellyfin.Api/Controllers/PlaylistsController.cs @@ -64,12 +64,15 @@ public class PlaylistsController : BaseJellyfinApiController /// <param name="userId">The user id.</param> /// <param name="mediaType">The media type.</param> /// <param name="createPlaylistRequest">The create playlist payload.</param> + /// <response code="200">Playlist created.</response> + /// <response code="403">User does not have permission to create playlists.</response> /// <returns> /// A <see cref="Task" /> that represents the asynchronous operation to create a playlist. /// The task result contains an <see cref="OkResult"/> indicating success. /// </returns> [HttpPost] [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] public async Task<ActionResult<PlaylistCreationResult>> CreatePlaylist( [FromQuery, ParameterObsolete] string? name, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder)), ParameterObsolete] IReadOnlyList<Guid> ids, @@ -102,9 +105,11 @@ public class PlaylistsController : BaseJellyfinApiController /// <param name="ids">Item id, comma delimited.</param> /// <param name="userId">The userId.</param> /// <response code="204">Items added to playlist.</response> + /// <response code="403">User does not have permission to add items to playlist.</response> /// <returns>An <see cref="NoContentResult"/> on success.</returns> [HttpPost("{playlistId}/Items")] [ProducesResponseType(StatusCodes.Status204NoContent)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] public async Task<ActionResult> AddToPlaylist( [FromRoute, Required] Guid playlistId, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] ids, @@ -122,9 +127,11 @@ public class PlaylistsController : BaseJellyfinApiController /// <param name="itemId">The item id.</param> /// <param name="newIndex">The new index.</param> /// <response code="204">Item moved to new index.</response> + /// <response code="403">User does not have permission to move item.</response> /// <returns>An <see cref="NoContentResult"/> on success.</returns> [HttpPost("{playlistId}/Items/{itemId}/Move/{newIndex}")] [ProducesResponseType(StatusCodes.Status204NoContent)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] public async Task<ActionResult> MoveItem( [FromRoute, Required] string playlistId, [FromRoute, Required] string itemId, @@ -140,9 +147,11 @@ public class PlaylistsController : BaseJellyfinApiController /// <param name="playlistId">The playlist id.</param> /// <param name="entryIds">The item ids, comma delimited.</param> /// <response code="204">Items removed.</response> + /// <response code="403">User does not have permission to get playlist.</response> /// <returns>An <see cref="NoContentResult"/> on success.</returns> [HttpDelete("{playlistId}/Items")] [ProducesResponseType(StatusCodes.Status204NoContent)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] public async Task<ActionResult> RemoveFromPlaylist( [FromRoute, Required] string playlistId, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] entryIds) @@ -164,9 +173,13 @@ public class PlaylistsController : BaseJellyfinApiController /// <param name="imageTypeLimit">Optional. The max number of images to return, per image type.</param> /// <param name="enableImageTypes">Optional. The image types to include in the output.</param> /// <response code="200">Original playlist returned.</response> + /// <response code="403">User does not have permission to get playlist items.</response> /// <response code="404">Playlist not found.</response> /// <returns>The original playlist items.</returns> [HttpGet("{playlistId}/Items")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + [ProducesResponseType(StatusCodes.Status404NotFound)] public ActionResult<QueryResult<BaseItemDto>> GetPlaylistItems( [FromRoute, Required] Guid playlistId, [FromQuery, Required] Guid userId, @@ -189,9 +202,7 @@ public class PlaylistsController : BaseJellyfinApiController : _userManager.GetUserById(userId); var items = playlist.GetManageableItems().ToArray(); - var count = items.Length; - if (startIndex.HasValue) { items = items.Skip(startIndex.Value).ToArray(); @@ -207,7 +218,6 @@ public class PlaylistsController : BaseJellyfinApiController .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); var dtos = _dtoService.GetBaseItemDtos(items.Select(i => i.Item2).ToList(), dtoOptions, user); - for (int index = 0; index < dtos.Count; index++) { dtos[index].PlaylistItemId = items[index].Item1.Id; diff --git a/Jellyfin.Server/Migrations/Routines/FixPlaylistOwner.cs b/Jellyfin.Server/Migrations/Routines/FixPlaylistOwner.cs index 55aadae79a..1dd938b1be 100644 --- a/Jellyfin.Server/Migrations/Routines/FixPlaylistOwner.cs +++ b/Jellyfin.Server/Migrations/Routines/FixPlaylistOwner.cs @@ -53,12 +53,19 @@ internal class FixPlaylistOwner : IMigrationRoutine foreach (var playlist in playlists) { var shares = playlist.Shares; - var firstEditShare = shares.First(x => x.CanEdit); - if (firstEditShare is not null && Guid.TryParse(firstEditShare.UserId, out var guid)) + if (shares.Length > 0) { - playlist.OwnerUserId = guid; - playlist.Shares = shares.Where(x => x != firstEditShare).ToArray(); - + var firstEditShare = shares.First(x => x.CanEdit); + if (firstEditShare is not null && Guid.TryParse(firstEditShare.UserId, out var guid)) + { + playlist.OwnerUserId = guid; + playlist.Shares = shares.Where(x => x != firstEditShare).ToArray(); + _playlistManager.UpdatePlaylistAsync(playlist).GetAwaiter().GetResult(); + } + } + else + { + playlist.OpenAccess = true; _playlistManager.UpdatePlaylistAsync(playlist).GetAwaiter().GetResult(); } } diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs index 344e996ea8..498df5ab06 100644 --- a/MediaBrowser.Controller/Playlists/Playlist.cs +++ b/MediaBrowser.Controller/Playlists/Playlist.cs @@ -34,10 +34,13 @@ namespace MediaBrowser.Controller.Playlists public Playlist() { Shares = Array.Empty<Share>(); + OpenAccess = false; } public Guid OwnerUserId { get; set; } + public bool OpenAccess { get; set; } + public Share[] Shares { get; set; } [JsonIgnore] @@ -233,6 +236,11 @@ namespace MediaBrowser.Controller.Playlists return base.IsVisible(user); } + if (OpenAccess) + { + return true; + } + var userId = user.Id; if (userId.Equals(OwnerUserId)) { -- cgit v1.2.3 From 603fce59df780626c3269eaa94d95504e823b2f6 Mon Sep 17 00:00:00 2001 From: TelepathicWalrus <48514138+TelepathicWalrus@users.noreply.github.com> Date: Mon, 15 May 2023 12:12:24 +0100 Subject: Audio normalization (#9222) Co-authored-by: Joe Rogers <1337joe@users.noreply.github.com> Co-authored-by: Bond-009 <bond.009@outlook.com> --- CONTRIBUTORS.md | 1 + .../Data/SqliteItemRepository.cs | 12 ++++- Emby.Server.Implementations/Dto/DtoService.cs | 1 + MediaBrowser.Controller/Entities/BaseItem.cs | 7 +++ MediaBrowser.Model/Configuration/LibraryOptions.cs | 2 + MediaBrowser.Model/Dto/BaseItemDto.cs | 6 +++ .../MediaInfo/AudioFileProber.cs | 60 ++++++++++++++++++++++ MediaBrowser.Providers/MediaInfo/ProbeProvider.cs | 2 +- 8 files changed, 88 insertions(+), 3 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index c9430b235f..0b322685d1 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -126,6 +126,7 @@ - [SuperSandro2000](https://github.com/SuperSandro2000) - [tbraeutigam](https://github.com/tbraeutigam) - [teacupx](https://github.com/teacupx) + - [TelepathicWalrus](https://github.com/TelepathicWalrus) - [Terror-Gene](https://github.com/Terror-Gene) - [ThatNerdyPikachu](https://github.com/ThatNerdyPikachu) - [ThibaultNocchi](https://github.com/ThibaultNocchi) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 22d485d335..c32e7c75ad 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -49,8 +49,8 @@ namespace Emby.Server.Implementations.Data private const string SaveItemCommandText = @"replace into TypedBaseItems - (guid,type,data,Path,StartDate,EndDate,ChannelId,IsMovie,IsSeries,EpisodeTitle,IsRepeat,CommunityRating,CustomRating,IndexNumber,IsLocked,Name,OfficialRating,MediaType,Overview,ParentIndexNumber,PremiereDate,ProductionYear,ParentId,Genres,InheritedParentalRatingValue,SortName,ForcedSortName,RunTimeTicks,Size,DateCreated,DateModified,PreferredMetadataLanguage,PreferredMetadataCountryCode,Width,Height,DateLastRefreshed,DateLastSaved,IsInMixedFolder,LockedFields,Studios,Audio,ExternalServiceId,Tags,IsFolder,UnratedType,TopParentId,TrailerTypes,CriticRating,CleanName,PresentationUniqueKey,OriginalTitle,PrimaryVersionId,DateLastMediaAdded,Album,IsVirtualItem,SeriesName,UserDataKey,SeasonName,SeasonId,SeriesId,ExternalSeriesId,Tagline,ProviderIds,Images,ProductionLocations,ExtraIds,TotalBitrate,ExtraType,Artists,AlbumArtists,ExternalId,SeriesPresentationUniqueKey,ShowId,OwnerId) - values (@guid,@type,@data,@Path,@StartDate,@EndDate,@ChannelId,@IsMovie,@IsSeries,@EpisodeTitle,@IsRepeat,@CommunityRating,@CustomRating,@IndexNumber,@IsLocked,@Name,@OfficialRating,@MediaType,@Overview,@ParentIndexNumber,@PremiereDate,@ProductionYear,@ParentId,@Genres,@InheritedParentalRatingValue,@SortName,@ForcedSortName,@RunTimeTicks,@Size,@DateCreated,@DateModified,@PreferredMetadataLanguage,@PreferredMetadataCountryCode,@Width,@Height,@DateLastRefreshed,@DateLastSaved,@IsInMixedFolder,@LockedFields,@Studios,@Audio,@ExternalServiceId,@Tags,@IsFolder,@UnratedType,@TopParentId,@TrailerTypes,@CriticRating,@CleanName,@PresentationUniqueKey,@OriginalTitle,@PrimaryVersionId,@DateLastMediaAdded,@Album,@IsVirtualItem,@SeriesName,@UserDataKey,@SeasonName,@SeasonId,@SeriesId,@ExternalSeriesId,@Tagline,@ProviderIds,@Images,@ProductionLocations,@ExtraIds,@TotalBitrate,@ExtraType,@Artists,@AlbumArtists,@ExternalId,@SeriesPresentationUniqueKey,@ShowId,@OwnerId)"; + (guid,type,data,Path,StartDate,EndDate,ChannelId,IsMovie,IsSeries,EpisodeTitle,IsRepeat,CommunityRating,CustomRating,IndexNumber,IsLocked,Name,OfficialRating,MediaType,Overview,ParentIndexNumber,PremiereDate,ProductionYear,ParentId,Genres,InheritedParentalRatingValue,SortName,ForcedSortName,RunTimeTicks,Size,DateCreated,DateModified,PreferredMetadataLanguage,PreferredMetadataCountryCode,Width,Height,DateLastRefreshed,DateLastSaved,IsInMixedFolder,LockedFields,Studios,Audio,ExternalServiceId,Tags,IsFolder,UnratedType,TopParentId,TrailerTypes,CriticRating,CleanName,PresentationUniqueKey,OriginalTitle,PrimaryVersionId,DateLastMediaAdded,Album,LUFS,IsVirtualItem,SeriesName,UserDataKey,SeasonName,SeasonId,SeriesId,ExternalSeriesId,Tagline,ProviderIds,Images,ProductionLocations,ExtraIds,TotalBitrate,ExtraType,Artists,AlbumArtists,ExternalId,SeriesPresentationUniqueKey,ShowId,OwnerId) + values (@guid,@type,@data,@Path,@StartDate,@EndDate,@ChannelId,@IsMovie,@IsSeries,@EpisodeTitle,@IsRepeat,@CommunityRating,@CustomRating,@IndexNumber,@IsLocked,@Name,@OfficialRating,@MediaType,@Overview,@ParentIndexNumber,@PremiereDate,@ProductionYear,@ParentId,@Genres,@InheritedParentalRatingValue,@SortName,@ForcedSortName,@RunTimeTicks,@Size,@DateCreated,@DateModified,@PreferredMetadataLanguage,@PreferredMetadataCountryCode,@Width,@Height,@DateLastRefreshed,@DateLastSaved,@IsInMixedFolder,@LockedFields,@Studios,@Audio,@ExternalServiceId,@Tags,@IsFolder,@UnratedType,@TopParentId,@TrailerTypes,@CriticRating,@CleanName,@PresentationUniqueKey,@OriginalTitle,@PrimaryVersionId,@DateLastMediaAdded,@Album,@LUFS,@IsVirtualItem,@SeriesName,@UserDataKey,@SeasonName,@SeasonId,@SeriesId,@ExternalSeriesId,@Tagline,@ProviderIds,@Images,@ProductionLocations,@ExtraIds,@TotalBitrate,@ExtraType,@Artists,@AlbumArtists,@ExternalId,@SeriesPresentationUniqueKey,@ShowId,@OwnerId)"; private readonly IServerConfigurationManager _config; private readonly IServerApplicationHost _appHost; @@ -110,6 +110,7 @@ namespace Emby.Server.Implementations.Data "PrimaryVersionId", "DateLastMediaAdded", "Album", + "LUFS", "CriticRating", "IsVirtualItem", "SeriesName", @@ -489,6 +490,7 @@ namespace Emby.Server.Implementations.Data AddColumn(db, "TypedBaseItems", "PrimaryVersionId", "Text", existingColumnNames); AddColumn(db, "TypedBaseItems", "DateLastMediaAdded", "DATETIME", existingColumnNames); AddColumn(db, "TypedBaseItems", "Album", "Text", existingColumnNames); + AddColumn(db, "TypedBaseItems", "LUFS", "Float", existingColumnNames); AddColumn(db, "TypedBaseItems", "IsVirtualItem", "BIT", existingColumnNames); AddColumn(db, "TypedBaseItems", "SeriesName", "Text", existingColumnNames); AddColumn(db, "TypedBaseItems", "UserDataKey", "Text", existingColumnNames); @@ -906,6 +908,7 @@ namespace Emby.Server.Implementations.Data } saveItemStatement.TryBind("@Album", item.Album); + saveItemStatement.TryBind("@LUFS", item.LUFS); saveItemStatement.TryBind("@IsVirtualItem", item.IsVirtualItem); if (item is IHasSeries hasSeriesName) @@ -1756,6 +1759,11 @@ namespace Emby.Server.Implementations.Data item.Album = album; } + if (reader.TryGetSingle(index++, out var lUFS)) + { + item.LUFS = lUFS; + } + if (reader.TryGetSingle(index++, out var criticRating)) { item.CriticRating = criticRating; diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index 8fa2f05662..7a6ed2cb80 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -906,6 +906,7 @@ namespace Emby.Server.Implementations.Dto // Add audio info if (item is Audio audio) { + dto.LUFS = audio.LUFS; dto.Album = audio.Album; if (audio.ExtraType.HasValue) { diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index adc7b2f953..1e868194e6 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -128,6 +128,13 @@ namespace MediaBrowser.Controller.Entities [JsonIgnore] public string Album { get; set; } + /// <summary> + /// Gets or sets the LUFS value. + /// </summary> + /// <value>The LUFS Value.</value> + [JsonIgnore] + public float LUFS { get; set; } + /// <summary> /// Gets or sets the channel identifier. /// </summary> diff --git a/MediaBrowser.Model/Configuration/LibraryOptions.cs b/MediaBrowser.Model/Configuration/LibraryOptions.cs index 81f2f02bc5..df68299465 100644 --- a/MediaBrowser.Model/Configuration/LibraryOptions.cs +++ b/MediaBrowser.Model/Configuration/LibraryOptions.cs @@ -30,6 +30,8 @@ namespace MediaBrowser.Model.Configuration public bool EnableRealtimeMonitor { get; set; } + public bool EnableLUFSScan { get; set; } + public bool EnableChapterImageExtraction { get; set; } public bool ExtractChapterImagesDuringLibraryScan { get; set; } diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs index 2a86fded22..27154c2978 100644 --- a/MediaBrowser.Model/Dto/BaseItemDto.cs +++ b/MediaBrowser.Model/Dto/BaseItemDto.cs @@ -779,6 +779,12 @@ namespace MediaBrowser.Model.Dto /// <value>The timer identifier.</value> public string TimerId { get; set; } + /// <summary> + /// Gets or sets the LUFS value. + /// </summary> + /// <value>The LUFS Value.</value> + public float LUFS { get; set; } + /// <summary> /// Gets or sets the current program. /// </summary> diff --git a/MediaBrowser.Providers/MediaInfo/AudioFileProber.cs b/MediaBrowser.Providers/MediaInfo/AudioFileProber.cs index b8578c46f8..e1dcbc9939 100644 --- a/MediaBrowser.Providers/MediaInfo/AudioFileProber.cs +++ b/MediaBrowser.Providers/MediaInfo/AudioFileProber.cs @@ -1,6 +1,9 @@ using System; using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; using System.Linq; +using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using Jellyfin.Data.Enums; @@ -14,6 +17,7 @@ using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.MediaInfo; +using Microsoft.Extensions.Logging; using TagLib; namespace MediaBrowser.Providers.MediaInfo @@ -23,6 +27,10 @@ namespace MediaBrowser.Providers.MediaInfo /// </summary> public class AudioFileProber { + // Default LUFS value for use with the web interface, at -18db gain will be 1(no db gain). + private const float DefaultLUFSValue = -18; + + private readonly ILogger<AudioFileProber> _logger; private readonly IMediaEncoder _mediaEncoder; private readonly IItemRepository _itemRepo; private readonly ILibraryManager _libraryManager; @@ -31,16 +39,19 @@ namespace MediaBrowser.Providers.MediaInfo /// <summary> /// Initializes a new instance of the <see cref="AudioFileProber"/> class. /// </summary> + /// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param> /// <param name="mediaSourceManager">Instance of the <see cref="IMediaSourceManager"/> interface.</param> /// <param name="mediaEncoder">Instance of the <see cref="IMediaEncoder"/> interface.</param> /// <param name="itemRepo">Instance of the <see cref="IItemRepository"/> interface.</param> /// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param> public AudioFileProber( + ILogger<AudioFileProber> logger, IMediaSourceManager mediaSourceManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, ILibraryManager libraryManager) { + _logger = logger; _mediaEncoder = mediaEncoder; _itemRepo = itemRepo; _libraryManager = libraryManager; @@ -89,6 +100,54 @@ namespace MediaBrowser.Providers.MediaInfo Fetch(item, result, cancellationToken); } + var libraryOptions = _libraryManager.GetLibraryOptions(item); + + if (libraryOptions.EnableLUFSScan) + { + string output; + using (var process = new Process() + { + StartInfo = new ProcessStartInfo + { + FileName = _mediaEncoder.EncoderPath, + Arguments = $"-hide_banner -i \"{path}\" -af ebur128=framelog=verbose -f null -", + RedirectStandardOutput = false, + RedirectStandardError = true + }, + }) + { + try + { + process.Start(); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error starting ffmpeg"); + + throw; + } + + output = await process.StandardError.ReadToEndAsync(cancellationToken).ConfigureAwait(false); + cancellationToken.ThrowIfCancellationRequested(); + MatchCollection split = Regex.Matches(output, @"I:\s+(.*?)\s+LUFS"); + + if (split.Count != 0) + { + item.LUFS = float.Parse(split[0].Groups[1].ValueSpan, CultureInfo.InvariantCulture.NumberFormat); + } + else + { + item.LUFS = DefaultLUFSValue; + } + } + } + else + { + item.LUFS = DefaultLUFSValue; + } + + _logger.LogDebug("LUFS for {ItemName} is {LUFS}.", item.Name, item.LUFS); + return ItemUpdateType.MetadataImport; } @@ -196,6 +255,7 @@ namespace MediaBrowser.Providers.MediaInfo audio.Album = tags.Album; audio.IndexNumber = Convert.ToInt32(tags.Track); audio.ParentIndexNumber = Convert.ToInt32(tags.Disc); + if (tags.Year != 0) { var year = Convert.ToInt32(tags.Year); diff --git a/MediaBrowser.Providers/MediaInfo/ProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/ProbeProvider.cs index 2800219552..114a929753 100644 --- a/MediaBrowser.Providers/MediaInfo/ProbeProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/ProbeProvider.cs @@ -79,7 +79,7 @@ namespace MediaBrowser.Providers.MediaInfo NamingOptions namingOptions) { _logger = loggerFactory.CreateLogger<ProbeProvider>(); - _audioProber = new AudioFileProber(mediaSourceManager, mediaEncoder, itemRepo, libraryManager); + _audioProber = new AudioFileProber(loggerFactory.CreateLogger<AudioFileProber>(), mediaSourceManager, mediaEncoder, itemRepo, libraryManager); _audioResolver = new AudioResolver(loggerFactory.CreateLogger<AudioResolver>(), localization, mediaEncoder, fileSystem, namingOptions); _subtitleResolver = new SubtitleResolver(loggerFactory.CreateLogger<SubtitleResolver>(), localization, mediaEncoder, fileSystem, namingOptions); _videoProber = new FFProbeVideoInfo( -- cgit v1.2.3 From eb52af4e6ab0a81b026063b2dd43dd81a1a08b8e Mon Sep 17 00:00:00 2001 From: Shadowghost <Shadowghost@users.noreply.github.com> Date: Mon, 15 May 2023 14:45:33 +0200 Subject: Fix playlists library and migration (#9770) --- .../Library/UserViewManager.cs | 20 ++++++++++++----- .../Playlists/PlaylistManager.cs | 25 +++------------------- .../Playlists/PlaylistsFolder.cs | 6 ------ Jellyfin.Api/Controllers/ItemsController.cs | 7 ++++-- Jellyfin.Api/Controllers/PlaylistsController.cs | 10 --------- .../Migrations/Routines/FixPlaylistOwner.cs | 6 ++++-- .../Playlists/IPlaylistManager.cs | 7 +++--- 7 files changed, 30 insertions(+), 51 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/UserViewManager.cs b/Emby.Server.Implementations/Library/UserViewManager.cs index 17f1d1905f..2c3dc18574 100644 --- a/Emby.Server.Implementations/Library/UserViewManager.cs +++ b/Emby.Server.Implementations/Library/UserViewManager.cs @@ -46,10 +46,9 @@ namespace Emby.Server.Implementations.Library public Folder[] GetUserViews(UserViewQuery query) { var user = _userManager.GetUserById(query.UserId); - if (user is null) { - throw new ArgumentException("User Id specified in the query does not exist.", nameof(query)); + throw new ArgumentException("User id specified in the query does not exist.", nameof(query)); } var folders = _libraryManager.GetUserRootFolder() @@ -58,7 +57,6 @@ namespace Emby.Server.Implementations.Library .ToList(); var groupedFolders = new List<ICollectionFolder>(); - var list = new List<Folder>(); foreach (var folder in folders) @@ -66,6 +64,20 @@ namespace Emby.Server.Implementations.Library var collectionFolder = folder as ICollectionFolder; var folderViewType = collectionFolder?.CollectionType; + // Playlist library requires special handling because the folder only refrences user playlists + if (string.Equals(folderViewType, CollectionType.Playlists, StringComparison.OrdinalIgnoreCase)) + { + var items = folder.GetItemList(new InternalItemsQuery(user) + { + ParentId = folder.ParentId + }); + + if (!items.Any(item => item.IsVisible(user))) + { + continue; + } + } + if (UserView.IsUserSpecific(folder)) { list.Add(_libraryManager.GetNamedView(user, folder.Name, folder.Id, folderViewType, null)); @@ -132,14 +144,12 @@ namespace Emby.Server.Implementations.Library } var sorted = _libraryManager.Sort(list, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending).ToList(); - var orders = user.GetPreferenceValues<Guid>(PreferenceKind.OrderedViews); return list .OrderBy(i => { var index = Array.IndexOf(orders, i.Id); - if (index == -1 && i is UserView view && !view.DisplayParentId.Equals(default)) diff --git a/Emby.Server.Implementations/Playlists/PlaylistManager.cs b/Emby.Server.Implementations/Playlists/PlaylistManager.cs index adb8ac7327..702f8d45bc 100644 --- a/Emby.Server.Implementations/Playlists/PlaylistManager.cs +++ b/Emby.Server.Implementations/Playlists/PlaylistManager.cs @@ -67,9 +67,8 @@ namespace Emby.Server.Implementations.Playlists public async Task<PlaylistCreationResult> CreatePlaylist(PlaylistCreationRequest options) { var name = options.Name; - var folderName = _fileSystem.GetValidFilename(name); - var parentFolder = GetPlaylistsFolder(Guid.Empty); + var parentFolder = GetPlaylistsFolder(options.UserId); if (parentFolder is null) { throw new ArgumentException(nameof(parentFolder)); @@ -80,7 +79,6 @@ namespace Emby.Server.Implementations.Playlists foreach (var itemId in options.ItemIdList) { var item = _libraryManager.GetItemById(itemId); - if (item is null) { throw new ArgumentException("No item exists with the supplied Id"); @@ -121,7 +119,6 @@ namespace Emby.Server.Implementations.Playlists } var user = _userManager.GetUserById(options.UserId); - var path = Path.Combine(parentFolder.Path, folderName); path = GetTargetPath(path); @@ -130,7 +127,6 @@ namespace Emby.Server.Implementations.Playlists try { Directory.CreateDirectory(path); - var playlist = new Playlist { Name = name, @@ -140,7 +136,6 @@ namespace Emby.Server.Implementations.Playlists }; playlist.SetMediaType(options.MediaType); - parentFolder.AddChild(playlist); await playlist.RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(_fileSystem)) { ForceSave = true }, CancellationToken.None) @@ -326,7 +321,8 @@ namespace Emby.Server.Implementations.Playlists } } - private void SavePlaylistFile(Playlist item) + /// <inheritdoc /> + public void SavePlaylistFile(Playlist item) { // this is probably best done as a metadata provider // saving a file over itself will require some work to prevent this from happening when not needed @@ -564,20 +560,5 @@ namespace Emby.Server.Implementations.Playlists } } } - - /// <inheritdoc /> - public async Task UpdatePlaylistAsync(Playlist playlist) - { - var currentPlaylist = (Playlist)_libraryManager.GetItemById(playlist.Id); - currentPlaylist.OwnerUserId = playlist.OwnerUserId; - currentPlaylist.Shares = playlist.Shares; - - await playlist.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); - - if (currentPlaylist.IsFile) - { - SavePlaylistFile(currentPlaylist); - } - } } } diff --git a/Emby.Server.Implementations/Playlists/PlaylistsFolder.cs b/Emby.Server.Implementations/Playlists/PlaylistsFolder.cs index e2f2e436f2..5492097152 100644 --- a/Emby.Server.Implementations/Playlists/PlaylistsFolder.cs +++ b/Emby.Server.Implementations/Playlists/PlaylistsFolder.cs @@ -27,11 +27,6 @@ namespace Emby.Server.Implementations.Playlists [JsonIgnore] public override string CollectionType => MediaBrowser.Model.Entities.CollectionType.Playlists; - public override bool IsVisible(User user) - { - return base.IsVisible(user) && GetChildren(user, true).Any(); - } - protected override IEnumerable<BaseItem> GetEligibleChildrenForRecursiveChildren(User user) { return base.GetEligibleChildrenForRecursiveChildren(user).OfType<Playlist>(); @@ -47,7 +42,6 @@ namespace Emby.Server.Implementations.Playlists query.Recursive = true; query.IncludeItemTypes = new[] { BaseItemKind.Playlist }; - query.Parent = null; return LibraryManager.GetItemsResult(query); } diff --git a/Jellyfin.Api/Controllers/ItemsController.cs b/Jellyfin.Api/Controllers/ItemsController.cs index 377526729a..d4116116bd 100644 --- a/Jellyfin.Api/Controllers/ItemsController.cs +++ b/Jellyfin.Api/Controllers/ItemsController.cs @@ -503,6 +503,7 @@ public class ItemsController : BaseJellyfinApiController } } + query.Parent = null; result = folder.GetItems(query); } else @@ -511,10 +512,12 @@ public class ItemsController : BaseJellyfinApiController result = new QueryResult<BaseItem>(itemsArray); } + // result might include items not accessible by the user, DtoService will remove them + var accessibleItems = _dtoService.GetBaseItemDtos(result.Items, dtoOptions, user); return new QueryResult<BaseItemDto>( startIndex, - result.TotalRecordCount, - _dtoService.GetBaseItemDtos(result.Items, dtoOptions, user)); + accessibleItems.Count, + accessibleItems); } /// <summary> diff --git a/Jellyfin.Api/Controllers/PlaylistsController.cs b/Jellyfin.Api/Controllers/PlaylistsController.cs index 20995bf1b4..8d2a738d4a 100644 --- a/Jellyfin.Api/Controllers/PlaylistsController.cs +++ b/Jellyfin.Api/Controllers/PlaylistsController.cs @@ -65,14 +65,12 @@ public class PlaylistsController : BaseJellyfinApiController /// <param name="mediaType">The media type.</param> /// <param name="createPlaylistRequest">The create playlist payload.</param> /// <response code="200">Playlist created.</response> - /// <response code="403">User does not have permission to create playlists.</response> /// <returns> /// A <see cref="Task" /> that represents the asynchronous operation to create a playlist. /// The task result contains an <see cref="OkResult"/> indicating success. /// </returns> [HttpPost] [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status403Forbidden)] public async Task<ActionResult<PlaylistCreationResult>> CreatePlaylist( [FromQuery, ParameterObsolete] string? name, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder)), ParameterObsolete] IReadOnlyList<Guid> ids, @@ -105,11 +103,9 @@ public class PlaylistsController : BaseJellyfinApiController /// <param name="ids">Item id, comma delimited.</param> /// <param name="userId">The userId.</param> /// <response code="204">Items added to playlist.</response> - /// <response code="403">User does not have permission to add items to playlist.</response> /// <returns>An <see cref="NoContentResult"/> on success.</returns> [HttpPost("{playlistId}/Items")] [ProducesResponseType(StatusCodes.Status204NoContent)] - [ProducesResponseType(StatusCodes.Status403Forbidden)] public async Task<ActionResult> AddToPlaylist( [FromRoute, Required] Guid playlistId, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] ids, @@ -127,11 +123,9 @@ public class PlaylistsController : BaseJellyfinApiController /// <param name="itemId">The item id.</param> /// <param name="newIndex">The new index.</param> /// <response code="204">Item moved to new index.</response> - /// <response code="403">User does not have permission to move item.</response> /// <returns>An <see cref="NoContentResult"/> on success.</returns> [HttpPost("{playlistId}/Items/{itemId}/Move/{newIndex}")] [ProducesResponseType(StatusCodes.Status204NoContent)] - [ProducesResponseType(StatusCodes.Status403Forbidden)] public async Task<ActionResult> MoveItem( [FromRoute, Required] string playlistId, [FromRoute, Required] string itemId, @@ -147,11 +141,9 @@ public class PlaylistsController : BaseJellyfinApiController /// <param name="playlistId">The playlist id.</param> /// <param name="entryIds">The item ids, comma delimited.</param> /// <response code="204">Items removed.</response> - /// <response code="403">User does not have permission to get playlist.</response> /// <returns>An <see cref="NoContentResult"/> on success.</returns> [HttpDelete("{playlistId}/Items")] [ProducesResponseType(StatusCodes.Status204NoContent)] - [ProducesResponseType(StatusCodes.Status403Forbidden)] public async Task<ActionResult> RemoveFromPlaylist( [FromRoute, Required] string playlistId, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] entryIds) @@ -173,12 +165,10 @@ public class PlaylistsController : BaseJellyfinApiController /// <param name="imageTypeLimit">Optional. The max number of images to return, per image type.</param> /// <param name="enableImageTypes">Optional. The image types to include in the output.</param> /// <response code="200">Original playlist returned.</response> - /// <response code="403">User does not have permission to get playlist items.</response> /// <response code="404">Playlist not found.</response> /// <returns>The original playlist items.</returns> [HttpGet("{playlistId}/Items")] [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] public ActionResult<QueryResult<BaseItemDto>> GetPlaylistItems( [FromRoute, Required] Guid playlistId, diff --git a/Jellyfin.Server/Migrations/Routines/FixPlaylistOwner.cs b/Jellyfin.Server/Migrations/Routines/FixPlaylistOwner.cs index 1dd938b1be..cf31820034 100644 --- a/Jellyfin.Server/Migrations/Routines/FixPlaylistOwner.cs +++ b/Jellyfin.Server/Migrations/Routines/FixPlaylistOwner.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Threading; using Jellyfin.Data.Enums; using MediaBrowser.Controller.Entities; @@ -60,13 +61,14 @@ internal class FixPlaylistOwner : IMigrationRoutine { playlist.OwnerUserId = guid; playlist.Shares = shares.Where(x => x != firstEditShare).ToArray(); - _playlistManager.UpdatePlaylistAsync(playlist).GetAwaiter().GetResult(); + playlist.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).GetAwaiter().GetResult(); + _playlistManager.SavePlaylistFile(playlist); } } else { playlist.OpenAccess = true; - _playlistManager.UpdatePlaylistAsync(playlist).GetAwaiter().GetResult(); + playlist.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).GetAwaiter().GetResult(); } } } diff --git a/MediaBrowser.Controller/Playlists/IPlaylistManager.cs b/MediaBrowser.Controller/Playlists/IPlaylistManager.cs index d889436629..d1a51c2cf6 100644 --- a/MediaBrowser.Controller/Playlists/IPlaylistManager.cs +++ b/MediaBrowser.Controller/Playlists/IPlaylistManager.cs @@ -66,10 +66,9 @@ namespace MediaBrowser.Controller.Playlists Task RemovePlaylistsAsync(Guid userId); /// <summary> - /// Updates a playlist. + /// Saves a playlist. /// </summary> - /// <param name="playlist">The updated playlist.</param> - /// <returns>Task.</returns> - Task UpdatePlaylistAsync(Playlist playlist); + /// <param name="item">The playlist.</param> + void SavePlaylistFile(Playlist item); } } -- cgit v1.2.3 From c809c36b30fe9fa4be34f15089a5855c343a0f4e Mon Sep 17 00:00:00 2001 From: Cody Robibero <cody@robibe.ro> Date: Mon, 15 May 2023 06:55:28 -0600 Subject: Fix readonlyspan usage --- Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs index ca3e45707b..7645c6c52d 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs @@ -462,10 +462,10 @@ namespace Emby.Server.Implementations.LiveTv.Listings } StringBuilder str = new StringBuilder("[", 1 + (programIds.Count * 13)); - foreach (ReadOnlySpan<char> i in programIds) + foreach (var i in programIds) { str.Append('"') - .Append(i.Slice(0, 10)) + .Append(i[..10]) .Append("\","); } -- cgit v1.2.3 From 155f3856c05645ac70f98029d66e5d465e3798fd Mon Sep 17 00:00:00 2001 From: Bill Thornton <billt2006@gmail.com> Date: Mon, 15 May 2023 15:28:33 -0400 Subject: Use default files to remove index.html from url --- Emby.Server.Implementations/ConfigurationOptions.cs | 2 +- Jellyfin.Api/Middleware/BaseUrlRedirectionMiddleware.cs | 2 -- Jellyfin.Server/Startup.cs | 8 +++++++- 3 files changed, 8 insertions(+), 4 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ConfigurationOptions.cs b/Emby.Server.Implementations/ConfigurationOptions.cs index f0a4c8ffbd..630265dac9 100644 --- a/Emby.Server.Implementations/ConfigurationOptions.cs +++ b/Emby.Server.Implementations/ConfigurationOptions.cs @@ -14,7 +14,7 @@ namespace Emby.Server.Implementations public static Dictionary<string, string?> DefaultConfiguration => new Dictionary<string, string?> { { HostWebClientKey, bool.TrueString }, - { DefaultRedirectKey, "web/index.html" }, + { DefaultRedirectKey, "web/" }, { FfmpegProbeSizeKey, "1G" }, { FfmpegAnalyzeDurationKey, "200M" }, { PlaylistsAllowDuplicatesKey, bool.FalseString }, diff --git a/Jellyfin.Api/Middleware/BaseUrlRedirectionMiddleware.cs b/Jellyfin.Api/Middleware/BaseUrlRedirectionMiddleware.cs index 7bcc328aa1..2241c68e7a 100644 --- a/Jellyfin.Api/Middleware/BaseUrlRedirectionMiddleware.cs +++ b/Jellyfin.Api/Middleware/BaseUrlRedirectionMiddleware.cs @@ -48,8 +48,6 @@ public class BaseUrlRedirectionMiddleware if (string.IsNullOrEmpty(localPath) || string.Equals(localPath, baseUrlPrefix, StringComparison.OrdinalIgnoreCase) || string.Equals(localPath, baseUrlPrefix + "/", StringComparison.OrdinalIgnoreCase) - || string.Equals(localPath, baseUrlPrefix + "/web", StringComparison.OrdinalIgnoreCase) - || string.Equals(localPath, baseUrlPrefix + "/web/", StringComparison.OrdinalIgnoreCase) || !localPath.StartsWith(baseUrlPrefix, StringComparison.OrdinalIgnoreCase) ) { diff --git a/Jellyfin.Server/Startup.cs b/Jellyfin.Server/Startup.cs index 57efd5820b..6394800f75 100644 --- a/Jellyfin.Server/Startup.cs +++ b/Jellyfin.Server/Startup.cs @@ -182,7 +182,7 @@ namespace Jellyfin.Server // This must be injected before any path related middleware. mainApp.UsePathTrim(); - mainApp.UseStaticFiles(); + if (appConfig.HostWebClient()) { var extensionProvider = new FileExtensionContentTypeProvider(); @@ -190,6 +190,11 @@ namespace Jellyfin.Server // subtitles octopus requires .data, .mem files. extensionProvider.Mappings.Add(".data", MediaTypeNames.Application.Octet); extensionProvider.Mappings.Add(".mem", MediaTypeNames.Application.Octet); + mainApp.UseDefaultFiles(new DefaultFilesOptions + { + FileProvider = new PhysicalFileProvider(_serverConfigurationManager.ApplicationPaths.WebPath), + RequestPath = "/web" + }); mainApp.UseStaticFiles(new StaticFileOptions { FileProvider = new PhysicalFileProvider(_serverConfigurationManager.ApplicationPaths.WebPath), @@ -200,6 +205,7 @@ namespace Jellyfin.Server mainApp.UseRobotsRedirection(); } + mainApp.UseStaticFiles(); mainApp.UseAuthentication(); mainApp.UseJellyfinApiSwagger(_serverConfigurationManager); mainApp.UseQueryStringDecoding(); -- cgit v1.2.3 From 55b10d298c516c1f7ae297990f6a3f2816ca8920 Mon Sep 17 00:00:00 2001 From: Abhinivesh Vijayan <vabhinivesh@gmail.com> Date: Tue, 16 May 2023 17:39:06 +0000 Subject: Translated using Weblate (Malayalam) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ml/ --- Emby.Server.Implementations/Localization/Core/ml.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ml.json b/Emby.Server.Implementations/Localization/Core/ml.json index acc7746c12..0620fbcdb0 100644 --- a/Emby.Server.Implementations/Localization/Core/ml.json +++ b/Emby.Server.Implementations/Localization/Core/ml.json @@ -119,5 +119,7 @@ "Genres": "വിഭാഗങ്ങൾ", "Channels": "ചാനലുകൾ", "TaskOptimizeDatabaseDescription": "ഡാറ്റാബേസ് ചുരുക്കുകയും സ്വതന്ത്ര ഇടം വെട്ടിച്ചുരുക്കുകയും ചെയ്യുന്നു. ലൈബ്രറി സ്‌കാൻ ചെയ്‌തതിനുശേഷം അല്ലെങ്കിൽ ഡാറ്റാബേസ് പരിഷ്‌ക്കരണങ്ങളെ സൂചിപ്പിക്കുന്ന മറ്റ് മാറ്റങ്ങൾ ചെയ്‌തതിന് ശേഷം ഈ ടാസ്‌ക് പ്രവർത്തിപ്പിക്കുന്നത് പ്രകടനം മെച്ചപ്പെടുത്തും.", - "TaskOptimizeDatabase": "ഡാറ്റാബേസ് ഒപ്റ്റിമൈസ് ചെയ്യുക" + "TaskOptimizeDatabase": "ഡാറ്റാബേസ് ഒപ്റ്റിമൈസ് ചെയ്യുക", + "HearingImpaired": "കേൾവി തകരാറുകൾ", + "External": "പുറമേയുള്ള" } -- cgit v1.2.3 From 3aa03a46402ea4293f35058909b81c4e914e17f3 Mon Sep 17 00:00:00 2001 From: Anvesh <anveshsai8@gmail.com> Date: Wed, 17 May 2023 07:34:58 +0000 Subject: Translated using Weblate (Telugu) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/te/ --- .../Localization/Core/te.json | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/te.json b/Emby.Server.Implementations/Localization/Core/te.json index a9a8ceae0e..24168b6112 100644 --- a/Emby.Server.Implementations/Localization/Core/te.json +++ b/Emby.Server.Implementations/Localization/Core/te.json @@ -19,5 +19,24 @@ "Channels": "ఛానెల్‌లు", "Books": "పుస్తకాలు", "Artists": "కళాకారులు", - "Albums": "ఆల్బమ్‌లు" + "Albums": "ఆల్బమ్‌లు", + "HearingImpaired": "వినికిడి లోపం", + "HomeVideos": "హోమ్ వీడియోలు", + "AppDeviceValues": "అప్లికేషన్ : {0}, పరికరం: {1}", + "Application": "అప్లికేషన్", + "AuthenticationSucceededWithUserName": "విజయవంతంగా ఆమోదించబడింది", + "CameraImageUploadedFrom": "{0} నుండి కొత్త కెమెరా చిత్రం అప్‌లోడ్ చేయబడింది", + "ChapterNameValue": "అధ్యాయం", + "DeviceOfflineWithName": "{0} డిస్‌కనెక్ట్ చేయబడింది", + "DeviceOnlineWithName": "{0} కనెక్ట్ చేయబడింది", + "External": "బాహ్య", + "FailedLoginAttemptWithUserName": "{0} నుండి విఫలమైన లాగిన్ ప్రయత్నం", + "HeaderFavoriteAlbums": "ఇష్టమైన ఆల్బమ్‌లు", + "HeaderFavoriteArtists": "ఇష్టమైన కళాకారులు", + "HeaderFavoriteEpisodes": "ఇష్టమైన ఎపిసోడ్‌లు", + "HeaderFavoriteShows": "ఇష్టమైన ప్రదర్శనలు", + "HeaderFavoriteSongs": "ఇష్టమైన పాటలు", + "HeaderLiveTV": "ప్రత్యక్ష TV", + "HeaderNextUp": "తదుపరి", + "HeaderRecordingGroups": "రికార్డింగ్ గుంపులు" } -- cgit v1.2.3 From 6ddc449a89ac615deb8c6d736e9cac7af4e02b9c Mon Sep 17 00:00:00 2001 From: Shadowghost <Ghost_of_Stone@web.de> Date: Tue, 2 Aug 2022 17:46:38 +0200 Subject: Implement NFO named season parsing --- .../Library/Resolvers/TV/SeasonResolver.cs | 26 +++++--- MediaBrowser.Controller/Entities/TV/Series.cs | 4 ++ MediaBrowser.Providers/Manager/MetadataService.cs | 1 + MediaBrowser.Providers/TV/SeriesMetadataService.cs | 78 ++++++++++++++++------ .../Parsers/SeasonNfoParser.cs | 12 ++++ .../Parsers/SeriesNfoParser.cs | 15 +++++ 6 files changed, 107 insertions(+), 29 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs index 62a524d2eb..e9538a5c97 100644 --- a/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs @@ -81,14 +81,24 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV if (season.IndexNumber.HasValue) { var seasonNumber = season.IndexNumber.Value; - - season.Name = seasonNumber == 0 ? - args.LibraryOptions.SeasonZeroDisplayName : - string.Format( - CultureInfo.InvariantCulture, - _localization.GetLocalizedString("NameSeasonNumber"), - seasonNumber, - args.LibraryOptions.PreferredMetadataLanguage); + if (string.IsNullOrEmpty(season.Name)) + { + var seasonNames = series.SeasonNames; + if (seasonNames.TryGetValue(seasonNumber, out var seasonName)) + { + season.Name = seasonName; + } + else + { + season.Name = seasonNumber == 0 ? + args.LibraryOptions.SeasonZeroDisplayName : + string.Format( + CultureInfo.InvariantCulture, + _localization.GetLocalizedString("NameSeasonNumber"), + seasonNumber, + args.LibraryOptions.PreferredMetadataLanguage); + } + } } return season; diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs index e7a8a773ec..a49c1609d9 100644 --- a/MediaBrowser.Controller/Entities/TV/Series.cs +++ b/MediaBrowser.Controller/Entities/TV/Series.cs @@ -28,12 +28,16 @@ namespace MediaBrowser.Controller.Entities.TV public Series() { AirDays = Array.Empty<DayOfWeek>(); + SeasonNames = new Dictionary<int, string>(); } public DayOfWeek[] AirDays { get; set; } public string AirTime { get; set; } + [JsonIgnore] + public Dictionary<int, string> SeasonNames { get; set; } + [JsonIgnore] public override bool SupportsAddingToPlaylist => true; diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs index 80f77f7c3a..834ef29f51 100644 --- a/MediaBrowser.Providers/Manager/MetadataService.cs +++ b/MediaBrowser.Providers/Manager/MetadataService.cs @@ -12,6 +12,7 @@ using Jellyfin.Extensions; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Configuration; diff --git a/MediaBrowser.Providers/TV/SeriesMetadataService.cs b/MediaBrowser.Providers/TV/SeriesMetadataService.cs index 97f9383971..b99f1cbdbf 100644 --- a/MediaBrowser.Providers/TV/SeriesMetadataService.cs +++ b/MediaBrowser.Providers/TV/SeriesMetadataService.cs @@ -41,7 +41,7 @@ namespace MediaBrowser.Providers.TV RemoveObsoleteEpisodes(item); RemoveObsoleteSeasons(item); - await FillInMissingSeasonsAsync(item, cancellationToken).ConfigureAwait(false); + await UpdateAndCreateSeasonsAsync(item, cancellationToken).ConfigureAwait(false); } /// <inheritdoc /> @@ -67,6 +67,24 @@ namespace MediaBrowser.Providers.TV var sourceItem = source.Item; var targetItem = target.Item; + var sourceSeasonNames = sourceItem.SeasonNames; + var targetSeasonNames = targetItem.SeasonNames; + + if (replaceData || targetSeasonNames.Count == 0) + { + targetItem.SeasonNames = sourceSeasonNames; + } + else if (targetSeasonNames.Count != sourceSeasonNames.Count || !sourceSeasonNames.Keys.All(targetSeasonNames.ContainsKey)) + { + foreach (var season in sourceSeasonNames) + { + var seasonNumber = season.Key; + if (!targetSeasonNames.ContainsKey(seasonNumber)) + { + targetItem.SeasonNames[seasonNumber] = season.Value; + } + } + } if (replaceData || string.IsNullOrEmpty(targetItem.AirTime)) { @@ -86,7 +104,7 @@ namespace MediaBrowser.Providers.TV private void RemoveObsoleteSeasons(Series series) { - // TODO Legacy. It's not really "physical" seasons as any virtual seasons are always converted to non-virtual in FillInMissingSeasonsAsync. + // TODO Legacy. It's not really "physical" seasons as any virtual seasons are always converted to non-virtual in UpdateAndCreateSeasonsAsync. var physicalSeasonNumbers = new HashSet<int>(); var virtualSeasons = new List<Season>(); foreach (var existingSeason in series.Children.OfType<Season>()) @@ -177,36 +195,43 @@ namespace MediaBrowser.Providers.TV } /// <summary> - /// Creates seasons for all episodes that aren't in a season folder. + /// Creates seasons for all episodes if they don't exist. /// If no season number can be determined, a dummy season will be created. + /// Updates seasons names. /// </summary> /// <param name="series">The series.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>The async task.</returns> - private async Task FillInMissingSeasonsAsync(Series series, CancellationToken cancellationToken) + private async Task UpdateAndCreateSeasonsAsync(Series series, CancellationToken cancellationToken) { + var seasonNames = series.SeasonNames; var seriesChildren = series.GetRecursiveChildren(i => i is Episode || i is Season); - var episodesInSeriesFolder = seriesChildren + var seasons = seriesChildren.OfType<Season>().ToList(); + var uniqueSeasonNumbers = seriesChildren .OfType<Episode>() - .Where(i => !i.IsInSeasonFolder); - - List<Season> seasons = seriesChildren.OfType<Season>().ToList(); + .Select(e => e.ParentIndexNumber >= 0 ? e.ParentIndexNumber : null) + .Distinct(); // Loop through the unique season numbers - foreach (var episode in episodesInSeriesFolder) + foreach (var seasonNumber in uniqueSeasonNumbers) { // Null season numbers will have a 'dummy' season created because seasons are always required. - var seasonNumber = episode.ParentIndexNumber >= 0 ? episode.ParentIndexNumber : null; var existingSeason = seasons.FirstOrDefault(i => i.IndexNumber == seasonNumber); + string? seasonName = null; + + if (seasonNumber.HasValue && seasonNames.ContainsKey(seasonNumber.Value)) + { + seasonName = seasonNames[seasonNumber.Value]; + } if (existingSeason is null) { - var season = await CreateSeasonAsync(series, seasonNumber, cancellationToken).ConfigureAwait(false); - seasons.Add(season); + var season = await CreateSeasonAsync(series, seasonName, seasonNumber, cancellationToken).ConfigureAwait(false); + series.AddChild(season); } - else if (existingSeason.IsVirtualItem) + else { - existingSeason.IsVirtualItem = false; + existingSeason.Name = GetValidSeasonNameForSeries(series, seasonName, seasonNumber); await existingSeason.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, cancellationToken).ConfigureAwait(false); } } @@ -216,21 +241,17 @@ namespace MediaBrowser.Providers.TV /// Creates a new season, adds it to the database by linking it to the [series] and refreshes the metadata. /// </summary> /// <param name="series">The series.</param> + /// <param name="seasonName">The season name.</param> /// <param name="seasonNumber">The season number.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>The newly created season.</returns> private async Task<Season> CreateSeasonAsync( Series series, + string? seasonName, int? seasonNumber, CancellationToken cancellationToken) { - string seasonName = seasonNumber switch - { - null => _localizationManager.GetLocalizedString("NameSeasonUnknown"), - 0 => LibraryManager.GetLibraryOptions(series).SeasonZeroDisplayName, - _ => string.Format(CultureInfo.InvariantCulture, _localizationManager.GetLocalizedString("NameSeasonNumber"), seasonNumber.Value) - }; - + seasonName = GetValidSeasonNameForSeries(series, seasonName, seasonNumber); Logger.LogInformation("Creating Season {SeasonName} entry for {SeriesName}", seasonName, series.Name); var season = new Season @@ -251,5 +272,20 @@ namespace MediaBrowser.Providers.TV return season; } + + private string GetValidSeasonNameForSeries(Series series, string? seasonName, int? seasonNumber) + { + if (string.IsNullOrEmpty(seasonName)) + { + seasonName = seasonNumber switch + { + null => _localizationManager.GetLocalizedString("NameSeasonUnknown"), + 0 => LibraryManager.GetLibraryOptions(series).SeasonZeroDisplayName, + _ => string.Format(CultureInfo.InvariantCulture, _localizationManager.GetLocalizedString("NameSeasonNumber"), seasonNumber.Value) + }; + } + + return seasonName; + } } } diff --git a/MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs index 2f5fd40e2f..51d5f932bc 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs @@ -55,6 +55,18 @@ namespace MediaBrowser.XbmcMetadata.Parsers break; } + case "seasonname": + { + var name = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(name)) + { + item.Name = name; + } + + break; + } + default: base.FetchDataFromXmlNode(reader, itemResult); break; diff --git a/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs index 3011d65a6d..f22b861eba 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Globalization; using System.Xml; using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Entities.TV; @@ -110,6 +112,19 @@ namespace MediaBrowser.XbmcMetadata.Parsers break; } + case "namedseason": + { + var parsed = int.TryParse(reader.GetAttribute("number"), NumberStyles.Integer, CultureInfo.InvariantCulture, out var seasonNumber); + var name = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(name) && parsed) + { + item.SeasonNames[seasonNumber] = name; + } + + break; + } + default: base.FetchDataFromXmlNode(reader, itemResult); break; -- cgit v1.2.3 From bf90f502ae62c8aa0c63dca7907106b843cc6639 Mon Sep 17 00:00:00 2001 From: PakuDrag <iceclout@tutanota.com> Date: Sat, 20 May 2023 18:17:01 +0000 Subject: Translated using Weblate (Turkish) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/tr/ --- Emby.Server.Implementations/Localization/Core/tr.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/tr.json b/Emby.Server.Implementations/Localization/Core/tr.json index b802db9822..9a140f8712 100644 --- a/Emby.Server.Implementations/Localization/Core/tr.json +++ b/Emby.Server.Implementations/Localization/Core/tr.json @@ -123,5 +123,6 @@ "TaskOptimizeDatabase": "Veritabanını optimize et", "TaskKeyframeExtractorDescription": "Daha hassas HLS çalma listeleri oluşturmak için video dosyalarından kareleri çıkarır. Bu görev uzun bir süre çalışabilir.", "TaskKeyframeExtractor": "Kare Ayırt Edici", - "External": "Harici" + "External": "Harici", + "HearingImpaired": "Duyma engelli" } -- cgit v1.2.3 From 45e99d2523e0da8c642731eeb371d6c373c5856f Mon Sep 17 00:00:00 2001 From: Shimul Roy <stenasaha@gmail.com> Date: Sat, 20 May 2023 09:40:38 +0000 Subject: Translated using Weblate (Bengali) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/bn/ --- Emby.Server.Implementations/Localization/Core/bn.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/bn.json b/Emby.Server.Implementations/Localization/Core/bn.json index 355fb3b21e..005926231d 100644 --- a/Emby.Server.Implementations/Localization/Core/bn.json +++ b/Emby.Server.Implementations/Localization/Core/bn.json @@ -117,7 +117,7 @@ "Forced": "জোরকরে", "TaskCleanActivityLogDescription": "নির্ধারিত সময়ের আগের কাজের হিসাব মুছে দিন খালি করুন.", "TaskCleanActivityLog": "কাজের ফাইল খালি করুন", - "Default": "প্রাথমিক", + "Default": "ডিফল্ট", "HearingImpaired": "দুর্বল শ্রবণক্ষমতাধরদের জন্য", "TaskOptimizeDatabaseDescription": "তথ্যভাণ্ডার সুবিন্যস্ত করে ও অব্যবহৃত জায়গা ছেড়ে দেয়। লাইব্রেরী স্ক্যান অথবা যেকোনো তথ্যভাণ্ডার পরিবর্তনের পর এই প্রক্রিয়া চালালে তথ্যভাণ্ডারের তথ্য প্রদান দ্রুততর হতে পারে।", "External": "বাহ্যিক", -- cgit v1.2.3 From b44c9eb88eb2431295756c90b59f6751c35c6647 Mon Sep 17 00:00:00 2001 From: Bond_009 <bond.009@outlook.com> Date: Tue, 23 May 2023 16:23:25 +0200 Subject: Check for Imdb id for series --- Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs index 8f69175d07..d4f275bed4 100644 --- a/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs @@ -184,6 +184,12 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV { var justName = Path.GetFileName(path.AsSpan()); + var imdbId = justName.GetAttributeValue("imdbid"); + if (!string.IsNullOrEmpty(imdbId)) + { + item.SetProviderId(MetadataProvider.Imdb, imdbId); + } + var tvdbId = justName.GetAttributeValue("tvdbid"); if (!string.IsNullOrEmpty(tvdbId)) { -- cgit v1.2.3 From 81746666ded02f844605fb26bc1c0ac2dd650bb0 Mon Sep 17 00:00:00 2001 From: Shadowghost <Ghost_of_Stone@web.de> Date: Fri, 26 May 2023 10:59:49 +0200 Subject: Fix TotalRecordCount calculation --- Emby.Server.Implementations/Library/LibraryManager.cs | 9 ++++++++- Emby.Server.Implementations/Playlists/PlaylistsFolder.cs | 2 +- Jellyfin.Api/Controllers/ItemsController.cs | 6 ++---- MediaBrowser.Controller/Entities/Folder.cs | 4 ++-- 4 files changed, 13 insertions(+), 8 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 75a1a5a4d8..ea45bf0ba0 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -1264,7 +1264,14 @@ namespace Emby.Server.Implementations.Library AddUserToQuery(query, query.User, allowExternalContent); } - return _itemRepository.GetItemList(query); + var itemList = _itemRepository.GetItemList(query); + var user = query.User; + if (user is not null) + { + return itemList.Where(i => i.IsVisible(user)).ToList(); + } + + return itemList; } public List<BaseItem> GetItemList(InternalItemsQuery query) diff --git a/Emby.Server.Implementations/Playlists/PlaylistsFolder.cs b/Emby.Server.Implementations/Playlists/PlaylistsFolder.cs index 5492097152..d67caa52dc 100644 --- a/Emby.Server.Implementations/Playlists/PlaylistsFolder.cs +++ b/Emby.Server.Implementations/Playlists/PlaylistsFolder.cs @@ -42,7 +42,7 @@ namespace Emby.Server.Implementations.Playlists query.Recursive = true; query.IncludeItemTypes = new[] { BaseItemKind.Playlist }; - return LibraryManager.GetItemsResult(query); + return QueryWithPostFiltering2(query); } public override string GetClientTypeName() diff --git a/Jellyfin.Api/Controllers/ItemsController.cs b/Jellyfin.Api/Controllers/ItemsController.cs index d4116116bd..7650b861f4 100644 --- a/Jellyfin.Api/Controllers/ItemsController.cs +++ b/Jellyfin.Api/Controllers/ItemsController.cs @@ -512,12 +512,10 @@ public class ItemsController : BaseJellyfinApiController result = new QueryResult<BaseItem>(itemsArray); } - // result might include items not accessible by the user, DtoService will remove them - var accessibleItems = _dtoService.GetBaseItemDtos(result.Items, dtoOptions, user); return new QueryResult<BaseItemDto>( startIndex, - accessibleItems.Count, - accessibleItems); + result.TotalRecordCount, + _dtoService.GetBaseItemDtos(result.Items, dtoOptions, user)); } /// <summary> diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index bccb4107ff..84952295c4 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -730,7 +730,7 @@ namespace MediaBrowser.Controller.Entities return LibraryManager.GetItemsResult(query); } - private QueryResult<BaseItem> QueryWithPostFiltering2(InternalItemsQuery query) + protected QueryResult<BaseItem> QueryWithPostFiltering2(InternalItemsQuery query) { var startIndex = query.StartIndex; var limit = query.Limit; @@ -1272,7 +1272,7 @@ namespace MediaBrowser.Controller.Entities { ArgumentNullException.ThrowIfNull(user); - return GetChildren(user, includeLinkedChildren, null); + return GetChildren(user, includeLinkedChildren, new InternalItemsQuery(user)); } public virtual List<BaseItem> GetChildren(User user, bool includeLinkedChildren, InternalItemsQuery query) -- cgit v1.2.3 From 1bf78409361996621981392e7f27dfc377ce49cc Mon Sep 17 00:00:00 2001 From: pranelio <pramanauskas@icloud.com> Date: Fri, 26 May 2023 13:36:16 +0000 Subject: Translated using Weblate (Lithuanian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/lt/ --- Emby.Server.Implementations/Localization/Core/lt-LT.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/lt-LT.json b/Emby.Server.Implementations/Localization/Core/lt-LT.json index e1c937b6cd..ce8d8fc322 100644 --- a/Emby.Server.Implementations/Localization/Core/lt-LT.json +++ b/Emby.Server.Implementations/Localization/Core/lt-LT.json @@ -20,9 +20,9 @@ "HeaderFavoriteAlbums": "Mėgstami Albumai", "HeaderFavoriteArtists": "Mėgstami Atlikėjai", "HeaderFavoriteEpisodes": "Mėgstamiausios serijos", - "HeaderFavoriteShows": "Mėgstamiausi serialai", - "HeaderFavoriteSongs": "Mėgstamos dainos", - "HeaderLiveTV": "TV gyvai", + "HeaderFavoriteShows": "Mėgstamiausios TV Laidos", + "HeaderFavoriteSongs": "Mėgstamos Dainos", + "HeaderLiveTV": "Tiesioginė TV", "HeaderNextUp": "Toliau eilėje", "HeaderRecordingGroups": "Įrašų grupės", "HomeVideos": "Namų vaizdo įrašai", -- cgit v1.2.3 From 093c2e03562589174aeba10b21606d7b927a9f8b Mon Sep 17 00:00:00 2001 From: Bucky Patel <ghousshumail+weblatejellyfintranslate@gmail.com> Date: Thu, 1 Jun 2023 06:46:24 +0000 Subject: Translated using Weblate (Hindi) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/hi/ --- .../Localization/Core/hi.json | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/hi.json b/Emby.Server.Implementations/Localization/Core/hi.json index 4bbb1dd352..47d3eeac5d 100644 --- a/Emby.Server.Implementations/Localization/Core/hi.json +++ b/Emby.Server.Implementations/Localization/Core/hi.json @@ -104,5 +104,24 @@ "TaskCleanActivityLog": "क्रियाकलाप लॉग साफ करें", "TasksChannelsCategory": "इंटरनेट प्रणाली", "TasksApplicationCategory": "अनुप्रयोग", - "TaskRefreshPeople": "लोगोकी जानकारी ताज़ी करें" + "TaskRefreshPeople": "लोगोकी जानकारी ताज़ी करें", + "TaskKeyframeExtractor": "कीफ़्रेम एक्सट्रैक्टर", + "TaskCleanActivityLogDescription": "कॉन्फ़िगर की गई आयु से पुरानी गतिविधि लॉग प्रविष्टियां हटाता है।", + "TaskRefreshChapterImagesDescription": "अध्याय वाले वीडियो के लिए थंबनेल बनाता है।", + "TaskRefreshLibraryDescription": "नई फ़ाइलों के लिए आपकी मीडिया लाइब्रेरी को स्कैन करता है और मेटाडेटा को ताज़ा करता है।", + "TaskCleanLogs": "स्वच्छ लॉग निर्देशिका", + "TaskUpdatePluginsDescription": "प्लगइन्स के लिए अपडेट डाउनलोड और इंस्टॉल करें जो स्वचालित रूप से अपडेट करने के लिए कॉन्फ़िगर किए गए हैं।", + "TaskCleanTranscode": "स्वच्छ ट्रांसकोड निर्देशिका", + "TaskCleanTranscodeDescription": "एक दिन से अधिक पुरानी ट्रांसकोड फ़ाइलें हटाता है.", + "TaskRefreshChannelsDescription": "इंटरनेट चैनल की जानकारी को ताज़ा करता है।", + "TaskOptimizeDatabaseDescription": "डेटाबेस को कॉम्पैक्ट करता है और मुक्त स्थान को छोटा करता है। लाइब्रेरी को स्कैन करने के बाद इस कार्य को चलाने या अन्य परिवर्तन करने से जो डेटाबेस संशोधनों को लागू करते हैं, प्रदर्शन में सुधार कर सकते हैं।", + "TaskRefreshChannels": "इंटरनेट चैनल की जानकारी को ताज़ा करता है", + "TaskRefreshChapterImages": "अध्याय छवियाँ निकालें", + "TaskCleanLogsDescription": "{0} दिन से अधिक पुरानी लॉग फ़ाइलें हटाता है।", + "TaskCleanCacheDescription": "उन कैश फ़ाइलों को हटाता है जिनकी अब सिस्टम को आवश्यकता नहीं है।", + "TaskUpdatePlugins": "अद्यतन प्लगइन्स", + "TaskRefreshPeopleDescription": "आपकी मीडिया लाइब्रेरी में अभिनेताओं और निर्देशकों के लिए मेटाडेटा अपडेट करता है।", + "TaskCleanCache": "स्वच्छ कैश निर्देशिका", + "TaskDownloadMissingSubtitlesDescription": "मेटाडेटा कॉन्फ़िगरेशन के आधार पर लापता उपशीर्षक के लिए इंटरनेट खोजता है।", + "TaskKeyframeExtractorDescription": "अधिक सटीक एचएलएस प्लेलिस्ट बनाने के लिए वीडियो फ़ाइलों से मुख्य-फ़्रेम निकालता है। यह कार्य लंबे समय तक चल सकता है।" } -- cgit v1.2.3 From 29368a1566afcbfa861e3cf431156ee0f73f1f2b Mon Sep 17 00:00:00 2001 From: Mark Lopez <m@silvenga.com> Date: Mon, 5 Jun 2023 11:46:13 -0500 Subject: Source SQLite cache_size from an Environment Variable (#9666) --- Emby.Server.Implementations/ConfigurationOptions.cs | 5 +++-- Emby.Server.Implementations/Data/SqliteItemRepository.cs | 10 ++++++++-- .../Extensions/ConfigurationExtensions.cs | 13 +++++++++++++ 3 files changed, 24 insertions(+), 4 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ConfigurationOptions.cs b/Emby.Server.Implementations/ConfigurationOptions.cs index 630265dac9..f0c2676279 100644 --- a/Emby.Server.Implementations/ConfigurationOptions.cs +++ b/Emby.Server.Implementations/ConfigurationOptions.cs @@ -11,14 +11,15 @@ namespace Emby.Server.Implementations /// <summary> /// Gets a new copy of the default configuration options. /// </summary> - public static Dictionary<string, string?> DefaultConfiguration => new Dictionary<string, string?> + public static Dictionary<string, string?> DefaultConfiguration => new() { { HostWebClientKey, bool.TrueString }, { DefaultRedirectKey, "web/" }, { FfmpegProbeSizeKey, "1G" }, { FfmpegAnalyzeDurationKey, "200M" }, { PlaylistsAllowDuplicatesKey, bool.FalseString }, - { BindToUnixSocketKey, bool.FalseString } + { BindToUnixSocketKey, bool.FalseString }, + { SqliteCacheSizeKey, "20000" } }; } } diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index c32e7c75ad..ca8f605a02 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -25,6 +25,7 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Extensions; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.Persistence; @@ -34,6 +35,7 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.Querying; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using SQLitePCL.pretty; @@ -319,13 +321,15 @@ namespace Emby.Server.Implementations.Data /// <param name="logger">Instance of the <see cref="ILogger{SqliteItemRepository}"/> interface.</param> /// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param> /// <param name="imageProcessor">Instance of the <see cref="IImageProcessor"/> interface.</param> + /// <param name="configuration">Instance of the <see cref="IConfiguration"/> interface.</param> /// <exception cref="ArgumentNullException">config is null.</exception> public SqliteItemRepository( IServerConfigurationManager config, IServerApplicationHost appHost, ILogger<SqliteItemRepository> logger, ILocalizationManager localization, - IImageProcessor imageProcessor) + IImageProcessor imageProcessor, + IConfiguration configuration) : base(logger) { _config = config; @@ -337,11 +341,13 @@ namespace Emby.Server.Implementations.Data _jsonOptions = JsonDefaults.Options; DbFilePath = Path.Combine(_config.ApplicationPaths.DataPath, "library.db"); + + CacheSize = configuration.GetSqliteCacheSize(); ReadConnectionsCount = Environment.ProcessorCount * 2; } /// <inheritdoc /> - protected override int? CacheSize => 20000; + protected override int? CacheSize { get; } /// <inheritdoc /> protected override TempStoreMode TempStore => TempStoreMode.Memory; diff --git a/MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs b/MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs index 3b5e8ece71..6c58064ce9 100644 --- a/MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs +++ b/MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs @@ -59,6 +59,11 @@ namespace MediaBrowser.Controller.Extensions /// </summary> public const string UnixSocketPermissionsKey = "kestrel:socketPermissions"; + /// <summary> + /// The cache size of the SQL database, see cache_size. + /// </summary> + public const string SqliteCacheSizeKey = "sqlite:cacheSize"; + /// <summary> /// Gets a value indicating whether the application should host static web content from the <see cref="IConfiguration"/>. /// </summary> @@ -115,5 +120,13 @@ namespace MediaBrowser.Controller.Extensions /// <returns>The unix socket permissions.</returns> public static string? GetUnixSocketPermissions(this IConfiguration configuration) => configuration[UnixSocketPermissionsKey]; + + /// <summary> + /// Gets the cache_size from the <see cref="IConfiguration" />. + /// </summary> + /// <param name="configuration">The configuration to read the setting from.</param> + /// <returns>The sqlite cache size.</returns> + public static int? GetSqliteCacheSize(this IConfiguration configuration) + => configuration.GetValue<int?>(SqliteCacheSizeKey); } } -- cgit v1.2.3