From 48facb797ed912e4ea6b04b17d1ff190ac2daac4 Mon Sep 17 00:00:00 2001 From: stefan Date: Wed, 12 Sep 2018 19:26:21 +0200 Subject: Update to 3.5.2 and .net core 2.1 --- .../Activity/ActivityLogEntryPoint.cs | 215 +- .../Activity/ActivityManager.cs | 13 +- .../Activity/ActivityRepository.cs | 108 +- .../AppBase/BaseApplicationPaths.cs | 9 + Emby.Server.Implementations/ApplicationHost.cs | 845 ++++---- Emby.Server.Implementations/Archiving/ZipClient.cs | 40 +- .../Browser/BrowserLauncher.cs | 26 +- .../Channels/ChannelConfigurations.cs | 29 - .../Channels/ChannelDynamicMediaSourceProvider.cs | 15 +- .../Channels/ChannelImageProvider.cs | 10 +- .../Channels/ChannelManager.cs | 1078 +++------- .../Channels/ChannelPostScanTask.cs | 183 +- .../Collections/CollectionImageProvider.cs | 14 +- .../Collections/CollectionManager.cs | 227 +- .../Collections/CollectionsDynamicFolder.cs | 34 - .../Configuration/ServerConfigurationManager.cs | 14 +- .../Data/BaseSqliteRepository.cs | 21 +- .../Data/CleanDatabaseScheduledTask.cs | 9 +- .../Data/ManagedConnection.cs | 1 - .../Data/SqliteDisplayPreferencesRepository.cs | 12 +- .../Data/SqliteExtensions.cs | 19 +- .../Data/SqliteItemRepository.cs | 1888 ++++++++++------- .../Data/SqliteUserDataRepository.cs | 209 +- .../Data/SqliteUserRepository.cs | 141 +- .../Devices/CameraUploadsDynamicFolder.cs | 40 - .../Devices/CameraUploadsFolder.cs | 71 - .../Devices/DeviceManager.cs | 380 +++- .../Devices/SqliteDeviceRepository.cs | 451 ---- .../Diagnostics/CommonProcess.cs | 49 +- Emby.Server.Implementations/Dto/DtoService.cs | 542 ++--- .../Emby.Server.Implementations.csproj | 940 ++------- .../EntryPoints/AutomaticRestartEntryPoint.cs | 1 - .../EntryPoints/ExternalPortForwarding.cs | 142 +- .../EntryPoints/KeepServerAwake.cs | 1 - .../EntryPoints/LibraryChangedNotifier.cs | 71 +- .../EntryPoints/LoadRegistrations.cs | 74 - .../EntryPoints/RecordingNotifier.cs | 7 +- .../EntryPoints/ServerEventNotifier.cs | 75 +- .../EntryPoints/StartupWizard.cs | 12 +- .../EntryPoints/SystemEvents.cs | 1 - .../EntryPoints/UdpServerEntryPoint.cs | 4 +- .../EntryPoints/UsageEntryPoint.cs | 49 +- .../EntryPoints/UsageReporter.cs | 2 +- .../EntryPoints/UserDataChangeNotifier.cs | 74 +- .../EnvironmentInfo/EnvironmentInfo.cs | 5 - .../FFMpeg/FFMpegInstallInfo.cs | 2 - Emby.Server.Implementations/FFMpeg/FFMpegLoader.cs | 111 +- .../HttpClientManager/HttpClientManager.cs | 12 +- .../HttpServer/FileWriter.cs | 21 +- .../HttpServer/HttpListenerHost.cs | 345 +++- .../HttpServer/HttpResultFactory.cs | 452 ++-- .../HttpServer/IHttpListener.cs | 3 +- .../HttpServer/LoggerUtils.cs | 13 - .../HttpServer/RangeRequestWriter.cs | 10 +- .../HttpServer/ResponseFilter.cs | 9 +- .../HttpServer/Security/AuthService.cs | 71 +- .../HttpServer/Security/AuthorizationContext.cs | 57 +- .../HttpServer/Security/SessionContext.cs | 15 +- .../HttpServer/SocketSharp/Extensions.cs | 12 - .../HttpServer/SocketSharp/HttpUtility.cs | 923 --------- .../HttpServer/SocketSharp/RequestMono.cs | 846 -------- .../HttpServer/SocketSharp/SharpWebSocket.cs | 159 -- .../SocketSharp/WebSocketSharpListener.cs | 220 -- .../SocketSharp/WebSocketSharpRequest.cs | 611 ------ .../SocketSharp/WebSocketSharpResponse.cs | 203 -- .../HttpServer/StreamWriter.cs | 8 +- .../HttpServer/WebSocketConnection.cs | 290 +++ Emby.Server.Implementations/HttpServerFactory.cs | 76 - .../IO/ExtendedFileSystemInfo.cs | 13 + Emby.Server.Implementations/IO/FileRefresher.cs | 9 +- Emby.Server.Implementations/IO/IsoManager.cs | 1 - Emby.Server.Implementations/IO/LibraryMonitor.cs | 76 +- .../IO/ManagedFileSystem.cs | 178 +- .../IO/MemoryStreamProvider.cs | 29 - .../IO/SharpCifsFileSystem.cs | 3 - Emby.Server.Implementations/IO/StreamHelper.cs | 190 ++ .../Images/BaseDynamicImageProvider.cs | 57 +- .../Library/CoreResolutionIgnoreRule.cs | 88 +- .../Library/DefaultAuthenticationProvider.cs | 105 + .../Library/ExclusiveLiveStream.cs | 42 + .../Library/LibraryManager.cs | 700 ++++--- .../Library/LiveStreamHelper.cs | 181 ++ .../Library/LocalTrailerPostScanTask.cs | 105 - .../Library/MediaSourceManager.cs | 599 ++++-- .../Library/MediaStreamSelector.cs | 217 ++ .../Library/MusicManager.cs | 12 +- .../Library/ResolverHelper.cs | 24 +- .../Library/Resolvers/Audio/AudioResolver.cs | 8 +- .../Library/Resolvers/Audio/MusicAlbumResolver.cs | 33 +- .../Library/Resolvers/BaseVideoResolver.cs | 5 - .../Library/Resolvers/Movies/BoxSetResolver.cs | 8 +- .../Library/Resolvers/Movies/MovieResolver.cs | 36 +- .../Library/Resolvers/PhotoResolver.cs | 3 +- .../Library/Resolvers/PlaylistResolver.cs | 27 +- .../Library/Resolvers/TV/SeasonResolver.cs | 16 +- .../Library/Resolvers/TV/SeriesResolver.cs | 22 +- .../Library/SearchEngine.cs | 152 +- .../Library/UserDataManager.cs | 106 +- Emby.Server.Implementations/Library/UserManager.cs | 369 +++- .../Library/UserViewManager.cs | 110 +- .../Library/Validators/ArtistsValidator.cs | 28 +- .../Library/Validators/PeopleValidator.cs | 28 +- .../Library/Validators/StudiosValidator.cs | 18 + .../LiveTv/ChannelImageProvider.cs | 85 - .../LiveTv/EmbyTV/DirectRecorder.cs | 42 +- .../LiveTv/EmbyTV/EmbyTV.cs | 831 ++++---- .../LiveTv/EmbyTV/EmbyTVRegistration.cs | 36 - .../LiveTv/EmbyTV/EncodedRecorder.cs | 81 +- .../LiveTv/EmbyTV/EntryPoint.cs | 1 - .../LiveTv/EmbyTV/RecordingHelper.cs | 27 +- .../LiveTv/EmbyTV/SeriesTimerManager.cs | 2 +- .../LiveTv/EmbyTV/TimerManager.cs | 32 +- .../LiveTv/Listings/SchedulesDirect.cs | 68 +- .../LiveTv/Listings/XmlTvListingsProvider.cs | 315 --- .../LiveTv/LiveStreamHelper.cs | 113 - .../LiveTv/LiveTvConfigurationFactory.cs | 2 +- .../LiveTv/LiveTvDtoService.cs | 234 +-- .../LiveTv/LiveTvManager.cs | 1541 +++++--------- .../LiveTv/LiveTvMediaSourceProvider.cs | 128 +- .../LiveTv/RecordingImageProvider.cs | 82 - .../LiveTv/TunerHosts/BaseTunerHost.cs | 110 +- .../LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs | 113 +- .../TunerHosts/HdHomerun/HdHomerunManager.cs | 125 +- .../TunerHosts/HdHomerun/HdHomerunUdpStream.cs | 61 +- .../LiveTv/TunerHosts/LiveStream.cs | 179 +- .../LiveTv/TunerHosts/M3UTunerHost.cs | 79 +- .../LiveTv/TunerHosts/SharedHttpStream.cs | 43 +- .../Localization/Core/ar.json | 11 +- .../Localization/Core/bg-BG.json | 15 +- .../Localization/Core/ca.json | 11 +- .../Localization/Core/cs.json | 11 +- .../Localization/Core/da.json | 11 +- .../Localization/Core/de.json | 11 +- .../Localization/Core/el.json | 77 +- .../Localization/Core/en-GB.json | 11 +- .../Localization/Core/en-US.json | 18 +- .../Localization/Core/es-AR.json | 15 +- .../Localization/Core/es-MX.json | 15 +- .../Localization/Core/es.json | 11 +- .../Localization/Core/fa.json | 100 + .../Localization/Core/fr-CA.json | 15 +- .../Localization/Core/fr.json | 17 +- .../Localization/Core/gsw.json | 15 +- .../Localization/Core/he.json | 33 +- .../Localization/Core/hr.json | 11 +- .../Localization/Core/hu.json | 33 +- .../Localization/Core/it.json | 13 +- .../Localization/Core/kk.json | 15 +- .../Localization/Core/ko.json | 15 +- .../Localization/Core/lt-LT.json | 15 +- .../Localization/Core/ms.json | 15 +- .../Localization/Core/nb.json | 11 +- .../Localization/Core/nl.json | 23 +- .../Localization/Core/pl.json | 19 +- .../Localization/Core/pt-BR.json | 11 +- .../Localization/Core/pt-PT.json | 15 +- .../Localization/Core/ru.json | 15 +- .../Localization/Core/sk.json | 27 +- .../Localization/Core/sl-SI.json | 15 +- .../Localization/Core/sv.json | 11 +- .../Localization/Core/tr.json | 15 +- .../Localization/Core/zh-CN.json | 13 +- .../Localization/Core/zh-HK.json | 15 +- .../Localization/LocalizationManager.cs | 128 +- .../Localization/Ratings/au.txt | 8 - .../Localization/Ratings/be.txt | 6 - .../Localization/Ratings/de.txt | 10 - .../Localization/Ratings/ru.txt | 5 - .../Localization/Ratings/us.txt | 2 +- .../Localization/iso6392.txt | 2 +- .../Logging/SimpleLogManager.cs | 83 +- .../MediaEncoder/EncodingManager.cs | 6 +- .../Migrations/IVersionMigration.cs | 9 - .../Net/DisposableManagedObjectBase.cs | 11 +- Emby.Server.Implementations/Net/IWebSocket.cs | 53 + Emby.Server.Implementations/Net/NetAcceptSocket.cs | 98 - Emby.Server.Implementations/Net/SocketFactory.cs | 35 - Emby.Server.Implementations/Net/UdpSocket.cs | 34 +- .../Net/WebSocketConnectEventArgs.cs | 31 + .../Networking/IPNetwork/BigIntegerExt.cs | 168 ++ .../Networking/IPNetwork/IPAddressCollection.cs | 104 + .../Networking/IPNetwork/IPNetwork.cs | 2170 ++++++++++++++++++++ .../Networking/IPNetwork/IPNetworkCollection.cs | 144 ++ .../Networking/IPNetwork/LICENSE.txt | 24 + .../Networking/NetworkManager.cs | 166 +- Emby.Server.Implementations/News/NewsEntryPoint.cs | 5 +- .../Notifications/CoreNotificationTypes.cs | 198 -- .../IConfigurableNotificationService.cs | 8 - .../Notifications/InternalNotificationService.cs | 61 - .../NotificationConfigurationFactory.cs | 21 - .../Notifications/NotificationManager.cs | 302 --- .../Notifications/Notifications.cs | 566 ----- .../Notifications/SqliteNotificationsRepository.cs | 358 ---- .../Notifications/WebSocketNotifier.cs | 56 - .../Playlists/ManualPlaylistsFolder.cs | 12 +- .../Playlists/PlaylistImageProvider.cs | 34 +- .../Playlists/PlaylistManager.cs | 265 ++- .../Playlists/PlaylistsDynamicFolder.cs | 31 - Emby.Server.Implementations/ResourceFileManager.cs | 74 + .../ScheduledTasks/ChapterImagesTask.cs | 6 +- .../ScheduledTasks/DailyTrigger.cs | 17 +- .../ScheduledTasks/IntervalTrigger.cs | 17 +- .../ScheduledTasks/PluginUpdateTask.cs | 23 +- .../ScheduledTasks/ScheduledTaskWorker.cs | 53 +- .../ScheduledTasks/StartupTrigger.cs | 11 +- .../ScheduledTasks/SystemEventTrigger.cs | 11 +- .../ScheduledTasks/TaskManager.cs | 35 +- .../ScheduledTasks/Tasks/DeleteCacheFileTask.cs | 2 +- .../ScheduledTasks/Tasks/DeleteLogFileTask.cs | 2 +- .../ScheduledTasks/Tasks/ReloadLoggerFileTask.cs | 10 +- .../ScheduledTasks/WeeklyTrigger.cs | 11 +- .../Security/AuthenticationRepository.cs | 314 ++- .../Security/MBLicenseFile.cs | 12 +- .../Security/PluginSecurityManager.cs | 264 +-- .../Serialization/JsonSerializer.cs | 52 +- .../ServerApplicationPaths.cs | 88 +- .../ServerManager/ServerManager.cs | 357 ---- .../ServerManager/WebSocketConnection.cs | 267 --- .../Services/RequestHelper.cs | 3 +- .../Services/ResponseHelper.cs | 48 +- .../Services/ServiceController.cs | 39 +- .../Services/ServiceExec.cs | 52 +- .../Services/ServiceHandler.cs | 90 +- .../Services/ServicePath.cs | 17 +- .../Services/SwaggerService.cs | 260 --- .../Services/UrlExtensions.cs | 2 +- .../Session/FirebaseSessionController.cs | 131 ++ .../Session/HttpSessionController.cs | 148 +- .../Session/SessionManager.cs | 918 +++++---- .../Session/SessionWebSocketListener.cs | 378 +--- .../Session/WebSocketController.cs | 208 +- .../Social/SharingManager.cs | 101 - .../Social/SharingRepository.cs | 135 -- Emby.Server.Implementations/SystemEvents.cs | 28 - .../TV/SeriesPostScanTask.cs | 241 --- Emby.Server.Implementations/TV/TVSeriesManager.cs | 38 +- .../TextEncoding/TextEncoding.cs | 3 + .../Threading/CommonTimer.cs | 1 - Emby.Server.Implementations/Udp/UdpServer.cs | 1 - .../Updates/InstallationManager.cs | 131 +- .../UserViews/CollectionFolderImageProvider.cs | 143 +- .../UserViews/DynamicImageProvider.cs | 48 +- .../UserViews/FolderImageProvider.cs | 57 +- Emby.Server.Implementations/packages.config | 9 - Emby.Server.Implementations/values.txt | 0 245 files changed, 13741 insertions(+), 17581 deletions(-) delete mode 100644 Emby.Server.Implementations/Channels/ChannelConfigurations.cs delete mode 100644 Emby.Server.Implementations/Collections/CollectionsDynamicFolder.cs delete mode 100644 Emby.Server.Implementations/Devices/CameraUploadsDynamicFolder.cs delete mode 100644 Emby.Server.Implementations/Devices/CameraUploadsFolder.cs delete mode 100644 Emby.Server.Implementations/Devices/SqliteDeviceRepository.cs delete mode 100644 Emby.Server.Implementations/EntryPoints/LoadRegistrations.cs delete mode 100644 Emby.Server.Implementations/HttpServer/SocketSharp/Extensions.cs delete mode 100644 Emby.Server.Implementations/HttpServer/SocketSharp/HttpUtility.cs delete mode 100644 Emby.Server.Implementations/HttpServer/SocketSharp/RequestMono.cs delete mode 100644 Emby.Server.Implementations/HttpServer/SocketSharp/SharpWebSocket.cs delete mode 100644 Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs delete mode 100644 Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpRequest.cs delete mode 100644 Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs create mode 100644 Emby.Server.Implementations/HttpServer/WebSocketConnection.cs delete mode 100644 Emby.Server.Implementations/HttpServerFactory.cs create mode 100644 Emby.Server.Implementations/IO/ExtendedFileSystemInfo.cs delete mode 100644 Emby.Server.Implementations/IO/MemoryStreamProvider.cs create mode 100644 Emby.Server.Implementations/IO/StreamHelper.cs create mode 100644 Emby.Server.Implementations/Library/DefaultAuthenticationProvider.cs create mode 100644 Emby.Server.Implementations/Library/ExclusiveLiveStream.cs create mode 100644 Emby.Server.Implementations/Library/LiveStreamHelper.cs delete mode 100644 Emby.Server.Implementations/Library/LocalTrailerPostScanTask.cs create mode 100644 Emby.Server.Implementations/Library/MediaStreamSelector.cs delete mode 100644 Emby.Server.Implementations/LiveTv/ChannelImageProvider.cs delete mode 100644 Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTVRegistration.cs delete mode 100644 Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs delete mode 100644 Emby.Server.Implementations/LiveTv/LiveStreamHelper.cs delete mode 100644 Emby.Server.Implementations/LiveTv/RecordingImageProvider.cs create mode 100644 Emby.Server.Implementations/Localization/Core/fa.json delete mode 100644 Emby.Server.Implementations/Localization/Ratings/au.txt delete mode 100644 Emby.Server.Implementations/Localization/Ratings/be.txt delete mode 100644 Emby.Server.Implementations/Localization/Ratings/de.txt delete mode 100644 Emby.Server.Implementations/Localization/Ratings/ru.txt delete mode 100644 Emby.Server.Implementations/Migrations/IVersionMigration.cs create mode 100644 Emby.Server.Implementations/Net/IWebSocket.cs delete mode 100644 Emby.Server.Implementations/Net/NetAcceptSocket.cs create mode 100644 Emby.Server.Implementations/Net/WebSocketConnectEventArgs.cs create mode 100644 Emby.Server.Implementations/Networking/IPNetwork/BigIntegerExt.cs create mode 100644 Emby.Server.Implementations/Networking/IPNetwork/IPAddressCollection.cs create mode 100644 Emby.Server.Implementations/Networking/IPNetwork/IPNetwork.cs create mode 100644 Emby.Server.Implementations/Networking/IPNetwork/IPNetworkCollection.cs create mode 100644 Emby.Server.Implementations/Networking/IPNetwork/LICENSE.txt delete mode 100644 Emby.Server.Implementations/Notifications/CoreNotificationTypes.cs delete mode 100644 Emby.Server.Implementations/Notifications/IConfigurableNotificationService.cs delete mode 100644 Emby.Server.Implementations/Notifications/InternalNotificationService.cs delete mode 100644 Emby.Server.Implementations/Notifications/NotificationConfigurationFactory.cs delete mode 100644 Emby.Server.Implementations/Notifications/NotificationManager.cs delete mode 100644 Emby.Server.Implementations/Notifications/Notifications.cs delete mode 100644 Emby.Server.Implementations/Notifications/SqliteNotificationsRepository.cs delete mode 100644 Emby.Server.Implementations/Notifications/WebSocketNotifier.cs delete mode 100644 Emby.Server.Implementations/Playlists/PlaylistsDynamicFolder.cs create mode 100644 Emby.Server.Implementations/ResourceFileManager.cs delete mode 100644 Emby.Server.Implementations/ServerManager/ServerManager.cs delete mode 100644 Emby.Server.Implementations/ServerManager/WebSocketConnection.cs delete mode 100644 Emby.Server.Implementations/Services/SwaggerService.cs create mode 100644 Emby.Server.Implementations/Session/FirebaseSessionController.cs delete mode 100644 Emby.Server.Implementations/Social/SharingManager.cs delete mode 100644 Emby.Server.Implementations/Social/SharingRepository.cs delete mode 100644 Emby.Server.Implementations/TV/SeriesPostScanTask.cs delete mode 100644 Emby.Server.Implementations/packages.config create mode 100644 Emby.Server.Implementations/values.txt (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs index 4e448ac64c..079d0af0ad 100644 --- a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs +++ b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs @@ -19,6 +19,11 @@ using System.Linq; using System.Text; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Extensions; +using MediaBrowser.Model.Notifications; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Dto; +using MediaBrowser.Controller.Devices; +using MediaBrowser.Controller.Authentication; namespace Emby.Server.Implementations.Activity { @@ -26,7 +31,6 @@ namespace Emby.Server.Implementations.Activity { private readonly IInstallationManager _installationManager; - //private readonly ILogManager _logManager; //private readonly ILogger _logger; private readonly ISessionManager _sessionManager; private readonly ITaskManager _taskManager; @@ -38,10 +42,10 @@ namespace Emby.Server.Implementations.Activity private readonly IUserManager _userManager; private readonly IServerConfigurationManager _config; private readonly IServerApplicationHost _appHost; + private readonly IDeviceManager _deviceManager; - public ActivityLogEntryPoint(ISessionManager sessionManager, ITaskManager taskManager, IActivityManager activityManager, ILocalizationManager localization, IInstallationManager installationManager, ILibraryManager libraryManager, ISubtitleManager subManager, IUserManager userManager, IServerConfigurationManager config, IServerApplicationHost appHost) + public ActivityLogEntryPoint(ISessionManager sessionManager, IDeviceManager deviceManager, ITaskManager taskManager, IActivityManager activityManager, ILocalizationManager localization, IInstallationManager installationManager, ILibraryManager libraryManager, ISubtitleManager subManager, IUserManager userManager, IServerConfigurationManager config, IServerApplicationHost appHost) { - //_logger = _logManager.GetLogger("ActivityLogEntryPoint"); _sessionManager = sessionManager; _taskManager = taskManager; _activityManager = activityManager; @@ -51,21 +55,18 @@ namespace Emby.Server.Implementations.Activity _subManager = subManager; _userManager = userManager; _config = config; - //_logManager = logManager; _appHost = appHost; + _deviceManager = deviceManager; } public void Run() { - //_taskManager.TaskExecuting += _taskManager_TaskExecuting; - //_taskManager.TaskCompleted += _taskManager_TaskCompleted; + _taskManager.TaskCompleted += _taskManager_TaskCompleted; - //_installationManager.PluginInstalled += _installationManager_PluginInstalled; - //_installationManager.PluginUninstalled += _installationManager_PluginUninstalled; - //_installationManager.PluginUpdated += _installationManager_PluginUpdated; - - //_libraryManager.ItemAdded += _libraryManager_ItemAdded; - //_libraryManager.ItemRemoved += _libraryManager_ItemRemoved; + _installationManager.PluginInstalled += _installationManager_PluginInstalled; + _installationManager.PluginUninstalled += _installationManager_PluginUninstalled; + _installationManager.PluginUpdated += _installationManager_PluginUpdated; + _installationManager.PackageInstallationFailed += _installationManager_PackageInstallationFailed; _sessionManager.SessionStarted += _sessionManager_SessionStarted; _sessionManager.AuthenticationFailed += _sessionManager_AuthenticationFailed; @@ -81,24 +82,33 @@ namespace Emby.Server.Implementations.Activity _userManager.UserCreated += _userManager_UserCreated; _userManager.UserPasswordChanged += _userManager_UserPasswordChanged; _userManager.UserDeleted += _userManager_UserDeleted; - _userManager.UserConfigurationUpdated += _userManager_UserConfigurationUpdated; + _userManager.UserPolicyUpdated += _userManager_UserPolicyUpdated; _userManager.UserLockedOut += _userManager_UserLockedOut; //_config.ConfigurationUpdated += _config_ConfigurationUpdated; //_config.NamedConfigurationUpdated += _config_NamedConfigurationUpdated; - //_logManager.LoggerLoaded += _logManager_LoggerLoaded; + _deviceManager.CameraImageUploaded += _deviceManager_CameraImageUploaded; _appHost.ApplicationUpdated += _appHost_ApplicationUpdated; } + void _deviceManager_CameraImageUploaded(object sender, GenericEventArgs e) + { + CreateLogEntry(new ActivityLogEntry + { + Name = string.Format(_localization.GetLocalizedString("CameraImageUploadedFrom"), e.Argument.Device.Name), + Type = NotificationType.CameraImageUploaded.ToString() + }); + } + void _userManager_UserLockedOut(object sender, GenericEventArgs e) { CreateLogEntry(new ActivityLogEntry { Name = string.Format(_localization.GetLocalizedString("UserLockedOutWithName"), e.Argument.Name), - Type = "UserLockedOut", - UserId = e.Argument.Id.ToString("N") + Type = NotificationType.UserLockedOut.ToString(), + UserId = e.Argument.Id }); } @@ -106,11 +116,10 @@ namespace Emby.Server.Implementations.Activity { CreateLogEntry(new ActivityLogEntry { - Name = string.Format(_localization.GetLocalizedString("SubtitleDownloadFailureForItem"), Notifications.Notifications.GetItemName(e.Item)), + Name = string.Format(_localization.GetLocalizedString("SubtitleDownloadFailureFromForItem"), e.Provider, Notifications.Notifications.GetItemName(e.Item)), Type = "SubtitleDownloadFailure", ItemId = e.Item.Id.ToString("N"), - ShortOverview = string.Format(_localization.GetLocalizedString("ProviderValue"), e.Provider), - Overview = LogHelper.GetLogMessage(e.Exception).ToString() + ShortOverview = e.Exception.Message }); } @@ -139,10 +148,9 @@ namespace Emby.Server.Implementations.Activity CreateLogEntry(new ActivityLogEntry { - Name = string.Format(_localization.GetLocalizedString("UserStoppedPlayingItemWithValues"), user.Name, Notifications.Notifications.GetItemName(item)), - Type = "PlaybackStopped", - ShortOverview = string.Format(_localization.GetLocalizedString("AppDeviceValues"), e.ClientName, e.DeviceName), - UserId = user.Id.ToString("N") + Name = string.Format(_localization.GetLocalizedString("UserStoppedPlayingItemWithValues"), user.Name, GetItemName(item), e.DeviceName), + Type = GetPlaybackStoppedNotificationType(item.MediaType), + UserId = user.Id }); } @@ -171,19 +179,71 @@ namespace Emby.Server.Implementations.Activity CreateLogEntry(new ActivityLogEntry { - Name = string.Format(_localization.GetLocalizedString("UserStartedPlayingItemWithValues"), user.Name, Notifications.Notifications.GetItemName(item)), - Type = "PlaybackStart", - ShortOverview = string.Format(_localization.GetLocalizedString("AppDeviceValues"), e.ClientName, e.DeviceName), - UserId = user.Id.ToString("N") + Name = string.Format(_localization.GetLocalizedString("UserStartedPlayingItemWithValues"), user.Name, GetItemName(item), e.DeviceName), + Type = GetPlaybackNotificationType(item.MediaType), + UserId = user.Id }); } + private static string GetItemName(BaseItemDto item) + { + var name = item.Name; + + if (!string.IsNullOrEmpty(item.SeriesName)) + { + name = item.SeriesName + " - " + name; + } + + if (item.Artists != null && item.Artists.Length > 0) + { + name = item.Artists[0] + " - " + name; + } + + return name; + } + + private string GetPlaybackNotificationType(string mediaType) + { + if (string.Equals(mediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase)) + { + return NotificationType.AudioPlayback.ToString(); + } + if (string.Equals(mediaType, MediaType.Game, StringComparison.OrdinalIgnoreCase)) + { + return NotificationType.GamePlayback.ToString(); + } + if (string.Equals(mediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase)) + { + return NotificationType.VideoPlayback.ToString(); + } + + return null; + } + + private string GetPlaybackStoppedNotificationType(string mediaType) + { + if (string.Equals(mediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase)) + { + return NotificationType.AudioPlaybackStopped.ToString(); + } + if (string.Equals(mediaType, MediaType.Game, StringComparison.OrdinalIgnoreCase)) + { + return NotificationType.GamePlaybackStopped.ToString(); + } + if (string.Equals(mediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase)) + { + return NotificationType.VideoPlaybackStopped.ToString(); + } + + return null; + } + void _sessionManager_SessionEnded(object sender, SessionEventArgs e) { string name; var session = e.SessionInfo; - if (string.IsNullOrWhiteSpace(session.UserName)) + if (string.IsNullOrEmpty(session.UserName)) { name = string.Format(_localization.GetLocalizedString("DeviceOfflineWithName"), session.DeviceName); @@ -200,17 +260,20 @@ namespace Emby.Server.Implementations.Activity Name = name, Type = "SessionEnded", ShortOverview = string.Format(_localization.GetLocalizedString("LabelIpAddressValue"), session.RemoteEndPoint), - UserId = session.UserId.HasValue ? session.UserId.Value.ToString("N") : null + UserId = session.UserId }); } - void _sessionManager_AuthenticationSucceeded(object sender, GenericEventArgs e) + void _sessionManager_AuthenticationSucceeded(object sender, GenericEventArgs e) { + var user = e.Argument.User; + CreateLogEntry(new ActivityLogEntry { - Name = string.Format(_localization.GetLocalizedString("AuthenticationSucceededWithUserName"), e.Argument.Username), + Name = string.Format(_localization.GetLocalizedString("AuthenticationSucceededWithUserName"), user.Name), Type = "AuthenticationSucceeded", - ShortOverview = string.Format(_localization.GetLocalizedString("LabelIpAddressValue"), e.Argument.RemoteEndPoint) + ShortOverview = string.Format(_localization.GetLocalizedString("LabelIpAddressValue"), e.Argument.SessionInfo.RemoteEndPoint), + UserId = user.Id }); } @@ -229,9 +292,8 @@ namespace Emby.Server.Implementations.Activity { CreateLogEntry(new ActivityLogEntry { - Name = _localization.GetLocalizedString("MessageApplicationUpdated"), - Type = "ApplicationUpdated", - ShortOverview = string.Format(_localization.GetLocalizedString("VersionNumber"), e.Argument.versionStr), + Name = string.Format(_localization.GetLocalizedString("MessageApplicationUpdatedTo"), e.Argument.versionStr), + Type = NotificationType.ApplicationUpdateInstalled.ToString(), Overview = e.Argument.description }); } @@ -254,13 +316,13 @@ namespace Emby.Server.Implementations.Activity }); } - void _userManager_UserConfigurationUpdated(object sender, GenericEventArgs e) + void _userManager_UserPolicyUpdated(object sender, GenericEventArgs e) { CreateLogEntry(new ActivityLogEntry { - Name = string.Format(_localization.GetLocalizedString("UserConfigurationUpdatedWithName"), e.Argument.Name), - Type = "UserConfigurationUpdated", - UserId = e.Argument.Id.ToString("N") + Name = string.Format(_localization.GetLocalizedString("UserPolicyUpdatedWithName"), e.Argument.Name), + Type = "UserPolicyUpdated", + UserId = e.Argument.Id }); } @@ -279,7 +341,7 @@ namespace Emby.Server.Implementations.Activity { Name = string.Format(_localization.GetLocalizedString("UserPasswordChangedWithName"), e.Argument.Name), Type = "UserPasswordChanged", - UserId = e.Argument.Id.ToString("N") + UserId = e.Argument.Id }); } @@ -289,7 +351,7 @@ namespace Emby.Server.Implementations.Activity { Name = string.Format(_localization.GetLocalizedString("UserCreatedWithName"), e.Argument.Name), Type = "UserCreated", - UserId = e.Argument.Id.ToString("N") + UserId = e.Argument.Id }); } @@ -309,7 +371,7 @@ namespace Emby.Server.Implementations.Activity string name; var session = e.SessionInfo; - if (string.IsNullOrWhiteSpace(session.UserName)) + if (string.IsNullOrEmpty(session.UserName)) { name = string.Format(_localization.GetLocalizedString("DeviceOnlineWithName"), session.DeviceName); @@ -326,36 +388,7 @@ namespace Emby.Server.Implementations.Activity Name = name, Type = "SessionStarted", ShortOverview = string.Format(_localization.GetLocalizedString("LabelIpAddressValue"), session.RemoteEndPoint), - UserId = session.UserId.HasValue ? session.UserId.Value.ToString("N") : null - }); - } - - void _libraryManager_ItemRemoved(object sender, ItemChangeEventArgs e) - { - if (e.Item.SourceType != SourceType.Library) - { - return; - } - - CreateLogEntry(new ActivityLogEntry - { - Name = string.Format(_localization.GetLocalizedString("ItemRemovedWithName"), Notifications.Notifications.GetItemName(e.Item)), - Type = "ItemRemoved" - }); - } - - void _libraryManager_ItemAdded(object sender, ItemChangeEventArgs e) - { - if (e.Item.SourceType != SourceType.Library) - { - return; - } - - CreateLogEntry(new ActivityLogEntry - { - Name = string.Format(_localization.GetLocalizedString("ItemAddedWithName"), Notifications.Notifications.GetItemName(e.Item)), - Type = "ItemAdded", - ItemId = e.Item.Id.ToString("N") + UserId = session.UserId }); } @@ -364,7 +397,7 @@ namespace Emby.Server.Implementations.Activity CreateLogEntry(new ActivityLogEntry { Name = string.Format(_localization.GetLocalizedString("PluginUpdatedWithName"), e.Argument.Item1.Name), - Type = "PluginUpdated", + Type = NotificationType.PluginUpdateInstalled.ToString(), ShortOverview = string.Format(_localization.GetLocalizedString("VersionNumber"), e.Argument.Item2.versionStr), Overview = e.Argument.Item2.description }); @@ -375,7 +408,7 @@ namespace Emby.Server.Implementations.Activity CreateLogEntry(new ActivityLogEntry { Name = string.Format(_localization.GetLocalizedString("PluginUninstalledWithName"), e.Argument.Name), - Type = "PluginUninstalled" + Type = NotificationType.PluginUninstalled.ToString() }); } @@ -384,25 +417,21 @@ namespace Emby.Server.Implementations.Activity CreateLogEntry(new ActivityLogEntry { Name = string.Format(_localization.GetLocalizedString("PluginInstalledWithName"), e.Argument.name), - Type = "PluginInstalled", + Type = NotificationType.PluginInstalled.ToString(), ShortOverview = string.Format(_localization.GetLocalizedString("VersionNumber"), e.Argument.versionStr) }); } - void _taskManager_TaskExecuting(object sender, GenericEventArgs e) + void _installationManager_PackageInstallationFailed(object sender, InstallationFailedEventArgs e) { - var task = e.Argument; - - var activityTask = task.ScheduledTask as IConfigurableScheduledTask; - if (activityTask != null && !activityTask.IsLogged) - { - return; - } + var installationInfo = e.InstallationInfo; CreateLogEntry(new ActivityLogEntry { - Name = string.Format(_localization.GetLocalizedString("ScheduledTaskStartedWithName"), task.Name), - Type = "ScheduledTaskStarted" + Name = string.Format(_localization.GetLocalizedString("NameInstallFailed"), installationInfo.Name), + Type = NotificationType.InstallationFailed.ToString(), + ShortOverview = string.Format(_localization.GetLocalizedString("VersionNumber"), installationInfo.Version), + Overview = e.Exception.Message }); } @@ -424,11 +453,11 @@ namespace Emby.Server.Implementations.Activity { var vals = new List(); - if (!string.IsNullOrWhiteSpace(e.Result.ErrorMessage)) + if (!string.IsNullOrEmpty(e.Result.ErrorMessage)) { vals.Add(e.Result.ErrorMessage); } - if (!string.IsNullOrWhiteSpace(e.Result.LongErrorMessage)) + if (!string.IsNullOrEmpty(e.Result.LongErrorMessage)) { vals.Add(e.Result.LongErrorMessage); } @@ -436,7 +465,7 @@ namespace Emby.Server.Implementations.Activity CreateLogEntry(new ActivityLogEntry { Name = string.Format(_localization.GetLocalizedString("ScheduledTaskFailedWithName"), task.Name), - Type = "ScheduledTaskFailed", + Type = NotificationType.TaskFailed.ToString(), Overview = string.Join(Environment.NewLine, vals.ToArray(vals.Count)), ShortOverview = runningTime, Severity = LogSeverity.Error @@ -458,15 +487,12 @@ namespace Emby.Server.Implementations.Activity public void Dispose() { - _taskManager.TaskExecuting -= _taskManager_TaskExecuting; _taskManager.TaskCompleted -= _taskManager_TaskCompleted; _installationManager.PluginInstalled -= _installationManager_PluginInstalled; _installationManager.PluginUninstalled -= _installationManager_PluginUninstalled; _installationManager.PluginUpdated -= _installationManager_PluginUpdated; - - _libraryManager.ItemAdded -= _libraryManager_ItemAdded; - _libraryManager.ItemRemoved -= _libraryManager_ItemRemoved; + _installationManager.PackageInstallationFailed -= _installationManager_PackageInstallationFailed; _sessionManager.SessionStarted -= _sessionManager_SessionStarted; _sessionManager.AuthenticationFailed -= _sessionManager_AuthenticationFailed; @@ -482,16 +508,15 @@ namespace Emby.Server.Implementations.Activity _userManager.UserCreated -= _userManager_UserCreated; _userManager.UserPasswordChanged -= _userManager_UserPasswordChanged; _userManager.UserDeleted -= _userManager_UserDeleted; - _userManager.UserConfigurationUpdated -= _userManager_UserConfigurationUpdated; + _userManager.UserPolicyUpdated -= _userManager_UserPolicyUpdated; _userManager.UserLockedOut -= _userManager_UserLockedOut; _config.ConfigurationUpdated -= _config_ConfigurationUpdated; _config.NamedConfigurationUpdated -= _config_NamedConfigurationUpdated; - //_logManager.LoggerLoaded -= _logManager_LoggerLoaded; + _deviceManager.CameraImageUploaded -= _deviceManager_CameraImageUploaded; _appHost.ApplicationUpdated -= _appHost_ApplicationUpdated; - GC.SuppressFinalize(this); } /// diff --git a/Emby.Server.Implementations/Activity/ActivityManager.cs b/Emby.Server.Implementations/Activity/ActivityManager.cs index 9a3f1ae472..047bebf231 100644 --- a/Emby.Server.Implementations/Activity/ActivityManager.cs +++ b/Emby.Server.Implementations/Activity/ActivityManager.cs @@ -6,7 +6,6 @@ using MediaBrowser.Model.Logging; using MediaBrowser.Model.Querying; using System; using System.Linq; -using System.Threading.Tasks; namespace Emby.Server.Implementations.Activity { @@ -27,7 +26,6 @@ namespace Emby.Server.Implementations.Activity public void Create(ActivityLogEntry entry) { - entry.Id = Guid.NewGuid().ToString("N"); entry.Date = DateTime.UtcNow; _repo.Create(entry); @@ -35,11 +33,11 @@ namespace Emby.Server.Implementations.Activity EventHelper.FireEventIfNotNull(EntryCreated, this, new GenericEventArgs(entry), _logger); } - public QueryResult GetActivityLogEntries(DateTime? minDate, int? startIndex, int? limit) + public QueryResult GetActivityLogEntries(DateTime? minDate, bool? hasUserId, int? startIndex, int? limit) { - var result = _repo.GetActivityLogEntries(minDate, startIndex, limit); + var result = _repo.GetActivityLogEntries(minDate, hasUserId, startIndex, limit); - foreach (var item in result.Items.Where(i => !string.IsNullOrWhiteSpace(i.UserId))) + foreach (var item in result.Items.Where(i => !i.UserId.Equals(Guid.Empty))) { var user = _userManager.GetUserById(item.UserId); @@ -52,5 +50,10 @@ namespace Emby.Server.Implementations.Activity return result; } + + public QueryResult GetActivityLogEntries(DateTime? minDate, int? startIndex, int? limit) + { + return GetActivityLogEntries(minDate, null, startIndex, limit); + } } } diff --git a/Emby.Server.Implementations/Activity/ActivityRepository.cs b/Emby.Server.Implementations/Activity/ActivityRepository.cs index 6293cc69fa..ce9f460ffb 100644 --- a/Emby.Server.Implementations/Activity/ActivityRepository.cs +++ b/Emby.Server.Implementations/Activity/ActivityRepository.cs @@ -48,20 +48,76 @@ namespace Emby.Server.Implementations.Activity { RunDefaultInitialization(connection); - string[] queries = { - "create table if not exists ActivityLogEntries (Id GUID PRIMARY KEY NOT NULL, Name TEXT NOT NULL, Overview TEXT, ShortOverview TEXT, Type TEXT NOT NULL, ItemId TEXT, UserId TEXT, DateCreated DATETIME NOT NULL, LogSeverity TEXT NOT NULL)", - "create index if not exists idx_ActivityLogEntries on ActivityLogEntries(Id)" - }; + connection.RunQueries(new[] + { + "create table if not exists ActivityLog (Id INTEGER PRIMARY KEY, Name TEXT NOT NULL, Overview TEXT, ShortOverview TEXT, Type TEXT NOT NULL, ItemId TEXT, UserId TEXT, DateCreated DATETIME NOT NULL, LogSeverity TEXT NOT NULL)", + "drop index if exists idx_ActivityLogEntries" + }); - connection.RunQueries(queries); + TryMigrate(connection); } } - private const string BaseActivitySelectText = "select Id, Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity from ActivityLogEntries"; + private void TryMigrate(ManagedConnection connection) + { + try + { + if (TableExists(connection, "ActivityLogEntries")) + { + connection.RunQueries(new[] + { + "INSERT INTO ActivityLog (Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity) SELECT Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity FROM ActivityLogEntries", + "drop table if exists ActivityLogEntries" + }); + } + } + catch (Exception ex) + { + Logger.ErrorException("Error migrating activity log database", ex); + } + } + + private const string BaseActivitySelectText = "select Id, Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity from ActivityLog"; public void Create(ActivityLogEntry entry) { - Update(entry); + if (entry == null) + { + throw new ArgumentNullException("entry"); + } + + using (WriteLock.Write()) + { + using (var connection = CreateConnection()) + { + connection.RunInTransaction(db => + { + using (var statement = db.PrepareStatement("insert into ActivityLog (Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity) values (@Name, @Overview, @ShortOverview, @Type, @ItemId, @UserId, @DateCreated, @LogSeverity)")) + { + statement.TryBind("@Name", entry.Name); + + statement.TryBind("@Overview", entry.Overview); + statement.TryBind("@ShortOverview", entry.ShortOverview); + statement.TryBind("@Type", entry.Type); + statement.TryBind("@ItemId", entry.ItemId); + + if (entry.UserId.Equals(Guid.Empty)) + { + statement.TryBindNull("@UserId"); + } + else + { + statement.TryBind("@UserId", entry.UserId.ToString("N")); + } + + statement.TryBind("@DateCreated", entry.Date.ToDateTimeParamValue()); + statement.TryBind("@LogSeverity", entry.Severity.ToString()); + + statement.MoveNext(); + } + }, TransactionMode); + } + } } public void Update(ActivityLogEntry entry) @@ -77,16 +133,25 @@ namespace Emby.Server.Implementations.Activity { connection.RunInTransaction(db => { - using (var statement = db.PrepareStatement("replace into ActivityLogEntries (Id, Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity) values (@Id, @Name, @Overview, @ShortOverview, @Type, @ItemId, @UserId, @DateCreated, @LogSeverity)")) + using (var statement = db.PrepareStatement("Update ActivityLog set Name=@Name,Overview=@Overview,ShortOverview=@ShortOverview,Type=@Type,ItemId=@ItemId,UserId=@UserId,DateCreated=@DateCreated,LogSeverity=@LogSeverity where Id=@Id")) { - statement.TryBind("@Id", entry.Id.ToGuidBlob()); - statement.TryBind("@Name", entry.Name); + statement.TryBind("@Id", entry.Id); + statement.TryBind("@Name", entry.Name); statement.TryBind("@Overview", entry.Overview); statement.TryBind("@ShortOverview", entry.ShortOverview); statement.TryBind("@Type", entry.Type); statement.TryBind("@ItemId", entry.ItemId); - statement.TryBind("@UserId", entry.UserId); + + if (entry.UserId.Equals(Guid.Empty)) + { + statement.TryBindNull("@UserId"); + } + else + { + statement.TryBind("@UserId", entry.UserId.ToString("N")); + } + statement.TryBind("@DateCreated", entry.Date.ToDateTimeParamValue()); statement.TryBind("@LogSeverity", entry.Severity.ToString()); @@ -97,7 +162,7 @@ namespace Emby.Server.Implementations.Activity } } - public QueryResult GetActivityLogEntries(DateTime? minDate, int? startIndex, int? limit) + public QueryResult GetActivityLogEntries(DateTime? minDate, bool? hasUserId, int? startIndex, int? limit) { using (WriteLock.Read()) { @@ -110,6 +175,17 @@ namespace Emby.Server.Implementations.Activity { whereClauses.Add("DateCreated>=@DateCreated"); } + if (hasUserId.HasValue) + { + if (hasUserId.Value) + { + whereClauses.Add("UserId not null"); + } + else + { + whereClauses.Add("UserId is null"); + } + } var whereTextWithoutPaging = whereClauses.Count == 0 ? string.Empty : @@ -121,7 +197,7 @@ namespace Emby.Server.Implementations.Activity string.Empty : " where " + string.Join(" AND ", whereClauses.ToArray(whereClauses.Count)); - whereClauses.Add(string.Format("Id NOT IN (SELECT Id FROM ActivityLogEntries {0} ORDER BY DateCreated DESC LIMIT {1})", + whereClauses.Add(string.Format("Id NOT IN (SELECT Id FROM ActivityLog {0} ORDER BY DateCreated DESC LIMIT {1})", pagingWhereText, startIndex.Value.ToString(_usCulture))); } @@ -141,7 +217,7 @@ namespace Emby.Server.Implementations.Activity var statementTexts = new List(); statementTexts.Add(commandText); - statementTexts.Add("select count (Id) from ActivityLogEntries" + whereTextWithoutPaging); + statementTexts.Add("select count (Id) from ActivityLog" + whereTextWithoutPaging); return connection.RunInTransaction(db => { @@ -187,7 +263,7 @@ namespace Emby.Server.Implementations.Activity var info = new ActivityLogEntry { - Id = reader[index].ReadGuidFromBlob().ToString("N") + Id = reader[index].ToInt64() }; index++; @@ -223,7 +299,7 @@ namespace Emby.Server.Implementations.Activity index++; if (reader[index].SQLiteType != SQLiteType.Null) { - info.UserId = reader[index].ToString(); + info.UserId = new Guid(reader[index].ToString()); } index++; diff --git a/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs b/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs index 1e63aa1a6d..52e421374b 100644 --- a/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs +++ b/Emby.Server.Implementations/AppBase/BaseApplicationPaths.cs @@ -49,6 +49,15 @@ namespace Emby.Server.Implementations.AppBase } } + private const string _virtualDataPath = "%AppDataPath%"; + public string VirtualDataPath + { + get + { + return _virtualDataPath; + } + } + /// /// Gets the image cache path. /// diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 26450c06ca..3208c6a1f8 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -1,12 +1,9 @@ using Emby.Common.Implementations.Serialization; +using Emby.Drawing; +using Emby.Photos; using Emby.Dlna; -using Emby.Dlna.ConnectionManager; -using Emby.Dlna.ContentDirectory; using Emby.Dlna.Main; -using Emby.Dlna.MediaReceiverRegistrar; using Emby.Dlna.Ssdp; -using Emby.Drawing; -using Emby.Photos; using Emby.Server.Implementations.Activity; using Emby.Server.Implementations.Archiving; using Emby.Server.Implementations.Channels; @@ -26,14 +23,13 @@ using Emby.Server.Implementations.LiveTv; using Emby.Server.Implementations.Localization; using Emby.Server.Implementations.MediaEncoder; using Emby.Server.Implementations.Net; -using Emby.Server.Implementations.Notifications; +using Emby.Notifications; using Emby.Server.Implementations.Playlists; using Emby.Server.Implementations.Reflection; using Emby.Server.Implementations.ScheduledTasks; using Emby.Server.Implementations.Security; using Emby.Server.Implementations.Serialization; using Emby.Server.Implementations.Session; -using Emby.Server.Implementations.Social; using Emby.Server.Implementations.Threading; using Emby.Server.Implementations.TV; using Emby.Server.Implementations.Updates; @@ -46,7 +42,7 @@ using MediaBrowser.Common.Events; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Common.Plugins; -using MediaBrowser.Common.Progress; +using MediaBrowser.Model.Extensions; using MediaBrowser.Common.Security; using MediaBrowser.Common.Updates; using MediaBrowser.Controller; @@ -60,6 +56,7 @@ using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.MediaEncoding; @@ -74,7 +71,6 @@ using MediaBrowser.Controller.Security; using MediaBrowser.Controller.Session; using MediaBrowser.Controller.Sorting; using MediaBrowser.Controller.Subtitles; -using MediaBrowser.Controller.Sync; using MediaBrowser.Controller.TV; using MediaBrowser.LocalMetadata.Savers; using MediaBrowser.MediaEncoding.BdInfo; @@ -93,7 +89,6 @@ using MediaBrowser.Model.News; using MediaBrowser.Model.Reflection; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Services; -using MediaBrowser.Model.Social; using MediaBrowser.Model.System; using MediaBrowser.Model.Tasks; using MediaBrowser.Model.Text; @@ -105,7 +100,6 @@ using MediaBrowser.Providers.Manager; using MediaBrowser.Providers.Subtitles; using MediaBrowser.WebDashboard.Api; using MediaBrowser.XbmcMetadata.Providers; -using OpenSubtitlesHandler; using ServiceStack; using System; using System.Collections.Concurrent; @@ -122,13 +116,16 @@ using System.Threading; using System.Threading.Tasks; using StringExtensions = MediaBrowser.Controller.Extensions.StringExtensions; using X509Certificate = System.Security.Cryptography.X509Certificates.X509Certificate; +using MediaBrowser.Controller.Authentication; +using System.Diagnostics; +using ServiceStack.Text.Jsv; namespace Emby.Server.Implementations { /// /// Class CompositionRoot /// - public abstract class ApplicationHost : IServerApplicationHost, IDependencyContainer, IDisposable + public abstract class ApplicationHost : IServerApplicationHost, IDisposable { /// /// Gets a value indicating whether this instance can self restart. @@ -218,29 +215,17 @@ namespace Emby.Server.Implementations /// The application paths. protected ServerApplicationPaths ApplicationPaths { get; set; } - /// - /// Gets assemblies that failed to load - /// - /// The failed assemblies. - public List FailedAssemblies { get; protected set; } - /// /// Gets all concrete types. /// /// All concrete types. - public Type[] AllConcreteTypes { get; protected set; } + public Tuple[] AllConcreteTypes { get; protected set; } /// /// The disposable parts /// protected readonly List DisposableParts = new List(); - /// - /// Gets a value indicating whether this instance is first run. - /// - /// true if this instance is first run; otherwise, false. - public bool IsFirstRun { get; private set; } - /// /// Gets the configuration manager. /// @@ -276,7 +261,6 @@ namespace Emby.Server.Implementations protected readonly SimpleInjector.Container Container = new SimpleInjector.Container(); protected ISystemEvents SystemEvents { get; set; } - protected IMemoryStreamFactory MemoryStreamFactory { get; set; } /// /// Gets the server configuration manager. @@ -296,11 +280,11 @@ namespace Emby.Server.Implementations return new ServerConfigurationManager(ApplicationPaths, LogManager, XmlSerializer, FileSystemManager); } - /// - /// Gets or sets the server manager. - /// - /// The server manager. - private IServerManager ServerManager { get; set; } + protected virtual IResourceFileManager CreateResourceFileManager() + { + return new ResourceFileManager(HttpResultFactory, LogManager.GetLogger("ResourceManager"), FileSystemManager); + } + /// /// Gets or sets the user manager. /// @@ -345,7 +329,7 @@ namespace Emby.Server.Implementations private IEncodingManager EncodingManager { get; set; } private IChannelManager ChannelManager { get; set; } - private ISyncManager SyncManager { get; set; } + protected ITextEncoding TextEncoding { get; private set; } /// /// Gets or sets the user data repository. @@ -355,7 +339,6 @@ namespace Emby.Server.Implementations private IUserRepository UserRepository { get; set; } internal IDisplayPreferencesRepository DisplayPreferencesRepository { get; set; } internal IItemRepository ItemRepository { get; set; } - private INotificationsRepository NotificationsRepository { get; set; } private INotificationManager NotificationManager { get; set; } private ISubtitleManager SubtitleManager { get; set; } @@ -386,7 +369,7 @@ namespace Emby.Server.Implementations /// /// The zip client. protected IZipClient ZipClient { get; private set; } - + protected IHttpResultFactory HttpResultFactory { get; private set; } protected IAuthService AuthService { get; private set; } public StartupOptions StartupOptions { get; private set; } @@ -428,11 +411,9 @@ namespace Emby.Server.Implementations XmlSerializer = new MyXmlSerializer(fileSystem, logManager.GetLogger("XmlSerializer")); NetworkManager = networkManager; + networkManager.LocalSubnetsFn = GetConfiguredLocalSubnets; EnvironmentInfo = environmentInfo; SystemEvents = systemEvents; - MemoryStreamFactory = new MemoryStreamProvider(); - - FailedAssemblies = new List(); ApplicationPaths = applicationPaths; LogManager = logManager; @@ -456,6 +437,27 @@ namespace Emby.Server.Implementations NetworkManager.NetworkChanged += NetworkManager_NetworkChanged; } + public string ExpandVirtualPath(string path) + { + var appPaths = ApplicationPaths; + + return path.Replace(appPaths.VirtualDataPath, appPaths.DataPath, StringComparison.OrdinalIgnoreCase) + .Replace(appPaths.VirtualInternalMetadataPath, appPaths.InternalMetadataPath, StringComparison.OrdinalIgnoreCase); + } + + public string ReverseVirtualPath(string path) + { + var appPaths = ApplicationPaths; + + return path.Replace(appPaths.DataPath, appPaths.VirtualDataPath, StringComparison.OrdinalIgnoreCase) + .Replace(appPaths.InternalMetadataPath, appPaths.VirtualInternalMetadataPath, StringComparison.OrdinalIgnoreCase); + } + + private string[] GetConfiguredLocalSubnets() + { + return ServerConfigurationManager.Configuration.LocalNetworkSubnets; + } + private void NetworkManager_NetworkChanged(object sender, EventArgs e) { _validAddressResults.Clear(); @@ -470,7 +472,7 @@ namespace Emby.Server.Implementations { get { - return _version ?? (_version = GetAssembly(GetType()).GetName().Version); + return _version ?? (_version = GetType().GetTypeInfo().Assembly.GetName().Version); } } @@ -500,9 +502,17 @@ namespace Emby.Server.Implementations } } - private Assembly GetAssembly(Type type) + private Tuple GetAssembly(Type type) { - return type.GetTypeInfo().Assembly; + var assembly = type.GetTypeInfo().Assembly; + string path = null; + + return new Tuple(assembly, path); + } + + public virtual IStreamHelper CreateStreamHelper() + { + return new StreamHelper(); } public virtual bool SupportsAutoRunAtStartup @@ -520,16 +530,7 @@ namespace Emby.Server.Implementations /// System.Object. public object CreateInstance(Type type) { - try - { - return Container.GetInstance(type); - } - catch (Exception ex) - { - Logger.ErrorException("Error creating {0}", ex, type.FullName); - - throw; - } + return Container.GetInstance(type); } /// @@ -537,8 +538,10 @@ namespace Emby.Server.Implementations /// /// The type. /// System.Object. - protected object CreateInstanceSafe(Type type) + protected object CreateInstanceSafe(Tuple typeInfo) { + var type = typeInfo.Item1; + try { return Container.GetInstance(type); @@ -615,15 +618,16 @@ namespace Emby.Server.Implementations /// /// The file. /// Assembly. - protected Assembly LoadAssembly(string file) + protected Tuple LoadAssembly(string file) { try { - return Assembly.Load(File.ReadAllBytes(file)); + var assembly = Assembly.Load(File.ReadAllBytes(file)); + + return new Tuple(assembly, file); } catch (Exception ex) { - FailedAssemblies.Add(file); Logger.ErrorException("Error loading assembly {0}", ex, file); return null; } @@ -634,11 +638,11 @@ namespace Emby.Server.Implementations /// /// /// IEnumerable{Type}. - public IEnumerable GetExportTypes() + public IEnumerable> GetExportTypes() { var currentType = typeof(T); - return AllConcreteTypes.Where(currentType.IsAssignableFrom); + return AllConcreteTypes.Where(i => currentType.IsAssignableFrom(i.Item1)); } /// @@ -666,6 +670,33 @@ namespace Emby.Server.Implementations return parts; } + public List> GetExportsWithInfo(bool manageLiftime = true) + { + var parts = GetExportTypes() + .Select(i => + { + var obj = CreateInstanceSafe(i); + + if (obj == null) + { + return null; + } + return new Tuple((T)obj, i.Item2); + }) + .Where(i => i != null) + .ToList(); + + if (manageLiftime) + { + lock (DisposableParts) + { + DisposableParts.AddRange(parts.Select(i => i.Item1).OfType()); + } + } + + return parts; + } + private void SetBaseExceptionMessage() { var builder = GetBaseExceptionMessage(ApplicationPaths); @@ -687,25 +718,42 @@ namespace Emby.Server.Implementations ConfigurationManager.ConfigurationUpdated += OnConfigurationUpdated; - await MediaEncoder.Init().ConfigureAwait(false); + MediaEncoder.Init(); - if (string.IsNullOrWhiteSpace(MediaEncoder.EncoderPath)) - { - if (ServerConfigurationManager.Configuration.IsStartupWizardCompleted) - { - ServerConfigurationManager.Configuration.IsStartupWizardCompleted = false; - ServerConfigurationManager.SaveConfiguration(); - } - } + //if (string.IsNullOrWhiteSpace(MediaEncoder.EncoderPath)) + //{ + // if (ServerConfigurationManager.Configuration.IsStartupWizardCompleted) + // { + // ServerConfigurationManager.Configuration.IsStartupWizardCompleted = false; + // ServerConfigurationManager.SaveConfiguration(); + // } + //} Logger.Info("ServerId: {0}", SystemId); + + var entryPoints = GetExports().ToList(); + RunEntryPoints(entryPoints, true); + Logger.Info("Core startup complete"); HttpServer.GlobalResponse = null; Logger.Info("Post-init migrations complete"); - foreach (var entryPoint in GetExports().ToList()) + RunEntryPoints(entryPoints, false); + Logger.Info("All entry points have started"); + + LogManager.RemoveConsoleOutput(); + } + + private void RunEntryPoints(IEnumerable entryPoints, bool isBeforeStartup) + { + foreach (var entryPoint in entryPoints) { + if (isBeforeStartup != (entryPoint is IRunBeforeStartup)) + { + continue; + } + var name = entryPoint.GetType().FullName; Logger.Info("Starting entry point {0}", name); var now = DateTime.UtcNow; @@ -719,9 +767,6 @@ namespace Emby.Server.Implementations } Logger.Info("Entry point completed: {0}. Duration: {1} seconds", name, (DateTime.UtcNow - now).TotalSeconds.ToString(CultureInfo.InvariantCulture), "ImageInfos"); } - Logger.Info("All entry points have started"); - - LogManager.RemoveConsoleOutput(); } /// @@ -741,20 +786,10 @@ namespace Emby.Server.Implementations private IJsonSerializer CreateJsonSerializer() { - try - { - // https://github.com/ServiceStack/ServiceStack/blob/master/tests/ServiceStack.WebHost.IntegrationTests/Web.config#L4 - Licensing.RegisterLicense("1001-e1JlZjoxMDAxLE5hbWU6VGVzdCBCdXNpbmVzcyxUeXBlOkJ1c2luZXNzLEhhc2g6UHVNTVRPclhvT2ZIbjQ5MG5LZE1mUTd5RUMzQnBucTFEbTE3TDczVEF4QUNMT1FhNXJMOWkzVjFGL2ZkVTE3Q2pDNENqTkQyUktRWmhvUVBhYTBiekJGUUZ3ZE5aZHFDYm9hL3lydGlwUHI5K1JsaTBYbzNsUC85cjVJNHE5QVhldDN6QkE4aTlvdldrdTgyTk1relY2eis2dFFqTThYN2lmc0JveHgycFdjPSxFeHBpcnk6MjAxMy0wMS0wMX0="); - } - catch - { - // Failing under mono - } - return new JsonSerializer(FileSystemManager, LogManager.GetLogger("JsonSerializer")); } - public async Task Init(IProgress progress) + public void Init() { HttpPort = ServerConfigurationManager.Configuration.HttpServerPortNumber; HttpsPort = ServerConfigurationManager.Configuration.HttpsPortNumber; @@ -766,39 +801,22 @@ namespace Emby.Server.Implementations HttpsPort = ServerConfiguration.DefaultHttpsPort; } - progress.Report(1); - JsonSerializer = CreateJsonSerializer(); OnLoggerLoaded(true); LogManager.LoggerLoaded += (s, e) => OnLoggerLoaded(false); - IsFirstRun = !ConfigurationManager.CommonConfiguration.IsStartupWizardCompleted; - progress.Report(2); - LogManager.LogSeverity = ConfigurationManager.CommonConfiguration.EnableDebugLevelLogging ? LogSeverity.Debug : LogSeverity.Info; - progress.Report(3); - DiscoverTypes(); - progress.Report(14); SetHttpLimit(); - progress.Report(15); - - var innerProgress = new ActionableProgress(); - innerProgress.RegisterAction(p => progress.Report(.8 * p + 15)); - await RegisterResources(innerProgress).ConfigureAwait(false); + RegisterResources(); FindParts(); - progress.Report(95); - - await InstallIsoMounters(CancellationToken.None).ConfigureAwait(false); - - progress.Report(100); } protected virtual void OnLoggerLoaded(bool isFirstLoad) @@ -810,16 +828,6 @@ namespace Emby.Server.Implementations LogEnvironmentInfo(Logger, ApplicationPaths, false); } - // Put the app config in the log for troubleshooting purposes - var configJson = new StringBuilder(JsonSerializer.SerializeToString(ConfigurationManager.CommonConfiguration)); - - if (!string.IsNullOrWhiteSpace(ServerConfigurationManager.Configuration.CertificatePassword)) - { - configJson = configJson.Replace(ServerConfigurationManager.Configuration.CertificatePassword, "####"); - } - - Logger.LogMultiline("Application configuration:", LogSeverity.Info, configJson); - if (Plugins != null) { var pluginBuilder = new StringBuilder(); @@ -834,17 +842,18 @@ namespace Emby.Server.Implementations } protected abstract IConnectManager CreateConnectManager(); - protected abstract ISyncManager CreateSyncManager(); protected virtual IHttpClient CreateHttpClient() { - return new HttpClientManager.HttpClientManager(ApplicationPaths, LogManager.GetLogger("HttpClient"), FileSystemManager, MemoryStreamFactory, GetDefaultUserAgent); + return new HttpClientManager.HttpClientManager(ApplicationPaths, LogManager.GetLogger("HttpClient"), FileSystemManager, GetDefaultUserAgent); } + public static IStreamHelper StreamHelper { get; set; } + /// /// Registers resources that classes will depend on /// - protected async Task RegisterResources(IProgress progress) + protected void RegisterResources() { RegisterSingleInstance(ConfigurationManager); RegisterSingleInstance(this); @@ -852,7 +861,6 @@ namespace Emby.Server.Implementations RegisterSingleInstance(ApplicationPaths); RegisterSingleInstance(JsonSerializer); - RegisterSingleInstance(MemoryStreamFactory); RegisterSingleInstance(SystemEvents); RegisterSingleInstance(LogManager, false); @@ -881,6 +889,10 @@ namespace Emby.Server.Implementations TimerFactory = new TimerFactory(); RegisterSingleInstance(TimerFactory); + var streamHelper = CreateStreamHelper(); + ApplicationHost.StreamHelper = streamHelper; + RegisterSingleInstance(streamHelper); + RegisterSingleInstance(CryptographyProvider); SocketFactory = new SocketFactory(LogManager.GetLogger("SocketFactory")); @@ -891,13 +903,14 @@ namespace Emby.Server.Implementations SecurityManager = new PluginSecurityManager(this, HttpClient, JsonSerializer, ApplicationPaths, LogManager, FileSystemManager, CryptographyProvider); RegisterSingleInstance(SecurityManager); - InstallationManager = new InstallationManager(LogManager.GetLogger("InstallationManager"), this, ApplicationPaths, HttpClient, JsonSerializer, SecurityManager, ConfigurationManager, FileSystemManager, CryptographyProvider, PackageRuntime); + InstallationManager = new InstallationManager(LogManager.GetLogger("InstallationManager"), this, ApplicationPaths, HttpClient, JsonSerializer, SecurityManager, ServerConfigurationManager, FileSystemManager, CryptographyProvider, PackageRuntime); RegisterSingleInstance(InstallationManager); ZipClient = new ZipClient(FileSystemManager); RegisterSingleInstance(ZipClient); - RegisterSingleInstance(new HttpResultFactory(LogManager, FileSystemManager, JsonSerializer, MemoryStreamFactory)); + HttpResultFactory = new HttpResultFactory(LogManager, FileSystemManager, JsonSerializer, CreateBrotliCompressor()); + RegisterSingleInstance(HttpResultFactory); RegisterSingleInstance(this); RegisterSingleInstance(ApplicationPaths); @@ -911,26 +924,25 @@ namespace Emby.Server.Implementations StringExtensions.LocalizationManager = LocalizationManager; RegisterSingleInstance(LocalizationManager); - ITextEncoding textEncoding = new TextEncoding.TextEncoding(FileSystemManager, LogManager.GetLogger("TextEncoding"), JsonSerializer); - RegisterSingleInstance(textEncoding); - Utilities.EncodingHelper = textEncoding; - BlurayExaminer = new BdInfoExaminer(FileSystemManager, textEncoding); + TextEncoding = new TextEncoding.TextEncoding(FileSystemManager, LogManager.GetLogger("TextEncoding"), JsonSerializer); + RegisterSingleInstance(TextEncoding); + BlurayExaminer = new BdInfoExaminer(FileSystemManager, TextEncoding); RegisterSingleInstance(BlurayExaminer); RegisterSingleInstance(new XmlReaderSettingsFactory()); - UserDataManager = new UserDataManager(LogManager, ServerConfigurationManager); + UserDataManager = new UserDataManager(LogManager, ServerConfigurationManager, () => UserManager); RegisterSingleInstance(UserDataManager); UserRepository = GetUserRepository(); // This is only needed for disposal purposes. If removing this, make sure to have the manager handle disposing it RegisterSingleInstance(UserRepository); - var displayPreferencesRepo = new SqliteDisplayPreferencesRepository(LogManager.GetLogger("SqliteDisplayPreferencesRepository"), JsonSerializer, ApplicationPaths, MemoryStreamFactory, FileSystemManager); + var displayPreferencesRepo = new SqliteDisplayPreferencesRepository(LogManager.GetLogger("SqliteDisplayPreferencesRepository"), JsonSerializer, ApplicationPaths, FileSystemManager); DisplayPreferencesRepository = displayPreferencesRepo; RegisterSingleInstance(DisplayPreferencesRepository); - var itemRepo = new SqliteItemRepository(ServerConfigurationManager, JsonSerializer, LogManager.GetLogger("SqliteItemRepository"), MemoryStreamFactory, assemblyInfo, FileSystemManager, EnvironmentInfo, TimerFactory); + var itemRepo = new SqliteItemRepository(ServerConfigurationManager, this, JsonSerializer, LogManager.GetLogger("SqliteItemRepository"), assemblyInfo, FileSystemManager, EnvironmentInfo, TimerFactory); ItemRepository = itemRepo; RegisterSingleInstance(ItemRepository); @@ -940,7 +952,7 @@ namespace Emby.Server.Implementations UserManager = new UserManager(LogManager.GetLogger("UserManager"), ServerConfigurationManager, UserRepository, XmlSerializer, NetworkManager, () => ImageProcessor, () => DtoService, () => ConnectManager, this, JsonSerializer, FileSystemManager, CryptographyProvider); RegisterSingleInstance(UserManager); - LibraryManager = new LibraryManager(Logger, TaskManager, UserManager, ServerConfigurationManager, UserDataManager, () => LibraryMonitor, FileSystemManager, () => ProviderManager, () => UserViewManager); + LibraryManager = new LibraryManager(this, Logger, TaskManager, UserManager, ServerConfigurationManager, UserDataManager, () => LibraryMonitor, FileSystemManager, () => ProviderManager, () => UserViewManager); RegisterSingleInstance(LibraryManager); var musicManager = new MusicManager(LibraryManager); @@ -949,24 +961,23 @@ namespace Emby.Server.Implementations LibraryMonitor = new LibraryMonitor(LogManager, TaskManager, LibraryManager, ServerConfigurationManager, FileSystemManager, TimerFactory, SystemEvents, EnvironmentInfo); RegisterSingleInstance(LibraryMonitor); - ProviderManager = new ProviderManager(HttpClient, ServerConfigurationManager, LibraryMonitor, LogManager, FileSystemManager, ApplicationPaths, () => LibraryManager, JsonSerializer, MemoryStreamFactory); - RegisterSingleInstance(ProviderManager); - RegisterSingleInstance(() => new SearchEngine(LogManager, LibraryManager, UserManager)); CertificateInfo = GetCertificateInfo(true); Certificate = GetCertificate(CertificateInfo); - HttpServer = HttpServerFactory.CreateServer(this, LogManager, ServerConfigurationManager, NetworkManager, MemoryStreamFactory, "Emby", "web/index.html", textEncoding, SocketFactory, CryptographyProvider, JsonSerializer, XmlSerializer, EnvironmentInfo, Certificate, FileSystemManager, SupportsDualModeSockets); - HttpServer.GlobalResponse = LocalizationManager.GetLocalizedString("StartupEmbyServerIsLoading"); - RegisterSingleInstance(HttpServer, false); - progress.Report(10); - - ServerManager = new ServerManager.ServerManager(this, JsonSerializer, LogManager.GetLogger("ServerManager"), ServerConfigurationManager, MemoryStreamFactory, textEncoding); - RegisterSingleInstance(ServerManager); + HttpServer = new HttpListenerHost(this, + LogManager.GetLogger("HttpServer"), + ServerConfigurationManager, + "web/index.html", + NetworkManager, + TextEncoding, + JsonSerializer, + XmlSerializer, + GetParseFn); - var innerProgress = new ActionableProgress(); - innerProgress.RegisterAction(p => progress.Report((.75 * p) + 15)); + HttpServer.GlobalResponse = LocalizationManager.GetLocalizedString("StartupEmbyServerIsLoading"); + RegisterSingleInstance(HttpServer); ImageProcessor = GetImageProcessor(); RegisterSingleInstance(ImageProcessor); @@ -974,112 +985,101 @@ namespace Emby.Server.Implementations TVSeriesManager = new TVSeriesManager(UserManager, UserDataManager, LibraryManager, ServerConfigurationManager); RegisterSingleInstance(TVSeriesManager); - SyncManager = CreateSyncManager(); - RegisterSingleInstance(SyncManager); - - DtoService = new DtoService(LogManager.GetLogger("DtoService"), LibraryManager, UserDataManager, ItemRepository, ImageProcessor, ServerConfigurationManager, FileSystemManager, ProviderManager, () => ChannelManager, SyncManager, this, () => DeviceManager, () => MediaSourceManager, () => LiveTvManager); - RegisterSingleInstance(DtoService); - var encryptionManager = new EncryptionManager(); RegisterSingleInstance(encryptionManager); ConnectManager = CreateConnectManager(); RegisterSingleInstance(ConnectManager); - var deviceRepo = new SqliteDeviceRepository(LogManager.GetLogger("DeviceManager"), ServerConfigurationManager, FileSystemManager, JsonSerializer); - deviceRepo.Initialize(); - DeviceManager = new DeviceManager(deviceRepo, UserManager, FileSystemManager, LibraryMonitor, ServerConfigurationManager, LogManager.GetLogger("DeviceManager"), NetworkManager); - RegisterSingleInstance(deviceRepo); + DeviceManager = new DeviceManager(AuthenticationRepository, JsonSerializer, LibraryManager, LocalizationManager, UserManager, FileSystemManager, LibraryMonitor, ServerConfigurationManager, LogManager.GetLogger("DeviceManager"), NetworkManager); RegisterSingleInstance(DeviceManager); var newsService = new Emby.Server.Implementations.News.NewsService(ApplicationPaths, JsonSerializer); RegisterSingleInstance(newsService); - progress.Report(15); + MediaSourceManager = new MediaSourceManager(ItemRepository, ApplicationPaths, LocalizationManager, UserManager, LibraryManager, LogManager.GetLogger("MediaSourceManager"), JsonSerializer, FileSystemManager, UserDataManager, TimerFactory, () => MediaEncoder); + RegisterSingleInstance(MediaSourceManager); + + SubtitleManager = new SubtitleManager(LogManager.GetLogger("SubtitleManager"), FileSystemManager, LibraryMonitor, MediaSourceManager, ServerConfigurationManager, LocalizationManager); + RegisterSingleInstance(SubtitleManager); + + ProviderManager = new ProviderManager(HttpClient, SubtitleManager, ServerConfigurationManager, LibraryMonitor, LogManager, FileSystemManager, ApplicationPaths, () => LibraryManager, JsonSerializer); + RegisterSingleInstance(ProviderManager); + + DtoService = new DtoService(LogManager.GetLogger("DtoService"), LibraryManager, UserDataManager, ItemRepository, ImageProcessor, ServerConfigurationManager, FileSystemManager, ProviderManager, () => ChannelManager, this, () => DeviceManager, () => MediaSourceManager, () => LiveTvManager); + RegisterSingleInstance(DtoService); ChannelManager = new ChannelManager(UserManager, DtoService, LibraryManager, LogManager.GetLogger("ChannelManager"), ServerConfigurationManager, FileSystemManager, UserDataManager, JsonSerializer, LocalizationManager, HttpClient, ProviderManager); RegisterSingleInstance(ChannelManager); - MediaSourceManager = new MediaSourceManager(ItemRepository, UserManager, LibraryManager, LogManager.GetLogger("MediaSourceManager"), JsonSerializer, FileSystemManager, UserDataManager, TimerFactory); - RegisterSingleInstance(MediaSourceManager); - SessionManager = new SessionManager(UserDataManager, LogManager.GetLogger("SessionManager"), LibraryManager, UserManager, musicManager, DtoService, ImageProcessor, JsonSerializer, this, HttpClient, AuthenticationRepository, DeviceManager, MediaSourceManager, TimerFactory); RegisterSingleInstance(SessionManager); var dlnaManager = new DlnaManager(XmlSerializer, FileSystemManager, ApplicationPaths, LogManager.GetLogger("Dlna"), JsonSerializer, this, assemblyInfo); RegisterSingleInstance(dlnaManager); - var connectionManager = new ConnectionManager(dlnaManager, ServerConfigurationManager, LogManager.GetLogger("UpnpConnectionManager"), HttpClient, new XmlReaderSettingsFactory()); - RegisterSingleInstance(connectionManager); - - CollectionManager = new CollectionManager(LibraryManager, FileSystemManager, LibraryMonitor, LogManager.GetLogger("CollectionManager"), ProviderManager); + CollectionManager = new CollectionManager(LibraryManager, ApplicationPaths, LocalizationManager, FileSystemManager, LibraryMonitor, LogManager.GetLogger("CollectionManager"), ProviderManager); RegisterSingleInstance(CollectionManager); PlaylistManager = new PlaylistManager(LibraryManager, FileSystemManager, LibraryMonitor, LogManager.GetLogger("PlaylistManager"), UserManager, ProviderManager); RegisterSingleInstance(PlaylistManager); - LiveTvManager = new LiveTvManager(this, ServerConfigurationManager, Logger, ItemRepository, ImageProcessor, UserDataManager, DtoService, UserManager, LibraryManager, TaskManager, LocalizationManager, JsonSerializer, ProviderManager, FileSystemManager, SecurityManager); + LiveTvManager = new LiveTvManager(this, HttpClient, ServerConfigurationManager, Logger, ItemRepository, ImageProcessor, UserDataManager, DtoService, UserManager, LibraryManager, TaskManager, LocalizationManager, JsonSerializer, ProviderManager, FileSystemManager, SecurityManager, () => ChannelManager); RegisterSingleInstance(LiveTvManager); UserViewManager = new UserViewManager(LibraryManager, LocalizationManager, UserManager, ChannelManager, LiveTvManager, ServerConfigurationManager); RegisterSingleInstance(UserViewManager); - var contentDirectory = new ContentDirectory(dlnaManager, UserDataManager, ImageProcessor, LibraryManager, ServerConfigurationManager, UserManager, LogManager.GetLogger("UpnpContentDirectory"), HttpClient, LocalizationManager, ChannelManager, MediaSourceManager, UserViewManager, () => MediaEncoder, new XmlReaderSettingsFactory(), TVSeriesManager); - RegisterSingleInstance(contentDirectory); - - var mediaRegistrar = new MediaReceiverRegistrar(LogManager.GetLogger("MediaReceiverRegistrar"), HttpClient, ServerConfigurationManager, new XmlReaderSettingsFactory()); - RegisterSingleInstance(mediaRegistrar); - NotificationManager = new NotificationManager(LogManager, UserManager, ServerConfigurationManager); RegisterSingleInstance(NotificationManager); - SubtitleManager = new SubtitleManager(LogManager.GetLogger("SubtitleManager"), FileSystemManager, LibraryMonitor, LibraryManager, MediaSourceManager, ServerConfigurationManager); - RegisterSingleInstance(SubtitleManager); - RegisterSingleInstance(new DeviceDiscovery(LogManager.GetLogger("IDeviceDiscovery"), ServerConfigurationManager, SocketFactory, TimerFactory)); ChapterManager = new ChapterManager(LibraryManager, LogManager.GetLogger("ChapterManager"), ServerConfigurationManager, ItemRepository); RegisterSingleInstance(ChapterManager); - await RegisterMediaEncoder(innerProgress).ConfigureAwait(false); - progress.Report(90); + RegisterMediaEncoder(assemblyInfo); EncodingManager = new EncodingManager(FileSystemManager, Logger, MediaEncoder, ChapterManager, LibraryManager); RegisterSingleInstance(EncodingManager); - var sharingRepo = new SharingRepository(LogManager.GetLogger("SharingRepository"), ApplicationPaths, FileSystemManager); - sharingRepo.Initialize(); - // This is only needed for disposal purposes. If removing this, make sure to have the manager handle disposing it - RegisterSingleInstance(sharingRepo); - RegisterSingleInstance(new SharingManager(sharingRepo, ServerConfigurationManager, LibraryManager, this)); - var activityLogRepo = GetActivityLogRepository(); RegisterSingleInstance(activityLogRepo); RegisterSingleInstance(new ActivityManager(LogManager.GetLogger("ActivityManager"), activityLogRepo, UserManager)); - var authContext = new AuthorizationContext(AuthenticationRepository, ConnectManager); + var authContext = new AuthorizationContext(AuthenticationRepository, ConnectManager, UserManager); RegisterSingleInstance(authContext); RegisterSingleInstance(new SessionContext(UserManager, authContext, SessionManager)); - AuthService = new AuthService(UserManager, authContext, ServerConfigurationManager, ConnectManager, SessionManager, DeviceManager); + AuthService = new AuthService(UserManager, authContext, ServerConfigurationManager, ConnectManager, SessionManager, NetworkManager); RegisterSingleInstance(AuthService); - SubtitleEncoder = new SubtitleEncoder(LibraryManager, LogManager.GetLogger("SubtitleEncoder"), ApplicationPaths, FileSystemManager, MediaEncoder, JsonSerializer, HttpClient, MediaSourceManager, MemoryStreamFactory, ProcessFactory, textEncoding); + SubtitleEncoder = new SubtitleEncoder(LibraryManager, LogManager.GetLogger("SubtitleEncoder"), ApplicationPaths, FileSystemManager, MediaEncoder, JsonSerializer, HttpClient, MediaSourceManager, ProcessFactory, TextEncoding); RegisterSingleInstance(SubtitleEncoder); + RegisterSingleInstance(CreateResourceFileManager()); + displayPreferencesRepo.Initialize(); var userDataRepo = new SqliteUserDataRepository(LogManager.GetLogger("SqliteUserDataRepository"), ApplicationPaths, FileSystemManager); + SetStaticProperties(); + + ((UserManager)UserManager).Initialize(); + ((UserDataManager)UserDataManager).Repository = userDataRepo; - itemRepo.Initialize(userDataRepo); + itemRepo.Initialize(userDataRepo, UserManager); ((LibraryManager)LibraryManager).ItemRepository = ItemRepository; - ConfigureNotificationsRepository(); - progress.Report(100); + } - SetStaticProperties(); + protected virtual IBrotliCompressor CreateBrotliCompressor() + { + return null; + } - ((UserManager)UserManager).Initialize(); + private static Func GetParseFn(Type propertyType) + { + return s => JsvReader.GetParseFn(propertyType)(s); } public virtual string PackageRuntime @@ -1099,7 +1099,13 @@ namespace Emby.Server.Implementations { var builder = new StringBuilder(); - builder.AppendLine(string.Format("Command line: {0}", string.Join(" ", Environment.GetCommandLineArgs()))); + // Distinct these to prevent users from reporting problems that aren't actually problems + var commandLineArgs = Environment + .GetCommandLineArgs() + .Distinct() + .ToArray(); + + builder.AppendLine(string.Format("Command line: {0}", string.Join(" ", commandLineArgs))); builder.AppendLine(string.Format("Operating system: {0}", Environment.OSVersion)); builder.AppendLine(string.Format("64-Bit OS: {0}", Environment.Is64BitOperatingSystem)); @@ -1136,37 +1142,6 @@ namespace Emby.Server.Implementations } } - /// - /// Installs the iso mounters. - /// - /// The cancellation token. - /// Task. - private async Task InstallIsoMounters(CancellationToken cancellationToken) - { - var list = new List(); - - foreach (var isoMounter in GetExports()) - { - try - { - if (isoMounter.RequiresInstallation && !isoMounter.IsInstalled) - { - Logger.Info("Installing {0}", isoMounter.Name); - - await isoMounter.Install(cancellationToken).ConfigureAwait(false); - } - - list.Add(isoMounter); - } - catch (Exception ex) - { - Logger.ErrorException("{0} failed to load.", ex, isoMounter.Name); - } - } - - IsoManager.AddParts(list); - } - protected string GetDefaultUserAgent() { var name = FormatAttribute(Name); @@ -1222,7 +1197,7 @@ namespace Emby.Server.Implementations //localCert.PrivateKey = PrivateKey.CreateFromFile(pvk_file).RSA; if (!localCert.HasPrivateKey) { - //throw new FileNotFoundException("Secure requested, no private key included", certificateLocation); + Logger.Error("No private key included in SSL cert {0}.", certificateLocation); return null; } @@ -1255,7 +1230,6 @@ namespace Emby.Server.Implementations info.FFProbeFilename = "ffprobe"; info.ArchiveType = "7z"; info.Version = "20170308"; - info.DownloadUrls = GetLinuxDownloadUrls(); } else if (EnvironmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows) { @@ -1263,7 +1237,6 @@ namespace Emby.Server.Implementations info.FFProbeFilename = "ffprobe.exe"; info.Version = "20170308"; info.ArchiveType = "7z"; - info.DownloadUrls = GetWindowsDownloadUrls(); } else if (EnvironmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.OSX) { @@ -1271,80 +1244,27 @@ namespace Emby.Server.Implementations info.FFProbeFilename = "ffprobe"; info.ArchiveType = "7z"; info.Version = "20170308"; - info.DownloadUrls = GetMacDownloadUrls(); - } - else - { - // No version available - user requirement - info.DownloadUrls = new string[] { }; } return info; } - private string[] GetMacDownloadUrls() - { - switch (EnvironmentInfo.SystemArchitecture) - { - case MediaBrowser.Model.System.Architecture.X64: - return new[] - { - "https://embydata.com/downloads/ffmpeg/osx/ffmpeg-x64-20170308.7z" - }; - } - - return new string[] { }; - } - - private string[] GetWindowsDownloadUrls() - { - switch (EnvironmentInfo.SystemArchitecture) - { - case MediaBrowser.Model.System.Architecture.X64: - return new[] - { - "https://embydata.com/downloads/ffmpeg/windows/ffmpeg-20170308-win64.7z" - }; - case MediaBrowser.Model.System.Architecture.X86: - return new[] - { - "https://embydata.com/downloads/ffmpeg/windows/ffmpeg-20170308-win32.7z" - }; - } - - return new string[] { }; - } - - private string[] GetLinuxDownloadUrls() + protected virtual FFMpegInfo GetFFMpegInfo() { - switch (EnvironmentInfo.SystemArchitecture) - { - case MediaBrowser.Model.System.Architecture.X64: - return new[] - { - "https://embydata.com/downloads/ffmpeg/linux/ffmpeg-git-20170301-64bit-static.7z" - }; - case MediaBrowser.Model.System.Architecture.X86: - return new[] - { - "https://embydata.com/downloads/ffmpeg/linux/ffmpeg-git-20170301-32bit-static.7z" - }; - } - - return new string[] { }; + return new FFMpegLoader(Logger, ApplicationPaths, HttpClient, ZipClient, FileSystemManager, GetFfmpegInstallInfo()) + .GetFFMpegInfo(StartupOptions); } /// /// Registers the media encoder. /// /// Task. - private async Task RegisterMediaEncoder(IProgress progress) + private void RegisterMediaEncoder(IAssemblyInfo assemblyInfo) { string encoderPath = null; string probePath = null; - var info = await new FFMpegLoader(Logger, ApplicationPaths, HttpClient, ZipClient, FileSystemManager, GetFfmpegInstallInfo()) - .GetFFMpegInfo(StartupOptions, progress).ConfigureAwait(false); + var info = GetFFMpegInfo(); encoderPath = info.EncoderPath; probePath = info.ProbePath; @@ -1366,12 +1286,11 @@ namespace Emby.Server.Implementations () => MediaSourceManager, HttpClient, ZipClient, - MemoryStreamFactory, ProcessFactory, - (Environment.ProcessorCount > 2 ? 14000 : 40000), - EnvironmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows, EnvironmentInfo, - BlurayExaminer); + BlurayExaminer, + assemblyInfo, + this); MediaEncoder = mediaEncoder; RegisterSingleInstance(MediaEncoder); @@ -1383,7 +1302,7 @@ namespace Emby.Server.Implementations /// Task{IUserRepository}. private IUserRepository GetUserRepository() { - var repo = new SqliteUserRepository(LogManager.GetLogger("SqliteUserRepository"), ApplicationPaths, JsonSerializer, MemoryStreamFactory); + var repo = new SqliteUserRepository(LogManager.GetLogger("SqliteUserRepository"), ApplicationPaths, JsonSerializer); repo.Initialize(); @@ -1392,7 +1311,7 @@ namespace Emby.Server.Implementations private IAuthenticationRepository GetAuthenticationRepository() { - var repo = new AuthenticationRepository(LogManager.GetLogger("AuthenticationRepository"), ServerConfigurationManager.ApplicationPaths); + var repo = new AuthenticationRepository(LogManager.GetLogger("AuthenticationRepository"), ServerConfigurationManager); repo.Initialize(); @@ -1408,25 +1327,13 @@ namespace Emby.Server.Implementations return repo; } - /// - /// Configures the repositories. - /// - private void ConfigureNotificationsRepository() - { - var repo = new SqliteNotificationsRepository(LogManager.GetLogger("SqliteNotificationsRepository"), ServerConfigurationManager.ApplicationPaths, FileSystemManager); - - repo.Initialize(); - - NotificationsRepository = repo; - - RegisterSingleInstance(NotificationsRepository); - } - /// /// Dirty hacks /// private void SetStaticProperties() { + ((SqliteItemRepository)ItemRepository).ImageProcessor = ImageProcessor; + // For now there's no real way to inject these properly BaseItem.Logger = LogManager.GetLogger("BaseItem"); BaseItem.ConfigurationManager = ServerConfigurationManager; @@ -1434,20 +1341,19 @@ namespace Emby.Server.Implementations BaseItem.ProviderManager = ProviderManager; BaseItem.LocalizationManager = LocalizationManager; BaseItem.ItemRepository = ItemRepository; - User.XmlSerializer = XmlSerializer; User.UserManager = UserManager; - Folder.UserManager = UserManager; BaseItem.FileSystem = FileSystemManager; BaseItem.UserDataManager = UserDataManager; BaseItem.ChannelManager = ChannelManager; - BaseItem.LiveTvManager = LiveTvManager; + Video.LiveTvManager = LiveTvManager; Folder.UserViewManager = UserViewManager; UserView.TVSeriesManager = TVSeriesManager; UserView.PlaylistManager = PlaylistManager; - BaseItem.CollectionManager = CollectionManager; + UserView.CollectionManager = CollectionManager; BaseItem.MediaSourceManager = MediaSourceManager; CollectionFolder.XmlSerializer = XmlSerializer; - Utilities.CryptographyProvider = CryptographyProvider; + CollectionFolder.JsonSerializer = JsonSerializer; + CollectionFolder.ApplicationHost = this; AuthenticatedAttribute.AuthService = AuthService; } @@ -1463,19 +1369,14 @@ namespace Emby.Server.Implementations ConfigurationManager.SaveConfiguration(); } - RegisterModules(); - ConfigurationManager.AddParts(GetExports()); - Plugins = GetExports().Select(LoadPlugin).Where(i => i != null).ToArray(); + Plugins = GetExportsWithInfo().Select(LoadPlugin).Where(i => i != null).ToArray(); - HttpServer.Init(GetExports(false)); - - ServerManager.AddWebSocketListeners(GetExports(false)); + HttpServer.Init(GetExports(false), GetExports()); StartServer(); LibraryManager.AddParts(GetExports(), - GetExports(), GetExports(), GetExports(), GetExports(), @@ -1493,18 +1394,21 @@ namespace Emby.Server.Implementations SubtitleManager.AddParts(GetExports()); - SessionManager.AddParts(GetExports()); - ChannelManager.AddParts(GetExports()); MediaSourceManager.AddParts(GetExports()); NotificationManager.AddParts(GetExports(), GetExports()); - SyncManager.AddParts(GetExports()); + UserManager.AddParts(GetExports()); + + IsoManager.AddParts(GetExports()); } - private IPlugin LoadPlugin(IPlugin plugin) + private IPlugin LoadPlugin(Tuple info) { + var plugin = info.Item1; + var assemblyFilePath = info.Item2; + try { var assemblyPlugin = plugin as IPluginAssembly; @@ -1514,10 +1418,9 @@ namespace Emby.Server.Implementations var assembly = plugin.GetType().Assembly; var assemblyName = assembly.GetName(); - var assemblyFileName = assemblyName.Name + ".dll"; - var assemblyFilePath = Path.Combine(ApplicationPaths.PluginsPath, assemblyFileName); + var dataFolderPath = Path.Combine(ApplicationPaths.PluginsPath, Path.GetFileNameWithoutExtension(assemblyFilePath)); - assemblyPlugin.SetAttributes(assemblyFilePath, assemblyFileName, assemblyName.Version); + assemblyPlugin.SetAttributes(assemblyFilePath, dataFolderPath, assemblyName.Version); try { @@ -1536,8 +1439,11 @@ namespace Emby.Server.Implementations } } - var isFirstRun = !File.Exists(plugin.ConfigurationFilePath); - plugin.SetStartupInfo(isFirstRun, File.GetLastWriteTimeUtc, s => Directory.CreateDirectory(s)); + var hasPluginConfiguration = plugin as IHasPluginConfiguration; + if (hasPluginConfiguration != null) + { + hasPluginConfiguration.SetStartupInfo(s => Directory.CreateDirectory(s)); + } } catch (Exception ex) { @@ -1553,18 +1459,32 @@ namespace Emby.Server.Implementations /// protected void DiscoverTypes() { - FailedAssemblies.Clear(); + Logger.Info("Loading assemblies"); - var assemblies = GetComposablePartAssemblies().ToList(); + var assemblyInfos = GetComposablePartAssemblies(); - foreach (var assembly in assemblies) + foreach (var assemblyInfo in assemblyInfos) { - Logger.Info("Loading {0}", assembly.FullName); + var assembly = assemblyInfo.Item1; + var path = assemblyInfo.Item2; + + if (path == null) + { + Logger.Info("Loading {0}", assembly.FullName); + } + else + { + Logger.Info("Loading {0} from {1}", assembly.FullName, path); + } } - AllConcreteTypes = assemblies + AllConcreteTypes = assemblyInfos .SelectMany(GetTypes) - .Where(t => t.IsClass && !t.IsAbstract && !t.IsInterface && !t.IsGenericType) + .Where(info => + { + var t = info.Item1; + return t.IsClass && !t.IsAbstract && !t.IsInterface && !t.IsGenericType; + }) .ToArray(); } @@ -1572,22 +1492,21 @@ namespace Emby.Server.Implementations /// Gets a list of types within an assembly /// This will handle situations that would normally throw an exception - such as a type within the assembly that depends on some other non-existant reference /// - /// The assembly. - /// IEnumerable{Type}. - /// assembly - protected List GetTypes(Assembly assembly) + protected List> GetTypes(Tuple assemblyInfo) { - if (assembly == null) + if (assemblyInfo == null) { - return new List(); + return new List>(); } + var assembly = assemblyInfo.Item1; + try { // This null checking really shouldn't be needed but adding it due to some // unhandled exceptions in mono 5.0 that are a little hard to hunt down var types = assembly.GetTypes() ?? new Type[] { }; - return types.Where(t => t != null).ToList(); + return types.Where(t => t != null).Select(i => new Tuple(i, assemblyInfo.Item2)).ToList(); } catch (ReflectionTypeLoadException ex) { @@ -1604,24 +1523,22 @@ namespace Emby.Server.Implementations // If it fails we can still get a list of the Types it was able to resolve var types = ex.Types ?? new Type[] { }; - return types.Where(t => t != null).ToList(); + return types.Where(t => t != null).Select(i => new Tuple(i, assemblyInfo.Item2)).ToList(); } catch (Exception ex) { Logger.ErrorException("Error loading types from assembly", ex); - return new List(); + return new List>(); } } private CertificateInfo CertificateInfo { get; set; } - private X509Certificate Certificate { get; set; } + protected X509Certificate Certificate { get; private set; } private IEnumerable GetUrlPrefixes() { - var hosts = new List(); - - hosts.Add("+"); + var hosts = new[] { "+" }; return hosts.SelectMany(i => { @@ -1639,6 +1556,8 @@ namespace Emby.Server.Implementations }); } + protected abstract IHttpListener CreateHttpListener(); + /// /// Starts the server. /// @@ -1646,12 +1565,16 @@ namespace Emby.Server.Implementations { try { - ServerManager.Start(GetUrlPrefixes().ToArray()); + ((HttpListenerHost)HttpServer).StartServer(GetUrlPrefixes().ToArray(), CreateHttpListener()); return; } catch (Exception ex) { - Logger.ErrorException("Error starting http server", ex); + var msg = string.Equals(ex.GetType().Name, "SocketException", StringComparison.OrdinalIgnoreCase) + ? "The http server is unable to start due to a Socket error. This can occasionally happen when the operating system takes longer than usual to release the IP bindings from the previous session. This can take up to five minutes. Please try waiting or rebooting the system." + : "Error starting Http Server"; + + Logger.ErrorException(msg, ex); if (HttpPort == ServerConfiguration.DefaultHttpPort) { @@ -1663,7 +1586,7 @@ namespace Emby.Server.Implementations try { - ServerManager.Start(GetUrlPrefixes().ToArray()); + ((HttpListenerHost)HttpServer).StartServer(GetUrlPrefixes().ToArray(), CreateHttpListener()); } catch (Exception ex) { @@ -1822,10 +1745,9 @@ namespace Emby.Server.Implementations /// Gets the composable part assemblies. /// /// IEnumerable{Assembly}. - protected IEnumerable GetComposablePartAssemblies() + protected List> GetComposablePartAssemblies() { - var list = GetPluginAssemblies() - .ToList(); + var list = GetPluginAssemblies(); // Gets all plugin assemblies by first reading all bytes of the .dll and calling Assembly.Load against that // This will prevent the .dll file from getting locked, and allow us to replace it when needed @@ -1863,10 +1785,13 @@ namespace Emby.Server.Implementations // Local metadata list.Add(GetAssembly(typeof(BoxSetXmlSaver))); + // Notifications + list.Add(GetAssembly(typeof(NotificationManager))); + // Xbmc list.Add(GetAssembly(typeof(ArtistNfoProvider))); - list.AddRange(GetAssembliesWithPartsInternal()); + list.AddRange(GetAssembliesWithPartsInternal().Select(i => new Tuple(i, null))); return list.ToList(); } @@ -1877,25 +1802,92 @@ namespace Emby.Server.Implementations /// Gets the plugin assemblies. /// /// IEnumerable{Assembly}. - private IEnumerable GetPluginAssemblies() + private List> GetPluginAssemblies() + { + // Copy pre-installed plugins + var sourcePath = Path.Combine(ApplicationPaths.ApplicationResourcesPath, "plugins"); + CopyPlugins(sourcePath, ApplicationPaths.PluginsPath); + + return GetPluginAssemblies(ApplicationPaths.PluginsPath); + } + + private void CopyPlugins(string source, string target) { + List files; + try { - return Directory.EnumerateFiles(ApplicationPaths.PluginsPath, "*.dll", SearchOption.TopDirectoryOnly) - .Where(EnablePlugin) + files = Directory.EnumerateFiles(source, "*.dll", SearchOption.TopDirectoryOnly) + .ToList(); + + } + catch (DirectoryNotFoundException) + { + return; + } + + if (files.Count == 0) + { + return; + } + + foreach (var sourceFile in files) + { + var filename = Path.GetFileName(sourceFile); + var targetFile = Path.Combine(target, filename); + + var targetFileExists = File.Exists(targetFile); + + if (!targetFileExists && ServerConfigurationManager.Configuration.UninstalledPlugins.Contains(filename, StringComparer.OrdinalIgnoreCase)) + { + continue; + } + + if (targetFileExists && GetDllVersion(targetFile) >= GetDllVersion(sourceFile)) + { + continue; + } + + Directory.CreateDirectory(target); + File.Copy(sourceFile, targetFile, true); + } + } + + private Version GetDllVersion(string path) + { + try + { + var result = Version.Parse(FileVersionInfo.GetVersionInfo(path).FileVersion); + + Logger.Info("File {0} has version {1}", path, result); + + return result; + } + catch (Exception ex) + { + Logger.ErrorException("Error getting version number from {0}", ex, path); + + return new Version(1, 0); + } + } + + private List> GetPluginAssemblies(string path) + { + try + { + return FilterAssembliesToLoad(Directory.EnumerateFiles(path, "*.dll", SearchOption.TopDirectoryOnly)) .Select(LoadAssembly) .Where(a => a != null) .ToList(); } catch (DirectoryNotFoundException) { - return new List(); + return new List>(); } } - private bool EnablePlugin(string path) + private IEnumerable FilterAssembliesToLoad(IEnumerable paths) { - var filename = Path.GetFileName(path); var exclude = new[] { @@ -1903,6 +1895,7 @@ namespace Emby.Server.Implementations "mbintros.dll", "embytv.dll", "Messenger.dll", + "Messages.dll", "MediaBrowser.Plugins.TvMazeProvider.dll", "MBBookshelf.dll", "MediaBrowser.Channels.Adult.YouJizz.dll", @@ -1927,10 +1920,49 @@ namespace Emby.Server.Implementations "MediaBrowser.Channels.HitboxTV.dll", "MediaBrowser.Channels.HockeyStreams.dll", "MediaBrowser.Plugins.ITV.dll", - "MediaBrowser.Plugins.Lastfm.dll" + "MediaBrowser.Plugins.Lastfm.dll", + "ServerRestart.dll", + "MediaBrowser.Plugins.NotifyMyAndroidNotifications.dll", + "MetadataViewer.dll" + }; + + var minRequiredVersions = new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { "GameBrowser.dll", new Version(3, 1) }, + { "moviethemesongs.dll", new Version(1, 6) }, + { "themesongs.dll", new Version(1, 2) } }; - return !exclude.Contains(filename ?? string.Empty, StringComparer.OrdinalIgnoreCase); + return paths.Where(path => + { + var filename = Path.GetFileName(path); + if (exclude.Contains(filename ?? string.Empty, StringComparer.OrdinalIgnoreCase)) + { + return false; + } + + Version minRequiredVersion; + if (minRequiredVersions.TryGetValue(filename, out minRequiredVersion)) + { + try + { + var version = Version.Parse(FileVersionInfo.GetVersionInfo(path).FileVersion); + + if (version < minRequiredVersion) + { + Logger.Info("Not loading {0} {1} because the minimum supported version is {2}. Please update to the newer version", filename, version, minRequiredVersion); + return false; + } + } + catch (Exception ex) + { + Logger.ErrorException("Error getting version number from {0}", ex, path); + + return false; + } + } + return true; + }); } /// @@ -1947,16 +1979,13 @@ namespace Emby.Server.Implementations IsShuttingDown = IsShuttingDown, Version = ApplicationVersion.ToString(), WebSocketPortNumber = HttpPort, - FailedPluginAssemblies = FailedAssemblies.ToArray(), - InProgressInstallations = InstallationManager.CurrentInstallations.Select(i => i.Item1).ToArray(), CompletedInstallations = InstallationManager.CompletedInstallations.ToArray(), Id = SystemId, ProgramDataPath = ApplicationPaths.ProgramDataPath, LogPath = ApplicationPaths.LogDirectoryPath, - ItemsByNamePath = ApplicationPaths.ItemsByNamePath, + ItemsByNamePath = ApplicationPaths.InternalMetadataPath, InternalMetadataPath = ApplicationPaths.InternalMetadataPath, CachePath = ApplicationPaths.CachePath, - MacAddress = GetMacAddress(), HttpServerPortNumber = HttpPort, SupportsHttps = SupportsHttps, HttpsPortNumber = HttpsPort, @@ -1979,6 +2008,16 @@ namespace Emby.Server.Implementations }; } + public WakeOnLanInfo[] GetWakeOnLanInfo() + { + return NetworkManager.GetMacAddresses() + .Select(i => new WakeOnLanInfo + { + MacAddress = i + }) + .ToArray(); + } + public async Task GetPublicSystemInfo(CancellationToken cancellationToken) { var localAddress = await GetLocalApiUrl(cancellationToken).ConfigureAwait(false); @@ -1998,7 +2037,7 @@ namespace Emby.Server.Implementations { get { - return SupportsHttps && (ServerConfigurationManager.Configuration.EnableHttps || ServerConfigurationManager.Configuration.RequireHttps); + return SupportsHttps && ServerConfigurationManager.Configuration.EnableHttps; } } @@ -2127,14 +2166,20 @@ namespace Emby.Server.Implementations return cachedResult; } + var logPing = false; + +#if DEBUG + logPing = true; +#endif + try { using (var response = await HttpClient.SendAsync(new HttpRequestOptions { Url = apiUrl, LogErrorResponseBody = false, - LogErrors = false, - LogRequest = false, + LogErrors = logPing, + LogRequest = logPing, TimeoutMs = 30000, BufferContent = false, @@ -2158,9 +2203,9 @@ namespace Emby.Server.Implementations Logger.Debug("Ping test result to {0}. Success: {1}", apiUrl, "Cancelled"); throw; } - catch + catch (Exception ex) { - Logger.Debug("Ping test result to {0}. Success: {1}", apiUrl, false); + Logger.Debug("Ping test result to {0}. Success: {1} {2}", apiUrl, false, ex.Message); _validAddressResults.AddOrUpdate(apiUrl, false, (k, v) => false); return false; @@ -2171,7 +2216,7 @@ namespace Emby.Server.Implementations { get { - return string.IsNullOrWhiteSpace(ServerConfigurationManager.Configuration.ServerName) + return string.IsNullOrEmpty(ServerConfigurationManager.Configuration.ServerName) ? Environment.MachineName : ServerConfigurationManager.Configuration.ServerName; } @@ -2181,23 +2226,6 @@ namespace Emby.Server.Implementations public int HttpsPort { get; private set; } - /// - /// Gets the mac address. - /// - /// System.String. - private string GetMacAddress() - { - try - { - return NetworkManager.GetMacAddress(); - } - catch (Exception ex) - { - Logger.ErrorException("Error getting mac address", ex); - return null; - } - } - /// /// Shuts down. /// @@ -2296,7 +2324,7 @@ namespace Emby.Server.Implementations try { var result = await new GithubUpdater(HttpClient, JsonSerializer).CheckForUpdateResult("MediaBrowser", - "Emby", + "Emby.Releases", ApplicationVersion, updateLevel, ReleaseAssetFilename, @@ -2370,7 +2398,7 @@ namespace Emby.Server.Implementations /// The hostname in private static string GetHostnameFromExternalDns(string externalDns) { - if (string.IsNullOrWhiteSpace(externalDns)) + if (string.IsNullOrEmpty(externalDns)) { return "localhost"; } @@ -2424,25 +2452,6 @@ namespace Emby.Server.Implementations { } - private void RegisterModules() - { - var moduleTypes = GetExportTypes(); - - foreach (var type in moduleTypes) - { - try - { - var instance = Activator.CreateInstance(type) as IDependencyModule; - if (instance != null) - instance.BindDependencies(this); - } - catch (Exception ex) - { - Logger.ErrorException("Error setting up dependency bindings for " + type.Name, ex); - } - } - } - /// /// Called when [application updated]. /// @@ -2471,7 +2480,6 @@ namespace Emby.Server.Implementations _disposed = true; Dispose(true); - GC.SuppressFinalize(this); } } @@ -2507,19 +2515,50 @@ namespace Emby.Server.Implementations } } - void IDependencyContainer.RegisterSingleInstance(T obj, bool manageLifetime) + private Dictionary _values; + public string GetValue(string name) { - RegisterSingleInstance(obj, manageLifetime); - } + if (_values == null) + { + _values = LoadValues(); + } - void IDependencyContainer.RegisterSingleInstance(Func func) - { - RegisterSingleInstance(func); + string value; + + if (_values.TryGetValue(name, out value)) + { + return value; + } + + return null; } - void IDependencyContainer.Register(Type typeInterface, Type typeImplementation) + private Dictionary LoadValues() { - Container.Register(typeInterface, typeImplementation); + Dictionary values = new Dictionary(StringComparer.OrdinalIgnoreCase); + + using (var stream = typeof(ApplicationHost).Assembly.GetManifestResourceStream(typeof(ApplicationHost).Namespace + ".values.txt")) + { + using (var reader = new StreamReader(stream)) + { + while (!reader.EndOfStream) + { + var line = reader.ReadLine(); + if (string.IsNullOrEmpty(line)) + { + continue; + } + + var index = line.IndexOf('='); + if (index != -1) + { + values[line.Substring(0, index)] = line.Substring(index + 1); + } + } + } + } + + return values; } } diff --git a/Emby.Server.Implementations/Archiving/ZipClient.cs b/Emby.Server.Implementations/Archiving/ZipClient.cs index 32938e151f..fd61f2617e 100644 --- a/Emby.Server.Implementations/Archiving/ZipClient.cs +++ b/Emby.Server.Implementations/Archiving/ZipClient.cs @@ -3,6 +3,7 @@ using MediaBrowser.Model.IO; using SharpCompress.Archives.Rar; using SharpCompress.Archives.SevenZip; using SharpCompress.Archives.Tar; +using SharpCompress.Common; using SharpCompress.Readers; using SharpCompress.Readers.GZip; using SharpCompress.Readers.Zip; @@ -185,44 +186,5 @@ namespace Emby.Server.Implementations.Archiving } } } - - /// - /// Extracts all from rar. - /// - /// The source file. - /// The target path. - /// if set to true [overwrite existing files]. - public void ExtractAllFromRar(string sourceFile, string targetPath, bool overwriteExistingFiles) - { - using (var fileStream = _fileSystem.OpenRead(sourceFile)) - { - ExtractAllFromRar(fileStream, targetPath, overwriteExistingFiles); - } - } - - /// - /// Extracts all from rar. - /// - /// The source. - /// The target path. - /// if set to true [overwrite existing files]. - public void ExtractAllFromRar(Stream source, string targetPath, bool overwriteExistingFiles) - { - using (var archive = RarArchive.Open(source)) - { - using (var reader = archive.ExtractAllEntries()) - { - var options = new ExtractionOptions(); - options.ExtractFullPath = true; - - if (overwriteExistingFiles) - { - options.Overwrite = true; - } - - reader.WriteAllToDirectory(targetPath, options); - } - } - } } } diff --git a/Emby.Server.Implementations/Browser/BrowserLauncher.cs b/Emby.Server.Implementations/Browser/BrowserLauncher.cs index 71497f6bf8..007f60a9bb 100644 --- a/Emby.Server.Implementations/Browser/BrowserLauncher.cs +++ b/Emby.Server.Implementations/Browser/BrowserLauncher.cs @@ -13,44 +13,22 @@ namespace Emby.Server.Implementations.Browser /// /// The page. /// The app host. - public static void OpenDashboardPage(string page, IServerApplicationHost appHost) + private static void OpenDashboardPage(string page, IServerApplicationHost appHost) { var url = appHost.GetLocalApiUrl("localhost") + "/web/" + page; OpenUrl(appHost, url); } - /// - /// Opens the community. - /// - public static void OpenCommunity(IServerApplicationHost appHost) - { - OpenUrl(appHost, "http://emby.media/community"); - } - - public static void OpenEmbyPremiere(IServerApplicationHost appHost) - { - OpenDashboardPage("supporterkey.html", appHost); - } - /// /// Opens the web client. /// /// The app host. - public static void OpenWebClient(IServerApplicationHost appHost) + public static void OpenWebApp(IServerApplicationHost appHost) { OpenDashboardPage("index.html", appHost); } - /// - /// Opens the dashboard. - /// - /// The app host. - public static void OpenDashboard(IServerApplicationHost appHost) - { - OpenDashboardPage("dashboard.html", appHost); - } - /// /// Opens the URL. /// diff --git a/Emby.Server.Implementations/Channels/ChannelConfigurations.cs b/Emby.Server.Implementations/Channels/ChannelConfigurations.cs deleted file mode 100644 index ef0973e7f4..0000000000 --- a/Emby.Server.Implementations/Channels/ChannelConfigurations.cs +++ /dev/null @@ -1,29 +0,0 @@ -using MediaBrowser.Common.Configuration; -using MediaBrowser.Model.Configuration; -using System.Collections.Generic; - -namespace Emby.Server.Implementations.Channels -{ - public static class ChannelConfigurationExtension - { - public static ChannelOptions GetChannelsConfiguration(this IConfigurationManager manager) - { - return manager.GetConfiguration("channels"); - } - } - - public class ChannelConfigurationFactory : IConfigurationFactory - { - public IEnumerable GetConfigurations() - { - return new List - { - new ConfigurationStore - { - Key = "channels", - ConfigurationType = typeof (ChannelOptions) - } - }; - } - } -} diff --git a/Emby.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs b/Emby.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs index 7be4101c80..8448d36406 100644 --- a/Emby.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs +++ b/Emby.Server.Implementations/Channels/ChannelDynamicMediaSourceProvider.cs @@ -18,24 +18,17 @@ namespace Emby.Server.Implementations.Channels _channelManager = (ChannelManager)channelManager; } - public Task> GetMediaSources(IHasMediaSources item, CancellationToken cancellationToken) + public Task> GetMediaSources(BaseItem item, CancellationToken cancellationToken) { - var baseItem = (BaseItem) item; - - if (baseItem.SourceType == SourceType.Channel) + if (item.SourceType == SourceType.Channel) { - return _channelManager.GetDynamicMediaSources(baseItem, cancellationToken); + return _channelManager.GetDynamicMediaSources(item, cancellationToken); } return Task.FromResult>(new List()); } - public Task> OpenMediaSource(string openToken, bool allowLiveStreamProbe, CancellationToken cancellationToken) - { - throw new NotImplementedException(); - } - - public Task CloseMediaSource(string liveStreamId) + public Task OpenMediaSource(string openToken, List currentLiveStreams, CancellationToken cancellationToken) { throw new NotImplementedException(); } diff --git a/Emby.Server.Implementations/Channels/ChannelImageProvider.cs b/Emby.Server.Implementations/Channels/ChannelImageProvider.cs index 0c363c585f..a6643e83ca 100644 --- a/Emby.Server.Implementations/Channels/ChannelImageProvider.cs +++ b/Emby.Server.Implementations/Channels/ChannelImageProvider.cs @@ -18,12 +18,12 @@ namespace Emby.Server.Implementations.Channels _channelManager = channelManager; } - public IEnumerable GetSupportedImages(IHasMetadata item) + public IEnumerable GetSupportedImages(BaseItem item) { return GetChannel(item).GetSupportedChannelImages(); } - public Task GetImage(IHasMetadata item, ImageType type, CancellationToken cancellationToken) + public Task GetImage(BaseItem item, ImageType type, CancellationToken cancellationToken) { var channel = GetChannel(item); @@ -35,19 +35,19 @@ namespace Emby.Server.Implementations.Channels get { return "Channel Image Provider"; } } - public bool Supports(IHasMetadata item) + public bool Supports(BaseItem item) { return item is Channel; } - private IChannel GetChannel(IHasMetadata item) + private IChannel GetChannel(BaseItem item) { var channel = (Channel)item; return ((ChannelManager)_channelManager).GetChannelProvider(channel); } - public bool HasChanged(IHasMetadata item, IDirectoryService directoryService) + public bool HasChanged(BaseItem item, IDirectoryService directoryService) { return GetSupportedImages(item).Any(i => !item.HasImage(i)); } diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs index c566ca25bd..e832c7c6f8 100644 --- a/Emby.Server.Implementations/Channels/ChannelManager.cs +++ b/Emby.Server.Implementations/Channels/ChannelManager.cs @@ -51,7 +51,6 @@ namespace Emby.Server.Implementations.Channels private readonly IProviderManager _providerManager; private readonly ILocalizationManager _localization; - private readonly ConcurrentDictionary _refreshedItems = new ConcurrentDictionary(); public ChannelManager(IUserManager userManager, IDtoService dtoService, ILibraryManager libraryManager, ILogger logger, IServerConfigurationManager config, IFileSystem fileSystem, IUserDataManager userDataManager, IJsonSerializer jsonSerializer, ILocalizationManager localization, IHttpClient httpClient, IProviderManager providerManager) { @@ -72,7 +71,7 @@ namespace Emby.Server.Implementations.Channels { get { - return TimeSpan.FromHours(6); + return TimeSpan.FromHours(3); } } @@ -81,6 +80,51 @@ namespace Emby.Server.Implementations.Channels Channels = channels.ToArray(); } + public bool EnableMediaSourceDisplay(BaseItem item) + { + var internalChannel = _libraryManager.GetItemById(item.ChannelId); + var channel = Channels.FirstOrDefault(i => GetInternalChannelId(i.Name).Equals(internalChannel.Id)); + + return !(channel is IDisableMediaSourceDisplay); + } + + public bool CanDelete(BaseItem item) + { + var internalChannel = _libraryManager.GetItemById(item.ChannelId); + var channel = Channels.FirstOrDefault(i => GetInternalChannelId(i.Name).Equals(internalChannel.Id)); + + var supportsDelete = channel as ISupportsDelete; + return supportsDelete != null && supportsDelete.CanDelete(item); + } + + public bool EnableMediaProbe(BaseItem item) + { + var internalChannel = _libraryManager.GetItemById(item.ChannelId); + var channel = Channels.FirstOrDefault(i => GetInternalChannelId(i.Name).Equals(internalChannel.Id)); + + return channel is ISupportsMediaProbe; + } + + public Task DeleteItem(BaseItem item) + { + var internalChannel = _libraryManager.GetItemById(item.ChannelId); + if (internalChannel == null) + { + throw new ArgumentException(); + } + + var channel = Channels.FirstOrDefault(i => GetInternalChannelId(i.Name).Equals(internalChannel.Id)); + + var supportsDelete = channel as ISupportsDelete; + + if (supportsDelete == null) + { + throw new ArgumentException(); + } + + return supportsDelete.DeleteItem(item.ExternalId, CancellationToken.None); + } + private IEnumerable GetAllChannels() { return Channels @@ -92,9 +136,9 @@ namespace Emby.Server.Implementations.Channels return GetAllChannels().Select(i => GetInternalChannelId(i.Name)); } - public Task> GetChannelsInternal(ChannelQuery query, CancellationToken cancellationToken) + public QueryResult GetChannelsInternal(ChannelQuery query) { - var user = string.IsNullOrWhiteSpace(query.UserId) + var user = query.UserId.Equals(Guid.Empty) ? null : _userManager.GetUserById(query.UserId); @@ -103,6 +147,25 @@ namespace Emby.Server.Implementations.Channels .OrderBy(i => i.SortName) .ToList(); + if (query.IsRecordingsFolder.HasValue) + { + var val = query.IsRecordingsFolder.Value; + channels = channels.Where(i => + { + try + { + var hasAttributes = GetChannelProvider(i) as IHasFolderAttributes; + + return (hasAttributes != null && hasAttributes.Attributes.Contains("Recordings", StringComparer.OrdinalIgnoreCase)) == val; + } + catch + { + return false; + } + + }).ToList(); + } + if (query.SupportsLatestItems.HasValue) { var val = query.SupportsLatestItems.Value; @@ -119,6 +182,23 @@ namespace Emby.Server.Implementations.Channels }).ToList(); } + + if (query.SupportsMediaDeletion.HasValue) + { + var val = query.SupportsMediaDeletion.Value; + channels = channels.Where(i => + { + try + { + return GetChannelProvider(i) is ISupportsDelete == val; + } + catch + { + return false; + } + + }).ToList(); + } if (query.IsFavorite.HasValue) { var val = query.IsFavorite.Value; @@ -161,22 +241,29 @@ namespace Emby.Server.Implementations.Channels var returnItems = all.ToArray(all.Count); - var result = new QueryResult + if (query.RefreshLatestChannelItems) + { + foreach (var item in returnItems) + { + var task = RefreshLatestChannelItems(GetChannelProvider(item), CancellationToken.None); + Task.WaitAll(task); + } + } + + return new QueryResult { Items = returnItems, TotalRecordCount = totalCount }; - - return Task.FromResult(result); } - public async Task> GetChannels(ChannelQuery query, CancellationToken cancellationToken) + public QueryResult GetChannels(ChannelQuery query) { - var user = string.IsNullOrWhiteSpace(query.UserId) + var user = query.UserId.Equals(Guid.Empty) ? null : _userManager.GetUserById(query.UserId); - var internalResult = await GetChannelsInternal(query, cancellationToken).ConfigureAwait(false); + var internalResult = GetChannelsInternal(query); var dtoOptions = new DtoOptions() { @@ -195,8 +282,6 @@ namespace Emby.Server.Implementations.Channels public async Task RefreshChannels(IProgress progress, CancellationToken cancellationToken) { - _refreshedItems.Clear(); - var allChannelsList = GetAllChannels().ToList(); var numComplete = 0; @@ -230,7 +315,7 @@ namespace Emby.Server.Implementations.Channels private Channel GetChannelEntity(IChannel channel) { - var item = GetChannel(GetInternalChannelId(channel.Name).ToString("N")); + var item = GetChannel(GetInternalChannelId(channel.Name)); if (item == null) { @@ -240,23 +325,23 @@ namespace Emby.Server.Implementations.Channels return item; } - private List GetSavedMediaSources(BaseItem item) + private List GetSavedMediaSources(BaseItem item) { - var path = Path.Combine(item.GetInternalMetadataPath(), "channelmediasources.json"); + var path = Path.Combine(item.GetInternalMetadataPath(), "channelmediasourceinfos.json"); try { - return _jsonSerializer.DeserializeFromFile>(path) ?? new List(); + return _jsonSerializer.DeserializeFromFile>(path) ?? new List(); } catch { - return new List(); + return new List(); } } - private void SaveMediaSources(BaseItem item, List mediaSources) + private void SaveMediaSources(BaseItem item, List mediaSources) { - var path = Path.Combine(item.GetInternalMetadataPath(), "channelmediasources.json"); + var path = Path.Combine(item.GetInternalMetadataPath(), "channelmediasourceinfos.json"); if (mediaSources == null || mediaSources.Count == 0) { @@ -278,10 +363,10 @@ namespace Emby.Server.Implementations.Channels public IEnumerable GetStaticMediaSources(BaseItem item, CancellationToken cancellationToken) { - IEnumerable results = GetSavedMediaSources(item); + IEnumerable results = GetSavedMediaSources(item); - return SortMediaInfoResults(results) - .Select(i => GetMediaSource(item, i)) + return results + .Select(i => NormalizeMediaSource(item, i)) .ToList(); } @@ -292,7 +377,7 @@ namespace Emby.Server.Implementations.Channels var requiresCallback = channelPlugin as IRequiresMediaInfoCallback; - IEnumerable results; + IEnumerable results; if (requiresCallback != null) { @@ -301,20 +386,20 @@ namespace Emby.Server.Implementations.Channels } else { - results = new List(); + results = new List(); } - return SortMediaInfoResults(results) - .Select(i => GetMediaSource(item, i)) + return results + .Select(i => NormalizeMediaSource(item, i)) .ToList(); } - private readonly ConcurrentDictionary>> _channelItemMediaInfo = - new ConcurrentDictionary>>(); + private readonly ConcurrentDictionary>> _channelItemMediaInfo = + new ConcurrentDictionary>>(); - private async Task> GetChannelItemMediaSourcesInternal(IRequiresMediaInfoCallback channel, string id, CancellationToken cancellationToken) + private async Task> GetChannelItemMediaSourcesInternal(IRequiresMediaInfoCallback channel, string id, CancellationToken cancellationToken) { - Tuple> cachedInfo; + Tuple> cachedInfo; if (_channelItemMediaInfo.TryGetValue(id, out cachedInfo)) { @@ -328,56 +413,24 @@ namespace Emby.Server.Implementations.Channels .ConfigureAwait(false); var list = mediaInfo.ToList(); - var item2 = new Tuple>(DateTime.UtcNow, list); + var item2 = new Tuple>(DateTime.UtcNow, list); _channelItemMediaInfo.AddOrUpdate(id, item2, (key, oldValue) => item2); return list; } - private MediaSourceInfo GetMediaSource(BaseItem item, ChannelMediaInfo info) + private MediaSourceInfo NormalizeMediaSource(BaseItem item, MediaSourceInfo info) { - var source = info.ToMediaSource(item.Id); + info.RunTimeTicks = info.RunTimeTicks ?? item.RunTimeTicks; - source.RunTimeTicks = source.RunTimeTicks ?? item.RunTimeTicks; - - return source; - } - - private IEnumerable SortMediaInfoResults(IEnumerable channelMediaSources) - { - var list = channelMediaSources.ToList(); - - var options = _config.GetChannelsConfiguration(); - - var width = options.PreferredStreamingWidth; - - if (width.HasValue) - { - var val = width.Value; - - var res = list - .OrderBy(i => i.Width.HasValue && i.Width.Value <= val ? 0 : 1) - .ThenBy(i => Math.Abs((i.Width ?? 0) - val)) - .ThenByDescending(i => i.Width ?? 0) - .ThenBy(list.IndexOf) - .ToList(); - - - return res; - } - - return list - .OrderByDescending(i => i.Width ?? 0) - .ThenBy(list.IndexOf); + return info; } private async Task GetChannel(IChannel channelInfo, CancellationToken cancellationToken) { - var parentFolder = GetInternalChannelFolder(cancellationToken); - var parentFolderId = parentFolder.Id; + var parentFolderId = Guid.Empty; var id = GetInternalChannelId(channelInfo.Name); - var idString = id.ToString("N"); var path = Channel.GetInternalMetadataPath(_config.ApplicationPaths.InternalMetadataPath, id); @@ -405,11 +458,11 @@ namespace Emby.Server.Implementations.Channels } item.Path = path; - if (!string.Equals(item.ChannelId, idString, StringComparison.OrdinalIgnoreCase)) + if (!item.ChannelId.Equals(id)) { forceUpdate = true; } - item.ChannelId = idString; + item.ChannelId = id; if (item.ParentId != parentFolderId) { @@ -419,25 +472,24 @@ namespace Emby.Server.Implementations.Channels item.OfficialRating = GetOfficialRating(channelInfo.ParentalRating); item.Overview = channelInfo.Description; - item.HomePageUrl = channelInfo.HomePageUrl; if (string.IsNullOrWhiteSpace(item.Name)) { item.Name = channelInfo.Name; } - item.OnMetadataChanged(); - if (isNew) { - _libraryManager.CreateItem(item, cancellationToken); + item.OnMetadataChanged(); + _libraryManager.CreateItem(item, null); } - else if (forceUpdate) + + await item.RefreshMetadata(new MetadataRefreshOptions(_fileSystem) { - item.UpdateToRepository(ItemUpdateType.None, cancellationToken); - } + ForceSave = !isNew && forceUpdate + + }, cancellationToken); - await item.RefreshMetadata(new MetadataRefreshOptions(_fileSystem), cancellationToken); return item; } @@ -458,6 +510,11 @@ namespace Emby.Server.Implementations.Channels } } + public Channel GetChannel(Guid id) + { + return _libraryManager.GetItemById(id) as Channel; + } + public Channel GetChannel(string id) { return _libraryManager.GetItemById(id) as Channel; @@ -468,14 +525,14 @@ namespace Emby.Server.Implementations.Channels return _libraryManager.GetItemIds(new InternalItemsQuery { IncludeItemTypes = new[] { typeof(Channel).Name }, - OrderBy = new Tuple[] { new Tuple(ItemSortBy.SortName, SortOrder.Ascending) } + OrderBy = new ValueTuple[] { new ValueTuple(ItemSortBy.SortName, SortOrder.Ascending) } }).Select(i => GetChannelFeatures(i.ToString("N"))).ToArray(); } public ChannelFeatures GetChannelFeatures(string id) { - if (string.IsNullOrWhiteSpace(id)) + if (string.IsNullOrEmpty(id)) { throw new ArgumentNullException("id"); } @@ -486,13 +543,8 @@ namespace Emby.Server.Implementations.Channels return GetChannelFeaturesDto(channel, channelProvider, channelProvider.GetChannelFeatures()); } - public bool SupportsSync(string channelId) + public bool SupportsExternalTransfer(Guid channelId) { - if (string.IsNullOrWhiteSpace(channelId)) - { - throw new ArgumentNullException("channelId"); - } - //var channel = GetChannel(channelId); var channelProvider = GetChannelProvider(channelId); @@ -503,7 +555,6 @@ namespace Emby.Server.Implementations.Channels IChannel provider, InternalChannelFeatures features) { - var isIndexable = provider is IIndexableChannel; var supportsLatest = provider is ISupportsLatestMedia; return new ChannelFeatures @@ -518,57 +569,28 @@ namespace Emby.Server.Implementations.Channels SupportsLatestMedia = supportsLatest, Name = channel.Name, Id = channel.Id.ToString("N"), - SupportsContentDownloading = features.SupportsContentDownloading && (isIndexable || supportsLatest), + SupportsContentDownloading = features.SupportsContentDownloading, AutoRefreshLevels = features.AutoRefreshLevels }; } private Guid GetInternalChannelId(string name) { - if (string.IsNullOrWhiteSpace(name)) + if (string.IsNullOrEmpty(name)) { throw new ArgumentNullException("name"); } return _libraryManager.GetNewItemId("Channel " + name, typeof(Channel)); } - public async Task> GetLatestChannelItems(AllChannelMediaQuery query, CancellationToken cancellationToken) + public async Task> GetLatestChannelItems(InternalItemsQuery query, CancellationToken cancellationToken) { - var user = string.IsNullOrWhiteSpace(query.UserId) - ? null - : _userManager.GetUserById(query.UserId); - - var limit = query.Limit; - - // See below about parental control - if (user != null) - { - query.StartIndex = null; - query.Limit = null; - } - var internalResult = await GetLatestChannelItemsInternal(query, cancellationToken).ConfigureAwait(false); var items = internalResult.Items; var totalRecordCount = internalResult.TotalRecordCount; - // Supporting parental control is a hack because it has to be done after querying the remote data source - // This will get screwy if apps try to page, so limit to 10 results in an attempt to always keep them on the first page - if (user != null) - { - items = items.Where(i => i.IsVisible(user)) - .Take(limit ?? 10) - .ToArray(); - - totalRecordCount = items.Length; - } - - var dtoOptions = new DtoOptions() - { - Fields = query.Fields - }; - - var returnItems = _dtoService.GetBaseItemDtos(items, dtoOptions, user); + var returnItems = _dtoService.GetBaseItemDtos(items, query.DtoOptions, query.User); var result = new QueryResult { @@ -579,407 +601,150 @@ namespace Emby.Server.Implementations.Channels return result; } - public async Task> GetLatestChannelItemsInternal(AllChannelMediaQuery query, CancellationToken cancellationToken) + public async Task> GetLatestChannelItemsInternal(InternalItemsQuery query, CancellationToken cancellationToken) { - var user = string.IsNullOrWhiteSpace(query.UserId) - ? null - : _userManager.GetUserById(query.UserId); - - if (!string.IsNullOrWhiteSpace(query.UserId) && user == null) - { - throw new ArgumentException("User not found."); - } - - var channels = GetAllChannels(); + var channels = GetAllChannels().Where(i => i is ISupportsLatestMedia).ToArray(); if (query.ChannelIds.Length > 0) { // Avoid implicitly captured closure var ids = query.ChannelIds; channels = channels - .Where(i => ids.Contains(GetInternalChannelId(i.Name).ToString("N"))) + .Where(i => ids.Contains(GetInternalChannelId(i.Name))) .ToArray(); } - // Avoid implicitly captured closure - var userId = query.UserId; - - var tasks = channels - .Select(async i => - { - var indexable = i as ISupportsLatestMedia; - - if (indexable != null) - { - try - { - var result = await GetLatestItems(indexable, i, userId, cancellationToken).ConfigureAwait(false); - - var resultItems = result.ToList(); - - return new Tuple(i, new ChannelItemResult - { - Items = resultItems, - TotalRecordCount = resultItems.Count - }); - } - catch (Exception ex) - { - _logger.ErrorException("Error getting all media from {0}", ex, i.Name); - } - } - return new Tuple(i, new ChannelItemResult()); - }); - - var results = await Task.WhenAll(tasks).ConfigureAwait(false); - - var totalCount = results.Length; - - IEnumerable> items = results - .SelectMany(i => i.Item2.Items.Select(m => new Tuple(i.Item1, m))); - - if (query.ContentTypes.Length > 0) - { - // Avoid implicitly captured closure - var contentTypes = query.ContentTypes; - - items = items.Where(i => contentTypes.Contains(i.Item2.ContentType)); - } - if (query.ExtraTypes.Length > 0) + if (channels.Length == 0) { - // Avoid implicitly captured closure - var contentTypes = query.ExtraTypes; - - items = items.Where(i => contentTypes.Contains(i.Item2.ExtraType)); + return new QueryResult(); } - // Avoid implicitly captured closure - var token = cancellationToken; - var internalItems = items.Select(i => - { - var channelProvider = i.Item1; - var internalChannelId = GetInternalChannelId(channelProvider.Name); - return GetChannelItemEntity(i.Item2, channelProvider, internalChannelId, token); - }).ToArray(); - - internalItems = ApplyFilters(internalItems, query.Filters, user).ToArray(); - RefreshIfNeeded(internalItems); - - if (query.StartIndex.HasValue) + foreach (var channel in channels) { - internalItems = internalItems.Skip(query.StartIndex.Value).ToArray(); - } - if (query.Limit.HasValue) - { - internalItems = internalItems.Take(query.Limit.Value).ToArray(); + await RefreshLatestChannelItems(channel, cancellationToken).ConfigureAwait(false); } - return new QueryResult - { - TotalRecordCount = totalCount, - Items = internalItems - }; - } + query.IsFolder = false; - private async Task> GetLatestItems(ISupportsLatestMedia indexable, IChannel channel, string userId, CancellationToken cancellationToken) - { - var cacheLength = CacheLength; - var cachePath = GetChannelDataCachePath(channel, userId, "channelmanager-latest", null, false); + // hack for trailers, figure out a better way later + var sortByPremiereDate = channels.Length == 1 && channels[0].GetType().Name.IndexOf("Trailer") != -1; - try + if (sortByPremiereDate) { - if (_fileSystem.GetLastWriteTimeUtc(cachePath).Add(cacheLength) > DateTime.UtcNow) + query.OrderBy = new [] { - return _jsonSerializer.DeserializeFromFile>(cachePath); - } - } - catch (FileNotFoundException) - { - - } - catch (IOException) - { - - } - - await _resourcePool.WaitAsync(cancellationToken).ConfigureAwait(false); - - try - { - try - { - if (_fileSystem.GetLastWriteTimeUtc(cachePath).Add(cacheLength) > DateTime.UtcNow) - { - return _jsonSerializer.DeserializeFromFile>(cachePath); - } - } - catch (FileNotFoundException) - { - - } - catch (IOException) - { - - } - - var result = await indexable.GetLatestMedia(new ChannelLatestMediaSearch - { - UserId = userId - - }, cancellationToken).ConfigureAwait(false); - - var resultItems = result.ToList(); - - CacheResponse(resultItems, cachePath); - - return resultItems; - } - finally - { - _resourcePool.Release(); + new ValueTuple(ItemSortBy.PremiereDate, SortOrder.Descending), + new ValueTuple(ItemSortBy.ProductionYear, SortOrder.Descending), + new ValueTuple(ItemSortBy.DateCreated, SortOrder.Descending) + }; } - } - - public async Task> GetAllMediaInternal(AllChannelMediaQuery query, CancellationToken cancellationToken) - { - var channels = GetAllChannels(); - - if (query.ChannelIds.Length > 0) + else { - // Avoid implicitly captured closure - var ids = query.ChannelIds; - channels = channels - .Where(i => ids.Contains(GetInternalChannelId(i.Name).ToString("N"))) - .ToArray(); - } - - var tasks = channels - .Select(async i => + query.OrderBy = new [] { - var indexable = i as IIndexableChannel; - - if (indexable != null) - { - try - { - var result = await GetAllItems(indexable, i, new InternalAllChannelMediaQuery - { - UserId = query.UserId, - ContentTypes = query.ContentTypes, - ExtraTypes = query.ExtraTypes, - TrailerTypes = query.TrailerTypes - - }, cancellationToken).ConfigureAwait(false); - - return new Tuple(i, result); - } - catch (Exception ex) - { - _logger.ErrorException("Error getting all media from {0}", ex, i.Name); - } - } - return new Tuple(i, new ChannelItemResult()); - }); - - var results = await Task.WhenAll(tasks).ConfigureAwait(false); - - var totalCount = results.Length; - - IEnumerable> items = results - .SelectMany(i => i.Item2.Items.Select(m => new Tuple(i.Item1, m))) - .OrderBy(i => i.Item2.Name); - - if (query.StartIndex.HasValue) - { - items = items.Skip(query.StartIndex.Value); - } - if (query.Limit.HasValue) - { - items = items.Take(query.Limit.Value); + new ValueTuple(ItemSortBy.DateCreated, SortOrder.Descending) + }; } - // Avoid implicitly captured closure - var token = cancellationToken; - var internalItems = items.Select(i => - { - var channelProvider = i.Item1; - var internalChannelId = GetInternalChannelId(channelProvider.Name); - return GetChannelItemEntity(i.Item2, channelProvider, internalChannelId, token); - }).ToArray(); - - return new QueryResult - { - TotalRecordCount = totalCount, - Items = internalItems - }; + return _libraryManager.GetItemsResult(query); } - public async Task> GetAllMedia(AllChannelMediaQuery query, CancellationToken cancellationToken) + private async Task RefreshLatestChannelItems(IChannel channel, CancellationToken cancellationToken) { - var user = string.IsNullOrWhiteSpace(query.UserId) - ? null - : _userManager.GetUserById(query.UserId); - - var internalResult = await GetAllMediaInternal(query, cancellationToken).ConfigureAwait(false); - - RefreshIfNeeded(internalResult.Items); - - var dtoOptions = new DtoOptions() - { - Fields = query.Fields - }; + var internalChannel = await GetChannel(channel, cancellationToken); - var returnItems = _dtoService.GetBaseItemDtos(internalResult.Items, dtoOptions, user); - - var result = new QueryResult - { - Items = returnItems, - TotalRecordCount = internalResult.TotalRecordCount - }; + var query = new InternalItemsQuery(); + query.Parent = internalChannel; + query.EnableTotalRecordCount = false; + query.ChannelIds = new Guid[] { internalChannel.Id }; - return result; - } - - private async Task GetAllItems(IIndexableChannel indexable, IChannel channel, InternalAllChannelMediaQuery query, CancellationToken cancellationToken) - { - var cacheLength = CacheLength; - var folderId = _jsonSerializer.SerializeToString(query).GetMD5().ToString("N"); - var cachePath = GetChannelDataCachePath(channel, query.UserId, folderId, null, false); - - try - { - if (_fileSystem.GetLastWriteTimeUtc(cachePath).Add(cacheLength) > DateTime.UtcNow) - { - return _jsonSerializer.DeserializeFromFile(cachePath); - } - } - catch (FileNotFoundException) - { + var result = await GetChannelItemsInternal(query, new SimpleProgress(), cancellationToken).ConfigureAwait(false); - } - catch (IOException) + foreach (var item in result.Items) { + var folder = item as Folder; - } - - await _resourcePool.WaitAsync(cancellationToken).ConfigureAwait(false); - - try - { - try + if (folder != null) { - if (_fileSystem.GetLastWriteTimeUtc(cachePath).Add(cacheLength) > DateTime.UtcNow) + await GetChannelItemsInternal(new InternalItemsQuery { - return _jsonSerializer.DeserializeFromFile(cachePath); - } - } - catch (FileNotFoundException) - { - - } - catch (IOException) - { + Parent = folder, + EnableTotalRecordCount = false, + ChannelIds = new Guid[] { internalChannel.Id } + }, new SimpleProgress(), cancellationToken).ConfigureAwait(false); } - - var result = await indexable.GetAllMedia(query, cancellationToken).ConfigureAwait(false); - - CacheResponse(result, cachePath); - - return result; - } - finally - { - _resourcePool.Release(); } } - public async Task> GetChannelItemsInternal(ChannelItemQuery query, IProgress progress, CancellationToken cancellationToken) + public async Task> GetChannelItemsInternal(InternalItemsQuery query, IProgress progress, CancellationToken cancellationToken) { // Get the internal channel entity - var channel = GetChannel(query.ChannelId); + var channel = GetChannel(query.ChannelIds[0]); // Find the corresponding channel provider plugin var channelProvider = GetChannelProvider(channel); - var channelInfo = channelProvider.GetChannelFeatures(); - - int? providerStartIndex = null; - int? providerLimit = null; - - if (channelInfo.MaxPageSize.HasValue) - { - providerStartIndex = query.StartIndex; - - if (query.Limit.HasValue && query.Limit.Value > channelInfo.MaxPageSize.Value) - { - query.Limit = Math.Min(query.Limit.Value, channelInfo.MaxPageSize.Value); - } - providerLimit = query.Limit; - - // This will cause some providers to fail - if (providerLimit == 0) - { - providerLimit = 1; - } - } - - var user = string.IsNullOrWhiteSpace(query.UserId) - ? null - : _userManager.GetUserById(query.UserId); + var user = query.User; ChannelItemSortField? sortField = null; - ChannelItemSortField parsedField; var sortDescending = false; - if (query.OrderBy.Length == 1 && - Enum.TryParse(query.OrderBy[0].Item1, true, out parsedField)) - { - sortField = parsedField; - sortDescending = query.OrderBy[0].Item2 == SortOrder.Descending; - } + var parentItem = !query.ParentId.Equals(Guid.Empty) ? _libraryManager.GetItemById(query.ParentId) : channel; var itemsResult = await GetChannelItems(channelProvider, user, - query.FolderId, - providerStartIndex, - providerLimit, + parentItem is Channel ? null : parentItem.ExternalId, sortField, sortDescending, cancellationToken) .ConfigureAwait(false); - var providerTotalRecordCount = providerLimit.HasValue ? itemsResult.TotalRecordCount : null; + if (query.ParentId.Equals(Guid.Empty)) + { + query.Parent = channel; + } + query.ChannelIds = Array.Empty(); - var internalItems = itemsResult.Items.Select(i => GetChannelItemEntity(i, channelProvider, channel.Id, cancellationToken)).ToArray(); + // Not yet sure why this is causing a problem + query.GroupByPresentationUniqueKey = false; - if (user != null) + //_logger.Debug("GetChannelItemsInternal"); + + // null if came from cache + if (itemsResult != null) { - internalItems = internalItems.Where(i => i.IsVisible(user)).ToArray(); + var internalItems = itemsResult.Items + .Select(i => GetChannelItemEntity(i, channelProvider, channel.Id, parentItem, cancellationToken)) + .ToArray(); + + var existingIds = _libraryManager.GetItemIds(query); + var deadIds = existingIds.Except(internalItems.Select(i => i.Id)) + .ToArray(); - if (providerTotalRecordCount.HasValue) + foreach (var deadId in deadIds) { - providerTotalRecordCount = providerTotalRecordCount.Value; + var deadItem = _libraryManager.GetItemById(deadId); + if (deadItem != null) + { + _libraryManager.DeleteItem(deadItem, new DeleteOptions + { + DeleteFileLocation = false, + DeleteFromExternalProvider = false + + }, parentItem, false); + } } } - return GetReturnItems(internalItems, providerTotalRecordCount, user, query); + return _libraryManager.GetItemsResult(query); } - public async Task> GetChannelItems(ChannelItemQuery query, CancellationToken cancellationToken) + public async Task> GetChannelItems(InternalItemsQuery query, CancellationToken cancellationToken) { - var user = string.IsNullOrWhiteSpace(query.UserId) - ? null - : _userManager.GetUserById(query.UserId); - var internalResult = await GetChannelItemsInternal(query, new SimpleProgress(), cancellationToken).ConfigureAwait(false); - var dtoOptions = new DtoOptions() - { - Fields = query.Fields - }; - - var returnItems = _dtoService.GetBaseItemDtos(internalResult.Items, dtoOptions, user); + var returnItems = _dtoService.GetBaseItemDtos(internalResult.Items, query.DtoOptions, query.User); var result = new QueryResult { @@ -993,29 +758,24 @@ namespace Emby.Server.Implementations.Channels private readonly SemaphoreSlim _resourcePool = new SemaphoreSlim(1, 1); private async Task GetChannelItems(IChannel channel, User user, - string folderId, - int? startIndex, - int? limit, + string externalFolderId, ChannelItemSortField? sortField, bool sortDescending, CancellationToken cancellationToken) { - var userId = user.Id.ToString("N"); + var userId = user == null ? null : user.Id.ToString("N"); var cacheLength = CacheLength; - var cachePath = GetChannelDataCachePath(channel, userId, folderId, sortField, sortDescending); + var cachePath = GetChannelDataCachePath(channel, userId, externalFolderId, sortField, sortDescending); try { - if (!startIndex.HasValue && !limit.HasValue) + if (_fileSystem.GetLastWriteTimeUtc(cachePath).Add(cacheLength) > DateTime.UtcNow) { - if (_fileSystem.GetLastWriteTimeUtc(cachePath).Add(cacheLength) > DateTime.UtcNow) + var cachedResult = _jsonSerializer.DeserializeFromFile(cachePath); + if (cachedResult != null) { - var cachedResult = _jsonSerializer.DeserializeFromFile(cachePath); - if (cachedResult != null) - { - return cachedResult; - } + return null; } } } @@ -1034,15 +794,12 @@ namespace Emby.Server.Implementations.Channels { try { - if (!startIndex.HasValue && !limit.HasValue) + if (_fileSystem.GetLastWriteTimeUtc(cachePath).Add(cacheLength) > DateTime.UtcNow) { - if (_fileSystem.GetLastWriteTimeUtc(cachePath).Add(cacheLength) > DateTime.UtcNow) + var cachedResult = _jsonSerializer.DeserializeFromFile(cachePath); + if (cachedResult != null) { - var cachedResult = _jsonSerializer.DeserializeFromFile(cachePath); - if (cachedResult != null) - { - return cachedResult; - } + return null; } } } @@ -1057,19 +814,13 @@ namespace Emby.Server.Implementations.Channels var query = new InternalChannelItemQuery { - UserId = userId, - StartIndex = startIndex, - Limit = limit, + UserId = user == null ? Guid.Empty : user.Id, SortBy = sortField, - SortDescending = sortDescending + SortDescending = sortDescending, + FolderId = externalFolderId }; - if (!string.IsNullOrWhiteSpace(folderId)) - { - var categoryItem = _libraryManager.GetItemById(new Guid(folderId)); - - query.FolderId = categoryItem.ExternalId; - } + query.FolderId = externalFolderId; var result = await channel.GetChannelItems(query, cancellationToken).ConfigureAwait(false); @@ -1078,10 +829,7 @@ namespace Emby.Server.Implementations.Channels throw new InvalidOperationException("Channel returned a null result from GetChannelItems"); } - if (!startIndex.HasValue && !limit.HasValue) - { - CacheResponse(result, cachePath); - } + CacheResponse(result, cachePath); return result; } @@ -1107,7 +855,7 @@ namespace Emby.Server.Implementations.Channels private string GetChannelDataCachePath(IChannel channel, string userId, - string folderId, + string externalFolderId, ChannelItemSortField? sortField, bool sortDescending) { @@ -1121,10 +869,10 @@ namespace Emby.Server.Implementations.Channels userCacheKey = hasCacheKey.GetCacheKey(userId) ?? string.Empty; } - var filename = string.IsNullOrWhiteSpace(folderId) ? "root" : folderId; + var filename = string.IsNullOrEmpty(externalFolderId) ? "root" : externalFolderId.GetMD5().ToString("N"); filename += userCacheKey; - var version = (channel.DataVersion ?? string.Empty).GetMD5().ToString("N"); + var version = ((channel.DataVersion ?? string.Empty) + "2").GetMD5().ToString("N"); if (sortField.HasValue) { @@ -1144,40 +892,6 @@ namespace Emby.Server.Implementations.Channels filename + ".json"); } - private QueryResult GetReturnItems(IEnumerable items, - int? totalCountFromProvider, - User user, - ChannelItemQuery query) - { - items = ApplyFilters(items, query.Filters, user); - - items = _libraryManager.Sort(items, user, query.OrderBy); - - var all = items.ToList(); - var totalCount = totalCountFromProvider ?? all.Count; - - if (!totalCountFromProvider.HasValue) - { - if (query.StartIndex.HasValue) - { - all = all.Skip(query.StartIndex.Value).ToList(); - } - if (query.Limit.HasValue) - { - all = all.Take(query.Limit.Value).ToList(); - } - } - - var returnItemArray = all.ToArray(all.Count); - RefreshIfNeeded(returnItemArray); - - return new QueryResult - { - Items = returnItemArray, - TotalRecordCount = totalCount - }; - } - private string GetIdToHash(string externalId, string channelName) { // Increment this as needed to force new downloads @@ -1185,7 +899,7 @@ namespace Emby.Server.Implementations.Channels return externalId + (channelName ?? string.Empty) + "16"; } - private T GetItemById(string idString, string channelName, string channnelDataVersion, out bool isNew) + private T GetItemById(string idString, string channelName, out bool isNew) where T : BaseItem, new() { var id = GetIdToHash(idString, channelName).GetMBId(typeof(T)); @@ -1201,7 +915,7 @@ namespace Emby.Server.Implementations.Channels _logger.ErrorException("Error retrieving channel item from database", ex); } - if (item == null || !string.Equals(item.ExternalEtag, channnelDataVersion, StringComparison.Ordinal)) + if (item == null) { item = new T(); isNew = true; @@ -1211,13 +925,14 @@ namespace Emby.Server.Implementations.Channels isNew = false; } - item.ExternalEtag = channnelDataVersion; item.Id = id; return item; } - private BaseItem GetChannelItemEntity(ChannelItemInfo info, IChannel channelProvider, Guid internalChannelId, CancellationToken cancellationToken) + private BaseItem GetChannelItemEntity(ChannelItemInfo info, IChannel channelProvider, Guid internalChannelId, BaseItem parentFolder, CancellationToken cancellationToken) { + var parentFolderId = parentFolder.Id; + BaseItem item; bool isNew; bool forceUpdate = false; @@ -1226,66 +941,76 @@ namespace Emby.Server.Implementations.Channels { if (info.FolderType == ChannelFolderType.MusicAlbum) { - item = GetItemById(info.Id, channelProvider.Name, channelProvider.DataVersion, out isNew); + item = GetItemById(info.Id, channelProvider.Name, out isNew); } else if (info.FolderType == ChannelFolderType.MusicArtist) { - item = GetItemById(info.Id, channelProvider.Name, channelProvider.DataVersion, out isNew); + item = GetItemById(info.Id, channelProvider.Name, out isNew); } else if (info.FolderType == ChannelFolderType.PhotoAlbum) { - item = GetItemById(info.Id, channelProvider.Name, channelProvider.DataVersion, out isNew); + item = GetItemById(info.Id, channelProvider.Name, out isNew); } else if (info.FolderType == ChannelFolderType.Series) { - item = GetItemById(info.Id, channelProvider.Name, channelProvider.DataVersion, out isNew); + item = GetItemById(info.Id, channelProvider.Name, out isNew); } else if (info.FolderType == ChannelFolderType.Season) { - item = GetItemById(info.Id, channelProvider.Name, channelProvider.DataVersion, out isNew); + item = GetItemById(info.Id, channelProvider.Name, out isNew); } else { - item = GetItemById(info.Id, channelProvider.Name, channelProvider.DataVersion, out isNew); + item = GetItemById(info.Id, channelProvider.Name, out isNew); } } else if (info.MediaType == ChannelMediaType.Audio) { if (info.ContentType == ChannelMediaContentType.Podcast) { - item = GetItemById(info.Id, channelProvider.Name, channelProvider.DataVersion, out isNew); + item = GetItemById(info.Id, channelProvider.Name, out isNew); } else { - item = GetItemById