From a3020f2917176baef19653a485cc0aaaeb8d00f2 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 21 Aug 2020 19:53:55 +0200 Subject: Use backdrop with library name as library thumbnail --- .../Images/BaseDynamicImageProvider.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs b/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs index 57302b506..5f7e51858 100644 --- a/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs +++ b/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs @@ -133,9 +133,20 @@ namespace Emby.Server.Implementations.Images protected virtual IEnumerable GetStripCollageImagePaths(BaseItem primaryItem, IEnumerable items) { + var useBackdrop = primaryItem is CollectionFolder; return items .Select(i => { + // Use Backdrop instead of Primary image for Library images. + if (useBackdrop) + { + var backdrop = i.GetImageInfo(ImageType.Backdrop, 0); + if (backdrop != null && backdrop.IsLocalFile) + { + return backdrop.Path; + } + } + var image = i.GetImageInfo(ImageType.Primary, 0); if (image != null && image.IsLocalFile) { @@ -190,7 +201,7 @@ namespace Emby.Server.Implementations.Images return null; } - ImageProcessor.CreateImageCollage(options); + ImageProcessor.CreateImageCollage(options, primaryItem.Name); return outputPath; } -- cgit v1.2.3 From 4db5700e18b98cb34784930707dc4ee2833865b6 Mon Sep 17 00:00:00 2001 From: Ryan Petris Date: Wed, 23 Sep 2020 14:12:26 -0700 Subject: Don't take a lock if there's no intention to manipulate the list of open streams. Instead, use a ConcurrentDictionary so that, in those situations, thread-safe access to the dictionary is ensured. --- .../Library/MediaSourceManager.cs | 51 ++++++++-------------- 1 file changed, 17 insertions(+), 34 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs index 67cf8bf5b..376a15570 100644 --- a/Emby.Server.Implementations/Library/MediaSourceManager.cs +++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs @@ -1,6 +1,7 @@ #pragma warning disable CS1591 using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Globalization; using System.IO; @@ -43,7 +44,7 @@ namespace Emby.Server.Implementations.Library private readonly ILocalizationManager _localizationManager; private readonly IApplicationPaths _appPaths; - private readonly Dictionary _openStreams = new Dictionary(StringComparer.OrdinalIgnoreCase); + private readonly ConcurrentDictionary _openStreams = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); private readonly SemaphoreSlim _liveStreamSemaphore = new SemaphoreSlim(1, 1); private IMediaSourceProvider[] _providers; @@ -582,29 +583,20 @@ namespace Emby.Server.Implementations.Library mediaSource.InferTotalBitrate(); } - public async Task GetDirectStreamProviderByUniqueId(string uniqueId, CancellationToken cancellationToken) + public Task GetDirectStreamProviderByUniqueId(string uniqueId, CancellationToken cancellationToken) { - await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); - - try + var info = _openStreams.Values.FirstOrDefault(i => { - var info = _openStreams.Values.FirstOrDefault(i => + var liveStream = i as ILiveStream; + if (liveStream != null) { - var liveStream = i as ILiveStream; - if (liveStream != null) - { - return string.Equals(liveStream.UniqueId, uniqueId, StringComparison.OrdinalIgnoreCase); - } + return string.Equals(liveStream.UniqueId, uniqueId, StringComparison.OrdinalIgnoreCase); + } - return false; - }); + return false; + }); - return info as IDirectStreamProvider; - } - finally - { - _liveStreamSemaphore.Release(); - } + return Task.FromResult(info as IDirectStreamProvider); } public async Task OpenLiveStream(LiveStreamRequest request, CancellationToken cancellationToken) @@ -793,29 +785,20 @@ namespace Emby.Server.Implementations.Library return new Tuple(info.MediaSource, info as IDirectStreamProvider); } - private async Task GetLiveStreamInfo(string id, CancellationToken cancellationToken) + private Task GetLiveStreamInfo(string id, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(id)) { throw new ArgumentNullException(nameof(id)); } - await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); - - try + if (_openStreams.TryGetValue(id, out ILiveStream info)) { - if (_openStreams.TryGetValue(id, out ILiveStream info)) - { - return info; - } - else - { - throw new ResourceNotFoundException(); - } + return Task.FromResult(info); } - finally + else { - _liveStreamSemaphore.Release(); + return Task.FromException(new ResourceNotFoundException()); } } @@ -844,7 +827,7 @@ namespace Emby.Server.Implementations.Library if (liveStream.ConsumerCount <= 0) { - _openStreams.Remove(id); + _openStreams.TryRemove(id, out _); _logger.LogInformation("Closing live stream {0}", id); -- cgit v1.2.3 From 05fa95f149582b3de1881c42aaa0ac00408a9947 Mon Sep 17 00:00:00 2001 From: Gary Wilber Date: Sat, 26 Sep 2020 19:33:59 -0700 Subject: Increase scan speed for music libraries --- .../Library/Resolvers/Audio/MusicAlbumResolver.cs | 66 +++++++++++----------- .../Library/Resolvers/Audio/MusicArtistResolver.cs | 14 ++++- 2 files changed, 47 insertions(+), 33 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs index 79b6dded3..ff485a18e 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; using Emby.Naming.Audio; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Library; @@ -113,50 +115,50 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio IFileSystem fileSystem, ILibraryManager libraryManager) { + // check for audio files before digging down into directories + var firstAudioFile = list + .Where(fileSystemInfo => !fileSystemInfo.IsDirectory) + .FirstOrDefault(fileSystemInfo => libraryManager.IsAudioFile(fileSystemInfo.FullName)); + if (firstAudioFile != null) + { + // at least one audio file exists + return true; + } + + if (!allowSubfolders) + { + // not music since no audio file exists and we're not looking into subfolders + return false; + } + var discSubfolderCount = 0; var notMultiDisc = false; var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions(); var parser = new AlbumParser(namingOptions); - foreach (var fileSystemInfo in list) + + var directories = list.Where(fileSystemInfo => fileSystemInfo.IsDirectory); + + var result = Parallel.ForEach(directories, (fileSystemInfo, state) => { - if (fileSystemInfo.IsDirectory) + var path = fileSystemInfo.FullName; + var hasMusic = ContainsMusic(directoryService.GetFileSystemEntries(path), false, directoryService, logger, fileSystem, libraryManager); + + if (hasMusic) { - if (allowSubfolders) + if (parser.IsMultiPart(path)) { - if (notMultiDisc) - { - continue; - } - - var path = fileSystemInfo.FullName; - var hasMusic = ContainsMusic(directoryService.GetFileSystemEntries(path), false, directoryService, logger, fileSystem, libraryManager); - - if (hasMusic) - { - if (parser.IsMultiPart(path)) - { - logger.LogDebug("Found multi-disc folder: " + path); - discSubfolderCount++; - } - else - { - // If there are folders underneath with music that are not multidisc, then this can't be a multi-disc album - notMultiDisc = true; - } - } + logger.LogDebug("Found multi-disc folder: " + path); + discSubfolderCount++; } - } - else - { - var fullName = fileSystemInfo.FullName; - - if (libraryManager.IsAudioFile(fullName)) + else { - return true; + // If there are folders underneath with music that are not multidisc, then this can't be a multi-disc album + notMultiDisc = true; + state.Stop(); } } - } + }); if (notMultiDisc) { diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs index 5f5cd0e92..e9e688fa6 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Threading.Tasks; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Library; @@ -94,7 +95,18 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio var albumResolver = new MusicAlbumResolver(_logger, _fileSystem, _libraryManager); // If we contain an album assume we are an artist folder - return args.FileSystemChildren.Where(i => i.IsDirectory).Any(i => albumResolver.IsMusicAlbum(i.FullName, directoryService)) ? new MusicArtist() : null; + var directories = args.FileSystemChildren.Where(i => i.IsDirectory); + + var result = Parallel.ForEach(directories, (fileSystemInfo, state) => + { + if (albumResolver.IsMusicAlbum(fileSystemInfo.FullName, directoryService)) + { + // stop once we see a music album + state.Stop(); + } + }); + + return !result.IsCompleted ? new MusicArtist() : null; } } } -- cgit v1.2.3 From 72534f9d667f2457cbe96ea870d140528e366ba2 Mon Sep 17 00:00:00 2001 From: Niels van Velzen Date: Fri, 25 Sep 2020 09:25:59 +0200 Subject: Use SessionMessageType for WebSocket messages --- Emby.Dlna/PlayTo/PlayToController.cs | 8 ++-- .../EntryPoints/LibraryChangedNotifier.cs | 7 +-- .../EntryPoints/RecordingNotifier.cs | 11 ++--- .../EntryPoints/UserDataChangeNotifier.cs | 2 +- .../HttpServer/WebSocketConnection.cs | 5 ++- .../Session/SessionManager.cs | 28 ++++++------ .../Session/SessionWebSocketListener.cs | 3 +- .../Session/WebSocketController.cs | 3 +- .../ActivityLogWebSocketListener.cs | 14 +++--- .../ScheduledTasksWebSocketListener.cs | 14 +++--- .../SessionInfoWebSocketListener.cs | 9 +++- .../Consumers/System/TaskCompletedNotifier.cs | 3 +- .../Updates/PluginInstallationCancelledNotifier.cs | 3 +- .../Updates/PluginInstallationFailedNotifier.cs | 3 +- .../Consumers/Updates/PluginInstalledNotifier.cs | 3 +- .../Consumers/Updates/PluginInstallingNotifier.cs | 3 +- .../Consumers/Updates/PluginUninstalledNotifier.cs | 3 +- .../Events/Consumers/Users/UserDeletedNotifier.cs | 3 +- .../Events/Consumers/Users/UserUpdatedNotifier.cs | 3 +- .../Net/BasePeriodicWebSocketListener.cs | 27 +++++++++--- .../Session/ISessionController.cs | 3 +- MediaBrowser.Controller/Session/ISessionManager.cs | 8 ++-- MediaBrowser.Model/Net/WebSocketMessage.cs | 3 +- MediaBrowser.Model/Session/SessionMessageType.cs | 50 ++++++++++++++++++++++ 24 files changed, 156 insertions(+), 63 deletions(-) create mode 100644 MediaBrowser.Model/Session/SessionMessageType.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Dlna/PlayTo/PlayToController.cs b/Emby.Dlna/PlayTo/PlayToController.cs index 460ac2d8d..d09a11930 100644 --- a/Emby.Dlna/PlayTo/PlayToController.cs +++ b/Emby.Dlna/PlayTo/PlayToController.cs @@ -811,7 +811,7 @@ namespace Emby.Dlna.PlayTo } /// - public Task SendMessage(string name, Guid messageId, T data, CancellationToken cancellationToken) + public Task SendMessage(SessionMessageType name, Guid messageId, T data, CancellationToken cancellationToken) { if (_disposed) { @@ -823,17 +823,17 @@ namespace Emby.Dlna.PlayTo return Task.CompletedTask; } - if (string.Equals(name, "Play", StringComparison.OrdinalIgnoreCase)) + if (name == SessionMessageType.Play) { return SendPlayCommand(data as PlayRequest, cancellationToken); } - if (string.Equals(name, "PlayState", StringComparison.OrdinalIgnoreCase)) + if (name == SessionMessageType.PlayState) { return SendPlaystateCommand(data as PlaystateRequest, cancellationToken); } - if (string.Equals(name, "GeneralCommand", StringComparison.OrdinalIgnoreCase)) + if (name == SessionMessageType.GeneralCommand) { return SendGeneralCommand(data as GeneralCommand, cancellationToken); } diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs index c9d21d963..ff64e217a 100644 --- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs @@ -16,6 +16,7 @@ using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Session; using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.EntryPoints @@ -105,7 +106,7 @@ namespace Emby.Server.Implementations.EntryPoints try { - _sessionManager.SendMessageToAdminSessions("RefreshProgress", dict, CancellationToken.None); + _sessionManager.SendMessageToAdminSessions(SessionMessageType.RefreshProgress, dict, CancellationToken.None); } catch { @@ -123,7 +124,7 @@ namespace Emby.Server.Implementations.EntryPoints try { - _sessionManager.SendMessageToAdminSessions("RefreshProgress", collectionFolderDict, CancellationToken.None); + _sessionManager.SendMessageToAdminSessions(SessionMessageType.RefreshProgress, collectionFolderDict, CancellationToken.None); } catch { @@ -345,7 +346,7 @@ namespace Emby.Server.Implementations.EntryPoints try { - await _sessionManager.SendMessageToUserSessions(new List { userId }, "LibraryChanged", info, cancellationToken).ConfigureAwait(false); + await _sessionManager.SendMessageToUserSessions(new List { userId }, SessionMessageType.LibraryChanged, info, cancellationToken).ConfigureAwait(false); } catch (Exception ex) { diff --git a/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs b/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs index 44d2580d6..824bb85f4 100644 --- a/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs @@ -10,6 +10,7 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Session; +using MediaBrowser.Model.Session; using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.EntryPoints @@ -46,25 +47,25 @@ namespace Emby.Server.Implementations.EntryPoints private async void OnLiveTvManagerSeriesTimerCreated(object sender, GenericEventArgs e) { - await SendMessage("SeriesTimerCreated", e.Argument).ConfigureAwait(false); + await SendMessage(SessionMessageType.SeriesTimerCreated, e.Argument).ConfigureAwait(false); } private async void OnLiveTvManagerTimerCreated(object sender, GenericEventArgs e) { - await SendMessage("TimerCreated", e.Argument).ConfigureAwait(false); + await SendMessage(SessionMessageType.TimerCreated, e.Argument).ConfigureAwait(false); } private async void OnLiveTvManagerSeriesTimerCancelled(object sender, GenericEventArgs e) { - await SendMessage("SeriesTimerCancelled", e.Argument).ConfigureAwait(false); + await SendMessage(SessionMessageType.SeriesTimerCancelled, e.Argument).ConfigureAwait(false); } private async void OnLiveTvManagerTimerCancelled(object sender, GenericEventArgs e) { - await SendMessage("TimerCancelled", e.Argument).ConfigureAwait(false); + await SendMessage(SessionMessageType.TimerCancelled, e.Argument).ConfigureAwait(false); } - private async Task SendMessage(string name, TimerEventInfo info) + private async Task SendMessage(SessionMessageType name, TimerEventInfo info) { var users = _userManager.Users.Where(i => i.HasPermission(PermissionKind.EnableLiveTvAccess)).Select(i => i.Id).ToList(); diff --git a/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs b/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs index 3618b88c5..ff09cc81e 100644 --- a/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs @@ -116,7 +116,7 @@ namespace Emby.Server.Implementations.EntryPoints private Task SendNotifications(Guid userId, List changedItems, CancellationToken cancellationToken) { - return _sessionManager.SendMessageToUserSessions(new List { userId }, "UserDataChanged", () => GetUserDataChangeInfo(userId, changedItems), cancellationToken); + return _sessionManager.SendMessageToUserSessions(new List { userId }, SessionMessageType.UserDataChanged, () => GetUserDataChangeInfo(userId, changedItems), cancellationToken); } private UserDataChangeInfo GetUserDataChangeInfo(Guid userId, List changedItems) diff --git a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs index 7eae4e764..fed2addf8 100644 --- a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs +++ b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs @@ -11,6 +11,7 @@ using System.Threading.Tasks; using MediaBrowser.Common.Json; using MediaBrowser.Controller.Net; using MediaBrowser.Model.Net; +using MediaBrowser.Model.Session; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; @@ -227,7 +228,7 @@ namespace Emby.Server.Implementations.HttpServer Connection = this }; - if (info.MessageType.Equals("KeepAlive", StringComparison.Ordinal)) + if (info.MessageType == SessionMessageType.KeepAlive) { await SendKeepAliveResponse().ConfigureAwait(false); } @@ -244,7 +245,7 @@ namespace Emby.Server.Implementations.HttpServer new WebSocketMessage { MessageId = Guid.NewGuid(), - MessageType = "KeepAlive" + MessageType = SessionMessageType.KeepAlive }, CancellationToken.None); } diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index e42d47853..df1b1e452 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -1064,10 +1064,10 @@ namespace Emby.Server.Implementations.Session AssertCanControl(session, controllingSession); } - return SendMessageToSession(session, "GeneralCommand", command, cancellationToken); + return SendMessageToSession(session, SessionMessageType.GeneralCommand, command, cancellationToken); } - private static async Task SendMessageToSession(SessionInfo session, string name, T data, CancellationToken cancellationToken) + private static async Task SendMessageToSession(SessionInfo session, SessionMessageType name, T data, CancellationToken cancellationToken) { var controllers = session.SessionControllers; var messageId = Guid.NewGuid(); @@ -1078,7 +1078,7 @@ namespace Emby.Server.Implementations.Session } } - private static Task SendMessageToSessions(IEnumerable sessions, string name, T data, CancellationToken cancellationToken) + private static Task SendMessageToSessions(IEnumerable sessions, SessionMessageType name, T data, CancellationToken cancellationToken) { IEnumerable GetTasks() { @@ -1178,7 +1178,7 @@ namespace Emby.Server.Implementations.Session } } - await SendMessageToSession(session, "Play", command, cancellationToken).ConfigureAwait(false); + await SendMessageToSession(session, SessionMessageType.Play, command, cancellationToken).ConfigureAwait(false); } /// @@ -1186,7 +1186,7 @@ namespace Emby.Server.Implementations.Session { CheckDisposed(); var session = GetSessionToRemoteControl(sessionId); - await SendMessageToSession(session, "SyncPlayCommand", command, cancellationToken).ConfigureAwait(false); + await SendMessageToSession(session, SessionMessageType.SyncPlayCommand, command, cancellationToken).ConfigureAwait(false); } /// @@ -1194,7 +1194,7 @@ namespace Emby.Server.Implementations.Session { CheckDisposed(); var session = GetSessionToRemoteControl(sessionId); - await SendMessageToSession(session, "SyncPlayGroupUpdate", command, cancellationToken).ConfigureAwait(false); + await SendMessageToSession(session, SessionMessageType.SyncPlayGroupUpdate, command, cancellationToken).ConfigureAwait(false); } private IEnumerable TranslateItemForPlayback(Guid id, User user) @@ -1297,7 +1297,7 @@ namespace Emby.Server.Implementations.Session } } - return SendMessageToSession(session, "Playstate", command, cancellationToken); + return SendMessageToSession(session, SessionMessageType.PlayState, command, cancellationToken); } private static void AssertCanControl(SessionInfo session, SessionInfo controllingSession) @@ -1322,7 +1322,7 @@ namespace Emby.Server.Implementations.Session { CheckDisposed(); - return SendMessageToSessions(Sessions, "RestartRequired", string.Empty, cancellationToken); + return SendMessageToSessions(Sessions, SessionMessageType.RestartRequired, string.Empty, cancellationToken); } /// @@ -1334,7 +1334,7 @@ namespace Emby.Server.Implementations.Session { CheckDisposed(); - return SendMessageToSessions(Sessions, "ServerShuttingDown", string.Empty, cancellationToken); + return SendMessageToSessions(Sessions, SessionMessageType.ServerShuttingDown, string.Empty, cancellationToken); } /// @@ -1348,7 +1348,7 @@ namespace Emby.Server.Implementations.Session _logger.LogDebug("Beginning SendServerRestartNotification"); - return SendMessageToSessions(Sessions, "ServerRestarting", string.Empty, cancellationToken); + return SendMessageToSessions(Sessions, SessionMessageType.ServerRestarting, string.Empty, cancellationToken); } /// @@ -1866,7 +1866,7 @@ namespace Emby.Server.Implementations.Session } /// - public Task SendMessageToAdminSessions(string name, T data, CancellationToken cancellationToken) + public Task SendMessageToAdminSessions(SessionMessageType name, T data, CancellationToken cancellationToken) { CheckDisposed(); @@ -1879,7 +1879,7 @@ namespace Emby.Server.Implementations.Session } /// - public Task SendMessageToUserSessions(List userIds, string name, Func dataFn, CancellationToken cancellationToken) + public Task SendMessageToUserSessions(List userIds, SessionMessageType name, Func dataFn, CancellationToken cancellationToken) { CheckDisposed(); @@ -1894,7 +1894,7 @@ namespace Emby.Server.Implementations.Session } /// - public Task SendMessageToUserSessions(List userIds, string name, T data, CancellationToken cancellationToken) + public Task SendMessageToUserSessions(List userIds, SessionMessageType name, T data, CancellationToken cancellationToken) { CheckDisposed(); @@ -1903,7 +1903,7 @@ namespace Emby.Server.Implementations.Session } /// - public Task SendMessageToUserDeviceSessions(string deviceId, string name, T data, CancellationToken cancellationToken) + public Task SendMessageToUserDeviceSessions(string deviceId, SessionMessageType name, T data, CancellationToken cancellationToken) { CheckDisposed(); diff --git a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs index 15c2af220..a5f847953 100644 --- a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs +++ b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs @@ -8,6 +8,7 @@ using Jellyfin.Data.Events; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Net; +using MediaBrowser.Model.Session; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; @@ -316,7 +317,7 @@ namespace Emby.Server.Implementations.Session return webSocket.SendAsync( new WebSocketMessage { - MessageType = "ForceKeepAlive", + MessageType = SessionMessageType.ForceKeepAlive, Data = WebSocketLostTimeout }, CancellationToken.None); diff --git a/Emby.Server.Implementations/Session/WebSocketController.cs b/Emby.Server.Implementations/Session/WebSocketController.cs index 94604ca1e..b986ffa1c 100644 --- a/Emby.Server.Implementations/Session/WebSocketController.cs +++ b/Emby.Server.Implementations/Session/WebSocketController.cs @@ -11,6 +11,7 @@ using System.Threading.Tasks; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Net; +using MediaBrowser.Model.Session; using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.Session @@ -65,7 +66,7 @@ namespace Emby.Server.Implementations.Session /// public Task SendMessage( - string name, + SessionMessageType name, Guid messageId, T data, CancellationToken cancellationToken) diff --git a/Jellyfin.Api/WebSocketListeners/ActivityLogWebSocketListener.cs b/Jellyfin.Api/WebSocketListeners/ActivityLogWebSocketListener.cs index 849b3b709..77d55828d 100644 --- a/Jellyfin.Api/WebSocketListeners/ActivityLogWebSocketListener.cs +++ b/Jellyfin.Api/WebSocketListeners/ActivityLogWebSocketListener.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using Jellyfin.Data.Events; using MediaBrowser.Controller.Net; using MediaBrowser.Model.Activity; +using MediaBrowser.Model.Session; using Microsoft.Extensions.Logging; namespace Jellyfin.Api.WebSocketListeners @@ -29,11 +30,14 @@ namespace Jellyfin.Api.WebSocketListeners _activityManager.EntryCreated += OnEntryCreated; } - /// - /// Gets the name. - /// - /// The name. - protected override string Name => "ActivityLogEntry"; + /// + protected override SessionMessageType Type => SessionMessageType.ActivityLogEntry; + + /// + protected override SessionMessageType StartType => SessionMessageType.ActivityLogEntryStart; + + /// + protected override SessionMessageType StopType => SessionMessageType.ActivityLogEntryStop; /// /// Gets the data to send. diff --git a/Jellyfin.Api/WebSocketListeners/ScheduledTasksWebSocketListener.cs b/Jellyfin.Api/WebSocketListeners/ScheduledTasksWebSocketListener.cs index 8a966c137..80314b923 100644 --- a/Jellyfin.Api/WebSocketListeners/ScheduledTasksWebSocketListener.cs +++ b/Jellyfin.Api/WebSocketListeners/ScheduledTasksWebSocketListener.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Threading.Tasks; using Jellyfin.Data.Events; using MediaBrowser.Controller.Net; +using MediaBrowser.Model.Session; using MediaBrowser.Model.Tasks; using Microsoft.Extensions.Logging; @@ -33,11 +34,14 @@ namespace Jellyfin.Api.WebSocketListeners _taskManager.TaskCompleted += OnTaskCompleted; } - /// - /// Gets the name. - /// - /// The name. - protected override string Name => "ScheduledTasksInfo"; + /// + protected override SessionMessageType Type => SessionMessageType.ScheduledTasksInfo; + + /// + protected override SessionMessageType StartType => SessionMessageType.ScheduledTasksInfoStart; + + /// + protected override SessionMessageType StopType => SessionMessageType.ScheduledTasksInfoStop; /// /// Gets the data to send. diff --git a/Jellyfin.Api/WebSocketListeners/SessionInfoWebSocketListener.cs b/Jellyfin.Api/WebSocketListeners/SessionInfoWebSocketListener.cs index 1fb5dc412..1cf43a005 100644 --- a/Jellyfin.Api/WebSocketListeners/SessionInfoWebSocketListener.cs +++ b/Jellyfin.Api/WebSocketListeners/SessionInfoWebSocketListener.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Session; +using MediaBrowser.Model.Session; using Microsoft.Extensions.Logging; namespace Jellyfin.Api.WebSocketListeners @@ -34,7 +35,13 @@ namespace Jellyfin.Api.WebSocketListeners } /// - protected override string Name => "Sessions"; + protected override SessionMessageType Type => SessionMessageType.Sessions; + + /// + protected override SessionMessageType StartType => SessionMessageType.SessionsStart; + + /// + protected override SessionMessageType StopType => SessionMessageType.SessionsStop; /// /// Gets the data to send. diff --git a/Jellyfin.Server.Implementations/Events/Consumers/System/TaskCompletedNotifier.cs b/Jellyfin.Server.Implementations/Events/Consumers/System/TaskCompletedNotifier.cs index 80ed56cd8..0993c6df7 100644 --- a/Jellyfin.Server.Implementations/Events/Consumers/System/TaskCompletedNotifier.cs +++ b/Jellyfin.Server.Implementations/Events/Consumers/System/TaskCompletedNotifier.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using MediaBrowser.Controller.Events; using MediaBrowser.Controller.Session; +using MediaBrowser.Model.Session; using MediaBrowser.Model.Tasks; namespace Jellyfin.Server.Implementations.Events.Consumers.System @@ -25,7 +26,7 @@ namespace Jellyfin.Server.Implementations.Events.Consumers.System /// public async Task OnEvent(TaskCompletionEventArgs eventArgs) { - await _sessionManager.SendMessageToAdminSessions("ScheduledTaskEnded", eventArgs.Result, CancellationToken.None).ConfigureAwait(false); + await _sessionManager.SendMessageToAdminSessions(SessionMessageType.ScheduledTaskEnded, eventArgs.Result, CancellationToken.None).ConfigureAwait(false); } } } diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallationCancelledNotifier.cs b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallationCancelledNotifier.cs index 1c600683a..1d790da6b 100644 --- a/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallationCancelledNotifier.cs +++ b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallationCancelledNotifier.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using MediaBrowser.Controller.Events; using MediaBrowser.Controller.Events.Updates; using MediaBrowser.Controller.Session; +using MediaBrowser.Model.Session; namespace Jellyfin.Server.Implementations.Events.Consumers.Updates { @@ -25,7 +26,7 @@ namespace Jellyfin.Server.Implementations.Events.Consumers.Updates /// public async Task OnEvent(PluginInstallationCancelledEventArgs eventArgs) { - await _sessionManager.SendMessageToAdminSessions("PackageInstallationCancelled", eventArgs.Argument, CancellationToken.None).ConfigureAwait(false); + await _sessionManager.SendMessageToAdminSessions(SessionMessageType.PackageInstallationCancelled, eventArgs.Argument, CancellationToken.None).ConfigureAwait(false); } } } diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallationFailedNotifier.cs b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallationFailedNotifier.cs index ea0c878d4..a1faf18fc 100644 --- a/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallationFailedNotifier.cs +++ b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallationFailedNotifier.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using MediaBrowser.Common.Updates; using MediaBrowser.Controller.Events; using MediaBrowser.Controller.Session; +using MediaBrowser.Model.Session; namespace Jellyfin.Server.Implementations.Events.Consumers.Updates { @@ -25,7 +26,7 @@ namespace Jellyfin.Server.Implementations.Events.Consumers.Updates /// public async Task OnEvent(InstallationFailedEventArgs eventArgs) { - await _sessionManager.SendMessageToAdminSessions("PackageInstallationFailed", eventArgs.InstallationInfo, CancellationToken.None).ConfigureAwait(false); + await _sessionManager.SendMessageToAdminSessions(SessionMessageType.PackageInstallationFailed, eventArgs.InstallationInfo, CancellationToken.None).ConfigureAwait(false); } } } diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstalledNotifier.cs b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstalledNotifier.cs index 3dda5a04c..bd1a71404 100644 --- a/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstalledNotifier.cs +++ b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstalledNotifier.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using MediaBrowser.Controller.Events; using MediaBrowser.Controller.Events.Updates; using MediaBrowser.Controller.Session; +using MediaBrowser.Model.Session; namespace Jellyfin.Server.Implementations.Events.Consumers.Updates { @@ -25,7 +26,7 @@ namespace Jellyfin.Server.Implementations.Events.Consumers.Updates /// public async Task OnEvent(PluginInstalledEventArgs eventArgs) { - await _sessionManager.SendMessageToAdminSessions("PackageInstallationCompleted", eventArgs.Argument, CancellationToken.None).ConfigureAwait(false); + await _sessionManager.SendMessageToAdminSessions(SessionMessageType.PackageInstallationCompleted, eventArgs.Argument, CancellationToken.None).ConfigureAwait(false); } } } diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallingNotifier.cs b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallingNotifier.cs index f691d11a7..b513ac64a 100644 --- a/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallingNotifier.cs +++ b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginInstallingNotifier.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using MediaBrowser.Controller.Events; using MediaBrowser.Controller.Events.Updates; using MediaBrowser.Controller.Session; +using MediaBrowser.Model.Session; namespace Jellyfin.Server.Implementations.Events.Consumers.Updates { @@ -25,7 +26,7 @@ namespace Jellyfin.Server.Implementations.Events.Consumers.Updates /// public async Task OnEvent(PluginInstallingEventArgs eventArgs) { - await _sessionManager.SendMessageToAdminSessions("PackageInstalling", eventArgs.Argument, CancellationToken.None).ConfigureAwait(false); + await _sessionManager.SendMessageToAdminSessions(SessionMessageType.PackageInstalling, eventArgs.Argument, CancellationToken.None).ConfigureAwait(false); } } } diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginUninstalledNotifier.cs b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginUninstalledNotifier.cs index 709692f6b..1fd7b9adf 100644 --- a/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginUninstalledNotifier.cs +++ b/Jellyfin.Server.Implementations/Events/Consumers/Updates/PluginUninstalledNotifier.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using MediaBrowser.Controller.Events; using MediaBrowser.Controller.Events.Updates; using MediaBrowser.Controller.Session; +using MediaBrowser.Model.Session; namespace Jellyfin.Server.Implementations.Events.Consumers.Updates { @@ -25,7 +26,7 @@ namespace Jellyfin.Server.Implementations.Events.Consumers.Updates /// public async Task OnEvent(PluginUninstalledEventArgs eventArgs) { - await _sessionManager.SendMessageToAdminSessions("PluginUninstalled", eventArgs.Argument, CancellationToken.None).ConfigureAwait(false); + await _sessionManager.SendMessageToAdminSessions(SessionMessageType.PackageUninstalled, eventArgs.Argument, CancellationToken.None).ConfigureAwait(false); } } } diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Users/UserDeletedNotifier.cs b/Jellyfin.Server.Implementations/Events/Consumers/Users/UserDeletedNotifier.cs index 10367a939..303e88621 100644 --- a/Jellyfin.Server.Implementations/Events/Consumers/Users/UserDeletedNotifier.cs +++ b/Jellyfin.Server.Implementations/Events/Consumers/Users/UserDeletedNotifier.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using Jellyfin.Data.Events.Users; using MediaBrowser.Controller.Events; using MediaBrowser.Controller.Session; +using MediaBrowser.Model.Session; namespace Jellyfin.Server.Implementations.Events.Consumers.Users { @@ -30,7 +31,7 @@ namespace Jellyfin.Server.Implementations.Events.Consumers.Users { await _sessionManager.SendMessageToUserSessions( new List { eventArgs.Argument.Id }, - "UserDeleted", + SessionMessageType.UserDeleted, eventArgs.Argument.Id.ToString("N", CultureInfo.InvariantCulture), CancellationToken.None).ConfigureAwait(false); } diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Users/UserUpdatedNotifier.cs b/Jellyfin.Server.Implementations/Events/Consumers/Users/UserUpdatedNotifier.cs index 6081dd044..a14911b94 100644 --- a/Jellyfin.Server.Implementations/Events/Consumers/Users/UserUpdatedNotifier.cs +++ b/Jellyfin.Server.Implementations/Events/Consumers/Users/UserUpdatedNotifier.cs @@ -6,6 +6,7 @@ using Jellyfin.Data.Events.Users; using MediaBrowser.Controller.Events; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Session; +using MediaBrowser.Model.Session; namespace Jellyfin.Server.Implementations.Events.Consumers.Users { @@ -33,7 +34,7 @@ namespace Jellyfin.Server.Implementations.Events.Consumers.Users { await _sessionManager.SendMessageToUserSessions( new List { e.Argument.Id }, - "UserUpdated", + SessionMessageType.UserUpdated, _userManager.GetUserDto(e.Argument), CancellationToken.None).ConfigureAwait(false); } diff --git a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs index 916dea58b..28227603b 100644 --- a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs +++ b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs @@ -8,6 +8,7 @@ using System.Net.WebSockets; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Model.Net; +using MediaBrowser.Model.Session; using Microsoft.Extensions.Logging; namespace MediaBrowser.Controller.Net @@ -28,10 +29,22 @@ namespace MediaBrowser.Controller.Net new List>(); /// - /// Gets the name. + /// Gets the type used for the messages sent to the client. /// - /// The name. - protected abstract string Name { get; } + /// The type. + protected abstract SessionMessageType Type { get; } + + /// + /// Gets the message type received from the client to start sending messages. + /// + /// The type. + protected abstract SessionMessageType StartType { get; } + + /// + /// Gets the message type received from the client to stop sending messages. + /// + /// The type. + protected abstract SessionMessageType StopType { get; } /// /// Gets the data to send. @@ -66,12 +79,12 @@ namespace MediaBrowser.Controller.Net throw new ArgumentNullException(nameof(message)); } - if (string.Equals(message.MessageType, Name + "Start", StringComparison.OrdinalIgnoreCase)) + if (message.MessageType == StartType) { Start(message); } - if (string.Equals(message.MessageType, Name + "Stop", StringComparison.OrdinalIgnoreCase)) + if (message.MessageType == StopType) { Stop(message); } @@ -159,7 +172,7 @@ namespace MediaBrowser.Controller.Net new WebSocketMessage { MessageId = Guid.NewGuid(), - MessageType = Name, + MessageType = Type, Data = data }, cancellationToken).ConfigureAwait(false); @@ -176,7 +189,7 @@ namespace MediaBrowser.Controller.Net } catch (Exception ex) { - Logger.LogError(ex, "Error sending web socket message {Name}", Name); + Logger.LogError(ex, "Error sending web socket message {Name}", Type); DisposeConnection(tuple); } } diff --git a/MediaBrowser.Controller/Session/ISessionController.cs b/MediaBrowser.Controller/Session/ISessionController.cs index 22d6e2a04..bc4ccd44c 100644 --- a/MediaBrowser.Controller/Session/ISessionController.cs +++ b/MediaBrowser.Controller/Session/ISessionController.cs @@ -3,6 +3,7 @@ using System; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Model.Session; namespace MediaBrowser.Controller.Session { @@ -23,6 +24,6 @@ namespace MediaBrowser.Controller.Session /// /// Sends the message. /// - Task SendMessage(string name, Guid messageId, T data, CancellationToken cancellationToken); + Task SendMessage(SessionMessageType name, Guid messageId, T data, CancellationToken cancellationToken); } } diff --git a/MediaBrowser.Controller/Session/ISessionManager.cs b/MediaBrowser.Controller/Session/ISessionManager.cs index 228b2331d..04c3004ee 100644 --- a/MediaBrowser.Controller/Session/ISessionManager.cs +++ b/MediaBrowser.Controller/Session/ISessionManager.cs @@ -188,16 +188,16 @@ namespace MediaBrowser.Controller.Session /// The data. /// The cancellation token. /// Task. - Task SendMessageToAdminSessions(string name, T data, CancellationToken cancellationToken); + Task SendMessageToAdminSessions(SessionMessageType name, T data, CancellationToken cancellationToken); /// /// Sends the message to user sessions. /// /// /// Task. - Task SendMessageToUserSessions(List userIds, string name, T data, CancellationToken cancellationToken); + Task SendMessageToUserSessions(List userIds, SessionMessageType name, T data, CancellationToken cancellationToken); - Task SendMessageToUserSessions(List userIds, string name, Func dataFn, CancellationToken cancellationToken); + Task SendMessageToUserSessions(List userIds, SessionMessageType name, Func dataFn, CancellationToken cancellationToken); /// /// Sends the message to user device sessions. @@ -208,7 +208,7 @@ namespace MediaBrowser.Controller.Session /// The data. /// The cancellation token. /// Task. - Task SendMessageToUserDeviceSessions(string deviceId, string name, T data, CancellationToken cancellationToken); + Task SendMessageToUserDeviceSessions(string deviceId, SessionMessageType name, T data, CancellationToken cancellationToken); /// /// Sends the restart required message. diff --git a/MediaBrowser.Model/Net/WebSocketMessage.cs b/MediaBrowser.Model/Net/WebSocketMessage.cs index 660eebeda..bffbbe612 100644 --- a/MediaBrowser.Model/Net/WebSocketMessage.cs +++ b/MediaBrowser.Model/Net/WebSocketMessage.cs @@ -2,6 +2,7 @@ #pragma warning disable CS1591 using System; +using MediaBrowser.Model.Session; namespace MediaBrowser.Model.Net { @@ -15,7 +16,7 @@ namespace MediaBrowser.Model.Net /// Gets or sets the type of the message. /// /// The type of the message. - public string MessageType { get; set; } + public SessionMessageType MessageType { get; set; } public Guid MessageId { get; set; } diff --git a/MediaBrowser.Model/Session/SessionMessageType.cs b/MediaBrowser.Model/Session/SessionMessageType.cs new file mode 100644 index 000000000..23c41026d --- /dev/null +++ b/MediaBrowser.Model/Session/SessionMessageType.cs @@ -0,0 +1,50 @@ +#pragma warning disable CS1591 + +namespace MediaBrowser.Model.Session +{ + /// + /// The different kinds of messages that are used in the WebSocket api. + /// + public enum SessionMessageType + { + // Server -> Client + ForceKeepAlive, + GeneralCommand, + UserDataChanged, + Sessions, + Play, + SyncPlayCommand, + SyncPlayGroupUpdate, + PlayState, + RestartRequired, + ServerShuttingDown, + ServerRestarting, + LibraryChanged, + UserDeleted, + UserUpdated, + SeriesTimerCreated, + TimerCreated, + SeriesTimerCancelled, + TimerCancelled, + RefreshProgress, + ScheduledTaskEnded, + PackageInstallationCancelled, + PackageInstallationFailed, + PackageInstallationCompleted, + PackageInstalling, + PackageUninstalled, + ActivityLogEntry, + ScheduledTasksInfo, + + // Client -> Server + ActivityLogEntryStart, + ActivityLogEntryStop, + SessionsStart, + SessionsStop, + ScheduledTasksInfoStart, + ScheduledTasksInfoStop, + + // Shared + KeepAlive, + } +} -- cgit v1.2.3 From 75041e7f39f76a39fcfae15d6cd2aee510b4d599 Mon Sep 17 00:00:00 2001 From: Gary Wilber Date: Sun, 27 Sep 2020 12:56:12 -0700 Subject: interlocked increment --- .../Library/Resolvers/Audio/MusicAlbumResolver.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs index ff485a18e..d690a383d 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using System.Threading.Tasks; using Emby.Naming.Audio; using MediaBrowser.Controller.Entities.Audio; @@ -132,7 +133,6 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio } var discSubfolderCount = 0; - var notMultiDisc = false; var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions(); var parser = new AlbumParser(namingOptions); @@ -149,18 +149,17 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio if (parser.IsMultiPart(path)) { logger.LogDebug("Found multi-disc folder: " + path); - discSubfolderCount++; + Interlocked.Increment(ref discSubfolderCount); } else { // If there are folders underneath with music that are not multidisc, then this can't be a multi-disc album - notMultiDisc = true; state.Stop(); } } }); - if (notMultiDisc) + if (!result.IsCompleted) { return false; } -- cgit v1.2.3 From 3cfbe6e3401546a56abc826736578d9bfa054d99 Mon Sep 17 00:00:00 2001 From: Gary Wilber Date: Sun, 27 Sep 2020 13:28:19 -0700 Subject: better audio file check --- .../Library/Resolvers/Audio/MusicAlbumResolver.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs index d690a383d..18ceb5e76 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs @@ -117,10 +117,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio ILibraryManager libraryManager) { // check for audio files before digging down into directories - var firstAudioFile = list - .Where(fileSystemInfo => !fileSystemInfo.IsDirectory) - .FirstOrDefault(fileSystemInfo => libraryManager.IsAudioFile(fileSystemInfo.FullName)); - if (firstAudioFile != null) + var foundAudioFile = list.Any(fileSystemInfo => !fileSystemInfo.IsDirectory && libraryManager.IsAudioFile(fileSystemInfo.FullName)); + if (foundAudioFile) { // at least one audio file exists return true; -- cgit v1.2.3 From 3b0c6c660d057336e26aa67cb36abaaccb00771d Mon Sep 17 00:00:00 2001 From: Justin LeCheminant Date: Wed, 30 Sep 2020 20:26:59 -0600 Subject: Removing string we don't use anymore. --- Emby.Server.Implementations/Localization/Core/af.json | 1 - Emby.Server.Implementations/Localization/Core/ar.json | 1 - Emby.Server.Implementations/Localization/Core/bg-BG.json | 1 - Emby.Server.Implementations/Localization/Core/bn.json | 1 - Emby.Server.Implementations/Localization/Core/ca.json | 1 - Emby.Server.Implementations/Localization/Core/cs.json | 1 - Emby.Server.Implementations/Localization/Core/da.json | 1 - Emby.Server.Implementations/Localization/Core/de.json | 1 - Emby.Server.Implementations/Localization/Core/el.json | 1 - Emby.Server.Implementations/Localization/Core/en-GB.json | 1 - Emby.Server.Implementations/Localization/Core/en-US.json | 1 - Emby.Server.Implementations/Localization/Core/es-AR.json | 1 - Emby.Server.Implementations/Localization/Core/es-MX.json | 1 - Emby.Server.Implementations/Localization/Core/es.json | 1 - Emby.Server.Implementations/Localization/Core/es_419.json | 1 - Emby.Server.Implementations/Localization/Core/es_DO.json | 1 - Emby.Server.Implementations/Localization/Core/fa.json | 1 - Emby.Server.Implementations/Localization/Core/fi.json | 1 - Emby.Server.Implementations/Localization/Core/fil.json | 1 - Emby.Server.Implementations/Localization/Core/fr-CA.json | 1 - Emby.Server.Implementations/Localization/Core/fr.json | 1 - Emby.Server.Implementations/Localization/Core/gsw.json | 1 - Emby.Server.Implementations/Localization/Core/he.json | 1 - Emby.Server.Implementations/Localization/Core/hr.json | 1 - Emby.Server.Implementations/Localization/Core/hu.json | 1 - Emby.Server.Implementations/Localization/Core/id.json | 1 - Emby.Server.Implementations/Localization/Core/is.json | 1 - Emby.Server.Implementations/Localization/Core/it.json | 1 - Emby.Server.Implementations/Localization/Core/ja.json | 1 - Emby.Server.Implementations/Localization/Core/kk.json | 1 - Emby.Server.Implementations/Localization/Core/ko.json | 1 - Emby.Server.Implementations/Localization/Core/lt-LT.json | 1 - Emby.Server.Implementations/Localization/Core/lv.json | 1 - Emby.Server.Implementations/Localization/Core/mk.json | 1 - Emby.Server.Implementations/Localization/Core/mr.json | 1 - Emby.Server.Implementations/Localization/Core/ms.json | 1 - Emby.Server.Implementations/Localization/Core/nb.json | 1 - Emby.Server.Implementations/Localization/Core/ne.json | 1 - Emby.Server.Implementations/Localization/Core/nl.json | 1 - Emby.Server.Implementations/Localization/Core/nn.json | 1 - Emby.Server.Implementations/Localization/Core/pl.json | 1 - Emby.Server.Implementations/Localization/Core/pt-BR.json | 1 - Emby.Server.Implementations/Localization/Core/pt-PT.json | 1 - Emby.Server.Implementations/Localization/Core/pt.json | 1 - Emby.Server.Implementations/Localization/Core/ro.json | 1 - Emby.Server.Implementations/Localization/Core/ru.json | 1 - Emby.Server.Implementations/Localization/Core/sk.json | 1 - Emby.Server.Implementations/Localization/Core/sl-SI.json | 1 - Emby.Server.Implementations/Localization/Core/sq.json | 1 - Emby.Server.Implementations/Localization/Core/sr.json | 1 - Emby.Server.Implementations/Localization/Core/sv.json | 1 - Emby.Server.Implementations/Localization/Core/ta.json | 1 - Emby.Server.Implementations/Localization/Core/th.json | 1 - Emby.Server.Implementations/Localization/Core/tr.json | 1 - Emby.Server.Implementations/Localization/Core/uk.json | 1 - Emby.Server.Implementations/Localization/Core/ur_PK.json | 1 - Emby.Server.Implementations/Localization/Core/vi.json | 1 - Emby.Server.Implementations/Localization/Core/zh-CN.json | 1 - Emby.Server.Implementations/Localization/Core/zh-HK.json | 1 - Emby.Server.Implementations/Localization/Core/zh-TW.json | 1 - 60 files changed, 60 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/af.json b/Emby.Server.Implementations/Localization/Core/af.json index d33e11893..977a1c2d7 100644 --- a/Emby.Server.Implementations/Localization/Core/af.json +++ b/Emby.Server.Implementations/Localization/Core/af.json @@ -85,7 +85,6 @@ "ItemAddedWithName": "{0} is in die versameling", "HomeVideos": "Tuis opnames", "HeaderRecordingGroups": "Groep Opnames", - "HeaderCameraUploads": "Kamera Oplaai", "Genres": "Genres", "FailedLoginAttemptWithUserName": "Mislukte aansluiting van {0}", "ChapterNameValue": "Hoofstuk", diff --git a/Emby.Server.Implementations/Localization/Core/ar.json b/Emby.Server.Implementations/Localization/Core/ar.json index 4eac8e75d..4b898e6fe 100644 --- a/Emby.Server.Implementations/Localization/Core/ar.json +++ b/Emby.Server.Implementations/Localization/Core/ar.json @@ -16,7 +16,6 @@ "Folders": "المجلدات", "Genres": "التضنيفات", "HeaderAlbumArtists": "فناني الألبومات", - "HeaderCameraUploads": "تحميلات الكاميرا", "HeaderContinueWatching": "استئناف", "HeaderFavoriteAlbums": "الألبومات المفضلة", "HeaderFavoriteArtists": "الفنانون المفضلون", diff --git a/Emby.Server.Implementations/Localization/Core/bg-BG.json b/Emby.Server.Implementations/Localization/Core/bg-BG.json index 3fc7c7dc0..1fed83276 100644 --- a/Emby.Server.Implementations/Localization/Core/bg-BG.json +++ b/Emby.Server.Implementations/Localization/Core/bg-BG.json @@ -16,7 +16,6 @@ "Folders": "Папки", "Genres": "Жанрове", "HeaderAlbumArtists": "Изпълнители на албуми", - "HeaderCameraUploads": "Качени от камера", "HeaderContinueWatching": "Продължаване на гледането", "HeaderFavoriteAlbums": "Любими албуми", "HeaderFavoriteArtists": "Любими изпълнители", diff --git a/Emby.Server.Implementations/Localization/Core/bn.json b/Emby.Server.Implementations/Localization/Core/bn.json index 1bd190982..5667bf337 100644 --- a/Emby.Server.Implementations/Localization/Core/bn.json +++ b/Emby.Server.Implementations/Localization/Core/bn.json @@ -14,7 +14,6 @@ "HeaderFavoriteArtists": "প্রিয় শিল্পীরা", "HeaderFavoriteAlbums": "প্রিয় এলবামগুলো", "HeaderContinueWatching": "দেখতে থাকুন", - "HeaderCameraUploads": "ক্যামেরার আপলোড সমূহ", "HeaderAlbumArtists": "এলবাম শিল্পী", "Genres": "জেনার", "Folders": "ফোল্ডারগুলো", diff --git a/Emby.Server.Implementations/Localization/Core/ca.json b/Emby.Server.Implementations/Localization/Core/ca.json index 2c802a39e..b7852eccb 100644 --- a/Emby.Server.Implementations/Localization/Core/ca.json +++ b/Emby.Server.Implementations/Localization/Core/ca.json @@ -16,7 +16,6 @@ "Folders": "Carpetes", "Genres": "Gèneres", "HeaderAlbumArtists": "Artistes del Àlbum", - "HeaderCameraUploads": "Pujades de Càmera", "HeaderContinueWatching": "Continua Veient", "HeaderFavoriteAlbums": "Àlbums Preferits", "HeaderFavoriteArtists": "Artistes Preferits", diff --git a/Emby.Server.Implementations/Localization/Core/cs.json b/Emby.Server.Implementations/Localization/Core/cs.json index 464ca28ca..b34fad066 100644 --- a/Emby.Server.Implementations/Localization/Core/cs.json +++ b/Emby.Server.Implementations/Localization/Core/cs.json @@ -16,7 +16,6 @@ "Folders": "Složky", "Genres": "Žánry", "HeaderAlbumArtists": "Umělci alba", - "HeaderCameraUploads": "Nahrané fotografie", "HeaderContinueWatching": "Pokračovat ve sledování", "HeaderFavoriteAlbums": "Oblíbená alba", "HeaderFavoriteArtists": "Oblíbení interpreti", diff --git a/Emby.Server.Implementations/Localization/Core/da.json b/Emby.Server.Implementations/Localization/Core/da.json index f5397b62c..b29ad94ef 100644 --- a/Emby.Server.Implementations/Localization/Core/da.json +++ b/Emby.Server.Implementations/Localization/Core/da.json @@ -16,7 +16,6 @@ "Folders": "Mapper", "Genres": "Genrer", "HeaderAlbumArtists": "Albumkunstnere", - "HeaderCameraUploads": "Kamera Uploads", "HeaderContinueWatching": "Fortsæt Afspilning", "HeaderFavoriteAlbums": "Favoritalbummer", "HeaderFavoriteArtists": "Favoritkunstnere", diff --git a/Emby.Server.Implementations/Localization/Core/de.json b/Emby.Server.Implementations/Localization/Core/de.json index fcbe9566e..97ad1694a 100644 --- a/Emby.Server.Implementations/Localization/Core/de.json +++ b/Emby.Server.Implementations/Localization/Core/de.json @@ -16,7 +16,6 @@ "Folders": "Verzeichnisse", "Genres": "Genres", "HeaderAlbumArtists": "Album-Interpreten", - "HeaderCameraUploads": "Kamera-Uploads", "HeaderContinueWatching": "Fortsetzen", "HeaderFavoriteAlbums": "Lieblingsalben", "HeaderFavoriteArtists": "Lieblings-Interpreten", diff --git a/Emby.Server.Implementations/Localization/Core/el.json b/Emby.Server.Implementations/Localization/Core/el.json index 0753ea39d..c45cc11cb 100644 --- a/Emby.Server.Implementations/Localization/Core/el.json +++ b/Emby.Server.Implementations/Localization/Core/el.json @@ -16,7 +16,6 @@ "Folders": "Φάκελοι", "Genres": "Είδη", "HeaderAlbumArtists": "Καλλιτέχνες του Άλμπουμ", - "HeaderCameraUploads": "Μεταφορτώσεις Κάμερας", "HeaderContinueWatching": "Συνεχίστε την παρακολούθηση", "HeaderFavoriteAlbums": "Αγαπημένα Άλμπουμ", "HeaderFavoriteArtists": "Αγαπημένοι Καλλιτέχνες", diff --git a/Emby.Server.Implementations/Localization/Core/en-GB.json b/Emby.Server.Implementations/Localization/Core/en-GB.json index 544c38cfa..57ff13219 100644 --- a/Emby.Server.Implementations/Localization/Core/en-GB.json +++ b/Emby.Server.Implementations/Localization/Core/en-GB.json @@ -16,7 +16,6 @@ "Folders": "Folders", "Genres": "Genres", "HeaderAlbumArtists": "Album Artists", - "HeaderCameraUploads": "Camera Uploads", "HeaderContinueWatching": "Continue Watching", "HeaderFavoriteAlbums": "Favourite Albums", "HeaderFavoriteArtists": "Favourite Artists", diff --git a/Emby.Server.Implementations/Localization/Core/en-US.json b/Emby.Server.Implementations/Localization/Core/en-US.json index 97a843160..92c54fb0e 100644 --- a/Emby.Server.Implementations/Localization/Core/en-US.json +++ b/Emby.Server.Implementations/Localization/Core/en-US.json @@ -16,7 +16,6 @@ "Folders": "Folders", "Genres": "Genres", "HeaderAlbumArtists": "Album Artists", - "HeaderCameraUploads": "Camera Uploads", "HeaderContinueWatching": "Continue Watching", "HeaderFavoriteAlbums": "Favorite Albums", "HeaderFavoriteArtists": "Favorite Artists", diff --git a/Emby.Server.Implementations/Localization/Core/es-AR.json b/Emby.Server.Implementations/Localization/Core/es-AR.json index ac96c788c..390074cdd 100644 --- a/Emby.Server.Implementations/Localization/Core/es-AR.json +++ b/Emby.Server.Implementations/Localization/Core/es-AR.json @@ -16,7 +16,6 @@ "Folders": "Carpetas", "Genres": "Géneros", "HeaderAlbumArtists": "Artistas de álbum", - "HeaderCameraUploads": "Subidas de cámara", "HeaderContinueWatching": "Seguir viendo", "HeaderFavoriteAlbums": "Álbumes favoritos", "HeaderFavoriteArtists": "Artistas favoritos", diff --git a/Emby.Server.Implementations/Localization/Core/es-MX.json b/Emby.Server.Implementations/Localization/Core/es-MX.json index 4ba324aa1..ab54c0ea6 100644 --- a/Emby.Server.Implementations/Localization/Core/es-MX.json +++ b/Emby.Server.Implementations/Localization/Core/es-MX.json @@ -16,7 +16,6 @@ "Folders": "Carpetas", "Genres": "Géneros", "HeaderAlbumArtists": "Artistas del álbum", - "HeaderCameraUploads": "Subidas desde la cámara", "HeaderContinueWatching": "Continuar viendo", "HeaderFavoriteAlbums": "Álbumes favoritos", "HeaderFavoriteArtists": "Artistas favoritos", diff --git a/Emby.Server.Implementations/Localization/Core/es.json b/Emby.Server.Implementations/Localization/Core/es.json index e7bd3959b..d4e0b299d 100644 --- a/Emby.Server.Implementations/Localization/Core/es.json +++ b/Emby.Server.Implementations/Localization/Core/es.json @@ -16,7 +16,6 @@ "Folders": "Carpetas", "Genres": "Géneros", "HeaderAlbumArtists": "Artistas del álbum", - "HeaderCameraUploads": "Subidas desde la cámara", "HeaderContinueWatching": "Continuar viendo", "HeaderFavoriteAlbums": "Álbumes favoritos", "HeaderFavoriteArtists": "Artistas favoritos", diff --git a/Emby.Server.Implementations/Localization/Core/es_419.json b/Emby.Server.Implementations/Localization/Core/es_419.json index 0959ef2ca..dcd30694f 100644 --- a/Emby.Server.Implementations/Localization/Core/es_419.json +++ b/Emby.Server.Implementations/Localization/Core/es_419.json @@ -105,7 +105,6 @@ "Inherit": "Heredar", "HomeVideos": "Videos caseros", "HeaderRecordingGroups": "Grupos de grabación", - "HeaderCameraUploads": "Subidas desde la cámara", "FailedLoginAttemptWithUserName": "Intento fallido de inicio de sesión desde {0}", "DeviceOnlineWithName": "{0} está conectado", "DeviceOfflineWithName": "{0} se ha desconectado", diff --git a/Emby.Server.Implementations/Localization/Core/es_DO.json b/Emby.Server.Implementations/Localization/Core/es_DO.json index 26732eb3f..b64ffbfbb 100644 --- a/Emby.Server.Implementations/Localization/Core/es_DO.json +++ b/Emby.Server.Implementations/Localization/Core/es_DO.json @@ -12,7 +12,6 @@ "Application": "Aplicación", "AppDeviceValues": "App: {0}, Dispositivo: {1}", "HeaderContinueWatching": "Continuar Viendo", - "HeaderCameraUploads": "Subidas de Cámara", "HeaderAlbumArtists": "Artistas del Álbum", "Genres": "Géneros", "Folders": "Carpetas", diff --git a/Emby.Server.Implementations/Localization/Core/fa.json b/Emby.Server.Implementations/Localization/Core/fa.json index 500c29217..1986decf0 100644 --- a/Emby.Server.Implementations/Localization/Core/fa.json +++ b/Emby.Server.Implementations/Localization/Core/fa.json @@ -16,7 +16,6 @@ "Folders": "پوشه‌ها", "Genres": "ژانرها", "HeaderAlbumArtists": "هنرمندان آلبوم", - "HeaderCameraUploads": "آپلودهای دوربین", "HeaderContinueWatching": "ادامه تماشا", "HeaderFavoriteAlbums": "آلبوم‌های مورد علاقه", "HeaderFavoriteArtists": "هنرمندان مورد علاقه", diff --git a/Emby.Server.Implementations/Localization/Core/fi.json b/Emby.Server.Implementations/Localization/Core/fi.json index f8d6e0e09..aae22583a 100644 --- a/Emby.Server.Implementations/Localization/Core/fi.json +++ b/Emby.Server.Implementations/Localization/Core/fi.json @@ -24,7 +24,6 @@ "HeaderFavoriteSongs": "Lempikappaleet", "HeaderFavoriteShows": "Lempisarjat", "HeaderFavoriteEpisodes": "Lempijaksot", - "HeaderCameraUploads": "Kamerasta Lähetetyt", "HeaderFavoriteArtists": "Lempiartistit", "HeaderFavoriteAlbums": "Lempialbumit", "HeaderContinueWatching": "Jatka katsomista", diff --git a/Emby.Server.Implementations/Localization/Core/fil.json b/Emby.Server.Implementations/Localization/Core/fil.json index 47daf2044..1a3e18832 100644 --- a/Emby.Server.Implementations/Localization/Core/fil.json +++ b/Emby.Server.Implementations/Localization/Core/fil.json @@ -73,7 +73,6 @@ "HeaderFavoriteArtists": "Paboritong Artista", "HeaderFavoriteAlbums": "Paboritong Albums", "HeaderContinueWatching": "Ituloy Manood", - "HeaderCameraUploads": "Camera Uploads", "HeaderAlbumArtists": "Artista ng Album", "Genres": "Kategorya", "Folders": "Folders", diff --git a/Emby.Server.Implementations/Localization/Core/fr-CA.json b/Emby.Server.Implementations/Localization/Core/fr-CA.json index cd1c8144f..3d7592e3c 100644 --- a/Emby.Server.Implementations/Localization/Core/fr-CA.json +++ b/Emby.Server.Implementations/Localization/Core/fr-CA.json @@ -16,7 +16,6 @@ "Folders": "Dossiers", "Genres": "Genres", "HeaderAlbumArtists": "Artistes de l'album", - "HeaderCameraUploads": "Photos transférées", "HeaderContinueWatching": "Continuer à regarder", "HeaderFavoriteAlbums": "Albums favoris", "HeaderFavoriteArtists": "Artistes favoris", diff --git a/Emby.Server.Implementations/Localization/Core/fr.json b/Emby.Server.Implementations/Localization/Core/fr.json index 7fc996821..f4ca8575a 100644 --- a/Emby.Server.Implementations/Localization/Core/fr.json +++ b/Emby.Server.Implementations/Localization/Core/fr.json @@ -16,7 +16,6 @@ "Folders": "Dossiers", "Genres": "Genres", "HeaderAlbumArtists": "Artistes", - "HeaderCameraUploads": "Photos transférées", "HeaderContinueWatching": "Continuer à regarder", "HeaderFavoriteAlbums": "Albums favoris", "HeaderFavoriteArtists": "Artistes préférés", diff --git a/Emby.Server.Implementations/Localization/Core/gsw.json b/Emby.Server.Implementations/Localization/Core/gsw.json index 8780a884b..ee1f8775e 100644 --- a/Emby.Server.Implementations/Localization/Core/gsw.json +++ b/Emby.Server.Implementations/Localization/Core/gsw.json @@ -16,7 +16,6 @@ "Folders": "Ordner", "Genres": "Genres", "HeaderAlbumArtists": "Album-Künstler", - "HeaderCameraUploads": "Kamera-Uploads", "HeaderContinueWatching": "weiter schauen", "HeaderFavoriteAlbums": "Lieblingsalben", "HeaderFavoriteArtists": "Lieblings-Künstler", diff --git a/Emby.Server.Implementations/Localization/Core/he.json b/Emby.Server.Implementations/Localization/Core/he.json index dc3a98154..f906d6e11 100644 --- a/Emby.Server.Implementations/Localization/Core/he.json +++ b/Emby.Server.Implementations/Localization/Core/he.json @@ -16,7 +16,6 @@ "Folders": "תיקיות", "Genres": "ז'אנרים", "HeaderAlbumArtists": "אמני האלבום", - "HeaderCameraUploads": "העלאות ממצלמה", "HeaderContinueWatching": "המשך לצפות", "HeaderFavoriteAlbums": "אלבומים מועדפים", "HeaderFavoriteArtists": "אמנים מועדפים", diff --git a/Emby.Server.Implementations/Localization/Core/hr.json b/Emby.Server.Implementations/Localization/Core/hr.json index 97c77017b..a3c936240 100644 --- a/Emby.Server.Implementations/Localization/Core/hr.json +++ b/Emby.Server.Implementations/Localization/Core/hr.json @@ -16,7 +16,6 @@ "Folders": "Mape", "Genres": "Žanrovi", "HeaderAlbumArtists": "Izvođači na albumu", - "HeaderCameraUploads": "Uvoz sa kamere", "HeaderContinueWatching": "Nastavi gledati", "HeaderFavoriteAlbums": "Omiljeni albumi", "HeaderFavoriteArtists": "Omiljeni izvođači", diff --git a/Emby.Server.Implementations/Localization/Core/hu.json b/Emby.Server.Implementations/Localization/Core/hu.json index c5c3844e3..343d213d4 100644 --- a/Emby.Server.Implementations/Localization/Core/hu.json +++ b/Emby.Server.Implementations/Localization/Core/hu.json @@ -16,7 +16,6 @@ "Folders": "Könyvtárak", "Genres": "Műfajok", "HeaderAlbumArtists": "Album előadók", - "HeaderCameraUploads": "Kamera feltöltések", "HeaderContinueWatching": "Megtekintés folytatása", "HeaderFavoriteAlbums": "Kedvenc albumok", "HeaderFavoriteArtists": "Kedvenc előadók", diff --git a/Emby.Server.Implementations/Localization/Core/id.json b/Emby.Server.Implementations/Localization/Core/id.json index 585fc6f02..ef3ed2580 100644 --- a/Emby.Server.Implementations/Localization/Core/id.json +++ b/Emby.Server.Implementations/Localization/Core/id.json @@ -20,7 +20,6 @@ "HeaderFavoriteArtists": "Artis Favorit", "HeaderFavoriteAlbums": "Album Favorit", "HeaderContinueWatching": "Lanjut Menonton", - "HeaderCameraUploads": "Unggahan Kamera", "HeaderAlbumArtists": "Album Artis", "Genres": "Aliran", "Folders": "Folder", diff --git a/Emby.Server.Implementations/Localization/Core/is.json b/Emby.Server.Implementations/Localization/Core/is.json index 0f0f9130b..0f769eaad 100644 --- a/Emby.Server.Implementations/Localization/Core/is.json +++ b/Emby.Server.Implementations/Localization/Core/is.json @@ -13,7 +13,6 @@ "HeaderFavoriteArtists": "Uppáhalds Listamenn", "HeaderFavoriteAlbums": "Uppáhalds Plötur", "HeaderContinueWatching": "Halda áfram að horfa", - "HeaderCameraUploads": "Myndavéla upphal", "HeaderAlbumArtists": "Höfundur plötu", "Genres": "Tegundir", "Folders": "Möppur", diff --git a/Emby.Server.Implementations/Localization/Core/it.json b/Emby.Server.Implementations/Localization/Core/it.json index bf1a0ef13..0a6238578 100644 --- a/Emby.Server.Implementations/Localization/Core/it.json +++ b/Emby.Server.Implementations/Localization/Core/it.json @@ -16,7 +16,6 @@ "Folders": "Cartelle", "Genres": "Generi", "HeaderAlbumArtists": "Artisti degli Album", - "HeaderCameraUploads": "Caricamenti Fotocamera", "HeaderContinueWatching": "Continua a guardare", "HeaderFavoriteAlbums": "Album Preferiti", "HeaderFavoriteArtists": "Artisti Preferiti", diff --git a/Emby.Server.Implementations/Localization/Core/ja.json b/Emby.Server.Implementations/Localization/Core/ja.json index a4d9f9ef6..35004f0eb 100644 --- a/Emby.Server.Implementations/Localization/Core/ja.json +++ b/Emby.Server.Implementations/Localization/Core/ja.json @@ -16,7 +16,6 @@ "Folders": "フォルダー", "Genres": "ジャンル", "HeaderAlbumArtists": "アルバムアーティスト", - "HeaderCameraUploads": "カメラアップロード", "HeaderContinueWatching": "視聴を続ける", "HeaderFavoriteAlbums": "お気に入りのアルバム", "HeaderFavoriteArtists": "お気に入りのアーティスト", diff --git a/Emby.Server.Implementations/Localization/Core/kk.json b/Emby.Server.Implementations/Localization/Core/kk.json index 5618ff4a8..91c1fb15b 100644 --- a/Emby.Server.Implementations/Localization/Core/kk.json +++ b/Emby.Server.Implementations/Localization/Core/kk.json @@ -16,7 +16,6 @@ "Folders": "Qaltalar", "Genres": "Janrlar", "HeaderAlbumArtists": "Álbom oryndaýshylary", - "HeaderCameraUploads": "Kameradan júktelgender", "HeaderContinueWatching": "Qaraýdy jalǵastyrý", "HeaderFavoriteAlbums": "Tańdaýly álbomdar", "HeaderFavoriteArtists": "Tańdaýly oryndaýshylar", diff --git a/Emby.Server.Implementations/Localization/Core/ko.json b/Emby.Server.Implementations/Localization/Core/ko.json index a33953c27..fb01e4645 100644 --- a/Emby.Server.Implementations/Localization/Core/ko.json +++ b/Emby.Server.Implementations/Localization/Core/ko.json @@ -16,7 +16,6 @@ "Folders": "폴더", "Genres": "장르", "HeaderAlbumArtists": "앨범 아티스트", - "HeaderCameraUploads": "카메라 업로드", "HeaderContinueWatching": "계속 시청하기", "HeaderFavoriteAlbums": "즐겨찾는 앨범", "HeaderFavoriteArtists": "즐겨찾는 아티스트", diff --git a/Emby.Server.Implementations/Localization/Core/lt-LT.json b/Emby.Server.Implementations/Localization/Core/lt-LT.json index 35053766b..d4cb592ef 100644 --- a/Emby.Server.Implementations/Localization/Core/lt-LT.json +++ b/Emby.Server.Implementations/Localization/Core/lt-LT.json @@ -16,7 +16,6 @@ "Folders": "Katalogai", "Genres": "Žanrai", "HeaderAlbumArtists": "Albumo atlikėjai", - "HeaderCameraUploads": "Kameros", "HeaderContinueWatching": "Žiūrėti toliau", "HeaderFavoriteAlbums": "Mėgstami Albumai", "HeaderFavoriteArtists": "Mėgstami Atlikėjai", diff --git a/Emby.Server.Implementations/Localization/Core/lv.json b/Emby.Server.Implementations/Localization/Core/lv.json index dbcf17287..5e3acfbe9 100644 --- a/Emby.Server.Implementations/Localization/Core/lv.json +++ b/Emby.Server.Implementations/Localization/Core/lv.json @@ -72,7 +72,6 @@ "ItemAddedWithName": "{0} tika pievienots bibliotēkai", "HeaderLiveTV": "Tiešraides TV", "HeaderContinueWatching": "Turpināt Skatīšanos", - "HeaderCameraUploads": "Kameras augšupielādes", "HeaderAlbumArtists": "Albumu Izpildītāji", "Genres": "Žanri", "Folders": "Mapes", diff --git a/Emby.Server.Implementations/Localization/Core/mk.json b/Emby.Server.Implementations/Localization/Core/mk.json index bbdf99aba..b780ef498 100644 --- a/Emby.Server.Implementations/Localization/Core/mk.json +++ b/Emby.Server.Implementations/Localization/Core/mk.json @@ -51,7 +51,6 @@ "HeaderFavoriteArtists": "Омилени Изведувачи", "HeaderFavoriteAlbums": "Омилени Албуми", "HeaderContinueWatching": "Продолжи со гледање", - "HeaderCameraUploads": "Поставувања од камера", "HeaderAlbumArtists": "Изведувачи од Албуми", "Genres": "Жанрови", "Folders": "Папки", diff --git a/Emby.Server.Implementations/Localization/Core/mr.json b/Emby.Server.Implementations/Localization/Core/mr.json index b6db2b0f2..fdb4171b5 100644 --- a/Emby.Server.Implementations/Localization/Core/mr.json +++ b/Emby.Server.Implementations/Localization/Core/mr.json @@ -54,7 +54,6 @@ "ItemAddedWithName": "{0} हे संग्रहालयात जोडले गेले", "HomeVideos": "घरचे व्हिडीयो", "HeaderRecordingGroups": "रेकॉर्डिंग गट", - "HeaderCameraUploads": "कॅमेरा अपलोड", "CameraImageUploadedFrom": "एक नवीन कॅमेरा चित्र {0} येथून अपलोड केले आहे", "Application": "अ‍ॅप्लिकेशन", "AppDeviceValues": "अ‍ॅप: {0}, यंत्र: {1}", diff --git a/Emby.Server.Implementations/Localization/Core/ms.json b/Emby.Server.Implementations/Localization/Core/ms.json index 7f8df1289..5e3d095ff 100644 --- a/Emby.Server.Implementations/Localization/Core/ms.json +++ b/Emby.Server.Implementations/Localization/Core/ms.json @@ -16,7 +16,6 @@ "Folders": "Fail-fail", "Genres": "Genre-genre", "HeaderAlbumArtists": "Album Artis-artis", - "HeaderCameraUploads": "Muatnaik Kamera", "HeaderContinueWatching": "Terus Menonton", "HeaderFavoriteAlbums": "Album-album Kegemaran", "HeaderFavoriteArtists": "Artis-artis Kegemaran", diff --git a/Emby.Server.Implementations/Localization/Core/nb.json b/Emby.Server.Implementations/Localization/Core/nb.json index 07a599121..245c3cd63 100644 --- a/Emby.Server.Implementations/Localization/Core/nb.json +++ b/Emby.Server.Implementations/Localization/Core/nb.json @@ -16,7 +16,6 @@ "Folders": "Mapper", "Genres": "Sjangre", "HeaderAlbumArtists": "Albumartister", - "HeaderCameraUploads": "Kameraopplastinger", "HeaderContinueWatching": "Fortsett å se", "HeaderFavoriteAlbums": "Favorittalbum", "HeaderFavoriteArtists": "Favorittartister", diff --git a/Emby.Server.Implementations/Localization/Core/ne.json b/Emby.Server.Implementations/Localization/Core/ne.json index 38c073709..8e820d40c 100644 --- a/Emby.Server.Implementations/Localization/Core/ne.json +++ b/Emby.Server.Implementations/Localization/Core/ne.json @@ -41,7 +41,6 @@ "HeaderFavoriteArtists": "मनपर्ने कलाकारहरू", "HeaderFavoriteAlbums": "मनपर्ने एल्बमहरू", "HeaderContinueWatching": "हेर्न जारी राख्नुहोस्", - "HeaderCameraUploads": "क्यामेरा अपलोडहरू", "HeaderAlbumArtists": "एल्बमका कलाकारहरू", "Genres": "विधाहरू", "Folders": "फोल्डरहरू", diff --git a/Emby.Server.Implementations/Localization/Core/nl.json b/Emby.Server.Implementations/Localization/Core/nl.json index 41c74d54d..e102b92b9 100644 --- a/Emby.Server.Implementations/Localization/Core/nl.json +++ b/Emby.Server.Implementations/Localization/Core/nl.json @@ -16,7 +16,6 @@ "Folders": "Mappen", "Genres": "Genres", "HeaderAlbumArtists": "Albumartiesten", - "HeaderCameraUploads": "Camera-uploads", "HeaderContinueWatching": "Kijken hervatten", "HeaderFavoriteAlbums": "Favoriete albums", "HeaderFavoriteArtists": "Favoriete artiesten", diff --git a/Emby.Server.Implementations/Localization/Core/nn.json b/Emby.Server.Implementations/Localization/Core/nn.json index fb6e81beb..6236515b2 100644 --- a/Emby.Server.Implementations/Localization/Core/nn.json +++ b/Emby.Server.Implementations/Localization/Core/nn.json @@ -19,7 +19,6 @@ "HeaderFavoriteArtists": "Favoritt Artistar", "HeaderFavoriteAlbums": "Favoritt Album", "HeaderContinueWatching": "Fortsett å sjå", - "HeaderCameraUploads": "Kamera Opplastingar", "HeaderAlbumArtists": "Album Artist", "Genres": "Sjangrar", "Folders": "Mapper", diff --git a/Emby.Server.Implementations/Localization/Core/pl.json b/Emby.Server.Implementations/Localization/Core/pl.json index bdc0d0169..003e591b3 100644 --- a/Emby.Server.Implementations/Localization/Core/pl.json +++ b/Emby.Server.Implementations/Localization/Core/pl.json @@ -16,7 +16,6 @@ "Folders": "Foldery", "Genres": "Gatunki", "HeaderAlbumArtists": "Wykonawcy albumów", - "HeaderCameraUploads": "Przekazane obrazy", "HeaderContinueWatching": "Kontynuuj odtwarzanie", "HeaderFavoriteAlbums": "Ulubione albumy", "HeaderFavoriteArtists": "Ulubieni wykonawcy", diff --git a/Emby.Server.Implementations/Localization/Core/pt-BR.json b/Emby.Server.Implementations/Localization/Core/pt-BR.json index 275195640..5e49ca702 100644 --- a/Emby.Server.Implementations/Localization/Core/pt-BR.json +++ b/Emby.Server.Implementations/Localization/Core/pt-BR.json @@ -16,7 +16,6 @@ "Folders": "Pastas", "Genres": "Gêneros", "HeaderAlbumArtists": "Artistas do Álbum", - "HeaderCameraUploads": "Envios da Câmera", "HeaderContinueWatching": "Continuar Assistindo", "HeaderFavoriteAlbums": "Álbuns Favoritos", "HeaderFavoriteArtists": "Artistas favoritos", diff --git a/Emby.Server.Implementations/Localization/Core/pt-PT.json b/Emby.Server.Implementations/Localization/Core/pt-PT.json index c1fb65743..8aa1ed549 100644 --- a/Emby.Server.Implementations/Localization/Core/pt-PT.json +++ b/Emby.Server.Implementations/Localization/Core/pt-PT.json @@ -16,7 +16,6 @@ "Folders": "Pastas", "Genres": "Géneros", "HeaderAlbumArtists": "Artistas do Álbum", - "HeaderCameraUploads": "Envios a partir da câmara", "HeaderContinueWatching": "Continuar a Ver", "HeaderFavoriteAlbums": "Álbuns Favoritos", "HeaderFavoriteArtists": "Artistas Favoritos", diff --git a/Emby.Server.Implementations/Localization/Core/pt.json b/Emby.Server.Implementations/Localization/Core/pt.json index b534d0bbe..2079940cd 100644 --- a/Emby.Server.Implementations/Localization/Core/pt.json +++ b/Emby.Server.Implementations/Localization/Core/pt.json @@ -83,7 +83,6 @@ "Playlists": "Listas de Reprodução", "Photos": "Fotografias", "Movies": "Filmes", - "HeaderCameraUploads": "Carregamentos a partir da câmara", "FailedLoginAttemptWithUserName": "Tentativa de ligação falhada a partir de {0}", "DeviceOnlineWithName": "{0} está connectado", "DeviceOfflineWithName": "{0} desconectou-se", diff --git a/Emby.Server.Implementations/Localization/Core/ro.json b/Emby.Server.Implementations/Localization/Core/ro.json index 699dd26da..bc008df3b 100644 --- a/Emby.Server.Implementations/Localization/Core/ro.json +++ b/Emby.Server.Implementations/Localization/Core/ro.json @@ -74,7 +74,6 @@ "HeaderFavoriteArtists": "Artiști Favoriți", "HeaderFavoriteAlbums": "Albume Favorite", "HeaderContinueWatching": "Vizionează în continuare", - "HeaderCameraUploads": "Incărcări Cameră Foto", "HeaderAlbumArtists": "Album Artiști", "Genres": "Genuri", "Folders": "Dosare", diff --git a/Emby.Server.Implementations/Localization/Core/ru.json b/Emby.Server.Implementations/Localization/Core/ru.json index 648aa384b..95b93afb8 100644 --- a/Emby.Server.Implementations/Localization/Core/ru.json +++ b/Emby.Server.Implementations/Localization/Core/ru.json @@ -16,7 +16,6 @@ "Folders": "Папки", "Genres": "Жанры", "HeaderAlbumArtists": "Исполнители альбома", - "HeaderCameraUploads": "Камеры", "HeaderContinueWatching": "Продолжение просмотра", "HeaderFavoriteAlbums": "Избранные альбомы", "HeaderFavoriteArtists": "Избранные исполнители", diff --git a/Emby.Server.Implementations/Localization/Core/sk.json b/Emby.Server.Implementations/Localization/Core/sk.json index 0ee652637..8e5026944 100644 --- a/Emby.Server.Implementations/Localization/Core/sk.json +++ b/Emby.Server.Implementations/Localization/Core/sk.json @@ -16,7 +16,6 @@ "Folders": "Priečinky", "Genres": "Žánre", "HeaderAlbumArtists": "Umelci albumu", - "HeaderCameraUploads": "Nahrané fotografie", "HeaderContinueWatching": "Pokračovať v pozeraní", "HeaderFavoriteAlbums": "Obľúbené albumy", "HeaderFavoriteArtists": "Obľúbení umelci", diff --git a/Emby.Server.Implementations/Localization/Core/sl-SI.json b/Emby.Server.Implementations/Localization/Core/sl-SI.json index 329c562e7..ff4b9e84f 100644 --- a/Emby.Server.Implementations/Localization/Core/sl-SI.json +++ b/Emby.Server.Implementations/Localization/Core/sl-SI.json @@ -16,7 +16,6 @@ "Folders": "Mape", "Genres": "Zvrsti", "HeaderAlbumArtists": "Izvajalci albuma", - "HeaderCameraUploads": "Posnetki kamere", "HeaderContinueWatching": "Nadaljuj gledanje", "HeaderFavoriteAlbums": "Priljubljeni albumi", "HeaderFavoriteArtists": "Priljubljeni izvajalci", diff --git a/Emby.Server.Implementations/Localization/Core/sq.json b/Emby.Server.Implementations/Localization/Core/sq.json index 347ba5f97..ecca5af4c 100644 --- a/Emby.Server.Implementations/Localization/Core/sq.json +++ b/Emby.Server.Implementations/Localization/Core/sq.json @@ -96,7 +96,6 @@ "HeaderFavoriteArtists": "Artistët e preferuar", "HeaderFavoriteAlbums": "Albumet e preferuar", "HeaderContinueWatching": "Vazhdo të shikosh", - "HeaderCameraUploads": "Ngarkimet nga Kamera", "HeaderAlbumArtists": "Artistët e albumeve", "Genres": "Zhanre", "Folders": "Dosje", diff --git a/Emby.Server.Implementations/Localization/Core/sr.json b/Emby.Server.Implementations/Localization/Core/sr.json index 5f3cbb1c8..2b1eccfaf 100644 --- a/Emby.Server.Implementations/Localization/Core/sr.json +++ b/Emby.Server.Implementations/Localization/Core/sr.json @@ -74,7 +74,6 @@ "HeaderFavoriteArtists": "Омиљени извођачи", "HeaderFavoriteAlbums": "Омиљени албуми", "HeaderContinueWatching": "Настави гледање", - "HeaderCameraUploads": "Слања са камере", "HeaderAlbumArtists": "Извођачи албума", "Genres": "Жанрови", "Folders": "Фасцикле", diff --git a/Emby.Server.Implementations/Localization/Core/sv.json b/Emby.Server.Implementations/Localization/Core/sv.json index c8662b2ca..12fda8a98 100644 --- a/Emby.Server.Implementations/Localization/Core/sv.json +++ b/Emby.Server.Implementations/Localization/Core/sv.json @@ -16,7 +16,6 @@ "Folders": "Mappar", "Genres": "Genrer", "HeaderAlbumArtists": "Albumartister", - "HeaderCameraUploads": "Kamerauppladdningar", "HeaderContinueWatching": "Fortsätt kolla", "HeaderFavoriteAlbums": "Favoritalbum", "HeaderFavoriteArtists": "Favoritartister", diff --git a/Emby.Server.Implementations/Localization/Core/ta.json b/Emby.Server.Implementations/Localization/Core/ta.json index 810b1b9ab..ae38f45e1 100644 --- a/Emby.Server.Implementations/Localization/Core/ta.json +++ b/Emby.Server.Implementations/Localization/Core/ta.json @@ -20,7 +20,6 @@ "MessageApplicationUpdated": "ஜெல்லிஃபின் சேவையகம் புதுப்பிக்கப்பட்டது", "Inherit": "மரபுரிமையாகப் பெறு", "HeaderRecordingGroups": "பதிவு குழுக்கள்", - "HeaderCameraUploads": "புகைப்பட பதிவேற்றங்கள்", "Folders": "கோப்புறைகள்", "FailedLoginAttemptWithUserName": "{0} இலிருந்து உள்நுழைவு முயற்சி தோல்வியடைந்தது", "DeviceOnlineWithName": "{0} இணைக்கப்பட்டது", diff --git a/Emby.Server.Implementations/Localization/Core/th.json b/Emby.Server.Implementations/Localization/Core/th.json index 3b77215a3..71dd2c7a3 100644 --- a/Emby.Server.Implementations/Localization/Core/th.json +++ b/Emby.Server.Implementations/Localization/Core/th.json @@ -50,7 +50,6 @@ "HeaderFavoriteArtists": "ศิลปินที่ชื่นชอบ", "HeaderFavoriteAlbums": "อัมบั้มที่ชื่นชอบ", "HeaderContinueWatching": "ดูต่อ", - "HeaderCameraUploads": "อัปโหลดรูปถ่าย", "HeaderAlbumArtists": "อัลบั้มศิลปิน", "Genres": "ประเภท", "Folders": "โฟลเดอร์", diff --git a/Emby.Server.Implementations/Localization/Core/tr.json b/Emby.Server.Implementations/Localization/Core/tr.json index 3cf3482eb..1e5f2cf19 100644 --- a/Emby.Server.Implementations/Localization/Core/tr.json +++ b/Emby.Server.Implementations/Localization/Core/tr.json @@ -16,7 +16,6 @@ "Folders": "Klasörler", "Genres": "Türler", "HeaderAlbumArtists": "Albüm Sanatçıları", - "HeaderCameraUploads": "Kamera Yüklemeleri", "HeaderContinueWatching": "İzlemeye Devam Et", "HeaderFavoriteAlbums": "Favori Albümler", "HeaderFavoriteArtists": "Favori Sanatçılar", diff --git a/Emby.Server.Implementations/Localization/Core/uk.json b/Emby.Server.Implementations/Localization/Core/uk.json index e673465a4..06cc5f633 100644 --- a/Emby.Server.Implementations/Localization/Core/uk.json +++ b/Emby.Server.Implementations/Localization/Core/uk.json @@ -16,7 +16,6 @@ "HeaderFavoriteArtists": "Улюблені виконавці", "HeaderFavoriteAlbums": "Улюблені альбоми", "HeaderContinueWatching": "Продовжити перегляд", - "HeaderCameraUploads": "Завантажено з камери", "HeaderAlbumArtists": "Виконавці альбому", "Genres": "Жанри", "Folders": "Каталоги", diff --git a/Emby.Server.Implementations/Localization/Core/ur_PK.json b/Emby.Server.Implementations/Localization/Core/ur_PK.json index 9a5874e29..fa7b2d4d0 100644 --- a/Emby.Server.Implementations/Localization/Core/ur_PK.json +++ b/Emby.Server.Implementations/Localization/Core/ur_PK.json @@ -105,7 +105,6 @@ "Inherit": "وراثت میں", "HomeVideos": "ہوم ویڈیو", "HeaderRecordingGroups": "ریکارڈنگ گروپس", - "HeaderCameraUploads": "کیمرہ اپلوڈز", "FailedLoginAttemptWithUserName": "لاگن کئ کوشش ناکام {0}", "DeviceOnlineWithName": "{0} متصل ھو چکا ھے", "DeviceOfflineWithName": "{0} منقطع ھو چکا ھے", diff --git a/Emby.Server.Implementations/Localization/Core/vi.json b/Emby.Server.Implementations/Localization/Core/vi.json index 2392c8347..ac74deff8 100644 --- a/Emby.Server.Implementations/Localization/Core/vi.json +++ b/Emby.Server.Implementations/Localization/Core/vi.json @@ -103,7 +103,6 @@ "HeaderFavoriteEpisodes": "Tập Phim Yêu Thích", "HeaderFavoriteArtists": "Nghệ Sĩ Yêu Thích", "HeaderFavoriteAlbums": "Album Ưa Thích", - "HeaderCameraUploads": "Máy Ảnh Tải Lên", "FailedLoginAttemptWithUserName": "Nỗ lực đăng nhập thất bại từ {0}", "DeviceOnlineWithName": "{0} đã kết nối", "DeviceOfflineWithName": "{0} đã ngắt kết nối", diff --git a/Emby.Server.Implementations/Localization/Core/zh-CN.json b/Emby.Server.Implementations/Localization/Core/zh-CN.json index 6b563a9b1..53a902de2 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-CN.json +++ b/Emby.Server.Implementations/Localization/Core/zh-CN.json @@ -16,7 +16,6 @@ "Folders": "文件夹", "Genres": "风格", "HeaderAlbumArtists": "专辑作家", - "HeaderCameraUploads": "相机上传", "HeaderContinueWatching": "继续观影", "HeaderFavoriteAlbums": "收藏的专辑", "HeaderFavoriteArtists": "最爱的艺术家", diff --git a/Emby.Server.Implementations/Localization/Core/zh-HK.json b/Emby.Server.Implementations/Localization/Core/zh-HK.json index 1ac62baca..435e294ef 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-HK.json +++ b/Emby.Server.Implementations/Localization/Core/zh-HK.json @@ -16,7 +16,6 @@ "Folders": "檔案夾", "Genres": "風格", "HeaderAlbumArtists": "專輯藝人", - "HeaderCameraUploads": "相機上載", "HeaderContinueWatching": "繼續觀看", "HeaderFavoriteAlbums": "最愛專輯", "HeaderFavoriteArtists": "最愛的藝人", diff --git a/Emby.Server.Implementations/Localization/Core/zh-TW.json b/Emby.Server.Implementations/Localization/Core/zh-TW.json index 7b6540c3e..30f726630 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-TW.json +++ b/Emby.Server.Implementations/Localization/Core/zh-TW.json @@ -16,7 +16,6 @@ "Folders": "資料夾", "Genres": "風格", "HeaderAlbumArtists": "專輯演出者", - "HeaderCameraUploads": "相機上傳", "HeaderContinueWatching": "繼續觀賞", "HeaderFavoriteAlbums": "最愛專輯", "HeaderFavoriteArtists": "最愛演出者", -- cgit v1.2.3 From d91a4f0c6d81177b03f0543056456058604e0ce1 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Thu, 1 Oct 2020 20:25:40 +0100 Subject: Update ApplicationHost.cs --- Emby.Server.Implementations/ApplicationHost.cs | 32 -------------------------- 1 file changed, 32 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 7a46fdf2e..b4f7319f8 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -819,38 +819,6 @@ namespace Emby.Server.Implementations { try { - if (plugin is IPluginAssembly assemblyPlugin) - { - var assembly = plugin.GetType().Assembly; - var assemblyName = assembly.GetName(); - var assemblyFilePath = assembly.Location; - - var dataFolderPath = Path.Combine(ApplicationPaths.PluginsPath, Path.GetFileNameWithoutExtension(assemblyFilePath)); - - assemblyPlugin.SetAttributes(assemblyFilePath, dataFolderPath, assemblyName.Version); - - try - { - var idAttributes = assembly.GetCustomAttributes(typeof(GuidAttribute), true); - if (idAttributes.Length > 0) - { - var attribute = (GuidAttribute)idAttributes[0]; - var assemblyId = new Guid(attribute.Value); - - assemblyPlugin.SetId(assemblyId); - } - } - catch (Exception ex) - { - Logger.LogError(ex, "Error getting plugin Id from {PluginName}.", plugin.GetType().FullName); - } - } - - if (plugin is IHasPluginConfiguration hasPluginConfiguration) - { - hasPluginConfiguration.SetStartupInfo(s => Directory.CreateDirectory(s)); - } - plugin.RegisterServices(ServiceCollection); } catch (Exception ex) -- cgit v1.2.3 From e9524f89d63894ea9af62cd1b61ddd89cb8b9e82 Mon Sep 17 00:00:00 2001 From: cvium Date: Thu, 24 Sep 2020 19:49:35 +0200 Subject: Migrate the TMDb providers to the TMDbLib library --- Emby.Server.Implementations/ApplicationHost.cs | 2 + .../Extensions/EnumerableExtensions.cs | 46 ++ .../MediaBrowser.Providers.csproj | 1 + .../Tmdb/BoxSets/TmdbBoxSetImageProvider.cs | 143 ++---- .../Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs | 254 ++-------- .../Tmdb/Models/Collections/CollectionImages.cs | 14 - .../Tmdb/Models/Collections/CollectionResult.cs | 23 - .../Plugins/Tmdb/Models/Collections/Part.cs | 17 - .../Plugins/Tmdb/Models/General/Backdrop.cs | 21 - .../Plugins/Tmdb/Models/General/Crew.cs | 19 - .../Plugins/Tmdb/Models/General/ExternalIds.cs | 17 - .../Plugins/Tmdb/Models/General/Genre.cs | 11 - .../Plugins/Tmdb/Models/General/Images.cs | 13 - .../Plugins/Tmdb/Models/General/Keyword.cs | 11 - .../Plugins/Tmdb/Models/General/Keywords.cs | 11 - .../Plugins/Tmdb/Models/General/Poster.cs | 21 - .../Plugins/Tmdb/Models/General/Profile.cs | 17 - .../Plugins/Tmdb/Models/General/Still.cs | 23 - .../Plugins/Tmdb/Models/General/StillImages.cs | 11 - .../Plugins/Tmdb/Models/General/Video.cs | 23 - .../Plugins/Tmdb/Models/General/Videos.cs | 11 - .../Tmdb/Models/Movies/BelongsToCollection.cs | 15 - .../Plugins/Tmdb/Models/Movies/Cast.cs | 19 - .../Plugins/Tmdb/Models/Movies/Casts.cs | 14 - .../Plugins/Tmdb/Models/Movies/Country.cs | 15 - .../Plugins/Tmdb/Models/Movies/MovieResult.cs | 80 ---- .../Tmdb/Models/Movies/ProductionCompany.cs | 11 - .../Tmdb/Models/Movies/ProductionCountry.cs | 11 - .../Plugins/Tmdb/Models/Movies/Releases.cs | 11 - .../Plugins/Tmdb/Models/Movies/SpokenLanguage.cs | 11 - .../Plugins/Tmdb/Models/Movies/Trailers.cs | 11 - .../Plugins/Tmdb/Models/Movies/Youtube.cs | 13 - .../Plugins/Tmdb/Models/People/PersonImages.cs | 12 - .../Plugins/Tmdb/Models/People/PersonResult.cs | 38 -- .../Tmdb/Models/Search/ExternalIdLookupResult.cs | 11 - .../Plugins/Tmdb/Models/Search/MovieResult.cs | 78 ---- .../Tmdb/Models/Search/PersonSearchResult.cs | 31 -- .../Plugins/Tmdb/Models/Search/TmdbSearchResult.cs | 33 -- .../Plugins/Tmdb/Models/Search/TvResult.cs | 25 - .../Plugins/Tmdb/Models/TV/Cast.cs | 19 - .../Plugins/Tmdb/Models/TV/ContentRating.cs | 11 - .../Plugins/Tmdb/Models/TV/ContentRatings.cs | 11 - .../Plugins/Tmdb/Models/TV/CreatedBy.cs | 13 - .../Plugins/Tmdb/Models/TV/Credits.cs | 14 - .../Plugins/Tmdb/Models/TV/Episode.cs | 23 - .../Plugins/Tmdb/Models/TV/EpisodeCredits.cs | 16 - .../Plugins/Tmdb/Models/TV/EpisodeResult.cs | 38 -- .../Plugins/Tmdb/Models/TV/GuestStar.cs | 19 - .../Plugins/Tmdb/Models/TV/Network.cs | 11 - .../Plugins/Tmdb/Models/TV/Season.cs | 17 - .../Plugins/Tmdb/Models/TV/SeasonImages.cs | 12 - .../Plugins/Tmdb/Models/TV/SeasonResult.cs | 33 -- .../Plugins/Tmdb/Models/TV/SeriesResult.cs | 71 --- .../Plugins/Tmdb/Movies/GenericTmdbMovieInfo.cs | 309 ------------- .../Plugins/Tmdb/Movies/TmdbImageProvider.cs | 212 --------- .../Plugins/Tmdb/Movies/TmdbImageSettings.cs | 22 - .../Plugins/Tmdb/Movies/TmdbMovieExternalId.cs | 3 +- .../Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs | 128 +++++ .../Plugins/Tmdb/Movies/TmdbMovieProvider.cs | 458 +++++++----------- .../Plugins/Tmdb/Movies/TmdbSearch.cs | 302 ------------ .../Plugins/Tmdb/Music/TmdbMusicVideoProvider.cs | 34 -- .../Plugins/Tmdb/People/TmdbPersonImageProvider.cs | 100 ++-- .../Plugins/Tmdb/People/TmdbPersonProvider.cs | 244 +++------- .../Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs | 112 ++--- .../Plugins/Tmdb/TV/TmdbEpisodeProvider.cs | 235 +++++----- .../Plugins/Tmdb/TV/TmdbEpisodeProviderBase.cs | 156 ------- .../Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs | 106 ++--- .../Plugins/Tmdb/TV/TmdbSeasonProvider.cs | 241 +++------- .../Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs | 159 ++----- .../Plugins/Tmdb/TV/TmdbSeriesProvider.cs | 513 +++++++-------------- .../Plugins/Tmdb/TmdbClientManager.cs | 469 +++++++++++++++++++ MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs | 90 +++- .../Plugins/Tmdb/Trailers/TmdbTrailerProvider.cs | 43 -- 73 files changed, 1570 insertions(+), 3822 deletions(-) create mode 100644 MediaBrowser.Model/Extensions/EnumerableExtensions.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Models/Collections/CollectionImages.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Models/Collections/CollectionResult.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Models/Collections/Part.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Models/General/Backdrop.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Models/General/Crew.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Models/General/ExternalIds.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Models/General/Genre.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Models/General/Images.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Models/General/Keyword.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Models/General/Keywords.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Models/General/Poster.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Models/General/Profile.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Models/General/Still.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Models/General/StillImages.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Models/General/Video.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Models/General/Videos.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/BelongsToCollection.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/Cast.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/Casts.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/Country.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/MovieResult.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/ProductionCompany.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/ProductionCountry.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/Releases.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/SpokenLanguage.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/Trailers.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/Youtube.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Models/People/PersonImages.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Models/People/PersonResult.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Models/Search/ExternalIdLookupResult.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Models/Search/MovieResult.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Models/Search/PersonSearchResult.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Models/Search/TmdbSearchResult.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Models/Search/TvResult.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Models/TV/Cast.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Models/TV/ContentRating.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Models/TV/ContentRatings.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Models/TV/CreatedBy.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Models/TV/Credits.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Models/TV/Episode.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Models/TV/EpisodeCredits.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Models/TV/EpisodeResult.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Models/TV/GuestStar.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Models/TV/Network.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Models/TV/Season.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Models/TV/SeasonImages.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Models/TV/SeasonResult.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Models/TV/SeriesResult.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Movies/GenericTmdbMovieInfo.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbImageProvider.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbImageSettings.cs create mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbSearch.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Music/TmdbMusicVideoProvider.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProviderBase.cs create mode 100644 MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs delete mode 100644 MediaBrowser.Providers/Plugins/Tmdb/Trailers/TmdbTrailerProvider.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 7a46fdf2e..0c8b0339b 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -97,6 +97,7 @@ using MediaBrowser.Model.Tasks; using MediaBrowser.Providers.Chapters; using MediaBrowser.Providers.Manager; using MediaBrowser.Providers.Plugins.TheTvdb; +using MediaBrowser.Providers.Plugins.Tmdb; using MediaBrowser.Providers.Subtitles; using MediaBrowser.XbmcMetadata.Providers; using Microsoft.AspNetCore.Mvc; @@ -537,6 +538,7 @@ namespace Emby.Server.Implementations ServiceCollection.AddSingleton(_fileSystemManager); ServiceCollection.AddSingleton(); + ServiceCollection.AddSingleton(); ServiceCollection.AddSingleton(_networkManager); diff --git a/MediaBrowser.Model/Extensions/EnumerableExtensions.cs b/MediaBrowser.Model/Extensions/EnumerableExtensions.cs new file mode 100644 index 000000000..712fa381e --- /dev/null +++ b/MediaBrowser.Model/Extensions/EnumerableExtensions.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using MediaBrowser.Model.Providers; + +namespace MediaBrowser.Model.Extensions +{ + /// + /// Extension methods for . + /// + public static class EnumerableExtensions + { + /// + /// Orders by requested language in descending order, prioritizing "en" over other non-matches. + /// + /// The remote image infos. + /// The requested language for the images. + /// The ordered remote image infos. + public static IEnumerable OrderByLanguageDescending(this IEnumerable remoteImageInfos, string requestedLanguage) + { + var isRequestedLanguageEn = string.Equals(requestedLanguage, "en", StringComparison.OrdinalIgnoreCase); + + return remoteImageInfos.OrderByDescending(i => + { + if (string.Equals(requestedLanguage, i.Language, StringComparison.OrdinalIgnoreCase)) + { + return 3; + } + + if (!isRequestedLanguageEn && string.Equals("en", i.Language, StringComparison.OrdinalIgnoreCase)) + { + return 2; + } + + if (string.IsNullOrEmpty(i.Language)) + { + return isRequestedLanguageEn ? 3 : 2; + } + + return 0; + }) + .ThenByDescending(i => i.CommunityRating ?? 0) + .ThenByDescending(i => i.VoteCount ?? 0); + } + } +} diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 813dd441f..11e30940f 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -21,6 +21,7 @@ + diff --git a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs index f6592afe4..df1e12240 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Net.Http; using System.Threading; @@ -12,25 +13,25 @@ using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Extensions; using MediaBrowser.Model.Providers; -using MediaBrowser.Providers.Plugins.Tmdb.Models.Collections; -using MediaBrowser.Providers.Plugins.Tmdb.Models.General; -using MediaBrowser.Providers.Plugins.Tmdb.Movies; namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets { public class TmdbBoxSetImageProvider : IRemoteImageProvider, IHasOrder { private readonly IHttpClientFactory _httpClientFactory; + private readonly TmdbClientManager _tmdbClientManager; - public TmdbBoxSetImageProvider(IHttpClientFactory httpClientFactory) + public TmdbBoxSetImageProvider(IHttpClientFactory httpClientFactory, TmdbClientManager tmdbClientManager) { _httpClientFactory = httpClientFactory; + _tmdbClientManager = tmdbClientManager; } - public string Name => ProviderName; + public string Name => TmdbUtils.ProviderName; - public static string ProviderName => TmdbUtils.ProviderName; + public int Order => 0; public bool Supports(BaseItem item) { @@ -48,112 +49,60 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets public async Task> GetImages(BaseItem item, CancellationToken cancellationToken) { - var tmdbId = item.GetProviderId(MetadataProvider.Tmdb); + var tmdbId = Convert.ToInt32(item.GetProviderId(MetadataProvider.Tmdb), CultureInfo.InvariantCulture); - if (!string.IsNullOrEmpty(tmdbId)) + if (tmdbId <= 0) { - var language = item.GetPreferredMetadataLanguage(); - - var mainResult = await TmdbBoxSetProvider.Current.GetMovieDbResult(tmdbId, null, cancellationToken).ConfigureAwait(false); + return Enumerable.Empty(); + } - if (mainResult != null) - { - var tmdbSettings = await TmdbMovieProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); + var language = item.GetPreferredMetadataLanguage(); - var tmdbImageUrl = tmdbSettings.images.GetImageUrl("original"); + var collection = await _tmdbClientManager.GetCollectionAsync(tmdbId, language, TmdbUtils.GetImageLanguagesParam(language), cancellationToken).ConfigureAwait(false); - return GetImages(mainResult, language, tmdbImageUrl); - } + if (collection?.Images == null) + { + return Enumerable.Empty(); } - return new List(); - } + var remoteImages = new List(); - private IEnumerable GetImages(CollectionResult obj, string language, string baseUrl) - { - var list = new List(); - - var images = obj.Images ?? new CollectionImages(); - - list.AddRange(GetPosters(images).Select(i => new RemoteImageInfo - { - Url = baseUrl + i.File_Path, - CommunityRating = i.Vote_Average, - VoteCount = i.Vote_Count, - Width = i.Width, - Height = i.Height, - Language = TmdbMovieProvider.AdjustImageLanguage(i.Iso_639_1, language), - ProviderName = Name, - Type = ImageType.Primary, - RatingType = RatingType.Score - })); - - list.AddRange(GetBackdrops(images).Select(i => new RemoteImageInfo + for (var i = 0; i < collection.Images.Posters.Count; i++) { - Url = baseUrl + i.File_Path, - CommunityRating = i.Vote_Average, - VoteCount = i.Vote_Count, - Width = i.Width, - Height = i.Height, - ProviderName = Name, - Type = ImageType.Backdrop, - RatingType = RatingType.Score - })); - - var isLanguageEn = string.Equals(language, "en", StringComparison.OrdinalIgnoreCase); - - return list.OrderByDescending(i => - { - if (string.Equals(language, i.Language, StringComparison.OrdinalIgnoreCase)) - { - return 3; - } - - if (!isLanguageEn) + var poster = collection.Images.Posters[i]; + remoteImages.Add(new RemoteImageInfo { - if (string.Equals("en", i.Language, StringComparison.OrdinalIgnoreCase)) - { - return 2; - } - } + Url = _tmdbClientManager.GetPosterUrl(poster.FilePath), + CommunityRating = poster.VoteAverage, + VoteCount = poster.VoteCount, + Width = poster.Width, + Height = poster.Height, + Language = TmdbUtils.AdjustImageLanguage(poster.Iso_639_1, language), + ProviderName = Name, + Type = ImageType.Primary, + RatingType = RatingType.Score + }); + } - if (string.IsNullOrEmpty(i.Language)) + for (var i = 0; i < collection.Images.Backdrops.Count; i++) + { + var backdrop = collection.Images.Backdrops[i]; + remoteImages.Add(new RemoteImageInfo { - return isLanguageEn ? 3 : 2; - } - - return 0; - }) - .ThenByDescending(i => i.CommunityRating ?? 0) - .ThenByDescending(i => i.VoteCount ?? 0); - } - - /// - /// Gets the posters. - /// - /// The images. - /// IEnumerable{MovieDbProvider.Poster}. - private IEnumerable GetPosters(CollectionImages images) - { - return images.Posters ?? new List(); - } - - /// - /// Gets the backdrops. - /// - /// The images. - /// IEnumerable{MovieDbProvider.Backdrop}. - private IEnumerable GetBackdrops(CollectionImages images) - { - var eligibleBackdrops = images.Backdrops == null ? new List() : - images.Backdrops; + Url = _tmdbClientManager.GetBackdropUrl(backdrop.FilePath), + CommunityRating = backdrop.VoteAverage, + VoteCount = backdrop.VoteCount, + Width = backdrop.Width, + Height = backdrop.Height, + ProviderName = Name, + Type = ImageType.Backdrop, + RatingType = RatingType.Score + }); + } - return eligibleBackdrops.OrderByDescending(i => i.Vote_Average) - .ThenByDescending(i => i.Vote_Count); + return remoteImages.OrderByLanguageDescending(language); } - public int Order => 0; - public Task GetImageResponse(string url, CancellationToken cancellationToken) { return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken); diff --git a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs index e7328b553..fcd8e614c 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs @@ -3,268 +3,116 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.IO; using System.Linq; using System.Net.Http; -using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities.Movies; -using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Globalization; -using MediaBrowser.Model.IO; using MediaBrowser.Model.Providers; -using MediaBrowser.Model.Serialization; -using MediaBrowser.Providers.Plugins.Tmdb.Models.Collections; -using MediaBrowser.Providers.Plugins.Tmdb.Models.General; -using MediaBrowser.Providers.Plugins.Tmdb.Movies; -using Microsoft.Extensions.Logging; namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets { public class TmdbBoxSetProvider : IRemoteMetadataProvider { - private const string GetCollectionInfo3 = TmdbUtils.BaseTmdbApiUrl + @"3/collection/{0}?api_key={1}&append_to_response=images"; - - internal static TmdbBoxSetProvider Current; - - private readonly ILogger _logger; - private readonly IJsonSerializer _json; - private readonly IServerConfigurationManager _config; - private readonly IFileSystem _fileSystem; private readonly IHttpClientFactory _httpClientFactory; - private readonly ILibraryManager _libraryManager; + private readonly TmdbClientManager _tmdbClientManager; - public TmdbBoxSetProvider( - ILogger logger, - IJsonSerializer json, - IServerConfigurationManager config, - IFileSystem fileSystem, - IHttpClientFactory httpClientFactory, - ILibraryManager libraryManager) + public TmdbBoxSetProvider(IHttpClientFactory httpClientFactory, TmdbClientManager tmdbClientManager) { - _logger = logger; - _json = json; - _config = config; - _fileSystem = fileSystem; _httpClientFactory = httpClientFactory; - _libraryManager = libraryManager; - Current = this; + _tmdbClientManager = tmdbClientManager; } - private readonly CultureInfo _usCulture = new CultureInfo("en-US"); + public string Name => TmdbUtils.ProviderName; public async Task> GetSearchResults(BoxSetInfo searchInfo, CancellationToken cancellationToken) { - var tmdbId = searchInfo.GetProviderId(MetadataProvider.Tmdb); + var tmdbId = Convert.ToInt32(searchInfo.GetProviderId(MetadataProvider.Tmdb), CultureInfo.InvariantCulture); + var language = searchInfo.MetadataLanguage; - if (!string.IsNullOrEmpty(tmdbId)) + if (tmdbId > 0) { - await EnsureInfo(tmdbId, searchInfo.MetadataLanguage, cancellationToken).ConfigureAwait(false); - - var dataFilePath = GetDataFilePath(_config.ApplicationPaths, tmdbId, searchInfo.MetadataLanguage); - var info = _json.DeserializeFromFile(dataFilePath); - - var images = (info.Images ?? new CollectionImages()).Posters ?? new List(); - - var tmdbSettings = await TmdbMovieProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); + var collection = await _tmdbClientManager.GetCollectionAsync(tmdbId, language, TmdbUtils.GetImageLanguagesParam(language), cancellationToken).ConfigureAwait(false); - var tmdbImageUrl = tmdbSettings.images.GetImageUrl("original"); + if (collection == null) + { + return Enumerable.Empty(); + } var result = new RemoteSearchResult { - Name = info.Name, - SearchProviderName = Name, - ImageUrl = images.Count == 0 ? null : (tmdbImageUrl + images[0].File_Path) + Name = collection.Name, + SearchProviderName = Name }; - result.SetProviderId(MetadataProvider.Tmdb, info.Id.ToString(_usCulture)); - - return new[] { result }; - } - - return await new TmdbSearch(_logger, _json, _libraryManager).GetSearchResults(searchInfo, cancellationToken).ConfigureAwait(false); - } - - public async Task> GetMetadata(BoxSetInfo id, CancellationToken cancellationToken) - { - var tmdbId = id.GetProviderId(MetadataProvider.Tmdb); - - // We don't already have an Id, need to fetch it - if (string.IsNullOrEmpty(tmdbId)) - { - var searchResults = await new TmdbSearch(_logger, _json, _libraryManager).GetSearchResults(id, cancellationToken).ConfigureAwait(false); - - var searchResult = searchResults.FirstOrDefault(); - - if (searchResult != null) - { - tmdbId = searchResult.GetProviderId(MetadataProvider.Tmdb); - } - } - - var result = new MetadataResult(); - - if (!string.IsNullOrEmpty(tmdbId)) - { - var mainResult = await GetMovieDbResult(tmdbId, id.MetadataLanguage, cancellationToken).ConfigureAwait(false); - - if (mainResult != null) + if (collection.Images != null) { - result.HasMetadata = true; - result.Item = GetItem(mainResult); + result.ImageUrl = _tmdbClientManager.GetPosterUrl(collection.PosterPath); } - } - return result; - } + result.SetProviderId(MetadataProvider.Tmdb, collection.Id.ToString(CultureInfo.InvariantCulture)); - internal async Task GetMovieDbResult(string tmdbId, string language, CancellationToken cancellationToken) - { - if (string.IsNullOrEmpty(tmdbId)) - { - throw new ArgumentNullException(nameof(tmdbId)); + return new[] { result }; } - await EnsureInfo(tmdbId, language, cancellationToken).ConfigureAwait(false); - - var dataFilePath = GetDataFilePath(_config.ApplicationPaths, tmdbId, language); + var collectionSearchResults = await _tmdbClientManager.SearchCollectionAsync(searchInfo.Name, language, cancellationToken).ConfigureAwait(false); - if (!string.IsNullOrEmpty(dataFilePath)) + var collections = new List(); + for (var i = 0; i < collectionSearchResults.Count; i++) { - return _json.DeserializeFromFile(dataFilePath); - } - - return null; - } - - private BoxSet GetItem(CollectionResult obj) - { - var item = new BoxSet - { - Name = obj.Name, - Overview = obj.Overview - }; - - item.SetProviderId(MetadataProvider.Tmdb, obj.Id.ToString(_usCulture)); - - return item; - } - - private async Task DownloadInfo(string tmdbId, string preferredMetadataLanguage, CancellationToken cancellationToken) - { - var mainResult = await FetchMainResult(tmdbId, preferredMetadataLanguage, cancellationToken).ConfigureAwait(false); + var collection = new RemoteSearchResult + { + Name = collectionSearchResults[i].Name, + SearchProviderName = Name + }; + collection.SetProviderId(MetadataProvider.Tmdb, collectionSearchResults[i].Id.ToString(CultureInfo.InvariantCulture)); - if (mainResult == null) - { - return; + collections.Add(collection); } - var dataFilePath = GetDataFilePath(_config.ApplicationPaths, tmdbId, preferredMetadataLanguage); - - Directory.CreateDirectory(Path.GetDirectoryName(dataFilePath)); - - _json.SerializeToFile(mainResult, dataFilePath); + return collections; } - private async Task FetchMainResult(string id, string language, CancellationToken cancellationToken) + public async Task> GetMetadata(BoxSetInfo id, CancellationToken cancellationToken) { - var url = string.Format(CultureInfo.InvariantCulture, GetCollectionInfo3, id, TmdbUtils.ApiKey); - - if (!string.IsNullOrEmpty(language)) + var tmdbId = Convert.ToInt32(id.GetProviderId(MetadataProvider.Tmdb), CultureInfo.InvariantCulture); + var language = id.MetadataLanguage; + // We don't already have an Id, need to fetch it + if (tmdbId <= 0) { - url += string.Format(CultureInfo.InvariantCulture, "&language={0}", TmdbMovieProvider.NormalizeLanguage(language)); + var searchResults = await _tmdbClientManager.SearchCollectionAsync(id.Name, language, cancellationToken).ConfigureAwait(false); - // Get images in english and with no language - url += "&include_image_language=" + TmdbMovieProvider.GetImageLanguagesParam(language); + if (searchResults != null && searchResults.Count > 0) + { + tmdbId = searchResults[0].Id; + } } - cancellationToken.ThrowIfCancellationRequested(); + var result = new MetadataResult(); - using var requestMessage = new HttpRequestMessage(HttpMethod.Get, url); - foreach (var header in TmdbUtils.AcceptHeaders) + if (tmdbId > 0) { - requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); - } - - using var mainResponse = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage, cancellationToken).ConfigureAwait(false); - await using var stream = await mainResponse.Content.ReadAsStreamAsync().ConfigureAwait(false); - var mainResult = await _json.DeserializeFromStreamAsync(stream).ConfigureAwait(false); + var collection = await _tmdbClientManager.GetCollectionAsync(tmdbId, language, TmdbUtils.GetImageLanguagesParam(language), cancellationToken).ConfigureAwait(false); - cancellationToken.ThrowIfCancellationRequested(); - - if (mainResult != null && string.IsNullOrEmpty(mainResult.Name)) - { - if (!string.IsNullOrEmpty(language) && !string.Equals(language, "en", StringComparison.OrdinalIgnoreCase)) + if (collection != null) { - url = string.Format(CultureInfo.InvariantCulture, GetCollectionInfo3, id, TmdbUtils.ApiKey) + "&language=en"; - - if (!string.IsNullOrEmpty(language)) + var item = new BoxSet { - // Get images in english and with no language - url += "&include_image_language=" + TmdbMovieProvider.GetImageLanguagesParam(language); - } - - using var langRequestMessage = new HttpRequestMessage(HttpMethod.Get, url); - foreach (var header in TmdbUtils.AcceptHeaders) - { - langRequestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); - } - - await using var langStream = await mainResponse.Content.ReadAsStreamAsync().ConfigureAwait(false); - mainResult = await _json.DeserializeFromStreamAsync(langStream).ConfigureAwait(false); - } - } + Name = collection.Name, + Overview = collection.Overview + }; - return mainResult; - } - - internal Task EnsureInfo(string tmdbId, string preferredMetadataLanguage, CancellationToken cancellationToken) - { - var path = GetDataFilePath(_config.ApplicationPaths, tmdbId, preferredMetadataLanguage); + item.SetProviderId(MetadataProvider.Tmdb, collection.Id.ToString(CultureInfo.InvariantCulture)); - var fileInfo = _fileSystem.GetFileSystemInfo(path); - - if (fileInfo.Exists) - { - // If it's recent or automatic updates are enabled, don't re-download - if ((DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 2) - { - return Task.CompletedTask; + result.HasMetadata = true; + result.Item = item; } } - return DownloadInfo(tmdbId, preferredMetadataLanguage, cancellationToken); - } - - public string Name => TmdbUtils.ProviderName; - - private static string GetDataFilePath(IApplicationPaths appPaths, string tmdbId, string preferredLanguage) - { - var path = GetDataPath(appPaths, tmdbId); - - var filename = string.Format(CultureInfo.InvariantCulture, "all-{0}.json", preferredLanguage ?? string.Empty); - - return Path.Combine(path, filename); - } - - private static string GetDataPath(IApplicationPaths appPaths, string tmdbId) - { - var dataPath = GetCollectionsDataPath(appPaths); - - return Path.Combine(dataPath, tmdbId); - } - - private static string GetCollectionsDataPath(IApplicationPaths appPaths) - { - var dataPath = Path.Combine(appPaths.CachePath, "tmdb-collections"); - - return dataPath; + return result; } public Task GetImageResponse(string url, CancellationToken cancellationToken) diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Models/Collections/CollectionImages.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/Collections/CollectionImages.cs deleted file mode 100644 index 0a8994d54..000000000 --- a/MediaBrowser.Providers/Plugins/Tmdb/Models/Collections/CollectionImages.cs +++ /dev/null @@ -1,14 +0,0 @@ -#pragma warning disable CS1591 - -using System.Collections.Generic; -using MediaBrowser.Providers.Plugins.Tmdb.Models.General; - -namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Collections -{ - public class CollectionImages - { - public List Backdrops { get; set; } - - public List Posters { get; set; } - } -} diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Models/Collections/CollectionResult.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/Collections/CollectionResult.cs deleted file mode 100644 index c6b851c23..000000000 --- a/MediaBrowser.Providers/Plugins/Tmdb/Models/Collections/CollectionResult.cs +++ /dev/null @@ -1,23 +0,0 @@ -#pragma warning disable CS1591 - -using System.Collections.Generic; - -namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Collections -{ - public class CollectionResult - { - public int Id { get; set; } - - public string Name { get; set; } - - public string Overview { get; set; } - - public string Poster_Path { get; set; } - - public string Backdrop_Path { get; set; } - - public List Parts { get; set; } - - public CollectionImages Images { get; set; } - } -} diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Models/Collections/Part.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/Collections/Part.cs deleted file mode 100644 index a48124b3e..000000000 --- a/MediaBrowser.Providers/Plugins/Tmdb/Models/Collections/Part.cs +++ /dev/null @@ -1,17 +0,0 @@ -#pragma warning disable CS1591 - -namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Collections -{ - public class Part - { - public string Title { get; set; } - - public int Id { get; set; } - - public string Release_Date { get; set; } - - public string Poster_Path { get; set; } - - public string Backdrop_Path { get; set; } - } -} diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Backdrop.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Backdrop.cs deleted file mode 100644 index 5b7627f6e..000000000 --- a/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Backdrop.cs +++ /dev/null @@ -1,21 +0,0 @@ -#pragma warning disable CS1591 - -namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General -{ - public class Backdrop - { - public double Aspect_Ratio { get; set; } - - public string File_Path { get; set; } - - public int Height { get; set; } - - public string Iso_639_1 { get; set; } - - public double Vote_Average { get; set; } - - public int Vote_Count { get; set; } - - public int Width { get; set; } - } -} diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Crew.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Crew.cs deleted file mode 100644 index 339ecb628..000000000 --- a/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Crew.cs +++ /dev/null @@ -1,19 +0,0 @@ -#pragma warning disable CS1591 - -namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General -{ - public class Crew - { - public int Id { get; set; } - - public string Credit_Id { get; set; } - - public string Name { get; set; } - - public string Department { get; set; } - - public string Job { get; set; } - - public string Profile_Path { get; set; } - } -} diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Models/General/ExternalIds.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/ExternalIds.cs deleted file mode 100644 index aac4420e8..000000000 --- a/MediaBrowser.Providers/Plugins/Tmdb/Models/General/ExternalIds.cs +++ /dev/null @@ -1,17 +0,0 @@ -#pragma warning disable CS1591 - -namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General -{ - public class ExternalIds - { - public string Imdb_Id { get; set; } - - public object Freebase_Id { get; set; } - - public string Freebase_Mid { get; set; } - - public int? Tvdb_Id { get; set; } - - public int? Tvrage_Id { get; set; } - } -} diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Genre.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Genre.cs deleted file mode 100644 index 9ba1c15c6..000000000 --- a/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Genre.cs +++ /dev/null @@ -1,11 +0,0 @@ -#pragma warning disable CS1591 - -namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General -{ - public class Genre - { - public int Id { get; set; } - - public string Name { get; set; } - } -} diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Images.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Images.cs deleted file mode 100644 index 0538cf174..000000000 --- a/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Images.cs +++ /dev/null @@ -1,13 +0,0 @@ -#pragma warning disable CS1591 - -using System.Collections.Generic; - -namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General -{ - public class Images - { - public List Backdrops { get; set; } - - public List Posters { get; set; } - } -} diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Keyword.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Keyword.cs deleted file mode 100644 index fff86931b..000000000 --- a/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Keyword.cs +++ /dev/null @@ -1,11 +0,0 @@ -#pragma warning disable CS1591 - -namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General -{ - public class Keyword - { - public int Id { get; set; } - - public string Name { get; set; } - } -} diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Keywords.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Keywords.cs deleted file mode 100644 index 235ecb568..000000000 --- a/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Keywords.cs +++ /dev/null @@ -1,11 +0,0 @@ -#pragma warning disable CS1591 - -using System.Collections.Generic; - -namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General -{ - public class Keywords - { - public List Results { get; set; } - } -} diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Poster.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Poster.cs deleted file mode 100644 index 4f61e978b..000000000 --- a/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Poster.cs +++ /dev/null @@ -1,21 +0,0 @@ -#pragma warning disable CS1591 - -namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General -{ - public class Poster - { - public double Aspect_Ratio { get; set; } - - public string File_Path { get; set; } - - public int Height { get; set; } - - public string Iso_639_1 { get; set; } - - public double Vote_Average { get; set; } - - public int Vote_Count { get; set; } - - public int Width { get; set; } - } -} diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Profile.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Profile.cs deleted file mode 100644 index 0a1f8843e..000000000 --- a/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Profile.cs +++ /dev/null @@ -1,17 +0,0 @@ -#pragma warning disable CS1591 - -namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General -{ - public class Profile - { - public string File_Path { get; set; } - - public int Width { get; set; } - - public int Height { get; set; } - - public object Iso_639_1 { get; set; } - - public double Aspect_Ratio { get; set; } - } -} diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Still.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Still.cs deleted file mode 100644 index 61de819b9..000000000 --- a/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Still.cs +++ /dev/null @@ -1,23 +0,0 @@ -#pragma warning disable CS1591 - -namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General -{ - public class Still - { - public double Aspect_Ratio { get; set; } - - public string File_Path { get; set; } - - public int Height { get; set; } - - public string Id { get; set; } - - public string Iso_639_1 { get; set; } - - public double Vote_Average { get; set; } - - public int Vote_Count { get; set; } - - public int Width { get; set; } - } -} diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Models/General/StillImages.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/StillImages.cs deleted file mode 100644 index 59ab18b7b..000000000 --- a/MediaBrowser.Providers/Plugins/Tmdb/Models/General/StillImages.cs +++ /dev/null @@ -1,11 +0,0 @@ -#pragma warning disable CS1591 - -using System.Collections.Generic; - -namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General -{ - public class StillImages - { - public List Stills { get; set; } - } -} diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Video.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Video.cs deleted file mode 100644 index ebd5c7ace..000000000 --- a/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Video.cs +++ /dev/null @@ -1,23 +0,0 @@ -#pragma warning disable CS1591 - -namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General -{ - public class Video - { - public string Id { get; set; } - - public string Iso_639_1 { get; set; } - - public string Iso_3166_1 { get; set; } - - public string Key { get; set; } - - public string Name { get; set; } - - public string Site { get; set; } - - public string Size { get; set; } - - public string Type { get; set; } - } -} diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Videos.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Videos.cs deleted file mode 100644 index 1c673fdbd..000000000 --- a/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Videos.cs +++ /dev/null @@ -1,11 +0,0 @@ -#pragma warning disable CS1591 - -using System.Collections.Generic; - -namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General -{ - public class Videos - { - public IReadOnlyList public class TmdbMovieProvider : IRemoteMetadataProvider, IHasOrder { - private const string TmdbConfigUrl = TmdbUtils.BaseTmdbApiUrl + "3/configuration?api_key={0}"; - private const string GetMovieInfo3 = TmdbUtils.BaseTmdbApiUrl + @"3/movie/{0}?api_key={1}&append_to_response=casts,releases,images,keywords,trailers"; - - private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - - private readonly IJsonSerializer _jsonSerializer; private readonly IHttpClientFactory _httpClientFactory; - private readonly IFileSystem _fileSystem; - private readonly IServerConfigurationManager _configurationManager; private readonly ILogger _logger; private readonly ILibraryManager _libraryManager; - private readonly IApplicationHost _appHost; + private readonly TmdbClientManager _tmdbClientManager; + + public string Name => TmdbUtils.ProviderName; - /// - /// The _TMDB settings task. - /// - private TmdbSettingsResult _tmdbSettings; + /// + public int Order => 1; + + internal static TmdbMovieProvider Current { get; private set; } public TmdbMovieProvider( - IJsonSerializer jsonSerializer, - IHttpClientFactory httpClientFactory, - IFileSystem fileSystem, - IServerConfigurationManager configurationManager, ILogger logger, ILibraryManager libraryManager, - IApplicationHost appHost) + TmdbClientManager tmdbClientManager, + IHttpClientFactory httpClientFactory) { - _jsonSerializer = jsonSerializer; - _httpClientFactory = httpClientFactory; - _fileSystem = fileSystem; - _configurationManager = configurationManager; _logger = logger; _libraryManager = libraryManager; - _appHost = appHost; + _tmdbClientManager = tmdbClientManager; + _httpClientFactory = httpClientFactory; Current = this; } - internal static TmdbMovieProvider Current { get; private set; } - - /// - public string Name => TmdbUtils.ProviderName; - - /// - public int Order => 1; - - public Task> GetSearchResults(MovieInfo searchInfo, CancellationToken cancellationToken) - { - return GetMovieSearchResults(searchInfo, cancellationToken); - } - - public async Task> GetMovieSearchResults(ItemLookupInfo searchInfo, CancellationToken cancellationToken) + public async Task> GetSearchResults(MovieInfo searchInfo, CancellationToken cancellationToken) { - var tmdbId = searchInfo.GetProviderId(MetadataProvider.Tmdb); + var tmdbId = Convert.ToInt32(searchInfo.GetProviderId(MetadataProvider.Tmdb), CultureInfo.InvariantCulture); - if (!string.IsNullOrEmpty(tmdbId)) + if (tmdbId == 0) { - cancellationToken.ThrowIfCancellationRequested(); - - await EnsureMovieInfo(tmdbId, searchInfo.MetadataLanguage, cancellationToken).ConfigureAwait(false); - - var dataFilePath = GetDataFilePath(tmdbId, searchInfo.MetadataLanguage); - - var obj = _jsonSerializer.DeserializeFromFile(dataFilePath); - - var tmdbSettings = await GetTmdbSettings(cancellationToken).ConfigureAwait(false); - - var tmdbImageUrl = tmdbSettings.images.GetImageUrl("original"); - - var remoteResult = new RemoteSearchResult + var movieResults = await _tmdbClientManager + .SearchMovieAsync(searchInfo.Name, searchInfo.MetadataLanguage, cancellationToken) + .ConfigureAwait(false); + var remoteSearchResults = new List(); + for (var i = 0; i < movieResults.Count; i++) { - Name = obj.GetTitle(), - SearchProviderName = Name, - ImageUrl = string.IsNullOrWhiteSpace(obj.Poster_Path) ? null : tmdbImageUrl + obj.Poster_Path - }; - - if (!string.IsNullOrWhiteSpace(obj.Release_Date)) - { - // These dates are always in this exact format - if (DateTime.TryParse(obj.Release_Date, _usCulture, DateTimeStyles.None, out var r)) + var movieResult = movieResults[i]; + var remoteSearchResult = new RemoteSearchResult { - remoteResult.PremiereDate = r.ToUniversalTime(); - remoteResult.ProductionYear = remoteResult.PremiereDate.Value.Year; - } + Name = movieResult.Title ?? movieResult.OriginalTitle, + ImageUrl = _tmdbClientManager.GetPosterUrl(movieResult.PosterPath), + Overview = movieResult.Overview, + SearchProviderName = Name + }; + + var releaseDate = movieResult.ReleaseDate?.ToUniversalTime(); + remoteSearchResult.PremiereDate = releaseDate; + remoteSearchResult.ProductionYear = releaseDate?.Year; + + remoteSearchResult.SetProviderId(MetadataProvider.Tmdb, movieResult.Id.ToString(CultureInfo.InvariantCulture)); + remoteSearchResults.Add(remoteSearchResult); } - remoteResult.SetProviderId(MetadataProvider.Tmdb, obj.Id.ToString(_usCulture)); - - if (!string.IsNullOrWhiteSpace(obj.Imdb_Id)) - { - remoteResult.SetProviderId(MetadataProvider.Imdb, obj.Imdb_Id); - } - - return new[] { remoteResult }; + return remoteSearchResults; } - return await new TmdbSearch(_logger, _jsonSerializer, _libraryManager).GetMovieSearchResults(searchInfo, cancellationToken).ConfigureAwait(false); - } - - public Task> GetMetadata(MovieInfo info, CancellationToken cancellationToken) - { - return GetItemMetadata(info, cancellationToken); - } + var movie = await _tmdbClientManager + .GetMovieAsync(tmdbId, searchInfo.MetadataLanguage, TmdbUtils.GetImageLanguagesParam(searchInfo.MetadataLanguage), cancellationToken) + .ConfigureAwait(false); - public Task> GetItemMetadata(ItemLookupInfo id, CancellationToken cancellationToken) - where T : BaseItem, new() - { - var movieDb = new GenericTmdbMovieInfo(_logger, _jsonSerializer, _libraryManager, _fileSystem); - - return movieDb.GetMetadata(id, cancellationToken); - } - - /// - /// Gets the TMDB settings. - /// - /// Task{TmdbSettingsResult}. - internal async Task GetTmdbSettings(CancellationToken cancellationToken) - { - if (_tmdbSettings != null) + var remoteResult = new RemoteSearchResult { - return _tmdbSettings; - } + Name = movie.Title ?? movie.OriginalTitle, + SearchProviderName = Name, + ImageUrl = _tmdbClientManager.GetPosterUrl(movie.PosterPath), + Overview = movie.Overview + }; - using var requestMessage = new HttpRequestMessage(HttpMethod.Get, string.Format(CultureInfo.InvariantCulture, TmdbConfigUrl, TmdbUtils.ApiKey)); - foreach (var header in TmdbUtils.AcceptHeaders) + if (movie.ReleaseDate != null) { - requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); + var releaseDate = movie.ReleaseDate.Value.ToUniversalTime(); + remoteResult.PremiereDate = releaseDate; + remoteResult.ProductionYear = releaseDate.Year; } - using var response = await GetMovieDbResponse(requestMessage, cancellationToken).ConfigureAwait(false); - await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); - _tmdbSettings = await _jsonSerializer.DeserializeFromStreamAsync(stream).ConfigureAwait(false); - return _tmdbSettings; - } - - /// - /// Gets the movie data path. - /// - /// The app paths. - /// The TMDB id. - /// System.String. - internal static string GetMovieDataPath(IApplicationPaths appPaths, string tmdbId) - { - var dataPath = GetMoviesDataPath(appPaths); - - return Path.Combine(dataPath, tmdbId); - } - - internal static string GetMoviesDataPath(IApplicationPaths appPaths) - { - var dataPath = Path.Combine(appPaths.CachePath, "tmdb-movies2"); - - return dataPath; - } + remoteResult.SetProviderId(MetadataProvider.Tmdb, movie.Id.ToString(CultureInfo.InvariantCulture)); - /// - /// Downloads the movie info. - /// - /// The id. - /// The preferred metadata language. - /// The cancellation token. - /// Task. - internal async Task DownloadMovieInfo(string id, string preferredMetadataLanguage, CancellationToken cancellationToken) - { - var mainResult = await FetchMainResult(id, true, preferredMetadataLanguage, cancellationToken).ConfigureAwait(false); - - if (mainResult == null) + if (!string.IsNullOrWhiteSpace(movie.ImdbId)) { - return; + remoteResult.SetProviderId(MetadataProvider.Imdb, movie.ImdbId); } - var dataFilePath = GetDataFilePath(id, preferredMetadataLanguage); - - Directory.CreateDirectory(Path.GetDirectoryName(dataFilePath)); - - _jsonSerializer.SerializeToFile(mainResult, dataFilePath); + return new[] { remoteResult }; } - internal Task EnsureMovieInfo(string tmdbId, string language, CancellationToken cancellationToken) + public async Task> GetMetadata(MovieInfo info, CancellationToken cancellationToken) { - if (string.IsNullOrEmpty(tmdbId)) - { - throw new ArgumentNullException(nameof(tmdbId)); - } + var tmdbId = info.GetProviderId(MetadataProvider.Tmdb); + var imdbId = info.GetProviderId(MetadataProvider.Imdb); - var path = GetDataFilePath(tmdbId, language); - - var fileInfo = _fileSystem.GetFileSystemInfo(path); - - if (fileInfo.Exists) + if (string.IsNullOrEmpty(tmdbId) && string.IsNullOrEmpty(imdbId)) { - // If it's recent or automatic updates are enabled, don't re-download - if ((DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 2) + // ParseName is required here. + // Caller provides the filename with extension stripped and NOT the parsed filename + var parsedName = _libraryManager.ParseName(info.Name); + var searchResults = await _tmdbClientManager.SearchMovieAsync(parsedName.Name, parsedName.Year ?? 0, info.MetadataLanguage, cancellationToken).ConfigureAwait(false); + + if (searchResults.Count > 0) { - return Task.CompletedTask; + tmdbId = searchResults[0].Id.ToString(CultureInfo.InvariantCulture); } } - return DownloadMovieInfo(tmdbId, language, cancellationToken); - } - - internal string GetDataFilePath(string tmdbId, string preferredLanguage) - { if (string.IsNullOrEmpty(tmdbId)) { - throw new ArgumentNullException(nameof(tmdbId)); + return new MetadataResult(); } - var path = GetMovieDataPath(_configurationManager.ApplicationPaths, tmdbId); + var movieResult = await _tmdbClientManager + .GetMovieAsync(Convert.ToInt32(tmdbId, CultureInfo.InvariantCulture), info.MetadataLanguage, TmdbUtils.GetImageLanguagesParam(info.MetadataLanguage), cancellationToken) + .ConfigureAwait(false); - if (string.IsNullOrWhiteSpace(preferredLanguage)) + var movie = new Movie + { + Name = movieResult.Title ?? movieResult.OriginalTitle, + Overview = movieResult.Overview?.Replace("\n\n", "\n", StringComparison.InvariantCulture), + Tagline = movieResult.Tagline, + ProductionLocations = movieResult.ProductionCountries.Select(pc => pc.Name).ToArray() + }; + var metadataResult = new MetadataResult + { + HasMetadata = true, + ResultLanguage = info.MetadataLanguage, + Item = movie + }; + + movie.SetProviderId(MetadataProvider.Tmdb, tmdbId); + movie.SetProviderId(MetadataProvider.Imdb, movieResult.ImdbId); + if (movieResult.BelongsToCollection != null) { - preferredLanguage = "alllang"; + movie.SetProviderId(MetadataProvider.TmdbCollection, movieResult.BelongsToCollection.Id.ToString(CultureInfo.InvariantCulture)); + movie.CollectionName = movieResult.BelongsToCollection.Name; } - var filename = string.Format(CultureInfo.InvariantCulture, "all-{0}.json", preferredLanguage); + movie.CommunityRating = Convert.ToSingle(movieResult.VoteAverage); - return Path.Combine(path, filename); - } + if (movieResult.Releases?.Countries != null) + { + var releases = movieResult.Releases.Countries.Where(i => !string.IsNullOrWhiteSpace(i.Certification)).ToList(); - public static string GetImageLanguagesParam(string preferredLanguage) - { - var languages = new List(); + var ourRelease = releases.FirstOrDefault(c => string.Equals(c.Iso_3166_1, info.MetadataCountryCode, StringComparison.OrdinalIgnoreCase)); + var usRelease = releases.FirstOrDefault(c => string.Equals(c.Iso_3166_1, "US", StringComparison.OrdinalIgnoreCase)); - if (!string.IsNullOrEmpty(preferredLanguage)) - { - preferredLanguage = NormalizeLanguage(preferredLanguage); + if (ourRelease != null) + { + var ratingPrefix = string.Equals(info.MetadataCountryCode, "us", StringComparison.OrdinalIgnoreCase) ? string.Empty : info.MetadataCountryCode + "-"; + var newRating = ratingPrefix + ourRelease.Certification; - languages.Add(preferredLanguage); + newRating = newRating.Replace("de-", "FSK-", StringComparison.OrdinalIgnoreCase); - if (preferredLanguage.Length == 5) // like en-US + movie.OfficialRating = newRating; + } + else if (usRelease != null) { - // Currenty, TMDB supports 2-letter language codes only - // They are planning to change this in the future, thus we're - // supplying both codes if we're having a 5-letter code. - languages.Add(preferredLanguage.Substring(0, 2)); + movie.OfficialRating = usRelease.Certification; } } - languages.Add("null"); + movie.PremiereDate = movieResult.ReleaseDate; + movie.ProductionYear = movieResult.ReleaseDate?.Year; - if (!string.Equals(preferredLanguage, "en", StringComparison.OrdinalIgnoreCase)) + if (movieResult.ProductionCompanies != null) { - languages.Add("en"); + movie.SetStudios(movieResult.ProductionCompanies.Select(c => c.Name)); } - return string.Join(',', languages); - } + var genres = movieResult.Genres; - public static string NormalizeLanguage(string language) - { - if (!string.IsNullOrEmpty(language)) + foreach (var genre in genres.Select(g => g.Name)) { - // They require this to be uppercase - // Everything after the hyphen must be written in uppercase due to a way TMDB wrote their api. - // See here: https://www.themoviedb.org/talk/5119221d760ee36c642af4ad?page=3#56e372a0c3a3685a9e0019ab - var parts = language.Split('-'); - - if (parts.Length == 2) - { - language = parts[0] + "-" + parts[1].ToUpperInvariant(); - } + movie.AddGenre(genre); } - return language; - } - - public static string AdjustImageLanguage(string imageLanguage, string requestLanguage) - { - if (!string.IsNullOrEmpty(imageLanguage) - && !string.IsNullOrEmpty(requestLanguage) - && requestLanguage.Length > 2 - && imageLanguage.Length == 2 - && requestLanguage.StartsWith(imageLanguage, StringComparison.OrdinalIgnoreCase)) + if (movieResult.Credits?.Cast != null) { - return requestLanguage; - } - - return imageLanguage; - } + // TODO configurable + foreach (var actor in movieResult.Credits.Cast.OrderBy(a => a.Order).Take(TmdbUtils.MaxCastMembers)) + { + var personInfo = new PersonInfo + { + Name = actor.Name.Trim(), + Role = actor.Character, + Type = PersonType.Actor, + SortOrder = actor.Order + }; - /// - /// Fetches the main result. - /// - /// The id. - /// if set to true [is TMDB identifier]. - /// The language. - /// The cancellation token. - /// Task{CompleteMovieData}. - internal async Task FetchMainResult(string id, bool isTmdbId, string language, CancellationToken cancellationToken) - { - var url = string.Format(CultureInfo.InvariantCulture, GetMovieInfo3, id, TmdbUtils.ApiKey); + if (!string.IsNullOrWhiteSpace(actor.ProfilePath)) + { + personInfo.ImageUrl = _tmdbClientManager.GetProfileUrl(actor.ProfilePath); + } - if (!string.IsNullOrEmpty(language)) - { - url += string.Format(CultureInfo.InvariantCulture, "&language={0}", NormalizeLanguage(language)); + if (actor.Id > 0) + { + personInfo.SetProviderId(MetadataProvider.Tmdb, actor.Id.ToString(CultureInfo.InvariantCulture)); + } - // Get images in english and with no language - url += "&include_image_language=" + GetImageLanguagesParam(language); + metadataResult.AddPerson(personInfo); + } } - cancellationToken.ThrowIfCancellationRequested(); - - using var requestMessage = new HttpRequestMessage(HttpMethod.Get, url); - foreach (var header in TmdbUtils.AcceptHeaders) + if (movieResult.Credits?.Crew != null) { - requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); - } + var keepTypes = new[] + { + PersonType.Director, + PersonType.Writer, + PersonType.Producer + }; - using var mainResponse = await GetMovieDbResponse(requestMessage, cancellationToken).ConfigureAwait(false); - if (mainResponse.StatusCode == HttpStatusCode.NotFound) - { - return null; - } + foreach (var person in movieResult.Credits.Crew) + { + // Normalize this + var type = TmdbUtils.MapCrewToPersonType(person); - await using var stream = await mainResponse.Content.ReadAsStreamAsync().ConfigureAwait(false); - var mainResult = await _jsonSerializer.DeserializeFromStreamAsync(stream).ConfigureAwait(false); + if (!keepTypes.Contains(type, StringComparer.OrdinalIgnoreCase) && + !keepTypes.Contains(person.Job ?? string.Empty, StringComparer.OrdinalIgnoreCase)) + { + continue; + } - cancellationToken.ThrowIfCancellationRequested(); + var personInfo = new PersonInfo + { + Name = person.Name.Trim(), + Role = person.Job, + Type = type + }; - // If the language preference isn't english, then have the overview fallback to english if it's blank - if (mainResult != null && - string.IsNullOrEmpty(mainResult.Overview) && - !string.IsNullOrEmpty(language) && - !string.Equals(language, "en", StringComparison.OrdinalIgnoreCase)) - { - _logger.LogInformation("MovieDbProvider couldn't find meta for language " + language + ". Trying English..."); + if (!string.IsNullOrWhiteSpace(person.ProfilePath)) + { + personInfo.ImageUrl = _tmdbClientManager.GetPosterUrl(person.ProfilePath); + } - url = string.Format(CultureInfo.InvariantCulture, GetMovieInfo3, id, TmdbUtils.ApiKey) + "&language=en"; + if (person.Id > 0) + { + personInfo.SetProviderId(MetadataProvider.Tmdb, person.Id.ToString(CultureInfo.InvariantCulture)); + } - if (!string.IsNullOrEmpty(language)) - { - // Get images in english and with no language - url += "&include_image_language=" + GetImageLanguagesParam(language); + metadataResult.AddPerson(personInfo); } + } + - using var langRequestMessage = new HttpRequestMessage(HttpMethod.Get, url); - foreach (var header in TmdbUtils.AcceptHeaders) + if (movieResult.Videos?.Results != null) + { + var trailers = new List(); + for (var i = 0; i < movieResult.Videos.Results.Count; i++) { - langRequestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); - } + var video = movieResult.Videos.Results[0]; + if (!TmdbUtils.IsTrailerType(video)) + { + continue; + } - using var langResponse = await GetMovieDbResponse(langRequestMessage, cancellationToken).ConfigureAwait(false); + trailers.Add(new MediaUrl + { + Url = string.Format(CultureInfo.InvariantCulture, "https://www.youtube.com/watch?v={0}", video.Key), + Name = video.Name + }); + } - await using var langStream = await langResponse.Content.ReadAsStreamAsync().ConfigureAwait(false); - var langResult = await _jsonSerializer.DeserializeFromStreamAsync(stream).ConfigureAwait(false); - mainResult.Overview = langResult.Overview; + movie.RemoteTrailers = trailers; } - return mainResult; - } - - /// - /// Gets the movie db response. - /// - /// A representing the asynchronous operation. - internal Task GetMovieDbResponse(HttpRequestMessage message, CancellationToken cancellationToken = default) - { - message.Headers.UserAgent.ParseAdd(_appHost.ApplicationUserAgent); - return _httpClientFactory.CreateClient(NamedClient.Default).SendAsync(message, cancellationToken); + return metadataResult; } /// diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbSearch.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbSearch.cs deleted file mode 100644 index 36a4eef8a..000000000 --- a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbSearch.cs +++ /dev/null @@ -1,302 +0,0 @@ -#pragma warning disable CS1591 - -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Text.RegularExpressions; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Providers; -using MediaBrowser.Model.Serialization; -using MediaBrowser.Providers.Plugins.Tmdb.Models.Search; -using Microsoft.Extensions.Logging; - -namespace MediaBrowser.Providers.Plugins.Tmdb.Movies -{ - public class TmdbSearch - { - private const string SearchUrl = TmdbUtils.BaseTmdbApiUrl + @"3/search/{3}?api_key={1}&query={0}&language={2}"; - private const string SearchUrlTvWithYear = TmdbUtils.BaseTmdbApiUrl + @"3/search/tv?api_key={1}&query={0}&language={2}&first_air_date_year={3}"; - private const string SearchUrlMovieWithYear = TmdbUtils.BaseTmdbApiUrl + @"3/search/movie?api_key={1}&query={0}&language={2}&primary_release_year={3}"; - - private static readonly CultureInfo _usCulture = new CultureInfo("en-US"); - - private static readonly Regex _cleanEnclosed = new Regex(@"\p{Ps}.*\p{Pe}", RegexOptions.Compiled); - private static readonly Regex _cleanNonWord = new Regex(@"[\W_]+", RegexOptions.Compiled); - private static readonly Regex _cleanStopWords = new Regex( - @"\b( # Start at word boundary - 19[0-9]{2}|20[0-9]{2}| # 1900-2099 - S[0-9]{2}| # Season - E[0-9]{2}| # Episode - (2160|1080|720|576|480)[ip]?| # Resolution - [xh]?264| # Encoding - (web|dvd|bd|hdtv|hd)rip| # *Rip - web|hdtv|mp4|bluray|ktr|dl|single|imageset|internal|doku|dubbed|retail|xxx|flac - ).* # Match rest of string", - RegexOptions.Compiled | RegexOptions.IgnorePatternWhitespace | RegexOptions.IgnoreCase); - - private readonly ILogger _logger; - private readonly IJsonSerializer _json; - private readonly ILibraryManager _libraryManager; - - public TmdbSearch(ILogger logger, IJsonSerializer json, ILibraryManager libraryManager) - { - _logger = logger; - _json = json; - _libraryManager = libraryManager; - } - - public Task> GetSearchResults(SeriesInfo idInfo, CancellationToken cancellationToken) - { - return GetSearchResults(idInfo, "tv", cancellationToken); - } - - public Task> GetMovieSearchResults(ItemLookupInfo idInfo, CancellationToken cancellationToken) - { - return GetSearchResults(idInfo, "movie", cancellationToken); - } - - public Task> GetSearchResults(BoxSetInfo idInfo, CancellationToken cancellationToken) - { - return GetSearchResults(idInfo, "collection", cancellationToken); - } - - private async Task> GetSearchResults(ItemLookupInfo idInfo, string searchType, CancellationToken cancellationToken) - { - var name = idInfo.Name; - var year = idInfo.Year; - - if (string.IsNullOrWhiteSpace(name)) - { - return new List(); - } - - var tmdbSettings = await TmdbMovieProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); - - var tmdbImageUrl = tmdbSettings.images.GetImageUrl("original"); - - // ParseName is required here. - // Caller provides the filename with extension stripped and NOT the parsed filename - var parsedName = _libraryManager.ParseName(name); - var yearInName = parsedName.Year; - name = parsedName.Name; - year ??= yearInName; - - var language = idInfo.MetadataLanguage.ToLowerInvariant(); - - // Replace sequences of non-word characters with space - // TMDB expects a space separated list of words make sure that is the case - name = _cleanNonWord.Replace(name, " ").Trim(); - - _logger.LogInformation("TmdbSearch: Finding id for item: {0} ({1})", name, year); - var results = await GetSearchResults(name, searchType, year, language, tmdbImageUrl, cancellationToken).ConfigureAwait(false); - - if (results.Count == 0) - { - // try in english if wasn't before - if (!string.Equals(language, "en", StringComparison.OrdinalIgnoreCase)) - { - results = await GetSearchResults(name, searchType, year, "en", tmdbImageUrl, cancellationToken).ConfigureAwait(false); - } - } - - // TODO: retrying alternatives should be done outside the search - // provider so that the retry logic can be common for all search - // providers - if (results.Count == 0) - { - var name2 = parsedName.Name; - - // Remove things enclosed in []{}() etc - name2 = _cleanEnclosed.Replace(name2, string.Empty); - - // Replace sequences of non-word characters with space - name2 = _cleanNonWord.Replace(name2, " "); - - // Clean based on common stop words / tokens - name2 = _cleanStopWords.Replace(name2, string.Empty); - - // Trim whitespace - name2 = name2.Trim(); - - // Search again if the new name is different - if (!string.Equals(name2, name, StringComparison.Ordinal) && !string.IsNullOrWhiteSpace(name2)) - { - _logger.LogInformation("TmdbSearch: Finding id for item: {0} ({1})", name2, year); - results = await GetSearchResults(name2, searchType, year, language, tmdbImageUrl, cancellationToken).ConfigureAwait(false); - - if (results.Count == 0 && !string.Equals(language, "en", StringComparison.OrdinalIgnoreCase)) - { - // one more time, in english - results = await GetSearchResults(name2, searchType, year, "en", tmdbImageUrl, cancellationToken).ConfigureAwait(false); - } - } - } - - return results.Where(i => - { - if (year.HasValue && i.ProductionYear.HasValue) - { - // Allow one year tolerance - return Math.Abs(year.Value - i.ProductionYear.Value) <= 1; - } - - return true; - }); - } - - private Task> GetSearchResults(string name, string type, int? year, string language, string baseImageUrl, CancellationToken cancellationToken) - { - switch (type) - { - case "tv": - return GetSearchResultsTv(name, year, language, baseImageUrl, cancellationToken); - default: - return GetSearchResultsGeneric(name, type, year, language, baseImageUrl, cancellationToken); - } - } - - private async Task> GetSearchResultsGeneric(string name, string type, int? year, string language, string baseImageUrl, CancellationToken cancellationToken) - { - if (string.IsNullOrWhiteSpace(name)) - { - throw new ArgumentException("String can't be null or empty.", nameof(name)); - } - - string url3; - if (year != null && string.Equals(type, "movie", StringComparison.OrdinalIgnoreCase)) - { - url3 = string.Format( - CultureInfo.InvariantCulture, - SearchUrlMovieWithYear, - WebUtility.UrlEncode(name), - TmdbUtils.ApiKey, - language, - year); - } - else - { - url3 = string.Format( - CultureInfo.InvariantCulture, - SearchUrl, - WebUtility.UrlEncode(name), - TmdbUtils.ApiKey, - language, - type); - } - - using var requestMessage = new HttpRequestMessage(HttpMethod.Get, url3); - foreach (var header in TmdbUtils.AcceptHeaders) - { - requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); - } - - using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage, cancellationToken).ConfigureAwait(false); - await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); - var searchResults = await _json.DeserializeFromStreamAsync>(stream).ConfigureAwait(false); - - var results = searchResults.Results ?? new List(); - - return results - .Select(i => - { - var remoteResult = new RemoteSearchResult - { - SearchProviderName = TmdbMovieProvider.Current.Name, - Name = i.Title ?? i.Name ?? i.Original_Title, - ImageUrl = string.IsNullOrWhiteSpace(i.Poster_Path) ? null : baseImageUrl + i.Poster_Path - }; - - if (!string.IsNullOrWhiteSpace(i.Release_Date)) - { - // These dates are always in this exact format - if (DateTime.TryParseExact(i.Release_Date, "yyyy-MM-dd", _usCulture, DateTimeStyles.None, out var r)) - { - remoteResult.PremiereDate = r.ToUniversalTime(); - remoteResult.ProductionYear = remoteResult.PremiereDate.Value.Year; - } - } - - remoteResult.SetProviderId(MetadataProvider.Tmdb, i.Id.ToString(_usCulture)); - - return remoteResult; - }) - .ToList(); - } - - private async Task> GetSearchResultsTv(string name, int? year, string language, string baseImageUrl, CancellationToken cancellationToken) - { - if (string.IsNullOrWhiteSpace(name)) - { - throw new ArgumentException("String can't be null or empty.", nameof(name)); - } - - string url3; - if (year == null) - { - url3 = string.Format( - CultureInfo.InvariantCulture, - SearchUrl, - WebUtility.UrlEncode(name), - TmdbUtils.ApiKey, - language, - "tv"); - } - else - { - url3 = string.Format( - CultureInfo.InvariantCulture, - SearchUrlTvWithYear, - WebUtility.UrlEncode(name), - TmdbUtils.ApiKey, - language, - year); - } - - using var requestMessage = new HttpRequestMessage(HttpMethod.Get, url3); - foreach (var header in TmdbUtils.AcceptHeaders) - { - requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); - } - - using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage, cancellationToken).ConfigureAwait(false); - await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); - var searchResults = await _json.DeserializeFromStreamAsync>(stream).ConfigureAwait(false); - - var results = searchResults.Results ?? new List(); - - return results - .Select(i => - { - var remoteResult = new RemoteSearchResult - { - SearchProviderName = TmdbMovieProvider.Current.Name, - Name = i.Name ?? i.Original_Name, - ImageUrl = string.IsNullOrWhiteSpace(i.Poster_Path) ? null : baseImageUrl + i.Poster_Path - }; - - if (!string.IsNullOrWhiteSpace(i.First_Air_Date)) - { - // These dates are always in this exact format - if (DateTime.TryParseExact(i.First_Air_Date, "yyyy-MM-dd", _usCulture, DateTimeStyles.None, out var r)) - { - remoteResult.PremiereDate = r.ToUniversalTime(); - remoteResult.ProductionYear = remoteResult.PremiereDate.Value.Year; - } - } - - remoteResult.SetProviderId(MetadataProvider.Tmdb, i.Id.ToString(_usCulture)); - - return remoteResult; - }) - .ToList(); - } - } -} diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Music/TmdbMusicVideoProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Music/TmdbMusicVideoProvider.cs deleted file mode 100644 index b88ecce87..000000000 --- a/MediaBrowser.Providers/Plugins/Tmdb/Music/TmdbMusicVideoProvider.cs +++ /dev/null @@ -1,34 +0,0 @@ -#pragma warning disable CS1591 - -using System; -using System.Collections.Generic; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Providers; -using MediaBrowser.Providers.Plugins.Tmdb.Movies; - -namespace MediaBrowser.Providers.Plugins.Tmdb.Music -{ - public class TmdbMusicVideoProvider : IRemoteMetadataProvider - { - public string Name => TmdbMovieProvider.Current.Name; - - public Task> GetMetadata(MusicVideoInfo info, CancellationToken cancellationToken) - { - return TmdbMovieProvider.Current.GetItemMetadata(info, cancellationToken); - } - - public Task> GetSearchResults(MusicVideoInfo searchInfo, CancellationToken cancellationToken) - { - return Task.FromResult((IEnumerable)new List()); - } - - public Task GetImageResponse(string url, CancellationToken cancellationToken) - { - throw new NotImplementedException(); - } - } -} diff --git a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs index f2d2c8120..99e417698 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Net.Http; using System.Threading; @@ -11,31 +12,25 @@ using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Extensions; using MediaBrowser.Model.Providers; using MediaBrowser.Model.Serialization; -using MediaBrowser.Providers.Plugins.Tmdb.Models.General; -using MediaBrowser.Providers.Plugins.Tmdb.Models.People; -using MediaBrowser.Providers.Plugins.Tmdb.Movies; namespace MediaBrowser.Providers.Plugins.Tmdb.People { public class TmdbPersonImageProvider : IRemoteImageProvider, IHasOrder { - private readonly IServerConfigurationManager _config; - private readonly IJsonSerializer _jsonSerializer; private readonly IHttpClientFactory _httpClientFactory; + private readonly TmdbClientManager _tmdbClientManager; - public TmdbPersonImageProvider(IServerConfigurationManager config, IJsonSerializer jsonSerializer, IHttpClientFactory httpClientFactory) + public TmdbPersonImageProvider(IHttpClientFactory httpClientFactory, TmdbClientManager tmdbClientManager) { - _config = config; - _jsonSerializer = jsonSerializer; _httpClientFactory = httpClientFactory; + _tmdbClientManager = tmdbClientManager; } - public static string ProviderName => TmdbUtils.ProviderName; - /// - public string Name => ProviderName; + public string Name => TmdbUtils.ProviderName; /// public int Order => 0; @@ -56,78 +51,37 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People public async Task> GetImages(BaseItem item, CancellationToken cancellationToken) { var person = (Person)item; - var id = person.GetProviderId(MetadataProvider.Tmdb); - - if (!string.IsNullOrEmpty(id)) - { - await TmdbPersonProvider.Current.EnsurePersonInfo(id, cancellationToken).ConfigureAwait(false); - - var dataFilePath = TmdbPersonProvider.GetPersonDataFilePath(_config.ApplicationPaths, id); - - var result = _jsonSerializer.DeserializeFromFile(dataFilePath); - - var images = result.Images ?? new PersonImages(); - - var tmdbSettings = await TmdbMovieProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); - - var tmdbImageUrl = tmdbSettings.images.GetImageUrl("original"); - - return GetImages(images, item.GetPreferredMetadataLanguage(), tmdbImageUrl); - } - - return new List(); - } - - private IEnumerable GetImages(PersonImages images, string preferredLanguage, string baseImageUrl) - { - var list = new List(); + var personTmdbId = Convert.ToInt32(person.GetProviderId(MetadataProvider.Tmdb), CultureInfo.InvariantCulture); - if (images.Profiles != null) + if (personTmdbId > 0) { - list.AddRange(images.Profiles.Select(i => new RemoteImageInfo + var personResult = await _tmdbClientManager.GetPersonAsync(personTmdbId, cancellationToken).ConfigureAwait(false); + if (personResult?.Images?.Profiles == null) { - ProviderName = Name, - Type = ImageType.Primary, - Width = i.Width, - Height = i.Height, - Language = GetLanguage(i), - Url = baseImageUrl + i.File_Path - })); - } - - var language = preferredLanguage; - - var isLanguageEn = string.Equals(language, "en", StringComparison.OrdinalIgnoreCase); - - return list.OrderByDescending(i => - { - if (string.Equals(language, i.Language, StringComparison.OrdinalIgnoreCase)) - { - return 3; + return Enumerable.Empty(); } - if (!isLanguageEn) - { - if (string.Equals("en", i.Language, StringComparison.OrdinalIgnoreCase)) - { - return 2; - } - } + var remoteImages = new List(); + var language = item.GetPreferredMetadataLanguage(); - if (string.IsNullOrEmpty(i.Language)) + for (var i = 0; i < personResult.Images.Profiles.Count; i++) { - return isLanguageEn ? 3 : 2; + var image = personResult.Images.Profiles[i]; + remoteImages.Add(new RemoteImageInfo + { + ProviderName = Name, + Type = ImageType.Primary, + Width = image.Width, + Height = image.Height, + Language = TmdbUtils.AdjustImageLanguage(image.Iso_639_1, language), + Url = _tmdbClientManager.GetProfileUrl(image.FilePath) + }); } - return 0; - }) - .ThenByDescending(i => i.CommunityRating ?? 0) - .ThenByDescending(i => i.VoteCount ?? 0); - } + return remoteImages.OrderByLanguageDescending(language); + } - private string GetLanguage(Profile profile) - { - return profile.Iso_639_1?.ToString(); + return new List(); } public Task GetImageResponse(string url, CancellationToken cancellationToken) diff --git a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs index 777ebce49..8c5e8590f 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs @@ -3,198 +3,133 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.IO; -using System.Linq; -using System.Net; using System.Net.Http; -using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.Configuration; -using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; -using MediaBrowser.Model.Net; using MediaBrowser.Model.Providers; using MediaBrowser.Model.Serialization; -using MediaBrowser.Providers.Plugins.Tmdb.Models.General; -using MediaBrowser.Providers.Plugins.Tmdb.Models.People; -using MediaBrowser.Providers.Plugins.Tmdb.Models.Search; -using MediaBrowser.Providers.Plugins.Tmdb.Movies; using Microsoft.Extensions.Logging; namespace MediaBrowser.Providers.Plugins.Tmdb.People { public class TmdbPersonProvider : IRemoteMetadataProvider { - private const string DataFileName = "info.json"; - - private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - - private readonly IJsonSerializer _jsonSerializer; - private readonly IFileSystem _fileSystem; - private readonly IServerConfigurationManager _configurationManager; private readonly IHttpClientFactory _httpClientFactory; + private readonly TmdbClientManager _tmdbClientManager; - public TmdbPersonProvider( - IFileSystem fileSystem, - IServerConfigurationManager configurationManager, - IJsonSerializer jsonSerializer, - IHttpClientFactory httpClientFactory) + public TmdbPersonProvider(IHttpClientFactory httpClientFactory, TmdbClientManager tmdbClientManager) { - _fileSystem = fileSystem; - _configurationManager = configurationManager; - _jsonSerializer = jsonSerializer; _httpClientFactory = httpClientFactory; - Current = this; + _tmdbClientManager = tmdbClientManager; } - internal static TmdbPersonProvider Current { get; private set; } - public string Name => TmdbUtils.ProviderName; public async Task> GetSearchResults(PersonLookupInfo searchInfo, CancellationToken cancellationToken) { - var tmdbId = searchInfo.GetProviderId(MetadataProvider.Tmdb); - - var tmdbSettings = await TmdbMovieProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); - - var tmdbImageUrl = tmdbSettings.images.GetImageUrl("original"); + var personTmdbId = Convert.ToInt32(searchInfo.GetProviderId(MetadataProvider.Tmdb), CultureInfo.InvariantCulture); - if (!string.IsNullOrEmpty(tmdbId)) + if (personTmdbId <= 0) { - await EnsurePersonInfo(tmdbId, cancellationToken).ConfigureAwait(false); + var personResult = await _tmdbClientManager.GetPersonAsync(personTmdbId, cancellationToken).ConfigureAwait(false); - var dataFilePath = GetPersonDataFilePath(_configurationManager.ApplicationPaths, tmdbId); - var info = _jsonSerializer.DeserializeFromFile(dataFilePath); - - IReadOnlyList images = info.Images?.Profiles ?? Array.Empty(); - - var result = new RemoteSearchResult + if (personResult != null) { - Name = info.Name, - - SearchProviderName = Name, + var result = new RemoteSearchResult + { + Name = personResult.Name, + SearchProviderName = Name, + Overview = personResult.Biography + }; - ImageUrl = images.Count == 0 ? null : (tmdbImageUrl + images[0].File_Path) - }; + if (personResult.Images?.Profiles != null && personResult.Images.Profiles.Count > 0) + { + result.ImageUrl = _tmdbClientManager.GetProfileUrl(personResult.Images.Profiles[0].FilePath); + } - result.SetProviderId(MetadataProvider.Tmdb, info.Id.ToString(_usCulture)); - result.SetProviderId(MetadataProvider.Imdb, info.Imdb_Id); + result.SetProviderId(MetadataProvider.Tmdb, personResult.Id.ToString(CultureInfo.InvariantCulture)); + result.SetProviderId(MetadataProvider.Imdb, personResult.ExternalIds.ImdbId); - return new[] { result }; + return new[] { result }; + } } + // TODO why? Because of the old rate limit? if (searchInfo.IsAutomated) { // Don't hammer moviedb searching by name - return Array.Empty(); + return new List(); } - var url = string.Format( - CultureInfo.InvariantCulture, - TmdbUtils.BaseTmdbApiUrl + @"3/search/person?api_key={1}&query={0}", - WebUtility.UrlEncode(searchInfo.Name), - TmdbUtils.ApiKey); + var personSearchResult = await _tmdbClientManager.SearchPersonAsync(searchInfo.Name, cancellationToken).ConfigureAwait(false); - using var requestMessage = new HttpRequestMessage(HttpMethod.Get, url); - foreach (var header in TmdbUtils.AcceptHeaders) + var remoteSearchResults = new List(); + for (var i = 0; i < personSearchResult.Count; i++) { - requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); - } - - var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage, cancellationToken).ConfigureAwait(false); - await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); - - var result2 = await _jsonSerializer.DeserializeFromStreamAsync>(stream).ConfigureAwait(false) - ?? new TmdbSearchResult(); - - return result2.Results.Select(i => GetSearchResult(i, tmdbImageUrl)); - } - - private RemoteSearchResult GetSearchResult(PersonSearchResult i, string baseImageUrl) - { - var result = new RemoteSearchResult - { - SearchProviderName = Name, - - Name = i.Name, - - ImageUrl = string.IsNullOrEmpty(i.Profile_Path) ? null : baseImageUrl + i.Profile_Path - }; + var person = personSearchResult[i]; + var remoteSearchResult = new RemoteSearchResult + { + SearchProviderName = Name, + Name = person.Name, + ImageUrl = _tmdbClientManager.GetProfileUrl(person.ProfilePath) + }; - result.SetProviderId(MetadataProvider.Tmdb, i.Id.ToString(_usCulture)); + remoteSearchResult.SetProviderId(MetadataProvider.Tmdb, person.Id.ToString(CultureInfo.InvariantCulture)); + remoteSearchResults.Add(remoteSearchResult); + } - return result; + return remoteSearchResults; } public async Task> GetMetadata(PersonLookupInfo id, CancellationToken cancellationToken) { - var tmdbId = id.GetProviderId(MetadataProvider.Tmdb); + var personTmdbId = Convert.ToInt32(id.GetProviderId(MetadataProvider.Tmdb), CultureInfo.InvariantCulture); // We don't already have an Id, need to fetch it - if (string.IsNullOrEmpty(tmdbId)) + if (personTmdbId <= 0) { - tmdbId = await GetTmdbId(id, cancellationToken).ConfigureAwait(false); + var personSearchResults = await _tmdbClientManager.SearchPersonAsync(id.Name, cancellationToken).ConfigureAwait(false); + if (personSearchResults.Count > 0) + { + personTmdbId = personSearchResults[0].Id; + } } var result = new MetadataResult(); - if (!string.IsNullOrEmpty(tmdbId)) + if (personTmdbId > 0) { - try - { - await EnsurePersonInfo(tmdbId, cancellationToken).ConfigureAwait(false); - } - catch (HttpException ex) - { - if (ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound) - { - return result; - } - - throw; - } - - var dataFilePath = GetPersonDataFilePath(_configurationManager.ApplicationPaths, tmdbId); - - var info = _jsonSerializer.DeserializeFromFile(dataFilePath); + var person = await _tmdbClientManager.GetPersonAsync(personTmdbId, cancellationToken).ConfigureAwait(false); - var item = new Person(); result.HasMetadata = true; - // Take name from incoming info, don't rename the person - // TODO: This should go in PersonMetadataService, not each person provider - item.Name = id.Name; - - // item.HomePageUrl = info.homepage; - - if (!string.IsNullOrWhiteSpace(info.Place_Of_Birth)) + var item = new Person { - item.ProductionLocations = new string[] { info.Place_Of_Birth }; - } - - item.Overview = info.Biography; - - if (DateTime.TryParseExact(info.Birthday, "yyyy-MM-dd", new CultureInfo("en-US"), DateTimeStyles.None, out var date)) - { - item.PremiereDate = date.ToUniversalTime(); - } + // Take name from incoming info, don't rename the person + // TODO: This should go in PersonMetadataService, not each person provider + Name = id.Name, + HomePageUrl = person.Homepage, + Overview = person.Biography, + PremiereDate = person.Birthday?.ToUniversalTime(), + EndDate = person.Deathday?.ToUniversalTime() + }; - if (DateTime.TryParseExact(info.Deathday, "yyyy-MM-dd", new CultureInfo("en-US"), DateTimeStyles.None, out date)) + if (!string.IsNullOrWhiteSpace(person.PlaceOfBirth)) { - item.EndDate = date.ToUniversalTime(); + item.ProductionLocations = new[] { person.PlaceOfBirth }; } - item.SetProviderId(MetadataProvider.Tmdb, info.Id.ToString(_usCulture)); + item.SetProviderId(MetadataProvider.Tmdb, person.Id.ToString(CultureInfo.InvariantCulture)); - if (!string.IsNullOrEmpty(info.Imdb_Id)) + if (!string.IsNullOrEmpty(person.ImdbId)) { - item.SetProviderId(MetadataProvider.Imdb, info.Imdb_Id); + item.SetProviderId(MetadataProvider.Imdb, person.ImdbId); } result.HasMetadata = true; @@ -204,65 +139,6 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People return result; } - /// - /// Gets the TMDB id. - /// - /// The information. - /// The cancellation token. - /// Task{System.String}. - private async Task GetTmdbId(PersonLookupInfo info, CancellationToken cancellationToken) - { - var results = await GetSearchResults(info, cancellationToken).ConfigureAwait(false); - - return results.Select(i => i.GetProviderId(MetadataProvider.Tmdb)).FirstOrDefault(); - } - - internal async Task EnsurePersonInfo(string id, CancellationToken cancellationToken) - { - var dataFilePath = GetPersonDataFilePath(_configurationManager.ApplicationPaths, id); - - var fileInfo = _fileSystem.GetFileSystemInfo(dataFilePath); - - if (fileInfo.Exists && (DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 2) - { - return; - } - - var url = string.Format( - CultureInfo.InvariantCulture, - TmdbUtils.BaseTmdbApiUrl + @"3/person/{1}?api_key={0}&append_to_response=credits,images,external_ids", - TmdbUtils.ApiKey, - id); - - using var requestMessage = new HttpRequestMessage(HttpMethod.Get, url); - foreach (var header in TmdbUtils.AcceptHeaders) - { - requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); - } - - using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage, cancellationToken).ConfigureAwait(false); - Directory.CreateDirectory(Path.GetDirectoryName(dataFilePath)); - await using var fs = new FileStream(dataFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true); - await response.Content.CopyToAsync(fs).ConfigureAwait(false); - } - - private static string GetPersonDataPath(IApplicationPaths appPaths, string tmdbId) - { - var letter = tmdbId.GetMD5().ToString().AsSpan().Slice(0, 1); - - return Path.Join(GetPersonsDataPath(appPaths), letter, tmdbId); - } - - internal static string GetPersonDataFilePath(IApplicationPaths appPaths, string tmdbId) - { - return Path.Combine(GetPersonDataPath(appPaths, tmdbId), DataFileName); - } - - private static string GetPersonsDataPath(IApplicationPaths appPaths) - { - return Path.Combine(appPaths.CachePath, "tmdb-people"); - } - public Task GetImageResponse(string url, CancellationToken cancellationToken) { return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken); diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs index c56774f8e..172db129e 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs @@ -2,40 +2,37 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Controller.Configuration; +using MediaBrowser.Common.Net; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Globalization; -using MediaBrowser.Model.IO; +using MediaBrowser.Model.Extensions; using MediaBrowser.Model.Providers; -using MediaBrowser.Model.Serialization; -using MediaBrowser.Providers.Plugins.Tmdb.Models.General; -using MediaBrowser.Providers.Plugins.Tmdb.Movies; -using Microsoft.Extensions.Logging; namespace MediaBrowser.Providers.Plugins.Tmdb.TV { - public class TmdbEpisodeImageProvider : - TmdbEpisodeProviderBase, - IRemoteImageProvider, - IHasOrder + public class TmdbEpisodeImageProvider : IRemoteImageProvider, IHasOrder { - public TmdbEpisodeImageProvider(IHttpClientFactory httpClientFactory, IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IFileSystem fileSystem, ILocalizationManager localization, ILoggerFactory loggerFactory) - : base(httpClientFactory, configurationManager, jsonSerializer, fileSystem, localization, loggerFactory) + private readonly IHttpClientFactory _httpClientFactory; + private readonly TmdbClientManager _tmdbClientManager; + + public TmdbEpisodeImageProvider(IHttpClientFactory httpClientFactory, TmdbClientManager tmdbClientManager) { + _httpClientFactory = httpClientFactory; + _tmdbClientManager = tmdbClientManager; } - public string Name => TmdbUtils.ProviderName; - // After TheTvDb public int Order => 1; + public string Name => TmdbUtils.ProviderName; + public IEnumerable GetSupportedImages(BaseItem item) { return new List @@ -49,13 +46,11 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV var episode = (Controller.Entities.TV.Episode)item; var series = episode.Series; - var seriesId = series?.GetProviderId(MetadataProvider.Tmdb); + var seriesTmdbId = Convert.ToInt32(series?.GetProviderId(MetadataProvider.Tmdb), CultureInfo.InvariantCulture); - var list = new List(); - - if (string.IsNullOrEmpty(seriesId)) + if (seriesTmdbId <= 0) { - return list; + return Enumerable.Empty(); } var seasonNumber = episode.ParentIndexNumber; @@ -63,76 +58,51 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV if (!seasonNumber.HasValue || !episodeNumber.HasValue) { - return list; + return Enumerable.Empty(); } var language = item.GetPreferredMetadataLanguage(); - var response = await GetEpisodeInfo( - seriesId, - seasonNumber.Value, - episodeNumber.Value, - language, - cancellationToken).ConfigureAwait(false); - - var tmdbSettings = await TmdbMovieProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); + var episodeResult = await _tmdbClientManager + .GetEpisodeAsync(seriesTmdbId, seasonNumber.Value, episodeNumber.Value, language, TmdbUtils.GetImageLanguagesParam(language), cancellationToken) + .ConfigureAwait(false); - var tmdbImageUrl = tmdbSettings.images.GetImageUrl("original"); - - list.AddRange(GetPosters(response.Images).Select(i => new RemoteImageInfo - { - Url = tmdbImageUrl + i.File_Path, - CommunityRating = i.Vote_Average, - VoteCount = i.Vote_Count, - Width = i.Width, - Height = i.Height, - Language = TmdbMovieProvider.AdjustImageLanguage(i.Iso_639_1, language), - ProviderName = Name, - Type = ImageType.Primary, - RatingType = RatingType.Score - })); - - var isLanguageEn = string.Equals(language, "en", StringComparison.OrdinalIgnoreCase); - - return list.OrderByDescending(i => + if (episodeResult?.Images?.Stills == null) { - if (string.Equals(language, i.Language, StringComparison.OrdinalIgnoreCase)) - { - return 3; - } + return Enumerable.Empty(); + } - if (!isLanguageEn) - { - if (string.Equals("en", i.Language, StringComparison.OrdinalIgnoreCase)) - { - return 2; - } - } + var remoteImages = new List(); - if (string.IsNullOrEmpty(i.Language)) + for (var i = 0; i < episodeResult.Images.Stills.Count; i++) + { + var image = episodeResult.Images.Stills[i]; + remoteImages.Add(new RemoteImageInfo { - return isLanguageEn ? 3 : 2; - } - - return 0; - }) - .ThenByDescending(i => i.CommunityRating ?? 0) - .ThenByDescending(i => i.VoteCount ?? 0); - } + Url = _tmdbClientManager.GetStillUrl(image.FilePath), + CommunityRating = image.VoteAverage, + VoteCount = image.VoteCount, + Width = image.Width, + Height = image.Height, + Language = TmdbUtils.AdjustImageLanguage(image.Iso_639_1, language), + ProviderName = Name, + Type = ImageType.Primary, + RatingType = RatingType.Score + }); + } - private IEnumerable GetPosters(StillImages images) - { - return images.Stills ?? new List(); + return remoteImages.OrderByLanguageDescending(language); } public Task GetImageResponse(string url, CancellationToken cancellationToken) { - return GetResponse(url, cancellationToken); + return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken); } public bool Supports(BaseItem item) { return item is Controller.Entities.TV.Episode; } + } } diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs index a7e3a03fe..b9d4a895d 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs @@ -4,10 +4,10 @@ using System; using System.Collections.Generic; using System.Globalization; using System.Linq; -using System.Net; using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; @@ -15,21 +15,21 @@ using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; -using MediaBrowser.Model.Net; using MediaBrowser.Model.Providers; using MediaBrowser.Model.Serialization; using Microsoft.Extensions.Logging; namespace MediaBrowser.Providers.Plugins.Tmdb.TV { - public class TmdbEpisodeProvider : - TmdbEpisodeProviderBase, - IRemoteMetadataProvider, - IHasOrder + public class TmdbEpisodeProvider : IRemoteMetadataProvider, IHasOrder { - public TmdbEpisodeProvider(IHttpClientFactory httpClientFactory, IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IFileSystem fileSystem, ILocalizationManager localization, ILoggerFactory loggerFactory) - : base(httpClientFactory, configurationManager, jsonSerializer, fileSystem, localization, loggerFactory) + private readonly IHttpClientFactory _httpClientFactory; + private readonly TmdbClientManager _tmdbClientManager; + + public TmdbEpisodeProvider(IHttpClientFactory httpClientFactory, TmdbClientManager tmdbClientManager) { + _httpClientFactory = httpClientFactory; + _tmdbClientManager = tmdbClientManager; } // After TheTvDb @@ -39,51 +39,54 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV public async Task> GetSearchResults(EpisodeInfo searchInfo, CancellationToken cancellationToken) { - var list = new List(); - // The search query must either provide an episode number or date if (!searchInfo.IndexNumber.HasValue || !searchInfo.ParentIndexNumber.HasValue) { - return list; + return Enumerable.Empty(); } - var metadataResult = await GetMetadata(searchInfo, cancellationToken).ConfigureAwait(false); + var metadataResult = await GetMetadata(searchInfo, cancellationToken); - if (metadataResult.HasMetadata) + if (!metadataResult.HasMetadata) { - var item = metadataResult.Item; - - list.Add(new RemoteSearchResult - { - IndexNumber = item.IndexNumber, - Name = item.Name, - ParentIndexNumber = item.ParentIndexNumber, - PremiereDate = item.PremiereDate, - ProductionYear = item.ProductionYear, - ProviderIds = item.ProviderIds, - SearchProviderName = Name, - IndexNumberEnd = item.IndexNumberEnd - }); + return Enumerable.Empty(); } + var list = new List(); + + var item = metadataResult.Item; + + list.Add(new RemoteSearchResult + { + IndexNumber = item.IndexNumber, + Name = item.Name, + ParentIndexNumber = item.ParentIndexNumber, + PremiereDate = item.PremiereDate, + ProductionYear = item.ProductionYear, + ProviderIds = item.ProviderIds, + SearchProviderName = Name, + IndexNumberEnd = item.IndexNumberEnd + }); + return list; } public async Task> GetMetadata(EpisodeInfo info, CancellationToken cancellationToken) { - var result = new MetadataResult(); + var metadataResult = new MetadataResult(); // Allowing this will dramatically increase scan times if (info.IsMissingEpisode) { - return result; + return metadataResult; } - info.SeriesProviderIds.TryGetValue(MetadataProvider.Tmdb.ToString(), out string seriesTmdbId); + info.SeriesProviderIds.TryGetValue(MetadataProvider.Tmdb.ToString(), out string tmdbId); - if (string.IsNullOrEmpty(seriesTmdbId)) + var seriesTmdbId = Convert.ToInt32(tmdbId, CultureInfo.InvariantCulture); + if (seriesTmdbId <= 0) { - return result; + return metadataResult; } var seasonNumber = info.ParentIndexNumber; @@ -91,125 +94,121 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV if (!seasonNumber.HasValue || !episodeNumber.HasValue) { - return result; + return metadataResult; } - try - { - var response = await GetEpisodeInfo(seriesTmdbId, seasonNumber.Value, episodeNumber.Value, info.MetadataLanguage, cancellationToken).ConfigureAwait(false); + var episodeResult = await _tmdbClientManager + .GetEpisodeAsync(seriesTmdbId, seasonNumber.Value, episodeNumber.Value, info.MetadataLanguage, TmdbUtils.GetImageLanguagesParam(info.MetadataLanguage), cancellationToken) + .ConfigureAwait(false); - result.HasMetadata = true; - result.QueriedById = true; + if (episodeResult == null) + { + return metadataResult; + } - if (!string.IsNullOrEmpty(response.Overview)) - { - // if overview is non-empty, we can assume that localized data was returned - result.ResultLanguage = info.MetadataLanguage; - } + metadataResult.HasMetadata = true; + metadataResult.QueriedById = true; - var item = new Episode(); - result.Item = item; + if (!string.IsNullOrEmpty(episodeResult.Overview)) + { + // if overview is non-empty, we can assume that localized data was returned + metadataResult.ResultLanguage = info.MetadataLanguage; + } - item.Name = info.Name; - item.IndexNumber = info.IndexNumber; - item.ParentIndexNumber = info.ParentIndexNumber; - item.IndexNumberEnd = info.IndexNumberEnd; + var item = new Episode + { + Name = info.Name, + IndexNumber = info.IndexNumber, + ParentIndexNumber = info.ParentIndexNumber, + IndexNumberEnd = info.IndexNumberEnd + }; - if (response.External_Ids != null && response.External_Ids.Tvdb_Id > 0) - { - item.SetProviderId(MetadataProvider.Tvdb, response.External_Ids.Tvdb_Id.Value.ToString(CultureInfo.InvariantCulture)); - } + if (!string.IsNullOrEmpty(episodeResult.ExternalIds?.TvdbId)) + { + item.SetProviderId(MetadataProvider.Tvdb, episodeResult.ExternalIds.TvdbId); + } - item.PremiereDate = response.Air_Date; - item.ProductionYear = result.Item.PremiereDate.Value.Year; + item.PremiereDate = episodeResult.AirDate; + item.ProductionYear = episodeResult.AirDate?.Year; - item.Name = response.Name; - item.Overview = response.Overview; + item.Name = episodeResult.Name; + item.Overview = episodeResult.Overview; - item.CommunityRating = (float)response.Vote_Average; + item.CommunityRating = Convert.ToSingle(episodeResult.VoteAverage); - if (response.Videos?.Results != null) + if (episodeResult.Videos?.Results != null) + { + foreach (var video in episodeResult.Videos.Results) { - foreach (var video in response.Videos.Results) + if (TmdbUtils.IsTrailerType(video)) { - if (video.Type.Equals("trailer", System.StringComparison.OrdinalIgnoreCase) - || video.Type.Equals("clip", System.StringComparison.OrdinalIgnoreCase)) - { - if (video.Site.Equals("youtube", System.StringComparison.OrdinalIgnoreCase)) - { - var videoUrl = string.Format(CultureInfo.InvariantCulture, "http://www.youtube.com/watch?v={0}", video.Key); - item.AddTrailerUrl(videoUrl); - } - } + var videoUrl = string.Format(CultureInfo.InvariantCulture, "http://www.youtube.com/watch?v={0}", video.Key); + item.AddTrailerUrl(videoUrl); } } + } - result.ResetPeople(); + var credits = episodeResult.Credits; - var credits = response.Credits; - if (credits != null) + if (credits?.Cast != null) + { + foreach (var actor in credits.Cast.OrderBy(a => a.Order).Take(TmdbUtils.MaxCastMembers)) { - // Actors, Directors, Writers - all in People - // actors come from cast - if (credits.Cast != null) + metadataResult.AddPerson(new PersonInfo { - foreach (var actor in credits.Cast.OrderBy(a => a.Order)) - { - result.AddPerson(new PersonInfo { Name = actor.Name.Trim(), Role = actor.Character, Type = PersonType.Actor, SortOrder = actor.Order }); - } - } - - // guest stars - if (credits.Guest_Stars != null) - { - foreach (var guest in credits.Guest_Stars.OrderBy(a => a.Order)) - { - result.AddPerson(new PersonInfo { Name = guest.Name.Trim(), Role = guest.Character, Type = PersonType.GuestStar, SortOrder = guest.Order }); - } - } + Name = actor.Name.Trim(), + Role = actor.Character, + Type = PersonType.Actor, + SortOrder = actor.Order + }); + } + } - // and the rest from crew - if (credits.Crew != null) + if (credits?.GuestStars != null) + { + foreach (var guest in credits.GuestStars.OrderBy(a => a.Order).Take(TmdbUtils.MaxCastMembers)) + { + metadataResult.AddPerson(new PersonInfo { - var keepTypes = new[] - { - PersonType.Director, - PersonType.Writer, - PersonType.Producer - }; - - foreach (var person in credits.Crew) - { - // Normalize this - var type = TmdbUtils.MapCrewToPersonType(person); - - if (!keepTypes.Contains(type, StringComparer.OrdinalIgnoreCase) && - !keepTypes.Contains(person.Job ?? string.Empty, StringComparer.OrdinalIgnoreCase)) - { - continue; - } - - result.AddPerson(new PersonInfo { Name = person.Name.Trim(), Role = person.Job, Type = type }); - } - } + Name = guest.Name.Trim(), + Role = guest.Character, + Type = PersonType.GuestStar, + SortOrder = guest.Order + }); } } - catch (HttpException ex) + + // and the rest from crew + if (credits?.Crew != null) { - if (ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound) + foreach (var person in credits.Crew) { - return result; - } + // Normalize this + var type = TmdbUtils.MapCrewToPersonType(person); + + if (!TmdbUtils.WantedCrewTypes.Contains(type, StringComparer.OrdinalIgnoreCase) + && !TmdbUtils.WantedCrewTypes.Contains(person.Job ?? string.Empty, StringComparer.OrdinalIgnoreCase)) + { + continue; + } - throw; + metadataResult.AddPerson(new PersonInfo + { + Name = person.Name.Trim(), + Role = person.Job, + Type = type + }); + } } - return result; + metadataResult.Item = item; + + return metadataResult; } public Task GetImageResponse(string url, CancellationToken cancellationToken) { - return GetResponse(url, cancellationToken); + return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken); } } } diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProviderBase.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProviderBase.cs deleted file mode 100644 index 34d2424a3..000000000 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProviderBase.cs +++ /dev/null @@ -1,156 +0,0 @@ -#pragma warning disable CS1591 - -using System; -using System.Globalization; -using System.IO; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Model.Globalization; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Serialization; -using MediaBrowser.Providers.Plugins.Tmdb.Models.TV; -using MediaBrowser.Providers.Plugins.Tmdb.Movies; -using Microsoft.Extensions.Logging; - -namespace MediaBrowser.Providers.Plugins.Tmdb.TV -{ - public abstract class TmdbEpisodeProviderBase - { - private const string EpisodeUrlPattern = TmdbUtils.BaseTmdbApiUrl + @"3/tv/{0}/season/{1}/episode/{2}?api_key={3}&append_to_response=images,external_ids,credits,videos"; - - private readonly IHttpClientFactory _httpClientFactory; - private readonly IServerConfigurationManager _configurationManager; - private readonly IJsonSerializer _jsonSerializer; - private readonly IFileSystem _fileSystem; - private readonly ILogger _logger; - - protected TmdbEpisodeProviderBase(IHttpClientFactory httpClientFactory, IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IFileSystem fileSystem, ILocalizationManager localization, ILoggerFactory loggerFactory) - { - _httpClientFactory = httpClientFactory; - _configurationManager = configurationManager; - _jsonSerializer = jsonSerializer; - _fileSystem = fileSystem; - _logger = loggerFactory.CreateLogger(); - } - - protected ILogger Logger => _logger; - - protected async Task GetEpisodeInfo( - string seriesTmdbId, - int season, - int episodeNumber, - string preferredMetadataLanguage, - CancellationToken cancellationToken) - { - await EnsureEpisodeInfo(seriesTmdbId, season, episodeNumber, preferredMetadataLanguage, cancellationToken) - .ConfigureAwait(false); - - var dataFilePath = GetDataFilePath(seriesTmdbId, season, episodeNumber, preferredMetadataLanguage); - - return _jsonSerializer.DeserializeFromFile(dataFilePath); - } - - internal Task EnsureEpisodeInfo(string tmdbId, int seasonNumber, int episodeNumber, string language, CancellationToken cancellationToken) - { - if (string.IsNullOrEmpty(tmdbId)) - { - throw new ArgumentNullException(nameof(tmdbId)); - } - - if (string.IsNullOrEmpty(language)) - { - throw new ArgumentNullException(nameof(language)); - } - - var path = GetDataFilePath(tmdbId, seasonNumber, episodeNumber, language); - - var fileInfo = _fileSystem.GetFileSystemInfo(path); - - if (fileInfo.Exists) - { - // If it's recent or automatic updates are enabled, don't re-download - if ((DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 2) - { - return Task.CompletedTask; - } - } - - return DownloadEpisodeInfo(tmdbId, seasonNumber, episodeNumber, language, cancellationToken); - } - - internal string GetDataFilePath(string tmdbId, int seasonNumber, int episodeNumber, string preferredLanguage) - { - if (string.IsNullOrEmpty(tmdbId)) - { - throw new ArgumentNullException(nameof(tmdbId)); - } - - if (string.IsNullOrEmpty(preferredLanguage)) - { - throw new ArgumentNullException(nameof(preferredLanguage)); - } - - var path = TmdbSeriesProvider.GetSeriesDataPath(_configurationManager.ApplicationPaths, tmdbId); - - var filename = string.Format( - CultureInfo.InvariantCulture, - "season-{0}-episode-{1}-{2}.json", - seasonNumber.ToString(CultureInfo.InvariantCulture), - episodeNumber.ToString(CultureInfo.InvariantCulture), - preferredLanguage); - - return Path.Combine(path, filename); - } - - internal async Task DownloadEpisodeInfo(string id, int seasonNumber, int episodeNumber, string preferredMetadataLanguage, CancellationToken cancellationToken) - { - var mainResult = await FetchMainResult(EpisodeUrlPattern, id, seasonNumber, episodeNumber, preferredMetadataLanguage, cancellationToken).ConfigureAwait(false); - - var dataFilePath = GetDataFilePath(id, seasonNumber, episodeNumber, preferredMetadataLanguage); - - Directory.CreateDirectory(Path.GetDirectoryName(dataFilePath)); - _jsonSerializer.SerializeToFile(mainResult, dataFilePath); - } - - internal async Task FetchMainResult(string urlPattern, string id, int seasonNumber, int episodeNumber, string language, CancellationToken cancellationToken) - { - var url = string.Format( - CultureInfo.InvariantCulture, - urlPattern, - id, - seasonNumber.ToString(CultureInfo.InvariantCulture), - episodeNumber, - TmdbUtils.ApiKey); - - if (!string.IsNullOrEmpty(language)) - { - url += string.Format(CultureInfo.InvariantCulture, "&language={0}", language); - } - - var includeImageLanguageParam = TmdbMovieProvider.GetImageLanguagesParam(language); - // Get images in english and with no language - url += "&include_image_language=" + includeImageLanguageParam; - - cancellationToken.ThrowIfCancellationRequested(); - - using var requestMessage = new HttpRequestMessage(HttpMethod.Get, url); - foreach (var header in TmdbUtils.AcceptHeaders) - { - requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); - } - - using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage, cancellationToken).ConfigureAwait(false); - await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); - return await _jsonSerializer.DeserializeFromStreamAsync(stream).ConfigureAwait(false); - } - - protected Task GetResponse(string url, CancellationToken cancellationToken) - { - return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken); - } - } -} diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs index dcc7f8700..aeb0e5d34 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; -using System.IO; +using System.Globalization; using System.Linq; using System.Net.Http; using System.Threading; @@ -13,29 +13,25 @@ using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Extensions; using MediaBrowser.Model.Providers; -using MediaBrowser.Model.Serialization; -using MediaBrowser.Providers.Plugins.Tmdb.Models.General; -using MediaBrowser.Providers.Plugins.Tmdb.Movies; namespace MediaBrowser.Providers.Plugins.Tmdb.TV { public class TmdbSeasonImageProvider : IRemoteImageProvider, IHasOrder { - private readonly IJsonSerializer _jsonSerializer; private readonly IHttpClientFactory _httpClientFactory; + private readonly TmdbClientManager _tmdbClientManager; - public TmdbSeasonImageProvider(IJsonSerializer jsonSerializer, IHttpClientFactory httpClientFactory) + public TmdbSeasonImageProvider(IHttpClientFactory httpClientFactory, TmdbClientManager tmdbClientManager) { - _jsonSerializer = jsonSerializer; _httpClientFactory = httpClientFactory; + _tmdbClientManager = tmdbClientManager; } public int Order => 1; - public string Name => ProviderName; - - public static string ProviderName => TmdbUtils.ProviderName; + public string Name => TmdbUtils.ProviderName; public Task GetImageResponse(string url, CancellationToken cancellationToken) { @@ -45,87 +41,45 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV public async Task> GetImages(BaseItem item, CancellationToken cancellationToken) { var season = (Season)item; - var series = season.Series; - - var seriesId = series?.GetProviderId(MetadataProvider.Tmdb); - - if (string.IsNullOrEmpty(seriesId)) - { - return Enumerable.Empty(); - } + var series = season?.Series; - var seasonNumber = season.IndexNumber; + var seriesTmdbId = Convert.ToInt32(series?.GetProviderId(MetadataProvider.Tmdb), CultureInfo.InvariantCulture); - if (!seasonNumber.HasValue) + if (seriesTmdbId <= 0 || season?.IndexNumber != null) { return Enumerable.Empty(); } var language = item.GetPreferredMetadataLanguage(); - var results = await FetchImages(season, seriesId, language, cancellationToken).ConfigureAwait(false); + var seasonResult = await _tmdbClientManager + .GetSeasonAsync(seriesTmdbId, season.IndexNumber.Value, language, TmdbUtils.GetImageLanguagesParam(language), cancellationToken) + .ConfigureAwait(false); - var tmdbSettings = await TmdbMovieProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); - - var tmdbImageUrl = tmdbSettings.images.GetImageUrl("original"); - - var list = results.Select(i => new RemoteImageInfo - { - Url = tmdbImageUrl + i.File_Path, - CommunityRating = i.Vote_Average, - VoteCount = i.Vote_Count, - Width = i.Width, - Height = i.Height, - Language = TmdbMovieProvider.AdjustImageLanguage(i.Iso_639_1, language), - ProviderName = Name, - Type = ImageType.Primary, - RatingType = RatingType.Score - }); - - var isLanguageEn = string.Equals(language, "en", StringComparison.OrdinalIgnoreCase); - - return list.OrderByDescending(i => + if (seasonResult?.Images?.Posters == null) { - if (string.Equals(language, i.Language, StringComparison.OrdinalIgnoreCase)) - { - return 3; - } - - if (!isLanguageEn) - { - if (string.Equals("en", i.Language, StringComparison.OrdinalIgnoreCase)) - { - return 2; - } - } - - if (string.IsNullOrEmpty(i.Language)) - { - return isLanguageEn ? 3 : 2; - } - - return 0; - }) - .ThenByDescending(i => i.CommunityRating ?? 0) - .ThenByDescending(i => i.VoteCount ?? 0); - } - - private async Task> FetchImages(Season item, string tmdbId, string language, CancellationToken cancellationToken) - { - var seasonNumber = item.IndexNumber.GetValueOrDefault(); - await TmdbSeasonProvider.Current.EnsureSeasonInfo(tmdbId, seasonNumber, language, cancellationToken).ConfigureAwait(false); - - var path = TmdbSeasonProvider.Current.GetDataFilePath(tmdbId, seasonNumber, language); + return Enumerable.Empty(); + } - if (!string.IsNullOrEmpty(path)) + var remoteImages = new List(); + for (var i = 0; i < seasonResult.Images.Posters.Count; i++) { - if (File.Exists(path)) + var image = seasonResult.Images.Posters[i]; + remoteImages.Add(new RemoteImageInfo { - return _jsonSerializer.DeserializeFromFile(path).Images.Posters; - } + Url = _tmdbClientManager.GetPosterUrl(image.FilePath), + CommunityRating = image.VoteAverage, + VoteCount = image.VoteCount, + Width = image.Width, + Height = image.Height, + Language = TmdbUtils.AdjustImageLanguage(image.Iso_639_1, language), + ProviderName = Name, + Type = ImageType.Primary, + RatingType = RatingType.Score + }); } - return null; + return remoteImages.OrderByLanguageDescending(language); } public IEnumerable GetSupportedImages(BaseItem item) diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs index c9b257fcc..6ca462474 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs @@ -3,53 +3,28 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.IO; -using System.Net; +using System.Linq; using System.Net.Http; -using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Globalization; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Net; using MediaBrowser.Model.Providers; -using MediaBrowser.Model.Serialization; -using MediaBrowser.Providers.Plugins.Tmdb.Models.TV; -using MediaBrowser.Providers.Plugins.Tmdb.Movies; -using Microsoft.Extensions.Logging; -using Season = MediaBrowser.Controller.Entities.TV.Season; namespace MediaBrowser.Providers.Plugins.Tmdb.TV { public class TmdbSeasonProvider : IRemoteMetadataProvider { - private const string GetTvInfo3 = TmdbUtils.BaseTmdbApiUrl + @"3/tv/{0}/season/{1}?api_key={2}&append_to_response=images,keywords,external_ids,credits,videos"; - private readonly IHttpClientFactory _httpClientFactory; - private readonly IServerConfigurationManager _configurationManager; - private readonly IJsonSerializer _jsonSerializer; - private readonly IFileSystem _fileSystem; - private readonly ILogger _logger; - - internal static TmdbSeasonProvider Current { get; private set; } - - public TmdbSeasonProvider( - IHttpClientFactory httpClientFactory, - IServerConfigurationManager configurationManager, - IFileSystem fileSystem, - IJsonSerializer jsonSerializer, - ILogger logger) + private readonly TmdbClientManager _tmdbClientManager; + + public TmdbSeasonProvider(IHttpClientFactory httpClientFactory, TmdbClientManager tmdbClientManager) { _httpClientFactory = httpClientFactory; - _configurationManager = configurationManager; - _fileSystem = fileSystem; - _jsonSerializer = jsonSerializer; - _logger = logger; - Current = this; + _tmdbClientManager = tmdbClientManager; } public string Name => TmdbUtils.ProviderName; @@ -62,180 +37,86 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV var seasonNumber = info.IndexNumber; - if (!string.IsNullOrWhiteSpace(seriesTmdbId) && seasonNumber.HasValue) + if (string.IsNullOrWhiteSpace(seriesTmdbId) || !seasonNumber.HasValue) { - try - { - var seasonInfo = await GetSeasonInfo(seriesTmdbId, seasonNumber.Value, info.MetadataLanguage, cancellationToken) - .ConfigureAwait(false); - - result.HasMetadata = true; - result.Item = new Season(); - - // Don't use moviedb season names for now until if/when we have field-level configuration - // result.Item.Name = seasonInfo.name; - - result.Item.Name = info.Name; - - result.Item.IndexNumber = seasonNumber; - - result.Item.Overview = seasonInfo.Overview; - - if (seasonInfo.External_Ids != null && seasonInfo.External_Ids.Tvdb_Id > 0) - { - result.Item.SetProviderId(MetadataProvider.Tvdb, seasonInfo.External_Ids.Tvdb_Id.Value.ToString(CultureInfo.InvariantCulture)); - } - - var credits = seasonInfo.Credits; - if (credits != null) - { - // Actors, Directors, Writers - all in People - // actors come from cast - if (credits.Cast != null) - { - // foreach (var actor in credits.cast.OrderBy(a => a.order)) result.Item.AddPerson(new PersonInfo { Name = actor.name.Trim(), Role = actor.character, Type = PersonType.Actor, SortOrder = actor.order }); - } - - // and the rest from crew - if (credits.Crew != null) - { - // foreach (var person in credits.crew) result.Item.AddPerson(new PersonInfo { Name = person.name.Trim(), Role = person.job, Type = person.department }); - } - } - - result.Item.PremiereDate = seasonInfo.Air_Date; - result.Item.ProductionYear = result.Item.PremiereDate.Value.Year; - } - catch (HttpException ex) - { - _logger.LogError(ex, "No metadata found for {0}", seasonNumber.Value); - - if (ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound) - { - return result; - } - - throw; - } + return result; } - return result; - } - - public Task> GetSearchResults(SeasonInfo searchInfo, CancellationToken cancellationToken) - { - return Task.FromResult>(new List()); - } - - public Task GetImageResponse(string url, CancellationToken cancellationToken) - { - return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken); - } - - private async Task GetSeasonInfo( - string seriesTmdbId, - int season, - string preferredMetadataLanguage, - CancellationToken cancellationToken) - { - await EnsureSeasonInfo(seriesTmdbId, season, preferredMetadataLanguage, cancellationToken) - .ConfigureAwait(false); - - var dataFilePath = GetDataFilePath(seriesTmdbId, season, preferredMetadataLanguage); - - return _jsonSerializer.DeserializeFromFile(dataFilePath); - } + var seasonResult = await _tmdbClientManager + .GetSeasonAsync(Convert.ToInt32(seriesTmdbId, CultureInfo.InvariantCulture), seasonNumber.Value, info.MetadataLanguage, TmdbUtils.GetImageLanguagesParam(info.MetadataLanguage), cancellationToken) + .ConfigureAwait(false); - internal Task EnsureSeasonInfo(string tmdbId, int seasonNumber, string language, CancellationToken cancellationToken) - { - if (string.IsNullOrEmpty(tmdbId)) + if (seasonResult == null) { - throw new ArgumentNullException(nameof(tmdbId)); + return result; } - if (string.IsNullOrEmpty(language)) + result.HasMetadata = true; + result.Item = new Season { - throw new ArgumentNullException(nameof(language)); - } + Name = info.Name, + IndexNumber = seasonNumber, + Overview = seasonResult?.Overview + }; - var path = GetDataFilePath(tmdbId, seasonNumber, language); - - var fileInfo = _fileSystem.GetFileSystemInfo(path); + if (!string.IsNullOrEmpty(seasonResult.ExternalIds?.TvdbId)) + { + result.Item.SetProviderId(MetadataProvider.Tvdb, seasonResult.ExternalIds.TvdbId); + } - if (fileInfo.Exists) + // TODO why was this disabled? + var credits = seasonResult.Credits; + if (credits?.Cast != null) { - // If it's recent or automatic updates are enabled, don't re-download - if ((DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 2) + var cast = credits.Cast.OrderBy(c => c.Order).Take(TmdbUtils.MaxCastMembers).ToList(); + for (var i = 0; i < cast.Count; i++) { - return Task.CompletedTask; + result.AddPerson(new PersonInfo + { + Name = cast[i].Name.Trim(), + Role = cast[i].Character, + Type = PersonType.Actor, + SortOrder = cast[i].Order + }); } } - return DownloadSeasonInfo(tmdbId, seasonNumber, language, cancellationToken); - } - - internal string GetDataFilePath(string tmdbId, int seasonNumber, string preferredLanguage) - { - if (string.IsNullOrEmpty(tmdbId)) + if (credits?.Crew != null) { - throw new ArgumentNullException(nameof(tmdbId)); - } + foreach (var person in credits.Crew) + { + // Normalize this + var type = TmdbUtils.MapCrewToPersonType(person); - if (string.IsNullOrEmpty(preferredLanguage)) - { - throw new ArgumentNullException(nameof(preferredLanguage)); - } + if (!TmdbUtils.WantedCrewTypes.Contains(type, StringComparer.OrdinalIgnoreCase) + && !TmdbUtils.WantedCrewTypes.Contains(person.Job ?? string.Empty, StringComparer.OrdinalIgnoreCase)) + { + continue; + } - var path = TmdbSeriesProvider.GetSeriesDataPath(_configurationManager.ApplicationPaths, tmdbId); + result.AddPerson(new PersonInfo + { + Name = person.Name.Trim(), + Role = person.Job, + Type = type + }); + } + } - var filename = string.Format( - CultureInfo.InvariantCulture, - "season-{0}-{1}.json", - seasonNumber.ToString(CultureInfo.InvariantCulture), - preferredLanguage); + result.Item.PremiereDate = seasonResult.AirDate; + result.Item.ProductionYear = seasonResult.AirDate?.Year; - return Path.Combine(path, filename); + return result; } - internal async Task DownloadSeasonInfo(string id, int seasonNumber, string preferredMetadataLanguage, CancellationToken cancellationToken) + public Task> GetSearchResults(SeasonInfo searchInfo, CancellationToken cancellationToken) { - var mainResult = await FetchMainResult(id, seasonNumber, preferredMetadataLanguage, cancellationToken).ConfigureAwait(false); - - var dataFilePath = GetDataFilePath(id, seasonNumber, preferredMetadataLanguage); - - Directory.CreateDirectory(Path.GetDirectoryName(dataFilePath)); - _jsonSerializer.SerializeToFile(mainResult, dataFilePath); + return Task.FromResult(Enumerable.Empty()); } - internal async Task FetchMainResult(string id, int seasonNumber, string language, CancellationToken cancellationToken) + public Task GetImageResponse(string url, CancellationToken cancellationToken) { - var url = string.Format( - CultureInfo.InvariantCulture, - GetTvInfo3, - id, - seasonNumber.ToString(CultureInfo.InvariantCulture), - TmdbUtils.ApiKey); - - if (!string.IsNullOrEmpty(language)) - { - url += string.Format(CultureInfo.InvariantCulture, "&language={0}", TmdbMovieProvider.NormalizeLanguage(language)); - } - - var includeImageLanguageParam = TmdbMovieProvider.GetImageLanguagesParam(language); - // Get images in english and with no language - url += "&include_image_language=" + includeImageLanguageParam; - - cancellationToken.ThrowIfCancellationRequested(); - - using var requestMessage = new HttpRequestMessage(HttpMethod.Get, url); - foreach (var header in TmdbUtils.AcceptHeaders) - { - requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); - } - - using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage, cancellationToken).ConfigureAwait(false); - await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); - return await _jsonSerializer.DeserializeFromStreamAsync(stream).ConfigureAwait(false); + return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken); } } } diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs index 179ceb825..0b477f75b 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; -using System.IO; +using System.Globalization; using System.Linq; using System.Net.Http; using System.Threading; @@ -13,28 +13,23 @@ using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Extensions; using MediaBrowser.Model.Providers; -using MediaBrowser.Model.Serialization; -using MediaBrowser.Providers.Plugins.Tmdb.Models.General; -using MediaBrowser.Providers.Plugins.Tmdb.Models.TV; -using MediaBrowser.Providers.Plugins.Tmdb.Movies; namespace MediaBrowser.Providers.Plugins.Tmdb.TV { public class TmdbSeriesImageProvider : IRemoteImageProvider, IHasOrder { - private readonly IJsonSerializer _jsonSerializer; private readonly IHttpClientFactory _httpClientFactory; + private readonly TmdbClientManager _tmdbClientManager; - public TmdbSeriesImageProvider(IJsonSerializer jsonSerializer, IHttpClientFactory httpClientFactory) + public TmdbSeriesImageProvider(IHttpClientFactory httpClientFactory, TmdbClientManager tmdbClientManager) { - _jsonSerializer = jsonSerializer; _httpClientFactory = httpClientFactory; + _tmdbClientManager = tmdbClientManager; } - public string Name => ProviderName; - - public static string ProviderName => TmdbUtils.ProviderName; + public string Name => TmdbUtils.ProviderName; // After tvdb and fanart public int Order => 2; @@ -55,124 +50,60 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV public async Task> GetImages(BaseItem item, CancellationToken cancellationToken) { - var list = new List(); - - var results = await FetchImages(item, null, cancellationToken).ConfigureAwait(false); + var tmdbId = item.GetProviderId(MetadataProvider.Tmdb); - if (results == null) + if (string.IsNullOrEmpty(tmdbId)) { - return list; + return null; } - var tmdbSettings = await TmdbMovieProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); - - var tmdbImageUrl = tmdbSettings.images.GetImageUrl("original"); - var language = item.GetPreferredMetadataLanguage(); - list.AddRange(GetPosters(results).Select(i => new RemoteImageInfo - { - Url = tmdbImageUrl + i.File_Path, - CommunityRating = i.Vote_Average, - VoteCount = i.Vote_Count, - Width = i.Width, - Height = i.Height, - Language = TmdbMovieProvider.AdjustImageLanguage(i.Iso_639_1, language), - ProviderName = Name, - Type = ImageType.Primary, - RatingType = RatingType.Score - })); - - list.AddRange(GetBackdrops(results).Select(i => new RemoteImageInfo - { - Url = tmdbImageUrl + i.File_Path, - CommunityRating = i.Vote_Average, - VoteCount = i.Vote_Count, - Width = i.Width, - Height = i.Height, - ProviderName = Name, - Type = ImageType.Backdrop, - RatingType = RatingType.Score - })); - - var isLanguageEn = string.Equals(language, "en", StringComparison.OrdinalIgnoreCase); - - return list.OrderByDescending(i => - { - if (string.Equals(language, i.Language, StringComparison.OrdinalIgnoreCase)) - { - return 3; - } - - if (!isLanguageEn) - { - if (string.Equals("en", i.Language, StringComparison.OrdinalIgnoreCase)) - { - return 2; - } - } - - if (string.IsNullOrEmpty(i.Language)) - { - return isLanguageEn ? 3 : 2; - } - - return 0; - }) - .ThenByDescending(i => i.CommunityRating ?? 0) - .ThenByDescending(i => i.VoteCount ?? 0); - } + var series = await _tmdbClientManager + .GetSeriesAsync(Convert.ToInt32(tmdbId, CultureInfo.InvariantCulture), language, TmdbUtils.GetImageLanguagesParam(language), cancellationToken) + .ConfigureAwait(false); - /// - /// Gets the posters. - /// - /// The images. - private IEnumerable GetPosters(Images images) - { - return images.Posters ?? new List(); - } - - /// - /// Gets the backdrops. - /// - /// The images. - private IEnumerable GetBackdrops(Images images) - { - var eligibleBackdrops = images.Backdrops ?? new List(); - - return eligibleBackdrops.OrderByDescending(i => i.Vote_Average) - .ThenByDescending(i => i.Vote_Count); - } - - /// - /// Fetches the images. - /// - /// The item. - /// The language. - /// The cancellation token. - /// Task{MovieImages}. - private async Task FetchImages( - BaseItem item, - string language, - CancellationToken cancellationToken) - { - var tmdbId = item.GetProviderId(MetadataProvider.Tmdb); - - if (string.IsNullOrEmpty(tmdbId)) + if (series?.Images == null) { - return null; + return Array.Empty(); } - await TmdbSeriesProvider.Current.EnsureSeriesInfo(tmdbId, language, cancellationToken).ConfigureAwait(false); + var remoteImages = new List(); - var path = TmdbSeriesProvider.Current.GetDataFilePath(tmdbId, language); + for (var i = 0; i < series.Images.Posters.Count; i++) + { + var poster = series.Images.Posters[i]; + remoteImages.Add(new RemoteImageInfo + { + Url = _tmdbClientManager.GetPosterUrl(poster.FilePath), + CommunityRating = poster.VoteAverage, + VoteCount = poster.VoteCount, + Width = poster.Width, + Height = poster.Height, + Language = TmdbUtils.AdjustImageLanguage(poster.Iso_639_1, language), + ProviderName = Name, + Type = ImageType.Primary, + RatingType = RatingType.Score + }); + } - if (!string.IsNullOrEmpty(path) && File.Exists(path)) + for (var i = 0; i < series.Images.Backdrops.Count; i++) { - return _jsonSerializer.DeserializeFromFile(path).Images; + var backdrop = series.Images.Backdrops[i]; + remoteImages.Add(new RemoteImageInfo + { + Url = _tmdbClientManager.GetBackdropUrl(backdrop.FilePath), + CommunityRating = backdrop.VoteAverage, + VoteCount = backdrop.VoteCount, + Width = backdrop.Width, + Height = backdrop.Height, + ProviderName = Name, + Type = ImageType.Backdrop, + RatingType = RatingType.Score + }); } - return null; + return remoteImages.OrderByLanguageDescending(language); } public Task GetImageResponse(string url, CancellationToken cancellationToken) diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs index 287ebca8c..f110785df 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs @@ -1,6 +1,7 @@ #pragma warning disable CS1591 using System; +using System.Collections; using System.Collections.Generic; using System.Globalization; using System.IO; @@ -9,7 +10,6 @@ using System.Net.Http; using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; @@ -17,93 +17,78 @@ using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Globalization; +using MediaBrowser.Model.IO; using MediaBrowser.Model.Providers; using MediaBrowser.Model.Serialization; -using MediaBrowser.Providers.Plugins.Tmdb.Models.Search; -using MediaBrowser.Providers.Plugins.Tmdb.Models.TV; using MediaBrowser.Providers.Plugins.Tmdb.Movies; using Microsoft.Extensions.Logging; +using TMDbLib.Objects.Find; +using TMDbLib.Objects.Search; +using TMDbLib.Objects.TvShows; namespace MediaBrowser.Providers.Plugins.Tmdb.TV { public class TmdbSeriesProvider : IRemoteMetadataProvider, IHasOrder { - private const string GetTvInfo3 = TmdbUtils.BaseTmdbApiUrl + @"3/tv/{0}?api_key={1}&append_to_response=credits,images,keywords,external_ids,videos,content_ratings"; - - private readonly IJsonSerializer _jsonSerializer; - private readonly IServerConfigurationManager _configurationManager; - private readonly ILogger _logger; private readonly IHttpClientFactory _httpClientFactory; - private readonly ILibraryManager _libraryManager; + private readonly TmdbClientManager _tmdbClientManager; private readonly CultureInfo _usCulture = new CultureInfo("en-US"); public TmdbSeriesProvider( - IJsonSerializer jsonSerializer, - IServerConfigurationManager configurationManager, - ILogger logger, IHttpClientFactory httpClientFactory, - ILibraryManager libraryManager) + TmdbClientManager tmdbClientManager) { - _jsonSerializer = jsonSerializer; - _configurationManager = configurationManager; - _logger = logger; _httpClientFactory = httpClientFactory; - _libraryManager = libraryManager; + _tmdbClientManager = tmdbClientManager; Current = this; } - internal static TmdbSeriesProvider Current { get; private set; } - public string Name => TmdbUtils.ProviderName; // After TheTVDB public int Order => 1; + internal static TmdbSeriesProvider Current { get; private set; } + public async Task> GetSearchResults(SeriesInfo searchInfo, CancellationToken cancellationToken) { var tmdbId = searchInfo.GetProviderId(MetadataProvider.Tmdb); if (!string.IsNullOrEmpty(tmdbId)) { - cancellationToken.ThrowIfCancellationRequested(); - - await EnsureSeriesInfo(tmdbId, searchInfo.MetadataLanguage, cancellationToken).ConfigureAwait(false); - - var dataFilePath = GetDataFilePath(tmdbId, searchInfo.MetadataLanguage); - - var obj = _jsonSerializer.DeserializeFromFile(dataFilePath); - - var tmdbSettings = await TmdbMovieProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); - var tmdbImageUrl = tmdbSettings.images.GetImageUrl("original"); + var series = await _tmdbClientManager + .GetSeriesAsync(Convert.ToInt32(tmdbId, CultureInfo.InvariantCulture), searchInfo.MetadataLanguage, searchInfo.MetadataLanguage, cancellationToken) + .ConfigureAwait(false); - var remoteResult = new RemoteSearchResult + if (series != null) { - Name = obj.Name, - SearchProviderName = Name, - ImageUrl = string.IsNullOrWhiteSpace(obj.Poster_Path) ? null : tmdbImageUrl + obj.Poster_Path - }; - - remoteResult.SetProviderId(MetadataProvider.Tmdb, obj.Id.ToString(_usCulture)); - remoteResult.SetProviderId(MetadataProvider.Imdb, obj.External_Ids.Imdb_Id); + var remoteResult = MapTvShowToRemoteSearchResult(series); - if (obj.External_Ids != null && obj.External_Ids.Tvdb_Id > 0) - { - remoteResult.SetProviderId(MetadataProvider.Tvdb, obj.External_Ids.Tvdb_Id.Value.ToString(_usCulture)); + return new[] { remoteResult }; } - - return new[] { remoteResult }; } var imdbId = searchInfo.GetProviderId(MetadataProvider.Imdb); if (!string.IsNullOrEmpty(imdbId)) { - var searchResult = await FindByExternalId(imdbId, "imdb_id", cancellationToken).ConfigureAwait(false); + var findResult = await _tmdbClientManager + .FindByExternalIdAsync(imdbId, FindExternalSource.Imdb, searchInfo.MetadataLanguage, cancellationToken) + .ConfigureAwait(false); - if (searchResult != null) + if (findResult?.TvResults != null) { - return new[] { searchResult }; + var imdbIdResults = new List(); + for (var i = 0; i < findResult.TvResults.Count; i++) + { + var remoteResult = MapSearchTvToRemoteSearchResult(findResult.TvResults[i]); + remoteResult.SetProviderId(MetadataProvider.Imdb, imdbId); + imdbIdResults.Add(remoteResult); + } + + return imdbIdResults; } } @@ -111,15 +96,79 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV if (!string.IsNullOrEmpty(tvdbId)) { - var searchResult = await FindByExternalId(tvdbId, "tvdb_id", cancellationToken).ConfigureAwait(false); + var findResult = await _tmdbClientManager + .FindByExternalIdAsync(tvdbId, FindExternalSource.TvDb, searchInfo.MetadataLanguage, cancellationToken) + .ConfigureAwait(false); + + if (findResult?.TvResults != null) + { + var tvIdResults = new List(); + for (var i = 0; i < findResult.TvResults.Count; i++) + { + var remoteResult = MapSearchTvToRemoteSearchResult(findResult.TvResults[i]); + remoteResult.SetProviderId(MetadataProvider.Tvdb, tvdbId); + tvIdResults.Add(remoteResult); + } + + return tvIdResults; + } + } + + var tvSearchResults = await _tmdbClientManager.SearchSeriesAsync(searchInfo.Name, searchInfo.MetadataLanguage, cancellationToken) + .ConfigureAwait(false); + + var remoteResults = new List(); + for (var i = 0; i < tvSearchResults.Count; i++) + { + remoteResults.Add(MapSearchTvToRemoteSearchResult(tvSearchResults[i])); + } + + return remoteResults; + } + + private RemoteSearchResult MapTvShowToRemoteSearchResult(TvShow series) + { + var remoteResult = new RemoteSearchResult + { + Name = series.Name ?? series.OriginalName, + SearchProviderName = Name, + ImageUrl = _tmdbClientManager.GetPosterUrl(series.PosterPath), + Overview = series.Overview + }; + + remoteResult.SetProviderId(MetadataProvider.Tmdb, series.Id.ToString(_usCulture)); + if (series.ExternalIds != null) + { + if (!string.IsNullOrEmpty(series.ExternalIds.ImdbId)) + { + remoteResult.SetProviderId(MetadataProvider.Imdb, series.ExternalIds.ImdbId); + } - if (searchResult != null) + if (!string.IsNullOrEmpty(series.ExternalIds.TvdbId)) { - return new[] { searchResult }; + remoteResult.SetProviderId(MetadataProvider.Tvdb, series.ExternalIds.TvdbId); } } - return await new TmdbSearch(_logger, _jsonSerializer, _libraryManager).GetSearchResults(searchInfo, cancellationToken).ConfigureAwait(false); + remoteResult.PremiereDate = series.FirstAirDate?.ToUniversalTime(); + + return remoteResult; + } + + private RemoteSearchResult MapSearchTvToRemoteSearchResult(SearchTv series) + { + var remoteResult = new RemoteSearchResult + { + Name = series.Name ?? series.OriginalName, + SearchProviderName = Name, + ImageUrl = _tmdbClientManager.GetPosterUrl(series.PosterPath), + Overview = series.Overview + }; + + remoteResult.SetProviderId(MetadataProvider.Tmdb, series.Id.ToString(_usCulture)); + remoteResult.PremiereDate = series.FirstAirDate?.ToUniversalTime(); + + return remoteResult; } public async Task> GetMetadata(SeriesInfo info, CancellationToken cancellationToken) @@ -137,11 +186,11 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV if (!string.IsNullOrEmpty(imdbId)) { - var searchResult = await FindByExternalId(imdbId, "imdb_id", cancellationToken).ConfigureAwait(false); + var searchResult = await _tmdbClientManager.FindByExternalIdAsync(imdbId, FindExternalSource.Imdb, info.MetadataLanguage, cancellationToken).ConfigureAwait(false); if (searchResult != null) { - tmdbId = searchResult.GetProviderId(MetadataProvider.Tmdb); + tmdbId = searchResult.TvResults.FirstOrDefault()?.Id.ToString(CultureInfo.InvariantCulture); } } } @@ -152,11 +201,11 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV if (!string.IsNullOrEmpty(tvdbId)) { - var searchResult = await FindByExternalId(tvdbId, "tvdb_id", cancellationToken).ConfigureAwait(false); + var searchResult = await _tmdbClientManager.FindByExternalIdAsync(tvdbId, FindExternalSource.TvDb, info.MetadataLanguage, cancellationToken).ConfigureAwait(false); if (searchResult != null) { - tmdbId = searchResult.GetProviderId(MetadataProvider.Tmdb); + tmdbId = searchResult.TvResults.FirstOrDefault()?.Id.ToString(CultureInfo.InvariantCulture); } } } @@ -164,13 +213,11 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV if (string.IsNullOrEmpty(tmdbId)) { result.QueriedById = false; - var searchResults = await new TmdbSearch(_logger, _jsonSerializer, _libraryManager).GetSearchResults(info, cancellationToken).ConfigureAwait(false); - - var searchResult = searchResults.FirstOrDefault(); + var searchResults = await _tmdbClientManager.SearchSeriesAsync(info.Name, info.MetadataLanguage, cancellationToken).ConfigureAwait(false); - if (searchResult != null) + if (searchResults.Count > 0) { - tmdbId = searchResult.GetProviderId(MetadataProvider.Tmdb); + tmdbId = searchResults[0].Id.ToString(CultureInfo.InvariantCulture); } } @@ -178,107 +225,83 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV { cancellationToken.ThrowIfCancellationRequested(); - result = await FetchMovieData(tmdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false); - - result.HasMetadata = result.Item != null; - } + var tvShow = await _tmdbClientManager + .GetSeriesAsync(Convert.ToInt32(tmdbId, CultureInfo.InvariantCulture), info.MetadataLanguage, TmdbUtils.GetImageLanguagesParam(info.MetadataLanguage), cancellationToken) + .ConfigureAwait(false); - return result; - } + result = new MetadataResult + { + Item = MapTvShowToSeries(tvShow, info.MetadataCountryCode), + ResultLanguage = info.MetadataLanguage ?? tvShow.OriginalLanguage + }; - private async Task> FetchMovieData(string tmdbId, string language, string preferredCountryCode, CancellationToken cancellationToken) - { - SeriesResult seriesInfo = await FetchMainResult(tmdbId, language, cancellationToken).ConfigureAwait(false); + foreach (var person in GetPersons(tvShow)) + { + result.AddPerson(person); + } - if (seriesInfo == null) - { - return null; + result.HasMetadata = result.Item != null; } - tmdbId = seriesInfo.Id.ToString(_usCulture); - - string dataFilePath = GetDataFilePath(tmdbId, language); - Directory.CreateDirectory(Path.GetDirectoryName(dataFilePath)); - _jsonSerializer.SerializeToFile(seriesInfo, dataFilePath); - - await EnsureSeriesInfo(tmdbId, language, cancellationToken).ConfigureAwait(false); - - var result = new MetadataResult - { - Item = new Series(), - ResultLanguage = seriesInfo.ResultLanguage - }; - - var settings = await TmdbMovieProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); - - ProcessMainInfo(result, seriesInfo, preferredCountryCode, settings); - return result; } - private void ProcessMainInfo(MetadataResult seriesResult, SeriesResult seriesInfo, string preferredCountryCode, TmdbSettingsResult settings) + private Series MapTvShowToSeries(TvShow seriesResult, string preferredCountryCode) { - var series = seriesResult.Item; + var series = new Series {Name = seriesResult.Name, OriginalTitle = seriesResult.OriginalName}; - series.Name = seriesInfo.Name; - series.OriginalTitle = seriesInfo.Original_Name; - series.SetProviderId(MetadataProvider.Tmdb, seriesInfo.Id.ToString(_usCulture)); + series.SetProviderId(MetadataProvider.Tmdb, seriesResult.Id.ToString(_usCulture)); - string voteAvg = seriesInfo.Vote_Average.ToString(CultureInfo.InvariantCulture); + series.CommunityRating = Convert.ToSingle(seriesResult.VoteAverage); - if (float.TryParse(voteAvg, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out float rating)) - { - series.CommunityRating = rating; - } + series.Overview = seriesResult.Overview; - series.Overview = seriesInfo.Overview; - - if (seriesInfo.Networks != null) + if (seriesResult.Networks != null) { - series.Studios = seriesInfo.Networks.Select(i => i.Name).ToArray(); + series.Studios = seriesResult.Networks.Select(i => i.Name).ToArray(); } - if (seriesInfo.Genres != null) + if (seriesResult.Genres != null) { - series.Genres = seriesInfo.Genres.Select(i => i.Name).ToArray(); + series.Genres = seriesResult.Genres.Select(i => i.Name).ToArray(); } - series.HomePageUrl = seriesInfo.Homepage; + series.HomePageUrl = seriesResult.Homepage; - series.RunTimeTicks = seriesInfo.Episode_Run_Time.Select(i => TimeSpan.FromMinutes(i).Ticks).FirstOrDefault(); + series.RunTimeTicks = seriesResult.EpisodeRunTime.Select(i => TimeSpan.FromMinutes(i).Ticks).FirstOrDefault(); - if (string.Equals(seriesInfo.Status, "Ended", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(seriesResult.Status, "Ended", StringComparison.OrdinalIgnoreCase)) { series.Status = SeriesStatus.Ended; - series.EndDate = seriesInfo.Last_Air_Date; + series.EndDate = seriesResult.LastAirDate; } else { series.Status = SeriesStatus.Continuing; } - series.PremiereDate = seriesInfo.First_Air_Date; + series.PremiereDate = seriesResult.FirstAirDate; - var ids = seriesInfo.External_Ids; + var ids = seriesResult.ExternalIds; if (ids != null) { - if (!string.IsNullOrWhiteSpace(ids.Imdb_Id)) + if (!string.IsNullOrWhiteSpace(ids.ImdbId)) { - series.SetProviderId(MetadataProvider.Imdb, ids.Imdb_Id); + series.SetProviderId(MetadataProvider.Imdb, ids.ImdbId); } - if (ids.Tvrage_Id > 0) + if (!string.IsNullOrEmpty(ids.TvrageId)) { - series.SetProviderId(MetadataProvider.TvRage, ids.Tvrage_Id.Value.ToString(_usCulture)); + series.SetProviderId(MetadataProvider.TvRage, ids.TvrageId); } - if (ids.Tvdb_Id > 0) + if (!string.IsNullOrEmpty(ids.TvdbId)) { - series.SetProviderId(MetadataProvider.Tvdb, ids.Tvdb_Id.Value.ToString(_usCulture)); + series.SetProviderId(MetadataProvider.Tvdb, ids.TvdbId); } } - var contentRatings = (seriesInfo.Content_Ratings ?? new ContentRatings()).Results ?? new List(); + var contentRatings = seriesResult.ContentRatings.Results ?? new List(); var ourRelease = contentRatings.FirstOrDefault(c => string.Equals(c.Iso_3166_1, preferredCountryCode, StringComparison.OrdinalIgnoreCase)); var usRelease = contentRatings.FirstOrDefault(c => string.Equals(c.Iso_3166_1, "US", StringComparison.OrdinalIgnoreCase)); @@ -297,254 +320,72 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV series.OfficialRating = minimumRelease.Rating; } - if (seriesInfo.Videos != null && seriesInfo.Videos.Results != null) + if (seriesResult.Videos?.Results != null) { - foreach (var video in seriesInfo.Videos.Results) + foreach (var video in seriesResult.Videos.Results) { - if ((video.Type.Equals("trailer", StringComparison.OrdinalIgnoreCase) - || video.Type.Equals("clip", StringComparison.OrdinalIgnoreCase)) - && video.Site.Equals("youtube", StringComparison.OrdinalIgnoreCase)) + if (TmdbUtils.IsTrailerType(video)) { series.AddTrailerUrl($"http://www.youtube.com/watch?v={video.Key}"); } } } - seriesResult.ResetPeople(); - var tmdbImageUrl = settings.images.GetImageUrl("original"); + return series; + } - if (seriesInfo.Credits != null) + private IEnumerable GetPersons(TvShow seriesResult) + { + if (seriesResult.Credits?.Cast != null) { - if (seriesInfo.Credits.Cast != null) + foreach (var actor in seriesResult.Credits.Cast.OrderBy(a => a.Order).Take(TmdbUtils.MaxCastMembers)) { - foreach (var actor in seriesInfo.Credits.Cast.OrderBy(a => a.Order)) + var personInfo = new PersonInfo { - var personInfo = new PersonInfo - { - Name = actor.Name.Trim(), - Role = actor.Character, - Type = PersonType.Actor, - SortOrder = actor.Order - }; - - if (!string.IsNullOrWhiteSpace(actor.Profile_Path)) - { - personInfo.ImageUrl = tmdbImageUrl + actor.Profile_Path; - } - - if (actor.Id > 0) - { - personInfo.SetProviderId(MetadataProvider.Tmdb, actor.Id.ToString(CultureInfo.InvariantCulture)); - } - - seriesResult.AddPerson(personInfo); - } - } - - if (seriesInfo.Credits.Crew != null) - { - var keepTypes = new[] - { - PersonType.Director, - PersonType.Writer, - PersonType.Producer + Name = actor.Name.Trim(), + Role = actor.Character, + Type = PersonType.Actor, + SortOrder = actor.Order, + ImageUrl = _tmdbClientManager.GetPosterUrl(actor.ProfilePath) }; - foreach (var person in seriesInfo.Credits.Crew) + if (actor.Id > 0) { - // Normalize this - var type = TmdbUtils.MapCrewToPersonType(person); - - if (!keepTypes.Contains(type, StringComparer.OrdinalIgnoreCase) - && !keepTypes.Contains(person.Job ?? string.Empty, StringComparer.OrdinalIgnoreCase)) - { - continue; - } - - seriesResult.AddPerson(new PersonInfo - { - Name = person.Name.Trim(), - Role = person.Job, - Type = type - }); + personInfo.SetProviderId(MetadataProvider.Tmdb, actor.Id.ToString(CultureInfo.InvariantCulture)); } - } - } - } - - internal static string GetSeriesDataPath(IApplicationPaths appPaths, string tmdbId) - { - var dataPath = GetSeriesDataPath(appPaths); - - return Path.Combine(dataPath, tmdbId); - } - - internal static string GetSeriesDataPath(IApplicationPaths appPaths) - { - var dataPath = Path.Combine(appPaths.CachePath, "tmdb-tv"); - - return dataPath; - } - - internal async Task DownloadSeriesInfo(string id, string preferredMetadataLanguage, CancellationToken cancellationToken) - { - SeriesResult mainResult = await FetchMainResult(id, preferredMetadataLanguage, cancellationToken).ConfigureAwait(false); - - if (mainResult == null) - { - return; - } - - var dataFilePath = GetDataFilePath(id, preferredMetadataLanguage); - - Directory.CreateDirectory(Path.GetDirectoryName(dataFilePath)); - - _jsonSerializer.SerializeToFile(mainResult, dataFilePath); - } - - internal async Task FetchMainResult(string id, string language, CancellationToken cancellationToken) - { - var url = string.Format(CultureInfo.InvariantCulture, GetTvInfo3, id, TmdbUtils.ApiKey); - - if (!string.IsNullOrEmpty(language)) - { - url += "&language=" + TmdbMovieProvider.NormalizeLanguage(language) - + "&include_image_language=" + TmdbMovieProvider.GetImageLanguagesParam(language); // Get images in english and with no language - } - - cancellationToken.ThrowIfCancellationRequested(); - - using var mainRequestMessage = new HttpRequestMessage(HttpMethod.Get, url); - foreach (var header in TmdbUtils.AcceptHeaders) - { - mainRequestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); - } - - using var mainResponse = await TmdbMovieProvider.Current.GetMovieDbResponse(mainRequestMessage, cancellationToken).ConfigureAwait(false); - await using var mainStream = await mainResponse.Content.ReadAsStreamAsync().ConfigureAwait(false); - var mainResult = await _jsonSerializer.DeserializeFromStreamAsync(mainStream).ConfigureAwait(false); - - if (!string.IsNullOrEmpty(language)) - { - mainResult.ResultLanguage = language; - } - - cancellationToken.ThrowIfCancellationRequested(); - - // If the language preference isn't english, then have the overview fallback to english if it's blank - if (mainResult != null && - string.IsNullOrEmpty(mainResult.Overview) && - !string.IsNullOrEmpty(language) && - !string.Equals(language, "en", StringComparison.OrdinalIgnoreCase)) - { - _logger.LogInformation("MovieDbSeriesProvider couldn't find meta for language {Language}. Trying English...", language); - - url = string.Format(CultureInfo.InvariantCulture, GetTvInfo3, id, TmdbUtils.ApiKey) + "&language=en"; - - if (!string.IsNullOrEmpty(language)) - { - // Get images in english and with no language - url += "&include_image_language=" + TmdbMovieProvider.GetImageLanguagesParam(language); - } - using var requestMessage = new HttpRequestMessage(HttpMethod.Get, url); - foreach (var header in TmdbUtils.AcceptHeaders) - { - mainRequestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); + yield return personInfo; } - - using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage, cancellationToken).ConfigureAwait(false); - await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); - var englishResult = await _jsonSerializer.DeserializeFromStreamAsync(stream).ConfigureAwait(false); - - mainResult.Overview = englishResult.Overview; - mainResult.ResultLanguage = "en"; - } - - return mainResult; - } - - internal Task EnsureSeriesInfo(string tmdbId, string language, CancellationToken cancellationToken) - { - if (string.IsNullOrEmpty(tmdbId)) - { - throw new ArgumentNullException(nameof(tmdbId)); } - var path = GetDataFilePath(tmdbId, language); - - var fileInfo = new FileInfo(path); - if (fileInfo.Exists) + if (seriesResult.Credits?.Crew != null) { - // If it's recent or automatic updates are enabled, don't re-download - if ((DateTime.UtcNow - fileInfo.LastWriteTimeUtc).TotalDays <= 2) + var keepTypes = new[] { - return Task.CompletedTask; - } - } - - return DownloadSeriesInfo(tmdbId, language, cancellationToken); - } - - internal string GetDataFilePath(string tmdbId, string preferredLanguage) - { - if (string.IsNullOrEmpty(tmdbId)) - { - throw new ArgumentNullException(nameof(tmdbId)); - } - - var path = GetSeriesDataPath(_configurationManager.ApplicationPaths, tmdbId); - - var filename = string.Format(CultureInfo.InvariantCulture, "series-{0}.json", preferredLanguage ?? string.Empty); - - return Path.Combine(path, filename); - } - - private async Task FindByExternalId(string id, string externalSource, CancellationToken cancellationToken) - { - var url = string.Format( - CultureInfo.InvariantCulture, - TmdbUtils.BaseTmdbApiUrl + @"3/find/{0}?api_key={1}&external_source={2}", - id, - TmdbUtils.ApiKey, - externalSource); - - using var requestMessage = new HttpRequestMessage(HttpMethod.Get, url); - foreach (var header in TmdbUtils.AcceptHeaders) - { - requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); - } - - using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage, cancellationToken).ConfigureAwait(false); - await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); - - var result = await _jsonSerializer.DeserializeFromStreamAsync(stream).ConfigureAwait(false); - - if (result != null && result.Tv_Results != null) - { - var tv = result.Tv_Results.FirstOrDefault(); + PersonType.Director, + PersonType.Writer, + PersonType.Producer + }; - if (tv != null) + foreach (var person in seriesResult.Credits.Crew) { - var tmdbSettings = await TmdbMovieProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); - var tmdbImageUrl = tmdbSettings.images.GetImageUrl("original"); + // Normalize this + var type = TmdbUtils.MapCrewToPersonType(person); - var remoteResult = new RemoteSearchResult + if (!keepTypes.Contains(type, StringComparer.OrdinalIgnoreCase) + && !keepTypes.Contains(person.Job ?? string.Empty, StringComparer.OrdinalIgnoreCase)) { - Name = tv.Name, - SearchProviderName = Name, - ImageUrl = string.IsNullOrWhiteSpace(tv.Poster_Path) - ? null - : tmdbImageUrl + tv.Poster_Path - }; - - remoteResult.SetProviderId(MetadataProvider.Tmdb, tv.Id.ToString(_usCulture)); + continue; + } - return remoteResult; + yield return new PersonInfo + { + Name = person.Name.Trim(), + Role = person.Job, + Type = type + }; } } - - return null; } public Task GetImageResponse(string url, CancellationToken cancellationToken) diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs b/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs new file mode 100644 index 000000000..537082300 --- /dev/null +++ b/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs @@ -0,0 +1,469 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Caching.Memory; +using TMDbLib.Client; +using TMDbLib.Objects.Collections; +using TMDbLib.Objects.Find; +using TMDbLib.Objects.General; +using TMDbLib.Objects.Movies; +using TMDbLib.Objects.People; +using TMDbLib.Objects.Search; +using TMDbLib.Objects.TvShows; + +namespace MediaBrowser.Providers.Plugins.Tmdb +{ + /// + /// Manager class for abstracting the TMDb API client library. + /// + public class TmdbClientManager + { + private const int CacheDurationInHours = 1; + + private readonly IMemoryCache _memoryCache; + private readonly TMDbClient _tmDbClient; + + /// + /// Initializes a new instance of the class. + /// + /// An instance of . + public TmdbClientManager(IMemoryCache memoryCache) + { + _memoryCache = memoryCache; + _tmDbClient = new TMDbClient(TmdbUtils.ApiKey); + // Not really interested in NotFoundException + _tmDbClient.ThrowApiExceptions = false; + } + + /// + /// Gets a movie from the TMDb API based on its TMDb id. + /// + /// The movie's TMDb id. + /// The movie's language. + /// A comma-separated list of image languages. + /// The cancellation token. + /// The TMDb movie or null if not found. + public async Task GetMovieAsync(int tmdbId, string language, string imageLanguages, CancellationToken cancellationToken) + { + var key = $"movie-{tmdbId.ToString(CultureInfo.InvariantCulture)}-{language}"; + if (_memoryCache.TryGetValue(key, out Movie movie)) + { + return movie; + } + + await EnsureClientConfigAsync().ConfigureAwait(false); + + // TODO include image language + movie = await _tmDbClient.GetMovieAsync( + tmdbId, + TmdbUtils.NormalizeLanguage(language), + MovieMethods.Credits | MovieMethods.Releases | MovieMethods.Images | MovieMethods.Keywords | MovieMethods.Videos, + cancellationToken).ConfigureAwait(false); + + if (movie != null) + { + _memoryCache.Set(key, movie, TimeSpan.FromHours(CacheDurationInHours)); + } + + return movie; + } + + /// + /// Gets a collection from the TMDb API based on its TMDb id. + /// + /// The collection's TMDb id. + /// The collection's language. + /// A comma-separated list of image languages. + /// The cancellation token. + /// The TMDb collection or null if not found. + public async Task GetCollectionAsync(int tmdbId, string language, string imageLanguages, CancellationToken cancellationToken) + { + var key = $"collection-{tmdbId.ToString(CultureInfo.InvariantCulture)}-{language}"; + if (_memoryCache.TryGetValue(key, out Collection collection)) + { + return collection; + } + + await EnsureClientConfigAsync().ConfigureAwait(false); + + // TODO include image language + collection = await _tmDbClient.GetCollectionAsync( + tmdbId, + TmdbUtils.NormalizeLanguage(language), + CollectionMethods.Images, + cancellationToken).ConfigureAwait(false); + + if (collection != null) + { + _memoryCache.Set(key, collection, TimeSpan.FromHours(CacheDurationInHours)); + } + + return collection; + } + + /// + /// Gets a tv show from the TMDb API based on its TMDb id. + /// + /// The tv show's TMDb id. + /// The tv show's language. + /// A comma-separated list of image languages. + /// The cancellation token. + /// The TMDb tv show information or null if not found. + public async Task GetSeriesAsync(int tmdbId, string language, string imageLanguages, CancellationToken cancellationToken) + { + var key = $"series-{tmdbId.ToString(CultureInfo.InvariantCulture)}-{language}"; + if (_memoryCache.TryGetValue(key, out TvShow series)) + { + return series; + } + + await EnsureClientConfigAsync().ConfigureAwait(false); + + // TODO include image language + series = await _tmDbClient.GetTvShowAsync( + tmdbId, + language: TmdbUtils.NormalizeLanguage(language), + extraMethods: TvShowMethods.Credits | TvShowMethods.Images | TvShowMethods.Keywords | TvShowMethods.ExternalIds | TvShowMethods.Videos | TvShowMethods.ContentRatings, + cancellationToken: cancellationToken).ConfigureAwait(false); + + if (series != null) + { + _memoryCache.Set(key, series, TimeSpan.FromHours(CacheDurationInHours)); + } + + return series; + } + + /// + /// Gets a tv season from the TMDb API based on the tv show's TMDb id. + /// + /// The tv season's TMDb id. + /// The season number. + /// The tv season's language. + /// A comma-separated list of image languages. + /// The cancellation token. + /// The TMDb tv season information or null if not found. + public async Task GetSeasonAsync(int tvShowId, int seasonNumber, string language, string imageLanguages, CancellationToken cancellationToken) + { + var key = $"season-{tvShowId.ToString(CultureInfo.InvariantCulture)}-s{seasonNumber.ToString(CultureInfo.InvariantCulture)}-{language}"; + if (_memoryCache.TryGetValue(key, out TvSeason season)) + { + return season; + } + + await EnsureClientConfigAsync().ConfigureAwait(false); + + // TODO include image language + season = await _tmDbClient.GetTvSeasonAsync( + tvShowId, + seasonNumber, + language: TmdbUtils.NormalizeLanguage(language), + extraMethods: TvSeasonMethods.Credits | TvSeasonMethods.Images | TvSeasonMethods.ExternalIds | TvSeasonMethods.Videos, + cancellationToken: cancellationToken).ConfigureAwait(false); + + if (season != null) + { + _memoryCache.Set(key, season, TimeSpan.FromHours(CacheDurationInHours)); + } + + return season; + } + + /// + /// Gets a movie from the TMDb API based on the tv show's TMDb id. + /// + /// The tv show's TMDb id. + /// The season number. + /// The episode number. + /// The episode's language. + /// A comma-separated list of image languages. + /// The cancellation token. + /// The TMDb tv episode information or null if not found. + public async Task GetEpisodeAsync(int tvShowId, int seasonNumber, int episodeNumber, string language, string imageLanguages, CancellationToken cancellationToken) + { + var key = $"episode-{tvShowId.ToString(CultureInfo.InvariantCulture)}-s{seasonNumber.ToString(CultureInfo.InvariantCulture)}e{episodeNumber.ToString(CultureInfo.InvariantCulture)}-{language}"; + if (_memoryCache.TryGetValue(key, out TvEpisode episode)) + { + return episode; + } + + await EnsureClientConfigAsync().ConfigureAwait(false); + + // TODO include image language + episode = await _tmDbClient.GetTvEpisodeAsync( + tvShowId, + seasonNumber, + episodeNumber, + language: TmdbUtils.NormalizeLanguage(language), + extraMethods: TvEpisodeMethods.Credits | TvEpisodeMethods.Images | TvEpisodeMethods.ExternalIds | TvEpisodeMethods.Videos, + cancellationToken: cancellationToken).ConfigureAwait(false); + + if (episode != null) + { + _memoryCache.Set(key, episode, TimeSpan.FromHours(CacheDurationInHours)); + } + + return episode; + } + + /// + /// Gets a person eg. cast or crew member from the TMDb API based on its TMDb id. + /// + /// The person's TMDb id. + /// The cancellation token. + /// The TMDb person information or null if not found. + public async Task GetPersonAsync(int personTmdbId, CancellationToken cancellationToken) + { + var key = $"person-{personTmdbId.ToString(CultureInfo.InvariantCulture)}"; + if (_memoryCache.TryGetValue(key, out Person person)) + { + return person; + } + + await EnsureClientConfigAsync().ConfigureAwait(false); + + person = await _tmDbClient.GetPersonAsync( + personTmdbId, + PersonMethods.TvCredits | PersonMethods.MovieCredits | PersonMethods.Images | PersonMethods.ExternalIds, + cancellationToken).ConfigureAwait(false); + + if (person != null) + { + _memoryCache.Set(key, person, TimeSpan.FromHours(CacheDurationInHours)); + } + + return person; + } + + /// + /// Gets an item from the TMDb API based on its id from an external service eg. IMDb id, TvDb id. + /// + /// The item's external id. + /// The source of the id eg. IMDb. + /// The item's language. + /// The cancellation token. + /// The TMDb item or null if not found. + public async Task FindByExternalIdAsync( + string externalId, + FindExternalSource source, + string language, + CancellationToken cancellationToken) + { + var key = $"find-{source.ToString()}-{externalId.ToString(CultureInfo.InvariantCulture)}-{language}"; + if (_memoryCache.TryGetValue(key, out FindContainer result)) + { + return result; + } + + await EnsureClientConfigAsync().ConfigureAwait(false); + + // TODO language + result = await _tmDbClient.FindAsync( + source, + externalId, + cancellationToken).ConfigureAwait(false); + + if (result != null) + { + _memoryCache.Set(key, result, TimeSpan.FromHours(CacheDurationInHours)); + } + + return result; + } + + /// + /// Searches for a tv show using the TMDb API based on its name. + /// + /// The name of the tv show. + /// The tv show's language. + /// The cancellation token. + /// The TMDb tv show information. + public async Task> SearchSeriesAsync(string name, string language, CancellationToken cancellationToken) + { + var key = $"searchseries-{name}-{language}"; + if (_memoryCache.TryGetValue(key, out SearchContainer series)) + { + return series.Results; + } + + await EnsureClientConfigAsync().ConfigureAwait(false); + + var searchResults = await _tmDbClient + .SearchTvShowAsync(name, TmdbUtils.NormalizeLanguage(language), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + if (searchResults.Results.Count > 0) + { + _memoryCache.Set(key, searchResults, TimeSpan.FromHours(CacheDurationInHours)); + } + + return searchResults.Results; + } + + /// + /// Searches for a person based on their name using the TMDb API. + /// + /// The name of the person. + /// The cancellation token. + /// The TMDb person information. + public async Task> SearchPersonAsync(string name, CancellationToken cancellationToken) + { + var key = $"searchperson-{name}"; + if (_memoryCache.TryGetValue(key, out SearchContainer person)) + { + return person.Results; + } + + await EnsureClientConfigAsync().ConfigureAwait(false); + + var searchResults = await _tmDbClient + .SearchPersonAsync(name, cancellationToken: cancellationToken) + .ConfigureAwait(false); + + if (searchResults.Results.Count > 0) + { + _memoryCache.Set(key, searchResults, TimeSpan.FromHours(CacheDurationInHours)); + } + + return searchResults.Results; + } + + /// + /// Searches for a movie based on its name using the TMDb API. + /// + /// The name of the movie. + /// The movie's language. + /// The cancellation token. + /// The TMDb movie information. + public Task> SearchMovieAsync(string name, string language, CancellationToken cancellationToken) + { + return SearchMovieAsync(name, 0, language, cancellationToken); + } + + /// + /// Searches for a movie based on its name using the TMDb API. + /// + /// The name of the movie. + /// The release year of the movie. + /// The movie's language. + /// The cancellation token. + /// The TMDb movie information. + public async Task> SearchMovieAsync(string name, int year, string language, CancellationToken cancellationToken) + { + var key = $"moviesearch-{name}-{year.ToString(CultureInfo.InvariantCulture)}-{language}"; + if (_memoryCache.TryGetValue(key, out SearchContainer movies)) + { + return movies.Results; + } + + await EnsureClientConfigAsync().ConfigureAwait(false); + + var searchResults = await _tmDbClient + .SearchMovieAsync(name, TmdbUtils.NormalizeLanguage(language), year: year, cancellationToken: cancellationToken) + .ConfigureAwait(false); + + if (searchResults.Results.Count > 0) + { + _memoryCache.Set(key, searchResults, TimeSpan.FromHours(CacheDurationInHours)); + } + + return searchResults.Results; + } + + /// + /// Searches for a collection based on its name using the TMDb API. + /// + /// The name of the collection. + /// The collection's language. + /// The cancellation token. + /// The TMDb collection information. + public async Task> SearchCollectionAsync(string name, string language, CancellationToken cancellationToken) + { + var key = $"collectionsearch-{name}-{language}"; + if (_memoryCache.TryGetValue(key, out SearchContainer collections)) + { + return collections.Results; + } + + await EnsureClientConfigAsync().ConfigureAwait(false); + + var searchResults = await _tmDbClient + .SearchCollectionAsync(name, TmdbUtils.NormalizeLanguage(language), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + if (searchResults.Results.Count > 0) + { + _memoryCache.Set(key, searchResults, TimeSpan.FromHours(CacheDurationInHours)); + } + + return searchResults.Results; + } + + /// + /// Gets the absolute URL of the poster. + /// + /// The relative URL of the poster. + /// The absolute URL. + public string GetPosterUrl(string posterPath) + { + if (string.IsNullOrEmpty(posterPath)) + { + return null; + } + + return _tmDbClient.GetImageUrl(_tmDbClient.Config.Images.PosterSizes[^1], posterPath).ToString(); + } + + /// + /// Gets the absolute URL of the backdrop image. + /// + /// The relative URL of the backdrop image. + /// The absolute URL. + public string GetBackdropUrl(string posterPath) + { + if (string.IsNullOrEmpty(posterPath)) + { + return null; + } + + return _tmDbClient.GetImageUrl(_tmDbClient.Config.Images.BackdropSizes[^1], posterPath).ToString(); + } + + /// + /// Gets the absolute URL of the profile image. + /// + /// The relative URL of the profile image. + /// The absolute URL. + public string GetProfileUrl(string actorProfilePath) + { + if (string.IsNullOrEmpty(actorProfilePath)) + { + return null; + } + + return _tmDbClient.GetImageUrl(_tmDbClient.Config.Images.ProfileSizes[^1], actorProfilePath).ToString(); + } + + /// + /// Gets the absolute URL of the still image. + /// + /// The relative URL of the still image. + /// The absolute URL. + public string GetStillUrl(string filePath) + { + if (string.IsNullOrEmpty(filePath)) + { + return null; + } + + return _tmDbClient.GetImageUrl(_tmDbClient.Config.Images.StillSizes[^1], filePath).ToString(); + } + + private Task EnsureClientConfigAsync() + { + return !_tmDbClient.HasConfig ? _tmDbClient.GetConfigAsync() : Task.CompletedTask; + } + } +} diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs b/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs index 1415d6976..1fb85e1a3 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs @@ -1,7 +1,10 @@ +#nullable enable + using System; +using System.Collections.Generic; using System.Net.Mime; using MediaBrowser.Model.Entities; -using MediaBrowser.Providers.Plugins.Tmdb.Models.General; +using TMDbLib.Objects.General; namespace MediaBrowser.Providers.Plugins.Tmdb { @@ -30,11 +33,26 @@ namespace MediaBrowser.Providers.Plugins.Tmdb /// public const string ApiKey = "4219e299c89411838049ab0dab19ebd5"; + /// + /// Maximum number of cast members to pull. + /// + public const int MaxCastMembers = 15; + /// /// Value of the Accept header for requests to the provider. /// public static readonly string[] AcceptHeaders = { MediaTypeNames.Application.Json, "image/*" }; + /// + /// The crew types to keep. + /// + public static readonly string[] WantedCrewTypes = + { + PersonType.Director, + PersonType.Writer, + PersonType.Producer + }; + /// /// Maps the TMDB provided roles for crew members to Jellyfin roles. /// @@ -61,5 +79,75 @@ namespace MediaBrowser.Providers.Plugins.Tmdb return null; } + + public static bool IsTrailerType(Video video) + { + return video.Site.Equals("youtube", StringComparison.OrdinalIgnoreCase) + && (!video.Type.Equals("trailer", StringComparison.OrdinalIgnoreCase) + || !video.Type.Equals("teaser", StringComparison.OrdinalIgnoreCase)); + } + + public static string GetImageLanguagesParam(string preferredLanguage) + { + var languages = new List(); + + if (!string.IsNullOrEmpty(preferredLanguage)) + { + preferredLanguage = NormalizeLanguage(preferredLanguage); + + languages.Add(preferredLanguage); + + if (preferredLanguage.Length == 5) // like en-US + { + // Currenty, TMDB supports 2-letter language codes only + // They are planning to change this in the future, thus we're + // supplying both codes if we're having a 5-letter code. + languages.Add(preferredLanguage.Substring(0, 2)); + } + } + + languages.Add("null"); + + if (!string.Equals(preferredLanguage, "en", StringComparison.OrdinalIgnoreCase)) + { + languages.Add("en"); + } + + return string.Join(',', languages); + } + + public static string NormalizeLanguage(string language) + { + if (string.IsNullOrEmpty(language)) + { + return language; + } + + // They require this to be uppercase + // Everything after the hyphen must be written in uppercase due to a way TMDB wrote their api. + // See here: https://www.themoviedb.org/talk/5119221d760ee36c642af4ad?page=3#56e372a0c3a3685a9e0019ab + var parts = language.Split('-'); + + if (parts.Length == 2) + { + language = parts[0] + "-" + parts[1].ToUpperInvariant(); + } + + return language; + } + + public static string AdjustImageLanguage(string imageLanguage, string requestLanguage) + { + if (!string.IsNullOrEmpty(imageLanguage) + && !string.IsNullOrEmpty(requestLanguage) + && requestLanguage.Length > 2 + && imageLanguage.Length == 2 + && requestLanguage.StartsWith(imageLanguage, StringComparison.OrdinalIgnoreCase)) + { + return requestLanguage; + } + + return imageLanguage; + } } } diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Trailers/TmdbTrailerProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Trailers/TmdbTrailerProvider.cs deleted file mode 100644 index 613dc17e3..000000000 --- a/MediaBrowser.Providers/Plugins/Tmdb/Trailers/TmdbTrailerProvider.cs +++ /dev/null @@ -1,43 +0,0 @@ -#pragma warning disable CS1591 - -using System.Collections.Generic; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Providers; -using MediaBrowser.Providers.Plugins.Tmdb.Movies; - -namespace MediaBrowser.Providers.Plugins.Tmdb.Trailers -{ - public class TmdbTrailerProvider : IHasOrder, IRemoteMetadataProvider - { - private readonly IHttpClientFactory _httpClientFactory; - - public TmdbTrailerProvider(IHttpClientFactory httpClientFactory) - { - _httpClientFactory = httpClientFactory; - } - - public string Name => TmdbMovieProvider.Current.Name; - - public int Order => 0; - - public Task> GetSearchResults(TrailerInfo searchInfo, CancellationToken cancellationToken) - { - return TmdbMovieProvider.Current.GetMovieSearchResults(searchInfo, cancellationToken); - } - - public Task> GetMetadata(TrailerInfo info, CancellationToken cancellationToken) - { - return TmdbMovieProvider.Current.GetItemMetadata(info, cancellationToken); - } - - public Task GetImageResponse(string url, CancellationToken cancellationToken) - { - return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken); - } - } -} -- cgit v1.2.3 From e96d41d5ef3b438437853e155578bf2dece4ad51 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 4 Oct 2020 16:45:00 +0200 Subject: Fix build --- Emby.Server.Implementations/SyncPlay/SyncPlayController.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/SyncPlay/SyncPlayController.cs b/Emby.Server.Implementations/SyncPlay/SyncPlayController.cs index 80b977731..538479512 100644 --- a/Emby.Server.Implementations/SyncPlay/SyncPlayController.cs +++ b/Emby.Server.Implementations/SyncPlay/SyncPlayController.cs @@ -301,8 +301,7 @@ namespace Emby.Server.Implementations.SyncPlay if (_group.IsPaused) { // Pick a suitable time that accounts for latency - var delay = _group.GetHighestPing() * 2; - delay = delay < _group.DefaultPing ? _group.DefaultPing : delay; + var delay = Math.Max(_group.GetHighestPing() * 2, GroupInfo.DefaultPing); // Unpause group and set starting point in future // Clients will start playback at LastActivity (datetime) from PositionTicks (playback position) @@ -452,8 +451,7 @@ namespace Emby.Server.Implementations.SyncPlay else { // Client, that was buffering, resumed playback but did not update others in time - delay = _group.GetHighestPing() * 2; - delay = delay < _group.DefaultPing ? _group.DefaultPing : delay; + delay = Math.Max(_group.GetHighestPing() * 2, GroupInfo.DefaultPing); _group.LastActivity = currentTime.AddMilliseconds( delay); @@ -497,7 +495,7 @@ namespace Emby.Server.Implementations.SyncPlay private void HandlePingUpdateRequest(SessionInfo session, PlaybackRequest request) { // Collected pings are used to account for network latency when unpausing playback - _group.UpdatePing(session, request.Ping ?? _group.DefaultPing); + _group.UpdatePing(session, request.Ping ?? GroupInfo.DefaultPing); } /// -- cgit v1.2.3 From 5a7dda337f4cdda0d0c61adef3d2b13772e708d0 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sun, 4 Oct 2020 11:50:00 -0400 Subject: Add active session tracking Adds a flag for a maximum number of user sessions, as well as an authentication check to ensure that the user is not above this level. --- Emby.Server.Implementations/Session/SessionManager.cs | 13 +++++++++++++ Jellyfin.Data/Entities/User.cs | 5 +++++ Jellyfin.Server.Implementations/Users/UserManager.cs | 2 ++ MediaBrowser.Model/Users/UserPolicy.cs | 6 ++++++ 4 files changed, 26 insertions(+) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index e42d47853..5903d395a 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -1484,6 +1484,19 @@ namespace Emby.Server.Implementations.Session throw new SecurityException("User is not allowed access from this device."); } + var sessionsCount = Sessions.Where(i => string.Equals(i.UserId, user.Id)).ToList().Count; + int maxActiveSessions = user.MaxActiveSessions; + _logger.LogDebug("Current/Max sessions for user {User}: {Sessions}/{Max}", user.Username, sessionsCount, maxActiveSessions); + if (maxActiveSessions >= 0 && sessionsCount >= maxActiveSessions) + { + throw new SecurityException( + "User {User} is at their maximum number of sessions ({Sessions}/{Max}).", + user.Username, + sessionsCount, + maxActiveSessions + ) + } + var token = GetAuthorizationToken(user, request.DeviceId, request.App, request.AppVersion, request.DeviceName); var session = LogSessionActivity( diff --git a/Jellyfin.Data/Entities/User.cs b/Jellyfin.Data/Entities/User.cs index f7ab57a1b..daa4de0b5 100644 --- a/Jellyfin.Data/Entities/User.cs +++ b/Jellyfin.Data/Entities/User.cs @@ -188,6 +188,11 @@ namespace Jellyfin.Data.Entities /// public int? LoginAttemptsBeforeLockout { get; set; } + /// + /// Gets or sets the maximum number of active sessions the user can have at once. + /// + public int? MaxActiveSessions { get; set; } + /// /// Gets or sets the subtitle mode. /// diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 8f04baa08..43698efb7 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -379,6 +379,7 @@ namespace Jellyfin.Server.Implementations.Users PasswordResetProviderId = user.PasswordResetProviderId, InvalidLoginAttemptCount = user.InvalidLoginAttemptCount, LoginAttemptsBeforeLockout = user.LoginAttemptsBeforeLockout ?? -1, + MaxActiveSessions = user.MaxActiveSessions ?? -1, IsAdministrator = user.HasPermission(PermissionKind.IsAdministrator), IsHidden = user.HasPermission(PermissionKind.IsHidden), IsDisabled = user.HasPermission(PermissionKind.IsDisabled), @@ -701,6 +702,7 @@ namespace Jellyfin.Server.Implementations.Users user.PasswordResetProviderId = policy.PasswordResetProviderId; user.InvalidLoginAttemptCount = policy.InvalidLoginAttemptCount; user.LoginAttemptsBeforeLockout = maxLoginAttempts; + user.MaxActiveSessions = policy.MaxActiveSessions; user.SyncPlayAccess = policy.SyncPlayAccess; user.SetPermission(PermissionKind.IsAdministrator, policy.IsAdministrator); user.SetPermission(PermissionKind.IsHidden, policy.IsHidden); diff --git a/MediaBrowser.Model/Users/UserPolicy.cs b/MediaBrowser.Model/Users/UserPolicy.cs index a1f01f7e8..53dcb6bbd 100644 --- a/MediaBrowser.Model/Users/UserPolicy.cs +++ b/MediaBrowser.Model/Users/UserPolicy.cs @@ -92,6 +92,10 @@ namespace MediaBrowser.Model.Users public int LoginAttemptsBeforeLockout { get; set; } + public int ActiveSessionCount { get; set; } + + public int MaxActiveSessions { get; set; } + public bool EnablePublicSharing { get; set; } public Guid[] BlockedMediaFolders { get; set; } @@ -144,6 +148,8 @@ namespace MediaBrowser.Model.Users LoginAttemptsBeforeLockout = -1; + MaxActiveSessions = -1; + EnableAllChannels = true; EnabledChannels = Array.Empty(); -- cgit v1.2.3 From 975fca51583de23bafb6720104a2a6857ab29b18 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sun, 4 Oct 2020 13:19:56 -0400 Subject: Fix syntax error --- Emby.Server.Implementations/Session/SessionManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index 5903d395a..ac59fe386 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -1494,7 +1494,7 @@ namespace Emby.Server.Implementations.Session user.Username, sessionsCount, maxActiveSessions - ) + ); } var token = GetAuthorizationToken(user, request.DeviceId, request.App, request.AppVersion, request.DeviceName); -- cgit v1.2.3 From b130af1b0c869fa6a35be4a53510e0f0fa4ad0d3 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sun, 4 Oct 2020 13:29:18 -0400 Subject: Remove variables from SecurityException --- Emby.Server.Implementations/Session/SessionManager.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index ac59fe386..46fa29919 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -1489,12 +1489,7 @@ namespace Emby.Server.Implementations.Session _logger.LogDebug("Current/Max sessions for user {User}: {Sessions}/{Max}", user.Username, sessionsCount, maxActiveSessions); if (maxActiveSessions >= 0 && sessionsCount >= maxActiveSessions) { - throw new SecurityException( - "User {User} is at their maximum number of sessions ({Sessions}/{Max}).", - user.Username, - sessionsCount, - maxActiveSessions - ); + throw new SecurityException("User is at their maximum number of sessions."); } var token = GetAuthorizationToken(user, request.DeviceId, request.App, request.AppVersion, request.DeviceName); -- cgit v1.2.3 From 6e1d1eed23442976ac0d9b2a2af44b7a6804ea09 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sun, 4 Oct 2020 13:30:21 -0400 Subject: Make log entry informational --- Emby.Server.Implementations/Session/SessionManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index 46fa29919..cd40bda55 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -1486,7 +1486,7 @@ namespace Emby.Server.Implementations.Session var sessionsCount = Sessions.Where(i => string.Equals(i.UserId, user.Id)).ToList().Count; int maxActiveSessions = user.MaxActiveSessions; - _logger.LogDebug("Current/Max sessions for user {User}: {Sessions}/{Max}", user.Username, sessionsCount, maxActiveSessions); + _logger.LogInformation("Current/Max sessions for user {User}: {Sessions}/{Max}", user.Username, sessionsCount, maxActiveSessions); if (maxActiveSessions >= 0 && sessionsCount >= maxActiveSessions) { throw new SecurityException("User is at their maximum number of sessions."); -- cgit v1.2.3 From d0ec6872f3418a3bc6bacff6ba068526f45fc167 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sun, 4 Oct 2020 14:06:20 -0400 Subject: Increase count check to 1 There's another way to disable a user anyways. --- Emby.Server.Implementations/Session/SessionManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index cd40bda55..90363e560 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -1487,7 +1487,7 @@ namespace Emby.Server.Implementations.Session var sessionsCount = Sessions.Where(i => string.Equals(i.UserId, user.Id)).ToList().Count; int maxActiveSessions = user.MaxActiveSessions; _logger.LogInformation("Current/Max sessions for user {User}: {Sessions}/{Max}", user.Username, sessionsCount, maxActiveSessions); - if (maxActiveSessions >= 0 && sessionsCount >= maxActiveSessions) + if (maxActiveSessions >= 1 && sessionsCount >= maxActiveSessions) { throw new SecurityException("User is at their maximum number of sessions."); } -- cgit v1.2.3 From 8dfa2015d0e804227fe9db09caf21315da7495e4 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sun, 4 Oct 2020 14:14:42 -0400 Subject: Make the count an int for cleanliness --- Emby.Server.Implementations/Session/SessionManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index 90363e560..04becff49 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -1484,7 +1484,7 @@ namespace Emby.Server.Implementations.Session throw new SecurityException("User is not allowed access from this device."); } - var sessionsCount = Sessions.Where(i => string.Equals(i.UserId, user.Id)).ToList().Count; + int sessionsCount = Sessions.Where(i => string.Equals(i.UserId, user.Id)).ToList().Count; int maxActiveSessions = user.MaxActiveSessions; _logger.LogInformation("Current/Max sessions for user {User}: {Sessions}/{Max}", user.Username, sessionsCount, maxActiveSessions); if (maxActiveSessions >= 1 && sessionsCount >= maxActiveSessions) -- cgit v1.2.3 From 5aa519fd399d8b09a360f6448acc992225f8c219 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sun, 4 Oct 2020 15:46:57 -0400 Subject: Implement better count method --- Emby.Server.Implementations/Session/SessionManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index 04becff49..d283ee8d8 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -1484,7 +1484,7 @@ namespace Emby.Server.Implementations.Session throw new SecurityException("User is not allowed access from this device."); } - int sessionsCount = Sessions.Where(i => string.Equals(i.UserId, user.Id)).ToList().Count; + int sessionsCount = Sessions.Count(i => string.Equals(i.UserId, user.Id, StringComparison.OrdinalIgnoreCase)); int maxActiveSessions = user.MaxActiveSessions; _logger.LogInformation("Current/Max sessions for user {User}: {Sessions}/{Max}", user.Username, sessionsCount, maxActiveSessions); if (maxActiveSessions >= 1 && sessionsCount >= maxActiveSessions) -- cgit v1.2.3 From df177b47524c0f7c5df21f32ebeaba5576d3f8a4 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sun, 4 Oct 2020 15:52:39 -0400 Subject: Revert "Implement better count method" This reverts commit 5aa519fd399d8b09a360f6448acc992225f8c219. --- Emby.Server.Implementations/Session/SessionManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index d283ee8d8..04becff49 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -1484,7 +1484,7 @@ namespace Emby.Server.Implementations.Session throw new SecurityException("User is not allowed access from this device."); } - int sessionsCount = Sessions.Count(i => string.Equals(i.UserId, user.Id, StringComparison.OrdinalIgnoreCase)); + int sessionsCount = Sessions.Where(i => string.Equals(i.UserId, user.Id)).ToList().Count; int maxActiveSessions = user.MaxActiveSessions; _logger.LogInformation("Current/Max sessions for user {User}: {Sessions}/{Max}", user.Username, sessionsCount, maxActiveSessions); if (maxActiveSessions >= 1 && sessionsCount >= maxActiveSessions) -- cgit v1.2.3 From 67e89dd5255a8ae38e0f0f489595d290e718b7a1 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sun, 4 Oct 2020 16:57:48 -0400 Subject: Use nicer count comparitor --- Emby.Server.Implementations/Session/SessionManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index 04becff49..fa4caeb18 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -1484,7 +1484,7 @@ namespace Emby.Server.Implementations.Session throw new SecurityException("User is not allowed access from this device."); } - int sessionsCount = Sessions.Where(i => string.Equals(i.UserId, user.Id)).ToList().Count; + int sessionsCount = Sessions.Count(i => i.UserId.Equals(user.Id)); int maxActiveSessions = user.MaxActiveSessions; _logger.LogInformation("Current/Max sessions for user {User}: {Sessions}/{Max}", user.Username, sessionsCount, maxActiveSessions); if (maxActiveSessions >= 1 && sessionsCount >= maxActiveSessions) -- cgit v1.2.3 From e77040a4fba93c0ca65650ce02998e9a35f28c40 Mon Sep 17 00:00:00 2001 From: Anthony Lavado Date: Mon, 5 Oct 2020 00:15:25 -0400 Subject: Fix Transcode Cleanup Schedule Sets the default time to midnight, and confirms to the same task scheduling commands that other tasks use. --- .../ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs index 691408167..dc6efcaf5 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs @@ -41,7 +41,13 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks /// Creates the triggers that define when the task will run. /// /// IEnumerable{BaseTaskTrigger}. - public IEnumerable GetDefaultTriggers() => new List(); + public IEnumerable GetDefaultTriggers() + { + return new[] + { + new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerDaily, TimeOfDayTicks = TimeSpan.FromHours(0).Ticks} + }; + } /// /// Returns the task to be executed. -- cgit v1.2.3 From 375f849b3e84a6f92e1928f7adcdb6a8a4115d25 Mon Sep 17 00:00:00 2001 From: Anthony Lavado Date: Mon, 5 Oct 2020 02:14:43 -0400 Subject: Simply trigger Co-authored-by: Claus Vium --- .../ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs index dc6efcaf5..2a40f8b1e 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs @@ -45,7 +45,11 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks { return new[] { - new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerDaily, TimeOfDayTicks = TimeSpan.FromHours(0).Ticks} + new TaskTriggerInfo + { + Type = TaskTriggerInfo.TriggerDaily, + TimeOfDayTicks = 0 + } }; } -- cgit v1.2.3 From 959d906c859a4a7c611907d500498a53ba92dc15 Mon Sep 17 00:00:00 2001 From: Anthony Lavado Date: Mon, 5 Oct 2020 02:25:09 -0400 Subject: Just enable instead --- .../ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs index 2a40f8b1e..26ef19354 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteTranscodeFileTask.cs @@ -41,17 +41,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks /// Creates the triggers that define when the task will run. /// /// IEnumerable{BaseTaskTrigger}. - public IEnumerable GetDefaultTriggers() - { - return new[] - { - new TaskTriggerInfo - { - Type = TaskTriggerInfo.TriggerDaily, - TimeOfDayTicks = 0 - } - }; - } + public IEnumerable GetDefaultTriggers() => new List(); /// /// Returns the task to be executed. @@ -158,7 +148,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks public bool IsHidden => false; /// - public bool IsEnabled => false; + public bool IsEnabled => true; /// public bool IsLogged => true; -- cgit v1.2.3 From 3d69bcd19090a3e467594375db3ee25f39e3def9 Mon Sep 17 00:00:00 2001 From: cvium Date: Tue, 6 Oct 2020 08:14:56 +0200 Subject: Fix playbackstart not triggering in the new eventmanager --- Emby.Server.Implementations/Session/SessionManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index b1ab20da2..607b322f2 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -666,7 +666,7 @@ namespace Emby.Server.Implementations.Session } } - var eventArgs = new PlaybackProgressEventArgs + var eventArgs = new PlaybackStartEventArgs { Item = libraryItem, Users = users, -- cgit v1.2.3 From 38cb8fee8a91c96f37199c64c7ef9414f7466112 Mon Sep 17 00:00:00 2001 From: cvium Date: Tue, 6 Oct 2020 14:44:07 +0200 Subject: Fix IWebSocketListener service registration --- .../HttpServer/WebSocketManager.cs | 17 +++++------------ Jellyfin.Server/CoreAppHost.cs | 11 +++++++++++ Jellyfin.Server/Program.cs | 2 +- MediaBrowser.Controller/Net/IWebSocketManager.cs | 6 ------ 4 files changed, 17 insertions(+), 19 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/HttpServer/WebSocketManager.cs b/Emby.Server.Implementations/HttpServer/WebSocketManager.cs index 89c1b7ea0..71ece80a7 100644 --- a/Emby.Server.Implementations/HttpServer/WebSocketManager.cs +++ b/Emby.Server.Implementations/HttpServer/WebSocketManager.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Net.WebSockets; using System.Threading.Tasks; using Jellyfin.Data.Events; @@ -14,16 +13,18 @@ namespace Emby.Server.Implementations.HttpServer { public class WebSocketManager : IWebSocketManager { + private readonly Lazy> _webSocketListeners; private readonly ILogger _logger; private readonly ILoggerFactory _loggerFactory; - private IWebSocketListener[] _webSocketListeners = Array.Empty(); private bool _disposed = false; public WebSocketManager( + Lazy> webSocketListeners, ILogger logger, ILoggerFactory loggerFactory) { + _webSocketListeners = webSocketListeners; _logger = logger; _loggerFactory = loggerFactory; } @@ -68,15 +69,6 @@ namespace Emby.Server.Implementations.HttpServer } } - /// - /// Adds the rest handlers. - /// - /// The web socket listeners. - public void Init(IEnumerable listeners) - { - _webSocketListeners = listeners.ToArray(); - } - /// /// Processes the web socket message received. /// @@ -90,7 +82,8 @@ namespace Emby.Server.Implementations.HttpServer IEnumerable GetTasks() { - foreach (var x in _webSocketListeners) + var listeners = _webSocketListeners.Value; + foreach (var x in listeners) { yield return x.ProcessMessageAsync(result); } diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs index 8d569a779..c44736447 100644 --- a/Jellyfin.Server/CoreAppHost.cs +++ b/Jellyfin.Server/CoreAppHost.cs @@ -4,6 +4,8 @@ using System.IO; using System.Reflection; using Emby.Drawing; using Emby.Server.Implementations; +using Emby.Server.Implementations.Session; +using Jellyfin.Api.WebSocketListeners; using Jellyfin.Drawing.Skia; using Jellyfin.Server.Implementations; using Jellyfin.Server.Implementations.Activity; @@ -14,6 +16,7 @@ using MediaBrowser.Controller; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Events; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Net; using MediaBrowser.Model.Activity; using MediaBrowser.Model.IO; using Microsoft.EntityFrameworkCore; @@ -80,6 +83,14 @@ namespace Jellyfin.Server ServiceCollection.AddSingleton(); ServiceCollection.AddSingleton(); + ServiceCollection.AddScoped(); + ServiceCollection.AddScoped(); + ServiceCollection.AddScoped(); + ServiceCollection.AddScoped(); + + // TODO fix circular dependency on IWebSocketManager + ServiceCollection.AddScoped(serviceProvider => new Lazy>(serviceProvider.GetRequiredService>)); + base.RegisterServices(); } diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index c933d679f..5573c0439 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -378,7 +378,7 @@ namespace Jellyfin.Server .ConfigureServices(services => { // Merge the external ServiceCollection into ASP.NET DI - services.TryAdd(serviceCollection); + services.Add(serviceCollection); }) .UseStartup(); } diff --git a/MediaBrowser.Controller/Net/IWebSocketManager.cs b/MediaBrowser.Controller/Net/IWebSocketManager.cs index e9f00ae88..ce74173e7 100644 --- a/MediaBrowser.Controller/Net/IWebSocketManager.cs +++ b/MediaBrowser.Controller/Net/IWebSocketManager.cs @@ -16,12 +16,6 @@ namespace MediaBrowser.Controller.Net /// event EventHandler> WebSocketConnected; - /// - /// Inits this instance. - /// - /// The websocket listeners. - void Init(IEnumerable listeners); - /// /// The HTTP request handler. /// -- cgit v1.2.3 From 137baab0ac608f96cac9649de7860b3bbdf2b21c Mon Sep 17 00:00:00 2001 From: cvium Date: Tue, 6 Oct 2020 20:19:36 +0200 Subject: Remove websocketmanager from ApplicationHost --- Emby.Server.Implementations/ApplicationHost.cs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 0c8b0339b..ee0af1025 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -128,7 +128,6 @@ namespace Emby.Server.Implementations private IMediaEncoder _mediaEncoder; private ISessionManager _sessionManager; private IHttpClientFactory _httpClientFactory; - private IWebSocketManager _webSocketManager; private string[] _urlPrefixes; @@ -259,8 +258,8 @@ namespace Emby.Server.Implementations IServiceCollection serviceCollection) { _xmlSerializer = new MyXmlSerializer(); - _jsonSerializer = new JsonSerializer(); - + _jsonSerializer = new JsonSerializer(); + ServiceCollection = serviceCollection; _networkManager = networkManager; @@ -667,7 +666,6 @@ namespace Emby.Server.Implementations _mediaEncoder = Resolve(); _sessionManager = Resolve(); _httpClientFactory = Resolve(); - _webSocketManager = Resolve(); ((AuthenticationRepository)Resolve()).Initialize(); @@ -788,7 +786,6 @@ namespace Emby.Server.Implementations .ToArray(); _urlPrefixes = GetUrlPrefixes().ToArray(); - _webSocketManager.Init(GetExports()); Resolve().AddParts( GetExports(), @@ -1090,7 +1087,7 @@ namespace Emby.Server.Implementations { // No metafile, so lets see if the folder is versioned. metafile = dir.Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries)[^1]; - + int versionIndex = dir.LastIndexOf('_'); if (versionIndex != -1 && Version.TryParse(dir.Substring(versionIndex + 1), out Version ver)) { @@ -1099,9 +1096,9 @@ namespace Emby.Server.Implementations } else { - // Un-versioned folder - Add it under the path name and version 0.0.0.1. + // Un-versioned folder - Add it under the path name and version 0.0.0.1. versions.Add((new Version(0, 0, 0, 1), metafile, dir)); - } + } } } catch -- cgit v1.2.3 From 67722a2034d9ab4b3d6df82f11c2c12a5bfedf38 Mon Sep 17 00:00:00 2001 From: shakesac Date: Fri, 9 Oct 2020 23:05:24 +0000 Subject: Translated using Weblate (Portuguese (Portugal)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/pt_PT/ --- Emby.Server.Implementations/Localization/Core/pt-PT.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/pt-PT.json b/Emby.Server.Implementations/Localization/Core/pt-PT.json index c1fb65743..a7c17ecb6 100644 --- a/Emby.Server.Implementations/Localization/Core/pt-PT.json +++ b/Emby.Server.Implementations/Localization/Core/pt-PT.json @@ -26,7 +26,7 @@ "HeaderLiveTV": "TV em Direto", "HeaderNextUp": "A Seguir", "HeaderRecordingGroups": "Grupos de Gravação", - "HomeVideos": "Videos caseiros", + "HomeVideos": "Vídeos Caseiros", "Inherit": "Herdar", "ItemAddedWithName": "{0} foi adicionado à biblioteca", "ItemRemovedWithName": "{0} foi removido da biblioteca", -- cgit v1.2.3 From 6f9beef7323cf133af481d003c7db7a7c6abc6dd Mon Sep 17 00:00:00 2001 From: Jan-Pieter Baert Date: Mon, 12 Oct 2020 19:22:33 +0200 Subject: Fix SA1012 and SA1013 warnings --- .../Data/SqliteItemRepository.cs | 2 +- .../LiveTv/RefreshChannelsScheduledTask.cs | 2 +- .../ScheduledTasks/Tasks/DeleteCacheFileTask.cs | 2 +- .../ScheduledTasks/Tasks/DeleteLogFileTask.cs | 2 +- .../MediaEncoding/EncodingHelper.cs | 18 +++++++++--------- .../MediaInfo/SubtitleScheduledTask.cs | 2 +- 6 files changed, 14 insertions(+), 14 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index d09f84e17..86a9908be 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -5357,7 +5357,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type itemCountColumns = new Dictionary() { - { "itemTypes", "(" + itemCountColumnQuery + ") as itemTypes"} + { "itemTypes", "(" + itemCountColumnQuery + ") as itemTypes" } }; } diff --git a/Emby.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs b/Emby.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs index f1b61f7c7..582b64923 100644 --- a/Emby.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs +++ b/Emby.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs @@ -43,7 +43,7 @@ namespace Emby.Server.Implementations.LiveTv return new[] { // Every so often - new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks} + new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks } }; } diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs index 5adcefc1f..692d1667d 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs @@ -71,7 +71,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks return new[] { // Every so often - new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks} + new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks } }; } diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs index 54e18eaea..184d155d4 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs @@ -65,7 +65,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks { return new[] { - new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks} + new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks } }; } diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index c5529ad5b..906afa7f6 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -70,15 +70,15 @@ namespace MediaBrowser.Controller.MediaEncoding var codecMap = new Dictionary(StringComparer.OrdinalIgnoreCase) { - {"qsv", hwEncoder + "_qsv"}, - {hwEncoder + "_qsv", hwEncoder + "_qsv"}, - {"nvenc", hwEncoder + "_nvenc"}, - {"amf", hwEncoder + "_amf"}, - {"omx", hwEncoder + "_omx"}, - {hwEncoder + "_v4l2m2m", hwEncoder + "_v4l2m2m"}, - {"mediacodec", hwEncoder + "_mediacodec"}, - {"vaapi", hwEncoder + "_vaapi"}, - {"videotoolbox", hwEncoder + "_videotoolbox"} + { "qsv", hwEncoder + "_qsv" }, + { hwEncoder + "_qsv", hwEncoder + "_qsv" }, + { "nvenc", hwEncoder + "_nvenc" }, + { "amf", hwEncoder + "_amf" }, + { "omx", hwEncoder + "_omx" }, + { hwEncoder + "_v4l2m2m", hwEncoder + "_v4l2m2m" }, + { "mediacodec", hwEncoder + "_mediacodec" }, + { "vaapi", hwEncoder + "_vaapi" }, + { "videotoolbox", hwEncoder + "_videotoolbox" } }; if (!string.IsNullOrEmpty(hwType) diff --git a/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs b/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs index d231bfa2f..9804ec3bb 100644 --- a/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs +++ b/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs @@ -214,7 +214,7 @@ namespace MediaBrowser.Providers.MediaInfo return new[] { // Every so often - new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks} + new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks } }; } } -- cgit v1.2.3 From ec91d3893d6f1aee7f75dabae3d2311f7592440b Mon Sep 17 00:00:00 2001 From: Jan-Pieter Baert Date: Mon, 12 Oct 2020 20:05:11 +0200 Subject: Fix SA 1116 warnings --- .../Data/BaseSqliteRepository.cs | 3 +- .../Data/SqliteItemRepository.cs | 33 ++++++++++++++-------- .../Data/SqliteUserDataRepository.cs | 9 ++++-- .../Library/Resolvers/Audio/AudioResolver.cs | 6 ++-- .../Library/Resolvers/Books/BookResolver.cs | 3 +- .../Security/AuthenticationRepository.cs | 18 ++++++++---- .../Entities/BaseItemExtensions.cs | 3 +- MediaBrowser.Controller/Entities/Folder.cs | 9 ++++-- .../Entities/UserViewBuilder.cs | 6 ++-- MediaBrowser.Controller/Library/ILibraryManager.cs | 3 +- .../MediaEncoding/EncodingHelper.cs | 3 +- .../MediaEncoding/EncodingJobInfo.cs | 6 ++-- .../MediaEncoding/IMediaEncoder.cs | 3 +- MediaBrowser.Controller/Resolvers/IItemResolver.cs | 3 +- MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs | 12 +++++--- MediaBrowser.Model/Dlna/DeviceProfile.cs | 3 +- MediaBrowser.Model/Dlna/StreamBuilder.cs | 15 ++++++---- .../Plugins/TheTvdb/TvdbClientManager.cs | 3 +- .../Plugins/TheTvdb/TvdbEpisodeProvider.cs | 3 +- 19 files changed, 96 insertions(+), 48 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs index 0fb050a7a..8c756a7f4 100644 --- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs +++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs @@ -157,7 +157,8 @@ namespace Emby.Server.Implementations.Data protected bool TableExists(ManagedConnection connection, string name) { - return connection.RunInTransaction(db => + return connection.RunInTransaction( + db => { using (var statement = PrepareStatement(db, "select DISTINCT tbl_name from sqlite_master")) { diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 86a9908be..1c2aeda70 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -219,7 +219,8 @@ namespace Emby.Server.Implementations.Data { connection.RunQueries(queries); - connection.RunInTransaction(db => + connection.RunInTransaction( + db => { var existingColumnNames = GetColumnNames(db, "AncestorIds"); AddColumn(db, "AncestorIds", "AncestorIdText", "Text", existingColumnNames); @@ -495,7 +496,8 @@ namespace Emby.Server.Implementations.Data using (var connection = GetConnection()) { - connection.RunInTransaction(db => + connection.RunInTransaction( + db => { using (var saveImagesStatement = base.PrepareStatement(db, "Update TypedBaseItems set Images=@Images where guid=@Id")) { @@ -546,7 +548,8 @@ namespace Emby.Server.Implementations.Data using (var connection = GetConnection()) { - connection.RunInTransaction(db => + connection.RunInTransaction( + db => { SaveItemsInTranscation(db, tuples); }, TransactionMode); @@ -2032,7 +2035,8 @@ namespace Emby.Server.Implementations.Data using (var connection = GetConnection()) { - connection.RunInTransaction(db => + connection.RunInTransaction( + db => { // First delete chapters db.Execute("delete from " + ChaptersTableName + " where ItemId=@ItemId", idBlob); @@ -2921,7 +2925,8 @@ namespace Emby.Server.Implementations.Data var result = new QueryResult(); using (var connection = GetConnection(true)) { - connection.RunInTransaction(db => + connection.RunInTransaction( + db => { var statements = PrepareAll(db, statementTexts); @@ -3324,7 +3329,8 @@ namespace Emby.Server.Implementations.Data var result = new QueryResult(); using (var connection = GetConnection(true)) { - connection.RunInTransaction(db => + connection.RunInTransaction( + db => { var statements = PrepareAll(db, statementTexts); @@ -4899,7 +4905,8 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type using (var connection = GetConnection()) { - connection.RunInTransaction(db => + connection.RunInTransaction( + db => { connection.ExecuteAll(sql); }, TransactionMode); @@ -4950,7 +4957,8 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type using (var connection = GetConnection()) { - connection.RunInTransaction(db => + connection.RunInTransaction( + db => { var idBlob = id.ToByteArray(); @@ -5744,7 +5752,8 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type using (var connection = GetConnection()) { - connection.RunInTransaction(db => + connection.RunInTransaction( + db => { var itemIdBlob = itemId.ToByteArray(); @@ -5898,7 +5907,8 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type using (var connection = GetConnection()) { - connection.RunInTransaction(db => + connection.RunInTransaction( + db => { var itemIdBlob = id.ToByteArray(); @@ -6232,7 +6242,8 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type using (var connection = GetConnection()) { - connection.RunInTransaction(db => + connection.RunInTransaction( + db => { var itemIdBlob = id.ToByteArray(); diff --git a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs index 4a78aac8e..2c4e8e0fc 100644 --- a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs @@ -44,7 +44,8 @@ namespace Emby.Server.Implementations.Data var users = userDatasTableExists ? null : userManager.Users; - connection.RunInTransaction(db => + connection.RunInTransaction( + db => { db.ExecuteAll(string.Join(";", new[] { @@ -178,7 +179,8 @@ namespace Emby.Server.Implementations.Data using (var connection = GetConnection()) { - connection.RunInTransaction(db => + connection.RunInTransaction( + db => { SaveUserData(db, internalUserId, key, userData); }, TransactionMode); @@ -246,7 +248,8 @@ namespace Emby.Server.Implementations.Data using (var connection = GetConnection()) { - connection.RunInTransaction(db => + connection.RunInTransaction( + db => { foreach (var userItemData in userDataList) { diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs index 03059e6d3..70be52411 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs @@ -32,7 +32,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio /// The priority. public override ResolverPriority Priority => ResolverPriority.Fourth; - public MultiItemResolverResult ResolveMultiple(Folder parent, + public MultiItemResolverResult ResolveMultiple( + Folder parent, List files, string collectionType, IDirectoryService directoryService) @@ -50,7 +51,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio return result; } - private MultiItemResolverResult ResolveMultipleInternal(Folder parent, + private MultiItemResolverResult ResolveMultipleInternal( + Folder parent, List files, string collectionType, IDirectoryService directoryService) diff --git a/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs index 86a5d8b7d..59af7ce8a 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs @@ -50,7 +50,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Books var fileExtension = Path.GetExtension(f.FullName) ?? string.Empty; - return _validExtensions.Contains(fileExtension, + return _validExtensions.Contains( + fileExtension, StringComparer .OrdinalIgnoreCase); }).ToList(); diff --git a/Emby.Server.Implementations/Security/AuthenticationRepository.cs b/Emby.Server.Implementations/Security/AuthenticationRepository.cs index 29393ae07..4bc12f44a 100644 --- a/Emby.Server.Implementations/Security/AuthenticationRepository.cs +++ b/Emby.Server.Implementations/Security/AuthenticationRepository.cs @@ -54,7 +54,8 @@ namespace Emby.Server.Implementations.Security { if (tableNewlyCreated && TableExists(connection, "AccessTokens")) { - connection.RunInTransaction(db => + connection.RunInTransaction( + db => { var existingColumnNames = GetColumnNames(db, "AccessTokens"); @@ -88,7 +89,8 @@ namespace Emby.Server.Implementations.Security using (var connection = GetConnection()) { - connection.RunInTransaction(db => + connection.RunInTransaction( + db => { using (var statement = db.PrepareStatement("insert into Tokens (AccessToken, DeviceId, AppName, AppVersion, DeviceName, UserId, UserName, IsActive, DateCreated, DateLastActivity) values (@AccessToken, @DeviceId, @AppName, @AppVersion, @DeviceName, @UserId, @UserName, @IsActive, @DateCreated, @DateLastActivity)")) { @@ -119,7 +121,8 @@ namespace Emby.Server.Implementations.Security using (var connection = GetConnection()) { - connection.RunInTransaction(db => + connection.RunInTransaction( + db => { using (var statement = db.PrepareStatement("Update Tokens set AccessToken=@AccessToken, DeviceId=@DeviceId, AppName=@AppName, AppVersion=@AppVersion, DeviceName=@DeviceName, UserId=@UserId, UserName=@UserName, DateCreated=@DateCreated, DateLastActivity=@DateLastActivity where Id=@Id")) { @@ -151,7 +154,8 @@ namespace Emby.Server.Implementations.Security using (var connection = GetConnection()) { - connection.RunInTransaction(db => + connection.RunInTransaction( + db => { using (var statement = db.PrepareStatement("Delete from Tokens where Id=@Id")) { @@ -346,7 +350,8 @@ namespace Emby.Server.Implementations.Security { using (var connection = GetConnection(true)) { - return connection.RunInTransaction(db => + return connection.RunInTransaction( + db => { using (var statement = base.PrepareStatement(db, "select CustomName from Devices where Id=@DeviceId")) { @@ -377,7 +382,8 @@ namespace Emby.Server.Implementations.Security using (var connection = GetConnection()) { - connection.RunInTransaction(db => + connection.RunInTransaction( + db => { using (var statement = db.PrepareStatement("replace into devices (Id, CustomName, Capabilities) VALUES (@Id, @CustomName, (Select Capabilities from Devices where Id=@Id))")) { diff --git a/MediaBrowser.Controller/Entities/BaseItemExtensions.cs b/MediaBrowser.Controller/Entities/BaseItemExtensions.cs index 8a69971d0..c65477d39 100644 --- a/MediaBrowser.Controller/Entities/BaseItemExtensions.cs +++ b/MediaBrowser.Controller/Entities/BaseItemExtensions.cs @@ -45,7 +45,8 @@ namespace MediaBrowser.Controller.Entities { if (file.StartsWith("http", System.StringComparison.OrdinalIgnoreCase)) { - item.SetImage(new ItemImageInfo + item.SetImage( + new ItemImageInfo { Path = file, Type = imageType diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index 901ea875b..35ddaffad 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -255,7 +255,8 @@ namespace MediaBrowser.Controller.Entities var id = child.Id; if (dictionary.ContainsKey(id)) { - Logger.LogError("Found folder containing items with duplicate id. Path: {path}, Child Name: {ChildName}", + Logger.LogError( + "Found folder containing items with duplicate id. Path: {path}, Child Name: {ChildName}", Path ?? Name, child.Path ?? child.Name); } @@ -984,7 +985,8 @@ namespace MediaBrowser.Controller.Entities return items; } - private static bool CollapseBoxSetItems(InternalItemsQuery query, + private static bool CollapseBoxSetItems( + InternalItemsQuery query, BaseItem queryParent, User user, IServerConfigurationManager configurationManager) @@ -1593,7 +1595,8 @@ namespace MediaBrowser.Controller.Entities /// The date played. /// if set to true [reset position]. /// Task. - public override void MarkPlayed(User user, + public override void MarkPlayed( + User user, DateTime? datePlayed, bool resetPosition) { diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs index 068a76769..7bb311900 100644 --- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs +++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs @@ -343,7 +343,8 @@ namespace MediaBrowser.Controller.Entities { var parentFolders = GetMediaFolders(parent, query.User, new[] { CollectionType.TvShows, string.Empty }); - var result = _tvSeriesManager.GetNextUp(new NextUpQuery + var result = _tvSeriesManager.GetNextUp( + new NextUpQuery { Limit = query.Limit, StartIndex = query.StartIndex, @@ -443,7 +444,8 @@ namespace MediaBrowser.Controller.Entities return Filter(item, query.User, query, BaseItem.UserDataManager, BaseItem.LibraryManager); } - public static QueryResult PostFilterAndSort(IEnumerable items, + public static QueryResult PostFilterAndSort( + IEnumerable items, BaseItem queryParent, int? totalRecordLimit, InternalItemsQuery query, diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index 332730bcc..32703c2fd 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -566,7 +566,8 @@ namespace MediaBrowser.Controller.Library int GetCount(InternalItemsQuery query); - void AddExternalSubtitleStreams(List streams, + void AddExternalSubtitleStreams( + List streams, string videoPath, string[] files); } diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 906afa7f6..f913c32e4 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -1822,7 +1822,8 @@ namespace MediaBrowser.Controller.MediaEncoding return (Convert.ToInt32(outputWidth), Convert.ToInt32(outputHeight)); } - public List GetScalingFilters(EncodingJobInfo state, + public List GetScalingFilters( + EncodingJobInfo state, int? videoWidth, int? videoHeight, Video3DFormat? threedFormat, diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs index c7ec878d2..6cd0c70d2 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs @@ -342,7 +342,8 @@ namespace MediaBrowser.Controller.MediaEncoding { var size = new ImageDimensions(VideoStream.Width.Value, VideoStream.Height.Value); - var newSize = DrawingUtils.Resize(size, + var newSize = DrawingUtils.Resize( + size, BaseRequest.Width ?? 0, BaseRequest.Height ?? 0, BaseRequest.MaxWidth ?? 0, @@ -368,7 +369,8 @@ namespace MediaBrowser.Controller.MediaEncoding { var size = new ImageDimensions(VideoStream.Width.Value, VideoStream.Height.Value); - var newSize = DrawingUtils.Resize(size, + var newSize = DrawingUtils.Resize( + size, BaseRequest.Width ?? 0, BaseRequest.Height ?? 0, BaseRequest.MaxWidth ?? 0, diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs index 17d6dc5d2..f6bc1f4de 100644 --- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs +++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs @@ -68,7 +68,8 @@ namespace MediaBrowser.Controller.MediaEncoding /// /// Extracts the video images on interval. /// - Task ExtractVideoImagesOnInterval(string[] inputFiles, + Task ExtractVideoImagesOnInterval( + string[] inputFiles, string container, MediaStream videoStream, MediaProtocol protocol, diff --git a/MediaBrowser.Controller/Resolvers/IItemResolver.cs b/MediaBrowser.Controller/Resolvers/IItemResolver.cs index eb7fb793a..75286eadc 100644 --- a/MediaBrowser.Controller/Resolvers/IItemResolver.cs +++ b/MediaBrowser.Controller/Resolvers/IItemResolver.cs @@ -29,7 +29,8 @@ namespace MediaBrowser.Controller.Resolvers public interface IMultiItemResolver { - MultiItemResolverResult ResolveMultiple(Folder parent, + MultiItemResolverResult ResolveMultiple( + Folder parent, List files, string collectionType, IDirectoryService directoryService); diff --git a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs index 93e60753a..8b73ecbd4 100644 --- a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs +++ b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs @@ -38,7 +38,8 @@ namespace MediaBrowser.Model.Dlna ";DLNA.ORG_FLAGS={0}", DlnaMaps.FlagsToString(flagValue)); - ResponseProfile mediaProfile = _profile.GetImageMediaProfile(container, + ResponseProfile mediaProfile = _profile.GetImageMediaProfile( + container, width, height); @@ -160,7 +161,8 @@ namespace MediaBrowser.Model.Dlna string dlnaflags = string.Format(CultureInfo.InvariantCulture, ";DLNA.ORG_FLAGS={0}", DlnaMaps.FlagsToString(flagValue)); - ResponseProfile mediaProfile = _profile.GetVideoMediaProfile(container, + ResponseProfile mediaProfile = _profile.GetVideoMediaProfile( + container, audioCodec, videoCodec, width, @@ -221,7 +223,8 @@ namespace MediaBrowser.Model.Dlna private static string GetImageOrgPnValue(string container, int? width, int? height) { MediaFormatProfile? format = new MediaFormatProfileResolver() - .ResolveImageFormat(container, + .ResolveImageFormat( + container, width, height); @@ -231,7 +234,8 @@ namespace MediaBrowser.Model.Dlna private static string GetAudioOrgPnValue(string container, int? audioBitrate, int? audioSampleRate, int? audioChannels) { MediaFormatProfile? format = new MediaFormatProfileResolver() - .ResolveAudioFormat(container, + .ResolveAudioFormat( + container, audioBitrate, audioSampleRate, audioChannels); diff --git a/MediaBrowser.Model/Dlna/DeviceProfile.cs b/MediaBrowser.Model/Dlna/DeviceProfile.cs index 7e921b1fd..44412f3e4 100644 --- a/MediaBrowser.Model/Dlna/DeviceProfile.cs +++ b/MediaBrowser.Model/Dlna/DeviceProfile.cs @@ -277,7 +277,8 @@ namespace MediaBrowser.Model.Dlna return null; } - public ResponseProfile GetVideoMediaProfile(string container, + public ResponseProfile GetVideoMediaProfile( + string container, string audioCodec, string videoCodec, int? width, diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index fc0aad072..4959a9b92 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -455,7 +455,8 @@ namespace MediaBrowser.Model.Dlna if (directPlayProfile == null) { - _logger.LogInformation("Profile: {0}, No audio direct play profiles found for {1} with codec {2}", + _logger.LogInformation( + "Profile: {0}, No audio direct play profiles found for {1} with codec {2}", options.Profile.Name ?? "Unknown Profile", item.Path ?? "Unknown path", audioStream.Codec ?? "Unknown codec"); @@ -679,7 +680,8 @@ namespace MediaBrowser.Model.Dlna bool isEligibleForDirectPlay = options.EnableDirectPlay && (options.ForceDirectPlay || directPlayEligibilityResult.Item1); bool isEligibleForDirectStream = options.EnableDirectStream && (options.ForceDirectStream || directStreamEligibilityResult.Item1); - _logger.LogInformation("Profile: {0}, Path: {1}, isEligibleForDirectPlay: {2}, isEligibleForDirectStream: {3}", + _logger.LogInformation( + "Profile: {0}, Path: {1}, isEligibleForDirectPlay: {2}, isEligibleForDirectStream: {3}", options.Profile.Name ?? "Unknown Profile", item.Path ?? "Unknown path", isEligibleForDirectPlay, @@ -973,7 +975,8 @@ namespace MediaBrowser.Model.Dlna if (directPlay == null) { - _logger.LogInformation("Profile: {0}, No video direct play profiles found for {1} with codec {2}", + _logger.LogInformation( + "Profile: {0}, No video direct play profiles found for {1} with codec {2}", profile.Name ?? "Unknown Profile", mediaSource.Path ?? "Unknown path", videoStream.Codec ?? "Unknown codec"); @@ -1137,7 +1140,8 @@ namespace MediaBrowser.Model.Dlna private void LogConditionFailure(DeviceProfile profile, string type, ProfileCondition condition, MediaSourceInfo mediaSource) { - _logger.LogInformation("Profile: {0}, DirectPlay=false. Reason={1}.{2} Condition: {3}. ConditionValue: {4}. IsRequired: {5}. Path: {6}", + _logger.LogInformation( + "Profile: {0}, DirectPlay=false. Reason={1}.{2} Condition: {3}. ConditionValue: {4}. IsRequired: {5}. Path: {6}", type, profile.Name ?? "Unknown Profile", condition.Property, @@ -1342,7 +1346,8 @@ namespace MediaBrowser.Model.Dlna if (itemBitrate > requestedMaxBitrate) { - _logger.LogInformation("Bitrate exceeds {PlayBackMethod} limit: media bitrate: {MediaBitrate}, max bitrate: {MaxBitrate}", + _logger.LogInformation( + "Bitrate exceeds {PlayBackMethod} limit: media bitrate: {MediaBitrate}, max bitrate: {MaxBitrate}", playMethod, itemBitrate, requestedMaxBitrate); return false; } diff --git a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbClientManager.cs b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbClientManager.cs index 5e9a4a225..ce0dab701 100644 --- a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbClientManager.cs +++ b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbClientManager.cs @@ -150,7 +150,8 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb string language, CancellationToken cancellationToken) { - searchInfo.SeriesProviderIds.TryGetValue(nameof(MetadataProvider.Tvdb), + searchInfo.SeriesProviderIds.TryGetValue( + nameof(MetadataProvider.Tvdb), out var seriesTvdbId); var episodeQuery = new EpisodeQuery(); diff --git a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeProvider.cs b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeProvider.cs index 5fa8a3e1c..fd72ea4a8 100644 --- a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeProvider.cs @@ -106,7 +106,8 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb .ConfigureAwait(false); if (string.IsNullOrEmpty(episodeTvdbId)) { - _logger.LogError("Episode {SeasonNumber}x{EpisodeNumber} not found for series {SeriesTvdbId}", + _logger.LogError( + "Episode {SeasonNumber}x{EpisodeNumber} not found for series {SeriesTvdbId}", searchInfo.ParentIndexNumber, searchInfo.IndexNumber, seriesTvdbId); return result; } -- cgit v1.2.3 From f998e521072ac5d3c0725cbe9fb544e005b15dfe Mon Sep 17 00:00:00 2001 From: crobibero Date: Tue, 13 Oct 2020 18:50:04 -0600 Subject: Update to dotnet 3.1.9 --- Emby.Server.Implementations/Emby.Server.Implementations.csproj | 8 ++++---- Jellyfin.Api/Jellyfin.Api.csproj | 4 ++-- Jellyfin.Data/Jellyfin.Data.csproj | 4 ++-- .../Jellyfin.Server.Implementations.csproj | 4 ++-- Jellyfin.Server/Jellyfin.Server.csproj | 8 ++++---- MediaBrowser.Common/MediaBrowser.Common.csproj | 4 ++-- MediaBrowser.Controller/MediaBrowser.Controller.csproj | 4 ++-- MediaBrowser.Model/MediaBrowser.Model.csproj | 2 +- MediaBrowser.Providers/MediaBrowser.Providers.csproj | 6 +++--- deployment/Dockerfile.debian.amd64 | 2 +- deployment/Dockerfile.debian.arm64 | 2 +- deployment/Dockerfile.debian.armhf | 2 +- deployment/Dockerfile.linux.amd64 | 2 +- deployment/Dockerfile.macos | 2 +- deployment/Dockerfile.portable | 2 +- deployment/Dockerfile.ubuntu.amd64 | 2 +- deployment/Dockerfile.ubuntu.arm64 | 2 +- deployment/Dockerfile.ubuntu.armhf | 2 +- deployment/Dockerfile.windows.amd64 | 2 +- tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj | 4 ++-- 20 files changed, 34 insertions(+), 34 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 9ed3cca99..c762aa0b8 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -32,10 +32,10 @@ - - - - + + + + diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj index 6a00db4b1..da0852ceb 100644 --- a/Jellyfin.Api/Jellyfin.Api.csproj +++ b/Jellyfin.Api/Jellyfin.Api.csproj @@ -14,9 +14,9 @@ - + - + diff --git a/Jellyfin.Data/Jellyfin.Data.csproj b/Jellyfin.Data/Jellyfin.Data.csproj index 6bb0d8ce2..5038988f9 100644 --- a/Jellyfin.Data/Jellyfin.Data.csproj +++ b/Jellyfin.Data/Jellyfin.Data.csproj @@ -41,8 +41,8 @@ - - + + diff --git a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj index 17ba09258..c52be3b8a 100644 --- a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj +++ b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj @@ -25,11 +25,11 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index 761a92f6d..7de34f416 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -38,10 +38,10 @@ - - - - + + + + diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj index 322740cca..e716a6610 100644 --- a/MediaBrowser.Common/MediaBrowser.Common.csproj +++ b/MediaBrowser.Common/MediaBrowser.Common.csproj @@ -18,8 +18,8 @@ - - + + diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 654470406..4374317d6 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -14,8 +14,8 @@ - - + + diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index 264681090..253ee7e79 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -34,7 +34,7 @@ - + diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 794490cc5..24400eae5 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -16,9 +16,9 @@ - - - + + + diff --git a/deployment/Dockerfile.debian.amd64 b/deployment/Dockerfile.debian.amd64 index 7202c5883..aaca8fe01 100644 --- a/deployment/Dockerfile.debian.amd64 +++ b/deployment/Dockerfile.debian.amd64 @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/f01e3d97-c1c3-4635-bc77-0c893be36820/6ec6acabc22468c6cc68b61625b14a7d/dotnet-sdk-3.1.402-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/fdd9ecec-56b4-40f4-b762-d7efe24fc3cd/ffef51844c92afa6714528e10609a30f/dotnet-sdk-3.1.403-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.debian.arm64 b/deployment/Dockerfile.debian.arm64 index e9f30213f..594da04ce 100644 --- a/deployment/Dockerfile.debian.arm64 +++ b/deployment/Dockerfile.debian.arm64 @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/f01e3d97-c1c3-4635-bc77-0c893be36820/6ec6acabc22468c6cc68b61625b14a7d/dotnet-sdk-3.1.402-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/fdd9ecec-56b4-40f4-b762-d7efe24fc3cd/ffef51844c92afa6714528e10609a30f/dotnet-sdk-3.1.403-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.debian.armhf b/deployment/Dockerfile.debian.armhf index 91a8a6e7a..3e6e2d0d7 100644 --- a/deployment/Dockerfile.debian.armhf +++ b/deployment/Dockerfile.debian.armhf @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/f01e3d97-c1c3-4635-bc77-0c893be36820/6ec6acabc22468c6cc68b61625b14a7d/dotnet-sdk-3.1.402-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/fdd9ecec-56b4-40f4-b762-d7efe24fc3cd/ffef51844c92afa6714528e10609a30f/dotnet-sdk-3.1.403-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.linux.amd64 b/deployment/Dockerfile.linux.amd64 index 828d5c2cf..f98881ebf 100644 --- a/deployment/Dockerfile.linux.amd64 +++ b/deployment/Dockerfile.linux.amd64 @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/f01e3d97-c1c3-4635-bc77-0c893be36820/6ec6acabc22468c6cc68b61625b14a7d/dotnet-sdk-3.1.402-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/fdd9ecec-56b4-40f4-b762-d7efe24fc3cd/ffef51844c92afa6714528e10609a30f/dotnet-sdk-3.1.403-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.macos b/deployment/Dockerfile.macos index 0b2a0fe5f..ec9d2d8c7 100644 --- a/deployment/Dockerfile.macos +++ b/deployment/Dockerfile.macos @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/f01e3d97-c1c3-4635-bc77-0c893be36820/6ec6acabc22468c6cc68b61625b14a7d/dotnet-sdk-3.1.402-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/fdd9ecec-56b4-40f4-b762-d7efe24fc3cd/ffef51844c92afa6714528e10609a30f/dotnet-sdk-3.1.403-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.portable b/deployment/Dockerfile.portable index 7d5de230f..3523f8ace 100644 --- a/deployment/Dockerfile.portable +++ b/deployment/Dockerfile.portable @@ -15,7 +15,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/f01e3d97-c1c3-4635-bc77-0c893be36820/6ec6acabc22468c6cc68b61625b14a7d/dotnet-sdk-3.1.402-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/fdd9ecec-56b4-40f4-b762-d7efe24fc3cd/ffef51844c92afa6714528e10609a30f/dotnet-sdk-3.1.403-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.ubuntu.amd64 b/deployment/Dockerfile.ubuntu.amd64 index 9c63f43df..0a365e1ae 100644 --- a/deployment/Dockerfile.ubuntu.amd64 +++ b/deployment/Dockerfile.ubuntu.amd64 @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/f01e3d97-c1c3-4635-bc77-0c893be36820/6ec6acabc22468c6cc68b61625b14a7d/dotnet-sdk-3.1.402-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/fdd9ecec-56b4-40f4-b762-d7efe24fc3cd/ffef51844c92afa6714528e10609a30f/dotnet-sdk-3.1.403-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.ubuntu.arm64 b/deployment/Dockerfile.ubuntu.arm64 index 51612dd44..ab3ec9b9f 100644 --- a/deployment/Dockerfile.ubuntu.arm64 +++ b/deployment/Dockerfile.ubuntu.arm64 @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/f01e3d97-c1c3-4635-bc77-0c893be36820/6ec6acabc22468c6cc68b61625b14a7d/dotnet-sdk-3.1.402-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/fdd9ecec-56b4-40f4-b762-d7efe24fc3cd/ffef51844c92afa6714528e10609a30f/dotnet-sdk-3.1.403-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.ubuntu.armhf b/deployment/Dockerfile.ubuntu.armhf index 4ed7f8687..fa41bdf48 100644 --- a/deployment/Dockerfile.ubuntu.armhf +++ b/deployment/Dockerfile.ubuntu.armhf @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/f01e3d97-c1c3-4635-bc77-0c893be36820/6ec6acabc22468c6cc68b61625b14a7d/dotnet-sdk-3.1.402-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/fdd9ecec-56b4-40f4-b762-d7efe24fc3cd/ffef51844c92afa6714528e10609a30f/dotnet-sdk-3.1.403-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.windows.amd64 b/deployment/Dockerfile.windows.amd64 index 5671cc598..7216b2363 100644 --- a/deployment/Dockerfile.windows.amd64 +++ b/deployment/Dockerfile.windows.amd64 @@ -15,7 +15,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/f01e3d97-c1c3-4635-bc77-0c893be36820/6ec6acabc22468c6cc68b61625b14a7d/dotnet-sdk-3.1.402-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/fdd9ecec-56b4-40f4-b762-d7efe24fc3cd/ffef51844c92afa6714528e10609a30f/dotnet-sdk-3.1.403-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj index 8a559b7b6..aae436fb7 100644 --- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj +++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj @@ -16,8 +16,8 @@ - - + + -- cgit v1.2.3