aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Emby.Server.Implementations/ApplicationHost.cs4
-rw-r--r--Emby.Server.Implementations/Collections/CollectionManager.cs56
-rw-r--r--Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs57
-rw-r--r--Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs12
-rw-r--r--Emby.Server.Implementations/Library/IgnorePatterns.cs44
-rw-r--r--Emby.Server.Implementations/Library/LibraryManager.cs29
-rw-r--r--Emby.Server.Implementations/Localization/Core/ar.json6
-rw-r--r--Emby.Server.Implementations/Localization/Core/es-AR.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/es-MX.json2
-rw-r--r--Emby.Server.Implementations/Localization/Core/hr.json52
-rw-r--r--Emby.Server.Implementations/Localization/Core/ne.json26
-rw-r--r--Emby.Server.Implementations/Localization/Core/pt.json7
-rw-r--r--Emby.Server.Implementations/Session/SessionManager.cs3
-rw-r--r--Jellyfin.Api/Controllers/StartupController.cs1
-rw-r--r--Jellyfin.Api/Jellyfin.Api.csproj2
-rw-r--r--Jellyfin.Server.Implementations/Users/UserManager.cs20
-rw-r--r--Jellyfin.Server/StartupOptions.cs5
-rw-r--r--MediaBrowser.Api/SyncPlay/SyncPlayService.cs43
-rw-r--r--MediaBrowser.Api/UserService.cs11
-rw-r--r--MediaBrowser.Controller/Configuration/IServerConfigurationManager.cs2
-rw-r--r--MediaBrowser.Controller/Extensions/ConfigurationExtensions.cs5
-rw-r--r--MediaBrowser.Controller/Library/ILibraryManager.cs4
-rw-r--r--MediaBrowser.Controller/Library/IUserManager.cs4
-rw-r--r--MediaBrowser.Controller/Providers/IExternalId.cs32
-rw-r--r--MediaBrowser.Controller/Session/SessionInfo.cs15
-rw-r--r--MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs2
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs7
-rw-r--r--MediaBrowser.Model/Configuration/ServerConfiguration.cs8
-rw-r--r--MediaBrowser.Model/Providers/ExternalIdInfo.cs32
-rw-r--r--MediaBrowser.Model/Providers/ExternalIdMediaType.cs71
-rw-r--r--MediaBrowser.Providers/Manager/ProviderManager.cs7
-rw-r--r--MediaBrowser.Providers/Movies/MovieExternalIds.cs11
-rw-r--r--MediaBrowser.Providers/Music/MusicExternalIds.cs6
-rw-r--r--MediaBrowser.Providers/Plugins/AudioDb/ExternalIds.cs21
-rw-r--r--MediaBrowser.Providers/Plugins/MusicBrainz/ExternalIds.cs31
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetExternalId.cs11
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieExternalId.cs11
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonExternalId.cs11
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesExternalId.cs11
-rw-r--r--MediaBrowser.Providers/TV/TvExternalIds.cs21
-rw-r--r--README.md4
-rw-r--r--tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj8
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj6
-rw-r--r--tests/Jellyfin.Server.Implementations.Tests/Library/IgnorePatternsTests.cs7
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 />
diff --git a/README.md b/README.md
index 99a66e306..83090100d 100644
--- a/README.md
+++ b/README.md
@@ -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));