diff options
44 files changed, 454 insertions, 276 deletions
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 14267b561..18b6834ef 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -566,10 +566,8 @@ namespace Emby.Server.Implementations serviceCollection.AddTransient(provider => new Lazy<IDtoService>(provider.GetRequiredService<IDtoService>)); // TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required - // TODO: Add StartupOptions.FFmpegPath to IConfiguration and remove this custom activation serviceCollection.AddTransient(provider => new Lazy<EncodingHelper>(provider.GetRequiredService<EncodingHelper>)); - serviceCollection.AddSingleton<IMediaEncoder>(provider => - ActivatorUtilities.CreateInstance<MediaBrowser.MediaEncoding.Encoder.MediaEncoder>(provider, _startupOptions.FFmpegPath ?? string.Empty)); + serviceCollection.AddSingleton<IMediaEncoder, MediaBrowser.MediaEncoding.Encoder.MediaEncoder>(); // TODO: Refactor to eliminate the circular dependencies here so that Lazy<T> isn't required serviceCollection.AddTransient(provider => new Lazy<ILibraryMonitor>(provider.GetRequiredService<ILibraryMonitor>)); diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs index 8fb9520d6..ac2edc1e2 100644 --- a/Emby.Server.Implementations/Collections/CollectionManager.cs +++ b/Emby.Server.Implementations/Collections/CollectionManager.cs @@ -363,60 +363,4 @@ namespace Emby.Server.Implementations.Collections return results.Values; } } - - /// <summary> - /// The collection manager entry point. - /// </summary> - public sealed class CollectionManagerEntryPoint : IServerEntryPoint - { - private readonly CollectionManager _collectionManager; - private readonly IServerConfigurationManager _config; - private readonly ILogger<CollectionManagerEntryPoint> _logger; - - /// <summary> - /// Initializes a new instance of the <see cref="CollectionManagerEntryPoint"/> class. - /// </summary> - /// <param name="collectionManager">The collection manager.</param> - /// <param name="config">The server configuration manager.</param> - /// <param name="logger">The logger.</param> - public CollectionManagerEntryPoint( - ICollectionManager collectionManager, - IServerConfigurationManager config, - ILogger<CollectionManagerEntryPoint> logger) - { - _collectionManager = (CollectionManager)collectionManager; - _config = config; - _logger = logger; - } - - /// <inheritdoc /> - public async Task RunAsync() - { - if (!_config.Configuration.CollectionsUpgraded && _config.Configuration.IsStartupWizardCompleted) - { - var path = _collectionManager.GetCollectionsFolderPath(); - - if (Directory.Exists(path)) - { - try - { - await _collectionManager.EnsureLibraryFolder(path, true).ConfigureAwait(false); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error creating camera uploads library"); - } - - _config.Configuration.CollectionsUpgraded = true; - _config.SaveConfiguration(); - } - } - } - - /// <inheritdoc /> - public void Dispose() - { - // Nothing to dispose - } - } } diff --git a/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs b/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs index 94ee1ced7..a15295fca 100644 --- a/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs +++ b/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs @@ -109,7 +109,6 @@ namespace Emby.Server.Implementations.Configuration if (!string.IsNullOrWhiteSpace(newPath) && !string.Equals(Configuration.CertificatePath, newPath, StringComparison.Ordinal)) { - // Validate if (!File.Exists(newPath)) { throw new FileNotFoundException( @@ -133,7 +132,6 @@ namespace Emby.Server.Implementations.Configuration if (!string.IsNullOrWhiteSpace(newPath) && !string.Equals(Configuration.MetadataPath, newPath, StringComparison.Ordinal)) { - // Validate if (!Directory.Exists(newPath)) { throw new DirectoryNotFoundException( @@ -146,60 +144,5 @@ namespace Emby.Server.Implementations.Configuration EnsureWriteAccess(newPath); } } - - /// <summary> - /// Sets all configuration values to their optimal values. - /// </summary> - /// <returns>If the configuration changed.</returns> - public bool SetOptimalValues() - { - var config = Configuration; - - var changed = false; - - if (!config.EnableCaseSensitiveItemIds) - { - config.EnableCaseSensitiveItemIds = true; - changed = true; - } - - if (!config.SkipDeserializationForBasicTypes) - { - config.SkipDeserializationForBasicTypes = true; - changed = true; - } - - if (!config.EnableSimpleArtistDetection) - { - config.EnableSimpleArtistDetection = true; - changed = true; - } - - if (!config.EnableNormalizedItemByNameIds) - { - config.EnableNormalizedItemByNameIds = true; - changed = true; - } - - if (!config.DisableLiveTvChannelUserDataName) - { - config.DisableLiveTvChannelUserDataName = true; - changed = true; - } - - if (!config.EnableNewOmdbSupport) - { - config.EnableNewOmdbSupport = true; - changed = true; - } - - if (!config.CollectionsUpgraded) - { - config.CollectionsUpgraded = true; - changed = true; - } - - return changed; - } } } diff --git a/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs b/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs index e140009ea..77b2c0a69 100644 --- a/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs +++ b/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using MediaBrowser.Controller; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Resolvers; @@ -13,19 +14,28 @@ namespace Emby.Server.Implementations.Library public class CoreResolutionIgnoreRule : IResolverIgnoreRule { private readonly ILibraryManager _libraryManager; + private readonly IServerApplicationPaths _serverApplicationPaths; /// <summary> /// Initializes a new instance of the <see cref="CoreResolutionIgnoreRule"/> class. /// </summary> /// <param name="libraryManager">The library manager.</param> - public CoreResolutionIgnoreRule(ILibraryManager libraryManager) + /// <param name="serverApplicationPaths">The server application paths.</param> + public CoreResolutionIgnoreRule(ILibraryManager libraryManager, IServerApplicationPaths serverApplicationPaths) { _libraryManager = libraryManager; + _serverApplicationPaths = serverApplicationPaths; } /// <inheritdoc /> public bool ShouldIgnore(FileSystemMetadata fileInfo, BaseItem parent) { + // Don't ignore application folders + if (fileInfo.FullName.Contains(_serverApplicationPaths.RootFolderPath, StringComparison.InvariantCulture)) + { + return false; + } + // Don't ignore top level folders if (fileInfo.IsDirectory && parent is AggregateFolder) { diff --git a/Emby.Server.Implementations/Library/IgnorePatterns.cs b/Emby.Server.Implementations/Library/IgnorePatterns.cs index 8c4098948..6e6ef1359 100644 --- a/Emby.Server.Implementations/Library/IgnorePatterns.cs +++ b/Emby.Server.Implementations/Library/IgnorePatterns.cs @@ -1,3 +1,6 @@ +#nullable enable + +using System; using System.Linq; using DotNet.Globbing; @@ -11,7 +14,7 @@ namespace Emby.Server.Implementations.Library /// <summary> /// Files matching these glob patterns will be ignored. /// </summary> - public static readonly string[] Patterns = new string[] + private static readonly string[] _patterns = { "**/small.jpg", "**/albumart.jpg", @@ -19,32 +22,51 @@ namespace Emby.Server.Implementations.Library // Directories "**/metadata/**", + "**/metadata", "**/ps3_update/**", + "**/ps3_update", "**/ps3_vprm/**", + "**/ps3_vprm", "**/extrafanart/**", + "**/extrafanart", "**/extrathumbs/**", + "**/extrathumbs", "**/.actors/**", + "**/.actors", "**/.wd_tv/**", + "**/.wd_tv", "**/lost+found/**", + "**/lost+found", // WMC temp recording directories that will constantly be written to "**/TempRec/**", + "**/TempRec", "**/TempSBE/**", + "**/TempSBE", // Synology "**/eaDir/**", + "**/eaDir", "**/@eaDir/**", + "**/@eaDir", "**/#recycle/**", + "**/#recycle", // Qnap "**/@Recycle/**", + "**/@Recycle", "**/.@__thumb/**", + "**/.@__thumb", "**/$RECYCLE.BIN/**", + "**/$RECYCLE.BIN", "**/System Volume Information/**", + "**/System Volume Information", "**/.grab/**", + "**/.grab", // Unix hidden files and directories "**/.*/**", + "**/.*", // thumbs.db "**/thumbs.db", @@ -56,19 +78,31 @@ namespace Emby.Server.Implementations.Library private static readonly GlobOptions _globOptions = new GlobOptions { - Evaluation = { + Evaluation = + { CaseInsensitive = true } }; - private static readonly Glob[] _globs = Patterns.Select(p => Glob.Parse(p, _globOptions)).ToArray(); + private static readonly Glob[] _globs = _patterns.Select(p => Glob.Parse(p, _globOptions)).ToArray(); /// <summary> /// Returns true if the supplied path should be ignored. /// </summary> - public static bool ShouldIgnore(string path) + /// <param name="path">The path to test.</param> + /// <returns>Whether to ignore the path.</returns> + public static bool ShouldIgnore(ReadOnlySpan<char> path) { - return _globs.Any(g => g.IsMatch(path)); + int len = _globs.Length; + for (int i = 0; i < len; i++) + { + if (_globs[i].IsMatch(path)) + { + return true; + } + } + + return false; } } } diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 6a20a015a..77d44e131 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -514,8 +514,8 @@ namespace Emby.Server.Implementations.Library return key.GetMD5(); } - public BaseItem ResolvePath(FileSystemMetadata fileInfo, Folder parent = null, bool allowIgnorePath = true) - => ResolvePath(fileInfo, new DirectoryService(_fileSystem), null, parent, allowIgnorePath: allowIgnorePath); + public BaseItem ResolvePath(FileSystemMetadata fileInfo, Folder parent = null) + => ResolvePath(fileInfo, new DirectoryService(_fileSystem), null, parent); private BaseItem ResolvePath( FileSystemMetadata fileInfo, @@ -523,8 +523,7 @@ namespace Emby.Server.Implementations.Library IItemResolver[] resolvers, Folder parent = null, string collectionType = null, - LibraryOptions libraryOptions = null, - bool allowIgnorePath = true) + LibraryOptions libraryOptions = null) { if (fileInfo == null) { @@ -548,7 +547,7 @@ namespace Emby.Server.Implementations.Library }; // Return null if ignore rules deem that we should do so - if (allowIgnorePath && IgnoreFile(args.FileInfo, args.Parent)) + if (IgnoreFile(args.FileInfo, args.Parent)) { return null; } @@ -713,7 +712,7 @@ namespace Emby.Server.Implementations.Library Directory.CreateDirectory(rootFolderPath); var rootFolder = GetItemById(GetNewItemId(rootFolderPath, typeof(AggregateFolder))) as AggregateFolder ?? - ((Folder) ResolvePath(_fileSystem.GetDirectoryInfo(rootFolderPath), allowIgnorePath: false)) + ((Folder) ResolvePath(_fileSystem.GetDirectoryInfo(rootFolderPath))) .DeepCopy<Folder, AggregateFolder>(); // In case program data folder was moved @@ -795,7 +794,7 @@ namespace Emby.Server.Implementations.Library if (tmpItem == null) { _logger.LogDebug("Creating new userRootFolder with DeepCopy"); - tmpItem = ((Folder)ResolvePath(_fileSystem.GetDirectoryInfo(userRootPath), allowIgnorePath: false)).DeepCopy<Folder, UserRootFolder>(); + tmpItem = ((Folder)ResolvePath(_fileSystem.GetDirectoryInfo(userRootPath))).DeepCopy<Folder, UserRootFolder>(); } // In case program data folder was moved @@ -1894,9 +1893,19 @@ namespace Emby.Server.Implementations.Library } } - ImageDimensions size = _imageProcessor.GetImageDimensions(item, image); - image.Width = size.Width; - image.Height = size.Height; + try + { + ImageDimensions size = _imageProcessor.GetImageDimensions(item, image); + image.Width = size.Width; + image.Height = size.Height; + } + catch (Exception ex) + { + _logger.LogError(ex, "Cannnot get image dimensions for {0}", image.Path); + image.Width = 0; + image.Height = 0; + continue; + } try { diff --git a/Emby.Server.Implementations/Localization/Core/ar.json b/Emby.Server.Implementations/Localization/Core/ar.json index d68928fce..4eac8e75d 100644 --- a/Emby.Server.Implementations/Localization/Core/ar.json +++ b/Emby.Server.Implementations/Localization/Core/ar.json @@ -1,5 +1,5 @@ { - "Albums": "ألبومات", + "Albums": "البومات", "AppDeviceValues": "تطبيق: {0}, جهاز: {1}", "Application": "تطبيق", "Artists": "الفنانين", @@ -14,7 +14,7 @@ "FailedLoginAttemptWithUserName": "عملية تسجيل الدخول فشلت من {0}", "Favorites": "المفضلة", "Folders": "المجلدات", - "Genres": "الأنواع", + "Genres": "التضنيفات", "HeaderAlbumArtists": "فناني الألبومات", "HeaderCameraUploads": "تحميلات الكاميرا", "HeaderContinueWatching": "استئناف", @@ -50,7 +50,7 @@ "NotificationOptionAudioPlayback": "بدأ تشغيل المقطع الصوتي", "NotificationOptionAudioPlaybackStopped": "تم إيقاف تشغيل المقطع الصوتي", "NotificationOptionCameraImageUploaded": "تم رفع صورة الكاميرا", - "NotificationOptionInstallationFailed": "فشل في التثبيت", + "NotificationOptionInstallationFailed": "فشل التثبيت", "NotificationOptionNewLibraryContent": "تم إضافة محتوى جديد", "NotificationOptionPluginError": "فشل في البرنامج المضاف", "NotificationOptionPluginInstalled": "تم تثبيت الملحق", diff --git a/Emby.Server.Implementations/Localization/Core/es-AR.json b/Emby.Server.Implementations/Localization/Core/es-AR.json index fc9a10f27..ac96c788c 100644 --- a/Emby.Server.Implementations/Localization/Core/es-AR.json +++ b/Emby.Server.Implementations/Localization/Core/es-AR.json @@ -20,7 +20,7 @@ "HeaderContinueWatching": "Seguir viendo", "HeaderFavoriteAlbums": "Álbumes favoritos", "HeaderFavoriteArtists": "Artistas favoritos", - "HeaderFavoriteEpisodes": "Episodios favoritos", + "HeaderFavoriteEpisodes": "Capítulos favoritos", "HeaderFavoriteShows": "Programas favoritos", "HeaderFavoriteSongs": "Canciones favoritas", "HeaderLiveTV": "TV en vivo", diff --git a/Emby.Server.Implementations/Localization/Core/es-MX.json b/Emby.Server.Implementations/Localization/Core/es-MX.json index 20b37ec9f..4ba324aa1 100644 --- a/Emby.Server.Implementations/Localization/Core/es-MX.json +++ b/Emby.Server.Implementations/Localization/Core/es-MX.json @@ -31,7 +31,7 @@ "ItemAddedWithName": "{0} fue agregado a la biblioteca", "ItemRemovedWithName": "{0} fue removido de la biblioteca", "LabelIpAddressValue": "Dirección IP: {0}", - "LabelRunningTimeValue": "Duración: {0}", + "LabelRunningTimeValue": "Tiempo de reproducción: {0}", "Latest": "Recientes", "MessageApplicationUpdated": "El servidor Jellyfin ha sido actualizado", "MessageApplicationUpdatedTo": "El servidor Jellyfin ha sido actualizado a {0}", diff --git a/Emby.Server.Implementations/Localization/Core/hr.json b/Emby.Server.Implementations/Localization/Core/hr.json index c169a35e7..97c77017b 100644 --- a/Emby.Server.Implementations/Localization/Core/hr.json +++ b/Emby.Server.Implementations/Localization/Core/hr.json @@ -5,23 +5,23 @@ "Artists": "Izvođači", "AuthenticationSucceededWithUserName": "{0} uspješno ovjerena", "Books": "Knjige", - "CameraImageUploadedFrom": "A new camera image has been uploaded from {0}", + "CameraImageUploadedFrom": "Nova fotografija sa kamere je uploadana iz {0}", "Channels": "Kanali", "ChapterNameValue": "Poglavlje {0}", "Collections": "Kolekcije", "DeviceOfflineWithName": "{0} se odspojilo", "DeviceOnlineWithName": "{0} je spojeno", "FailedLoginAttemptWithUserName": "Neuspjeli pokušaj prijave za {0}", - "Favorites": "Omiljeni", + "Favorites": "Favoriti", "Folders": "Mape", "Genres": "Žanrovi", - "HeaderAlbumArtists": "Izvođači albuma", - "HeaderCameraUploads": "Camera Uploads", - "HeaderContinueWatching": "Continue Watching", + "HeaderAlbumArtists": "Izvođači na albumu", + "HeaderCameraUploads": "Uvoz sa kamere", + "HeaderContinueWatching": "Nastavi gledati", "HeaderFavoriteAlbums": "Omiljeni albumi", "HeaderFavoriteArtists": "Omiljeni izvođači", "HeaderFavoriteEpisodes": "Omiljene epizode", - "HeaderFavoriteShows": "Omiljene emisije", + "HeaderFavoriteShows": "Omiljene serije", "HeaderFavoriteSongs": "Omiljene pjesme", "HeaderLiveTV": "TV uživo", "HeaderNextUp": "Sljedeće je", @@ -34,23 +34,23 @@ "LabelRunningTimeValue": "Vrijeme rada: {0}", "Latest": "Najnovije", "MessageApplicationUpdated": "Jellyfin Server je ažuriran", - "MessageApplicationUpdatedTo": "Jellyfin Server has been updated to {0}", + "MessageApplicationUpdatedTo": "Jellyfin Server je ažuriran na {0}", "MessageNamedServerConfigurationUpdatedWithValue": "Odjeljak postavka servera {0} je ažuriran", "MessageServerConfigurationUpdated": "Postavke servera su ažurirane", "MixedContent": "Miješani sadržaj", "Movies": "Filmovi", "Music": "Glazba", "MusicVideos": "Glazbeni spotovi", - "NameInstallFailed": "{0} installation failed", + "NameInstallFailed": "{0} neuspješnih instalacija", "NameSeasonNumber": "Sezona {0}", - "NameSeasonUnknown": "Season Unknown", - "NewVersionIsAvailable": "A new version of Jellyfin Server is available for download.", + "NameSeasonUnknown": "Nepoznata sezona", + "NewVersionIsAvailable": "Nova verzija Jellyfin servera je dostupna za preuzimanje.", "NotificationOptionApplicationUpdateAvailable": "Dostupno ažuriranje aplikacije", "NotificationOptionApplicationUpdateInstalled": "Instalirano ažuriranje aplikacije", "NotificationOptionAudioPlayback": "Reprodukcija glazbe započeta", "NotificationOptionAudioPlaybackStopped": "Reprodukcija audiozapisa je zaustavljena", "NotificationOptionCameraImageUploaded": "Slike kamere preuzete", - "NotificationOptionInstallationFailed": "Instalacija nije izvršena", + "NotificationOptionInstallationFailed": "Instalacija neuspješna", "NotificationOptionNewLibraryContent": "Novi sadržaj je dodan", "NotificationOptionPluginError": "Dodatak otkazao", "NotificationOptionPluginInstalled": "Dodatak instaliran", @@ -62,7 +62,7 @@ "NotificationOptionVideoPlayback": "Reprodukcija videa započeta", "NotificationOptionVideoPlaybackStopped": "Reprodukcija videozapisa je zaustavljena", "Photos": "Slike", - "Playlists": "Popisi", + "Playlists": "Popis za reprodukciju", "Plugin": "Dodatak", "PluginInstalledWithName": "{0} je instalirano", "PluginUninstalledWithName": "{0} je deinstalirano", @@ -70,15 +70,15 @@ "ProviderValue": "Pružitelj: {0}", "ScheduledTaskFailedWithName": "{0} neuspjelo", "ScheduledTaskStartedWithName": "{0} pokrenuto", - "ServerNameNeedsToBeRestarted": "{0} needs to be restarted", - "Shows": "Shows", + "ServerNameNeedsToBeRestarted": "{0} treba biti ponovno pokrenuto", + "Shows": "Serije", "Songs": "Pjesme", "StartupEmbyServerIsLoading": "Jellyfin Server se učitava. Pokušajte ponovo kasnije.", "SubtitleDownloadFailureForItem": "Titlovi prijevoda nisu preuzeti za {0}", - "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}", + "SubtitleDownloadFailureFromForItem": "Prijevodi nisu uspješno preuzeti {0} od {1}", "Sync": "Sink.", "System": "Sistem", - "TvShows": "TV Shows", + "TvShows": "Serije", "User": "Korisnik", "UserCreatedWithName": "Korisnik {0} je stvoren", "UserDeletedWithName": "Korisnik {0} je obrisan", @@ -87,10 +87,10 @@ "UserOfflineFromDevice": "{0} se odspojilo od {1}", "UserOnlineFromDevice": "{0} je online od {1}", "UserPasswordChangedWithName": "Lozinka je promijenjena za korisnika {0}", - "UserPolicyUpdatedWithName": "User policy has been updated for {0}", + "UserPolicyUpdatedWithName": "Pravila za korisnika su ažurirana za {0}", "UserStartedPlayingItemWithValues": "{0} je pokrenuo {1}", "UserStoppedPlayingItemWithValues": "{0} je zaustavio {1}", - "ValueHasBeenAddedToLibrary": "{0} has been added to your media library", + "ValueHasBeenAddedToLibrary": "{0} je dodano u medijsku biblioteku", "ValueSpecialEpisodeName": "Specijal - {0}", "VersionNumber": "Verzija {0}", "TaskRefreshLibraryDescription": "Skenira vašu medijsku knjižnicu sa novim datotekama i osvježuje metapodatke.", @@ -100,5 +100,19 @@ "TaskCleanCacheDescription": "Briše priručne datoteke nepotrebne za sistem.", "TaskCleanCache": "Očisti priručnu memoriju", "TasksApplicationCategory": "Aplikacija", - "TasksMaintenanceCategory": "Održavanje" + "TasksMaintenanceCategory": "Održavanje", + "TaskDownloadMissingSubtitlesDescription": "Pretraživanje interneta za prijevodima koji nedostaju bazirano na konfiguraciji meta podataka.", + "TaskDownloadMissingSubtitles": "Preuzimanje prijevoda koji nedostaju", + "TaskRefreshChannelsDescription": "Osvježava informacije o internet kanalima.", + "TaskRefreshChannels": "Osvježi kanale", + "TaskCleanTranscodeDescription": "Briše transkodirane fajlove starije od jednog dana.", + "TaskCleanTranscode": "Očisti direktorij za transkodiranje", + "TaskUpdatePluginsDescription": "Preuzima i instalira ažuriranja za dodatke koji su podešeni da se ažuriraju automatski.", + "TaskUpdatePlugins": "Ažuriraj dodatke", + "TaskRefreshPeopleDescription": "Ažurira meta podatke za glumce i redatelje u vašoj medijskoj biblioteci.", + "TaskRefreshPeople": "Osvježi ljude", + "TaskCleanLogsDescription": "Briši logove koji su stariji od {0} dana.", + "TaskCleanLogs": "Očisti direktorij sa logovima", + "TasksChannelsCategory": "Internet kanali", + "TasksLibraryCategory": "Biblioteka" } diff --git a/Emby.Server.Implementations/Localization/Core/ne.json b/Emby.Server.Implementations/Localization/Core/ne.json index 73fae3931..38c073709 100644 --- a/Emby.Server.Implementations/Localization/Core/ne.json +++ b/Emby.Server.Implementations/Localization/Core/ne.json @@ -58,5 +58,29 @@ "Books": "पुस्तकहरु", "Artists": "कलाकारहरू", "Application": "अनुप्रयोगहरू", - "Albums": "एल्बमहरू" + "Albums": "एल्बमहरू", + "TasksLibraryCategory": "पुस्तकालय", + "TasksApplicationCategory": "अनुप्रयोग", + "TasksMaintenanceCategory": "मर्मत", + "UserPolicyUpdatedWithName": "प्रयोगकर्ता नीति को लागी अद्यावधिक गरिएको छ {0}", + "UserPasswordChangedWithName": "पासवर्ड प्रयोगकर्ताका लागि परिवर्तन गरिएको छ {0}", + "UserOnlineFromDevice": "{0} बाट अनलाइन छ {1}", + "UserOfflineFromDevice": "{0} बाट विच्छेदन भएको छ {1}", + "UserLockedOutWithName": "प्रयोगकर्ता {0} लक गरिएको छ", + "UserDeletedWithName": "प्रयोगकर्ता {0} हटाइएको छ", + "UserCreatedWithName": "प्रयोगकर्ता {0} सिर्जना गरिएको छ", + "User": "प्रयोगकर्ता", + "PluginInstalledWithName": "", + "StartupEmbyServerIsLoading": "Jellyfin सर्भर लोड हुँदैछ। कृपया छिट्टै फेरि प्रयास गर्नुहोस्।", + "Songs": "गीतहरू", + "Shows": "शोहरू", + "ServerNameNeedsToBeRestarted": "{0} लाई पुन: सुरु गर्नु पर्छ", + "ScheduledTaskStartedWithName": "{0} सुरु भयो", + "ScheduledTaskFailedWithName": "{0} असफल", + "ProviderValue": "प्रदायक: {0}", + "Plugin": "प्लगइनहरू", + "Playlists": "प्लेलिस्टहरू", + "Photos": "तस्बिरहरु", + "NotificationOptionVideoPlaybackStopped": "भिडियो प्लेब्याक रोकियो", + "NotificationOptionVideoPlayback": "भिडियो प्लेब्याक सुरु भयो" } diff --git a/Emby.Server.Implementations/Localization/Core/pt.json b/Emby.Server.Implementations/Localization/Core/pt.json index 25c5b9053..5365fff23 100644 --- a/Emby.Server.Implementations/Localization/Core/pt.json +++ b/Emby.Server.Implementations/Localization/Core/pt.json @@ -101,7 +101,8 @@ "TaskCleanLogsDescription": "Deletar arquivos de log que existe a mais de {0} dias.", "TaskCleanLogs": "Limpar diretório de log", "TaskRefreshLibrary": "Escanear biblioteca de mídias", - "TaskRefreshChapterImagesDescription": "Criar miniaturas para videos que tem capítulos.", - "TaskCleanCacheDescription": "Deletar arquivos de cache que não são mais usados pelo sistema.", - "TasksChannelsCategory": "Canais de Internet" + "TaskRefreshChapterImagesDescription": "Cria miniaturas para vídeos que têm capítulos.", + "TaskCleanCacheDescription": "Apaga ficheiros em cache que já não são usados pelo sistema.", + "TasksChannelsCategory": "Canais de Internet", + "TaskRefreshChapterImages": "Extrair Imagens do Capítulo" } diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index 75fdedd10..d069d1ada 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -502,7 +502,8 @@ namespace Emby.Server.Implementations.Session Client = appName, DeviceId = deviceId, ApplicationVersion = appVersion, - Id = key.GetMD5().ToString("N", CultureInfo.InvariantCulture) + Id = key.GetMD5().ToString("N", CultureInfo.InvariantCulture), + ServerId = _appHost.SystemId }; var username = user?.Username; diff --git a/Jellyfin.Api/Controllers/StartupController.cs b/Jellyfin.Api/Controllers/StartupController.cs index 6ec0a4e26..04f134b8b 100644 --- a/Jellyfin.Api/Controllers/StartupController.cs +++ b/Jellyfin.Api/Controllers/StartupController.cs @@ -36,7 +36,6 @@ namespace Jellyfin.Api.Controllers public void CompleteWizard() { _config.Configuration.IsStartupWizardCompleted = true; - _config.SetOptimalValues(); _config.SaveConfiguration(); } diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj index 55c5ef1b1..93c4612b6 100644 --- a/Jellyfin.Api/Jellyfin.Api.csproj +++ b/Jellyfin.Api/Jellyfin.Api.csproj @@ -16,7 +16,7 @@ <PackageReference Include="Microsoft.AspNetCore.Authentication" Version="2.2.0" /> <PackageReference Include="Microsoft.AspNetCore.Authorization" Version="3.1.5" /> <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" /> - <PackageReference Include="Swashbuckle.AspNetCore" Version="5.5.0" /> + <PackageReference Include="Swashbuckle.AspNetCore" Version="5.5.1" /> </ItemGroup> <ItemGroup> diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 904114758..ae5c311bf 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -12,6 +12,7 @@ using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; using MediaBrowser.Common; using MediaBrowser.Common.Cryptography; +using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Authentication; using MediaBrowser.Controller.Drawing; @@ -192,15 +193,15 @@ namespace Jellyfin.Server.Implementations.Users } /// <inheritdoc/> - public void DeleteUser(User user) + public void DeleteUser(Guid userId) { + var dbContext = _dbProvider.CreateContext(); + var user = dbContext.Users.Find(userId); if (user == null) { - throw new ArgumentNullException(nameof(user)); + throw new ResourceNotFoundException(nameof(userId)); } - var dbContext = _dbProvider.CreateContext(); - if (dbContext.Users.Find(user.Id) == null) { throw new ArgumentException(string.Format( @@ -226,9 +227,18 @@ namespace Jellyfin.Server.Implementations.Users CultureInfo.InvariantCulture, "The user '{0}' cannot be deleted because there must be at least one admin user in the system.", user.Username), - nameof(user)); + nameof(userId)); + } + + // Clear all entities related to the user from the database. + if (user.ProfileImage != null) + { + dbContext.Remove(user.ProfileImage); } + dbContext.RemoveRange(user.Permissions); + dbContext.RemoveRange(user.Preferences); + dbContext.RemoveRange(user.AccessSchedules); dbContext.Users.Remove(user); dbContext.SaveChanges(); OnUserDeleted?.Invoke(this, new GenericEventArgs<User>(user)); diff --git a/Jellyfin.Server/StartupOptions.cs b/Jellyfin.Server/StartupOptions.cs index a26114e77..41a1430d2 100644 --- a/Jellyfin.Server/StartupOptions.cs +++ b/Jellyfin.Server/StartupOptions.cs @@ -101,6 +101,11 @@ namespace Jellyfin.Server config.Add(UdpServer.AddressOverrideConfigKey, PublishedServerUrl.ToString()); } + if (FFmpegPath != null) + { + config.Add(ConfigurationExtensions.FfmpegPathKey, FFmpegPath); + } + return config; } } diff --git a/MediaBrowser.Api/SyncPlay/SyncPlayService.cs b/MediaBrowser.Api/SyncPlay/SyncPlayService.cs index 1e14ea552..88cddfff7 100644 --- a/MediaBrowser.Api/SyncPlay/SyncPlayService.cs +++ b/MediaBrowser.Api/SyncPlay/SyncPlayService.cs @@ -11,21 +11,16 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Api.SyncPlay { - [Route("/SyncPlay/{SessionId}/NewGroup", "POST", Summary = "Create a new SyncPlay group")] + [Route("/SyncPlay/NewGroup", "POST", Summary = "Create a new SyncPlay group")] [Authenticated] public class SyncPlayNewGroup : IReturnVoid { - [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string SessionId { get; set; } } - [Route("/SyncPlay/{SessionId}/JoinGroup", "POST", Summary = "Join an existing SyncPlay group")] + [Route("/SyncPlay/JoinGroup", "POST", Summary = "Join an existing SyncPlay group")] [Authenticated] public class SyncPlayJoinGroup : IReturnVoid { - [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string SessionId { get; set; } - /// <summary> /// Gets or sets the Group id. /// </summary> @@ -41,63 +36,48 @@ namespace MediaBrowser.Api.SyncPlay public string PlayingItemId { get; set; } } - [Route("/SyncPlay/{SessionId}/LeaveGroup", "POST", Summary = "Leave joined SyncPlay group")] + [Route("/SyncPlay/LeaveGroup", "POST", Summary = "Leave joined SyncPlay group")] [Authenticated] public class SyncPlayLeaveGroup : IReturnVoid { - [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string SessionId { get; set; } } - [Route("/SyncPlay/{SessionId}/ListGroups", "POST", Summary = "List SyncPlay groups")] + [Route("/SyncPlay/ListGroups", "GET", Summary = "List SyncPlay groups")] [Authenticated] public class SyncPlayListGroups : IReturnVoid { - [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string SessionId { get; set; } - /// <summary> /// Gets or sets the filter item id. /// </summary> /// <value>The filter item id.</value> - [ApiMember(Name = "FilterItemId", Description = "Filter by item id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] + [ApiMember(Name = "FilterItemId", Description = "Filter by item id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] public string FilterItemId { get; set; } } - [Route("/SyncPlay/{SessionId}/PlayRequest", "POST", Summary = "Request play in SyncPlay group")] + [Route("/SyncPlay/PlayRequest", "POST", Summary = "Request play in SyncPlay group")] [Authenticated] public class SyncPlayPlayRequest : IReturnVoid { - [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string SessionId { get; set; } } - [Route("/SyncPlay/{SessionId}/PauseRequest", "POST", Summary = "Request pause in SyncPlay group")] + [Route("/SyncPlay/PauseRequest", "POST", Summary = "Request pause in SyncPlay group")] [Authenticated] public class SyncPlayPauseRequest : IReturnVoid { - [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string SessionId { get; set; } } - [Route("/SyncPlay/{SessionId}/SeekRequest", "POST", Summary = "Request seek in SyncPlay group")] + [Route("/SyncPlay/SeekRequest", "POST", Summary = "Request seek in SyncPlay group")] [Authenticated] public class SyncPlaySeekRequest : IReturnVoid { - [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string SessionId { get; set; } - [ApiMember(Name = "PositionTicks", IsRequired = true, DataType = "long", ParameterType = "query", Verb = "POST")] public long PositionTicks { get; set; } } - [Route("/SyncPlay/{SessionId}/BufferingRequest", "POST", Summary = "Request group wait in SyncPlay group while buffering")] + [Route("/SyncPlay/BufferingRequest", "POST", Summary = "Request group wait in SyncPlay group while buffering")] [Authenticated] public class SyncPlayBufferingRequest : IReturnVoid { - [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string SessionId { get; set; } - /// <summary> /// Gets or sets the date used to pin PositionTicks in time. /// </summary> @@ -116,13 +96,10 @@ namespace MediaBrowser.Api.SyncPlay public bool BufferingDone { get; set; } } - [Route("/SyncPlay/{SessionId}/UpdatePing", "POST", Summary = "Update session ping")] + [Route("/SyncPlay/UpdatePing", "POST", Summary = "Update session ping")] [Authenticated] public class SyncPlayUpdatePing : IReturnVoid { - [ApiMember(Name = "SessionId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] - public string SessionId { get; set; } - [ApiMember(Name = "Ping", IsRequired = true, DataType = "double", ParameterType = "query", Verb = "POST")] public double Ping { get; set; } } diff --git a/MediaBrowser.Api/UserService.cs b/MediaBrowser.Api/UserService.cs index 131def554..6e9d788dc 100644 --- a/MediaBrowser.Api/UserService.cs +++ b/MediaBrowser.Api/UserService.cs @@ -365,15 +365,8 @@ namespace MediaBrowser.Api public Task DeleteAsync(DeleteUser request) { - var user = _userManager.GetUserById(request.Id); - - if (user == null) - { - throw new ResourceNotFoundException("User not found"); - } - - _sessionMananger.RevokeUserTokens(user.Id, null); - _userManager.DeleteUser(user); + _userManager.DeleteUser(request.Id); + _sessionMananger.RevokeUserTokens(request.Id, null); return Task.CompletedTask; } diff --git a/MediaBrowser.Controller/Configuration/IServerConfigurationManager.cs b/MediaBrowser.Controller/Configuration/IServerConfigurationManager.cs index a5c5e3bcc..43ad04dba 100644 --- a/MediaBrowser.Controller/Configuration/IServerConfigurationManager.cs +++ b/MediaBrowser.Controller/Configuration/IServerConfigurationManager.cs @@ -19,7 +19,5 @@ namespace MediaBrowser.Controller.Configuration /// </summary> /// <value>The configuration.</value> ServerConfiguration Configuration { get; } - - bool SetOptimalValues(); } } diff --git a/MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs b/MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs index c0043c0ef..c2932cc4c 100644 --- a/MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs +++ b/MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs @@ -24,6 +24,11 @@ namespace MediaBrowser.Controller.Extensions public const string FfmpegAnalyzeDurationKey = "FFmpeg:analyzeduration"; /// <summary> + /// The key for the FFmpeg path option. + /// </summary> + public const string FfmpegPathKey = "ffmpeg"; + + /// <summary> /// The key for a setting that indicates whether playlists should allow duplicate entries. /// </summary> public const string PlaylistsAllowDuplicatesKey = "playlists:allowDuplicates"; diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index 47c080ebd..9d6646857 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -30,12 +30,10 @@ namespace MediaBrowser.Controller.Library /// </summary> /// <param name="fileInfo">The file information.</param> /// <param name="parent">The parent.</param> - /// <param name="allowIgnorePath">Allow the path to be ignored.</param> /// <returns>BaseItem.</returns> BaseItem ResolvePath( FileSystemMetadata fileInfo, - Folder parent = null, - bool allowIgnorePath = true); + Folder parent = null); /// <summary> /// Resolves a set of files into a list of BaseItem. diff --git a/MediaBrowser.Controller/Library/IUserManager.cs b/MediaBrowser.Controller/Library/IUserManager.cs index fe3e4f9e6..e73fe7120 100644 --- a/MediaBrowser.Controller/Library/IUserManager.cs +++ b/MediaBrowser.Controller/Library/IUserManager.cs @@ -111,8 +111,8 @@ namespace MediaBrowser.Controller.Library /// <summary> /// Deletes the specified user. /// </summary> - /// <param name="user">The user to be deleted.</param> - void DeleteUser(User user); + /// <param name="userId">The id of the user to be deleted.</param> + void DeleteUser(Guid userId); /// <summary> /// Resets the password. diff --git a/MediaBrowser.Controller/Providers/IExternalId.cs b/MediaBrowser.Controller/Providers/IExternalId.cs index d7e337bda..5e38446bc 100644 --- a/MediaBrowser.Controller/Providers/IExternalId.cs +++ b/MediaBrowser.Controller/Providers/IExternalId.cs @@ -1,15 +1,45 @@ using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Providers; namespace MediaBrowser.Controller.Providers { + /// <summary> + /// Represents an identifier for an external provider. + /// </summary> public interface IExternalId { - string Name { get; } + /// <summary> + /// Gets the display name of the provider associated with this ID type. + /// </summary> + string ProviderName { get; } + /// <summary> + /// Gets the unique key to distinguish this provider/type pair. This should be unique across providers. + /// </summary> + // TODO: This property is not actually unique across the concrete types at the moment. It should be updated to be unique. string Key { get; } + /// <summary> + /// Gets the specific media type for this id. This is used to distinguish between the different + /// external id types for providers with multiple ids. + /// A null value indicates there is no specific media type associated with the external id, or this is the + /// default id for the external provider so there is no need to specify a type. + /// </summary> + /// <remarks> + /// This can be used along with the <see cref="ProviderName"/> to localize the external id on the client. + /// </remarks> + ExternalIdMediaType? Type { get; } + + /// <summary> + /// Gets the URL format string for this id. + /// </summary> string UrlFormatString { get; } + /// <summary> + /// Determines whether this id supports a given item type. + /// </summary> + /// <param name="item">The item.</param> + /// <returns>True if this item is supported, otherwise false.</returns> bool Supports(IHasProviderIds item); } } diff --git a/MediaBrowser.Controller/Session/SessionInfo.cs b/MediaBrowser.Controller/Session/SessionInfo.cs index 36bc11be4..4b088998c 100644 --- a/MediaBrowser.Controller/Session/SessionInfo.cs +++ b/MediaBrowser.Controller/Session/SessionInfo.cs @@ -109,6 +109,12 @@ namespace MediaBrowser.Controller.Session public string DeviceName { get; set; } /// <summary> + /// Gets or sets the type of the device. + /// </summary> + /// <value>The type of the device.</value> + public string DeviceType { get; set; } + + /// <summary> /// Gets or sets the now playing item. /// </summary> /// <value>The now playing item.</value> @@ -215,8 +221,17 @@ namespace MediaBrowser.Controller.Session public string PlaylistItemId { get; set; } + public string ServerId { get; set; } + public string UserPrimaryImageTag { get; set; } + /// <summary> + /// Gets or sets the supported commands. + /// </summary> + /// <value>The supported commands.</value> + public string[] SupportedCommands + => Capabilities == null ? Array.Empty<string>() : Capabilities.SupportedCommands; + public Tuple<ISessionController, bool> EnsureController<T>(Func<SessionInfo, ISessionController> factory) { var controllers = SessionControllers.ToList(); diff --git a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs index e11ceb826..4ac249840 100644 --- a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs +++ b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs @@ -81,7 +81,7 @@ namespace MediaBrowser.LocalMetadata.Parsers var id = info.Key + "Id"; if (!_validProviderIds.ContainsKey(id)) { - _validProviderIds.Add(id, info.Key); + _validProviderIds.Add(id, info.Key!); } } diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 9397a347f..62fdbc618 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -21,6 +21,7 @@ using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.System; using Microsoft.Extensions.Logging; using System.Diagnostics; +using Microsoft.Extensions.Configuration; namespace MediaBrowser.MediaEncoding.Encoder { @@ -46,7 +47,7 @@ namespace MediaBrowser.MediaEncoding.Encoder private readonly object _runningProcessesLock = new object(); private readonly List<ProcessWrapper> _runningProcesses = new List<ProcessWrapper>(); - private string _ffmpegPath; + private string _ffmpegPath = string.Empty; private string _ffprobePath; public MediaEncoder( @@ -55,14 +56,14 @@ namespace MediaBrowser.MediaEncoding.Encoder IFileSystem fileSystem, ILocalizationManager localization, Lazy<EncodingHelper> encodingHelperFactory, - string startupOptionsFFmpegPath) + IConfiguration config) { _logger = logger; _configurationManager = configurationManager; _fileSystem = fileSystem; _localization = localization; _encodingHelperFactory = encodingHelperFactory; - _startupOptionFFmpegPath = startupOptionsFFmpegPath; + _startupOptionFFmpegPath = config.GetValue<string>(Controller.Extensions.ConfigurationExtensions.FfmpegPathKey) ?? string.Empty; } private EncodingHelper EncodingHelper => _encodingHelperFactory.Value; diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index b87c8fbab..a0c21a666 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -82,8 +82,6 @@ namespace MediaBrowser.Model.Configuration public bool EnableRemoteAccess { get; set; } - public bool CollectionsUpgraded { get; set; } - /// <summary> /// Gets or sets a value indicating whether [enable case sensitive item ids]. /// </summary> @@ -269,6 +267,9 @@ namespace MediaBrowser.Model.Configuration PathSubstitutions = Array.Empty<PathSubstitution>(); IgnoreVirtualInterfaces = false; EnableSimpleArtistDetection = false; + SkipDeserializationForBasicTypes = true; + + PluginRepositories = new List<RepositoryInfo>(); DisplaySpecialsWithinSeasons = true; EnableExternalContentInSuggestions = true; @@ -282,6 +283,9 @@ namespace MediaBrowser.Model.Configuration EnableHttps = false; EnableDashboardResponseCaching = true; EnableCaseSensitiveItemIds = true; + EnableNormalizedItemByNameIds = true; + DisableLiveTvChannelUserDataName = true; + EnableNewOmdbSupport = true; AutoRunWebApp = true; EnableRemoteAccess = true; diff --git a/MediaBrowser.Model/Providers/ExternalIdInfo.cs b/MediaBrowser.Model/Providers/ExternalIdInfo.cs index f2e6d8ef3..01784554f 100644 --- a/MediaBrowser.Model/Providers/ExternalIdInfo.cs +++ b/MediaBrowser.Model/Providers/ExternalIdInfo.cs @@ -1,26 +1,36 @@ -#nullable disable -#pragma warning disable CS1591 - namespace MediaBrowser.Model.Providers { + /// <summary> + /// Represents the external id information for serialization to the client. + /// </summary> public class ExternalIdInfo { /// <summary> - /// Gets or sets the name. + /// Gets or sets the display name of the external id provider (IE: IMDB, MusicBrainz, etc). + /// </summary> + // TODO: This should be renamed to ProviderName + public string? Name { get; set; } + + /// <summary> + /// Gets or sets the unique key for this id. This key should be unique across all providers. /// </summary> - /// <value>The name.</value> - public string Name { get; set; } + // TODO: This property is not actually unique across the concrete types at the moment. It should be updated to be unique. + public string? Key { get; set; } /// <summary> - /// Gets or sets the key. + /// Gets or sets the specific media type for this id. This is used to distinguish between the different + /// external id types for providers with multiple ids. + /// A null value indicates there is no specific media type associated with the external id, or this is the + /// default id for the external provider so there is no need to specify a type. /// </summary> - /// <value>The key.</value> - public string Key { get; set; } + /// <remarks> + /// This can be used along with the <see cref="Name"/> to localize the external id on the client. + /// </remarks> + public ExternalIdMediaType? Type { get; set; } /// <summary> /// Gets or sets the URL format string. /// </summary> - /// <value>The URL format string.</value> - public string UrlFormatString { get; set; } + public string? UrlFormatString { get; set; } } } diff --git a/MediaBrowser.Model/Providers/ExternalIdMediaType.cs b/MediaBrowser.Model/Providers/ExternalIdMediaType.cs new file mode 100644 index 000000000..5303c8f58 --- /dev/null +++ b/MediaBrowser.Model/Providers/ExternalIdMediaType.cs @@ -0,0 +1,71 @@ +namespace MediaBrowser.Model.Providers +{ + /// <summary> + /// The specific media type of an <see cref="ExternalIdInfo"/>. + /// </summary> + /// <remarks> + /// Client applications may use this as a translation key. + /// </remarks> + public enum ExternalIdMediaType + { + /// <summary> + /// A music album. + /// </summary> + Album = 1, + + /// <summary> + /// The artist of a music album. + /// </summary> + AlbumArtist = 2, + + /// <summary> + /// The artist of a media item. + /// </summary> + Artist = 3, + + /// <summary> + /// A boxed set of media. + /// </summary> + BoxSet = 4, + + /// <summary> + /// A series episode. + /// </summary> + Episode = 5, + + /// <summary> + /// A movie. + /// </summary> + Movie = 6, + + /// <summary> + /// An alternative artist apart from the main artist. + /// </summary> + OtherArtist = 7, + + /// <summary> + /// A person. + /// </summary> + Person = 8, + + /// <summary> + /// A release group. + /// </summary> + ReleaseGroup = 9, + + /// <summary> + /// A single season of a series. + /// </summary> + Season = 10, + + /// <summary> + /// A series. + /// </summary> + Series = 11, + + /// <summary> + /// A music track. + /// </summary> + Track = 12 + } +} diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index a51007a0e..86a182fe5 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -102,7 +102,7 @@ namespace MediaBrowser.Providers.Manager _metadataServices = metadataServices.OrderBy(i => i.Order).ToArray(); _metadataProviders = metadataProviders.ToArray(); - _externalIds = externalIds.OrderBy(i => i.Name).ToArray(); + _externalIds = externalIds.OrderBy(i => i.ProviderName).ToArray(); _savers = metadataSavers.Where(i => { @@ -900,7 +900,7 @@ namespace MediaBrowser.Providers.Manager return new ExternalUrl { - Name = i.Name, + Name = i.ProviderName, Url = string.Format( CultureInfo.InvariantCulture, i.UrlFormatString, @@ -914,8 +914,9 @@ namespace MediaBrowser.Providers.Manager return GetExternalIds(item) .Select(i => new ExternalIdInfo { - Name = i.Name, + Name = i.ProviderName, Key = i.Key, + Type = i.Type, UrlFormatString = i.UrlFormatString }); } diff --git a/MediaBrowser.Providers/Movies/MovieExternalIds.cs b/MediaBrowser.Providers/Movies/MovieExternalIds.cs index 4a0cca35e..14080841c 100644 --- a/MediaBrowser.Providers/Movies/MovieExternalIds.cs +++ b/MediaBrowser.Providers/Movies/MovieExternalIds.cs @@ -6,18 +6,22 @@ using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Providers; namespace MediaBrowser.Providers.Movies { public class ImdbExternalId : IExternalId { /// <inheritdoc /> - public string Name => "IMDb"; + public string ProviderName => "IMDb"; /// <inheritdoc /> public string Key => MetadataProvider.Imdb.ToString(); /// <inheritdoc /> + public ExternalIdMediaType? Type => null; + + /// <inheritdoc /> public string UrlFormatString => "https://www.imdb.com/title/{0}"; /// <inheritdoc /> @@ -36,12 +40,15 @@ namespace MediaBrowser.Providers.Movies public class ImdbPersonExternalId : IExternalId { /// <inheritdoc /> - public string Name => "IMDb"; + public string ProviderName => "IMDb"; /// <inheritdoc /> public string Key => MetadataProvider.Imdb.ToString(); /// <inheritdoc /> + public ExternalIdMediaType? Type => ExternalIdMediaType.Person; + + /// <inheritdoc /> public string UrlFormatString => "https://www.imdb.com/name/{0}"; /// <inheritdoc /> diff --git a/MediaBrowser.Providers/Music/MusicExternalIds.cs b/MediaBrowser.Providers/Music/MusicExternalIds.cs index 30c69dd0e..a1726b996 100644 --- a/MediaBrowser.Providers/Music/MusicExternalIds.cs +++ b/MediaBrowser.Providers/Music/MusicExternalIds.cs @@ -3,18 +3,22 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Providers; namespace MediaBrowser.Providers.Music { public class ImvdbId : IExternalId { /// <inheritdoc /> - public string Name => "IMVDb"; + public string ProviderName => "IMVDb"; /// <inheritdoc /> public string Key => "IMVDb"; /// <inheritdoc /> + public ExternalIdMediaType? Type => null; + + /// <inheritdoc /> public string UrlFormatString => null; /// <inheritdoc /> diff --git a/MediaBrowser.Providers/Plugins/AudioDb/ExternalIds.cs b/MediaBrowser.Providers/Plugins/AudioDb/ExternalIds.cs index 770dfe80f..1cc1f0fa1 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/ExternalIds.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/ExternalIds.cs @@ -3,18 +3,22 @@ using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Providers; namespace MediaBrowser.Providers.Plugins.AudioDb { public class AudioDbAlbumExternalId : IExternalId { /// <inheritdoc /> - public string Name => "TheAudioDb"; + public string ProviderName => "TheAudioDb"; /// <inheritdoc /> public string Key => MetadataProvider.AudioDbAlbum.ToString(); /// <inheritdoc /> + public ExternalIdMediaType? Type => null; + + /// <inheritdoc /> public string UrlFormatString => "https://www.theaudiodb.com/album/{0}"; /// <inheritdoc /> @@ -24,12 +28,15 @@ namespace MediaBrowser.Providers.Plugins.AudioDb public class AudioDbOtherAlbumExternalId : IExternalId { /// <inheritdoc /> - public string Name => "TheAudioDb Album"; + public string ProviderName => "TheAudioDb"; /// <inheritdoc /> public string Key => MetadataProvider.AudioDbAlbum.ToString(); /// <inheritdoc /> + public ExternalIdMediaType? Type => ExternalIdMediaType.Album; + + /// <inheritdoc /> public string UrlFormatString => "https://www.theaudiodb.com/album/{0}"; /// <inheritdoc /> @@ -39,12 +46,15 @@ namespace MediaBrowser.Providers.Plugins.AudioDb public class AudioDbArtistExternalId : IExternalId { /// <inheritdoc /> - public string Name => "TheAudioDb"; + public string ProviderName => "TheAudioDb"; /// <inheritdoc /> public string Key => MetadataProvider.AudioDbArtist.ToString(); /// <inheritdoc /> + public ExternalIdMediaType? Type => ExternalIdMediaType.Artist; + + /// <inheritdoc /> public string UrlFormatString => "https://www.theaudiodb.com/artist/{0}"; /// <inheritdoc /> @@ -54,12 +64,15 @@ namespace MediaBrowser.Providers.Plugins.AudioDb public class AudioDbOtherArtistExternalId : IExternalId { /// <inheritdoc /> - public string Name => "TheAudioDb Artist"; + public string ProviderName => "TheAudioDb"; /// <inheritdoc /> public string Key => MetadataProvider.AudioDbArtist.ToString(); /// <inheritdoc /> + public ExternalIdMediaType? Type => ExternalIdMediaType.OtherArtist; + + /// <inheritdoc /> public string UrlFormatString => "https://www.theaudiodb.com/artist/{0}"; /// <inheritdoc /> diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/ExternalIds.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/ExternalIds.cs index 8857cb618..5600c389c 100644 --- a/MediaBrowser.Providers/Plugins/MusicBrainz/ExternalIds.cs +++ b/MediaBrowser.Providers/Plugins/MusicBrainz/ExternalIds.cs @@ -3,6 +3,7 @@ using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Providers; using MediaBrowser.Providers.Plugins.MusicBrainz; namespace MediaBrowser.Providers.Music @@ -10,12 +11,15 @@ namespace MediaBrowser.Providers.Music public class MusicBrainzReleaseGroupExternalId : IExternalId { /// <inheritdoc /> - public string Name => "MusicBrainz Release Group"; + public string ProviderName => "MusicBrainz"; /// <inheritdoc /> public string Key => MetadataProvider.MusicBrainzReleaseGroup.ToString(); /// <inheritdoc /> + public ExternalIdMediaType? Type => ExternalIdMediaType.ReleaseGroup; + + /// <inheritdoc /> public string UrlFormatString => Plugin.Instance.Configuration.Server + "/release-group/{0}"; /// <inheritdoc /> @@ -25,12 +29,15 @@ namespace MediaBrowser.Providers.Music public class MusicBrainzAlbumArtistExternalId : IExternalId { /// <inheritdoc /> - public string Name => "MusicBrainz Album Artist"; + public string ProviderName => "MusicBrainz"; /// <inheritdoc /> public string Key => MetadataProvider.MusicBrainzAlbumArtist.ToString(); /// <inheritdoc /> + public ExternalIdMediaType? Type => ExternalIdMediaType.AlbumArtist; + + /// <inheritdoc /> public string UrlFormatString => Plugin.Instance.Configuration.Server + "/artist/{0}"; /// <inheritdoc /> @@ -40,12 +47,15 @@ namespace MediaBrowser.Providers.Music public class MusicBrainzAlbumExternalId : IExternalId { /// <inheritdoc /> - public string Name => "MusicBrainz Album"; + public string ProviderName => "MusicBrainz"; /// <inheritdoc /> public string Key => MetadataProvider.MusicBrainzAlbum.ToString(); /// <inheritdoc /> + public ExternalIdMediaType? Type => ExternalIdMediaType.Album; + + /// <inheritdoc /> public string UrlFormatString => Plugin.Instance.Configuration.Server + "/release/{0}"; /// <inheritdoc /> @@ -55,12 +65,15 @@ namespace MediaBrowser.Providers.Music public class MusicBrainzArtistExternalId : IExternalId { /// <inheritdoc /> - public string Name => "MusicBrainz"; + public string ProviderName => "MusicBrainz"; /// <inheritdoc /> public string Key => MetadataProvider.MusicBrainzArtist.ToString(); /// <inheritdoc /> + public ExternalIdMediaType? Type => ExternalIdMediaType.Artist; + + /// <inheritdoc /> public string UrlFormatString => Plugin.Instance.Configuration.Server + "/artist/{0}"; /// <inheritdoc /> @@ -70,13 +83,16 @@ namespace MediaBrowser.Providers.Music public class MusicBrainzOtherArtistExternalId : IExternalId { /// <inheritdoc /> - public string Name => "MusicBrainz Artist"; + public string ProviderName => "MusicBrainz"; /// <inheritdoc /> public string Key => MetadataProvider.MusicBrainzArtist.ToString(); /// <inheritdoc /> + public ExternalIdMediaType? Type => ExternalIdMediaType.OtherArtist; + + /// <inheritdoc /> public string UrlFormatString => Plugin.Instance.Configuration.Server + "/artist/{0}"; /// <inheritdoc /> @@ -86,12 +102,15 @@ namespace MediaBrowser.Providers.Music public class MusicBrainzTrackId : IExternalId { /// <inheritdoc /> - public string Name => "MusicBrainz Track"; + public string ProviderName => "MusicBrainz"; /// <inheritdoc /> public string Key => MetadataProvider.MusicBrainzTrack.ToString(); /// <inheritdoc /> + public ExternalIdMediaType? Type => ExternalIdMediaType.Track; + + /// <inheritdoc /> public string UrlFormatString => Plugin.Instance.Configuration.Server + "/track/{0}"; /// <inheritdoc /> diff --git a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetExternalId.cs b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetExternalId.cs index 629f95d53..1f7ec6433 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetExternalId.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetExternalId.cs @@ -1,21 +1,26 @@ -#pragma warning disable CS1591 - using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Providers; namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets { + /// <summary> + /// External ID for a TMDB box set. + /// </summary> public class TmdbBoxSetExternalId : IExternalId { /// <inheritdoc /> - public string Name => TmdbUtils.ProviderName; + public string ProviderName => TmdbUtils.ProviderName; /// <inheritdoc /> public string Key => MetadataProvider.TmdbCollection.ToString(); /// <inheritdoc /> + public ExternalIdMediaType? Type => ExternalIdMediaType.BoxSet; + + /// <inheritdoc /> public string UrlFormatString => TmdbUtils.BaseTmdbUrl + "collection/{0}"; /// <inheritdoc /> diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieExternalId.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieExternalId.cs index 095f57398..9610e4058 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieExternalId.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieExternalId.cs @@ -1,22 +1,27 @@ -#pragma warning disable CS1591 - using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Providers; namespace MediaBrowser.Providers.Plugins.Tmdb.Movies { + /// <summary> + /// External ID for a TMBD movie. + /// </summary> public class TmdbMovieExternalId : IExternalId { /// <inheritdoc /> - public string Name => TmdbUtils.ProviderName; + public string ProviderName => TmdbUtils.ProviderName; /// <inheritdoc /> public string Key => MetadataProvider.Tmdb.ToString(); /// <inheritdoc /> + public ExternalIdMediaType? Type => ExternalIdMediaType.Movie; + + /// <inheritdoc /> public string UrlFormatString => TmdbUtils.BaseTmdbUrl + "movie/{0}"; /// <inheritdoc /> diff --git a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonExternalId.cs b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonExternalId.cs index 2c6cf5db4..de74a7a4c 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonExternalId.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonExternalId.cs @@ -1,20 +1,25 @@ -#pragma warning disable CS1591 - using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Providers; namespace MediaBrowser.Providers.Plugins.Tmdb.People { + /// <summary> + /// External ID for a TMDB person. + /// </summary> public class TmdbPersonExternalId : IExternalId { /// <inheritdoc /> - public string Name => TmdbUtils.ProviderName; + public string ProviderName => TmdbUtils.ProviderName; /// <inheritdoc /> public string Key => MetadataProvider.Tmdb.ToString(); /// <inheritdoc /> + public ExternalIdMediaType? Type => ExternalIdMediaType.Person; + + /// <inheritdoc /> public string UrlFormatString => TmdbUtils.BaseTmdbUrl + "person/{0}"; /// <inheritdoc /> diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesExternalId.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesExternalId.cs index 1da203fd7..6ecc055d7 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesExternalId.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesExternalId.cs @@ -1,20 +1,25 @@ -#pragma warning disable CS1591 - using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Providers; namespace MediaBrowser.Providers.Plugins.Tmdb.TV { + /// <summary> + /// External ID for a TMDB series. + /// </summary> public class TmdbSeriesExternalId : IExternalId { /// <inheritdoc /> - public string Name => TmdbUtils.ProviderName; + public string ProviderName => TmdbUtils.ProviderName; /// <inheritdoc /> public string Key => MetadataProvider.Tmdb.ToString(); /// <inheritdoc /> + public ExternalIdMediaType? Type => ExternalIdMediaType.Series; + + /// <inheritdoc /> public string UrlFormatString => TmdbUtils.BaseTmdbUrl + "tv/{0}"; /// <inheritdoc /> diff --git a/MediaBrowser.Providers/TV/TvExternalIds.cs b/MediaBrowser.Providers/TV/TvExternalIds.cs index 1d904a84c..a6040edd1 100644 --- a/MediaBrowser.Providers/TV/TvExternalIds.cs +++ b/MediaBrowser.Providers/TV/TvExternalIds.cs @@ -3,6 +3,7 @@ using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Providers; using MediaBrowser.Providers.Plugins.TheTvdb; namespace MediaBrowser.Providers.TV @@ -10,12 +11,15 @@ namespace MediaBrowser.Providers.TV public class Zap2ItExternalId : IExternalId { /// <inheritdoc /> - public string Name => "Zap2It"; + public string ProviderName => "Zap2It"; /// <inheritdoc /> public string Key => MetadataProvider.Zap2It.ToString(); /// <inheritdoc /> + public ExternalIdMediaType? Type => null; + + /// <inheritdoc /> public string UrlFormatString => "http://tvlistings.zap2it.com/overview.html?programSeriesId={0}"; /// <inheritdoc /> @@ -25,12 +29,15 @@ namespace MediaBrowser.Providers.TV public class TvdbExternalId : IExternalId { /// <inheritdoc /> - public string Name => "TheTVDB"; + public string ProviderName => "TheTVDB"; /// <inheritdoc /> public string Key => MetadataProvider.Tvdb.ToString(); /// <inheritdoc /> + public ExternalIdMediaType? Type => null; + + /// <inheritdoc /> public string UrlFormatString => TvdbUtils.TvdbBaseUrl + "?tab=series&id={0}"; /// <inheritdoc /> @@ -40,12 +47,15 @@ namespace MediaBrowser.Providers.TV public class TvdbSeasonExternalId : IExternalId { /// <inheritdoc /> - public string Name => "TheTVDB"; + public string ProviderName => "TheTVDB"; /// <inheritdoc /> public string Key => MetadataProvider.Tvdb.ToString(); /// <inheritdoc /> + public ExternalIdMediaType? Type => ExternalIdMediaType.Season; + + /// <inheritdoc /> public string UrlFormatString => null; /// <inheritdoc /> @@ -55,12 +65,15 @@ namespace MediaBrowser.Providers.TV public class TvdbEpisodeExternalId : IExternalId { /// <inheritdoc /> - public string Name => "TheTVDB"; + public string ProviderName => "TheTVDB"; /// <inheritdoc /> public string Key => MetadataProvider.Tvdb.ToString(); /// <inheritdoc /> + public ExternalIdMediaType? Type => ExternalIdMediaType.Episode; + + /// <inheritdoc /> public string UrlFormatString => TvdbUtils.TvdbBaseUrl + "?tab=episode&id={0}"; /// <inheritdoc /> @@ -16,8 +16,8 @@ <a href="https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/?utm_source=widget"> <img alt="Translation Status" src="https://translate.jellyfin.org/widgets/jellyfin/-/jellyfin-core/svg-badge.svg"/> </a> -<a href="https://dev.azure.com/jellyfin-project/jellyfin/_build?definitionId=1"> -<img alt="Azure Builds" src="https://dev.azure.com/jellyfin-project/jellyfin/_apis/build/status/Jellyfin%20CI"/> +<a href="https://dev.azure.com/jellyfin-project/jellyfin/_build?definitionId=29"> +<img alt="Azure Builds" src="https://dev.azure.com/jellyfin-project/jellyfin/_apis/build/status/Jellyfin%20Server"/> </a> <a href="https://hub.docker.com/r/jellyfin/jellyfin"> <img alt="Docker Pull Count" src="https://img.shields.io/docker/pulls/jellyfin/jellyfin.svg"/> diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj index 074bf2371..100f95424 100644 --- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj +++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj @@ -13,15 +13,15 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="AutoFixture" Version="4.11.0" /> - <PackageReference Include="AutoFixture.AutoMoq" Version="4.11.0" /> - <PackageReference Include="AutoFixture.Xunit2" Version="4.11.0" /> + <PackageReference Include="AutoFixture" Version="4.12.0" /> + <PackageReference Include="AutoFixture.AutoMoq" Version="4.12.0" /> + <PackageReference Include="AutoFixture.Xunit2" Version="4.12.0" /> <PackageReference Include="Microsoft.Extensions.Options" Version="3.1.5" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" /> <PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" /> <PackageReference Include="coverlet.collector" Version="1.3.0" /> - <PackageReference Include="Moq" Version="4.14.3" /> + <PackageReference Include="Moq" Version="4.14.4" /> </ItemGroup> <!-- Code 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 91fd0e2e2..dd53c9902 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj +++ b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj @@ -14,9 +14,9 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="AutoFixture" Version="4.11.0" /> - <PackageReference Include="AutoFixture.AutoMoq" Version="4.11.0" /> - <PackageReference Include="Moq" Version="4.14.3" /> + <PackageReference Include="AutoFixture" Version="4.12.0" /> + <PackageReference Include="AutoFixture.AutoMoq" Version="4.12.0" /> + <PackageReference Include="Moq" Version="4.14.4" /> <PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" /> <PackageReference Include="coverlet.collector" Version="1.3.0" /> diff --git a/tests/Jellyfin.Server.Implementations.Tests/Library/IgnorePatternsTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Library/IgnorePatternsTests.cs index 26dee38c6..c145ddc9d 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Library/IgnorePatternsTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Library/IgnorePatternsTests.cs @@ -7,12 +7,19 @@ namespace Jellyfin.Server.Implementations.Tests.Library { [Theory] [InlineData("/media/small.jpg", true)] + [InlineData("/media/albumart.jpg", true)] + [InlineData("/media/movie.sample.mp4", true)] [InlineData("/media/movies/#Recycle/test.txt", true)] [InlineData("/media/movies/#recycle/", true)] + [InlineData("/media/movies/#recycle", true)] [InlineData("thumbs.db", true)] [InlineData(@"C:\media\movies\movie.avi", false)] [InlineData("/media/.hiddendir/file.mp4", true)] [InlineData("/media/dir/.hiddenfile.mp4", true)] + [InlineData("/volume1/video/Series/@eaDir", true)] + [InlineData("/volume1/video/Series/@eaDir/file.txt", true)] + [InlineData("/directory/@Recycle", true)] + [InlineData("/directory/@Recycle/file.mp3", true)] public void PathIgnored(string path, bool expected) { Assert.Equal(expected, IgnorePatterns.ShouldIgnore(path)); |
