From 8ce3e74e8112a94773df22827849bf274fc88198 Mon Sep 17 00:00:00 2001 From: LukePulverenti Date: Sun, 24 Feb 2013 16:53:54 -0500 Subject: More DI --- MediaBrowser.Api/Javascript/ApiClient.js | 1360 ++++++++++++++++++++ .../Javascript/JavascriptApiClientService.cs | 58 + MediaBrowser.Api/LibraryService.cs | 25 +- MediaBrowser.Api/MediaBrowser.Api.csproj | 2 + MediaBrowser.Api/PluginService.cs | 29 +- .../ScheduledTasks/ScheduledTaskService.cs | 33 +- MediaBrowser.Api/SystemService.cs | 35 +- MediaBrowser.Api/UserLibrary/UserLibraryService.cs | 65 +- MediaBrowser.Api/UserService.cs | 45 +- .../ApiClient.js | 1360 -------------------- .../JavascriptApiClientService.cs | 58 - .../MediaBrowser.ApiInteraction.Javascript.csproj | 105 -- .../Properties/AssemblyInfo.cs | 30 - .../packages.config | 8 - .../BaseApplicationPaths.cs | 304 +++++ .../MediaBrowser.Common.Implementations.csproj | 87 ++ .../Properties/AssemblyInfo.cs | 31 + .../ScheduledTasks/TaskManager.cs | 322 +++++ .../ScheduledTasks/Tasks/DeleteCacheFileTask.cs | 119 ++ .../ScheduledTasks/Tasks/DeleteLogFileTask.cs | 107 ++ .../ScheduledTasks/Tasks/ReloadLoggerTask.cs | 71 + .../ScheduledTasks/Tasks/SystemUpdateTask.cs | 122 ++ .../Serialization/JsonSerializer.cs | 240 ++++ .../Serialization/ProtobufSerializer.cs | 158 +++ .../Serialization/XmlSerializer.cs | 140 ++ .../packages.config | 5 + MediaBrowser.Common/Kernel/BaseApplicationPaths.cs | 304 ----- MediaBrowser.Common/Kernel/BaseKernel.cs | 379 ++---- MediaBrowser.Common/Kernel/IApplicationHost.cs | 21 + MediaBrowser.Common/Kernel/IApplicationPaths.cs | 76 ++ MediaBrowser.Common/Kernel/IKernel.cs | 17 +- MediaBrowser.Common/Kernel/TcpManager.cs | 21 +- MediaBrowser.Common/MediaBrowser.Common.csproj | 19 +- .../Net/Handlers/BaseSerializationHandler.cs | 19 +- MediaBrowser.Common/Net/IWebSocket.cs | 2 +- MediaBrowser.Common/Net/NativeWebSocket.cs | 159 --- MediaBrowser.Common/Net/WebSocketConnection.cs | 33 +- MediaBrowser.Common/Plugins/BasePlugin.cs | 22 +- MediaBrowser.Common/Plugins/IPlugin.cs | 4 +- .../ScheduledTasks/BaseScheduledTask.cs | 140 +- .../ScheduledTasks/BaseTaskTrigger.cs | 64 - MediaBrowser.Common/ScheduledTasks/DailyTrigger.cs | 30 +- .../ScheduledTasks/IScheduledTask.cs | 8 +- MediaBrowser.Common/ScheduledTasks/ITaskManager.cs | 29 +- MediaBrowser.Common/ScheduledTasks/ITaskTrigger.cs | 26 + .../ScheduledTasks/IntervalTrigger.cs | 30 +- .../ScheduledTasks/ScheduledTaskHelpers.cs | 4 +- .../ScheduledTasks/StartupTrigger.cs | 25 +- .../ScheduledTasks/SystemEventTrigger.cs | 23 +- MediaBrowser.Common/ScheduledTasks/TaskManager.cs | 159 --- .../ScheduledTasks/Tasks/DeleteCacheFileTask.cs | 118 -- .../ScheduledTasks/Tasks/DeleteLogFileTask.cs | 106 -- .../ScheduledTasks/Tasks/ReloadLoggerTask.cs | 70 - .../ScheduledTasks/Tasks/SystemUpdateTask.cs | 121 -- .../ScheduledTasks/WeeklyTrigger.cs | 30 +- .../Serialization/DynamicProtobufSerializer.cs | 157 --- .../Serialization/JsonSerializer.cs | 259 ---- MediaBrowser.Common/Serialization/XmlSerializer.cs | 212 --- MediaBrowser.Common/packages.config | 1 - MediaBrowser.Controller/Drawing/ImageManager.cs | 45 +- MediaBrowser.Controller/Entities/User.cs | 15 +- MediaBrowser.Controller/IServerApplicationPaths.cs | 85 ++ MediaBrowser.Controller/Kernel.cs | 64 +- .../MediaBrowser.Controller.csproj | 2 +- MediaBrowser.Controller/MediaInfo/FFMpegManager.cs | 79 +- .../MediaInfo/FFProbeVideoInfoProvider.cs | 30 +- .../Providers/Movies/MovieDbProvider.cs | 31 +- .../Providers/Movies/MovieProviderFromJson.cs | 8 +- .../Providers/Movies/PersonProviderFromJson.cs | 8 +- .../Providers/Movies/TmdbPersonProvider.cs | 29 +- .../ScheduledTasks/ChapterImagesTask.cs | 4 +- .../ScheduledTasks/ImageCleanupTask.cs | 7 +- .../ScheduledTasks/PeopleValidationTask.cs | 4 +- .../ScheduledTasks/PluginUpdateTask.cs | 4 +- .../ScheduledTasks/RefreshMediaLibraryTask.cs | 4 +- MediaBrowser.Controller/ServerApplicationPaths.cs | 334 ----- .../Updates/InstallationManager.cs | 46 +- MediaBrowser.Model/MediaBrowser.Model.csproj | 3 + .../Serialization/IJsonSerializer.cs | 103 ++ .../Serialization/IProtobufSerializer.cs | 63 + MediaBrowser.Model/Serialization/IXmlSerializer.cs | 69 + .../MediaBrowser.Networking.csproj | 2 + MediaBrowser.Networking/Web/HttpServer.cs | 21 +- MediaBrowser.Networking/Web/NativeWebSocket.cs | 145 +++ MediaBrowser.Networking/Web/ServerFactory.cs | 28 + .../WebSocket/AlchemyWebSocket.cs | 8 +- .../MediaBrowser.Server.Implementations.csproj | 102 ++ .../Properties/AssemblyInfo.cs | 30 + .../ServerApplicationPaths.cs | 335 +++++ .../Sqlite/SQLiteDisplayPreferencesRepository.cs | 174 +++ .../Sqlite/SQLiteExtensions.cs | 61 + .../Sqlite/SQLiteItemRepository.cs | 303 +++++ .../Sqlite/SQLiteRepository.cs | 317 +++++ .../Sqlite/SQLiteUserDataRepository.cs | 173 +++ .../Sqlite/SQLiteUserRepository.cs | 182 +++ .../WorldWeatherOnline/WeatherProvider.cs | 345 +++++ .../packages.config | 4 + .../MediaBrowser.Server.Sqlite.csproj | 98 -- .../Properties/AssemblyInfo.cs | 31 - .../SQLiteDisplayPreferencesRepository.cs | 149 --- MediaBrowser.Server.Sqlite/SQLiteExtensions.cs | 61 - MediaBrowser.Server.Sqlite/SQLiteItemRepository.cs | 278 ---- MediaBrowser.Server.Sqlite/SQLiteRepository.cs | 317 ----- .../SQLiteUserDataRepository.cs | 148 --- MediaBrowser.Server.Sqlite/SQLiteUserRepository.cs | 157 --- MediaBrowser.Server.Sqlite/packages.config | 4 - .../MediaBrowser.Server.WorldWeatherOnline.csproj | 72 -- .../Properties/AssemblyInfo.cs | 31 - .../WeatherProvider.cs | 332 ----- MediaBrowser.ServerApplication/App.xaml.cs | 194 +-- MediaBrowser.ServerApplication/ApplicationHost.cs | 498 +++++++ .../LibraryExplorer.xaml.cs | 15 +- MediaBrowser.ServerApplication/MainWindow.xaml.cs | 15 +- .../MediaBrowser.ServerApplication.csproj | 39 +- MediaBrowser.ServerApplication/packages.config | 5 - MediaBrowser.WebDashboard/Api/DashboardService.cs | 3 +- MediaBrowser.sln | 81 +- 117 files changed, 7138 insertions(+), 6264 deletions(-) create mode 100644 MediaBrowser.Api/Javascript/ApiClient.js create mode 100644 MediaBrowser.Api/Javascript/JavascriptApiClientService.cs delete mode 100644 MediaBrowser.ApiInteraction.Javascript/ApiClient.js delete mode 100644 MediaBrowser.ApiInteraction.Javascript/JavascriptApiClientService.cs delete mode 100644 MediaBrowser.ApiInteraction.Javascript/MediaBrowser.ApiInteraction.Javascript.csproj delete mode 100644 MediaBrowser.ApiInteraction.Javascript/Properties/AssemblyInfo.cs delete mode 100644 MediaBrowser.ApiInteraction.Javascript/packages.config create mode 100644 MediaBrowser.Common.Implementations/BaseApplicationPaths.cs create mode 100644 MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj create mode 100644 MediaBrowser.Common.Implementations/Properties/AssemblyInfo.cs create mode 100644 MediaBrowser.Common.Implementations/ScheduledTasks/TaskManager.cs create mode 100644 MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs create mode 100644 MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs create mode 100644 MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/ReloadLoggerTask.cs create mode 100644 MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/SystemUpdateTask.cs create mode 100644 MediaBrowser.Common.Implementations/Serialization/JsonSerializer.cs create mode 100644 MediaBrowser.Common.Implementations/Serialization/ProtobufSerializer.cs create mode 100644 MediaBrowser.Common.Implementations/Serialization/XmlSerializer.cs create mode 100644 MediaBrowser.Common.Implementations/packages.config delete mode 100644 MediaBrowser.Common/Kernel/BaseApplicationPaths.cs create mode 100644 MediaBrowser.Common/Kernel/IApplicationPaths.cs delete mode 100644 MediaBrowser.Common/Net/NativeWebSocket.cs delete mode 100644 MediaBrowser.Common/ScheduledTasks/BaseTaskTrigger.cs create mode 100644 MediaBrowser.Common/ScheduledTasks/ITaskTrigger.cs delete mode 100644 MediaBrowser.Common/ScheduledTasks/TaskManager.cs delete mode 100644 MediaBrowser.Common/ScheduledTasks/Tasks/DeleteCacheFileTask.cs delete mode 100644 MediaBrowser.Common/ScheduledTasks/Tasks/DeleteLogFileTask.cs delete mode 100644 MediaBrowser.Common/ScheduledTasks/Tasks/ReloadLoggerTask.cs delete mode 100644 MediaBrowser.Common/ScheduledTasks/Tasks/SystemUpdateTask.cs delete mode 100644 MediaBrowser.Common/Serialization/DynamicProtobufSerializer.cs delete mode 100644 MediaBrowser.Common/Serialization/JsonSerializer.cs delete mode 100644 MediaBrowser.Common/Serialization/XmlSerializer.cs create mode 100644 MediaBrowser.Controller/IServerApplicationPaths.cs delete mode 100644 MediaBrowser.Controller/ServerApplicationPaths.cs create mode 100644 MediaBrowser.Model/Serialization/IJsonSerializer.cs create mode 100644 MediaBrowser.Model/Serialization/IProtobufSerializer.cs create mode 100644 MediaBrowser.Model/Serialization/IXmlSerializer.cs create mode 100644 MediaBrowser.Networking/Web/NativeWebSocket.cs create mode 100644 MediaBrowser.Networking/Web/ServerFactory.cs create mode 100644 MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj create mode 100644 MediaBrowser.Server.Implementations/Properties/AssemblyInfo.cs create mode 100644 MediaBrowser.Server.Implementations/ServerApplicationPaths.cs create mode 100644 MediaBrowser.Server.Implementations/Sqlite/SQLiteDisplayPreferencesRepository.cs create mode 100644 MediaBrowser.Server.Implementations/Sqlite/SQLiteExtensions.cs create mode 100644 MediaBrowser.Server.Implementations/Sqlite/SQLiteItemRepository.cs create mode 100644 MediaBrowser.Server.Implementations/Sqlite/SQLiteRepository.cs create mode 100644 MediaBrowser.Server.Implementations/Sqlite/SQLiteUserDataRepository.cs create mode 100644 MediaBrowser.Server.Implementations/Sqlite/SQLiteUserRepository.cs create mode 100644 MediaBrowser.Server.Implementations/WorldWeatherOnline/WeatherProvider.cs create mode 100644 MediaBrowser.Server.Implementations/packages.config delete mode 100644 MediaBrowser.Server.Sqlite/MediaBrowser.Server.Sqlite.csproj delete mode 100644 MediaBrowser.Server.Sqlite/Properties/AssemblyInfo.cs delete mode 100644 MediaBrowser.Server.Sqlite/SQLiteDisplayPreferencesRepository.cs delete mode 100644 MediaBrowser.Server.Sqlite/SQLiteExtensions.cs delete mode 100644 MediaBrowser.Server.Sqlite/SQLiteItemRepository.cs delete mode 100644 MediaBrowser.Server.Sqlite/SQLiteRepository.cs delete mode 100644 MediaBrowser.Server.Sqlite/SQLiteUserDataRepository.cs delete mode 100644 MediaBrowser.Server.Sqlite/SQLiteUserRepository.cs delete mode 100644 MediaBrowser.Server.Sqlite/packages.config delete mode 100644 MediaBrowser.Server.WorldWeatherOnline/MediaBrowser.Server.WorldWeatherOnline.csproj delete mode 100644 MediaBrowser.Server.WorldWeatherOnline/Properties/AssemblyInfo.cs delete mode 100644 MediaBrowser.Server.WorldWeatherOnline/WeatherProvider.cs create mode 100644 MediaBrowser.ServerApplication/ApplicationHost.cs diff --git a/MediaBrowser.Api/Javascript/ApiClient.js b/MediaBrowser.Api/Javascript/ApiClient.js new file mode 100644 index 000000000..df1c8f2c0 --- /dev/null +++ b/MediaBrowser.Api/Javascript/ApiClient.js @@ -0,0 +1,1360 @@ +/** + * Represents a javascript version of ApiClient. + * This should be kept up to date with all possible api methods and parameters + */ +var ApiClient = { + + serverProtocol: "http", + + /** + * Gets or sets the host name of the server + */ + serverHostName: "localhost", + + serverPortNumber: 8096, + + /** + * Detects the hostname and port of MB server based on the current url + */ + inferServerFromUrl: function () { + + var loc = window.location; + + ApiClient.serverProtocol = loc.protocol; + ApiClient.serverHostName = loc.hostname; + ApiClient.serverPortNumber = loc.port; + }, + + /** + * Creates an api url based on a handler name and query string parameters + * @param {String} name + * @param {Object} params + */ + getUrl: function (name, params) { + + if (!name) { + throw new Error("Url name cannot be empty"); + } + + params = params || {}; + + var url = ApiClient.serverProtocol + "//" + ApiClient.serverHostName + ":" + ApiClient.serverPortNumber + "/mediabrowser/" + name; + + if (params) { + url += "?" + $.param(params); + + } + return url; + }, + + /** + * Returns the name of the current browser + */ + getDeviceName: function () { + + /*if ($.browser.chrome) { + return "Chrome"; + } + if ($.browser.safari) { + return "Safari"; + } + if ($.browser.webkit) { + return "WebKit"; + } + if ($.browser.msie) { + return "Internet Explorer"; + } + if ($.browser.firefox) { + return "Firefox"; + } + if ($.browser.mozilla) { + return "Firefox"; + } + if ($.browser.opera) { + return "Opera"; + }*/ + + return "Web Browser"; + }, + + /** + * Creates a custom api url based on a handler name and query string parameters + * @param {String} name + * @param {Object} params + */ + getCustomUrl: function (name, params) { + + if (!name) { + throw new Error("Url name cannot be empty"); + } + + params = params || {}; + params.client = "Dashboard"; + params.device = ApiClient.getDeviceName(); + params.format = "json"; + + var url = ApiClient.serverProtocol + "//" + ApiClient.serverHostName + ":" + ApiClient.serverPortNumber + "/mediabrowser/" + name; + + if (params) { + url += "?" + $.param(params); + + } + return url; + }, + + /** + * Gets an item from the server + * Omit itemId to get the root folder. + */ + getItem: function (userId, itemId) { + + if (!userId) { + throw new Error("null userId"); + } + + var url = ApiClient.getUrl("Users/" + userId + "/Items/" + itemId); + + return $.getJSON(url); + }, + + /** + * Gets the root folder from the server + */ + getRootFolder: function (userId) { + + return ApiClient.getItem(userId); + }, + + /** + * Gets the current server status + */ + getSystemInfo: function () { + + var url = ApiClient.getUrl("System/Info"); + + return $.getJSON(url); + }, + + /** + * Gets all cultures known to the server + */ + getCultures: function () { + + var url = ApiClient.getUrl("Localization/cultures"); + + return $.getJSON(url); + }, + + /** + * Gets all countries known to the server + */ + getCountries: function () { + + var url = ApiClient.getUrl("Localization/countries"); + + return $.getJSON(url); + }, + + /** + * Gets plugin security info + */ + getPluginSecurityInfo: function () { + + var url = ApiClient.getUrl("Plugins/SecurityInfo"); + + return $.getJSON(url); + }, + + /** + * Gets the directory contents of a path on the server + */ + getDirectoryContents: function (path, options) { + + if (!path) { + throw new Error("null path"); + } + + options = options || {}; + + options.path = path; + + var url = ApiClient.getUrl("Environment/DirectoryContents", options); + + return $.getJSON(url); + }, + + /** + * Gets a list of physical drives from the server + */ + getDrives: function () { + + var url = ApiClient.getUrl("Environment/Drives"); + + return $.getJSON(url); + }, + + /** + * Gets a list of network computers from the server + */ + getNetworkComputers: function () { + + var url = ApiClient.getUrl("Environment/NetworkComputers"); + + return $.getJSON(url); + }, + + /** + * Cancels a package installation + */ + cancelPackageInstallation: function (installationId) { + + if (!installationId) { + throw new Error("null installationId"); + } + + var url = ApiClient.getUrl("Packages/Installing/" + id); + + return $.ajax({ + type: "DELETE", + url: url, + dataType: "json" + }); + }, + + /** + * Installs or updates a new plugin + */ + installPlugin: function (name, updateClass, version) { + + if (!name) { + throw new Error("null name"); + } + + if (!updateClass) { + throw new Error("null updateClass"); + } + + var options = { + updateClass: updateClass + }; + + if (version) { + options.version = version; + } + + var url = ApiClient.getUrl("Packages/Installed/" + name, options); + + return $.post(url); + }, + + /** + * Instructs the server to perform a pending kernel reload or app restart. + * If a restart is not currently required, nothing will happen. + */ + performPendingRestart: function () { + + var url = ApiClient.getUrl("System/Restart"); + + return $.post(url); + }, + + /** + * Gets information about an installable package + */ + getPackageInfo: function (name) { + + if (!name) { + throw new Error("null name"); + } + + var url = ApiClient.getUrl("Packages/" + name); + + return $.getJSON(url); + }, + + /** + * Gets the latest available application update (if any) + */ + getAvailableApplicationUpdate: function () { + + var url = ApiClient.getUrl("Packages/Updates", { PackageType: "System" }); + + return $.getJSON(url); + }, + + /** + * Gets the latest available plugin updates (if any) + */ + getAvailablePluginUpdates: function () { + + var url = ApiClient.getUrl("Packages/Updates", { PackageType: "UserInstalled" }); + + return $.getJSON(url); + }, + + /** + * Gets the virtual folder for a view. Specify a userId to get a user view, or omit for the default view. + */ + getVirtualFolders: function (userId) { + + var url = userId ? "Users/" + userId + "/VirtualFolders" : "Library/DefaultVirtualFolders"; + + url = ApiClient.getUrl(url); + + return $.getJSON(url); + }, + + /** + * Gets all the paths of the locations in the physical root. + */ + getPhysicalPaths: function () { + + var url = ApiClient.getUrl("Library/PhysicalPaths"); + + return $.getJSON(url); + }, + + /** + * Gets the current server configuration + */ + getServerConfiguration: function () { + + var url = ApiClient.getUrl("System/Configuration"); + + return $.getJSON(url); + }, + + /** + * Gets the server's scheduled tasks + */ + getScheduledTasks: function () { + + var url = ApiClient.getUrl("ScheduledTasks"); + + return $.getJSON(url); + }, + + /** + * Starts a scheduled task + */ + startScheduledTask: function (id) { + + if (!id) { + throw new Error("null id"); + } + + var url = ApiClient.getUrl("ScheduledTasks/Running/" + id); + + return $.post(url); + }, + + /** + * Gets a scheduled task + */ + getScheduledTask: function (id) { + + if (!id) { + throw new Error("null id"); + } + + var url = ApiClient.getUrl("ScheduledTasks/" + id); + + return $.getJSON(url); + }, + + /** + * Stops a scheduled task + */ + stopScheduledTask: function (id) { + + if (!id) { + throw new Error("null id"); + } + + var url = ApiClient.getUrl("ScheduledTasks/Running/" + id); + + return $.ajax({ + type: "DELETE", + url: url, + dataType: "json" + }); + }, + + /** + * Gets the configuration of a plugin + * @param {String} Id + */ + getPluginConfiguration: function (id) { + + if (!id) { + throw new Error("null Id"); + } + + var url = ApiClient.getUrl("Plugins/" + id + "/Configuration"); + + return $.getJSON(url); + }, + + /** + * Gets a list of plugins that are available to be installed + */ + getAvailablePlugins: function () { + + var url = ApiClient.getUrl("Packages", { PackageType: "UserInstalled" }); + + return $.getJSON(url); + }, + + /** + * Uninstalls a plugin + * @param {String} Id + */ + uninstallPlugin: function (id) { + + if (!id) { + throw new Error("null Id"); + } + + var url = ApiClient.getUrl("Plugins/" + id); + + return $.ajax({ + type: "DELETE", + url: url, + dataType: "json" + }); + }, + + /** + * Removes a virtual folder from either the default view or a user view + * @param {String} name + */ + removeVirtualFolder: function (name, userId) { + + if (!name) { + throw new Error("null name"); + } + + var params = { + name: name, + action: "RemoveVirtualFolder" + }; + + if (userId) { + params.userId = userId; + } + + var url = ApiClient.getUrl("UpdateMediaLibrary", params); + + return $.post(url); + }, + + /** + * Adds a virtual folder to either the default view or a user view + * @param {String} name + */ + addVirtualFolder: function (name, userId) { + + if (!name) { + throw new Error("null name"); + } + + var params = { + name: name, + action: "addVirtualFolder" + }; + + if (userId) { + params.userId = userId; + } + + var url = ApiClient.getUrl("UpdateMediaLibrary", params); + + return $.post(url); + }, + + /** + * Renames a virtual folder, within either the default view or a user view + * @param {String} name + */ + renameVirtualFolder: function (name, newName, userId) { + + if (!name) { + throw new Error("null name"); + } + + if (!newName) { + throw new Error("null newName"); + } + + var params = { + name: name, + newName: newName, + action: "RenameVirtualFolder" + }; + + if (userId) { + params.userId = userId; + } + + var url = ApiClient.getUrl("UpdateMediaLibrary", params); + + return $.post(url); + }, + + /** + * Adds an additional mediaPath to an existing virtual folder, within either the default view or a user view + * @param {String} name + */ + addMediaPath: function (virtualFolderName, mediaPath, userId) { + + if (!virtualFolderName) { + throw new Error("null virtualFolderName"); + } + + if (!mediaPath) { + throw new Error("null mediaPath"); + } + + var params = { + virtualFolderName: virtualFolderName, + mediaPath: mediaPath, + action: "addMediaPath" + }; + + if (userId) { + params.userId = userId; + } + + var url = ApiClient.getUrl("UpdateMediaLibrary", params); + + return $.post(url); + }, + + /** + * Removes a media path from a virtual folder, within either the default view or a user view + * @param {String} name + */ + removeMediaPath: function (virtualFolderName, mediaPath, userId) { + + if (!virtualFolderName) { + throw new Error("null virtualFolderName"); + } + + if (!mediaPath) { + throw new Error("null mediaPath"); + } + + var params = { + virtualFolderName: virtualFolderName, + mediaPath: mediaPath, + action: "RemoveMediaPath" + }; + + if (userId) { + params.userId = userId; + } + + var url = ApiClient.getUrl("UpdateMediaLibrary", params); + + return $.post(url); + }, + + /** + * Deletes a user + * @param {String} id + */ + deleteUser: function (id) { + + if (!id) { + throw new Error("null id"); + } + + var url = ApiClient.getUrl("Users/" + id); + + return $.ajax({ + type: "DELETE", + url: url, + dataType: "json" + }); + }, + + /** + * Deletes a user image + * @param {String} userId + * @param {String} imageType The type of image to delete, based on the server-side ImageType enum. + */ + deleteUserImage: function (userId, imageType) { + + if (!userId) { + throw new Error("null userId"); + } + + if (!imageType) { + throw new Error("null imageType"); + } + + var url = ApiClient.getUrl("Users/" + userId + "/Images/" + imageType); + + return $.ajax({ + type: "DELETE", + url: url, + dataType: "json" + }); + }, + + /** + * Uploads a user image + * @param {String} userId + * @param {String} imageType The type of image to delete, based on the server-side ImageType enum. + * @param {Object} file The file from the input element + */ + uploadUserImage: function (userId, imageType, file) { + + if (!userId) { + throw new Error("null userId"); + } + + if (!imageType) { + throw new Error("null imageType"); + } + + if (!file || !file.type.match('image.*')) { + throw new Error("File must be an image."); + } + + var deferred = $.Deferred(); + + var reader = new FileReader(); + + reader.onerror = function () { + deferred.reject(); + }; + + reader.onabort = function () { + deferred.reject(); + }; + + // Closure to capture the file information. + reader.onload = function (e) { + + var data = window.btoa(e.target.result); + + var params = { + userId: userId, + type: imageType, + filename: file.name + }; + + var url = ApiClient.getUrl("UploadImage", params); + + $.post(url, data).done(function (result) { + + deferred.resolveWith(null, [result]); + + }).fail(function () { + deferred.reject(); + }); + }; + + // Read in the image file as a data URL. + reader.readAsBinaryString(file); + + return deferred.promise(); + }, + + /** + * Gets the list of installed plugins on the server + */ + getInstalledPlugins: function () { + + var url = ApiClient.getUrl("Plugins"); + + return $.getJSON(url); + }, + + /** + * Gets a user by id + * @param {String} id + */ + getUser: function (id) { + + if (!id) { + throw new Error("Must supply a userId"); + } + + var url = ApiClient.getUrl("Users/" + id); + + return $.getJSON(url); + }, + + /** + * Gets a studio + */ + getStudio: function (name) { + + if (!name) { + throw new Error("null name"); + } + + var url = ApiClient.getUrl("Library/Studios/" + name); + + return $.getJSON(url); + }, + + /** + * Gets a genre + */ + getGenre: function (name) { + + if (!name) { + throw new Error("null name"); + } + + var url = ApiClient.getUrl("Library/Genres/" + name); + + return $.getJSON(url); + }, + + /** + * Gets a year + */ + getYear: function (year) { + + if (!year) { + throw new Error("null year"); + } + + var url = ApiClient.getUrl("Library/Years/" + year); + + return $.getJSON(url); + }, + + /** + * Gets a Person + */ + getPerson: function (name) { + + if (!name) { + throw new Error("null name"); + } + + var url = ApiClient.getUrl("Library/Persons/" + name); + + return $.getJSON(url); + }, + + /** + * Gets weather info + * @param {String} location - us zip code / city, state, country / city, country + * Omit location to get weather info using stored server configuration value + */ + getWeatherInfo: function (location) { + + var url = ApiClient.getUrl("weather", { + location: location + }); + + return $.getJSON(url); + }, + + /** + * Gets all users from the server + */ + getAllUsers: function () { + + var url = ApiClient.getUrl("users"); + + return $.getJSON(url); + }, + + /** + * Gets all available parental ratings from the server + */ + getParentalRatings: function () { + + var url = ApiClient.getUrl("Localization/ParentalRatings"); + + return $.getJSON(url); + }, + + /** + * Gets a list of all available conrete BaseItem types from the server + */ + getItemTypes: function (options) { + + var url = ApiClient.getUrl("Library/ItemTypes", options); + + return $.getJSON(url); + }, + + /** + * Constructs a url for a user image + * @param {String} userId + * @param {Object} options + * Options supports the following properties: + * width - download the image at a fixed width + * height - download the image at a fixed height + * maxWidth - download the image at a maxWidth + * maxHeight - download the image at a maxHeight + * quality - A scale of 0-100. This should almost always be omitted as the default will suffice. + * For best results do not specify both width and height together, as aspect ratio might be altered. + */ + getUserImageUrl: function (userId, options) { + + if (!userId) { + throw new Error("null userId"); + } + + options = options || { + }; + + var url = "Users/" + userId + "/Images/" + options.type; + + if (options.index != null) { + url += "/" + options.index; + } + + // Don't put these on the query string + delete options.type; + delete options.index; + + return ApiClient.getUrl(url, options); + }, + + /** + * Constructs a url for a person image + * @param {String} name + * @param {Object} options + * Options supports the following properties: + * width - download the image at a fixed width + * height - download the image at a fixed height + * maxWidth - download the image at a maxWidth + * maxHeight - download the image at a maxHeight + * quality - A scale of 0-100. This should almost always be omitted as the default will suffice. + * For best results do not specify both width and height together, as aspect ratio might be altered. + */ + getPersonImageUrl: function (name, options) { + + if (!name) { + throw new Error("null name"); + } + + options = options || { + }; + + var url = "Persons/" + name + "/Images/" + options.type; + + if (options.index != null) { + url += "/" + options.index; + } + + // Don't put these on the query string + delete options.type; + delete options.index; + + return ApiClient.getUrl(url, options); + }, + + /** + * Constructs a url for a year image + * @param {String} year + * @param {Object} options + * Options supports the following properties: + * width - download the image at a fixed width + * height - download the image at a fixed height + * maxWidth - download the image at a maxWidth + * maxHeight - download the image at a maxHeight + * quality - A scale of 0-100. This should almost always be omitted as the default will suffice. + * For best results do not specify both width and height together, as aspect ratio might be altered. + */ + getYearImageUrl: function (year, options) { + + if (!year) { + throw new Error("null year"); + } + + options = options || { + }; + + var url = "Years/" + year + "/Images/" + options.type; + + if (options.index != null) { + url += "/" + options.index; + } + + // Don't put these on the query string + delete options.type; + delete options.index; + + return ApiClient.getUrl(url, options); + }, + + /** + * Constructs a url for a genre image + * @param {String} name + * @param {Object} options + * Options supports the following properties: + * width - download the image at a fixed width + * height - download the image at a fixed height + * maxWidth - download the image at a maxWidth + * maxHeight - download the image at a maxHeight + * quality - A scale of 0-100. This should almost always be omitted as the default will suffice. + * For best results do not specify both width and height together, as aspect ratio might be altered. + */ + getGenreImageUrl: function (name, options) { + + if (!name) { + throw new Error("null name"); + } + + options = options || { + }; + + var url = "Genres/" + name + "/Images/" + options.type; + + if (options.index != null) { + url += "/" + options.index; + } + + // Don't put these on the query string + delete options.type; + delete options.index; + + return ApiClient.getUrl(url, options); + }, + + /** + * Constructs a url for a genre image + * @param {String} name + * @param {Object} options + * Options supports the following properties: + * width - download the image at a fixed width + * height - download the image at a fixed height + * maxWidth - download the image at a maxWidth + * maxHeight - download the image at a maxHeight + * quality - A scale of 0-100. This should almost always be omitted as the default will suffice. + * For best results do not specify both width and height together, as aspect ratio might be altered. + */ + getStudioImageUrl: function (name, options) { + + if (!name) { + throw new Error("null name"); + } + + options = options || { + }; + + var url = "Studios/" + name + "/Images/" + options.type; + + if (options.index != null) { + url += "/" + options.index; + } + + // Don't put these on the query string + delete options.type; + delete options.index; + + return ApiClient.getUrl(url, options); + }, + + /** + * Constructs a url for an item image + * @param {String} itemId + * @param {Object} options + * Options supports the following properties: + * type - Primary, logo, backdrop, etc. See the server-side enum ImageType + * index - When downloading a backdrop, use this to specify which one (omitting is equivalent to zero) + * width - download the image at a fixed width + * height - download the image at a fixed height + * maxWidth - download the image at a maxWidth + * maxHeight - download the image at a maxHeight + * quality - A scale of 0-100. This should almost always be omitted as the default will suffice. + * For best results do not specify both width and height together, as aspect ratio might be altered. + */ + getImageUrl: function (itemId, options) { + + if (!itemId) { + throw new Error("itemId cannot be empty"); + } + + options = options || { + }; + + var url = "Items/" + itemId + "/Images/" + options.type; + + if (options.index != null) { + url += "/" + options.index; + } + + // Don't put these on the query string + delete options.type; + delete options.index; + + return ApiClient.getUrl(url, options); + }, + + /** + * Constructs a url for an item logo image + * If the item doesn't have a logo, it will inherit a logo from a parent + * @param {Object} item A BaseItem + * @param {Object} options + * Options supports the following properties: + * width - download the image at a fixed width + * height - download the image at a fixed height + * maxWidth - download the image at a maxWidth + * maxHeight - download the image at a maxHeight + * quality - A scale of 0-100. This should almost always be omitted as the default will suffice. + * For best results do not specify both width and height together, as aspect ratio might be altered. + */ + getLogoImageUrl: function (item, options) { + + if (!item) { + throw new Error("null item"); + } + + options = options || { + }; + + options.imageType = "logo"; + + var logoItemId = item.HasLogo ? item.Id : item.ParentLogoItemId; + + return logoItemId ? ApiClient.getImageUrl(logoItemId, options) : null; + }, + + /** + * Constructs an array of backdrop image url's for an item + * If the item doesn't have any backdrops, it will inherit them from a parent + * @param {Object} item A BaseItem + * @param {Object} options + * Options supports the following properties: + * width - download the image at a fixed width + * height - download the image at a fixed height + * maxWidth - download the image at a maxWidth + * maxHeight - download the image at a maxHeight + * quality - A scale of 0-100. This should almost always be omitted as the default will suffice. + * For best results do not specify both width and height together, as aspect ratio might be altered. + */ + getBackdropImageUrl: function (item, options) { + + if (!item) { + throw new Error("null item"); + } + + options = options || { + }; + + options.imageType = "backdrop"; + + var backdropItemId; + var backdropCount; + + if (!item.BackdropCount) { + backdropItemId = item.ParentBackdropItemId; + backdropCount = item.ParentBackdropCount || 0; + } else { + backdropItemId = item.Id; + backdropCount = item.BackdropCount; + } + + if (!backdropItemId) { + return []; + } + + var files = []; + + for (var i = 0; i < backdropCount; i++) { + + options.imageIndex = i; + + files[i] = ApiClient.getImageUrl(backdropItemId, options); + } + + return files; + }, + + /** + * Authenticates a user + * @param {String} userId + * @param {String} password + */ + authenticateUser: function (userId, password) { + + if (!userId) { + throw new Error("null userId"); + } + + var url = ApiClient.getUrl("Users/" + userId + "/authenticate"); + + var postData = { + }; + + if (password) { + postData.password = password; + } + return $.post(url, postData); + }, + + /** + * Updates a user's password + * @param {String} userId + * @param {String} currentPassword + * @param {String} newPassword + */ + updateUserPassword: function (userId, currentPassword, newPassword) { + + if (!userId) { + throw new Error("null userId"); + } + + var url = ApiClient.getUrl("Users/" + userId + "/Password"); + + var postData = { + }; + + if (currentPassword) { + postData.currentPassword = currentPassword; + } + if (newPassword) { + postData.newPassword = newPassword; + } + return $.post(url, postData); + }, + + /** + * Resets a user's password + * @param {String} userId + */ + resetUserPassword: function (userId) { + + if (!userId) { + throw new Error("null userId"); + } + + var url = ApiClient.getUrl("Users/" + userId + "/Password"); + + var postData = { + }; + + postData.resetPassword = 1; + return $.post(url, postData); + }, + + /** + * Updates the server's configuration + * @param {Object} configuration + */ + updateServerConfiguration: function (configuration) { + + if (!configuration) { + throw new Error("null configuration"); + } + + var url = ApiClient.getUrl("System/Configuration"); + + return $.post(url, JSON.stringify(configuration)); + }, + + /** + * Creates a user + * @param {Object} user + */ + createUser: function (user) { + + if (!user) { + throw new Error("null user"); + } + + var url = ApiClient.getUrl("Users"); + + return $.post(url, JSON.stringify(user)); + }, + + /** + * Updates a user + * @param {Object} user + */ + updateUser: function (user) { + + if (!user) { + throw new Error("null user"); + } + + var url = ApiClient.getUrl("Users/" + user.Id); + + return $.post(url, JSON.stringify(user)); + }, + + /** + * Updates the Triggers for a ScheduledTask + * @param {String} id + * @param {Object} triggers + */ + updateScheduledTaskTriggers: function (id, triggers) { + + if (!id) { + throw new Error("null id"); + } + + if (!triggers) { + throw new Error("null triggers"); + } + + var url = ApiClient.getUrl("ScheduledTasks/" + id + "/Triggers"); + + return $.post(url, JSON.stringify(triggers)); + }, + + /** + * Updates a plugin's configuration + * @param {String} Id + * @param {Object} configuration + */ + updatePluginConfiguration: function (id, configuration) { + + if (!id) { + throw new Error("null Id"); + } + + if (!configuration) { + throw new Error("null configuration"); + } + + var url = ApiClient.getUrl("Plugins/" + id + "/Configuration"); + + return $.post(url, JSON.stringify(configuration)); + }, + + /** + * Gets items based on a query, typicall for children of a folder + * @param {String} userId + * @param {Object} options + * Options accepts the following properties: + * itemId - Localize the search to a specific folder (root if omitted) + * startIndex - Use for paging + * limit - Use to limit results to a certain number of items + * filter - Specify one or more ItemFilters, comma delimeted (see server-side enum) + * sortBy - Specify an ItemSortBy (comma-delimeted list see server-side enum) + * sortOrder - ascending/descending + * fields - additional fields to include aside from basic info. This is a comma delimited list. See server-side enum ItemFields. + * index - the name of the dynamic, localized index function + * dynamicSortBy - the name of the dynamic localized sort function + * recursive - Whether or not the query should be recursive + * searchTerm - search term to use as a filter + */ + getItems: function (userId, options) { + + if (!userId) { + throw new Error("null userId"); + } + + return $.getJSON(ApiClient.getUrl("Users/" + userId + "/Items", options)); + }, + + /** + * Marks an item as played or unplayed + * This should not be used to update playstate following playback. + * There are separate playstate check-in methods for that. This should be used for a + * separate option to reset playstate. + * @param {String} userId + * @param {String} itemId + * @param {Boolean} wasPlayed + */ + updatePlayedStatus: function (userId, itemId, wasPlayed) { + + if (!userId) { + throw new Error("null userId"); + } + + if (!itemId) { + throw new Error("null itemId"); + } + + var url = "Users/" + userId + "/PlayedItems/" + itemId; + + var method = wasPlayed ? "POST" : "DELETE"; + + return $.ajax({ + type: method, + url: url, + dataType: "json" + }); + }, + + /** + * Updates a user's favorite status for an item and returns the updated UserItemData object. + * @param {String} userId + * @param {String} itemId + * @param {Boolean} isFavorite + */ + updateFavoriteStatus: function (userId, itemId, isFavorite) { + + if (!userId) { + throw new Error("null userId"); + } + + if (!itemId) { + throw new Error("null itemId"); + } + + var url = "Users/" + userId + "/FavoriteItems/" + itemId; + + var method = isFavorite ? "POST" : "DELETE"; + + return $.ajax({ + type: method, + url: url, + dataType: "json" + }); + }, + + /** + * Updates a user's personal rating for an item + * @param {String} userId + * @param {String} itemId + * @param {Boolean} likes + */ + updateUserItemRating: function (userId, itemId, likes) { + + if (!userId) { + throw new Error("null userId"); + } + + if (!itemId) { + throw new Error("null itemId"); + } + + var url = ApiClient.getUrl("Users/" + userId + "/Items/" + itemId + "/Rating", { + likes: likes + }); + + return $.post(url); + }, + + /** + * Clears a user's personal rating for an item + * @param {String} userId + * @param {String} itemId + */ + clearUserItemRating: function (userId, itemId) { + + if (!userId) { + throw new Error("null userId"); + } + + if (!itemId) { + throw new Error("null itemId"); + } + + var url = ApiClient.getUrl("Users/" + userId + "/Items/" + itemId + "/Rating"); + + return $.ajax({ + type: "DELETE", + url: url, + dataType: "json" + }); + } +}; + +// Do this initially. The consumer can always override later +ApiClient.inferServerFromUrl(); diff --git a/MediaBrowser.Api/Javascript/JavascriptApiClientService.cs b/MediaBrowser.Api/Javascript/JavascriptApiClientService.cs new file mode 100644 index 000000000..8c7499a7c --- /dev/null +++ b/MediaBrowser.Api/Javascript/JavascriptApiClientService.cs @@ -0,0 +1,58 @@ +using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.Net; +using ServiceStack.ServiceHost; +using System; +using System.IO; +using System.Threading.Tasks; + +namespace MediaBrowser.Api.Javascript +{ + /// + /// Class GetJavascriptApiClient + /// + [Route("/JsApiClient.js", "GET")] + [ServiceStack.ServiceHost.Api(("Gets an api wrapper in Javascript"))] + public class GetJavascriptApiClient + { + /// + /// Version identifier for caching + /// + /// The v. + public string V { get; set; } + } + + /// + /// Class JavascriptApiClientService + /// + public class JavascriptApiClientService : BaseRestService + { + /// + /// Gets the specified request. + /// + /// The request. + /// System.Object. + public object Get(GetJavascriptApiClient request) + { + TimeSpan? cacheDuration = null; + + // If there's a version number in the query string we can cache this unconditionally + if (!string.IsNullOrEmpty(request.V)) + { + cacheDuration = TimeSpan.FromDays(365); + } + + var assembly = GetType().Assembly.GetName(); + + return ToStaticResult(assembly.Version.ToString().GetMD5(), null, cacheDuration, MimeTypes.GetMimeType("script.js"), GetStream); + } + + /// + /// Gets the stream. + /// + /// Stream. + private Task GetStream() + { + return Task.FromResult(GetType().Assembly.GetManifestResourceStream("MediaBrowser.Api.Javascript.ApiClient.js")); + } + } +} diff --git a/MediaBrowser.Api/LibraryService.cs b/MediaBrowser.Api/LibraryService.cs index 4ca073c10..434b04eff 100644 --- a/MediaBrowser.Api/LibraryService.cs +++ b/MediaBrowser.Api/LibraryService.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Common.Net; +using MediaBrowser.Common.Kernel; +using MediaBrowser.Common.Net; using MediaBrowser.Controller; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; @@ -97,6 +98,26 @@ namespace MediaBrowser.Api /// public class LibraryService : BaseRestService { + /// + /// The _app host + /// + private readonly IApplicationHost _appHost; + + /// + /// Initializes a new instance of the class. + /// + /// The app host. + /// appHost + public LibraryService(IApplicationHost appHost) + { + if (appHost == null) + { + throw new ArgumentNullException("appHost"); + } + + _appHost = appHost; + } + /// /// Gets the specified request. /// @@ -210,7 +231,7 @@ namespace MediaBrowser.Api { var kernel = (Kernel)Kernel; - var allTypes = kernel.AllTypes.Where(t => !t.IsAbstract && t.IsSubclassOf(typeof(BaseItem))); + var allTypes = _appHost.AllConcreteTypes.Where(t => t.IsSubclassOf(typeof(BaseItem))); if (request.HasInternetProvider) { diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj index 9bdcdb650..c6c822624 100644 --- a/MediaBrowser.Api/MediaBrowser.Api.csproj +++ b/MediaBrowser.Api/MediaBrowser.Api.csproj @@ -80,6 +80,7 @@ + @@ -128,6 +129,7 @@ + diff --git a/MediaBrowser.Api/PluginService.cs b/MediaBrowser.Api/PluginService.cs index f982602bf..1f906f814 100644 --- a/MediaBrowser.Api/PluginService.cs +++ b/MediaBrowser.Api/PluginService.cs @@ -1,15 +1,15 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; -using MediaBrowser.Common.Serialization; using MediaBrowser.Controller; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Plugins; +using MediaBrowser.Model.Serialization; using ServiceStack.ServiceHost; +using ServiceStack.Text.Controller; using System; using System.Collections.Generic; using System.IO; using System.Linq; -using ServiceStack.Text.Controller; namespace MediaBrowser.Api { @@ -119,6 +119,27 @@ namespace MediaBrowser.Api /// public class PluginService : BaseRestService { + /// + /// The _json serializer + /// + private readonly IJsonSerializer _jsonSerializer; + + /// + /// Initializes a new instance of the class. + /// + /// The json serializer. + /// jsonSerializer + public PluginService(IJsonSerializer jsonSerializer) + : base() + { + if (jsonSerializer == null) + { + throw new ArgumentNullException("jsonSerializer"); + } + + _jsonSerializer = jsonSerializer; + } + /// /// Gets the specified request. /// @@ -198,7 +219,7 @@ namespace MediaBrowser.Api { var kernel = (Kernel)Kernel; - var info = JsonSerializer.DeserializeFromStream(request.RequestStream); + var info = _jsonSerializer.DeserializeFromStream(request.RequestStream); kernel.PluginSecurityManager.SupporterKey = info.SupporterKey; kernel.PluginSecurityManager.LegacyKey = info.LegacyKey; @@ -217,7 +238,7 @@ namespace MediaBrowser.Api var plugin = Kernel.Plugins.First(p => p.Id == id); - var configuration = JsonSerializer.DeserializeFromStream(request.RequestStream, plugin.ConfigurationType) as BasePluginConfiguration; + var configuration = _jsonSerializer.DeserializeFromStream(request.RequestStream, plugin.ConfigurationType) as BasePluginConfiguration; plugin.UpdateConfiguration(configuration); } diff --git a/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs b/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs index 1ca744542..e79ab90da 100644 --- a/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs +++ b/MediaBrowser.Api/ScheduledTasks/ScheduledTaskService.cs @@ -1,7 +1,7 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Common.ScheduledTasks; -using MediaBrowser.Common.Serialization; +using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Tasks; using ServiceStack.ServiceHost; using System; @@ -90,11 +90,32 @@ namespace MediaBrowser.Api.ScheduledTasks /// The task manager. private ITaskManager TaskManager { get; set; } - public ScheduledTaskService(ITaskManager taskManager) + /// + /// The _json serializer + /// + private readonly IJsonSerializer _jsonSerializer; + + /// + /// Initializes a new instance of the class. + /// + /// The task manager. + /// The json serializer. + /// taskManager + public ScheduledTaskService(ITaskManager taskManager, IJsonSerializer jsonSerializer) { + if (taskManager == null) + { + throw new ArgumentNullException("taskManager"); + } + if (jsonSerializer == null) + { + throw new ArgumentNullException("jsonSerializer"); + } + TaskManager = taskManager; + _jsonSerializer = jsonSerializer; } - + /// /// Gets the specified request. /// @@ -113,6 +134,7 @@ namespace MediaBrowser.Api.ScheduledTasks /// /// The request. /// IEnumerable{TaskInfo}. + /// Task not found public object Get(GetScheduledTask request) { var task = TaskManager.ScheduledTasks.FirstOrDefault(i => i.Id == request.Id); @@ -131,6 +153,7 @@ namespace MediaBrowser.Api.ScheduledTasks /// Posts the specified request. /// /// The request. + /// Task not found public void Post(StartScheduledTask request) { var task = TaskManager.ScheduledTasks.FirstOrDefault(i => i.Id == request.Id); @@ -147,6 +170,7 @@ namespace MediaBrowser.Api.ScheduledTasks /// Posts the specified request. /// /// The request. + /// Task not found public void Delete(StopScheduledTask request) { var task = TaskManager.ScheduledTasks.FirstOrDefault(i => i.Id == request.Id); @@ -163,6 +187,7 @@ namespace MediaBrowser.Api.ScheduledTasks /// Posts the specified request. /// /// The request. + /// Task not found public void Post(UpdateScheduledTaskTriggers request) { // We need to parse this manually because we told service stack not to with IRequiresRequestStream @@ -177,7 +202,7 @@ namespace MediaBrowser.Api.ScheduledTasks throw new ResourceNotFoundException("Task not found"); } - var triggerInfos = JsonSerializer.DeserializeFromStream(request.RequestStream); + var triggerInfos = _jsonSerializer.DeserializeFromStream(request.RequestStream); task.Triggers = triggerInfos.Select(ScheduledTaskHelpers.GetTrigger); } diff --git a/MediaBrowser.Api/SystemService.cs b/MediaBrowser.Api/SystemService.cs index 04632aa8e..7921d024a 100644 --- a/MediaBrowser.Api/SystemService.cs +++ b/MediaBrowser.Api/SystemService.cs @@ -1,15 +1,19 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; -using MediaBrowser.Common.Serialization; using MediaBrowser.Controller; using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Serialization; using MediaBrowser.Model.System; using ServiceStack.ServiceHost; +using System; using System.IO; using System.Threading.Tasks; namespace MediaBrowser.Api { + /// + /// Class GetSystemInfo + /// [Route("/System/Info", "GET")] public class GetSystemInfo : IReturn { @@ -40,6 +44,10 @@ namespace MediaBrowser.Api [Route("/System/Configuration", "POST")] public class UpdateConfiguration : IRequiresRequestStream { + /// + /// The raw Http Request Input Stream + /// + /// The request stream. public Stream RequestStream { get; set; } } @@ -48,6 +56,27 @@ namespace MediaBrowser.Api /// public class SystemService : BaseRestService { + /// + /// The _json serializer + /// + private readonly IJsonSerializer _jsonSerializer; + + /// + /// Initializes a new instance of the class. + /// + /// The json serializer. + /// jsonSerializer + public SystemService(IJsonSerializer jsonSerializer) + : base() + { + if (jsonSerializer == null) + { + throw new ArgumentNullException("jsonSerializer"); + } + + _jsonSerializer = jsonSerializer; + } + /// /// Gets the specified request. /// @@ -95,8 +124,8 @@ namespace MediaBrowser.Api /// The request. public void Post(UpdateConfiguration request) { - var serverConfig = JsonSerializer.DeserializeFromStream(request.RequestStream); - + var serverConfig = _jsonSerializer.DeserializeFromStream(request.RequestStream); + var kernel = (Kernel)Kernel; kernel.UpdateConfiguration(serverConfig); diff --git a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs index 576ff8892..4267947ad 100644 --- a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs +++ b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs @@ -1,11 +1,11 @@ using MediaBrowser.Common.Net; -using MediaBrowser.Common.Serialization; using MediaBrowser.Controller; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Serialization; using ServiceStack.ServiceHost; using System; using System.Collections.Generic; @@ -28,7 +28,7 @@ namespace MediaBrowser.Api.UserLibrary /// /// The user id. public Guid UserId { get; set; } - + /// /// Gets or sets the id. /// @@ -48,7 +48,7 @@ namespace MediaBrowser.Api.UserLibrary /// /// The user id. public Guid UserId { get; set; } - + /// /// Gets or sets the item id. /// @@ -68,7 +68,7 @@ namespace MediaBrowser.Api.UserLibrary /// /// The user id. public Guid UserId { get; set; } - + /// /// Gets or sets the id. /// @@ -106,7 +106,7 @@ namespace MediaBrowser.Api.UserLibrary /// /// The user id. public Guid UserId { get; set; } - + /// /// Gets or sets the id. /// @@ -125,7 +125,7 @@ namespace MediaBrowser.Api.UserLibrary /// /// The user id. public Guid UserId { get; set; } - + /// /// Gets or sets the id. /// @@ -144,7 +144,7 @@ namespace MediaBrowser.Api.UserLibrary /// /// The user id. public Guid UserId { get; set; } - + /// /// Gets or sets the id. /// @@ -163,7 +163,7 @@ namespace MediaBrowser.Api.UserLibrary /// /// The user id. public Guid UserId { get; set; } - + /// /// Gets or sets the id. /// @@ -215,6 +215,9 @@ namespace MediaBrowser.Api.UserLibrary public string Id { get; set; } } + /// + /// Class GetLocalTrailers + /// [Route("/Users/{UserId}/Items/{Id}/LocalTrailers", "GET")] public class GetLocalTrailers : IReturn> { @@ -231,6 +234,9 @@ namespace MediaBrowser.Api.UserLibrary public string Id { get; set; } } + /// + /// Class GetSpecialFeatures + /// [Route("/Users/{UserId}/Items/{Id}/SpecialFeatures", "GET")] public class GetSpecialFeatures : IReturn> { @@ -253,6 +259,32 @@ namespace MediaBrowser.Api.UserLibrary /// public class UserLibraryService : BaseRestService { + /// + /// The _json serializer + /// + private readonly IJsonSerializer _jsonSerializer; + + /// + /// Initializes a new instance of the class. + /// + /// The json serializer. + /// jsonSerializer + public UserLibraryService(IJsonSerializer jsonSerializer) + : base() + { + if (jsonSerializer == null) + { + throw new ArgumentNullException("jsonSerializer"); + } + + _jsonSerializer = jsonSerializer; + } + + /// + /// Gets the specified request. + /// + /// The request. + /// System.Object. public object Get(GetSpecialFeatures request) { var kernel = (Kernel)Kernel; @@ -260,7 +292,7 @@ namespace MediaBrowser.Api.UserLibrary var user = kernel.GetUserById(request.UserId); var item = DtoBuilder.GetItemByClientId(request.Id, user.Id); - + // Get everything var fields = Enum.GetNames(typeof(ItemFields)).Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)).ToList(); @@ -272,7 +304,12 @@ namespace MediaBrowser.Api.UserLibrary return ToOptimizedResult(items); } - + + /// + /// Gets the specified request. + /// + /// The request. + /// System.Object. public object Get(GetLocalTrailers request) { var kernel = (Kernel)Kernel; @@ -280,7 +317,7 @@ namespace MediaBrowser.Api.UserLibrary var user = kernel.GetUserById(request.UserId); var item = DtoBuilder.GetItemByClientId(request.Id, user.Id); - + // Get everything var fields = Enum.GetNames(typeof(ItemFields)).Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)).ToList(); @@ -290,7 +327,7 @@ namespace MediaBrowser.Api.UserLibrary return ToOptimizedResult(items); } - + /// /// Gets the specified request. /// @@ -303,7 +340,7 @@ namespace MediaBrowser.Api.UserLibrary var user = kernel.GetUserById(request.UserId); var item = string.IsNullOrEmpty(request.Id) ? user.RootFolder : DtoBuilder.GetItemByClientId(request.Id, user.Id); - + // Get everything var fields = Enum.GetNames(typeof(ItemFields)).Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)).ToList(); @@ -366,7 +403,7 @@ namespace MediaBrowser.Api.UserLibrary var item = (Folder)DtoBuilder.GetItemByClientId(itemId, user.Id); - var displayPreferences = JsonSerializer.DeserializeFromStream(request.RequestStream); + var displayPreferences = _jsonSerializer.DeserializeFromStream(request.RequestStream); var task = kernel.LibraryManager.SaveDisplayPreferencesForFolder(user, item, displayPreferences); diff --git a/MediaBrowser.Api/UserService.cs b/MediaBrowser.Api/UserService.cs index c76479d19..d4490d856 100644 --- a/MediaBrowser.Api/UserService.cs +++ b/MediaBrowser.Api/UserService.cs @@ -1,16 +1,16 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; -using MediaBrowser.Common.Serialization; using MediaBrowser.Controller; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Serialization; using ServiceStack.ServiceHost; +using ServiceStack.Text.Controller; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; -using ServiceStack.Text.Controller; namespace MediaBrowser.Api { @@ -135,6 +135,39 @@ namespace MediaBrowser.Api /// public class UserService : BaseRestService { + /// + /// The _XML serializer + /// + private readonly IXmlSerializer _xmlSerializer; + + /// + /// The _json serializer + /// + private readonly IJsonSerializer _jsonSerializer; + + /// + /// Initializes a new instance of the class. + /// + /// The XML serializer. + /// The json serializer. + /// xmlSerializer + public UserService(IXmlSerializer xmlSerializer, IJsonSerializer jsonSerializer) + : base() + { + if (jsonSerializer == null) + { + throw new ArgumentNullException("jsonSerializer"); + } + + if (xmlSerializer == null) + { + throw new ArgumentNullException("xmlSerializer"); + } + + _jsonSerializer = jsonSerializer; + _xmlSerializer = xmlSerializer; + } + /// /// Gets the specified request. /// @@ -262,10 +295,10 @@ namespace MediaBrowser.Api // https://code.google.com/p/servicestack/source/browse/trunk/Common/ServiceStack.Text/ServiceStack.Text/Controller/PathInfo.cs var pathInfo = PathInfo.Parse(Request.PathInfo); var id = new Guid(pathInfo.GetArgumentValue(1)); - + var kernel = (Kernel)Kernel; - var dtoUser = JsonSerializer.DeserializeFromStream(request.RequestStream); + var dtoUser = _jsonSerializer.DeserializeFromStream(request.RequestStream); var user = kernel.GetUserById(id); @@ -273,7 +306,7 @@ namespace MediaBrowser.Api Task.WaitAll(task); - user.UpdateConfiguration(dtoUser.Configuration); + user.UpdateConfiguration(dtoUser.Configuration, _xmlSerializer); } /// @@ -285,7 +318,7 @@ namespace MediaBrowser.Api { var kernel = (Kernel)Kernel; - var dtoUser = JsonSerializer.DeserializeFromStream(request.RequestStream); + var dtoUser = _jsonSerializer.DeserializeFromStream(request.RequestStream); var newUser = kernel.UserManager.CreateUser(dtoUser.Name).Result; diff --git a/MediaBrowser.ApiInteraction.Javascript/ApiClient.js b/MediaBrowser.ApiInteraction.Javascript/ApiClient.js deleted file mode 100644 index df1c8f2c0..000000000 --- a/MediaBrowser.ApiInteraction.Javascript/ApiClient.js +++ /dev/null @@ -1,1360 +0,0 @@ -/** - * Represents a javascript version of ApiClient. - * This should be kept up to date with all possible api methods and parameters - */ -var ApiClient = { - - serverProtocol: "http", - - /** - * Gets or sets the host name of the server - */ - serverHostName: "localhost", - - serverPortNumber: 8096, - - /** - * Detects the hostname and port of MB server based on the current url - */ - inferServerFromUrl: function () { - - var loc = window.location; - - ApiClient.serverProtocol = loc.protocol; - ApiClient.serverHostName = loc.hostname; - ApiClient.serverPortNumber = loc.port; - }, - - /** - * Creates an api url based on a handler name and query string parameters - * @param {String} name - * @param {Object} params - */ - getUrl: function (name, params) { - - if (!name) { - throw new Error("Url name cannot be empty"); - } - - params = params || {}; - - var url = ApiClient.serverProtocol + "//" + ApiClient.serverHostName + ":" + ApiClient.serverPortNumber + "/mediabrowser/" + name; - - if (params) { - url += "?" + $.param(params); - - } - return url; - }, - - /** - * Returns the name of the current browser - */ - getDeviceName: function () { - - /*if ($.browser.chrome) { - return "Chrome"; - } - if ($.browser.safari) { - return "Safari"; - } - if ($.browser.webkit) { - return "WebKit"; - } - if ($.browser.msie) { - return "Internet Explorer"; - } - if ($.browser.firefox) { - return "Firefox"; - } - if ($.browser.mozilla) { - return "Firefox"; - } - if ($.browser.opera) { - return "Opera"; - }*/ - - return "Web Browser"; - }, - - /** - * Creates a custom api url based on a handler name and query string parameters - * @param {String} name - * @param {Object} params - */ - getCustomUrl: function (name, params) { - - if (!name) { - throw new Error("Url name cannot be empty"); - } - - params = params || {}; - params.client = "Dashboard"; - params.device = ApiClient.getDeviceName(); - params.format = "json"; - - var url = ApiClient.serverProtocol + "//" + ApiClient.serverHostName + ":" + ApiClient.serverPortNumber + "/mediabrowser/" + name; - - if (params) { - url += "?" + $.param(params); - - } - return url; - }, - - /** - * Gets an item from the server - * Omit itemId to get the root folder. - */ - getItem: function (userId, itemId) { - - if (!userId) { - throw new Error("null userId"); - } - - var url = ApiClient.getUrl("Users/" + userId + "/Items/" + itemId); - - return $.getJSON(url); - }, - - /** - * Gets the root folder from the server - */ - getRootFolder: function (userId) { - - return ApiClient.getItem(userId); - }, - - /** - * Gets the current server status - */ - getSystemInfo: function () { - - var url = ApiClient.getUrl("System/Info"); - - return $.getJSON(url); - }, - - /** - * Gets all cultures known to the server - */ - getCultures: function () { - - var url = ApiClient.getUrl("Localization/cultures"); - - return $.getJSON(url); - }, - - /** - * Gets all countries known to the server - */ - getCountries: function () { - - var url = ApiClient.getUrl("Localization/countries"); - - return $.getJSON(url); - }, - - /** - * Gets plugin security info - */ - getPluginSecurityInfo: function () { - - var url = ApiClient.getUrl("Plugins/SecurityInfo"); - - return $.getJSON(url); - }, - - /** - * Gets the directory contents of a path on the server - */ - getDirectoryContents: function (path, options) { - - if (!path) { - throw new Error("null path"); - } - - options = options || {}; - - options.path = path; - - var url = ApiClient.getUrl("Environment/DirectoryContents", options); - - return $.getJSON(url); - }, - - /** - * Gets a list of physical drives from the server - */ - getDrives: function () { - - var url = ApiClient.getUrl("Environment/Drives"); - - return $.getJSON(url); - }, - - /** - * Gets a list of network computers from the server - */ - getNetworkComputers: function () { - - var url = ApiClient.getUrl("Environment/NetworkComputers"); - - return $.getJSON(url); - }, - - /** - * Cancels a package installation - */ - cancelPackageInstallation: function (installationId) { - - if (!installationId) { - throw new Error("null installationId"); - } - - var url = ApiClient.getUrl("Packages/Installing/" + id); - - return $.ajax({ - type: "DELETE", - url: url, - dataType: "json" - }); - }, - - /** - * Installs or updates a new plugin - */ - installPlugin: function (name, updateClass, version) { - - if (!name) { - throw new Error("null name"); - } - - if (!updateClass) { - throw new Error("null updateClass"); - } - - var options = { - updateClass: updateClass - }; - - if (version) { - options.version = version; - } - - var url = ApiClient.getUrl("Packages/Installed/" + name, options); - - return $.post(url); - }, - - /** - * Instructs the server to perform a pending kernel reload or app restart. - * If a restart is not currently required, nothing will happen. - */ - performPendingRestart: function () { - - var url = ApiClient.getUrl("System/Restart"); - - return $.post(url); - }, - - /** - * Gets information about an installable package - */ - getPackageInfo: function (name) { - - if (!name) { - throw new Error("null name"); - } - - var url = ApiClient.getUrl("Packages/" + name); - - return $.getJSON(url); - }, - - /** - * Gets the latest available application update (if any) - */ - getAvailableApplicationUpdate: function () { - - var url = ApiClient.getUrl("Packages/Updates", { PackageType: "System" }); - - return $.getJSON(url); - }, - - /** - * Gets the latest available plugin updates (if any) - */ - getAvailablePluginUpdates: function () { - - var url = ApiClient.getUrl("Packages/Updates", { PackageType: "UserInstalled" }); - - return $.getJSON(url); - }, - - /** - * Gets the virtual folder for a view. Specify a userId to get a user view, or omit for the default view. - */ - getVirtualFolders: function (userId) { - - var url = userId ? "Users/" + userId + "/VirtualFolders" : "Library/DefaultVirtualFolders"; - - url = ApiClient.getUrl(url); - - return $.getJSON(url); - }, - - /** - * Gets all the paths of the locations in the physical root. - */ - getPhysicalPaths: function () { - - var url = ApiClient.getUrl("Library/PhysicalPaths"); - - return $.getJSON(url); - }, - - /** - * Gets the current server configuration - */ - getServerConfiguration: function () { - - var url = ApiClient.getUrl("System/Configuration"); - - return $.getJSON(url); - }, - - /** - * Gets the server's scheduled tasks - */ - getScheduledTasks: function () { - - var url = ApiClient.getUrl("ScheduledTasks"); - - return $.getJSON(url); - }, - - /** - * Starts a scheduled task - */ - startScheduledTask: function (id) { - - if (!id) { - throw new Error("null id"); - } - - var url = ApiClient.getUrl("ScheduledTasks/Running/" + id); - - return $.post(url); - }, - - /** - * Gets a scheduled task - */ - getScheduledTask: function (id) { - - if (!id) { - throw new Error("null id"); - } - - var url = ApiClient.getUrl("ScheduledTasks/" + id); - - return $.getJSON(url); - }, - - /** - * Stops a scheduled task - */ - stopScheduledTask: function (id) { - - if (!id) { - throw new Error("null id"); - } - - var url = ApiClient.getUrl("ScheduledTasks/Running/" + id); - - return $.ajax({ - type: "DELETE", - url: url, - dataType: "json" - }); - }, - - /** - * Gets the configuration of a plugin - * @param {String} Id - */ - getPluginConfiguration: function (id) { - - if (!id) { - throw new Error("null Id"); - } - - var url = ApiClient.getUrl("Plugins/" + id + "/Configuration"); - - return $.getJSON(url); - }, - - /** - * Gets a list of plugins that are available to be installed - */ - getAvailablePlugins: function () { - - var url = ApiClient.getUrl("Packages", { PackageType: "UserInstalled" }); - - return $.getJSON(url); - }, - - /** - * Uninstalls a plugin - * @param {String} Id - */ - uninstallPlugin: function (id) { - - if (!id) { - throw new Error("null Id"); - } - - var url = ApiClient.getUrl("Plugins/" + id); - - return $.ajax({ - type: "DELETE", - url: url, - dataType: "json" - }); - }, - - /** - * Removes a virtual folder from either the default view or a user view - * @param {String} name - */ - removeVirtualFolder: function (name, userId) { - - if (!name) { - throw new Error("null name"); - } - - var params = { - name: name, - action: "RemoveVirtualFolder" - }; - - if (userId) { - params.userId = userId; - } - - var url = ApiClient.getUrl("UpdateMediaLibrary", params); - - return $.post(url); - }, - - /** - * Adds a virtual folder to either the default view or a user view - * @param {String} name - */ - addVirtualFolder: function (name, userId) { - - if (!name) { - throw new Error("null name"); - } - - var params = { - name: name, - action: "addVirtualFolder" - }; - - if (userId) { - params.userId = userId; - } - - var url = ApiClient.getUrl("UpdateMediaLibrary", params); - - return $.post(url); - }, - - /** - * Renames a virtual folder, within either the default view or a user view - * @param {String} name - */ - renameVirtualFolder: function (name, newName, userId) { - - if (!name) { - throw new Error("null name"); - } - - if (!newName) { - throw new Error("null newName"); - } - - var params = { - name: name, - newName: newName, - action: "RenameVirtualFolder" - }; - - if (userId) { - params.userId = userId; - } - - var url = ApiClient.getUrl("UpdateMediaLibrary", params); - - return $.post(url); - }, - - /** - * Adds an additional mediaPath to an existing virtual folder, within either the default view or a user view - * @param {String} name - */ - addMediaPath: function (virtualFolderName, mediaPath, userId) { - - if (!virtualFolderName) { - throw new Error("null virtualFolderName"); - } - - if (!mediaPath) { - throw new Error("null mediaPath"); - } - - var params = { - virtualFolderName: virtualFolderName, - mediaPath: mediaPath, - action: "addMediaPath" - }; - - if (userId) { - params.userId = userId; - } - - var url = ApiClient.getUrl("UpdateMediaLibrary", params); - - return $.post(url); - }, - - /** - * Removes a media path from a virtual folder, within either the default view or a user view - * @param {String} name - */ - removeMediaPath: function (virtualFolderName, mediaPath, userId) { - - if (!virtualFolderName) { - throw new Error("null virtualFolderName"); - } - - if (!mediaPath) { - throw new Error("null mediaPath"); - } - - var params = { - virtualFolderName: virtualFolderName, - mediaPath: mediaPath, - action: "RemoveMediaPath" - }; - - if (userId) { - params.userId = userId; - } - - var url = ApiClient.getUrl("UpdateMediaLibrary", params); - - return $.post(url); - }, - - /** - * Deletes a user - * @param {String} id - */ - deleteUser: function (id) { - - if (!id) { - throw new Error("null id"); - } - - var url = ApiClient.getUrl("Users/" + id); - - return $.ajax({ - type: "DELETE", - url: url, - dataType: "json" - }); - }, - - /** - * Deletes a user image - * @param {String} userId - * @param {String} imageType The type of image to delete, based on the server-side ImageType enum. - */ - deleteUserImage: function (userId, imageType) { - - if (!userId) { - throw new Error("null userId"); - } - - if (!imageType) { - throw new Error("null imageType"); - } - - var url = ApiClient.getUrl("Users/" + userId + "/Images/" + imageType); - - return $.ajax({ - type: "DELETE", - url: url, - dataType: "json" - }); - }, - - /** - * Uploads a user image - * @param {String} userId - * @param {String} imageType The type of image to delete, based on the server-side ImageType enum. - * @param {Object} file The file from the input element - */ - uploadUserImage: function (userId, imageType, file) { - - if (!userId) { - throw new Error("null userId"); - } - - if (!imageType) { - throw new Error("null imageType"); - } - - if (!file || !file.type.match('image.*')) { - throw new Error("File must be an image."); - } - - var deferred = $.Deferred(); - - var reader = new FileReader(); - - reader.onerror = function () { - deferred.reject(); - }; - - reader.onabort = function () { - deferred.reject(); - }; - - // Closure to capture the file information. - reader.onload = function (e) { - - var data = window.btoa(e.target.result); - - var params = { - userId: userId, - type: imageType, - filename: file.name - }; - - var url = ApiClient.getUrl("UploadImage", params); - - $.post(url, data).done(function (result) { - - deferred.resolveWith(null, [result]); - - }).fail(function () { - deferred.reject(); - }); - }; - - // Read in the image file as a data URL. - reader.readAsBinaryString(file); - - return deferred.promise(); - }, - - /** - * Gets the list of installed plugins on the server - */ - getInstalledPlugins: function () { - - var url = ApiClient.getUrl("Plugins"); - - return $.getJSON(url); - }, - - /** - * Gets a user by id - * @param {String} id - */ - getUser: function (id) { - - if (!id) { - throw new Error("Must supply a userId"); - } - - var url = ApiClient.getUrl("Users/" + id); - - return $.getJSON(url); - }, - - /** - * Gets a studio - */ - getStudio: function (name) { - - if (!name) { - throw new Error("null name"); - } - - var url = ApiClient.getUrl("Library/Studios/" + name); - - return $.getJSON(url); - }, - - /** - * Gets a genre - */ - getGenre: function (name) { - - if (!name) { - throw new Error("null name"); - } - - var url = ApiClient.getUrl("Library/Genres/" + name); - - return $.getJSON(url); - }, - - /** - * Gets a year - */ - getYear: function (year) { - - if (!year) { - throw new Error("null year"); - } - - var url = ApiClient.getUrl("Library/Years/" + year); - - return $.getJSON(url); - }, - - /** - * Gets a Person - */ - getPerson: function (name) { - - if (!name) { - throw new Error("null name"); - } - - var url = ApiClient.getUrl("Library/Persons/" + name); - - return $.getJSON(url); - }, - - /** - * Gets weather info - * @param {String} location - us zip code / city, state, country / city, country - * Omit location to get weather info using stored server configuration value - */ - getWeatherInfo: function (location) { - - var url = ApiClient.getUrl("weather", { - location: location - }); - - return $.getJSON(url); - }, - - /** - * Gets all users from the server - */ - getAllUsers: function () { - - var url = ApiClient.getUrl("users"); - - return $.getJSON(url); - }, - - /** - * Gets all available parental ratings from the server - */ - getParentalRatings: function () { - - var url = ApiClient.getUrl("Localization/ParentalRatings"); - - return $.getJSON(url); - }, - - /** - * Gets a list of all available conrete BaseItem types from the server - */ - getItemTypes: function (options) { - - var url = ApiClient.getUrl("Library/ItemTypes", options); - - return $.getJSON(url); - }, - - /** - * Constructs a url for a user image - * @param {String} userId - * @param {Object} options - * Options supports the following properties: - * width - download the image at a fixed width - * height - download the image at a fixed height - * maxWidth - download the image at a maxWidth - * maxHeight - download the image at a maxHeight - * quality - A scale of 0-100. This should almost always be omitted as the default will suffice. - * For best results do not specify both width and height together, as aspect ratio might be altered. - */ - getUserImageUrl: function (userId, options) { - - if (!userId) { - throw new Error("null userId"); - } - - options = options || { - }; - - var url = "Users/" + userId + "/Images/" + options.type; - - if (options.index != null) { - url += "/" + options.index; - } - - // Don't put these on the query string - delete options.type; - delete options.index; - - return ApiClient.getUrl(url, options); - }, - - /** - * Constructs a url for a person image - * @param {String} name - * @param {Object} options - * Options supports the following properties: - * width - download the image at a fixed width - * height - download the image at a fixed height - * maxWidth - download the image at a maxWidth - * maxHeight - download the image at a maxHeight - * quality - A scale of 0-100. This should almost always be omitted as the default will suffice. - * For best results do not specify both width and height together, as aspect ratio might be altered. - */ - getPersonImageUrl: function (name, options) { - - if (!name) { - throw new Error("null name"); - } - - options = options || { - }; - - var url = "Persons/" + name + "/Images/" + options.type; - - if (options.index != null) { - url += "/" + options.index; - } - - // Don't put these on the query string - delete options.type; - delete options.index; - - return ApiClient.getUrl(url, options); - }, - - /** - * Constructs a url for a year image - * @param {String} year - * @param {Object} options - * Options supports the following properties: - * width - download the image at a fixed width - * height - download the image at a fixed height - * maxWidth - download the image at a maxWidth - * maxHeight - download the image at a maxHeight - * quality - A scale of 0-100. This should almost always be omitted as the default will suffice. - * For best results do not specify both width and height together, as aspect ratio might be altered. - */ - getYearImageUrl: function (year, options) { - - if (!year) { - throw new Error("null year"); - } - - options = options || { - }; - - var url = "Years/" + year + "/Images/" + options.type; - - if (options.index != null) { - url += "/" + options.index; - } - - // Don't put these on the query string - delete options.type; - delete options.index; - - return ApiClient.getUrl(url, options); - }, - - /** - * Constructs a url for a genre image - * @param {String} name - * @param {Object} options - * Options supports the following properties: - * width - download the image at a fixed width - * height - download the image at a fixed height - * maxWidth - download the image at a maxWidth - * maxHeight - download the image at a maxHeight - * quality - A scale of 0-100. This should almost always be omitted as the default will suffice. - * For best results do not specify both width and height together, as aspect ratio might be altered. - */ - getGenreImageUrl: function (name, options) { - - if (!name) { - throw new Error("null name"); - } - - options = options || { - }; - - var url = "Genres/" + name + "/Images/" + options.type; - - if (options.index != null) { - url += "/" + options.index; - } - - // Don't put these on the query string - delete options.type; - delete options.index; - - return ApiClient.getUrl(url, options); - }, - - /** - * Constructs a url for a genre image - * @param {String} name - * @param {Object} options - * Options supports the following properties: - * width - download the image at a fixed width - * height - download the image at a fixed height - * maxWidth - download the image at a maxWidth - * maxHeight - download the image at a maxHeight - * quality - A scale of 0-100. This should almost always be omitted as the default will suffice. - * For best results do not specify both width and height together, as aspect ratio might be altered. - */ - getStudioImageUrl: function (name, options) { - - if (!name) { - throw new Error("null name"); - } - - options = options || { - }; - - var url = "Studios/" + name + "/Images/" + options.type; - - if (options.index != null) { - url += "/" + options.index; - } - - // Don't put these on the query string - delete options.type; - delete options.index; - - return ApiClient.getUrl(url, options); - }, - - /** - * Constructs a url for an item image - * @param {String} itemId - * @param {Object} options - * Options supports the following properties: - * type - Primary, logo, backdrop, etc. See the server-side enum ImageType - * index - When downloading a backdrop, use this to specify which one (omitting is equivalent to zero) - * width - download the image at a fixed width - * height - download the image at a fixed height - * maxWidth - download the image at a maxWidth - * maxHeight - download the image at a maxHeight - * quality - A scale of 0-100. This should almost always be omitted as the default will suffice. - * For best results do not specify both width and height together, as aspect ratio might be altered. - */ - getImageUrl: function (itemId, options) { - - if (!itemId) { - throw new Error("itemId cannot be empty"); - } - - options = options || { - }; - - var url = "Items/" + itemId + "/Images/" + options.type; - - if (options.index != null) { - url += "/" + options.index; - } - - // Don't put these on the query string - delete options.type; - delete options.index; - - return ApiClient.getUrl(url, options); - }, - - /** - * Constructs a url for an item logo image - * If the item doesn't have a logo, it will inherit a logo from a parent - * @param {Object} item A BaseItem - * @param {Object} options - * Options supports the following properties: - * width - download the image at a fixed width - * height - download the image at a fixed height - * maxWidth - download the image at a maxWidth - * maxHeight - download the image at a maxHeight - * quality - A scale of 0-100. This should almost always be omitted as the default will suffice. - * For best results do not specify both width and height together, as aspect ratio might be altered. - */ - getLogoImageUrl: function (item, options) { - - if (!item) { - throw new Error("null item"); - } - - options = options || { - }; - - options.imageType = "logo"; - - var logoItemId = item.HasLogo ? item.Id : item.ParentLogoItemId; - - return logoItemId ? ApiClient.getImageUrl(logoItemId, options) : null; - }, - - /** - * Constructs an array of backdrop image url's for an item - * If the item doesn't have any backdrops, it will inherit them from a parent - * @param {Object} item A BaseItem - * @param {Object} options - * Options supports the following properties: - * width - download the image at a fixed width - * height - download the image at a fixed height - * maxWidth - download the image at a maxWidth - * maxHeight - download the image at a maxHeight - * quality - A scale of 0-100. This should almost always be omitted as the default will suffice. - * For best results do not specify both width and height together, as aspect ratio might be altered. - */ - getBackdropImageUrl: function (item, options) { - - if (!item) { - throw new Error("null item"); - } - - options = options || { - }; - - options.imageType = "backdrop"; - - var backdropItemId; - var backdropCount; - - if (!item.BackdropCount) { - backdropItemId = item.ParentBackdropItemId; - backdropCount = item.ParentBackdropCount || 0; - } else { - backdropItemId = item.Id; - backdropCount = item.BackdropCount; - } - - if (!backdropItemId) { - return []; - } - - var files = []; - - for (var i = 0; i < backdropCount; i++) { - - options.imageIndex = i; - - files[i] = ApiClient.getImageUrl(backdropItemId, options); - } - - return files; - }, - - /** - * Authenticates a user - * @param {String} userId - * @param {String} password - */ - authenticateUser: function (userId, password) { - - if (!userId) { - throw new Error("null userId"); - } - - var url = ApiClient.getUrl("Users/" + userId + "/authenticate"); - - var postData = { - }; - - if (password) { - postData.password = password; - } - return $.post(url, postData); - }, - - /** - * Updates a user's password - * @param {String} userId - * @param {String} currentPassword - * @param {String} newPassword - */ - updateUserPassword: function (userId, currentPassword, newPassword) { - - if (!userId) { - throw new Error("null userId"); - } - - var url = ApiClient.getUrl("Users/" + userId + "/Password"); - - var postData = { - }; - - if (currentPassword) { - postData.currentPassword = currentPassword; - } - if (newPassword) { - postData.newPassword = newPassword; - } - return $.post(url, postData); - }, - - /** - * Resets a user's password - * @param {String} userId - */ - resetUserPassword: function (userId) { - - if (!userId) { - throw new Error("null userId"); - } - - var url = ApiClient.getUrl("Users/" + userId + "/Password"); - - var postData = { - }; - - postData.resetPassword = 1; - return $.post(url, postData); - }, - - /** - * Updates the server's configuration - * @param {Object} configuration - */ - updateServerConfiguration: function (configuration) { - - if (!configuration) { - throw new Error("null configuration"); - } - - var url = ApiClient.getUrl("System/Configuration"); - - return $.post(url, JSON.stringify(configuration)); - }, - - /** - * Creates a user - * @param {Object} user - */ - createUser: function (user) { - - if (!user) { - throw new Error("null user"); - } - - var url = ApiClient.getUrl("Users"); - - return $.post(url, JSON.stringify(user)); - }, - - /** - * Updates a user - * @param {Object} user - */ - updateUser: function (user) { - - if (!user) { - throw new Error("null user"); - } - - var url = ApiClient.getUrl("Users/" + user.Id); - - return $.post(url, JSON.stringify(user)); - }, - - /** - * Updates the Triggers for a ScheduledTask - * @param {String} id - * @param {Object} triggers - */ - updateScheduledTaskTriggers: function (id, triggers) { - - if (!id) { - throw new Error("null id"); - } - - if (!triggers) { - throw new Error("null triggers"); - } - - var url = ApiClient.getUrl("ScheduledTasks/" + id + "/Triggers"); - - return $.post(url, JSON.stringify(triggers)); - }, - - /** - * Updates a plugin's configuration - * @param {String} Id - * @param {Object} configuration - */ - updatePluginConfiguration: function (id, configuration) { - - if (!id) { - throw new Error("null Id"); - } - - if (!configuration) { - throw new Error("null configuration"); - } - - var url = ApiClient.getUrl("Plugins/" + id + "/Configuration"); - - return $.post(url, JSON.stringify(configuration)); - }, - - /** - * Gets items based on a query, typicall for children of a folder - * @param {String} userId - * @param {Object} options - * Options accepts the following properties: - * itemId - Localize the search to a specific folder (root if omitted) - * startIndex - Use for paging - * limit - Use to limit results to a certain number of items - * filter - Specify one or more ItemFilters, comma delimeted (see server-side enum) - * sortBy - Specify an ItemSortBy (comma-delimeted list see server-side enum) - * sortOrder - ascending/descending - * fields - additional fields to include aside from basic info. This is a comma delimited list. See server-side enum ItemFields. - * index - the name of the dynamic, localized index function - * dynamicSortBy - the name of the dynamic localized sort function - * recursive - Whether or not the query should be recursive - * searchTerm - search term to use as a filter - */ - getItems: function (userId, options) { - - if (!userId) { - throw new Error("null userId"); - } - - return $.getJSON(ApiClient.getUrl("Users/" + userId + "/Items", options)); - }, - - /** - * Marks an item as played or unplayed - * This should not be used to update playstate following playback. - * There are separate playstate check-in methods for that. This should be used for a - * separate option to reset playstate. - * @param {String} userId - * @param {String} itemId - * @param {Boolean} wasPlayed - */ - updatePlayedStatus: function (userId, itemId, wasPlayed) { - - if (!userId) { - throw new Error("null userId"); - } - - if (!itemId) { - throw new Error("null itemId"); - } - - var url = "Users/" + userId + "/PlayedItems/" + itemId; - - var method = wasPlayed ? "POST" : "DELETE"; - - return $.ajax({ - type: method, - url: url, - dataType: "json" - }); - }, - - /** - * Updates a user's favorite status for an item and returns the updated UserItemData object. - * @param {String} userId - * @param {String} itemId - * @param {Boolean} isFavorite - */ - updateFavoriteStatus: function (userId, itemId, isFavorite) { - - if (!userId) { - throw new Error("null userId"); - } - - if (!itemId) { - throw new Error("null itemId"); - } - - var url = "Users/" + userId + "/FavoriteItems/" + itemId; - - var method = isFavorite ? "POST" : "DELETE"; - - return $.ajax({ - type: method, - url: url, - dataType: "json" - }); - }, - - /** - * Updates a user's personal rating for an item - * @param {String} userId - * @param {String} itemId - * @param {Boolean} likes - */ - updateUserItemRating: function (userId, itemId, likes) { - - if (!userId) { - throw new Error("null userId"); - } - - if (!itemId) { - throw new Error("null itemId"); - } - - var url = ApiClient.getUrl("Users/" + userId + "/Items/" + itemId + "/Rating", { - likes: likes - }); - - return $.post(url); - }, - - /** - * Clears a user's personal rating for an item - * @param {String} userId - * @param {String} itemId - */ - clearUserItemRating: function (userId, itemId) { - - if (!userId) { - throw new Error("null userId"); - } - - if (!itemId) { - throw new Error("null itemId"); - } - - var url = ApiClient.getUrl("Users/" + userId + "/Items/" + itemId + "/Rating"); - - return $.ajax({ - type: "DELETE", - url: url, - dataType: "json" - }); - } -}; - -// Do this initially. The consumer can always override later -ApiClient.inferServerFromUrl(); diff --git a/MediaBrowser.ApiInteraction.Javascript/JavascriptApiClientService.cs b/MediaBrowser.ApiInteraction.Javascript/JavascriptApiClientService.cs deleted file mode 100644 index c03146e69..000000000 --- a/MediaBrowser.ApiInteraction.Javascript/JavascriptApiClientService.cs +++ /dev/null @@ -1,58 +0,0 @@ -using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Net; -using ServiceStack.ServiceHost; -using System; -using System.IO; -using System.Threading.Tasks; - -namespace MediaBrowser.ApiInteraction.Javascript -{ - /// - /// Class GetJavascriptApiClient - /// - [Route("/JsApiClient.js", "GET")] - [Api(("Gets an api wrapper in Javascript"))] - public class GetJavascriptApiClient - { - /// - /// Version identifier for caching - /// - /// The v. - public string V { get; set; } - } - - /// - /// Class JavascriptApiClientService - /// - public class JavascriptApiClientService : BaseRestService - { - /// - /// Gets the specified request. - /// - /// The request. - /// System.Object. - public object Get(GetJavascriptApiClient request) - { - TimeSpan? cacheDuration = null; - - // If there's a version number in the query string we can cache this unconditionally - if (!string.IsNullOrEmpty(request.V)) - { - cacheDuration = TimeSpan.FromDays(365); - } - - var assembly = GetType().Assembly.GetName(); - - return ToStaticResult(assembly.Version.ToString().GetMD5(), null, cacheDuration, MimeTypes.GetMimeType("script.js"), GetStream); - } - - /// - /// Gets the stream. - /// - /// Stream. - private Task GetStream() - { - return Task.FromResult(GetType().Assembly.GetManifestResourceStream("MediaBrowser.ApiInteraction.Javascript.ApiClient.js")); - } - } -} diff --git a/MediaBrowser.ApiInteraction.Javascript/MediaBrowser.ApiInteraction.Javascript.csproj b/MediaBrowser.ApiInteraction.Javascript/MediaBrowser.ApiInteraction.Javascript.csproj deleted file mode 100644 index 68e8c880d..000000000 --- a/MediaBrowser.ApiInteraction.Javascript/MediaBrowser.ApiInteraction.Javascript.csproj +++ /dev/null @@ -1,105 +0,0 @@ - - - - - Debug - AnyCPU - {767B536E-D90C-4D74-A14B-8564B16F3499} - Library - Properties - MediaBrowser.ApiInteraction.Javascript - MediaBrowser.ApiInteraction.Javascript - v4.5 - 512 - ..\ - true - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - Always - - - - False - ..\packages\ServiceStack.3.9.37\lib\net35\ServiceStack.dll - - - False - ..\packages\ServiceStack.Common.3.9.37\lib\net35\ServiceStack.Common.dll - - - False - ..\packages\ServiceStack.Common.3.9.37\lib\net35\ServiceStack.Interfaces.dll - - - False - ..\packages\ServiceStack.OrmLite.SqlServer.3.9.37\lib\ServiceStack.OrmLite.dll - - - False - ..\packages\ServiceStack.OrmLite.SqlServer.3.9.37\lib\ServiceStack.OrmLite.SqlServer.dll - - - False - ..\packages\ServiceStack.Redis.3.9.37\lib\net35\ServiceStack.Redis.dll - - - False - ..\packages\ServiceStack.3.9.37\lib\net35\ServiceStack.ServiceInterface.dll - - - False - ..\packages\ServiceStack.Text.3.9.37\lib\net35\ServiceStack.Text.dll - - - - - - - - Properties\SharedVersion.cs - - - - - - - {9142eefa-7570-41e1-bfcc-468bb571af2f} - MediaBrowser.Common - - - - - - - - - - - xcopy "$(TargetPath)" "$(SolutionDir)\MediaBrowser.ServerApplication\CorePlugins\" /y - - - - \ No newline at end of file diff --git a/MediaBrowser.ApiInteraction.Javascript/Properties/AssemblyInfo.cs b/MediaBrowser.ApiInteraction.Javascript/Properties/AssemblyInfo.cs deleted file mode 100644 index 1a0333e49..000000000 --- a/MediaBrowser.ApiInteraction.Javascript/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("MediaBrowser.ApiInteraction.Javascript")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("MediaBrowser.ApiInteraction.Javascript")] -[assembly: AssemblyCopyright("Copyright © 2012")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("97f9d4da-d7de-47d9-ae68-06d78679d327")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// \ No newline at end of file diff --git a/MediaBrowser.ApiInteraction.Javascript/packages.config b/MediaBrowser.ApiInteraction.Javascript/packages.config deleted file mode 100644 index ea25110aa..000000000 --- a/MediaBrowser.ApiInteraction.Javascript/packages.config +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/MediaBrowser.Common.Implementations/BaseApplicationPaths.cs b/MediaBrowser.Common.Implementations/BaseApplicationPaths.cs new file mode 100644 index 000000000..93478b22c --- /dev/null +++ b/MediaBrowser.Common.Implementations/BaseApplicationPaths.cs @@ -0,0 +1,304 @@ +using System; +using System.Configuration; +using System.IO; +using System.Reflection; + +namespace MediaBrowser.Common.Implementations +{ + /// + /// Provides a base class to hold common application paths used by both the Ui and Server. + /// This can be subclassed to add application-specific paths. + /// + public abstract class BaseApplicationPaths + { + /// + /// The _program data path + /// + private string _programDataPath; + /// + /// Gets the path to the program data folder + /// + /// The program data path. + public string ProgramDataPath + { + get + { + return _programDataPath ?? (_programDataPath = GetProgramDataPath()); + } + } + + /// + /// The _data directory + /// + private string _dataDirectory; + /// + /// Gets the folder path to the data directory + /// + /// The data directory. + public string DataPath + { + get + { + if (_dataDirectory == null) + { + _dataDirectory = Path.Combine(ProgramDataPath, "data"); + + if (!Directory.Exists(_dataDirectory)) + { + Directory.CreateDirectory(_dataDirectory); + } + } + + return _dataDirectory; + } + } + + /// + /// The _image cache path + /// + private string _imageCachePath; + /// + /// Gets the image cache path. + /// + /// The image cache path. + public string ImageCachePath + { + get + { + if (_imageCachePath == null) + { + _imageCachePath = Path.Combine(CachePath, "images"); + + if (!Directory.Exists(_imageCachePath)) + { + Directory.CreateDirectory(_imageCachePath); + } + } + + return _imageCachePath; + } + } + + /// + /// The _plugins path + /// + private string _pluginsPath; + /// + /// Gets the path to the plugin directory + /// + /// The plugins path. + public string PluginsPath + { + get + { + if (_pluginsPath == null) + { + _pluginsPath = Path.Combine(ProgramDataPath, "plugins"); + if (!Directory.Exists(_pluginsPath)) + { + Directory.CreateDirectory(_pluginsPath); + } + } + + return _pluginsPath; + } + } + + /// + /// The _plugin configurations path + /// + private string _pluginConfigurationsPath; + /// + /// Gets the path to the plugin configurations directory + /// + /// The plugin configurations path. + public string PluginConfigurationsPath + { + get + { + if (_pluginConfigurationsPath == null) + { + _pluginConfigurationsPath = Path.Combine(PluginsPath, "configurations"); + if (!Directory.Exists(_pluginConfigurationsPath)) + { + Directory.CreateDirectory(_pluginConfigurationsPath); + } + } + + return _pluginConfigurationsPath; + } + } + + private string _tempUpdatePath; + /// + /// Gets the path to where temporary update files will be stored + /// + /// The plugin configurations path. + public string TempUpdatePath + { + get + { + if (_tempUpdatePath == null) + { + _tempUpdatePath = Path.Combine(ProgramDataPath, "Updates"); + if (!Directory.Exists(_tempUpdatePath)) + { + Directory.CreateDirectory(_tempUpdatePath); + } + } + + return _tempUpdatePath; + } + } + + /// + /// The _log directory path + /// + private string _logDirectoryPath; + /// + /// Gets the path to the log directory + /// + /// The log directory path. + public string LogDirectoryPath + { + get + { + if (_logDirectoryPath == null) + { + _logDirectoryPath = Path.Combine(ProgramDataPath, "logs"); + if (!Directory.Exists(_logDirectoryPath)) + { + Directory.CreateDirectory(_logDirectoryPath); + } + } + return _logDirectoryPath; + } + } + + /// + /// The _configuration directory path + /// + private string _configurationDirectoryPath; + /// + /// Gets the path to the application configuration root directory + /// + /// The configuration directory path. + public string ConfigurationDirectoryPath + { + get + { + if (_configurationDirectoryPath == null) + { + _configurationDirectoryPath = Path.Combine(ProgramDataPath, "config"); + if (!Directory.Exists(_configurationDirectoryPath)) + { + Directory.CreateDirectory(_configurationDirectoryPath); + } + } + return _configurationDirectoryPath; + } + } + + /// + /// The _system configuration file path + /// + private string _systemConfigurationFilePath; + /// + /// Gets the path to the system configuration file + /// + /// The system configuration file path. + public string SystemConfigurationFilePath + { + get + { + return _systemConfigurationFilePath ?? (_systemConfigurationFilePath = Path.Combine(ConfigurationDirectoryPath, "system.xml")); + } + } + + /// + /// The _cache directory + /// + private string _cachePath; + /// + /// Gets the folder path to the cache directory + /// + /// The cache directory. + public string CachePath + { + get + { + if (_cachePath == null) + { + _cachePath = Path.Combine(ProgramDataPath, "cache"); + + if (!Directory.Exists(_cachePath)) + { + Directory.CreateDirectory(_cachePath); + } + } + + return _cachePath; + } + } + + /// + /// The _temp directory + /// + private string _tempDirectory; + /// + /// Gets the folder path to the temp directory within the cache folder + /// + /// The temp directory. + public string TempDirectory + { + get + { + if (_tempDirectory == null) + { + _tempDirectory = Path.Combine(CachePath, "temp"); + + if (!Directory.Exists(_tempDirectory)) + { + Directory.CreateDirectory(_tempDirectory); + } + } + + return _tempDirectory; + } + } + + /// + /// Gets the path to the application's ProgramDataFolder + /// + /// System.String. + public static string GetProgramDataPath() + { +#if DEBUG + string programDataPath = ConfigurationManager.AppSettings["DebugProgramDataPath"]; + +#else + string programDataPath = Path.Combine(ConfigurationManager.AppSettings["ReleaseProgramDataPath"], ConfigurationManager.AppSettings["ProgramDataFolderName"]); +#endif + + programDataPath = programDataPath.Replace("%CommonApplicationData%", Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData)); + + // If it's a relative path, e.g. "..\" + if (!Path.IsPathRooted(programDataPath)) + { + var path = Assembly.GetExecutingAssembly().Location; + path = Path.GetDirectoryName(path); + + programDataPath = Path.Combine(path, programDataPath); + + programDataPath = Path.GetFullPath(programDataPath); + } + + if (!Directory.Exists(programDataPath)) + { + Directory.CreateDirectory(programDataPath); + } + + return programDataPath; + } + } +} diff --git a/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj b/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj new file mode 100644 index 000000000..d271db060 --- /dev/null +++ b/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj @@ -0,0 +1,87 @@ + + + + + Debug + AnyCPU + {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D} + Library + Properties + MediaBrowser.Common.Implementations + MediaBrowser.Common.Implementations + v4.5 + 512 + ..\ + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\protobuf-net.2.0.0.621\lib\net40\protobuf-net.dll + + + ..\packages\ServiceStack.Text.3.9.37\lib\net35\ServiceStack.Text.dll + + + + + + + + + + + + + Properties\SharedVersion.cs + + + + + + + + + + + + + + + {9142eefa-7570-41e1-bfcc-468bb571af2f} + MediaBrowser.Common + + + {7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b} + MediaBrowser.Model + + + + + + + + + \ No newline at end of file diff --git a/MediaBrowser.Common.Implementations/Properties/AssemblyInfo.cs b/MediaBrowser.Common.Implementations/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..f9c3e0bd1 --- /dev/null +++ b/MediaBrowser.Common.Implementations/Properties/AssemblyInfo.cs @@ -0,0 +1,31 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("MediaBrowser.Common.Implementations")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("MediaBrowser.Common.Implementations")] +[assembly: AssemblyCopyright("Copyright © 2013")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("fc7d85c6-0fe7-4db6-8158-54f7b18f17cd")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// diff --git a/MediaBrowser.Common.Implementations/ScheduledTasks/TaskManager.cs b/MediaBrowser.Common.Implementations/ScheduledTasks/TaskManager.cs new file mode 100644 index 000000000..c6eca29d1 --- /dev/null +++ b/MediaBrowser.Common.Implementations/ScheduledTasks/TaskManager.cs @@ -0,0 +1,322 @@ +using MediaBrowser.Common.Kernel; +using MediaBrowser.Common.ScheduledTasks; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Serialization; +using MediaBrowser.Model.Tasks; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace MediaBrowser.Common.Implementations.ScheduledTasks +{ + /// + /// Class TaskManager + /// + public class TaskManager : ITaskManager + { + /// + /// Gets the list of Scheduled Tasks + /// + /// The scheduled tasks. + public IScheduledTask[] ScheduledTasks { get; private set; } + + /// + /// The _task queue + /// + private readonly List _taskQueue = new List(); + + /// + /// The _logger + /// + private readonly ILogger _logger; + + /// + /// The _application paths + /// + private readonly IApplicationPaths _applicationPaths; + + /// + /// The _json serializer + /// + private readonly IJsonSerializer _jsonSerializer; + + /// + /// Initializes a new instance of the class. + /// + /// The application paths. + /// The json serializer. + /// The logger. + /// kernel + public TaskManager(IApplicationPaths applicationPaths, IJsonSerializer jsonSerializer, ILogger logger) + { + if (applicationPaths == null) + { + throw new ArgumentException("applicationPaths"); + } + if (jsonSerializer == null) + { + throw new ArgumentException("jsonSerializer"); + } + if (logger == null) + { + throw new ArgumentException("logger"); + } + + _applicationPaths = applicationPaths; + _jsonSerializer = jsonSerializer; + _logger = logger; + + ScheduledTasks = new IScheduledTask[] {}; + } + + /// + /// Cancels if running and queue. + /// + /// + public void CancelIfRunningAndQueue() + where T : IScheduledTask + { + ScheduledTasks.OfType().First().CancelIfRunning(); + QueueScheduledTask(); + } + + /// + /// Queues the scheduled task. + /// + /// + public void QueueScheduledTask() + where T : IScheduledTask + { + var scheduledTask = ScheduledTasks.OfType().First(); + + QueueScheduledTask(scheduledTask); + } + + /// + /// Queues the scheduled task. + /// + /// The task. + public void QueueScheduledTask(IScheduledTask task) + { + var type = task.GetType(); + + var scheduledTask = ScheduledTasks.First(t => t.GetType() == type); + + lock (_taskQueue) + { + // If it's idle just execute immediately + if (scheduledTask.State == TaskState.Idle) + { + scheduledTask.Execute(); + return; + } + + if (!_taskQueue.Contains(type)) + { + _logger.Info("Queueing task {0}", type.Name); + _taskQueue.Add(type); + } + else + { + _logger.Info("Task already queued: {0}", type.Name); + } + } + } + + /// + /// Called when [task completed]. + /// + /// The task. + public void OnTaskCompleted(IScheduledTask task) + { + // Execute queued tasks + lock (_taskQueue) + { + var copy = _taskQueue.ToList(); + + foreach (var type in copy) + { + var scheduledTask = ScheduledTasks.First(t => t.GetType() == type); + + if (scheduledTask.State == TaskState.Idle) + { + scheduledTask.Execute(); + + _taskQueue.Remove(type); + } + } + } + } + + /// + /// Adds the tasks. + /// + /// The tasks. + public void AddTasks(IEnumerable tasks) + { + var myTasks = ScheduledTasks.ToList(); + + myTasks.AddRange(tasks); + + ScheduledTasks = myTasks.ToArray(); + } + + /// + /// The _scheduled tasks configuration directory + /// + private string _scheduledTasksConfigurationDirectory; + /// + /// Gets the scheduled tasks configuration directory. + /// + /// The scheduled tasks configuration directory. + private string ScheduledTasksConfigurationDirectory + { + get + { + if (_scheduledTasksConfigurationDirectory == null) + { + _scheduledTasksConfigurationDirectory = Path.Combine(_applicationPaths.ConfigurationDirectoryPath, "ScheduledTasks"); + + if (!Directory.Exists(_scheduledTasksConfigurationDirectory)) + { + Directory.CreateDirectory(_scheduledTasksConfigurationDirectory); + } + } + return _scheduledTasksConfigurationDirectory; + } + } + + /// + /// The _scheduled tasks data directory + /// + private string _scheduledTasksDataDirectory; + /// + /// Gets the scheduled tasks data directory. + /// + /// The scheduled tasks data directory. + private string ScheduledTasksDataDirectory + { + get + { + if (_scheduledTasksDataDirectory == null) + { + _scheduledTasksDataDirectory = Path.Combine(_applicationPaths.DataPath, "ScheduledTasks"); + + if (!Directory.Exists(_scheduledTasksDataDirectory)) + { + Directory.CreateDirectory(_scheduledTasksDataDirectory); + } + } + return _scheduledTasksDataDirectory; + } + } + + /// + /// Gets the history file path. + /// + /// The history file path. + private string GetHistoryFilePath(IScheduledTask task) + { + return Path.Combine(ScheduledTasksDataDirectory, task.Id + ".js"); + } + + /// + /// Gets the configuration file path. + /// + /// The task. + /// System.String. + private string GetConfigurationFilePath(IScheduledTask task) + { + return Path.Combine(ScheduledTasksConfigurationDirectory, task.Id + ".js"); + } + + /// + /// Called when [task completed]. + /// + /// The task. + /// The start time. + /// The end time. + /// The status. + public void OnTaskCompleted(IScheduledTask task, DateTime startTime, DateTime endTime, TaskCompletionStatus status) + { + var elapsedTime = endTime - startTime; + + _logger.Info("{0} {1} after {2} minute(s) and {3} seconds", task.Name, status, Math.Truncate(elapsedTime.TotalMinutes), elapsedTime.Seconds); + + var result = new TaskResult + { + StartTimeUtc = startTime, + EndTimeUtc = endTime, + Status = status, + Name = task.Name, + Id = task.Id + }; + + _jsonSerializer.SerializeToFile(result, GetHistoryFilePath(task)); + + //task.LastExecutionResult = result; + } + + /// + /// Gets the last execution result. + /// + /// The task. + /// TaskResult. + public TaskResult GetLastExecutionResult(IScheduledTask task) + { + return _jsonSerializer.DeserializeFromFile(GetHistoryFilePath(task)); + } + + /// + /// Loads the triggers. + /// + /// The task. + /// IEnumerable{BaseTaskTrigger}. + public IEnumerable LoadTriggers(IScheduledTask task) + { + try + { + return _jsonSerializer.DeserializeFromFile>(GetConfigurationFilePath(task)) + .Select(ScheduledTaskHelpers.GetTrigger) + .ToList(); + } + catch (IOException) + { + // File doesn't exist. No biggie. Return defaults. + return task.GetDefaultTriggers(); + } + } + + /// + /// Saves the triggers. + /// + /// The task. + /// The triggers. + public void SaveTriggers(IScheduledTask task, IEnumerable triggers) + { + _jsonSerializer.SerializeToFile(triggers.Select(ScheduledTaskHelpers.GetTriggerInfo), GetConfigurationFilePath(task)); + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged and - optionally - managed resources. + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool dispose) + { + foreach (var task in ScheduledTasks) + { + task.Dispose(); + } + } + } +} diff --git a/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs b/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs new file mode 100644 index 000000000..2ef056658 --- /dev/null +++ b/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs @@ -0,0 +1,119 @@ +using MediaBrowser.Common.Kernel; +using MediaBrowser.Common.ScheduledTasks; +using MediaBrowser.Model.Logging; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks +{ + /// + /// Deletes old cache files + /// + public class DeleteCacheFileTask : BaseScheduledTask + { + /// + /// Initializes a new instance of the class. + /// + /// The kernel. + /// The task manager. + /// The logger. + public DeleteCacheFileTask(IKernel kernel, ITaskManager taskManager, ILogger logger) + : base(kernel, taskManager, logger) + { + } + + /// + /// Creates the triggers that define when the task will run + /// + /// IEnumerable{BaseTaskTrigger}. + public override IEnumerable GetDefaultTriggers() + { + var trigger = new DailyTrigger { TimeOfDay = TimeSpan.FromHours(2) }; //2am + + return new[] { trigger }; + } + + /// + /// Returns the task to be executed + /// + /// The cancellation token. + /// The progress. + /// Task. + protected override Task ExecuteInternal(CancellationToken cancellationToken, IProgress progress) + { + return Task.Run(() => + { + var minDateModified = DateTime.UtcNow.AddMonths(-2); + + DeleteCacheFilesFromDirectory(cancellationToken, Kernel.ApplicationPaths.CachePath, minDateModified, progress); + }); + } + + + /// + /// Deletes the cache files from directory with a last write time less than a given date + /// + /// The task cancellation token. + /// The directory. + /// The min date modified. + /// The progress. + private void DeleteCacheFilesFromDirectory(CancellationToken cancellationToken, string directory, DateTime minDateModified, IProgress progress) + { + var filesToDelete = new DirectoryInfo(directory).EnumerateFileSystemInfos("*", SearchOption.AllDirectories) + .Where(f => !f.Attributes.HasFlag(FileAttributes.Directory) && f.LastWriteTimeUtc < minDateModified) + .ToList(); + + var index = 0; + + foreach (var file in filesToDelete) + { + double percent = index; + percent /= filesToDelete.Count; + + progress.Report(100 * percent); + + cancellationToken.ThrowIfCancellationRequested(); + + File.Delete(file.FullName); + + index++; + } + + progress.Report(100); + } + + /// + /// Gets the name of the task + /// + /// The name. + public override string Name + { + get { return "Cache file cleanup"; } + } + + /// + /// Gets the description. + /// + /// The description. + public override string Description + { + get { return "Deletes cache files no longer needed by the system"; } + } + + /// + /// Gets the category. + /// + /// The category. + public override string Category + { + get + { + return "Maintenance"; + } + } + } +} diff --git a/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs b/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs new file mode 100644 index 000000000..dd00a7148 --- /dev/null +++ b/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteLogFileTask.cs @@ -0,0 +1,107 @@ +using MediaBrowser.Common.Kernel; +using MediaBrowser.Common.ScheduledTasks; +using MediaBrowser.Model.Logging; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks +{ + /// + /// Deletes old log files + /// + public class DeleteLogFileTask : BaseScheduledTask + { + /// + /// Initializes a new instance of the class. + /// + /// The kernel. + /// The task manager. + /// The logger. + public DeleteLogFileTask(IKernel kernel, ITaskManager taskManager, ILogger logger) + : base(kernel, taskManager, logger) + { + } + + /// + /// Creates the triggers that define when the task will run + /// + /// IEnumerable{BaseTaskTrigger}. + public override IEnumerable GetDefaultTriggers() + { + var trigger = new DailyTrigger { TimeOfDay = TimeSpan.FromHours(2) }; //2am + + return new[] { trigger }; + } + + /// + /// Returns the task to be executed + /// + /// The cancellation token. + /// The progress. + /// Task. + protected override Task ExecuteInternal(CancellationToken cancellationToken, IProgress progress) + { + return Task.Run(() => + { + // Delete log files more than n days old + var minDateModified = DateTime.UtcNow.AddDays(-(Kernel.Configuration.LogFileRetentionDays)); + + var filesToDelete = new DirectoryInfo(Kernel.ApplicationPaths.LogDirectoryPath).EnumerateFileSystemInfos("*", SearchOption.AllDirectories) + .Where(f => f.LastWriteTimeUtc < minDateModified) + .ToList(); + + var index = 0; + + foreach (var file in filesToDelete) + { + double percent = index; + percent /= filesToDelete.Count; + + progress.Report(100 * percent); + + cancellationToken.ThrowIfCancellationRequested(); + + File.Delete(file.FullName); + + index++; + } + + progress.Report(100); + }); + } + + /// + /// Gets the name of the task + /// + /// The name. + public override string Name + { + get { return "Log file cleanup"; } + } + + /// + /// Gets the description. + /// + /// The description. + public override string Description + { + get { return string.Format("Deletes log files that are more than {0} days old.", Kernel.Configuration.LogFileRetentionDays); } + } + + /// + /// Gets the category. + /// + /// The category. + public override string Category + { + get + { + return "Maintenance"; + } + } + } +} diff --git a/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/ReloadLoggerTask.cs b/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/ReloadLoggerTask.cs new file mode 100644 index 000000000..79c633c76 --- /dev/null +++ b/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/ReloadLoggerTask.cs @@ -0,0 +1,71 @@ +using MediaBrowser.Common.Kernel; +using MediaBrowser.Common.ScheduledTasks; +using MediaBrowser.Model.Logging; +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks +{ + /// + /// Class ReloadLoggerFileTask + /// + public class ReloadLoggerFileTask : BaseScheduledTask + { + /// + /// Initializes a new instance of the class. + /// + /// The kernel. + /// The task manager. + /// The logger. + public ReloadLoggerFileTask(IKernel kernel, ITaskManager taskManager, ILogger logger) + : base(kernel, taskManager, logger) + { + } + + /// + /// Gets the default triggers. + /// + /// IEnumerable{BaseTaskTrigger}. + public override IEnumerable GetDefaultTriggers() + { + var trigger = new DailyTrigger { TimeOfDay = TimeSpan.FromHours(0) }; //12am + + return new[] { trigger }; + } + + /// + /// Executes the internal. + /// + /// The cancellation token. + /// The progress. + /// Task. + protected override Task ExecuteInternal(CancellationToken cancellationToken, IProgress progress) + { + cancellationToken.ThrowIfCancellationRequested(); + + progress.Report(0); + + return Task.Run(() => Kernel.ReloadLogger()); + } + + /// + /// Gets the name. + /// + /// The name. + public override string Name + { + get { return "Start new log file"; } + } + + /// + /// Gets the description. + /// + /// The description. + public override string Description + { + get { return "Moves logging to a new file to help reduce log file sizes."; } + } + } +} diff --git a/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/SystemUpdateTask.cs b/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/SystemUpdateTask.cs new file mode 100644 index 000000000..a101ad3dd --- /dev/null +++ b/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/SystemUpdateTask.cs @@ -0,0 +1,122 @@ +using MediaBrowser.Common.Kernel; +using MediaBrowser.Common.ScheduledTasks; +using MediaBrowser.Model.Logging; +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks +{ + /// + /// Plugin Update Task + /// + public class SystemUpdateTask : BaseScheduledTask + { + /// + /// The _app host + /// + private readonly IApplicationHost _appHost; + + /// + /// Initializes a new instance of the class. + /// + /// The app host. + /// The task manager. + /// The kernel. + /// The logger. + public SystemUpdateTask(IApplicationHost appHost, ITaskManager taskManager, IKernel kernel, ILogger logger) + : base(kernel, taskManager, logger) + { + _appHost = appHost; + } + + /// + /// Creates the triggers that define when the task will run + /// + /// IEnumerable{BaseTaskTrigger}. + public override IEnumerable GetDefaultTriggers() + { + return new ITaskTrigger[] { + + // 1am + new DailyTrigger { TimeOfDay = TimeSpan.FromHours(1) }, + + new IntervalTrigger { Interval = TimeSpan.FromHours(2)} + }; + } + + /// + /// Returns the task to be executed + /// + /// The cancellation token. + /// The progress. + /// Task. + protected override async Task ExecuteInternal(CancellationToken cancellationToken, IProgress progress) + { + if (!_appHost.CanSelfUpdate) return; + + EventHandler innerProgressHandler = (sender, e) => progress.Report(e * .1); + + // Create a progress object for the update check + var innerProgress = new Progress(); + innerProgress.ProgressChanged += innerProgressHandler; + + var updateInfo = await _appHost.CheckForApplicationUpdate(cancellationToken, innerProgress).ConfigureAwait(false); + + // Release the event handler + innerProgress.ProgressChanged -= innerProgressHandler; + + progress.Report(10); + + if (!updateInfo.IsUpdateAvailable) + { + progress.Report(100); + return; + } + + cancellationToken.ThrowIfCancellationRequested(); + + if (Kernel.Configuration.EnableAutoUpdate) + { + Logger.Info("Update Revision {0} available. Updating...", updateInfo.AvailableVersion); + + innerProgressHandler = (sender, e) => progress.Report((e * .9) + .1); + + innerProgress = new Progress(); + innerProgress.ProgressChanged += innerProgressHandler; + + await _appHost.UpdateApplication(cancellationToken, innerProgress).ConfigureAwait(false); + + // Release the event handler + innerProgress.ProgressChanged -= innerProgressHandler; + + Kernel.OnApplicationUpdated(updateInfo.AvailableVersion); + } + else + { + Logger.Info("A new version of Media Browser is available."); + } + + progress.Report(100); + } + + /// + /// Gets the name of the task + /// + /// The name. + public override string Name + { + get { return "Check for application updates"; } + } + + /// + /// Gets the description. + /// + /// The description. + public override string Description + { + get { return "Downloads and installs application updates."; } + } + } +} diff --git a/MediaBrowser.Common.Implementations/Serialization/JsonSerializer.cs b/MediaBrowser.Common.Implementations/Serialization/JsonSerializer.cs new file mode 100644 index 000000000..bc8935a86 --- /dev/null +++ b/MediaBrowser.Common.Implementations/Serialization/JsonSerializer.cs @@ -0,0 +1,240 @@ +using MediaBrowser.Model.Serialization; +using System; +using System.IO; + +namespace MediaBrowser.Common.Implementations.Serialization +{ + /// + /// Provides a wrapper around third party json serialization. + /// + public class JsonSerializer : IJsonSerializer + { + public JsonSerializer() + { + Configure(); + } + + /// + /// Serializes to stream. + /// + /// + /// The obj. + /// The stream. + /// obj + public void SerializeToStream(T obj, Stream stream) + where T : class + { + if (obj == null) + { + throw new ArgumentNullException("obj"); + } + + if (stream == null) + { + throw new ArgumentNullException("stream"); + } + + ServiceStack.Text.JsonSerializer.SerializeToStream(obj, obj.GetType(), stream); + } + + /// + /// Serializes to file. + /// + /// + /// The obj. + /// The file. + /// obj + public void SerializeToFile(T obj, string file) + where T : class + { + if (obj == null) + { + throw new ArgumentNullException("obj"); + } + + if (string.IsNullOrEmpty(file)) + { + throw new ArgumentNullException("file"); + } + + using (Stream stream = File.Open(file, FileMode.Create)) + { + SerializeToStream(obj, stream); + } + } + + /// + /// Deserializes from file. + /// + /// The type. + /// The file. + /// System.Object. + /// type + public object DeserializeFromFile(Type type, string file) + { + if (type == null) + { + throw new ArgumentNullException("type"); + } + + if (string.IsNullOrEmpty(file)) + { + throw new ArgumentNullException("file"); + } + + using (Stream stream = File.OpenRead(file)) + { + return ServiceStack.Text.JsonSerializer.DeserializeFromStream(type, stream); + } + } + + /// + /// Deserializes from file. + /// + /// + /// The file. + /// ``0. + /// file + public T DeserializeFromFile(string file) + where T : class + { + if (string.IsNullOrEmpty(file)) + { + throw new ArgumentNullException("file"); + } + + using (Stream stream = File.OpenRead(file)) + { + return ServiceStack.Text.JsonSerializer.DeserializeFromStream(stream); + } + } + + /// + /// Deserializes from stream. + /// + /// + /// The stream. + /// ``0. + /// stream + public T DeserializeFromStream(Stream stream) + { + if (stream == null) + { + throw new ArgumentNullException("stream"); + } + + return ServiceStack.Text.JsonSerializer.DeserializeFromStream(stream); + } + + /// + /// Deserializes from string. + /// + /// + /// The text. + /// ``0. + /// text + public T DeserializeFromString(string text) + { + if (string.IsNullOrEmpty(text)) + { + throw new ArgumentNullException("text"); + } + + return ServiceStack.Text.JsonSerializer.DeserializeFromString(text); + } + + /// + /// Deserializes from stream. + /// + /// The stream. + /// The type. + /// System.Object. + /// stream + public object DeserializeFromStream(Stream stream, Type type) + { + if (stream == null) + { + throw new ArgumentNullException("stream"); + } + + if (type == null) + { + throw new ArgumentNullException("type"); + } + + return ServiceStack.Text.JsonSerializer.DeserializeFromStream(type, stream); + } + + /// + /// Configures this instance. + /// + private void Configure() + { + ServiceStack.Text.JsConfig.DateHandler = ServiceStack.Text.JsonDateHandler.ISO8601; + ServiceStack.Text.JsConfig.ExcludeTypeInfo = true; + ServiceStack.Text.JsConfig.IncludeNullValues = false; + } + + /// + /// Deserializes from string. + /// + /// The json. + /// The type. + /// System.Object. + /// json + public object DeserializeFromString(string json, Type type) + { + if (string.IsNullOrEmpty(json)) + { + throw new ArgumentNullException("json"); + } + + if (type == null) + { + throw new ArgumentNullException("type"); + } + + return ServiceStack.Text.JsonSerializer.DeserializeFromString(json, type); + } + + /// + /// Serializes to string. + /// + /// + /// The obj. + /// System.String. + /// obj + public string SerializeToString(T obj) + where T : class + { + if (obj == null) + { + throw new ArgumentNullException("obj"); + } + + return ServiceStack.Text.JsonSerializer.SerializeToString(obj, obj.GetType()); + } + + /// + /// Serializes to bytes. + /// + /// + /// The obj. + /// System.Byte[][]. + /// obj + public byte[] SerializeToBytes(T obj) + where T : class + { + if (obj == null) + { + throw new ArgumentNullException("obj"); + } + + using (var stream = new MemoryStream()) + { + SerializeToStream(obj, stream); + return stream.ToArray(); + } + } + } +} diff --git a/MediaBrowser.Common.Implementations/Serialization/ProtobufSerializer.cs b/MediaBrowser.Common.Implementations/Serialization/ProtobufSerializer.cs new file mode 100644 index 000000000..85325f3c1 --- /dev/null +++ b/MediaBrowser.Common.Implementations/Serialization/ProtobufSerializer.cs @@ -0,0 +1,158 @@ +using MediaBrowser.Model.Serialization; +using ProtoBuf; +using ProtoBuf.Meta; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace MediaBrowser.Common.Implementations.Serialization +{ + /// + /// Creates a compiled protobuf serializer based on a set of assemblies + /// + public class ProtobufSerializer : IProtobufSerializer + { + /// + /// Gets or sets the type model. + /// + /// The type model. + private TypeModel TypeModel { get; set; } + + /// + /// Serializes to stream. + /// + /// The obj. + /// The stream. + /// obj + public void SerializeToStream(object obj, Stream stream) + { + if (obj == null) + { + throw new ArgumentNullException("obj"); + } + if (stream == null) + { + throw new ArgumentNullException("stream"); + } + + TypeModel.Serialize(stream, obj); + } + + /// + /// Deserializes from stream. + /// + /// The stream. + /// The type. + /// System.Object. + /// stream + public object DeserializeFromStream(Stream stream, Type type) + { + if (stream == null) + { + throw new ArgumentNullException("stream"); + } + + return TypeModel.Deserialize(stream, null, type); + } + + /// + /// Deserializes from stream. + /// + /// + /// The stream. + /// ``0. + public T DeserializeFromStream(Stream stream) + where T : class + { + return DeserializeFromStream(stream, typeof(T)) as T; + } + + /// + /// Serializes to file. + /// + /// + /// The obj. + /// The file. + /// file + public void SerializeToFile(T obj, string file) + { + if (string.IsNullOrEmpty(file)) + { + throw new ArgumentNullException("file"); + } + + using (Stream stream = File.Open(file, FileMode.Create)) + { + SerializeToStream(obj, stream); + } + } + + /// + /// Deserializes from file. + /// + /// + /// The file. + /// ``0. + /// file + public T DeserializeFromFile(string file) + where T : class + { + if (string.IsNullOrEmpty(file)) + { + throw new ArgumentNullException("file"); + } + + using (Stream stream = File.OpenRead(file)) + { + return DeserializeFromStream(stream); + } + } + + /// + /// Serializes to bytes. + /// + /// + /// The obj. + /// System.Byte[][]. + /// obj + public byte[] SerializeToBytes(T obj) + where T : class + { + if (obj == null) + { + throw new ArgumentNullException("obj"); + } + + using (var stream = new MemoryStream()) + { + SerializeToStream(obj, stream); + return stream.ToArray(); + } + } + + /// + /// Creates the specified assemblies. + /// + /// DynamicProtobufSerializer. + /// assemblies + public static ProtobufSerializer Create(IEnumerable types) + { + if (types == null) + { + throw new ArgumentNullException("types"); + } + + var model = TypeModel.Create(); + var attributeType = typeof(ProtoContractAttribute); + + // Find all ProtoContracts in the current assembly + foreach (var type in types.Where(t => Attribute.IsDefined(t, attributeType))) + { + model.Add(type, true); + } + + return new ProtobufSerializer { TypeModel = model.Compile() }; + } + } +} diff --git a/MediaBrowser.Common.Implementations/Serialization/XmlSerializer.cs b/MediaBrowser.Common.Implementations/Serialization/XmlSerializer.cs new file mode 100644 index 000000000..d01199f6f --- /dev/null +++ b/MediaBrowser.Common.Implementations/Serialization/XmlSerializer.cs @@ -0,0 +1,140 @@ +using MediaBrowser.Model.Serialization; +using System; +using System.IO; +using System.Xml; + +namespace MediaBrowser.Common.Implementations.Serialization +{ + /// + /// Provides a wrapper around third party xml serialization. + /// + public class XmlSerializer : IXmlSerializer + { + /// + /// Serializes to writer. + /// + /// The obj. + /// The writer. + private void SerializeToWriter(object obj, XmlTextWriter writer) + { + writer.Formatting = Formatting.Indented; + var netSerializer = new System.Xml.Serialization.XmlSerializer(obj.GetType()); + netSerializer.Serialize(writer, obj); + } + + /// + /// Deserializes from stream. + /// + /// + /// The stream. + /// ``0. + public T DeserializeFromStream(Stream stream) + { + using (var reader = new XmlTextReader(stream)) + { + var netSerializer = new System.Xml.Serialization.XmlSerializer(typeof(T)); + + return (T)netSerializer.Deserialize(reader); + } + } + + /// + /// Deserializes from stream. + /// + /// The type. + /// The stream. + /// System.Object. + public object DeserializeFromStream(Type type, Stream stream) + { + using (var reader = new XmlTextReader(stream)) + { + var netSerializer = new System.Xml.Serialization.XmlSerializer(type); + + return netSerializer.Deserialize(reader); + } + } + + /// + /// Serializes to stream. + /// + /// The obj. + /// The stream. + public void SerializeToStream(object obj, Stream stream) + { + using (var writer = new XmlTextWriter(stream, null)) + { + SerializeToWriter(obj, writer); + } + } + + /// + /// Deserializes from file. + /// + /// + /// The file. + /// ``0. + public T DeserializeFromFile(string file) + { + using (var stream = File.OpenRead(file)) + { + return DeserializeFromStream(stream); + } + } + + /// + /// Serializes to file. + /// + /// The obj. + /// The file. + public void SerializeToFile(object obj, string file) + { + using (var stream = new FileStream(file, FileMode.Create)) + { + SerializeToStream(obj, stream); + } + } + + /// + /// Deserializes from file. + /// + /// The type. + /// The file. + /// System.Object. + public object DeserializeFromFile(Type type, string file) + { + using (var stream = File.OpenRead(file)) + { + return DeserializeFromStream(type, stream); + } + } + + /// + /// Deserializes from bytes. + /// + /// The type. + /// The buffer. + /// System.Object. + public object DeserializeFromBytes(Type type, byte[] buffer) + { + using (var stream = new MemoryStream(buffer)) + { + return DeserializeFromStream(type, stream); + } + } + + /// + /// Serializes to bytes. + /// + /// The obj. + /// System.Byte[][]. + public byte[] SerializeToBytes(object obj) + { + using (var stream = new MemoryStream()) + { + SerializeToStream(obj, stream); + + return stream.ToArray(); + } + } + } +} diff --git a/MediaBrowser.Common.Implementations/packages.config b/MediaBrowser.Common.Implementations/packages.config new file mode 100644 index 000000000..14eb42cac --- /dev/null +++ b/MediaBrowser.Common.Implementations/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/MediaBrowser.Common/Kernel/BaseApplicationPaths.cs b/MediaBrowser.Common/Kernel/BaseApplicationPaths.cs deleted file mode 100644 index 936c484c8..000000000 --- a/MediaBrowser.Common/Kernel/BaseApplicationPaths.cs +++ /dev/null @@ -1,304 +0,0 @@ -using System; -using System.Configuration; -using System.IO; -using System.Reflection; - -namespace MediaBrowser.Common.Kernel -{ - /// - /// Provides a base class to hold common application paths used by both the Ui and Server. - /// This can be subclassed to add application-specific paths. - /// - public abstract class BaseApplicationPaths - { - /// - /// The _program data path - /// - private string _programDataPath; - /// - /// Gets the path to the program data folder - /// - /// The program data path. - public string ProgramDataPath - { - get - { - return _programDataPath ?? (_programDataPath = GetProgramDataPath()); - } - } - - /// - /// The _data directory - /// - private string _dataDirectory; - /// - /// Gets the folder path to the data directory - /// - /// The data directory. - public string DataPath - { - get - { - if (_dataDirectory == null) - { - _dataDirectory = Path.Combine(ProgramDataPath, "data"); - - if (!Directory.Exists(_dataDirectory)) - { - Directory.CreateDirectory(_dataDirectory); - } - } - - return _dataDirectory; - } - } - - /// - /// The _image cache path - /// - private string _imageCachePath; - /// - /// Gets the image cache path. - /// - /// The image cache path. - public string ImageCachePath - { - get - { - if (_imageCachePath == null) - { - _imageCachePath = Path.Combine(CachePath, "images"); - - if (!Directory.Exists(_imageCachePath)) - { - Directory.CreateDirectory(_imageCachePath); - } - } - - return _imageCachePath; - } - } - - /// - /// The _plugins path - /// - private string _pluginsPath; - /// - /// Gets the path to the plugin directory - /// - /// The plugins path. - public string PluginsPath - { - get - { - if (_pluginsPath == null) - { - _pluginsPath = Path.Combine(ProgramDataPath, "plugins"); - if (!Directory.Exists(_pluginsPath)) - { - Directory.CreateDirectory(_pluginsPath); - } - } - - return _pluginsPath; - } - } - - /// - /// The _plugin configurations path - /// - private string _pluginConfigurationsPath; - /// - /// Gets the path to the plugin configurations directory - /// - /// The plugin configurations path. - public string PluginConfigurationsPath - { - get - { - if (_pluginConfigurationsPath == null) - { - _pluginConfigurationsPath = Path.Combine(PluginsPath, "configurations"); - if (!Directory.Exists(_pluginConfigurationsPath)) - { - Directory.CreateDirectory(_pluginConfigurationsPath); - } - } - - return _pluginConfigurationsPath; - } - } - - private string _tempUpdatePath; - /// - /// Gets the path to where temporary update files will be stored - /// - /// The plugin configurations path. - public string TempUpdatePath - { - get - { - if (_tempUpdatePath == null) - { - _tempUpdatePath = Path.Combine(ProgramDataPath, "Updates"); - if (!Directory.Exists(_tempUpdatePath)) - { - Directory.CreateDirectory(_tempUpdatePath); - } - } - - return _tempUpdatePath; - } - } - - /// - /// The _log directory path - /// - private string _logDirectoryPath; - /// - /// Gets the path to the log directory - /// - /// The log directory path. - public string LogDirectoryPath - { - get - { - if (_logDirectoryPath == null) - { - _logDirectoryPath = Path.Combine(ProgramDataPath, "logs"); - if (!Directory.Exists(_logDirectoryPath)) - { - Directory.CreateDirectory(_logDirectoryPath); - } - } - return _logDirectoryPath; - } - } - - /// - /// The _configuration directory path - /// - private string _configurationDirectoryPath; - /// - /// Gets the path to the application configuration root directory - /// - /// The configuration directory path. - public string ConfigurationDirectoryPath - { - get - { - if (_configurationDirectoryPath == null) - { - _configurationDirectoryPath = Path.Combine(ProgramDataPath, "config"); - if (!Directory.Exists(_configurationDirectoryPath)) - { - Directory.CreateDirectory(_configurationDirectoryPath); - } - } - return _configurationDirectoryPath; - } - } - - /// - /// The _system configuration file path - /// - private string _systemConfigurationFilePath; - /// - /// Gets the path to the system configuration file - /// - /// The system configuration file path. - public string SystemConfigurationFilePath - { - get - { - return _systemConfigurationFilePath ?? (_systemConfigurationFilePath = Path.Combine(ConfigurationDirectoryPath, "system.xml")); - } - } - - /// - /// The _cache directory - /// - private string _cachePath; - /// - /// Gets the folder path to the cache directory - /// - /// The cache directory. - public string CachePath - { - get - { - if (_cachePath == null) - { - _cachePath = Path.Combine(ProgramDataPath, "cache"); - - if (!Directory.Exists(_cachePath)) - { - Directory.CreateDirectory(_cachePath); - } - } - - return _cachePath; - } - } - - /// - /// The _temp directory - /// - private string _tempDirectory; - /// - /// Gets the folder path to the temp directory within the cache folder - /// - /// The temp directory. - public string TempDirectory - { - get - { - if (_tempDirectory == null) - { - _tempDirectory = Path.Combine(CachePath, "temp"); - - if (!Directory.Exists(_tempDirectory)) - { - Directory.CreateDirectory(_tempDirectory); - } - } - - return _tempDirectory; - } - } - - /// - /// Gets the path to the application's ProgramDataFolder - /// - /// System.String. - public static string GetProgramDataPath() - { -#if DEBUG - string programDataPath = ConfigurationManager.AppSettings["DebugProgramDataPath"]; - -#else - string programDataPath = Path.Combine(ConfigurationManager.AppSettings["ReleaseProgramDataPath"], ConfigurationManager.AppSettings["ProgramDataFolderName"]); -#endif - - programDataPath = programDataPath.Replace("%CommonApplicationData%", Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData)); - - // If it's a relative path, e.g. "..\" - if (!Path.IsPathRooted(programDataPath)) - { - var path = Assembly.GetExecutingAssembly().Location; - path = Path.GetDirectoryName(path); - - programDataPath = Path.Combine(path, programDataPath); - - programDataPath = Path.GetFullPath(programDataPath); - } - - if (!Directory.Exists(programDataPath)) - { - Directory.CreateDirectory(programDataPath); - } - - return programDataPath; - } - } -} diff --git a/MediaBrowser.Common/Kernel/BaseKernel.cs b/MediaBrowser.Common/Kernel/BaseKernel.cs index 28ccd8602..a5a9b46ec 100644 --- a/MediaBrowser.Common/Kernel/BaseKernel.cs +++ b/MediaBrowser.Common/Kernel/BaseKernel.cs @@ -2,16 +2,14 @@ using MediaBrowser.Common.Net; using MediaBrowser.Common.Plugins; using MediaBrowser.Common.ScheduledTasks; -using MediaBrowser.Common.Serialization; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Serialization; using MediaBrowser.Model.System; using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; -using System.Reflection; using System.Threading; using System.Threading.Tasks; @@ -24,7 +22,7 @@ namespace MediaBrowser.Common.Kernel /// The type of the T application paths type. public abstract class BaseKernel : IDisposable, IKernel where TConfigurationType : BaseApplicationConfiguration, new() - where TApplicationPathsType : BaseApplicationPaths, new() + where TApplicationPathsType : IApplicationPaths { /// /// Occurs when [has pending restart changed]. @@ -129,7 +127,7 @@ namespace MediaBrowser.Common.Kernel get { // Lazy load - LazyInitializer.EnsureInitialized(ref _configuration, ref _configurationLoaded, ref _configurationSyncLock, () => XmlSerializer.GetXmlConfiguration(ApplicationPaths.SystemConfigurationFilePath, Logger)); + LazyInitializer.EnsureInitialized(ref _configuration, ref _configurationLoaded, ref _configurationSyncLock, () => GetXmlConfiguration(ApplicationPaths.SystemConfigurationFilePath)); return _configuration; } protected set @@ -161,19 +159,6 @@ namespace MediaBrowser.Common.Kernel /// The application paths. public TApplicationPathsType ApplicationPaths { get; private set; } - /// - /// The _failed assembly loads - /// - private readonly List _failedPluginAssemblies = new List(); - /// - /// Gets the plugin assemblies that failed to load. - /// - /// The failed assembly loads. - public IEnumerable FailedPluginAssemblies - { - get { return _failedPluginAssemblies; } - } - /// /// Gets the list of currently loaded plugins /// @@ -204,46 +189,6 @@ namespace MediaBrowser.Common.Kernel /// The rest services. public IEnumerable RestServices { get; private set; } - /// - /// The disposable parts - /// - private readonly List _disposableParts = new List(); - - /// - /// The _protobuf serializer initialized - /// - private bool _protobufSerializerInitialized; - /// - /// The _protobuf serializer sync lock - /// - private object _protobufSerializerSyncLock = new object(); - /// - /// Gets a dynamically compiled generated serializer that can serialize protocontracts without reflection - /// - private DynamicProtobufSerializer _protobufSerializer; - /// - /// Gets the protobuf serializer. - /// - /// The protobuf serializer. - public DynamicProtobufSerializer ProtobufSerializer - { - get - { - // Lazy load - LazyInitializer.EnsureInitialized(ref _protobufSerializer, ref _protobufSerializerInitialized, ref _protobufSerializerSyncLock, () => DynamicProtobufSerializer.Create(AllTypes)); - return _protobufSerializer; - } - private set - { - _protobufSerializer = value; - - if (value == null) - { - _protobufSerializerInitialized = false; - } - } - } - /// /// Gets the UDP server port number. /// This can't be configurable because then the user would have to configure their client to discover the server. @@ -301,42 +246,40 @@ namespace MediaBrowser.Common.Kernel protected IApplicationHost ApplicationHost { get; private set; } /// - /// Gets or sets the task manager. - /// - /// The task manager. - protected ITaskManager TaskManager { get; set; } - - /// - /// Gets the assemblies. + /// The _XML serializer /// - /// The assemblies. - protected Assembly[] Assemblies { get; private set; } - - /// - /// Gets all types. - /// - /// All types. - public Type[] AllTypes { get; private set; } + private readonly IXmlSerializer _xmlSerializer; /// /// Initializes a new instance of the class. /// /// The app host. + /// The app paths. + /// The XML serializer. /// The logger. /// isoManager - protected BaseKernel(IApplicationHost appHost, ILogger logger) + protected BaseKernel(IApplicationHost appHost, TApplicationPathsType appPaths, IXmlSerializer xmlSerializer, ILogger logger) { if (appHost == null) { throw new ArgumentNullException("appHost"); } - + if (appPaths == null) + { + throw new ArgumentNullException("appPaths"); + } + if (xmlSerializer == null) + { + throw new ArgumentNullException("xmlSerializer"); + } if (logger == null) { throw new ArgumentNullException("logger"); } + ApplicationPaths = appPaths; ApplicationHost = appHost; + _xmlSerializer = xmlSerializer; Logger = logger; } @@ -344,14 +287,12 @@ namespace MediaBrowser.Common.Kernel /// Initializes the Kernel /// /// Task. - public async Task Init() + public Task Init() { - ApplicationPaths = new TApplicationPathsType(); - IsFirstRun = !File.Exists(ApplicationPaths.SystemConfigurationFilePath); // Performs initializations that can be reloaded at anytime - await Reload().ConfigureAwait(false); + return Reload(); } /// @@ -377,7 +318,6 @@ namespace MediaBrowser.Common.Kernel { // Set these to null so that they can be lazy loaded again Configuration = null; - ProtobufSerializer = null; ReloadLogger(); @@ -388,14 +328,12 @@ namespace MediaBrowser.Common.Kernel await OnConfigurationLoaded().ConfigureAwait(false); - DisposeTaskManager(); - TaskManager = new TaskManager(Logger); + FindParts(); - Logger.Info("Loading Plugins"); - await ReloadComposableParts().ConfigureAwait(false); + await OnComposablePartsLoaded().ConfigureAwait(false); DisposeTcpManager(); - TcpManager = new TcpManager(ApplicationHost, this, ApplicationHost.Resolve(), Logger); + TcpManager = (TcpManager)ApplicationHost.CreateInstance(typeof(TcpManager)); } /// @@ -417,184 +355,14 @@ namespace MediaBrowser.Common.Kernel OnLoggerLoaded(); } - /// - /// Uses MEF to locate plugins - /// Subclasses can use this to locate types within plugins - /// - /// Task. - private async Task ReloadComposableParts() - { - _failedPluginAssemblies.Clear(); - - DisposeComposableParts(); - - Assemblies = GetComposablePartAssemblies().ToArray(); - - AllTypes = Assemblies.SelectMany(GetTypes).ToArray(); - - ComposeParts(AllTypes); - - await OnComposablePartsLoaded().ConfigureAwait(false); - } - - /// - /// Composes the parts. - /// - /// All types. - private void ComposeParts(IEnumerable allTypes) - { - var concreteTypes = allTypes.Where(t => t.IsClass && !t.IsAbstract && !t.IsInterface && !t.IsGenericType).ToArray(); - - RegisterExportedValues(); - - FindParts(concreteTypes); - } - /// /// Composes the parts with ioc container. /// - /// All types. - protected virtual void FindParts(Type[] allTypes) - { - RestServices = GetExports(allTypes); - WebSocketListeners = GetExports(allTypes); - Plugins = GetExports(allTypes); - - var tasks = GetExports(allTypes, false); - - TaskManager.AddTasks(tasks); - } - - /// - /// Gets the exports. - /// - /// - /// All types. - /// if set to true [manage liftime]. - /// IEnumerable{``0}. - protected IEnumerable GetExports(Type[] allTypes, bool manageLiftime = true) - { - var currentType = typeof(T); - - Logger.Info("Composing instances of " + currentType.Name); - - var parts = allTypes.Where(currentType.IsAssignableFrom).Select(Instantiate).Cast().ToArray(); - - if (manageLiftime) - { - _disposableParts.AddRange(parts.OfType()); - } - - return parts; - } - - /// - /// Instantiates the specified type. - /// - /// The type. - /// System.Object. - private object Instantiate(Type type) - { - return ApplicationHost.CreateInstance(type); - } - - /// - /// Composes the exported values. - /// - /// The container. - protected virtual void RegisterExportedValues() + protected virtual void FindParts() { - ApplicationHost.RegisterSingleInstance(this); - ApplicationHost.RegisterSingleInstance(TaskManager); - } - - /// - /// Gets the composable part assemblies. - /// - /// IEnumerable{Assembly}. - protected virtual IEnumerable GetComposablePartAssemblies() - { - // 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 - var pluginAssemblies = Directory.EnumerateFiles(ApplicationPaths.PluginsPath, "*.dll", SearchOption.TopDirectoryOnly) - .Select(file => - { - try - { - return Assembly.Load(File.ReadAllBytes((file))); - } - catch (Exception ex) - { - _failedPluginAssemblies.Add(file); - Logger.ErrorException("Error loading {0}", ex, file); - return null; - } - - }).Where(a => a != null); - - foreach (var pluginAssembly in pluginAssemblies) - { - yield return pluginAssembly; - } - - var runningDirectory = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName); - var corePluginDirectory = Path.Combine(runningDirectory, "CorePlugins"); - - // This will prevent the .dll file from getting locked, and allow us to replace it when needed - pluginAssemblies = Directory.EnumerateFiles(corePluginDirectory, "*.dll", SearchOption.TopDirectoryOnly) - .Select(file => - { - try - { - return Assembly.Load(File.ReadAllBytes((file))); - } - catch (Exception ex) - { - _failedPluginAssemblies.Add(file); - Logger.ErrorException("Error loading {0}", ex, file); - return null; - } - - }).Where(a => a != null); - - foreach (var pluginAssembly in pluginAssemblies) - { - yield return pluginAssembly; - } - - // Include composable parts in the Model assembly - yield return typeof(SystemInfo).Assembly; - - // Include composable parts in the Common assembly - yield return Assembly.GetExecutingAssembly(); - - // Include composable parts in the subclass assembly - yield return GetType().Assembly; - } - - /// - /// 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 - private static IEnumerable GetTypes(Assembly assembly) - { - if (assembly == null) - { - throw new ArgumentNullException("assembly"); - } - - try - { - return assembly.GetTypes(); - } - catch (ReflectionTypeLoadException ex) - { - // If it fails we can still get a list of the Types it was able to resolve - return ex.Types.Where(t => t != null); - } + RestServices = ApplicationHost.GetExports(); + WebSocketListeners = ApplicationHost.GetExports(); + Plugins = ApplicationHost.GetExports(); } /// @@ -612,7 +380,7 @@ namespace MediaBrowser.Common.Kernel try { - plugin.Initialize(this, Logger); + plugin.Initialize(this, _xmlSerializer, Logger); Logger.Info("{0} {1} initialized.", plugin.Name, plugin.Version); } @@ -654,12 +422,7 @@ namespace MediaBrowser.Common.Kernel if (dispose) { DisposeTcpManager(); - DisposeTaskManager(); DisposeHttpManager(); - - DisposeComposableParts(); - - _disposableParts.Clear(); } } @@ -675,18 +438,6 @@ namespace MediaBrowser.Common.Kernel } } - /// - /// Disposes the task manager. - /// - private void DisposeTaskManager() - { - if (TaskManager != null) - { - TaskManager.Dispose(); - TaskManager = null; - } - } - /// /// Disposes the HTTP manager. /// @@ -699,17 +450,6 @@ namespace MediaBrowser.Common.Kernel } } - /// - /// Disposes all objects gathered through MEF composable parts - /// - protected virtual void DisposeComposableParts() - { - foreach (var part in _disposableParts) - { - part.Dispose(); - } - } - /// /// Gets the current application version /// @@ -761,7 +501,7 @@ namespace MediaBrowser.Common.Kernel IsNetworkDeployed = ApplicationHost.CanSelfUpdate, WebSocketPortNumber = TcpManager.WebSocketPortNumber, SupportsNativeWebSocket = TcpManager.SupportsNativeWebSocket, - FailedPluginAssemblies = FailedPluginAssemblies.ToArray() + FailedPluginAssemblies = ApplicationHost.FailedAssemblies.ToArray() }; } @@ -777,7 +517,7 @@ namespace MediaBrowser.Common.Kernel { lock (_configurationSaveLock) { - XmlSerializer.SerializeToFile(Configuration, ApplicationPaths.SystemConfigurationFilePath); + _xmlSerializer.SerializeToFile(Configuration, ApplicationPaths.SystemConfigurationFilePath); } OnConfigurationUpdated(); @@ -787,7 +527,7 @@ namespace MediaBrowser.Common.Kernel /// Gets the application paths. /// /// The application paths. - BaseApplicationPaths IKernel.ApplicationPaths + IApplicationPaths IKernel.ApplicationPaths { get { return ApplicationPaths; } } @@ -798,6 +538,63 @@ namespace MediaBrowser.Common.Kernel BaseApplicationConfiguration IKernel.Configuration { get { return Configuration; } + } + + /// + /// Reads an xml configuration file from the file system + /// It will immediately re-serialize and save if new serialization data is available due to property changes + /// + /// The type. + /// The path. + /// System.Object. + public object GetXmlConfiguration(Type type, string path) + { + Logger.Info("Loading {0} at {1}", type.Name, path); + + object configuration; + + byte[] buffer = null; + + // Use try/catch to avoid the extra file system lookup using File.Exists + try + { + buffer = File.ReadAllBytes(path); + + configuration = _xmlSerializer.DeserializeFromBytes(type, buffer); + } + catch (FileNotFoundException) + { + configuration = ApplicationHost.CreateInstance(type); + } + + // Take the object we just got and serialize it back to bytes + var newBytes = _xmlSerializer.SerializeToBytes(configuration); + + // If the file didn't exist before, or if something has changed, re-save + if (buffer == null || !buffer.SequenceEqual(newBytes)) + { + Logger.Info("Saving {0} to {1}", type.Name, path); + + // Save it after load in case we got new items + File.WriteAllBytes(path, newBytes); + } + + return configuration; + } + + + /// + /// Reads an xml configuration file from the file system + /// It will immediately save the configuration after loading it, just + /// in case there are new serializable properties + /// + /// + /// The path. + /// ``0. + private T GetXmlConfiguration(string path) + where T : class + { + return GetXmlConfiguration(typeof(T), path) as T; } } } diff --git a/MediaBrowser.Common/Kernel/IApplicationHost.cs b/MediaBrowser.Common/Kernel/IApplicationHost.cs index fe2f00a12..4b564581b 100644 --- a/MediaBrowser.Common/Kernel/IApplicationHost.cs +++ b/MediaBrowser.Common/Kernel/IApplicationHost.cs @@ -1,5 +1,6 @@ using MediaBrowser.Model.Updates; using System; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; @@ -32,6 +33,26 @@ namespace MediaBrowser.Common.Kernel /// true if this instance can self update; otherwise, false. bool CanSelfUpdate { get; } + /// + /// Gets the failed assemblies. + /// + /// The failed assemblies. + IEnumerable FailedAssemblies { get; } + + /// + /// Gets all concrete types. + /// + /// All concrete types. + Type[] AllConcreteTypes { get; } + + /// + /// Gets the exports. + /// + /// + /// if set to true [manage liftime]. + /// IEnumerable{``0}. + IEnumerable GetExports(bool manageLiftime = true); + /// /// Checks for update. /// diff --git a/MediaBrowser.Common/Kernel/IApplicationPaths.cs b/MediaBrowser.Common/Kernel/IApplicationPaths.cs new file mode 100644 index 000000000..abb783642 --- /dev/null +++ b/MediaBrowser.Common/Kernel/IApplicationPaths.cs @@ -0,0 +1,76 @@ + +namespace MediaBrowser.Common.Kernel +{ + /// + /// Interface IApplicationPaths + /// + public interface IApplicationPaths + { + /// + /// Gets the path to the program data folder + /// + /// The program data path. + string ProgramDataPath { get; } + + /// + /// Gets the folder path to the data directory + /// + /// The data directory. + string DataPath { get; } + + /// + /// Gets the image cache path. + /// + /// The image cache path. + string ImageCachePath { get; } + + /// + /// Gets the path to the plugin directory + /// + /// The plugins path. + string PluginsPath { get; } + + /// + /// Gets the path to the plugin configurations directory + /// + /// The plugin configurations path. + string PluginConfigurationsPath { get; } + + /// + /// Gets the path to where temporary update files will be stored + /// + /// The plugin configurations path. + string TempUpdatePath { get; } + + /// + /// Gets the path to the log directory + /// + /// The log directory path. + string LogDirectoryPath { get; } + + /// + /// Gets the path to the application configuration root directory + /// + /// The configuration directory path. + string ConfigurationDirectoryPath { get; } + + /// + /// Gets the path to the system configuration file + /// + /// The system configuration file path. + string SystemConfigurationFilePath { get; } + + /// + /// Gets the folder path to the cache directory + /// + /// The cache directory. + string CachePath { get; } + + /// + /// Gets the folder path to the temp directory within the cache folder + /// + /// The temp directory. + string TempDirectory { get; } + } + +} diff --git a/MediaBrowser.Common/Kernel/IKernel.cs b/MediaBrowser.Common/Kernel/IKernel.cs index 253368f4f..fb629a24d 100644 --- a/MediaBrowser.Common/Kernel/IKernel.cs +++ b/MediaBrowser.Common/Kernel/IKernel.cs @@ -1,6 +1,5 @@ using MediaBrowser.Common.Net; using MediaBrowser.Common.Plugins; -using MediaBrowser.Common.Serialization; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.System; using System; @@ -18,7 +17,7 @@ namespace MediaBrowser.Common.Kernel /// Gets the application paths. /// /// The application paths. - BaseApplicationPaths ApplicationPaths { get; } + IApplicationPaths ApplicationPaths { get; } /// /// Gets the configuration. @@ -32,12 +31,6 @@ namespace MediaBrowser.Common.Kernel /// The kernel context. KernelContext KernelContext { get; } - /// - /// Gets the protobuf serializer. - /// - /// The protobuf serializer. - DynamicProtobufSerializer ProtobufSerializer { get; } - /// /// Inits this instance. /// @@ -156,5 +149,13 @@ namespace MediaBrowser.Common.Kernel /// Notifies the pending restart. /// void NotifyPendingRestart(); + + /// + /// Gets the XML configuration. + /// + /// The type. + /// The path. + /// System.Object. + object GetXmlConfiguration(Type type, string path); } } diff --git a/MediaBrowser.Common/Kernel/TcpManager.cs b/MediaBrowser.Common/Kernel/TcpManager.cs index 9a998823f..c04b77599 100644 --- a/MediaBrowser.Common/Kernel/TcpManager.cs +++ b/MediaBrowser.Common/Kernel/TcpManager.cs @@ -1,5 +1,4 @@ using MediaBrowser.Common.Net; -using MediaBrowser.Common.Serialization; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Logging; using System; @@ -14,6 +13,7 @@ using System.Reflection; using System.Text; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Model.Serialization; namespace MediaBrowser.Common.Kernel { @@ -35,6 +35,12 @@ namespace MediaBrowser.Common.Kernel /// The HTTP server. private IHttpServer HttpServer { get; set; } + /// + /// Gets or sets the json serializer. + /// + /// The json serializer. + private IJsonSerializer _jsonSerializer; + /// /// This subscribes to HttpListener requests and finds the appropriate BaseHandler to process it /// @@ -96,8 +102,10 @@ namespace MediaBrowser.Common.Kernel /// The application host. /// The kernel. /// The network manager. + /// The json serializer. /// The logger. - public TcpManager(IApplicationHost applicationHost, IKernel kernel, INetworkManager networkManager, ILogger logger) + /// applicationHost + public TcpManager(IApplicationHost applicationHost, IKernel kernel, INetworkManager networkManager, IJsonSerializer jsonSerializer, ILogger logger) { if (applicationHost == null) { @@ -111,12 +119,17 @@ namespace MediaBrowser.Common.Kernel { throw new ArgumentNullException("networkManager"); } + if (jsonSerializer == null) + { + throw new ArgumentNullException("jsonSerializer"); + } if (logger == null) { throw new ArgumentNullException("logger"); } _logger = logger; + _jsonSerializer = jsonSerializer; _kernel = kernel; _applicationHost = applicationHost; _networkManager = networkManager; @@ -203,7 +216,7 @@ namespace MediaBrowser.Common.Kernel /// The instance containing the event data. void HttpServer_WebSocketConnected(object sender, WebSocketConnectEventArgs e) { - var connection = new WebSocketConnection(e.WebSocket, e.Endpoint, ProcessWebSocketMessageReceived, _logger); + var connection = new WebSocketConnection(e.WebSocket, e.Endpoint, ProcessWebSocketMessageReceived, _jsonSerializer, _logger); _webSocketConnections.Add(connection); } @@ -342,7 +355,7 @@ namespace MediaBrowser.Common.Kernel _logger.Info("Sending web socket message {0}", messageType); var message = new WebSocketMessage { MessageType = messageType, Data = dataFunction() }; - var bytes = JsonSerializer.SerializeToBytes(message); + var bytes = _jsonSerializer.SerializeToBytes(message); var tasks = connections.Select(s => Task.Run(() => { diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj index 28789a816..f9147141d 100644 --- a/MediaBrowser.Common/MediaBrowser.Common.csproj +++ b/MediaBrowser.Common/MediaBrowser.Common.csproj @@ -38,10 +38,6 @@ - - False - ..\packages\protobuf-net.2.0.0.621\lib\net40\protobuf-net.dll - False ..\packages\ServiceStack.3.9.37\lib\net35\ServiceStack.dll @@ -75,14 +71,12 @@ ..\packages\ServiceStack.Text.3.9.37\lib\net35\ServiceStack.Text.dll - - @@ -98,8 +92,8 @@ - + @@ -116,7 +110,6 @@ - @@ -130,26 +123,18 @@ Resources.resx - - + - - - - - - - diff --git a/MediaBrowser.Common/Net/Handlers/BaseSerializationHandler.cs b/MediaBrowser.Common/Net/Handlers/BaseSerializationHandler.cs index 293cb6e98..a00152d78 100644 --- a/MediaBrowser.Common/Net/Handlers/BaseSerializationHandler.cs +++ b/MediaBrowser.Common/Net/Handlers/BaseSerializationHandler.cs @@ -1,6 +1,5 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Kernel; -using MediaBrowser.Common.Serialization; using System; using System.IO; using System.Threading.Tasks; @@ -104,15 +103,15 @@ namespace MediaBrowser.Common.Net.Handlers { return Task.Run(() => { - switch (SerializationFormat) - { - case SerializationFormat.Protobuf: - Kernel.ProtobufSerializer.SerializeToStream(_objectToSerialize, stream); - break; - default: - JsonSerializer.SerializeToStream(_objectToSerialize, stream); - break; - } + //switch (SerializationFormat) + //{ + // case SerializationFormat.Protobuf: + // Kernel.ProtobufSerializer.SerializeToStream(_objectToSerialize, stream); + // break; + // default: + // JsonSerializer.SerializeToStream(_objectToSerialize, stream); + // break; + //} }); } } diff --git a/MediaBrowser.Common/Net/IWebSocket.cs b/MediaBrowser.Common/Net/IWebSocket.cs index 96299e3b2..3fd4b1241 100644 --- a/MediaBrowser.Common/Net/IWebSocket.cs +++ b/MediaBrowser.Common/Net/IWebSocket.cs @@ -20,7 +20,7 @@ namespace MediaBrowser.Common.Net /// Gets or sets the receive action. /// /// The receive action. - Action OnReceiveDelegate { get; set; } + Action OnReceiveDelegate { get; set; } /// /// Sends the async. diff --git a/MediaBrowser.Common/Net/NativeWebSocket.cs b/MediaBrowser.Common/Net/NativeWebSocket.cs deleted file mode 100644 index 23f3d4be3..000000000 --- a/MediaBrowser.Common/Net/NativeWebSocket.cs +++ /dev/null @@ -1,159 +0,0 @@ -using MediaBrowser.Common.Serialization; -using MediaBrowser.Model.Logging; -using System; -using System.IO; -using System.Net.WebSockets; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Common.Net -{ - /// - /// Class NativeWebSocket - /// - public class NativeWebSocket : IWebSocket - { - /// - /// The logger - /// - private readonly ILogger _logger; - - /// - /// Gets or sets the web socket. - /// - /// The web socket. - private WebSocket WebSocket { get; set; } - - /// - /// Initializes a new instance of the class. - /// - /// The socket. - /// The logger. - /// socket - public NativeWebSocket(WebSocket socket, ILogger logger) - { - if (socket == null) - { - throw new ArgumentNullException("socket"); - } - - if (logger == null) - { - throw new ArgumentNullException("logger"); - } - - _logger = logger; - WebSocket = socket; - - Receive(); - } - - /// - /// Gets or sets the state. - /// - /// The state. - public WebSocketState State - { - get { return WebSocket.State; } - } - - /// - /// Receives this instance. - /// - private async void Receive() - { - while (true) - { - byte[] bytes; - - try - { - bytes = await ReceiveBytesAsync(CancellationToken.None).ConfigureAwait(false); - } - catch (WebSocketException ex) - { - _logger.ErrorException("Error reveiving web socket message", ex); - - break; - } - - if (OnReceiveDelegate != null) - { - using (var memoryStream = new MemoryStream(bytes)) - { - try - { - var messageResult = JsonSerializer.DeserializeFromStream(memoryStream); - - OnReceiveDelegate(messageResult); - } - catch (Exception ex) - { - _logger.ErrorException("Error processing web socket message", ex); - } - } - } - } - } - - /// - /// Receives the async. - /// - /// The cancellation token. - /// Task{WebSocketMessageInfo}. - /// Connection closed - private async Task ReceiveBytesAsync(CancellationToken cancellationToken) - { - var bytes = new byte[4096]; - var buffer = new ArraySegment(bytes); - - var result = await WebSocket.ReceiveAsync(buffer, cancellationToken).ConfigureAwait(false); - - if (result.CloseStatus.HasValue) - { - throw new WebSocketException("Connection closed"); - } - - return buffer.Array; - } - - /// - /// Sends the async. - /// - /// The bytes. - /// The type. - /// if set to true [end of message]. - /// The cancellation token. - /// Task. - public Task SendAsync(byte[] bytes, WebSocketMessageType type, bool endOfMessage, CancellationToken cancellationToken) - { - return WebSocket.SendAsync(new ArraySegment(bytes), type, true, cancellationToken); - } - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - public void Dispose() - { - Dispose(true); - } - - /// - /// Releases unmanaged and - optionally - managed resources. - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected virtual void Dispose(bool dispose) - { - if (dispose) - { - WebSocket.Dispose(); - } - } - - /// - /// Gets or sets the receive action. - /// - /// The receive action. - public Action OnReceiveDelegate { get; set; } - } -} diff --git a/MediaBrowser.Common/Net/WebSocketConnection.cs b/MediaBrowser.Common/Net/WebSocketConnection.cs index ab691c823..6b22ef78e 100644 --- a/MediaBrowser.Common/Net/WebSocketConnection.cs +++ b/MediaBrowser.Common/Net/WebSocketConnection.cs @@ -1,7 +1,7 @@ -using MediaBrowser.Common.Serialization; +using System.IO; using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Serialization; using System; -using System.Net; using System.Net.WebSockets; using System.Threading; using System.Threading.Tasks; @@ -38,14 +38,21 @@ namespace MediaBrowser.Common.Net /// private readonly ILogger _logger; + /// + /// The _json serializer + /// + private readonly IJsonSerializer _jsonSerializer; + /// /// Initializes a new instance of the class. /// /// The socket. /// The remote end point. /// The receive action. + /// The json serializer. + /// The logger. /// socket - public WebSocketConnection(IWebSocket socket, string remoteEndPoint, Action receiveAction, ILogger logger) + public WebSocketConnection(IWebSocket socket, string remoteEndPoint, Action receiveAction, IJsonSerializer jsonSerializer, ILogger logger) { if (socket == null) { @@ -59,11 +66,16 @@ namespace MediaBrowser.Common.Net { throw new ArgumentNullException("receiveAction"); } + if (jsonSerializer == null) + { + throw new ArgumentNullException("jsonSerializer"); + } if (logger == null) { throw new ArgumentNullException("logger"); } + _jsonSerializer = jsonSerializer; _socket = socket; _socket.OnReceiveDelegate = info => OnReceive(info, receiveAction); RemoteEndPoint = remoteEndPoint; @@ -73,12 +85,19 @@ namespace MediaBrowser.Common.Net /// /// Called when [receive]. /// - /// The info. + /// The bytes. /// The callback. - private void OnReceive(WebSocketMessageInfo info, Action callback) + private void OnReceive(byte[] bytes, Action callback) { try { + WebSocketMessageInfo info; + + using (var memoryStream = new MemoryStream(bytes)) + { + info = _jsonSerializer.DeserializeFromStream(memoryStream); + } + info.Connection = this; callback(info); @@ -103,8 +122,8 @@ namespace MediaBrowser.Common.Net { throw new ArgumentNullException("message"); } - - var bytes = JsonSerializer.SerializeToBytes(message); + + var bytes = _jsonSerializer.SerializeToBytes(message); return SendAsync(bytes, cancellationToken); } diff --git a/MediaBrowser.Common/Plugins/BasePlugin.cs b/MediaBrowser.Common/Plugins/BasePlugin.cs index d089aa1b3..dad3867fc 100644 --- a/MediaBrowser.Common/Plugins/BasePlugin.cs +++ b/MediaBrowser.Common/Plugins/BasePlugin.cs @@ -1,5 +1,4 @@ using MediaBrowser.Common.Kernel; -using MediaBrowser.Common.Serialization; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Plugins; using System; @@ -7,6 +6,7 @@ using System.IO; using System.Reflection; using System.Runtime.InteropServices; using System.Threading; +using MediaBrowser.Model.Serialization; namespace MediaBrowser.Common.Plugins { @@ -188,7 +188,7 @@ namespace MediaBrowser.Common.Plugins get { // Lazy load - LazyInitializer.EnsureInitialized(ref _configuration, ref _configurationInitialized, ref _configurationSyncLock, () => XmlSerializer.GetXmlConfiguration(ConfigurationType, ConfigurationFilePath, Logger) as TConfigurationType); + LazyInitializer.EnsureInitialized(ref _configuration, ref _configurationInitialized, ref _configurationSyncLock, () => Kernel.GetXmlConfiguration(ConfigurationType, ConfigurationFilePath) as TConfigurationType); return _configuration; } protected set @@ -269,26 +269,38 @@ namespace MediaBrowser.Common.Plugins /// The logger. public ILogger Logger { get; private set; } + /// + /// Gets the XML serializer. + /// + /// The XML serializer. + protected IXmlSerializer XmlSerializer { get; private set; } + /// /// Starts the plugin. /// /// The kernel. + /// The XML serializer. /// The logger. /// kernel - public void Initialize(IKernel kernel, ILogger logger) + public void Initialize(IKernel kernel, IXmlSerializer xmlSerializer, ILogger logger) { if (kernel == null) { throw new ArgumentNullException("kernel"); } + if (xmlSerializer == null) + { + throw new ArgumentNullException("xmlSerializer"); + } + if (logger == null) { throw new ArgumentNullException("logger"); } - + + XmlSerializer = xmlSerializer; Logger = logger; - Kernel = kernel; if (kernel.KernelContext == KernelContext.Server) diff --git a/MediaBrowser.Common/Plugins/IPlugin.cs b/MediaBrowser.Common/Plugins/IPlugin.cs index 198678491..1b25cdcb1 100644 --- a/MediaBrowser.Common/Plugins/IPlugin.cs +++ b/MediaBrowser.Common/Plugins/IPlugin.cs @@ -1,6 +1,7 @@ using MediaBrowser.Common.Kernel; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Plugins; +using MediaBrowser.Model.Serialization; using System; namespace MediaBrowser.Common.Plugins @@ -107,9 +108,10 @@ namespace MediaBrowser.Common.Plugins /// Starts the plugin. /// /// The kernel. + /// The XML serializer. /// The logger. /// kernel - void Initialize(IKernel kernel, ILogger logger); + void Initialize(IKernel kernel, IXmlSerializer xmlSerializer, ILogger logger); /// /// Disposes the plugins. Undos all actions performed during Init. diff --git a/MediaBrowser.Common/ScheduledTasks/BaseScheduledTask.cs b/MediaBrowser.Common/ScheduledTasks/BaseScheduledTask.cs index 845faf31a..09ceaa9ae 100644 --- a/MediaBrowser.Common/ScheduledTasks/BaseScheduledTask.cs +++ b/MediaBrowser.Common/ScheduledTasks/BaseScheduledTask.cs @@ -1,6 +1,5 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Kernel; -using MediaBrowser.Common.Serialization; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Tasks; using System; @@ -90,7 +89,7 @@ namespace MediaBrowser.Common.ScheduledTasks { try { - return JsonSerializer.DeserializeFromFile(HistoryFilePath); + return TaskManager.GetLastExecutionResult(this); } catch (IOException) { @@ -109,74 +108,6 @@ namespace MediaBrowser.Common.ScheduledTasks } } - /// - /// The _scheduled tasks data directory - /// - private string _scheduledTasksDataDirectory; - /// - /// Gets the scheduled tasks data directory. - /// - /// The scheduled tasks data directory. - private string ScheduledTasksDataDirectory - { - get - { - if (_scheduledTasksDataDirectory == null) - { - _scheduledTasksDataDirectory = Path.Combine(Kernel.ApplicationPaths.DataPath, "ScheduledTasks"); - - if (!Directory.Exists(_scheduledTasksDataDirectory)) - { - Directory.CreateDirectory(_scheduledTasksDataDirectory); - } - } - return _scheduledTasksDataDirectory; - } - } - - /// - /// The _scheduled tasks configuration directory - /// - private string _scheduledTasksConfigurationDirectory; - /// - /// Gets the scheduled tasks configuration directory. - /// - /// The scheduled tasks configuration directory. - private string ScheduledTasksConfigurationDirectory - { - get - { - if (_scheduledTasksConfigurationDirectory == null) - { - _scheduledTasksConfigurationDirectory = Path.Combine(Kernel.ApplicationPaths.ConfigurationDirectoryPath, "ScheduledTasks"); - - if (!Directory.Exists(_scheduledTasksConfigurationDirectory)) - { - Directory.CreateDirectory(_scheduledTasksConfigurationDirectory); - } - } - return _scheduledTasksConfigurationDirectory; - } - } - - /// - /// Gets the configuration file path. - /// - /// The configuration file path. - private string ConfigurationFilePath - { - get { return Path.Combine(ScheduledTasksConfigurationDirectory, Id + ".js"); } - } - - /// - /// Gets the history file path. - /// - /// The history file path. - private string HistoryFilePath - { - get { return Path.Combine(ScheduledTasksDataDirectory, Id + ".js"); } - } - /// /// Gets the current cancellation token /// @@ -217,7 +148,7 @@ namespace MediaBrowser.Common.ScheduledTasks /// /// The _triggers /// - private IEnumerable _triggers; + private IEnumerable _triggers; /// /// The _triggers initialized /// @@ -231,24 +162,11 @@ namespace MediaBrowser.Common.ScheduledTasks /// /// The triggers. /// value - public IEnumerable Triggers + public IEnumerable Triggers { get { - LazyInitializer.EnsureInitialized(ref _triggers, ref _triggersInitialized, ref _triggersSyncLock, () => - { - try - { - return JsonSerializer.DeserializeFromFile>(ConfigurationFilePath) - .Select(ScheduledTaskHelpers.GetTrigger) - .ToList(); - } - catch (IOException) - { - // File doesn't exist. No biggie. Return defaults. - return GetDefaultTriggers(); - } - }); + LazyInitializer.EnsureInitialized(ref _triggers, ref _triggersInitialized, ref _triggersSyncLock, () => TaskManager.LoadTriggers(this)); return _triggers; } @@ -271,7 +189,7 @@ namespace MediaBrowser.Common.ScheduledTasks ReloadTriggerEvents(false); - JsonSerializer.SerializeToFile(_triggers.Select(ScheduledTaskHelpers.GetTriggerInfo), ConfigurationFilePath); + TaskManager.SaveTriggers(this, _triggers); } } @@ -279,7 +197,7 @@ namespace MediaBrowser.Common.ScheduledTasks /// Creates the triggers that define when the task will run /// /// IEnumerable{BaseTaskTrigger}. - protected abstract IEnumerable GetDefaultTriggers(); + public abstract IEnumerable GetDefaultTriggers(); /// /// Returns the task to be executed @@ -314,6 +232,7 @@ namespace MediaBrowser.Common.ScheduledTasks /// The _id /// private Guid? _id; + /// /// Gets the unique id. /// @@ -352,13 +271,19 @@ namespace MediaBrowser.Common.ScheduledTasks /// /// The source of the event. /// The instance containing the event data. - void trigger_Triggered(object sender, EventArgs e) + async void trigger_Triggered(object sender, EventArgs e) { - var trigger = (BaseTaskTrigger)sender; + var trigger = (ITaskTrigger)sender; Logger.Info("{0} fired for task: {1}", trigger.GetType().Name, Name); + trigger.Stop(); + TaskManager.QueueScheduledTask(this); + + await Task.Delay(1000).ConfigureAwait(false); + + trigger.Start(false); } /// @@ -404,10 +329,9 @@ namespace MediaBrowser.Common.ScheduledTasks status = TaskCompletionStatus.Failed; } + var startTime = CurrentExecutionStartTime; var endTime = DateTime.UtcNow; - LogResult(endTime, status); - Kernel.TcpManager.SendWebSocketMessage("ScheduledTaskEndExecute", LastExecutionResult); progress.ProgressChanged -= progress_ProgressChanged; @@ -415,33 +339,7 @@ namespace MediaBrowser.Common.ScheduledTasks CurrentCancellationTokenSource = null; CurrentProgress = null; - TaskManager.OnTaskCompleted(this); - } - - /// - /// Logs the result. - /// - /// The end time. - /// The status. - private void LogResult(DateTime endTime, TaskCompletionStatus status) - { - var startTime = CurrentExecutionStartTime; - var elapsedTime = endTime - startTime; - - Logger.Info("{0} {1} after {2} minute(s) and {3} seconds", Name, status, Math.Truncate(elapsedTime.TotalMinutes), elapsedTime.Seconds); - - var result = new TaskResult - { - StartTimeUtc = startTime, - EndTimeUtc = endTime, - Status = status, - Name = Name, - Id = Id - }; - - JsonSerializer.SerializeToFile(result, HistoryFilePath); - - LastExecutionResult = result; + TaskManager.OnTaskCompleted(this, startTime, endTime, status); } /// @@ -501,7 +399,7 @@ namespace MediaBrowser.Common.ScheduledTasks if (State == TaskState.Running) { - LogResult(DateTime.UtcNow, TaskCompletionStatus.Aborted); + TaskManager.OnTaskCompleted(this, CurrentExecutionStartTime, DateTime.UtcNow, TaskCompletionStatus.Aborted); } if (CurrentCancellationTokenSource != null) @@ -519,7 +417,7 @@ namespace MediaBrowser.Common.ScheduledTasks foreach (var trigger in Triggers) { trigger.Triggered -= trigger_Triggered; - trigger.Dispose(); + trigger.Stop(); } } } diff --git a/MediaBrowser.Common/ScheduledTasks/BaseTaskTrigger.cs b/MediaBrowser.Common/ScheduledTasks/BaseTaskTrigger.cs deleted file mode 100644 index ed302ed39..000000000 --- a/MediaBrowser.Common/ScheduledTasks/BaseTaskTrigger.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; -using System.Threading.Tasks; - -namespace MediaBrowser.Common.ScheduledTasks -{ - /// - /// Use to indicate that a scheduled task should run - /// - public abstract class BaseTaskTrigger : IDisposable - { - /// - /// Fires when the trigger condition is satisfied and the task should run - /// - internal event EventHandler Triggered; - - /// - /// Called when [triggered]. - /// - protected async void OnTriggered() - { - Stop(); - - if (Triggered != null) - { - Triggered(this, EventArgs.Empty); - } - - await Task.Delay(1000).ConfigureAwait(false); - - Start(false); - } - - /// - /// Stars waiting for the trigger action - /// - protected internal abstract void Start(bool isApplicationStartup); - - /// - /// Stops waiting for the trigger action - /// - protected internal abstract void Stop(); - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Releases unmanaged and - optionally - managed resources. - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected virtual void Dispose(bool dispose) - { - if (dispose) - { - Stop(); - } - } - } -} diff --git a/MediaBrowser.Common/ScheduledTasks/DailyTrigger.cs b/MediaBrowser.Common/ScheduledTasks/DailyTrigger.cs index fb749f77c..bfecadee7 100644 --- a/MediaBrowser.Common/ScheduledTasks/DailyTrigger.cs +++ b/MediaBrowser.Common/ScheduledTasks/DailyTrigger.cs @@ -6,7 +6,7 @@ namespace MediaBrowser.Common.ScheduledTasks /// /// Represents a task trigger that fires everyday /// - public class DailyTrigger : BaseTaskTrigger + public class DailyTrigger : ITaskTrigger { /// /// Get the time of day to trigger the task to run @@ -24,7 +24,7 @@ namespace MediaBrowser.Common.ScheduledTasks /// Stars waiting for the trigger action /// /// if set to true [is application startup]. - protected internal override void Start(bool isApplicationStartup) + public void Start(bool isApplicationStartup) { DisposeTimer(); @@ -39,33 +39,35 @@ namespace MediaBrowser.Common.ScheduledTasks /// /// Stops waiting for the trigger action /// - protected internal override void Stop() + public void Stop() { DisposeTimer(); } /// - /// Disposes this instance. + /// Disposes the timer. /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected override void Dispose(bool dispose) + private void DisposeTimer() { - if (dispose) + if (Timer != null) { - DisposeTimer(); + Timer.Dispose(); } - - base.Dispose(dispose); } /// - /// Disposes the timer. + /// Occurs when [triggered]. /// - private void DisposeTimer() + public event EventHandler Triggered; + + /// + /// Called when [triggered]. + /// + private void OnTriggered() { - if (Timer != null) + if (Triggered != null) { - Timer.Dispose(); + Triggered(this, EventArgs.Empty); } } } diff --git a/MediaBrowser.Common/ScheduledTasks/IScheduledTask.cs b/MediaBrowser.Common/ScheduledTasks/IScheduledTask.cs index cba5fc5d0..6f3a3857f 100644 --- a/MediaBrowser.Common/ScheduledTasks/IScheduledTask.cs +++ b/MediaBrowser.Common/ScheduledTasks/IScheduledTask.cs @@ -14,7 +14,7 @@ namespace MediaBrowser.Common.ScheduledTasks /// Gets the triggers. /// /// The triggers. - IEnumerable Triggers { get; set; } + IEnumerable Triggers { get; set; } /// /// Gets the last execution result. @@ -75,5 +75,11 @@ namespace MediaBrowser.Common.ScheduledTasks /// Cancels if running. /// void CancelIfRunning(); + + /// + /// Gets the default triggers. + /// + /// IEnumerable{BaseTaskTrigger}. + IEnumerable GetDefaultTriggers(); } } \ No newline at end of file diff --git a/MediaBrowser.Common/ScheduledTasks/ITaskManager.cs b/MediaBrowser.Common/ScheduledTasks/ITaskManager.cs index 430208869..42d7020e6 100644 --- a/MediaBrowser.Common/ScheduledTasks/ITaskManager.cs +++ b/MediaBrowser.Common/ScheduledTasks/ITaskManager.cs @@ -1,4 +1,5 @@ -using System; +using MediaBrowser.Model.Tasks; +using System; using System.Collections.Generic; namespace MediaBrowser.Common.ScheduledTasks @@ -41,6 +42,30 @@ namespace MediaBrowser.Common.ScheduledTasks /// Called when [task completed]. /// /// The task. - void OnTaskCompleted(IScheduledTask task); + /// The start time. + /// The end time. + /// The status. + void OnTaskCompleted(IScheduledTask task, DateTime startTime, DateTime endTime, TaskCompletionStatus status); + + /// + /// Gets the last execution result. + /// + /// The task. + /// TaskResult. + TaskResult GetLastExecutionResult(IScheduledTask task); + + /// + /// Loads the triggers. + /// + /// The task. + /// IEnumerable{BaseTaskTrigger}. + IEnumerable LoadTriggers(IScheduledTask task); + + /// + /// Saves the triggers. + /// + /// The task. + /// The triggers. + void SaveTriggers(IScheduledTask task, IEnumerable triggers); } } \ No newline at end of file diff --git a/MediaBrowser.Common/ScheduledTasks/ITaskTrigger.cs b/MediaBrowser.Common/ScheduledTasks/ITaskTrigger.cs new file mode 100644 index 000000000..66701650e --- /dev/null +++ b/MediaBrowser.Common/ScheduledTasks/ITaskTrigger.cs @@ -0,0 +1,26 @@ +using System; + +namespace MediaBrowser.Common.ScheduledTasks +{ + /// + /// Interface ITaskTrigger + /// + public interface ITaskTrigger + { + /// + /// Fires when the trigger condition is satisfied and the task should run + /// + event EventHandler Triggered; + + /// + /// Stars waiting for the trigger action + /// + /// if set to true [is application startup]. + void Start(bool isApplicationStartup); + + /// + /// Stops waiting for the trigger action + /// + void Stop(); + } +} \ No newline at end of file diff --git a/MediaBrowser.Common/ScheduledTasks/IntervalTrigger.cs b/MediaBrowser.Common/ScheduledTasks/IntervalTrigger.cs index 759447b10..cac6d1fa3 100644 --- a/MediaBrowser.Common/ScheduledTasks/IntervalTrigger.cs +++ b/MediaBrowser.Common/ScheduledTasks/IntervalTrigger.cs @@ -6,7 +6,7 @@ namespace MediaBrowser.Common.ScheduledTasks /// /// Represents a task trigger that runs repeatedly on an interval /// - public class IntervalTrigger : BaseTaskTrigger + public class IntervalTrigger : ITaskTrigger { /// /// Gets or sets the interval. @@ -24,7 +24,7 @@ namespace MediaBrowser.Common.ScheduledTasks /// Stars waiting for the trigger action /// /// if set to true [is application startup]. - protected internal override void Start(bool isApplicationStartup) + public void Start(bool isApplicationStartup) { DisposeTimer(); @@ -34,33 +34,35 @@ namespace MediaBrowser.Common.ScheduledTasks /// /// Stops waiting for the trigger action /// - protected internal override void Stop() + public void Stop() { DisposeTimer(); } /// - /// Disposes this instance. + /// Disposes the timer. /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected override void Dispose(bool dispose) + private void DisposeTimer() { - if (dispose) + if (Timer != null) { - DisposeTimer(); + Timer.Dispose(); } - - base.Dispose(dispose); } /// - /// Disposes the timer. + /// Occurs when [triggered]. /// - private void DisposeTimer() + public event EventHandler Triggered; + + /// + /// Called when [triggered]. + /// + private void OnTriggered() { - if (Timer != null) + if (Triggered != null) { - Timer.Dispose(); + Triggered(this, EventArgs.Empty); } } } diff --git a/MediaBrowser.Common/ScheduledTasks/ScheduledTaskHelpers.cs b/MediaBrowser.Common/ScheduledTasks/ScheduledTaskHelpers.cs index 9942da17f..e67eb3626 100644 --- a/MediaBrowser.Common/ScheduledTasks/ScheduledTaskHelpers.cs +++ b/MediaBrowser.Common/ScheduledTasks/ScheduledTaskHelpers.cs @@ -35,7 +35,7 @@ namespace MediaBrowser.Common.ScheduledTasks /// /// The trigger. /// TaskTriggerInfo. - public static TaskTriggerInfo GetTriggerInfo(BaseTaskTrigger trigger) + public static TaskTriggerInfo GetTriggerInfo(ITaskTrigger trigger) { var info = new TaskTriggerInfo { @@ -81,7 +81,7 @@ namespace MediaBrowser.Common.ScheduledTasks /// BaseTaskTrigger. /// /// Invalid trigger type: + info.Type - public static BaseTaskTrigger GetTrigger(TaskTriggerInfo info) + public static ITaskTrigger GetTrigger(TaskTriggerInfo info) { if (info.Type.Equals(typeof(DailyTrigger).Name, StringComparison.OrdinalIgnoreCase)) { diff --git a/MediaBrowser.Common/ScheduledTasks/StartupTrigger.cs b/MediaBrowser.Common/ScheduledTasks/StartupTrigger.cs index a254d2be9..e48551425 100644 --- a/MediaBrowser.Common/ScheduledTasks/StartupTrigger.cs +++ b/MediaBrowser.Common/ScheduledTasks/StartupTrigger.cs @@ -1,17 +1,18 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; namespace MediaBrowser.Common.ScheduledTasks { /// /// Class StartupTaskTrigger /// - public class StartupTrigger : BaseTaskTrigger + public class StartupTrigger : ITaskTrigger { /// /// Stars waiting for the trigger action /// /// if set to true [is application startup]. - protected internal async override void Start(bool isApplicationStartup) + public async void Start(bool isApplicationStartup) { if (isApplicationStartup) { @@ -24,8 +25,24 @@ namespace MediaBrowser.Common.ScheduledTasks /// /// Stops waiting for the trigger action /// - protected internal override void Stop() + public void Stop() { } + + /// + /// Occurs when [triggered]. + /// + public event EventHandler Triggered; + + /// + /// Called when [triggered]. + /// + private void OnTriggered() + { + if (Triggered != null) + { + Triggered(this, EventArgs.Empty); + } + } } } diff --git a/MediaBrowser.Common/ScheduledTasks/SystemEventTrigger.cs b/MediaBrowser.Common/ScheduledTasks/SystemEventTrigger.cs index 45d1fae8e..751da0ca5 100644 --- a/MediaBrowser.Common/ScheduledTasks/SystemEventTrigger.cs +++ b/MediaBrowser.Common/ScheduledTasks/SystemEventTrigger.cs @@ -1,5 +1,6 @@ using MediaBrowser.Model.Tasks; using Microsoft.Win32; +using System; using System.Threading.Tasks; namespace MediaBrowser.Common.ScheduledTasks @@ -7,7 +8,7 @@ namespace MediaBrowser.Common.ScheduledTasks /// /// Class SystemEventTrigger /// - public class SystemEventTrigger : BaseTaskTrigger + public class SystemEventTrigger : ITaskTrigger { /// /// Gets or sets the system event. @@ -19,7 +20,7 @@ namespace MediaBrowser.Common.ScheduledTasks /// Stars waiting for the trigger action /// /// if set to true [is application startup]. - protected internal override void Start(bool isApplicationStartup) + public void Start(bool isApplicationStartup) { switch (SystemEvent) { @@ -32,7 +33,7 @@ namespace MediaBrowser.Common.ScheduledTasks /// /// Stops waiting for the trigger action /// - protected internal override void Stop() + public void Stop() { SystemEvents.PowerModeChanged -= SystemEvents_PowerModeChanged; } @@ -52,5 +53,21 @@ namespace MediaBrowser.Common.ScheduledTasks OnTriggered(); } } + + /// + /// Occurs when [triggered]. + /// + public event EventHandler Triggered; + + /// + /// Called when [triggered]. + /// + private void OnTriggered() + { + if (Triggered != null) + { + Triggered(this, EventArgs.Empty); + } + } } } diff --git a/MediaBrowser.Common/ScheduledTasks/TaskManager.cs b/MediaBrowser.Common/ScheduledTasks/TaskManager.cs deleted file mode 100644 index 946c42d2e..000000000 --- a/MediaBrowser.Common/ScheduledTasks/TaskManager.cs +++ /dev/null @@ -1,159 +0,0 @@ -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Tasks; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace MediaBrowser.Common.ScheduledTasks -{ - /// - /// Class TaskManager - /// - internal class TaskManager : ITaskManager - { - /// - /// Gets the list of Scheduled Tasks - /// - /// The scheduled tasks. - public IScheduledTask[] ScheduledTasks { get; private set; } - - /// - /// The _task queue - /// - private readonly List _taskQueue = new List(); - - /// - /// The _logger - /// - private readonly ILogger _logger; - - /// - /// Initializes a new instance of the class. - /// - /// The logger. - public TaskManager(ILogger logger) - { - if (logger == null) - { - throw new ArgumentException("logger"); - } - - _logger = logger; - - ScheduledTasks = new IScheduledTask[] {}; - } - - /// - /// Cancels if running and queue. - /// - /// - public void CancelIfRunningAndQueue() - where T : IScheduledTask - { - ScheduledTasks.OfType().First().CancelIfRunning(); - QueueScheduledTask(); - } - - /// - /// Queues the scheduled task. - /// - /// - public void QueueScheduledTask() - where T : IScheduledTask - { - var scheduledTask = ScheduledTasks.OfType().First(); - - QueueScheduledTask(scheduledTask); - } - - /// - /// Queues the scheduled task. - /// - /// The task. - public void QueueScheduledTask(IScheduledTask task) - { - var type = task.GetType(); - - var scheduledTask = ScheduledTasks.First(t => t.GetType() == type); - - lock (_taskQueue) - { - // If it's idle just execute immediately - if (scheduledTask.State == TaskState.Idle) - { - scheduledTask.Execute(); - return; - } - - if (!_taskQueue.Contains(type)) - { - _logger.Info("Queueing task {0}", type.Name); - _taskQueue.Add(type); - } - else - { - _logger.Info("Task already queued: {0}", type.Name); - } - } - } - - /// - /// Called when [task completed]. - /// - /// The task. - public void OnTaskCompleted(IScheduledTask task) - { - // Execute queued tasks - lock (_taskQueue) - { - var copy = _taskQueue.ToList(); - - foreach (var type in copy) - { - var scheduledTask = ScheduledTasks.First(t => t.GetType() == type); - - if (scheduledTask.State == TaskState.Idle) - { - scheduledTask.Execute(); - - _taskQueue.Remove(type); - } - } - } - } - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Releases unmanaged and - optionally - managed resources. - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected virtual void Dispose(bool dispose) - { - foreach (var task in ScheduledTasks) - { - task.Dispose(); - } - } - - /// - /// Adds the tasks. - /// - /// The tasks. - public void AddTasks(IEnumerable tasks) - { - var myTasks = ScheduledTasks.ToList(); - - myTasks.AddRange(tasks); - - ScheduledTasks = myTasks.ToArray(); - } - } -} diff --git a/MediaBrowser.Common/ScheduledTasks/Tasks/DeleteCacheFileTask.cs b/MediaBrowser.Common/ScheduledTasks/Tasks/DeleteCacheFileTask.cs deleted file mode 100644 index b06134ee2..000000000 --- a/MediaBrowser.Common/ScheduledTasks/Tasks/DeleteCacheFileTask.cs +++ /dev/null @@ -1,118 +0,0 @@ -using MediaBrowser.Common.Kernel; -using MediaBrowser.Model.Logging; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Common.ScheduledTasks.Tasks -{ - /// - /// Deletes old cache files - /// - public class DeleteCacheFileTask : BaseScheduledTask - { - /// - /// Initializes a new instance of the class. - /// - /// The kernel. - /// The task manager. - /// The logger. - public DeleteCacheFileTask(IKernel kernel, ITaskManager taskManager, ILogger logger) - : base(kernel, taskManager, logger) - { - } - - /// - /// Creates the triggers that define when the task will run - /// - /// IEnumerable{BaseTaskTrigger}. - protected override IEnumerable GetDefaultTriggers() - { - var trigger = new DailyTrigger { TimeOfDay = TimeSpan.FromHours(2) }; //2am - - return new[] { trigger }; - } - - /// - /// Returns the task to be executed - /// - /// The cancellation token. - /// The progress. - /// Task. - protected override Task ExecuteInternal(CancellationToken cancellationToken, IProgress progress) - { - return Task.Run(() => - { - var minDateModified = DateTime.UtcNow.AddMonths(-2); - - DeleteCacheFilesFromDirectory(cancellationToken, Kernel.ApplicationPaths.CachePath, minDateModified, progress); - }); - } - - - /// - /// Deletes the cache files from directory with a last write time less than a given date - /// - /// The task cancellation token. - /// The directory. - /// The min date modified. - /// The progress. - private void DeleteCacheFilesFromDirectory(CancellationToken cancellationToken, string directory, DateTime minDateModified, IProgress progress) - { - var filesToDelete = new DirectoryInfo(directory).EnumerateFileSystemInfos("*", SearchOption.AllDirectories) - .Where(f => !f.Attributes.HasFlag(FileAttributes.Directory) && f.LastWriteTimeUtc < minDateModified) - .ToList(); - - var index = 0; - - foreach (var file in filesToDelete) - { - double percent = index; - percent /= filesToDelete.Count; - - progress.Report(100 * percent); - - cancellationToken.ThrowIfCancellationRequested(); - - File.Delete(file.FullName); - - index++; - } - - progress.Report(100); - } - - /// - /// Gets the name of the task - /// - /// The name. - public override string Name - { - get { return "Cache file cleanup"; } - } - - /// - /// Gets the description. - /// - /// The description. - public override string Description - { - get { return "Deletes cache files no longer needed by the system"; } - } - - /// - /// Gets the category. - /// - /// The category. - public override string Category - { - get - { - return "Maintenance"; - } - } - } -} diff --git a/MediaBrowser.Common/ScheduledTasks/Tasks/DeleteLogFileTask.cs b/MediaBrowser.Common/ScheduledTasks/Tasks/DeleteLogFileTask.cs deleted file mode 100644 index 0b243cb10..000000000 --- a/MediaBrowser.Common/ScheduledTasks/Tasks/DeleteLogFileTask.cs +++ /dev/null @@ -1,106 +0,0 @@ -using MediaBrowser.Common.Kernel; -using MediaBrowser.Model.Logging; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Common.ScheduledTasks.Tasks -{ - /// - /// Deletes old log files - /// - public class DeleteLogFileTask : BaseScheduledTask - { - /// - /// Initializes a new instance of the class. - /// - /// The kernel. - /// The task manager. - /// The logger. - public DeleteLogFileTask(IKernel kernel, ITaskManager taskManager, ILogger logger) - : base(kernel, taskManager, logger) - { - } - - /// - /// Creates the triggers that define when the task will run - /// - /// IEnumerable{BaseTaskTrigger}. - protected override IEnumerable GetDefaultTriggers() - { - var trigger = new DailyTrigger { TimeOfDay = TimeSpan.FromHours(2) }; //2am - - return new[] { trigger }; - } - - /// - /// Returns the task to be executed - /// - /// The cancellation token. - /// The progress. - /// Task. - protected override Task ExecuteInternal(CancellationToken cancellationToken, IProgress progress) - { - return Task.Run(() => - { - // Delete log files more than n days old - var minDateModified = DateTime.UtcNow.AddDays(-(Kernel.Configuration.LogFileRetentionDays)); - - var filesToDelete = new DirectoryInfo(Kernel.ApplicationPaths.LogDirectoryPath).EnumerateFileSystemInfos("*", SearchOption.AllDirectories) - .Where(f => f.LastWriteTimeUtc < minDateModified) - .ToList(); - - var index = 0; - - foreach (var file in filesToDelete) - { - double percent = index; - percent /= filesToDelete.Count; - - progress.Report(100 * percent); - - cancellationToken.ThrowIfCancellationRequested(); - - File.Delete(file.FullName); - - index++; - } - - progress.Report(100); - }); - } - - /// - /// Gets the name of the task - /// - /// The name. - public override string Name - { - get { return "Log file cleanup"; } - } - - /// - /// Gets the description. - /// - /// The description. - public override string Description - { - get { return string.Format("Deletes log files that are more than {0} days old.", Kernel.Configuration.LogFileRetentionDays); } - } - - /// - /// Gets the category. - /// - /// The category. - public override string Category - { - get - { - return "Maintenance"; - } - } - } -} diff --git a/MediaBrowser.Common/ScheduledTasks/Tasks/ReloadLoggerTask.cs b/MediaBrowser.Common/ScheduledTasks/Tasks/ReloadLoggerTask.cs deleted file mode 100644 index 35cbe98f1..000000000 --- a/MediaBrowser.Common/ScheduledTasks/Tasks/ReloadLoggerTask.cs +++ /dev/null @@ -1,70 +0,0 @@ -using MediaBrowser.Common.Kernel; -using MediaBrowser.Model.Logging; -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Common.ScheduledTasks.Tasks -{ - /// - /// Class ReloadLoggerFileTask - /// - public class ReloadLoggerFileTask : BaseScheduledTask - { - /// - /// Initializes a new instance of the class. - /// - /// The kernel. - /// The task manager. - /// The logger. - public ReloadLoggerFileTask(IKernel kernel, ITaskManager taskManager, ILogger logger) - : base(kernel, taskManager, logger) - { - } - - /// - /// Gets the default triggers. - /// - /// IEnumerable{BaseTaskTrigger}. - protected override IEnumerable GetDefaultTriggers() - { - var trigger = new DailyTrigger { TimeOfDay = TimeSpan.FromHours(0) }; //12am - - return new[] { trigger }; - } - - /// - /// Executes the internal. - /// - /// The cancellation token. - /// The progress. - /// Task. - protected override Task ExecuteInternal(CancellationToken cancellationToken, IProgress progress) - { - cancellationToken.ThrowIfCancellationRequested(); - - progress.Report(0); - - return Task.Run(() => Kernel.ReloadLogger()); - } - - /// - /// Gets the name. - /// - /// The name. - public override string Name - { - get { return "Start new log file"; } - } - - /// - /// Gets the description. - /// - /// The description. - public override string Description - { - get { return "Moves logging to a new file to help reduce log file sizes."; } - } - } -} diff --git a/MediaBrowser.Common/ScheduledTasks/Tasks/SystemUpdateTask.cs b/MediaBrowser.Common/ScheduledTasks/Tasks/SystemUpdateTask.cs deleted file mode 100644 index f02293a5e..000000000 --- a/MediaBrowser.Common/ScheduledTasks/Tasks/SystemUpdateTask.cs +++ /dev/null @@ -1,121 +0,0 @@ -using MediaBrowser.Common.Kernel; -using MediaBrowser.Model.Logging; -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Common.ScheduledTasks.Tasks -{ - /// - /// Plugin Update Task - /// - public class SystemUpdateTask : BaseScheduledTask - { - /// - /// The _app host - /// - private readonly IApplicationHost _appHost; - - /// - /// Initializes a new instance of the class. - /// - /// The app host. - /// The task manager. - /// The kernel. - /// The logger. - public SystemUpdateTask(IApplicationHost appHost, ITaskManager taskManager, IKernel kernel, ILogger logger) - : base(kernel, taskManager, logger) - { - _appHost = appHost; - } - - /// - /// Creates the triggers that define when the task will run - /// - /// IEnumerable{BaseTaskTrigger}. - protected override IEnumerable GetDefaultTriggers() - { - return new BaseTaskTrigger[] { - - // 1am - new DailyTrigger { TimeOfDay = TimeSpan.FromHours(1) }, - - new IntervalTrigger { Interval = TimeSpan.FromHours(2)} - }; - } - - /// - /// Returns the task to be executed - /// - /// The cancellation token. - /// The progress. - /// Task. - protected override async Task ExecuteInternal(CancellationToken cancellationToken, IProgress progress) - { - if (!_appHost.CanSelfUpdate) return; - - EventHandler innerProgressHandler = (sender, e) => progress.Report(e * .1); - - // Create a progress object for the update check - var innerProgress = new Progress(); - innerProgress.ProgressChanged += innerProgressHandler; - - var updateInfo = await _appHost.CheckForApplicationUpdate(cancellationToken, innerProgress).ConfigureAwait(false); - - // Release the event handler - innerProgress.ProgressChanged -= innerProgressHandler; - - progress.Report(10); - - if (!updateInfo.IsUpdateAvailable) - { - progress.Report(100); - return; - } - - cancellationToken.ThrowIfCancellationRequested(); - - if (Kernel.Configuration.EnableAutoUpdate) - { - Logger.Info("Update Revision {0} available. Updating...", updateInfo.AvailableVersion); - - innerProgressHandler = (sender, e) => progress.Report((e * .9) + .1); - - innerProgress = new Progress(); - innerProgress.ProgressChanged += innerProgressHandler; - - await _appHost.UpdateApplication(cancellationToken, innerProgress).ConfigureAwait(false); - - // Release the event handler - innerProgress.ProgressChanged -= innerProgressHandler; - - Kernel.OnApplicationUpdated(updateInfo.AvailableVersion); - } - else - { - Logger.Info("A new version of Media Browser is available."); - } - - progress.Report(100); - } - - /// - /// Gets the name of the task - /// - /// The name. - public override string Name - { - get { return "Check for application updates"; } - } - - /// - /// Gets the description. - /// - /// The description. - public override string Description - { - get { return "Downloads and installs application updates."; } - } - } -} diff --git a/MediaBrowser.Common/ScheduledTasks/WeeklyTrigger.cs b/MediaBrowser.Common/ScheduledTasks/WeeklyTrigger.cs index afeacc2b3..cfb3f1fab 100644 --- a/MediaBrowser.Common/ScheduledTasks/WeeklyTrigger.cs +++ b/MediaBrowser.Common/ScheduledTasks/WeeklyTrigger.cs @@ -6,7 +6,7 @@ namespace MediaBrowser.Common.ScheduledTasks /// /// Represents a task trigger that fires on a weekly basis /// - public class WeeklyTrigger : BaseTaskTrigger + public class WeeklyTrigger : ITaskTrigger { /// /// Get the time of day to trigger the task to run @@ -30,7 +30,7 @@ namespace MediaBrowser.Common.ScheduledTasks /// Stars waiting for the trigger action /// /// if set to true [is application startup]. - protected internal override void Start(bool isApplicationStartup) + public void Start(bool isApplicationStartup) { DisposeTimer(); @@ -69,33 +69,35 @@ namespace MediaBrowser.Common.ScheduledTasks /// /// Stops waiting for the trigger action /// - protected internal override void Stop() + public void Stop() { DisposeTimer(); } /// - /// Disposes this instance. + /// Disposes the timer. /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected override void Dispose(bool dispose) + private void DisposeTimer() { - if (dispose) + if (Timer != null) { - DisposeTimer(); + Timer.Dispose(); } - - base.Dispose(dispose); } /// - /// Disposes the timer. + /// Occurs when [triggered]. /// - private void DisposeTimer() + public event EventHandler Triggered; + + /// + /// Called when [triggered]. + /// + private void OnTriggered() { - if (Timer != null) + if (Triggered != null) { - Timer.Dispose(); + Triggered(this, EventArgs.Empty); } } } diff --git a/MediaBrowser.Common/Serialization/DynamicProtobufSerializer.cs b/MediaBrowser.Common/Serialization/DynamicProtobufSerializer.cs deleted file mode 100644 index 359cf9da0..000000000 --- a/MediaBrowser.Common/Serialization/DynamicProtobufSerializer.cs +++ /dev/null @@ -1,157 +0,0 @@ -using ProtoBuf; -using ProtoBuf.Meta; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; - -namespace MediaBrowser.Common.Serialization -{ - /// - /// Creates a compiled protobuf serializer based on a set of assemblies - /// - public class DynamicProtobufSerializer - { - /// - /// Gets or sets the type model. - /// - /// The type model. - public TypeModel TypeModel { get; set; } - - /// - /// Serializes to stream. - /// - /// The obj. - /// The stream. - /// obj - public void SerializeToStream(object obj, Stream stream) - { - if (obj == null) - { - throw new ArgumentNullException("obj"); - } - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - TypeModel.Serialize(stream, obj); - } - - /// - /// Deserializes from stream. - /// - /// The stream. - /// The type. - /// System.Object. - /// stream - public object DeserializeFromStream(Stream stream, Type type) - { - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - return TypeModel.Deserialize(stream, null, type); - } - - /// - /// Deserializes from stream. - /// - /// - /// The stream. - /// ``0. - public T DeserializeFromStream(Stream stream) - where T : class - { - return DeserializeFromStream(stream, typeof(T)) as T; - } - - /// - /// Serializes to file. - /// - /// - /// The obj. - /// The file. - /// file - public void SerializeToFile(T obj, string file) - { - if (string.IsNullOrEmpty(file)) - { - throw new ArgumentNullException("file"); - } - - using (Stream stream = File.Open(file, FileMode.Create)) - { - SerializeToStream(obj, stream); - } - } - - /// - /// Deserializes from file. - /// - /// - /// The file. - /// ``0. - /// file - public T DeserializeFromFile(string file) - where T : class - { - if (string.IsNullOrEmpty(file)) - { - throw new ArgumentNullException("file"); - } - - using (Stream stream = File.OpenRead(file)) - { - return DeserializeFromStream(stream); - } - } - - /// - /// Serializes to bytes. - /// - /// - /// The obj. - /// System.Byte[][]. - /// obj - public byte[] SerializeToBytes(T obj) - where T : class - { - if (obj == null) - { - throw new ArgumentNullException("obj"); - } - - using (var stream = new MemoryStream()) - { - SerializeToStream(obj, stream); - return stream.ToArray(); - } - } - - /// - /// Creates the specified assemblies. - /// - /// DynamicProtobufSerializer. - /// assemblies - public static DynamicProtobufSerializer Create(IEnumerable types) - { - if (types == null) - { - throw new ArgumentNullException("types"); - } - - var model = TypeModel.Create(); - var attributeType = typeof(ProtoContractAttribute); - - // Find all ProtoContracts in the current assembly - foreach (var type in types.Where(t => Attribute.IsDefined(t, attributeType))) - { - model.Add(type, true); - } - - return new DynamicProtobufSerializer { TypeModel = model.Compile() }; - } - } -} diff --git a/MediaBrowser.Common/Serialization/JsonSerializer.cs b/MediaBrowser.Common/Serialization/JsonSerializer.cs deleted file mode 100644 index 5b6e354a8..000000000 --- a/MediaBrowser.Common/Serialization/JsonSerializer.cs +++ /dev/null @@ -1,259 +0,0 @@ -using System; -using System.IO; - -namespace MediaBrowser.Common.Serialization -{ - /// - /// Provides a wrapper around third party json serialization. - /// - public class JsonSerializer - { - /// - /// Serializes to stream. - /// - /// - /// The obj. - /// The stream. - /// obj - public static void SerializeToStream(T obj, Stream stream) - where T : class - { - if (obj == null) - { - throw new ArgumentNullException("obj"); - } - - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - Configure(); - - ServiceStack.Text.JsonSerializer.SerializeToStream(obj, obj.GetType(), stream); - } - - /// - /// Serializes to file. - /// - /// - /// The obj. - /// The file. - /// obj - public static void SerializeToFile(T obj, string file) - where T : class - { - if (obj == null) - { - throw new ArgumentNullException("obj"); - } - - if (string.IsNullOrEmpty(file)) - { - throw new ArgumentNullException("file"); - } - - Configure(); - - using (Stream stream = File.Open(file, FileMode.Create)) - { - SerializeToStream(obj, stream); - } - } - - /// - /// Deserializes from file. - /// - /// The type. - /// The file. - /// System.Object. - /// type - public static object DeserializeFromFile(Type type, string file) - { - if (type == null) - { - throw new ArgumentNullException("type"); - } - - if (string.IsNullOrEmpty(file)) - { - throw new ArgumentNullException("file"); - } - - Configure(); - - using (Stream stream = File.OpenRead(file)) - { - return ServiceStack.Text.JsonSerializer.DeserializeFromStream(type, stream); - } - } - - /// - /// Deserializes from file. - /// - /// - /// The file. - /// ``0. - /// file - public static T DeserializeFromFile(string file) - where T : class - { - if (string.IsNullOrEmpty(file)) - { - throw new ArgumentNullException("file"); - } - - Configure(); - - using (Stream stream = File.OpenRead(file)) - { - return ServiceStack.Text.JsonSerializer.DeserializeFromStream(stream); - } - } - - /// - /// Deserializes from stream. - /// - /// - /// The stream. - /// ``0. - /// stream - public static T DeserializeFromStream(Stream stream) - { - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - Configure(); - - return ServiceStack.Text.JsonSerializer.DeserializeFromStream(stream); - } - - /// - /// Deserializes from string. - /// - /// - /// The text. - /// ``0. - /// text - public static T DeserializeFromString(string text) - { - if (string.IsNullOrEmpty(text)) - { - throw new ArgumentNullException("text"); - } - - Configure(); - - return ServiceStack.Text.JsonSerializer.DeserializeFromString(text); - } - - /// - /// Deserializes from stream. - /// - /// The stream. - /// The type. - /// System.Object. - /// stream - public static object DeserializeFromStream(Stream stream, Type type) - { - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - if (type == null) - { - throw new ArgumentNullException("type"); - } - - Configure(); - - return ServiceStack.Text.JsonSerializer.DeserializeFromStream(type, stream); - } - - /// - /// The _is configured - /// - private static bool _isConfigured; - /// - /// Configures this instance. - /// - internal static void Configure() - { - if (!_isConfigured) - { - ServiceStack.Text.JsConfig.DateHandler = ServiceStack.Text.JsonDateHandler.ISO8601; - ServiceStack.Text.JsConfig.ExcludeTypeInfo = true; - ServiceStack.Text.JsConfig.IncludeNullValues = false; - _isConfigured = true; - } - } - - /// - /// Deserializes from string. - /// - /// The json. - /// The type. - /// System.Object. - /// json - public static object DeserializeFromString(string json, Type type) - { - if (string.IsNullOrEmpty(json)) - { - throw new ArgumentNullException("json"); - } - - if (type == null) - { - throw new ArgumentNullException("type"); - } - - Configure(); - - return ServiceStack.Text.JsonSerializer.DeserializeFromString(json, type); - } - - /// - /// Serializes to string. - /// - /// - /// The obj. - /// System.String. - /// obj - public static string SerializeToString(T obj) - where T : class - { - if (obj == null) - { - throw new ArgumentNullException("obj"); - } - - Configure(); - return ServiceStack.Text.JsonSerializer.SerializeToString(obj, obj.GetType()); - } - - /// - /// Serializes to bytes. - /// - /// - /// The obj. - /// System.Byte[][]. - /// obj - public static byte[] SerializeToBytes(T obj) - where T : class - { - if (obj == null) - { - throw new ArgumentNullException("obj"); - } - - using (var stream = new MemoryStream()) - { - SerializeToStream(obj, stream); - return stream.ToArray(); - } - } - } -} diff --git a/MediaBrowser.Common/Serialization/XmlSerializer.cs b/MediaBrowser.Common/Serialization/XmlSerializer.cs deleted file mode 100644 index 07010a5d2..000000000 --- a/MediaBrowser.Common/Serialization/XmlSerializer.cs +++ /dev/null @@ -1,212 +0,0 @@ -using MediaBrowser.Model.Logging; -using System; -using System.IO; -using System.Linq; -using System.Xml; - -namespace MediaBrowser.Common.Serialization -{ - /// - /// Provides a wrapper around third party xml serialization. - /// - public class XmlSerializer - { - /// - /// Serializes to writer. - /// - /// - /// The obj. - /// The writer. - public static void SerializeToWriter(T obj, XmlTextWriter writer) - { - writer.Formatting = Formatting.Indented; - var netSerializer = new System.Xml.Serialization.XmlSerializer(typeof(T)); - netSerializer.Serialize(writer, obj); - } - - /// - /// Serializes to writer. - /// - /// The obj. - /// The writer. - public static void SerializeToWriter(object obj, XmlTextWriter writer) - { - writer.Formatting = Formatting.Indented; - var netSerializer = new System.Xml.Serialization.XmlSerializer(obj.GetType()); - netSerializer.Serialize(writer, obj); - } - - /// - /// Deserializes from stream. - /// - /// - /// The stream. - /// ``0. - public static T DeserializeFromStream(Stream stream) - { - using (var reader = new XmlTextReader(stream)) - { - var netSerializer = new System.Xml.Serialization.XmlSerializer(typeof(T)); - - return (T)netSerializer.Deserialize(reader); - } - } - - /// - /// Deserializes from stream. - /// - /// The type. - /// The stream. - /// System.Object. - public static object DeserializeFromStream(Type type, Stream stream) - { - using (var reader = new XmlTextReader(stream)) - { - var netSerializer = new System.Xml.Serialization.XmlSerializer(type); - - return netSerializer.Deserialize(reader); - } - } - - /// - /// Serializes to stream. - /// - /// The obj. - /// The stream. - public static void SerializeToStream(object obj, Stream stream) - { - using (var writer = new XmlTextWriter(stream, null)) - { - SerializeToWriter(obj, writer); - } - } - - /// - /// Deserializes from file. - /// - /// - /// The file. - /// ``0. - public static T DeserializeFromFile(string file) - { - using (var stream = File.OpenRead(file)) - { - return DeserializeFromStream(stream); - } - } - - /// - /// Serializes to file. - /// - /// The obj. - /// The file. - public static void SerializeToFile(object obj, string file) - { - using (var stream = new FileStream(file, FileMode.Create)) - { - SerializeToStream(obj, stream); - } - } - - /// - /// Deserializes from file. - /// - /// The type. - /// The file. - /// System.Object. - public static object DeserializeFromFile(Type type, string file) - { - using (var stream = File.OpenRead(file)) - { - return DeserializeFromStream(type, stream); - } - } - - /// - /// Deserializes from bytes. - /// - /// The type. - /// The buffer. - /// System.Object. - public static object DeserializeFromBytes(Type type, byte[] buffer) - { - using (var stream = new MemoryStream(buffer)) - { - return DeserializeFromStream(type, stream); - } - } - - /// - /// Serializes to bytes. - /// - /// The obj. - /// System.Byte[][]. - public static byte[] SerializeToBytes(object obj) - { - using (var stream = new MemoryStream()) - { - SerializeToStream(obj, stream); - - return stream.ToArray(); - } - } - - /// - /// Reads an xml configuration file from the file system - /// It will immediately re-serialize and save if new serialization data is available due to property changes - /// - /// The type. - /// The path. - /// The logger. - /// System.Object. - public static object GetXmlConfiguration(Type type, string path, ILogger logger) - { - logger.Info("Loading {0} at {1}", type.Name, path); - - object configuration; - - byte[] buffer = null; - - // Use try/catch to avoid the extra file system lookup using File.Exists - try - { - buffer = File.ReadAllBytes(path); - - configuration = DeserializeFromBytes(type, buffer); - } - catch (FileNotFoundException) - { - configuration = Activator.CreateInstance(type); - } - - // Take the object we just got and serialize it back to bytes - var newBytes = SerializeToBytes(configuration); - - // If the file didn't exist before, or if something has changed, re-save - if (buffer == null || !buffer.SequenceEqual(newBytes)) - { - logger.Info("Saving {0} to {1}", type.Name, path); - - // Save it after load in case we got new items - File.WriteAllBytes(path, newBytes); - } - - return configuration; - } - - /// - /// Reads an xml configuration file from the file system - /// It will immediately save the configuration after loading it, just - /// in case there are new serializable properties - /// - /// - /// The path. - /// The logger. - /// ``0. - public static T GetXmlConfiguration(string path, ILogger logger) - where T : class - { - return GetXmlConfiguration(typeof(T), path, logger) as T; - } - } -} diff --git a/MediaBrowser.Common/packages.config b/MediaBrowser.Common/packages.config index 0ebaf6297..ea25110aa 100644 --- a/MediaBrowser.Common/packages.config +++ b/MediaBrowser.Common/packages.config @@ -1,6 +1,5 @@  - diff --git a/MediaBrowser.Controller/Drawing/ImageManager.cs b/MediaBrowser.Controller/Drawing/ImageManager.cs index a0ba9d550..766d56115 100644 --- a/MediaBrowser.Controller/Drawing/ImageManager.cs +++ b/MediaBrowser.Controller/Drawing/ImageManager.cs @@ -1,12 +1,12 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Common.IO; -using MediaBrowser.Common.Kernel; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Drawing; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Serialization; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -22,7 +22,7 @@ namespace MediaBrowser.Controller.Drawing /// /// Class ImageManager /// - public class ImageManager : BaseManager + public class ImageManager : IDisposable { /// /// Gets the image size cache. @@ -57,20 +57,32 @@ namespace MediaBrowser.Controller.Drawing /// private readonly ILogger _logger; + /// + /// The _protobuf serializer + /// + private readonly IProtobufSerializer _protobufSerializer; + + /// + /// The _kernel + /// + private readonly Kernel _kernel; + /// /// Initializes a new instance of the class. /// /// The kernel. + /// The protobuf serializer. /// The logger. - public ImageManager(Kernel kernel, ILogger logger) - : base(kernel) + public ImageManager(Kernel kernel, IProtobufSerializer protobufSerializer, ILogger logger) { + _protobufSerializer = protobufSerializer; _logger = logger; + _kernel = kernel; - ImageSizeCache = new FileSystemRepository(Path.Combine(Kernel.ApplicationPaths.ImageCachePath, "image-sizes")); - ResizedImageCache = new FileSystemRepository(Path.Combine(Kernel.ApplicationPaths.ImageCachePath, "resized-images")); - CroppedImageCache = new FileSystemRepository(Path.Combine(Kernel.ApplicationPaths.ImageCachePath, "cropped-images")); - EnhancedImageCache = new FileSystemRepository(Path.Combine(Kernel.ApplicationPaths.ImageCachePath, "enhanced-images")); + ImageSizeCache = new FileSystemRepository(Path.Combine(_kernel.ApplicationPaths.ImageCachePath, "image-sizes")); + ResizedImageCache = new FileSystemRepository(Path.Combine(_kernel.ApplicationPaths.ImageCachePath, "resized-images")); + CroppedImageCache = new FileSystemRepository(Path.Combine(_kernel.ApplicationPaths.ImageCachePath, "cropped-images")); + EnhancedImageCache = new FileSystemRepository(Path.Combine(_kernel.ApplicationPaths.ImageCachePath, "enhanced-images")); } /// @@ -276,7 +288,7 @@ namespace MediaBrowser.Controller.Drawing try { - var result = Kernel.ProtobufSerializer.DeserializeFromFile(fullCachePath); + var result = _protobufSerializer.DeserializeFromFile(fullCachePath); return new ImageSize { Width = result[0], Height = result[1] }; } @@ -305,7 +317,7 @@ namespace MediaBrowser.Controller.Drawing { var output = new[] { width, height }; - Kernel.ProtobufSerializer.SerializeToFile(output, cachePath); + _protobufSerializer.SerializeToFile(output, cachePath); } /// @@ -472,7 +484,7 @@ namespace MediaBrowser.Controller.Drawing throw new ArgumentNullException("item"); } - var supportedEnhancers = Kernel.ImageEnhancers.Where(i => i.Supports(item, imageType)).ToList(); + var supportedEnhancers = _kernel.ImageEnhancers.Where(i => i.Supports(item, imageType)).ToList(); // No enhancement - don't cache if (supportedEnhancers.Count == 0) @@ -526,7 +538,7 @@ namespace MediaBrowser.Controller.Drawing var dateModified = GetImageDateModified(item, imagePath); - var supportedEnhancers = Kernel.ImageEnhancers.Where(i => i.Supports(item, imageType)); + var supportedEnhancers = _kernel.ImageEnhancers.Where(i => i.Supports(item, imageType)); return GetImageCacheTag(imagePath, dateModified, supportedEnhancers, item, imageType); } @@ -600,11 +612,16 @@ namespace MediaBrowser.Controller.Drawing return result; } + public void Dispose() + { + Dispose(true); + } + /// /// Releases unmanaged and - optionally - managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected override void Dispose(bool dispose) + protected void Dispose(bool dispose) { if (dispose) { @@ -613,8 +630,6 @@ namespace MediaBrowser.Controller.Drawing CroppedImageCache.Dispose(); EnhancedImageCache.Dispose(); } - - base.Dispose(dispose); } } } diff --git a/MediaBrowser.Controller/Entities/User.cs b/MediaBrowser.Controller/Entities/User.cs index 426e7f18e..5abd3e5a8 100644 --- a/MediaBrowser.Controller/Entities/User.cs +++ b/MediaBrowser.Controller/Entities/User.cs @@ -1,15 +1,13 @@ using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.IO; -using MediaBrowser.Common.Serialization; using MediaBrowser.Controller.IO; using MediaBrowser.Model.Configuration; -using MediaBrowser.Model.Tasks; using System; using System.IO; using System.Linq; using System.Runtime.Serialization; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Model.Serialization; namespace MediaBrowser.Controller.Entities { @@ -170,7 +168,7 @@ namespace MediaBrowser.Controller.Entities get { // Lazy load - LazyInitializer.EnsureInitialized(ref _configuration, ref _configurationInitialized, ref _configurationSyncLock, () => XmlSerializer.GetXmlConfiguration(ConfigurationFilePath, Logger)); + LazyInitializer.EnsureInitialized(ref _configuration, ref _configurationInitialized, ref _configurationSyncLock, () => (UserConfiguration)Kernel.Instance.GetXmlConfiguration(typeof(UserConfiguration), ConfigurationFilePath)); return _configuration; } private set @@ -338,9 +336,9 @@ namespace MediaBrowser.Controller.Entities /// /// Saves the current configuration to the file system /// - public void SaveConfiguration() + public void SaveConfiguration(IXmlSerializer serializer) { - XmlSerializer.SerializeToFile(Configuration, ConfigurationFilePath); + serializer.SerializeToFile(Configuration, ConfigurationFilePath); } /// @@ -376,8 +374,9 @@ namespace MediaBrowser.Controller.Entities /// Updates the configuration. /// /// The config. + /// The serializer. /// config - public void UpdateConfiguration(UserConfiguration config) + public void UpdateConfiguration(UserConfiguration config, IXmlSerializer serializer) { if (config == null) { @@ -387,7 +386,7 @@ namespace MediaBrowser.Controller.Entities var customLibraryChanged = config.UseCustomLibrary != Configuration.UseCustomLibrary; Configuration = config; - SaveConfiguration(); + SaveConfiguration(serializer); // Force these to be lazy loaded again if (customLibraryChanged) diff --git a/MediaBrowser.Controller/IServerApplicationPaths.cs b/MediaBrowser.Controller/IServerApplicationPaths.cs new file mode 100644 index 000000000..b5fcdef28 --- /dev/null +++ b/MediaBrowser.Controller/IServerApplicationPaths.cs @@ -0,0 +1,85 @@ +using MediaBrowser.Common.Kernel; + +namespace MediaBrowser.Controller +{ + public interface IServerApplicationPaths : IApplicationPaths + { + /// + /// Gets the path to the base root media directory + /// + /// The root folder path. + string RootFolderPath { get; } + + /// + /// Gets the path to the default user view directory. Used if no specific user view is defined. + /// + /// The default user views path. + string DefaultUserViewsPath { get; } + + /// + /// Gets the path to localization data. + /// + /// The localization path. + string LocalizationPath { get; } + + /// + /// Gets the path to the Images By Name directory + /// + /// The images by name path. + string ImagesByNamePath { get; } + + /// + /// Gets the path to the People directory + /// + /// The people path. + string PeoplePath { get; } + + /// + /// Gets the path to the Genre directory + /// + /// The genre path. + string GenrePath { get; } + + /// + /// Gets the path to the Studio directory + /// + /// The studio path. + string StudioPath { get; } + + /// + /// Gets the path to the Year directory + /// + /// The year path. + string YearPath { get; } + + /// + /// Gets the path to the General IBN directory + /// + /// The general path. + string GeneralPath { get; } + + /// + /// Gets the path to the Ratings IBN directory + /// + /// The ratings path. + string RatingsPath { get; } + + /// + /// Gets the path to the user configuration directory + /// + /// The user configuration directory path. + string UserConfigurationDirectoryPath { get; } + + /// + /// Gets the FF MPEG stream cache path. + /// + /// The FF MPEG stream cache path. + string FFMpegStreamCachePath { get; } + + /// + /// Gets the folder path to tools + /// + /// The media tools path. + string MediaToolsPath { get; } + } +} \ No newline at end of file diff --git a/MediaBrowser.Controller/Kernel.cs b/MediaBrowser.Controller/Kernel.cs index 5ee590bde..d879b888b 100644 --- a/MediaBrowser.Controller/Kernel.cs +++ b/MediaBrowser.Controller/Kernel.cs @@ -1,5 +1,6 @@ using MediaBrowser.Common.Kernel; using MediaBrowser.Common.Plugins; +using MediaBrowser.Common.ScheduledTasks; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.IO; @@ -16,6 +17,7 @@ using MediaBrowser.Controller.Updates; using MediaBrowser.Controller.Weather; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Serialization; using MediaBrowser.Model.System; using System; using System.Collections.Generic; @@ -28,7 +30,7 @@ namespace MediaBrowser.Controller /// /// Class Kernel /// - public class Kernel : BaseKernel + public class Kernel : BaseKernel { /// /// The MB admin URL @@ -291,17 +293,24 @@ namespace MediaBrowser.Controller get { return 7359; } } + private readonly ITaskManager _taskManager; + /// /// Creates a kernel based on a Data path, which is akin to our current programdata path /// /// The app host. + /// The app paths. + /// The XML serializer. + /// The task manager. /// The logger. /// isoManager - public Kernel(IApplicationHost appHost, ILogger logger) - : base(appHost, logger) + public Kernel(IApplicationHost appHost, IServerApplicationPaths appPaths, IXmlSerializer xmlSerializer, ITaskManager taskManager, ILogger logger) + : base(appHost, appPaths, xmlSerializer, logger) { Instance = this; + _taskManager = taskManager; + // For now there's no real way to inject this properly BaseItem.Logger = logger; Ratings.Logger = logger; @@ -310,21 +319,10 @@ namespace MediaBrowser.Controller BaseMetadataProvider.Logger = logger; } - /// - /// Composes the exported values. - /// - protected override void RegisterExportedValues() - { - ApplicationHost.RegisterSingleInstance(this); - - base.RegisterExportedValues(); - } - /// /// Composes the parts with ioc container. /// - /// All types. - protected override void FindParts(Type[] allTypes) + protected override void FindParts() { InstallationManager = (InstallationManager)ApplicationHost.CreateInstance(typeof(InstallationManager)); FFMpegManager = (FFMpegManager)ApplicationHost.CreateInstance(typeof(FFMpegManager)); @@ -335,21 +333,21 @@ namespace MediaBrowser.Controller UserDataManager = (UserDataManager)ApplicationHost.CreateInstance(typeof(UserDataManager)); PluginSecurityManager = (PluginSecurityManager)ApplicationHost.CreateInstance(typeof(PluginSecurityManager)); - base.FindParts(allTypes); - - EntityResolutionIgnoreRules = GetExports(allTypes); - UserDataRepositories = GetExports(allTypes); - UserRepositories = GetExports(allTypes); - DisplayPreferencesRepositories = GetExports(allTypes); - ItemRepositories = GetExports(allTypes); - WeatherProviders = GetExports(allTypes); - IntroProviders = GetExports(allTypes); - PluginConfigurationPages = GetExports(allTypes); - ImageEnhancers = GetExports(allTypes).OrderBy(e => e.Priority).ToArray(); - PluginFolderCreators = GetExports(allTypes); - StringFiles = GetExports(allTypes); - EntityResolvers = GetExports(allTypes).OrderBy(e => e.Priority).ToArray(); - MetadataProviders = GetExports(allTypes).OrderBy(e => e.Priority).ToArray(); + base.FindParts(); + + EntityResolutionIgnoreRules = ApplicationHost.GetExports(); + UserDataRepositories = ApplicationHost.GetExports(); + UserRepositories = ApplicationHost.GetExports(); + DisplayPreferencesRepositories = ApplicationHost.GetExports(); + ItemRepositories = ApplicationHost.GetExports(); + WeatherProviders = ApplicationHost.GetExports(); + IntroProviders = ApplicationHost.GetExports(); + PluginConfigurationPages = ApplicationHost.GetExports(); + ImageEnhancers = ApplicationHost.GetExports().OrderBy(e => e.Priority).ToArray(); + PluginFolderCreators = ApplicationHost.GetExports(); + StringFiles = ApplicationHost.GetExports(); + EntityResolvers = ApplicationHost.GetExports().OrderBy(e => e.Priority).ToArray(); + MetadataProviders = ApplicationHost.GetExports().OrderBy(e => e.Priority).ToArray(); } /// @@ -471,7 +469,7 @@ namespace MediaBrowser.Controller { DisposeFileSystemManager(); - FileSystemManager = new FileSystemManager(this, Logger, TaskManager); + FileSystemManager = new FileSystemManager(this, Logger, _taskManager); FileSystemManager.StartWatchers(); } @@ -570,11 +568,11 @@ namespace MediaBrowser.Controller ProviderManager.ValidateCurrentlyRunningProviders(); // Any number of configuration settings could change the way the library is refreshed, so do that now - TaskManager.CancelIfRunningAndQueue(); + _taskManager.CancelIfRunningAndQueue(); if (refreshPeopleAfterUpdate) { - TaskManager.CancelIfRunningAndQueue(); + _taskManager.CancelIfRunningAndQueue(); } }); } diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 35540020e..1e901055e 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -110,6 +110,7 @@ + @@ -183,7 +184,6 @@ - diff --git a/MediaBrowser.Controller/MediaInfo/FFMpegManager.cs b/MediaBrowser.Controller/MediaInfo/FFMpegManager.cs index 9c9b0e9f2..0f535208b 100644 --- a/MediaBrowser.Controller/MediaInfo/FFMpegManager.cs +++ b/MediaBrowser.Controller/MediaInfo/FFMpegManager.cs @@ -1,12 +1,10 @@ -using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.IO; -using MediaBrowser.Common.Kernel; -using MediaBrowser.Common.Serialization; +using MediaBrowser.Common.IO; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Serialization; using System; using System.Collections.Generic; using System.ComponentModel; @@ -23,7 +21,7 @@ namespace MediaBrowser.Controller.MediaInfo /// /// Class FFMpegManager /// - public class FFMpegManager : BaseManager + public class FFMpegManager : IDisposable { /// /// Gets or sets the video image cache. @@ -47,30 +45,66 @@ namespace MediaBrowser.Controller.MediaInfo /// Gets or sets the zip client. /// /// The zip client. - private IZipClient ZipClient { get; set; } + private readonly IZipClient _zipClient; + /// + /// The _logger + /// + private readonly Kernel _kernel; + /// /// The _logger /// private readonly ILogger _logger; + /// + /// Gets the json serializer. + /// + /// The json serializer. + private readonly IJsonSerializer _jsonSerializer; + + /// + /// The _protobuf serializer + /// + private readonly IProtobufSerializer _protobufSerializer; + /// /// Initializes a new instance of the class. /// /// The kernel. /// The zip client. + /// The json serializer. + /// The protobuf serializer. /// The logger. /// zipClient - public FFMpegManager(Kernel kernel, IZipClient zipClient, ILogger logger) - : base(kernel) + public FFMpegManager(Kernel kernel, IZipClient zipClient, IJsonSerializer jsonSerializer, IProtobufSerializer protobufSerializer, ILogger logger) { + if (kernel == null) + { + throw new ArgumentNullException("kernel"); + } if (zipClient == null) { throw new ArgumentNullException("zipClient"); } + if (jsonSerializer == null) + { + throw new ArgumentNullException("jsonSerializer"); + } + if (protobufSerializer == null) + { + throw new ArgumentNullException("protobufSerializer"); + } + if (logger == null) + { + throw new ArgumentNullException("logger"); + } + _kernel = kernel; + _zipClient = zipClient; + _jsonSerializer = jsonSerializer; + _protobufSerializer = protobufSerializer; _logger = logger; - ZipClient = zipClient; // Not crazy about this but it's the only way to suppress ffmpeg crash dialog boxes SetErrorMode(ErrorModes.SEM_FAILCRITICALERRORS | ErrorModes.SEM_NOALIGNMENTFAULTEXCEPT | ErrorModes.SEM_NOGPFAULTERRORBOX | ErrorModes.SEM_NOOPENFILEERRORBOX); @@ -82,11 +116,16 @@ namespace MediaBrowser.Controller.MediaInfo Task.Run(() => VersionedDirectoryPath = GetVersionedDirectoryPath()); } + public void Dispose() + { + Dispose(true); + } + /// /// Releases unmanaged and - optionally - managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected override void Dispose(bool dispose) + protected void Dispose(bool dispose) { if (dispose) { @@ -95,8 +134,6 @@ namespace MediaBrowser.Controller.MediaInfo AudioImageCache.Dispose(); VideoImageCache.Dispose(); } - - base.Dispose(dispose); } /// @@ -186,7 +223,7 @@ namespace MediaBrowser.Controller.MediaInfo { if (_videoImagesDataPath == null) { - _videoImagesDataPath = Path.Combine(Kernel.ApplicationPaths.DataPath, "ffmpeg-video-images"); + _videoImagesDataPath = Path.Combine(_kernel.ApplicationPaths.DataPath, "ffmpeg-video-images"); if (!Directory.Exists(_videoImagesDataPath)) { @@ -212,7 +249,7 @@ namespace MediaBrowser.Controller.MediaInfo { if (_audioImagesDataPath == null) { - _audioImagesDataPath = Path.Combine(Kernel.ApplicationPaths.DataPath, "ffmpeg-audio-images"); + _audioImagesDataPath = Path.Combine(_kernel.ApplicationPaths.DataPath, "ffmpeg-audio-images"); if (!Directory.Exists(_audioImagesDataPath)) { @@ -238,7 +275,7 @@ namespace MediaBrowser.Controller.MediaInfo { if (_subtitleCachePath == null) { - _subtitleCachePath = Path.Combine(Kernel.ApplicationPaths.CachePath, "ffmpeg-subtitles"); + _subtitleCachePath = Path.Combine(_kernel.ApplicationPaths.CachePath, "ffmpeg-subtitles"); if (!Directory.Exists(_subtitleCachePath)) { @@ -265,7 +302,7 @@ namespace MediaBrowser.Controller.MediaInfo var filename = resource.Substring(resource.IndexOf(prefix, StringComparison.OrdinalIgnoreCase) + prefix.Length); - var versionedDirectoryPath = Path.Combine(Kernel.ApplicationPaths.MediaToolsPath, Path.GetFileNameWithoutExtension(filename)); + var versionedDirectoryPath = Path.Combine(_kernel.ApplicationPaths.MediaToolsPath, Path.GetFileNameWithoutExtension(filename)); if (!Directory.Exists(versionedDirectoryPath)) { @@ -287,7 +324,7 @@ namespace MediaBrowser.Controller.MediaInfo { using (var resourceStream = assembly.GetManifestResourceStream(zipFileResourcePath)) { - ZipClient.ExtractAll(resourceStream, targetPath, false); + _zipClient.ExtractAll(resourceStream, targetPath, false); } } @@ -353,7 +390,7 @@ namespace MediaBrowser.Controller.MediaInfo // Avoid File.Exists by just trying to deserialize try { - return Task.FromResult(Kernel.ProtobufSerializer.DeserializeFromFile(cacheFilePath)); + return Task.FromResult(_protobufSerializer.DeserializeFromFile(cacheFilePath)); } catch (FileNotFoundException) { @@ -428,7 +465,7 @@ namespace MediaBrowser.Controller.MediaInfo process.BeginErrorReadLine(); } - result = JsonSerializer.DeserializeFromStream(process.StandardOutput.BaseStream); + result = _jsonSerializer.DeserializeFromStream(process.StandardOutput.BaseStream); if (extractChapters) { @@ -470,7 +507,7 @@ namespace MediaBrowser.Controller.MediaInfo AddChapters(result, standardError); } - Kernel.ProtobufSerializer.SerializeToFile(result, cacheFile); + _protobufSerializer.SerializeToFile(result, cacheFile); return result; } @@ -595,7 +632,7 @@ namespace MediaBrowser.Controller.MediaInfo if (saveItem && changesMade) { - await Kernel.ItemRepository.SaveItem(video, CancellationToken.None).ConfigureAwait(false); + await _kernel.ItemRepository.SaveItem(video, CancellationToken.None).ConfigureAwait(false); } } diff --git a/MediaBrowser.Controller/Providers/MediaInfo/FFProbeVideoInfoProvider.cs b/MediaBrowser.Controller/Providers/MediaInfo/FFProbeVideoInfoProvider.cs index 443d28b67..8905656ef 100644 --- a/MediaBrowser.Controller/Providers/MediaInfo/FFProbeVideoInfoProvider.cs +++ b/MediaBrowser.Controller/Providers/MediaInfo/FFProbeVideoInfoProvider.cs @@ -4,6 +4,7 @@ using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.MediaInfo; using MediaBrowser.Model.Entities; using MediaBrowser.Model.MediaInfo; +using MediaBrowser.Model.Serialization; using System; using System.Collections.Generic; using System.IO; @@ -28,29 +29,44 @@ namespace MediaBrowser.Controller.Providers.MediaInfo /// Gets or sets the bluray examiner. /// /// The bluray examiner. - private IBlurayExaminer BlurayExaminer { get; set; } + private readonly IBlurayExaminer _blurayExaminer; - /// + /// /// The _iso manager /// private readonly IIsoManager _isoManager; + /// + /// The _protobuf serializer + /// + private readonly IProtobufSerializer _protobufSerializer; + /// /// Initializes a new instance of the class. /// /// The iso manager. /// The bluray examiner. + /// The protobuf serializer. /// blurayExaminer - public FFProbeVideoInfoProvider(IIsoManager isoManager, IBlurayExaminer blurayExaminer) + public FFProbeVideoInfoProvider(IIsoManager isoManager, IBlurayExaminer blurayExaminer, IProtobufSerializer protobufSerializer) : base() { + if (isoManager == null) + { + throw new ArgumentNullException("isoManager"); + } if (blurayExaminer == null) { throw new ArgumentNullException("blurayExaminer"); } + if (protobufSerializer == null) + { + throw new ArgumentNullException("protobufSerializer"); + } - BlurayExaminer = blurayExaminer; + _blurayExaminer = blurayExaminer; _isoManager = isoManager; + _protobufSerializer = protobufSerializer; BdInfoCache = new FileSystemRepository(Path.Combine(Kernel.Instance.ApplicationPaths.CachePath, "bdinfo")); } @@ -315,13 +331,13 @@ namespace MediaBrowser.Controller.Providers.MediaInfo try { - result = Kernel.Instance.ProtobufSerializer.DeserializeFromFile(cacheFile); + result = _protobufSerializer.DeserializeFromFile(cacheFile); } catch (FileNotFoundException) { result = GetBDInfo(inputPath); - Kernel.Instance.ProtobufSerializer.SerializeToFile(result, cacheFile); + _protobufSerializer.SerializeToFile(result, cacheFile); } cancellationToken.ThrowIfCancellationRequested(); @@ -400,7 +416,7 @@ namespace MediaBrowser.Controller.Providers.MediaInfo /// VideoStream. private BlurayDiscInfo GetBDInfo(string path) { - return BlurayExaminer.GetDiscInfo(path); + return _blurayExaminer.GetDiscInfo(path); } /// diff --git a/MediaBrowser.Controller/Providers/Movies/MovieDbProvider.cs b/MediaBrowser.Controller/Providers/Movies/MovieDbProvider.cs index 72f8a3fc7..bc261de6d 100644 --- a/MediaBrowser.Controller/Providers/Movies/MovieDbProvider.cs +++ b/MediaBrowser.Controller/Providers/Movies/MovieDbProvider.cs @@ -1,5 +1,4 @@ using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Serialization; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Model.Entities; @@ -15,6 +14,7 @@ using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Model.Serialization; namespace MediaBrowser.Controller.Providers.Movies { @@ -30,6 +30,27 @@ namespace MediaBrowser.Controller.Providers.Movies /// public class MovieDbProvider : BaseMetadataProvider { + /// + /// Gets the json serializer. + /// + /// The json serializer. + protected IJsonSerializer JsonSerializer { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The json serializer. + /// jsonSerializer + public MovieDbProvider(IJsonSerializer jsonSerializer) + : base() + { + if (jsonSerializer == null) + { + throw new ArgumentNullException("jsonSerializer"); + } + JsonSerializer = jsonSerializer; + } + /// /// Gets the priority. /// @@ -93,7 +114,7 @@ namespace MediaBrowser.Controller.Providers.Movies { get { - LazyInitializer.EnsureInitialized(ref _tmdbSettingsTask, ref _tmdbSettingsTaskInitialized, ref _tmdbSettingsTaskSyncLock, GetTmdbSettings); + LazyInitializer.EnsureInitialized(ref _tmdbSettingsTask, ref _tmdbSettingsTaskInitialized, ref _tmdbSettingsTaskSyncLock, () => GetTmdbSettings(JsonSerializer)); return _tmdbSettingsTask; } } @@ -102,13 +123,13 @@ namespace MediaBrowser.Controller.Providers.Movies /// Gets the TMDB settings. /// /// Task{TmdbSettingsResult}. - private static async Task GetTmdbSettings() + private static async Task GetTmdbSettings(IJsonSerializer jsonSerializer) { try { using (var json = await Kernel.Instance.HttpManager.Get(String.Format(TmdbConfigUrl, ApiKey), Kernel.Instance.ResourcePools.MovieDb, CancellationToken.None).ConfigureAwait(false)) { - return JsonSerializer.DeserializeFromStream(json); + return jsonSerializer.DeserializeFromStream(json); } } catch (HttpException e) @@ -168,7 +189,7 @@ namespace MediaBrowser.Controller.Providers.Movies { //in addition to ours, we need to set the last refreshed time for the local data provider //so it won't see the new files we download and process them all over again - if (JsonProvider == null) JsonProvider = new MovieProviderFromJson(); + if (JsonProvider == null) JsonProvider = new MovieProviderFromJson(JsonSerializer); var data = item.ProviderData.GetValueOrDefault(JsonProvider.Id, new BaseProviderInfo { ProviderId = JsonProvider.Id }); data.LastRefreshed = value; item.ProviderData[JsonProvider.Id] = data; diff --git a/MediaBrowser.Controller/Providers/Movies/MovieProviderFromJson.cs b/MediaBrowser.Controller/Providers/Movies/MovieProviderFromJson.cs index 45079ddda..5de17aab0 100644 --- a/MediaBrowser.Controller/Providers/Movies/MovieProviderFromJson.cs +++ b/MediaBrowser.Controller/Providers/Movies/MovieProviderFromJson.cs @@ -1,5 +1,5 @@ -using MediaBrowser.Common.Serialization; -using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Model.Serialization; using System; using System.IO; using System.Threading; @@ -12,6 +12,10 @@ namespace MediaBrowser.Controller.Providers.Movies /// public class MovieProviderFromJson : MovieDbProvider { + public MovieProviderFromJson(IJsonSerializer jsonSerializer) : base(jsonSerializer) + { + } + /// /// Gets the priority. /// diff --git a/MediaBrowser.Controller/Providers/Movies/PersonProviderFromJson.cs b/MediaBrowser.Controller/Providers/Movies/PersonProviderFromJson.cs index b4b4933e2..7517ec65c 100644 --- a/MediaBrowser.Controller/Providers/Movies/PersonProviderFromJson.cs +++ b/MediaBrowser.Controller/Providers/Movies/PersonProviderFromJson.cs @@ -1,5 +1,5 @@ -using MediaBrowser.Common.Serialization; -using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Model.Serialization; using System; using System.IO; using System.Threading; @@ -12,6 +12,10 @@ namespace MediaBrowser.Controller.Providers.Movies /// class PersonProviderFromJson : TmdbPersonProvider { + public PersonProviderFromJson(IJsonSerializer jsonSerializer) : base(jsonSerializer) + { + } + /// /// Supportses the specified item. /// diff --git a/MediaBrowser.Controller/Providers/Movies/TmdbPersonProvider.cs b/MediaBrowser.Controller/Providers/Movies/TmdbPersonProvider.cs index 7b5d62fb0..32013614c 100644 --- a/MediaBrowser.Controller/Providers/Movies/TmdbPersonProvider.cs +++ b/MediaBrowser.Controller/Providers/Movies/TmdbPersonProvider.cs @@ -1,5 +1,4 @@ -using MediaBrowser.Common.Serialization; -using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Net; using System; @@ -10,6 +9,7 @@ using System.Linq; using System.Net; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Model.Serialization; namespace MediaBrowser.Controller.Providers.Movies { @@ -23,6 +23,27 @@ namespace MediaBrowser.Controller.Providers.Movies /// protected const string MetaFileName = "MBPerson.json"; + /// + /// Gets the json serializer. + /// + /// The json serializer. + protected IJsonSerializer JsonSerializer { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The json serializer. + /// jsonSerializer + public TmdbPersonProvider(IJsonSerializer jsonSerializer) + : base() + { + if (jsonSerializer == null) + { + throw new ArgumentNullException("jsonSerializer"); + } + JsonSerializer = jsonSerializer; + } + /// /// Supportses the specified item. /// @@ -56,7 +77,7 @@ namespace MediaBrowser.Controller.Providers.Movies protected override async Task FetchAsyncInternal(BaseItem item, bool force, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - + var person = (Person)item; var tasks = new List(); @@ -169,7 +190,7 @@ namespace MediaBrowser.Controller.Providers.Movies } cancellationToken.ThrowIfCancellationRequested(); - + if (searchResult != null && searchResult.Biography != null) { ProcessInfo(person, searchResult); diff --git a/MediaBrowser.Controller/ScheduledTasks/ChapterImagesTask.cs b/MediaBrowser.Controller/ScheduledTasks/ChapterImagesTask.cs index 1a64bb853..536033719 100644 --- a/MediaBrowser.Controller/ScheduledTasks/ChapterImagesTask.cs +++ b/MediaBrowser.Controller/ScheduledTasks/ChapterImagesTask.cs @@ -28,9 +28,9 @@ namespace MediaBrowser.Controller.ScheduledTasks /// Creates the triggers that define when the task will run /// /// IEnumerable{BaseTaskTrigger}. - protected override IEnumerable GetDefaultTriggers() + public override IEnumerable GetDefaultTriggers() { - return new BaseTaskTrigger[] + return new ITaskTrigger[] { new DailyTrigger { TimeOfDay = TimeSpan.FromHours(4) } }; diff --git a/MediaBrowser.Controller/ScheduledTasks/ImageCleanupTask.cs b/MediaBrowser.Controller/ScheduledTasks/ImageCleanupTask.cs index 8dd0895c9..7e0094a67 100644 --- a/MediaBrowser.Controller/ScheduledTasks/ImageCleanupTask.cs +++ b/MediaBrowser.Controller/ScheduledTasks/ImageCleanupTask.cs @@ -20,7 +20,8 @@ namespace MediaBrowser.Controller.ScheduledTasks /// Initializes a new instance of the class. /// /// The kernel. - /// + /// The task manager. + /// The logger. public ImageCleanupTask(Kernel kernel, ITaskManager taskManager, ILogger logger) : base(kernel, taskManager, logger) { @@ -30,9 +31,9 @@ namespace MediaBrowser.Controller.ScheduledTasks /// Creates the triggers that define when the task will run /// /// IEnumerable{BaseTaskTrigger}. - protected override IEnumerable GetDefaultTriggers() + public override IEnumerable GetDefaultTriggers() { - return new BaseTaskTrigger[] + return new ITaskTrigger[] { new DailyTrigger { TimeOfDay = TimeSpan.FromHours(2) } }; diff --git a/MediaBrowser.Controller/ScheduledTasks/PeopleValidationTask.cs b/MediaBrowser.Controller/ScheduledTasks/PeopleValidationTask.cs index ee55ff2e9..595de684d 100644 --- a/MediaBrowser.Controller/ScheduledTasks/PeopleValidationTask.cs +++ b/MediaBrowser.Controller/ScheduledTasks/PeopleValidationTask.cs @@ -26,9 +26,9 @@ namespace MediaBrowser.Controller.ScheduledTasks /// Creates the triggers that define when the task will run /// /// IEnumerable{BaseTaskTrigger}. - protected override IEnumerable GetDefaultTriggers() + public override IEnumerable GetDefaultTriggers() { - return new BaseTaskTrigger[] + return new ITaskTrigger[] { new DailyTrigger { TimeOfDay = TimeSpan.FromHours(2) }, diff --git a/MediaBrowser.Controller/ScheduledTasks/PluginUpdateTask.cs b/MediaBrowser.Controller/ScheduledTasks/PluginUpdateTask.cs index 854c3b82f..7a1007f1b 100644 --- a/MediaBrowser.Controller/ScheduledTasks/PluginUpdateTask.cs +++ b/MediaBrowser.Controller/ScheduledTasks/PluginUpdateTask.cs @@ -29,9 +29,9 @@ namespace MediaBrowser.Controller.ScheduledTasks /// Creates the triggers that define when the task will run /// /// IEnumerable{BaseTaskTrigger}. - protected override IEnumerable GetDefaultTriggers() + public override IEnumerable GetDefaultTriggers() { - return new BaseTaskTrigger[] { + return new ITaskTrigger[] { // 1:30am new DailyTrigger { TimeOfDay = TimeSpan.FromHours(1.5) }, diff --git a/MediaBrowser.Controller/ScheduledTasks/RefreshMediaLibraryTask.cs b/MediaBrowser.Controller/ScheduledTasks/RefreshMediaLibraryTask.cs index 76e60f2ef..104b432f4 100644 --- a/MediaBrowser.Controller/ScheduledTasks/RefreshMediaLibraryTask.cs +++ b/MediaBrowser.Controller/ScheduledTasks/RefreshMediaLibraryTask.cs @@ -27,9 +27,9 @@ namespace MediaBrowser.Controller.ScheduledTasks /// Gets the default triggers. /// /// IEnumerable{BaseTaskTrigger}. - protected override IEnumerable GetDefaultTriggers() + public override IEnumerable GetDefaultTriggers() { - return new BaseTaskTrigger[] { + return new ITaskTrigger[] { new StartupTrigger(), diff --git a/MediaBrowser.Controller/ServerApplicationPaths.cs b/MediaBrowser.Controller/ServerApplicationPaths.cs deleted file mode 100644 index a376afed8..000000000 --- a/MediaBrowser.Controller/ServerApplicationPaths.cs +++ /dev/null @@ -1,334 +0,0 @@ -using MediaBrowser.Common.Kernel; -using System.IO; - -namespace MediaBrowser.Controller -{ - /// - /// Extends BaseApplicationPaths to add paths that are only applicable on the server - /// - public class ServerApplicationPaths : BaseApplicationPaths - { - /// - /// The _root folder path - /// - private string _rootFolderPath; - /// - /// Gets the path to the base root media directory - /// - /// The root folder path. - public string RootFolderPath - { - get - { - if (_rootFolderPath == null) - { - _rootFolderPath = Path.Combine(ProgramDataPath, "Root"); - if (!Directory.Exists(_rootFolderPath)) - { - Directory.CreateDirectory(_rootFolderPath); - } - } - return _rootFolderPath; - } - } - - /// - /// The _default user views path - /// - private string _defaultUserViewsPath; - /// - /// Gets the path to the default user view directory. Used if no specific user view is defined. - /// - /// The default user views path. - public string DefaultUserViewsPath - { - get - { - if (_defaultUserViewsPath == null) - { - _defaultUserViewsPath = Path.Combine(RootFolderPath, "Default"); - if (!Directory.Exists(_defaultUserViewsPath)) - { - Directory.CreateDirectory(_defaultUserViewsPath); - } - } - return _defaultUserViewsPath; - } - } - - /// - /// The _localization path - /// - private string _localizationPath; - /// - /// Gets the path to localization data. - /// - /// The localization path. - public string LocalizationPath - { - get - { - if (_localizationPath == null) - { - _localizationPath = Path.Combine(ProgramDataPath, "Localization"); - if (!Directory.Exists(_localizationPath)) - { - Directory.CreateDirectory(_localizationPath); - } - } - return _localizationPath; - } - } - - /// - /// The _ibn path - /// - private string _ibnPath; - /// - /// Gets the path to the Images By Name directory - /// - /// The images by name path. - public string ImagesByNamePath - { - get - { - if (_ibnPath == null) - { - _ibnPath = Path.Combine(ProgramDataPath, "ImagesByName"); - if (!Directory.Exists(_ibnPath)) - { - Directory.CreateDirectory(_ibnPath); - } - } - - return _ibnPath; - } - } - - /// - /// The _people path - /// - private string _peoplePath; - /// - /// Gets the path to the People directory - /// - /// The people path. - public string PeoplePath - { - get - { - if (_peoplePath == null) - { - _peoplePath = Path.Combine(ImagesByNamePath, "People"); - if (!Directory.Exists(_peoplePath)) - { - Directory.CreateDirectory(_peoplePath); - } - } - - return _peoplePath; - } - } - - /// - /// The _genre path - /// - private string _genrePath; - /// - /// Gets the path to the Genre directory - /// - /// The genre path. - public string GenrePath - { - get - { - if (_genrePath == null) - { - _genrePath = Path.Combine(ImagesByNamePath, "Genre"); - if (!Directory.Exists(_genrePath)) - { - Directory.CreateDirectory(_genrePath); - } - } - - return _genrePath; - } - } - - /// - /// The _studio path - /// - private string _studioPath; - /// - /// Gets the path to the Studio directory - /// - /// The studio path. - public string StudioPath - { - get - { - if (_studioPath == null) - { - _studioPath = Path.Combine(ImagesByNamePath, "Studio"); - if (!Directory.Exists(_studioPath)) - { - Directory.CreateDirectory(_studioPath); - } - } - - return _studioPath; - } - } - - /// - /// The _year path - /// - private string _yearPath; - /// - /// Gets the path to the Year directory - /// - /// The year path. - public string YearPath - { - get - { - if (_yearPath == null) - { - _yearPath = Path.Combine(ImagesByNamePath, "Year"); - if (!Directory.Exists(_yearPath)) - { - Directory.CreateDirectory(_yearPath); - } - } - - return _yearPath; - } - } - - /// - /// The _general path - /// - private string _generalPath; - /// - /// Gets the path to the General IBN directory - /// - /// The general path. - public string GeneralPath - { - get - { - if (_generalPath == null) - { - _generalPath = Path.Combine(ImagesByNamePath, "General"); - if (!Directory.Exists(_generalPath)) - { - Directory.CreateDirectory(_generalPath); - } - } - - return _generalPath; - } - } - - /// - /// The _ratings path - /// - private string _ratingsPath; - /// - /// Gets the path to the Ratings IBN directory - /// - /// The ratings path. - public string RatingsPath - { - get - { - if (_ratingsPath == null) - { - _ratingsPath = Path.Combine(ImagesByNamePath, "Ratings"); - if (!Directory.Exists(_ratingsPath)) - { - Directory.CreateDirectory(_ratingsPath); - } - } - - return _ratingsPath; - } - } - - /// - /// The _user configuration directory path - /// - private string _userConfigurationDirectoryPath; - /// - /// Gets the path to the user configuration directory - /// - /// The user configuration directory path. - public string UserConfigurationDirectoryPath - { - get - { - if (_userConfigurationDirectoryPath == null) - { - _userConfigurationDirectoryPath = Path.Combine(ConfigurationDirectoryPath, "users"); - if (!Directory.Exists(_userConfigurationDirectoryPath)) - { - Directory.CreateDirectory(_userConfigurationDirectoryPath); - } - } - return _userConfigurationDirectoryPath; - } - } - - /// - /// The _f F MPEG stream cache path - /// - private string _fFMpegStreamCachePath; - /// - /// Gets the FF MPEG stream cache path. - /// - /// The FF MPEG stream cache path. - public string FFMpegStreamCachePath - { - get - { - if (_fFMpegStreamCachePath == null) - { - _fFMpegStreamCachePath = Path.Combine(CachePath, "ffmpeg-streams"); - - if (!Directory.Exists(_fFMpegStreamCachePath)) - { - Directory.CreateDirectory(_fFMpegStreamCachePath); - } - } - - return _fFMpegStreamCachePath; - } - } - - /// - /// The _media tools path - /// - private string _mediaToolsPath; - /// - /// Gets the folder path to tools - /// - /// The media tools path. - public string MediaToolsPath - { - get - { - if (_mediaToolsPath == null) - { - _mediaToolsPath = Path.Combine(ProgramDataPath, "MediaTools"); - - if (!Directory.Exists(_mediaToolsPath)) - { - Directory.CreateDirectory(_mediaToolsPath); - } - } - - return _mediaToolsPath; - } - } - } -} diff --git a/MediaBrowser.Controller/Updates/InstallationManager.cs b/MediaBrowser.Controller/Updates/InstallationManager.cs index bef5a6472..af544dd51 100644 --- a/MediaBrowser.Controller/Updates/InstallationManager.cs +++ b/MediaBrowser.Controller/Updates/InstallationManager.cs @@ -1,18 +1,17 @@ -using System.Security.Cryptography; -using MediaBrowser.Common.Events; -using MediaBrowser.Common.Kernel; +using MediaBrowser.Common.Events; using MediaBrowser.Common.Net; using MediaBrowser.Common.Plugins; using MediaBrowser.Common.Progress; -using MediaBrowser.Common.Serialization; using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Updates; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Security.Cryptography; using System.Threading; using System.Threading.Tasks; @@ -32,8 +31,8 @@ namespace MediaBrowser.Controller.Updates /// /// The completed installations /// - public readonly ConcurrentBag CompletedInstallations = new ConcurrentBag(); - + public readonly ConcurrentBag CompletedInstallations = new ConcurrentBag(); + #region PluginUninstalled Event /// /// Occurs when [plugin uninstalled]. @@ -68,7 +67,7 @@ namespace MediaBrowser.Controller.Updates _logger.Info("Plugin updated: {0} {1} {2}", newVersion.name, newVersion.version, newVersion.classification); EventHelper.QueueEventIfNotNull(PluginUpdated, this, new GenericEventArgs> { Argument = new Tuple(plugin, newVersion) }, _logger); - + Kernel.NotifyPendingRestart(); } #endregion @@ -108,15 +107,22 @@ namespace MediaBrowser.Controller.Updates /// private readonly INetworkManager _networkManager; + /// + /// Gets the json serializer. + /// + /// The json serializer. + protected IJsonSerializer JsonSerializer { get; private set; } + /// /// Initializes a new instance of the class. /// /// The kernel. /// The zip client. /// The network manager. + /// /// The logger. /// zipClient - public InstallationManager(Kernel kernel, IZipClient zipClient, INetworkManager networkManager, ILogger logger) + public InstallationManager(Kernel kernel, IZipClient zipClient, INetworkManager networkManager, IJsonSerializer jsonSerializer, ILogger logger) : base(kernel) { if (zipClient == null) @@ -131,6 +137,12 @@ namespace MediaBrowser.Controller.Updates { throw new ArgumentNullException("logger"); } + if (jsonSerializer == null) + { + throw new ArgumentNullException("jsonSerializer"); + } + + JsonSerializer = jsonSerializer; _networkManager = networkManager; _logger = logger; @@ -161,7 +173,7 @@ namespace MediaBrowser.Controller.Updates package.versions = package.versions.Where(v => !string.IsNullOrWhiteSpace(v.sourceUrl)) .OrderByDescending(v => v.version).ToList(); } - + if (packageType.HasValue) { packages = packages.Where(p => p.type == packageType.Value).ToList(); @@ -178,7 +190,7 @@ namespace MediaBrowser.Controller.Updates // Remove packages with no versions packages = packages.Where(p => p.versions.Any()).ToList(); - + return packages; } } @@ -320,7 +332,7 @@ namespace MediaBrowser.Controller.Updates var innerCancellationTokenSource = new CancellationTokenSource(); var tuple = new Tuple(installationInfo, innerCancellationTokenSource); - + // Add it to the in-progress list lock (CurrentInstallations) { @@ -364,7 +376,7 @@ namespace MediaBrowser.Controller.Updates _logger.Info("Package installation cancelled: {0} {1}", package.name, package.versionStr); Kernel.TcpManager.SendWebSocketMessage("PackageInstallationCancelled", installationInfo); - + throw; } catch @@ -373,7 +385,7 @@ namespace MediaBrowser.Controller.Updates { CurrentInstallations.Remove(tuple); } - + Kernel.TcpManager.SendWebSocketMessage("PackageInstallationFailed", installationInfo); throw; @@ -421,7 +433,7 @@ namespace MediaBrowser.Controller.Updates } cancellationToken.ThrowIfCancellationRequested(); - + // Success - move it to the real target based on type if (isArchive) { @@ -435,7 +447,7 @@ namespace MediaBrowser.Controller.Updates throw; } - } + } else { try @@ -448,8 +460,8 @@ namespace MediaBrowser.Controller.Updates _logger.ErrorException("Error attempting to move file from {0} to {1}", e, tempFile, target); throw; } - } - + } + // Set last update time if we were installed before var plugin = Kernel.Plugins.FirstOrDefault(p => p.Name.Equals(package.name, StringComparison.OrdinalIgnoreCase)); diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index 372aefdc6..8e8f88c20 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -75,6 +75,9 @@ + + + diff --git a/MediaBrowser.Model/Serialization/IJsonSerializer.cs b/MediaBrowser.Model/Serialization/IJsonSerializer.cs new file mode 100644 index 000000000..056389d2e --- /dev/null +++ b/MediaBrowser.Model/Serialization/IJsonSerializer.cs @@ -0,0 +1,103 @@ +using System; +using System.IO; + +namespace MediaBrowser.Model.Serialization +{ + public interface IJsonSerializer + { + /// + /// Serializes to stream. + /// + /// + /// The obj. + /// The stream. + /// obj + void SerializeToStream(T obj, Stream stream) + where T : class; + + /// + /// Serializes to file. + /// + /// + /// The obj. + /// The file. + /// obj + void SerializeToFile(T obj, string file) + where T : class; + + /// + /// Deserializes from file. + /// + /// The type. + /// The file. + /// System.Object. + /// type + object DeserializeFromFile(Type type, string file); + + /// + /// Deserializes from file. + /// + /// + /// The file. + /// ``0. + /// file + T DeserializeFromFile(string file) + where T : class; + + /// + /// Deserializes from stream. + /// + /// + /// The stream. + /// ``0. + /// stream + T DeserializeFromStream(Stream stream); + + /// + /// Deserializes from string. + /// + /// + /// The text. + /// ``0. + /// text + T DeserializeFromString(string text); + + /// + /// Deserializes from stream. + /// + /// The stream. + /// The type. + /// System.Object. + /// stream + object DeserializeFromStream(Stream stream, Type type); + + /// + /// Deserializes from string. + /// + /// The json. + /// The type. + /// System.Object. + /// json + object DeserializeFromString(string json, Type type); + + /// + /// Serializes to string. + /// + /// + /// The obj. + /// System.String. + /// obj + string SerializeToString(T obj) + where T : class; + + /// + /// Serializes to bytes. + /// + /// + /// The obj. + /// System.Byte[][]. + /// obj + byte[] SerializeToBytes(T obj) + where T : class; + } +} \ No newline at end of file diff --git a/MediaBrowser.Model/Serialization/IProtobufSerializer.cs b/MediaBrowser.Model/Serialization/IProtobufSerializer.cs new file mode 100644 index 000000000..4c3ef6960 --- /dev/null +++ b/MediaBrowser.Model/Serialization/IProtobufSerializer.cs @@ -0,0 +1,63 @@ +using System; +using System.IO; + +namespace MediaBrowser.Model.Serialization +{ + public interface IProtobufSerializer + { + /// + /// Serializes to stream. + /// + /// The obj. + /// The stream. + /// obj + void SerializeToStream(object obj, Stream stream); + + /// + /// Deserializes from stream. + /// + /// The stream. + /// The type. + /// System.Object. + /// stream + object DeserializeFromStream(Stream stream, Type type); + + /// + /// Deserializes from stream. + /// + /// + /// The stream. + /// ``0. + T DeserializeFromStream(Stream stream) + where T : class; + + /// + /// Serializes to file. + /// + /// + /// The obj. + /// The file. + /// file + void SerializeToFile(T obj, string file); + + /// + /// Deserializes from file. + /// + /// + /// The file. + /// ``0. + /// file + T DeserializeFromFile(string file) + where T : class; + + /// + /// Serializes to bytes. + /// + /// + /// The obj. + /// System.Byte[][]. + /// obj + byte[] SerializeToBytes(T obj) + where T : class; + } +} \ No newline at end of file diff --git a/MediaBrowser.Model/Serialization/IXmlSerializer.cs b/MediaBrowser.Model/Serialization/IXmlSerializer.cs new file mode 100644 index 000000000..1d0e0302b --- /dev/null +++ b/MediaBrowser.Model/Serialization/IXmlSerializer.cs @@ -0,0 +1,69 @@ +using System; +using System.IO; + +namespace MediaBrowser.Model.Serialization +{ + public interface IXmlSerializer + { + /// + /// Deserializes from stream. + /// + /// + /// The stream. + /// ``0. + T DeserializeFromStream(Stream stream); + + /// + /// Deserializes from stream. + /// + /// The type. + /// The stream. + /// System.Object. + object DeserializeFromStream(Type type, Stream stream); + + /// + /// Serializes to stream. + /// + /// The obj. + /// The stream. + void SerializeToStream(object obj, Stream stream); + + /// + /// Deserializes from file. + /// + /// + /// The file. + /// ``0. + T DeserializeFromFile(string file); + + /// + /// Serializes to file. + /// + /// The obj. + /// The file. + void SerializeToFile(object obj, string file); + + /// + /// Deserializes from file. + /// + /// The type. + /// The file. + /// System.Object. + object DeserializeFromFile(Type type, string file); + + /// + /// Deserializes from bytes. + /// + /// The type. + /// The buffer. + /// System.Object. + object DeserializeFromBytes(Type type, byte[] buffer); + + /// + /// Serializes to bytes. + /// + /// The obj. + /// System.Byte[][]. + byte[] SerializeToBytes(object obj); + } +} \ No newline at end of file diff --git a/MediaBrowser.Networking/MediaBrowser.Networking.csproj b/MediaBrowser.Networking/MediaBrowser.Networking.csproj index 7e4a0b160..41fd6ceab 100644 --- a/MediaBrowser.Networking/MediaBrowser.Networking.csproj +++ b/MediaBrowser.Networking/MediaBrowser.Networking.csproj @@ -112,6 +112,8 @@ + + diff --git a/MediaBrowser.Networking/Web/HttpServer.cs b/MediaBrowser.Networking/Web/HttpServer.cs index c280abedf..ab4b8558f 100644 --- a/MediaBrowser.Networking/Web/HttpServer.cs +++ b/MediaBrowser.Networking/Web/HttpServer.cs @@ -4,6 +4,7 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Kernel; using MediaBrowser.Common.Net; using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Serialization; using ServiceStack.Api.Swagger; using ServiceStack.Common.Web; using ServiceStack.Configuration; @@ -60,6 +61,12 @@ namespace MediaBrowser.Networking.Web /// The HTTP listener. private IDisposable HttpListener { get; set; } + /// + /// Gets or sets the protobuf serializer. + /// + /// The protobuf serializer. + private IProtobufSerializer ProtobufSerializer { get; set; } + /// /// Occurs when [web socket connected]. /// @@ -82,17 +89,22 @@ namespace MediaBrowser.Networking.Web /// /// The application host. /// The kernel. + /// The protobuf serializer. /// The logger. /// Name of the server. /// The default redirectpath. /// urlPrefix - public HttpServer(IApplicationHost applicationHost, IKernel kernel, ILogger logger, string serverName, string defaultRedirectpath) + public HttpServer(IApplicationHost applicationHost, IKernel kernel, IProtobufSerializer protobufSerializer, ILogger logger, string serverName, string defaultRedirectpath) : base() { if (kernel == null) { throw new ArgumentNullException("kernel"); } + if (protobufSerializer == null) + { + throw new ArgumentNullException("protobufSerializer"); + } if (logger == null) { throw new ArgumentNullException("logger"); @@ -112,6 +124,7 @@ namespace MediaBrowser.Networking.Web ServerName = serverName; DefaultRedirectPath = defaultRedirectpath; + ProtobufSerializer = protobufSerializer; _logger = logger; ApplicationHost = applicationHost; @@ -121,7 +134,7 @@ namespace MediaBrowser.Networking.Web Kernel = kernel; EndpointHost.ConfigureHost(this, ServerName, CreateServiceManager()); - ContentTypeFilters.Register(ContentType.ProtoBuf, (reqCtx, res, stream) => Kernel.ProtobufSerializer.SerializeToStream(res, stream), (type, stream) => Kernel.ProtobufSerializer.DeserializeFromStream(stream, type)); + ContentTypeFilters.Register(ContentType.ProtoBuf, (reqCtx, res, stream) => ProtobufSerializer.SerializeToStream(res, stream), (type, stream) => ProtobufSerializer.DeserializeFromStream(stream, type)); Init(); } @@ -132,6 +145,10 @@ namespace MediaBrowser.Networking.Web /// The container. public override void Configure(Container container) { + JsConfig.DateHandler = JsonDateHandler.ISO8601; + JsConfig.ExcludeTypeInfo = true; + JsConfig.IncludeNullValues = false; + SetConfig(new EndpointHostConfig { DefaultRedirectPath = DefaultRedirectPath, diff --git a/MediaBrowser.Networking/Web/NativeWebSocket.cs b/MediaBrowser.Networking/Web/NativeWebSocket.cs new file mode 100644 index 000000000..ad28d1a7f --- /dev/null +++ b/MediaBrowser.Networking/Web/NativeWebSocket.cs @@ -0,0 +1,145 @@ +using MediaBrowser.Model.Logging; +using System; +using System.Net.WebSockets; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Common.Net +{ + /// + /// Class NativeWebSocket + /// + public class NativeWebSocket : IWebSocket + { + /// + /// The logger + /// + private readonly ILogger _logger; + + /// + /// Gets or sets the web socket. + /// + /// The web socket. + private WebSocket WebSocket { get; set; } + + /// + /// Initializes a new instance of the class. + /// + /// The socket. + /// The logger. + /// socket + public NativeWebSocket(WebSocket socket, ILogger logger) + { + if (socket == null) + { + throw new ArgumentNullException("socket"); + } + + if (logger == null) + { + throw new ArgumentNullException("logger"); + } + + _logger = logger; + WebSocket = socket; + + Receive(); + } + + /// + /// Gets or sets the state. + /// + /// The state. + public WebSocketState State + { + get { return WebSocket.State; } + } + + /// + /// Receives this instance. + /// + private async void Receive() + { + while (true) + { + byte[] bytes; + + try + { + bytes = await ReceiveBytesAsync(CancellationToken.None).ConfigureAwait(false); + } + catch (WebSocketException ex) + { + _logger.ErrorException("Error reveiving web socket message", ex); + + break; + } + + if (OnReceiveDelegate != null) + { + OnReceiveDelegate(bytes); + } + } + } + + /// + /// Receives the async. + /// + /// The cancellation token. + /// Task{WebSocketMessageInfo}. + /// Connection closed + private async Task ReceiveBytesAsync(CancellationToken cancellationToken) + { + var bytes = new byte[4096]; + var buffer = new ArraySegment(bytes); + + var result = await WebSocket.ReceiveAsync(buffer, cancellationToken).ConfigureAwait(false); + + if (result.CloseStatus.HasValue) + { + throw new WebSocketException("Connection closed"); + } + + return buffer.Array; + } + + /// + /// Sends the async. + /// + /// The bytes. + /// The type. + /// if set to true [end of message]. + /// The cancellation token. + /// Task. + public Task SendAsync(byte[] bytes, WebSocketMessageType type, bool endOfMessage, CancellationToken cancellationToken) + { + return WebSocket.SendAsync(new ArraySegment(bytes), type, true, cancellationToken); + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + Dispose(true); + } + + /// + /// Releases unmanaged and - optionally - managed resources. + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool dispose) + { + if (dispose) + { + WebSocket.Dispose(); + } + } + + /// + /// Gets or sets the receive action. + /// + /// The receive action. + public Action OnReceiveDelegate { get; set; } + } +} diff --git a/MediaBrowser.Networking/Web/ServerFactory.cs b/MediaBrowser.Networking/Web/ServerFactory.cs new file mode 100644 index 000000000..b93f2ca1c --- /dev/null +++ b/MediaBrowser.Networking/Web/ServerFactory.cs @@ -0,0 +1,28 @@ +using MediaBrowser.Common.Kernel; +using MediaBrowser.Common.Net; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Serialization; + +namespace MediaBrowser.Networking.Web +{ + /// + /// Class ServerFactory + /// + public static class ServerFactory + { + /// + /// Creates the server. + /// + /// The application host. + /// The kernel. + /// The protobuf serializer. + /// The logger. + /// Name of the server. + /// The default redirectpath. + /// IHttpServer. + public static IHttpServer CreateServer(IApplicationHost applicationHost, IKernel kernel, IProtobufSerializer protobufSerializer, ILogger logger, string serverName, string defaultRedirectpath) + { + return new HttpServer(applicationHost, kernel, protobufSerializer, logger, serverName, defaultRedirectpath); + } + } +} diff --git a/MediaBrowser.Networking/WebSocket/AlchemyWebSocket.cs b/MediaBrowser.Networking/WebSocket/AlchemyWebSocket.cs index fbdb3186f..5eca1a78c 100644 --- a/MediaBrowser.Networking/WebSocket/AlchemyWebSocket.cs +++ b/MediaBrowser.Networking/WebSocket/AlchemyWebSocket.cs @@ -1,9 +1,9 @@ using Alchemy.Classes; using MediaBrowser.Common.Net; -using MediaBrowser.Common.Serialization; using MediaBrowser.Model.Logging; using System; using System.Net.WebSockets; +using System.Text; using System.Threading; using System.Threading.Tasks; @@ -83,9 +83,9 @@ namespace MediaBrowser.Networking.WebSocket { try { - var messageResult = JsonSerializer.DeserializeFromString(json); + var bytes = Encoding.UTF8.GetBytes(json); - OnReceiveDelegate(messageResult); + OnReceiveDelegate(bytes); } catch (Exception ex) { @@ -128,6 +128,6 @@ namespace MediaBrowser.Networking.WebSocket /// Gets or sets the receive action. /// /// The receive action. - public Action OnReceiveDelegate { get; set; } + public Action OnReceiveDelegate { get; set; } } } diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj new file mode 100644 index 000000000..416a7d746 --- /dev/null +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -0,0 +1,102 @@ + + + + + Debug + AnyCPU + {2E781478-814D-4A48-9D80-BFF206441A65} + Library + Properties + MediaBrowser.Server.Implementations + MediaBrowser.Server.Implementations + v4.5 + 512 + ..\ + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + ..\packages\System.Data.SQLite.1.0.84.0\lib\net45\System.Data.SQLite.dll + + + ..\packages\System.Data.SQLite.1.0.84.0\lib\net45\System.Data.SQLite.Linq.dll + + + + + + + + + + Properties\SharedVersion.cs + + + + + + + + + + + + + + {c4d2573a-3fd3-441f-81af-174ac4cd4e1d} + MediaBrowser.Common.Implementations + + + {9142eefa-7570-41e1-bfcc-468bb571af2f} + MediaBrowser.Common + + + {17e1f4e6-8abd-4fe5-9ecf-43d4b6087ba2} + MediaBrowser.Controller + + + {7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b} + MediaBrowser.Model + + + + + Always + + + Always + + + + + + + + + + \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Properties/AssemblyInfo.cs b/MediaBrowser.Server.Implementations/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..db656b934 --- /dev/null +++ b/MediaBrowser.Server.Implementations/Properties/AssemblyInfo.cs @@ -0,0 +1,30 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("MediaBrowser.Server.Implementations")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("MediaBrowser.Server.Implementations")] +[assembly: AssemblyCopyright("Copyright © 2013")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("0537cdd3-a069-4d86-9318-d46d8b119903")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// diff --git a/MediaBrowser.Server.Implementations/ServerApplicationPaths.cs b/MediaBrowser.Server.Implementations/ServerApplicationPaths.cs new file mode 100644 index 000000000..e473808f5 --- /dev/null +++ b/MediaBrowser.Server.Implementations/ServerApplicationPaths.cs @@ -0,0 +1,335 @@ +using MediaBrowser.Common.Implementations; +using MediaBrowser.Controller; +using System.IO; + +namespace MediaBrowser.Server.Implementations +{ + /// + /// Extends BaseApplicationPaths to add paths that are only applicable on the server + /// + public class ServerApplicationPaths : BaseApplicationPaths, IServerApplicationPaths + { + /// + /// The _root folder path + /// + private string _rootFolderPath; + /// + /// Gets the path to the base root media directory + /// + /// The root folder path. + public string RootFolderPath + { + get + { + if (_rootFolderPath == null) + { + _rootFolderPath = Path.Combine(ProgramDataPath, "Root"); + if (!Directory.Exists(_rootFolderPath)) + { + Directory.CreateDirectory(_rootFolderPath); + } + } + return _rootFolderPath; + } + } + + /// + /// The _default user views path + /// + private string _defaultUserViewsPath; + /// + /// Gets the path to the default user view directory. Used if no specific user view is defined. + /// + /// The default user views path. + public string DefaultUserViewsPath + { + get + { + if (_defaultUserViewsPath == null) + { + _defaultUserViewsPath = Path.Combine(RootFolderPath, "Default"); + if (!Directory.Exists(_defaultUserViewsPath)) + { + Directory.CreateDirectory(_defaultUserViewsPath); + } + } + return _defaultUserViewsPath; + } + } + + /// + /// The _localization path + /// + private string _localizationPath; + /// + /// Gets the path to localization data. + /// + /// The localization path. + public string LocalizationPath + { + get + { + if (_localizationPath == null) + { + _localizationPath = Path.Combine(ProgramDataPath, "Localization"); + if (!Directory.Exists(_localizationPath)) + { + Directory.CreateDirectory(_localizationPath); + } + } + return _localizationPath; + } + } + + /// + /// The _ibn path + /// + private string _ibnPath; + /// + /// Gets the path to the Images By Name directory + /// + /// The images by name path. + public string ImagesByNamePath + { + get + { + if (_ibnPath == null) + { + _ibnPath = Path.Combine(ProgramDataPath, "ImagesByName"); + if (!Directory.Exists(_ibnPath)) + { + Directory.CreateDirectory(_ibnPath); + } + } + + return _ibnPath; + } + } + + /// + /// The _people path + /// + private string _peoplePath; + /// + /// Gets the path to the People directory + /// + /// The people path. + public string PeoplePath + { + get + { + if (_peoplePath == null) + { + _peoplePath = Path.Combine(ImagesByNamePath, "People"); + if (!Directory.Exists(_peoplePath)) + { + Directory.CreateDirectory(_peoplePath); + } + } + + return _peoplePath; + } + } + + /// + /// The _genre path + /// + private string _genrePath; + /// + /// Gets the path to the Genre directory + /// + /// The genre path. + public string GenrePath + { + get + { + if (_genrePath == null) + { + _genrePath = Path.Combine(ImagesByNamePath, "Genre"); + if (!Directory.Exists(_genrePath)) + { + Directory.CreateDirectory(_genrePath); + } + } + + return _genrePath; + } + } + + /// + /// The _studio path + /// + private string _studioPath; + /// + /// Gets the path to the Studio directory + /// + /// The studio path. + public string StudioPath + { + get + { + if (_studioPath == null) + { + _studioPath = Path.Combine(ImagesByNamePath, "Studio"); + if (!Directory.Exists(_studioPath)) + { + Directory.CreateDirectory(_studioPath); + } + } + + return _studioPath; + } + } + + /// + /// The _year path + /// + private string _yearPath; + /// + /// Gets the path to the Year directory + /// + /// The year path. + public string YearPath + { + get + { + if (_yearPath == null) + { + _yearPath = Path.Combine(ImagesByNamePath, "Year"); + if (!Directory.Exists(_yearPath)) + { + Directory.CreateDirectory(_yearPath); + } + } + + return _yearPath; + } + } + + /// + /// The _general path + /// + private string _generalPath; + /// + /// Gets the path to the General IBN directory + /// + /// The general path. + public string GeneralPath + { + get + { + if (_generalPath == null) + { + _generalPath = Path.Combine(ImagesByNamePath, "General"); + if (!Directory.Exists(_generalPath)) + { + Directory.CreateDirectory(_generalPath); + } + } + + return _generalPath; + } + } + + /// + /// The _ratings path + /// + private string _ratingsPath; + /// + /// Gets the path to the Ratings IBN directory + /// + /// The ratings path. + public string RatingsPath + { + get + { + if (_ratingsPath == null) + { + _ratingsPath = Path.Combine(ImagesByNamePath, "Ratings"); + if (!Directory.Exists(_ratingsPath)) + { + Directory.CreateDirectory(_ratingsPath); + } + } + + return _ratingsPath; + } + } + + /// + /// The _user configuration directory path + /// + private string _userConfigurationDirectoryPath; + /// + /// Gets the path to the user configuration directory + /// + /// The user configuration directory path. + public string UserConfigurationDirectoryPath + { + get + { + if (_userConfigurationDirectoryPath == null) + { + _userConfigurationDirectoryPath = Path.Combine(ConfigurationDirectoryPath, "users"); + if (!Directory.Exists(_userConfigurationDirectoryPath)) + { + Directory.CreateDirectory(_userConfigurationDirectoryPath); + } + } + return _userConfigurationDirectoryPath; + } + } + + /// + /// The _f F MPEG stream cache path + /// + private string _fFMpegStreamCachePath; + /// + /// Gets the FF MPEG stream cache path. + /// + /// The FF MPEG stream cache path. + public string FFMpegStreamCachePath + { + get + { + if (_fFMpegStreamCachePath == null) + { + _fFMpegStreamCachePath = Path.Combine(CachePath, "ffmpeg-streams"); + + if (!Directory.Exists(_fFMpegStreamCachePath)) + { + Directory.CreateDirectory(_fFMpegStreamCachePath); + } + } + + return _fFMpegStreamCachePath; + } + } + + /// + /// The _media tools path + /// + private string _mediaToolsPath; + /// + /// Gets the folder path to tools + /// + /// The media tools path. + public string MediaToolsPath + { + get + { + if (_mediaToolsPath == null) + { + _mediaToolsPath = Path.Combine(ProgramDataPath, "MediaTools"); + + if (!Directory.Exists(_mediaToolsPath)) + { + Directory.CreateDirectory(_mediaToolsPath); + } + } + + return _mediaToolsPath; + } + } + } +} diff --git a/MediaBrowser.Server.Implementations/Sqlite/SQLiteDisplayPreferencesRepository.cs b/MediaBrowser.Server.Implementations/Sqlite/SQLiteDisplayPreferencesRepository.cs new file mode 100644 index 000000000..53a467c1a --- /dev/null +++ b/MediaBrowser.Server.Implementations/Sqlite/SQLiteDisplayPreferencesRepository.cs @@ -0,0 +1,174 @@ +using MediaBrowser.Common.Kernel; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Persistence; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Serialization; +using System; +using System.Collections.Generic; +using System.Data; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Server.Implementations.Sqlite +{ + /// + /// Class SQLiteDisplayPreferencesRepository + /// + class SQLiteDisplayPreferencesRepository : SqliteRepository, IDisplayPreferencesRepository + { + /// + /// The repository name + /// + public const string RepositoryName = "SQLite"; + + /// + /// Gets the name of the repository + /// + /// The name. + public string Name + { + get + { + return RepositoryName; + } + } + + /// + /// The _protobuf serializer + /// + private readonly IProtobufSerializer _protobufSerializer; + + /// + /// The _app paths + /// + private readonly IApplicationPaths _appPaths; + + /// + /// Initializes a new instance of the class. + /// + /// The app paths. + /// The protobuf serializer. + /// The logger. + /// protobufSerializer + public SQLiteDisplayPreferencesRepository(IApplicationPaths appPaths, IProtobufSerializer protobufSerializer, ILogger logger) + : base(logger) + { + if (protobufSerializer == null) + { + throw new ArgumentNullException("protobufSerializer"); + } + if (appPaths == null) + { + throw new ArgumentNullException("appPaths"); + } + + _protobufSerializer = protobufSerializer; + _appPaths = appPaths; + } + + /// + /// Opens the connection to the database + /// + /// Task. + public async Task Initialize() + { + var dbFile = Path.Combine(_appPaths.DataPath, "displaypreferences.db"); + + await ConnectToDB(dbFile).ConfigureAwait(false); + + string[] queries = { + + "create table if not exists display_prefs (item_id GUID, user_id GUID, data BLOB)", + "create unique index if not exists idx_display_prefs on display_prefs (item_id, user_id)", + "create table if not exists schema_version (table_name primary key, version)", + //pragmas + "pragma temp_store = memory" + }; + + RunQueries(queries); + } + + /// + /// Save the display preferences associated with an item in the repo + /// + /// The item. + /// The cancellation token. + /// Task. + /// item + public Task SaveDisplayPrefs(Folder item, CancellationToken cancellationToken) + { + if (item == null) + { + throw new ArgumentNullException("item"); + } + + if (cancellationToken == null) + { + throw new ArgumentNullException("cancellationToken"); + } + + cancellationToken.ThrowIfCancellationRequested(); + + return Task.Run(() => + { + var cmd = connection.CreateCommand(); + + cmd.CommandText = "delete from display_prefs where item_id = @guid"; + cmd.AddParam("@guid", item.DisplayPrefsId); + + QueueCommand(cmd); + + if (item.DisplayPrefs != null) + { + foreach (var data in item.DisplayPrefs) + { + cmd = connection.CreateCommand(); + cmd.CommandText = "insert into display_prefs (item_id, user_id, data) values (@1, @2, @3)"; + cmd.AddParam("@1", item.DisplayPrefsId); + cmd.AddParam("@2", data.UserId); + + cmd.AddParam("@3", _protobufSerializer.SerializeToBytes(data)); + + QueueCommand(cmd); + } + } + }); + } + + /// + /// Gets display preferences for an item + /// + /// The item. + /// IEnumerable{DisplayPreferences}. + /// + public IEnumerable RetrieveDisplayPrefs(Folder item) + { + if (item == null) + { + throw new ArgumentNullException("item"); + } + + var cmd = connection.CreateCommand(); + cmd.CommandText = "select data from display_prefs where item_id = @guid"; + var guidParam = cmd.Parameters.Add("@guid", DbType.Guid); + guidParam.Value = item.DisplayPrefsId; + + using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult)) + { + while (reader.Read()) + { + using (var stream = GetStream(reader, 0)) + { + var data = _protobufSerializer.DeserializeFromStream(stream); + if (data != null) + { + yield return data; + } + } + } + } + } + } +} diff --git a/MediaBrowser.Server.Implementations/Sqlite/SQLiteExtensions.cs b/MediaBrowser.Server.Implementations/Sqlite/SQLiteExtensions.cs new file mode 100644 index 000000000..6aed8a352 --- /dev/null +++ b/MediaBrowser.Server.Implementations/Sqlite/SQLiteExtensions.cs @@ -0,0 +1,61 @@ +using System; +using System.Data; +using System.Data.SQLite; + +namespace MediaBrowser.Server.Implementations.Sqlite +{ + /// + /// Class SQLiteExtensions + /// + static class SQLiteExtensions + { + /// + /// Adds the param. + /// + /// The CMD. + /// The param. + /// SQLiteParameter. + /// + public static SQLiteParameter AddParam(this SQLiteCommand cmd, string param) + { + if (string.IsNullOrEmpty(param)) + { + throw new ArgumentNullException(); + } + + var sqliteParam = new SQLiteParameter(param); + cmd.Parameters.Add(sqliteParam); + return sqliteParam; + } + + /// + /// Adds the param. + /// + /// The CMD. + /// The param. + /// The data. + /// SQLiteParameter. + /// + public static SQLiteParameter AddParam(this SQLiteCommand cmd, string param, object data) + { + if (string.IsNullOrEmpty(param)) + { + throw new ArgumentNullException(); + } + + var sqliteParam = AddParam(cmd, param); + sqliteParam.Value = data; + return sqliteParam; + } + + /// + /// Determines whether the specified conn is open. + /// + /// The conn. + /// true if the specified conn is open; otherwise, false. + public static bool IsOpen(this SQLiteConnection conn) + { + return conn.State == ConnectionState.Open; + } + } +} diff --git a/MediaBrowser.Server.Implementations/Sqlite/SQLiteItemRepository.cs b/MediaBrowser.Server.Implementations/Sqlite/SQLiteItemRepository.cs new file mode 100644 index 000000000..034cd2188 --- /dev/null +++ b/MediaBrowser.Server.Implementations/Sqlite/SQLiteItemRepository.cs @@ -0,0 +1,303 @@ +using MediaBrowser.Common.Kernel; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Persistence; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Serialization; +using System; +using System.Collections.Generic; +using System.Data; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Server.Implementations.Sqlite +{ + /// + /// Class SQLiteItemRepository + /// + public class SQLiteItemRepository : SqliteRepository, IItemRepository + { + /// + /// The _type mapper + /// + private readonly TypeMapper _typeMapper = new TypeMapper(); + + /// + /// The repository name + /// + public const string RepositoryName = "SQLite"; + + /// + /// Gets the name of the repository + /// + /// The name. + public string Name + { + get + { + return RepositoryName; + } + } + + /// + /// Gets the json serializer. + /// + /// The json serializer. + private readonly IJsonSerializer _jsonSerializer; + + /// + /// The _app paths + /// + private readonly IApplicationPaths _appPaths; + + /// + /// Initializes a new instance of the class. + /// + /// The app paths. + /// The json serializer. + /// The logger. + /// appPaths + public SQLiteItemRepository(IApplicationPaths appPaths, IJsonSerializer jsonSerializer, ILogger logger) + : base(logger) + { + if (appPaths == null) + { + throw new ArgumentNullException("appPaths"); + } + if (jsonSerializer == null) + { + throw new ArgumentNullException("jsonSerializer"); + } + + _appPaths = appPaths; + _jsonSerializer = jsonSerializer; + } + + /// + /// Opens the connection to the database + /// + /// Task. + public async Task Initialize() + { + var dbFile = Path.Combine(_appPaths.DataPath, "library.db"); + + await ConnectToDB(dbFile).ConfigureAwait(false); + + string[] queries = { + + "create table if not exists items (guid GUID primary key, obj_type, data BLOB)", + "create index if not exists idx_items on items(guid)", + "create table if not exists children (guid GUID, child GUID)", + "create unique index if not exists idx_children on children(guid, child)", + "create table if not exists schema_version (table_name primary key, version)", + //triggers + TriggerSql, + //pragmas + "pragma temp_store = memory" + }; + + RunQueries(queries); + } + + //cascade delete triggers + /// + /// The trigger SQL + /// + protected string TriggerSql = + @"CREATE TRIGGER if not exists delete_item + AFTER DELETE + ON items + FOR EACH ROW + BEGIN + DELETE FROM children WHERE children.guid = old.child; + DELETE FROM children WHERE children.child = old.child; + END"; + + /// + /// Save a standard item in the repo + /// + /// The item. + /// The cancellation token. + /// Task. + /// item + public Task SaveItem(BaseItem item, CancellationToken cancellationToken) + { + if (item == null) + { + throw new ArgumentNullException("item"); + } + + if (cancellationToken == null) + { + throw new ArgumentNullException("cancellationToken"); + } + + cancellationToken.ThrowIfCancellationRequested(); + + return Task.Run(() => + { + var serialized = _jsonSerializer.SerializeToBytes(item); + + cancellationToken.ThrowIfCancellationRequested(); + + var cmd = connection.CreateCommand(); + cmd.CommandText = "replace into items (guid, obj_type, data) values (@1, @2, @3)"; + cmd.AddParam("@1", item.Id); + cmd.AddParam("@2", item.GetType().FullName); + cmd.AddParam("@3", serialized); + QueueCommand(cmd); + }); + } + + /// + /// Retrieve a standard item from the repo + /// + /// The id. + /// BaseItem. + /// + public BaseItem RetrieveItem(Guid id) + { + if (id == Guid.Empty) + { + throw new ArgumentException(); + } + + return RetrieveItemInternal(id); + } + + /// + /// Internal retrieve from items or users table + /// + /// The id. + /// BaseItem. + /// + protected BaseItem RetrieveItemInternal(Guid id) + { + if (id == Guid.Empty) + { + throw new ArgumentException(); + } + + var cmd = connection.CreateCommand(); + cmd.CommandText = "select obj_type,data from items where guid = @guid"; + var guidParam = cmd.Parameters.Add("@guid", DbType.Guid); + guidParam.Value = id; + + using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow)) + { + if (reader.Read()) + { + var type = reader.GetString(0); + using (var stream = GetStream(reader, 1)) + { + var itemType = _typeMapper.GetType(type); + + if (itemType == null) + { + Logger.Error("Cannot find type {0}. Probably belongs to plug-in that is no longer loaded.", type); + return null; + } + + var item = _jsonSerializer.DeserializeFromStream(stream, itemType); + return item as BaseItem; + } + } + } + return null; + } + + /// + /// Retrieve all the children of the given folder + /// + /// The parent. + /// IEnumerable{BaseItem}. + /// + public IEnumerable RetrieveChildren(Folder parent) + { + if (parent == null) + { + throw new ArgumentNullException(); + } + + var cmd = connection.CreateCommand(); + cmd.CommandText = "select obj_type,data from items where guid in (select child from children where guid = @guid)"; + var guidParam = cmd.Parameters.Add("@guid", DbType.Guid); + guidParam.Value = parent.Id; + + using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult)) + { + while (reader.Read()) + { + var type = reader.GetString(0); + + using (var stream = GetStream(reader, 1)) + { + var itemType = _typeMapper.GetType(type); + if (itemType == null) + { + Logger.Error("Cannot find type {0}. Probably belongs to plug-in that is no longer loaded.", type); + continue; + } + var item = _jsonSerializer.DeserializeFromStream(stream, itemType) as BaseItem; + if (item != null) + { + item.Parent = parent; + yield return item; + } + } + } + } + } + + /// + /// Save references to all the children for the given folder + /// (Doesn't actually save the child entities) + /// + /// The id. + /// The children. + /// The cancellation token. + /// Task. + /// id + public Task SaveChildren(Guid id, IEnumerable children, CancellationToken cancellationToken) + { + if (id == Guid.Empty) + { + throw new ArgumentNullException("id"); + } + + if (children == null) + { + throw new ArgumentNullException("children"); + } + + if (cancellationToken == null) + { + throw new ArgumentNullException("cancellationToken"); + } + + cancellationToken.ThrowIfCancellationRequested(); + + return Task.Run(() => + { + var cmd = connection.CreateCommand(); + + cmd.CommandText = "delete from children where guid = @guid"; + cmd.AddParam("@guid", id); + + QueueCommand(cmd); + + foreach (var child in children) + { + var guid = child.Id; + cmd = connection.CreateCommand(); + cmd.AddParam("@guid", id); + cmd.CommandText = "replace into children (guid, child) values (@guid, @child)"; + var childParam = cmd.Parameters.Add("@child", DbType.Guid); + + childParam.Value = guid; + QueueCommand(cmd); + } + }); + } + } +} diff --git a/MediaBrowser.Server.Implementations/Sqlite/SQLiteRepository.cs b/MediaBrowser.Server.Implementations/Sqlite/SQLiteRepository.cs new file mode 100644 index 000000000..dead97959 --- /dev/null +++ b/MediaBrowser.Server.Implementations/Sqlite/SQLiteRepository.cs @@ -0,0 +1,317 @@ +using MediaBrowser.Model.Logging; +using System; +using System.Collections.Concurrent; +using System.Data; +using System.Data.Common; +using System.Data.SQLite; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Server.Implementations.Sqlite +{ + /// + /// Class SqliteRepository + /// + public abstract class SqliteRepository : IDisposable + { + /// + /// The db file name + /// + protected string dbFileName; + /// + /// The connection + /// + protected SQLiteConnection connection; + /// + /// The delayed commands + /// + protected ConcurrentQueue delayedCommands = new ConcurrentQueue(); + /// + /// The flush interval + /// + private const int FlushInterval = 5000; + + /// + /// The flush timer + /// + private Timer FlushTimer; + + /// + /// Gets the logger. + /// + /// The logger. + protected ILogger Logger { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The logger. + /// logger + protected SqliteRepository(ILogger logger) + { + if (logger == null) + { + throw new ArgumentNullException("logger"); + } + + Logger = logger; + } + + /// + /// Connects to DB. + /// + /// The db path. + /// Task{System.Boolean}. + /// dbPath + protected async Task ConnectToDB(string dbPath) + { + if (string.IsNullOrEmpty(dbPath)) + { + throw new ArgumentNullException("dbPath"); + } + + dbFileName = dbPath; + var connectionstr = new SQLiteConnectionStringBuilder + { + PageSize = 4096, + CacheSize = 40960, + SyncMode = SynchronizationModes.Off, + DataSource = dbPath, + JournalMode = SQLiteJournalModeEnum.Memory + }; + + connection = new SQLiteConnection(connectionstr.ConnectionString); + + await connection.OpenAsync().ConfigureAwait(false); + + // Run once + FlushTimer = new Timer(Flush, null, TimeSpan.FromMilliseconds(FlushInterval), TimeSpan.FromMilliseconds(-1)); + } + + /// + /// Runs the queries. + /// + /// The queries. + /// true if XXXX, false otherwise + /// queries + protected void RunQueries(string[] queries) + { + if (queries == null) + { + throw new ArgumentNullException("queries"); + } + + using (var tran = connection.BeginTransaction()) + { + try + { + var cmd = connection.CreateCommand(); + + foreach (var query in queries) + { + cmd.Transaction = tran; + cmd.CommandText = query; + cmd.ExecuteNonQuery(); + } + + tran.Commit(); + } + catch (Exception e) + { + Logger.ErrorException("Error running queries", e); + tran.Rollback(); + throw; + } + } + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged and - optionally - managed resources. + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool dispose) + { + if (dispose) + { + Logger.Info("Disposing " + GetType().Name); + + try + { + // If we're not already flushing, do it now + if (!IsFlushing) + { + Flush(null); + } + + // Don't dispose in the middle of a flush + while (IsFlushing) + { + Thread.Sleep(50); + } + + if (FlushTimer != null) + { + FlushTimer.Dispose(); + FlushTimer = null; + } + + if (connection.IsOpen()) + { + connection.Close(); + } + + connection.Dispose(); + } + catch (Exception ex) + { + Logger.ErrorException("Error disposing database", ex); + } + } + } + + /// + /// Queues the command. + /// + /// The CMD. + /// cmd + protected void QueueCommand(SQLiteCommand cmd) + { + if (cmd == null) + { + throw new ArgumentNullException("cmd"); + } + + delayedCommands.Enqueue(cmd); + } + + /// + /// The is flushing + /// + private bool IsFlushing; + + /// + /// Flushes the specified sender. + /// + /// The sender. + private void Flush(object sender) + { + // Cannot call Count on a ConcurrentQueue since it's an O(n) operation + // Use IsEmpty instead + if (delayedCommands.IsEmpty) + { + FlushTimer.Change(TimeSpan.FromMilliseconds(FlushInterval), TimeSpan.FromMilliseconds(-1)); + return; + } + + if (IsFlushing) + { + return; + } + + IsFlushing = true; + var numCommands = 0; + + using (var tran = connection.BeginTransaction()) + { + try + { + while (!delayedCommands.IsEmpty) + { + SQLiteCommand command; + + delayedCommands.TryDequeue(out command); + + command.Connection = connection; + command.Transaction = tran; + + command.ExecuteNonQuery(); + numCommands++; + } + + tran.Commit(); + } + catch (Exception e) + { + Logger.ErrorException("Failed to commit transaction.", e); + tran.Rollback(); + } + } + + Logger.Info("SQL Delayed writer executed " + numCommands + " commands"); + + FlushTimer.Change(TimeSpan.FromMilliseconds(FlushInterval), TimeSpan.FromMilliseconds(-1)); + IsFlushing = false; + } + + /// + /// Executes the command. + /// + /// The CMD. + /// Task. + /// cmd + public async Task ExecuteCommand(DbCommand cmd) + { + if (cmd == null) + { + throw new ArgumentNullException("cmd"); + } + + using (var tran = connection.BeginTransaction()) + { + try + { + cmd.Connection = connection; + cmd.Transaction = tran; + + await cmd.ExecuteNonQueryAsync().ConfigureAwait(false); + + tran.Commit(); + } + catch (Exception e) + { + Logger.ErrorException("Failed to commit transaction.", e); + tran.Rollback(); + } + } + } + + /// + /// Gets a stream from a DataReader at a given ordinal + /// + /// The reader. + /// The ordinal. + /// Stream. + /// reader + protected static Stream GetStream(IDataReader reader, int ordinal) + { + if (reader == null) + { + throw new ArgumentNullException("reader"); + } + + var memoryStream = new MemoryStream(); + var num = 0L; + var array = new byte[4096]; + long bytes; + do + { + bytes = reader.GetBytes(ordinal, num, array, 0, array.Length); + memoryStream.Write(array, 0, (int)bytes); + num += bytes; + } + while (bytes > 0L); + memoryStream.Position = 0; + return memoryStream; + } + } +} diff --git a/MediaBrowser.Server.Implementations/Sqlite/SQLiteUserDataRepository.cs b/MediaBrowser.Server.Implementations/Sqlite/SQLiteUserDataRepository.cs new file mode 100644 index 000000000..5787a614a --- /dev/null +++ b/MediaBrowser.Server.Implementations/Sqlite/SQLiteUserDataRepository.cs @@ -0,0 +1,173 @@ +using MediaBrowser.Common.Kernel; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Persistence; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Serialization; +using System; +using System.Collections.Generic; +using System.Data; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Server.Implementations.Sqlite +{ + /// + /// Class SQLiteUserDataRepository + /// + public class SQLiteUserDataRepository : SqliteRepository, IUserDataRepository + { + /// + /// The repository name + /// + public const string RepositoryName = "SQLite"; + + /// + /// Gets the name of the repository + /// + /// The name. + public string Name + { + get + { + return RepositoryName; + } + } + + /// + /// The _protobuf serializer + /// + private readonly IProtobufSerializer _protobufSerializer; + + /// + /// The _app paths + /// + private readonly IApplicationPaths _appPaths; + + /// + /// Initializes a new instance of the class. + /// + /// The app paths. + /// The protobuf serializer. + /// The logger. + /// protobufSerializer + public SQLiteUserDataRepository(IApplicationPaths appPaths, IProtobufSerializer protobufSerializer, ILogger logger) + : base(logger) + { + if (protobufSerializer == null) + { + throw new ArgumentNullException("protobufSerializer"); + } + if (appPaths == null) + { + throw new ArgumentNullException("appPaths"); + } + + _protobufSerializer = protobufSerializer; + _appPaths = appPaths; + } + + /// + /// Opens the connection to the database + /// + /// Task. + public async Task Initialize() + { + var dbFile = Path.Combine(_appPaths.DataPath, "userdata.db"); + + await ConnectToDB(dbFile).ConfigureAwait(false); + + string[] queries = { + + "create table if not exists user_data (item_id GUID, user_id GUID, data BLOB)", + "create unique index if not exists idx_user_data on user_data (item_id, user_id)", + "create table if not exists schema_version (table_name primary key, version)", + //pragmas + "pragma temp_store = memory" + }; + + RunQueries(queries); + } + + /// + /// Save the user specific data associated with an item in the repo + /// + /// The item. + /// The cancellation token. + /// Task. + /// item + public Task SaveUserData(BaseItem item, CancellationToken cancellationToken) + { + if (item == null) + { + throw new ArgumentNullException("item"); + } + + if (cancellationToken == null) + { + throw new ArgumentNullException("cancellationToken"); + } + + return Task.Run(() => + { + cancellationToken.ThrowIfCancellationRequested(); + + var cmd = connection.CreateCommand(); + + cmd.CommandText = "delete from user_data where item_id = @guid"; + cmd.AddParam("@guid", item.UserDataId); + + QueueCommand(cmd); + + if (item.UserData != null) + { + foreach (var data in item.UserData) + { + cmd = connection.CreateCommand(); + cmd.CommandText = "insert into user_data (item_id, user_id, data) values (@1, @2, @3)"; + cmd.AddParam("@1", item.UserDataId); + cmd.AddParam("@2", data.UserId); + + cmd.AddParam("@3", _protobufSerializer.SerializeToBytes(data)); + + QueueCommand(cmd); + } + } + }); + } + + /// + /// Gets user data for an item + /// + /// The item. + /// IEnumerable{UserItemData}. + /// item + public IEnumerable RetrieveUserData(BaseItem item) + { + if (item == null) + { + throw new ArgumentNullException("item"); + } + + var cmd = connection.CreateCommand(); + cmd.CommandText = "select data from user_data where item_id = @guid"; + var guidParam = cmd.Parameters.Add("@guid", DbType.Guid); + guidParam.Value = item.UserDataId; + + using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult)) + { + while (reader.Read()) + { + using (var stream = GetStream(reader, 0)) + { + var data = _protobufSerializer.DeserializeFromStream(stream); + if (data != null) + { + yield return data; + } + } + } + } + } + } +} diff --git a/MediaBrowser.Server.Implementations/Sqlite/SQLiteUserRepository.cs b/MediaBrowser.Server.Implementations/Sqlite/SQLiteUserRepository.cs new file mode 100644 index 000000000..1e6f370eb --- /dev/null +++ b/MediaBrowser.Server.Implementations/Sqlite/SQLiteUserRepository.cs @@ -0,0 +1,182 @@ +using MediaBrowser.Common.Kernel; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Persistence; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Serialization; +using System; +using System.Collections.Generic; +using System.Data; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Server.Implementations.Sqlite +{ + /// + /// Class SQLiteUserRepository + /// + public class SQLiteUserRepository : SqliteRepository, IUserRepository + { + /// + /// The repository name + /// + public const string RepositoryName = "SQLite"; + + /// + /// Gets the name of the repository + /// + /// The name. + public string Name + { + get + { + return RepositoryName; + } + } + + /// + /// Gets the json serializer. + /// + /// The json serializer. + private readonly IJsonSerializer _jsonSerializer; + + /// + /// The _app paths + /// + private readonly IApplicationPaths _appPaths; + + /// + /// Initializes a new instance of the class. + /// + /// The app paths. + /// The json serializer. + /// The logger. + /// appPaths + public SQLiteUserRepository(IApplicationPaths appPaths, IJsonSerializer jsonSerializer, ILogger logger) + : base(logger) + { + if (appPaths == null) + { + throw new ArgumentNullException("appPaths"); + } + if (jsonSerializer == null) + { + throw new ArgumentNullException("jsonSerializer"); + } + + _appPaths = appPaths; + _jsonSerializer = jsonSerializer; + } + + /// + /// Opens the connection to the database + /// + /// Task. + public async Task Initialize() + { + var dbFile = Path.Combine(_appPaths.DataPath, "users.db"); + + await ConnectToDB(dbFile).ConfigureAwait(false); + + string[] queries = { + + "create table if not exists users (guid GUID primary key, data BLOB)", + "create index if not exists idx_users on users(guid)", + "create table if not exists schema_version (table_name primary key, version)", + //pragmas + "pragma temp_store = memory" + }; + + RunQueries(queries); + } + + /// + /// Save a user in the repo + /// + /// The user. + /// The cancellation token. + /// Task. + /// user + public Task SaveUser(User user, CancellationToken cancellationToken) + { + if (user == null) + { + throw new ArgumentNullException("user"); + } + + if (cancellationToken == null) + { + throw new ArgumentNullException("cancellationToken"); + } + + return Task.Run(() => + { + cancellationToken.ThrowIfCancellationRequested(); + + var serialized = _jsonSerializer.SerializeToBytes(user); + + cancellationToken.ThrowIfCancellationRequested(); + + var cmd = connection.CreateCommand(); + cmd.CommandText = "replace into users (guid, data) values (@1, @2)"; + cmd.AddParam("@1", user.Id); + cmd.AddParam("@2", serialized); + QueueCommand(cmd); + }); + } + + /// + /// Retrieve all users from the database + /// + /// IEnumerable{User}. + public IEnumerable RetrieveAllUsers() + { + var cmd = connection.CreateCommand(); + cmd.CommandText = "select data from users"; + + using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult)) + { + while (reader.Read()) + { + using (var stream = GetStream(reader, 0)) + { + var user = _jsonSerializer.DeserializeFromStream(stream); + yield return user; + } + } + } + } + + /// + /// Deletes the user. + /// + /// The user. + /// The cancellation token. + /// Task. + /// user + public Task DeleteUser(User user, CancellationToken cancellationToken) + { + if (user == null) + { + throw new ArgumentNullException("user"); + } + + if (cancellationToken == null) + { + throw new ArgumentNullException("cancellationToken"); + } + + return Task.Run(() => + { + cancellationToken.ThrowIfCancellationRequested(); + + var cmd = connection.CreateCommand(); + cmd.CommandText = "delete from users where guid=@guid"; + var guidParam = cmd.Parameters.Add("@guid", DbType.Guid); + guidParam.Value = user.Id; + + return ExecuteCommand(cmd); + }); + } + } +} diff --git a/MediaBrowser.Server.Implementations/WorldWeatherOnline/WeatherProvider.cs b/MediaBrowser.Server.Implementations/WorldWeatherOnline/WeatherProvider.cs new file mode 100644 index 000000000..1b75a58b8 --- /dev/null +++ b/MediaBrowser.Server.Implementations/WorldWeatherOnline/WeatherProvider.cs @@ -0,0 +1,345 @@ +using MediaBrowser.Controller; +using MediaBrowser.Controller.Weather; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Serialization; +using MediaBrowser.Model.Weather; +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Server.Implementations.WorldWeatherOnline +{ + /// + /// Based on http://www.worldweatheronline.com/free-weather-feed.aspx + /// The classes in this file are a reproduction of the json output, which will then be converted to our weather model classes + /// + public class WeatherProvider : IWeatherProvider + { + /// + /// Gets or sets the logger. + /// + /// The logger. + private ILogger Logger { get; set; } + + /// + /// Gets the json serializer. + /// + /// The json serializer. + protected IJsonSerializer JsonSerializer { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The json serializer. + /// The logger. + /// logger + public WeatherProvider(IJsonSerializer jsonSerializer, ILogger logger) + { + if (logger == null) + { + throw new ArgumentNullException("logger"); + } + + if (jsonSerializer == null) + { + throw new ArgumentNullException("jsonSerializer"); + } + + JsonSerializer = jsonSerializer; + Logger = logger; + } + + /// + /// The _weather semaphore + /// + private readonly SemaphoreSlim _weatherSemaphore = new SemaphoreSlim(10, 10); + + /// + /// Gets the weather info async. + /// + /// The location. + /// The cancellation token. + /// Task{WeatherInfo}. + /// location + public async Task GetWeatherInfoAsync(string location, CancellationToken cancellationToken) + { + if (string.IsNullOrWhiteSpace(location)) + { + throw new ArgumentNullException("location"); + } + + if (cancellationToken == null) + { + throw new ArgumentNullException("cancellationToken"); + } + + const int numDays = 5; + const string apiKey = "24902f60f1231941120109"; + + var url = "http://free.worldweatheronline.com/feed/weather.ashx?q=" + location + "&format=json&num_of_days=" + numDays + "&key=" + apiKey; + + Logger.Info("Accessing weather from " + url); + + using (var stream = await Kernel.Instance.HttpManager.Get(url, _weatherSemaphore, cancellationToken).ConfigureAwait(false)) + { + var data = JsonSerializer.DeserializeFromStream(stream).data; + + return GetWeatherInfo(data); + } + } + + /// + /// Converst the json output to our WeatherInfo model class + /// + /// The data. + /// WeatherInfo. + private WeatherInfo GetWeatherInfo(WeatherData data) + { + var info = new WeatherInfo(); + + if (data.current_condition != null) + { + var condition = data.current_condition.FirstOrDefault(); + + if (condition != null) + { + info.CurrentWeather = condition.ToWeatherStatus(); + } + } + + if (data.weather != null) + { + info.Forecasts = data.weather.Select(w => w.ToWeatherForecast()).ToArray(); + } + + return info; + } + } + + /// + /// Class WeatherResult + /// + class WeatherResult + { + /// + /// Gets or sets the data. + /// + /// The data. + public WeatherData data { get; set; } + } + + /// + /// Class WeatherData + /// + public class WeatherData + { + /// + /// Gets or sets the current_condition. + /// + /// The current_condition. + public WeatherCondition[] current_condition { get; set; } + /// + /// Gets or sets the weather. + /// + /// The weather. + public DailyWeatherInfo[] weather { get; set; } + } + + /// + /// Class WeatherCondition + /// + public class WeatherCondition + { + /// + /// Gets or sets the temp_ C. + /// + /// The temp_ C. + public string temp_C { get; set; } + /// + /// Gets or sets the temp_ F. + /// + /// The temp_ F. + public string temp_F { get; set; } + /// + /// Gets or sets the humidity. + /// + /// The humidity. + public string humidity { get; set; } + /// + /// Gets or sets the weather code. + /// + /// The weather code. + public string weatherCode { get; set; } + + /// + /// To the weather status. + /// + /// WeatherStatus. + public WeatherStatus ToWeatherStatus() + { + return new WeatherStatus + { + TemperatureCelsius = int.Parse(temp_C), + TemperatureFahrenheit = int.Parse(temp_F), + Humidity = int.Parse(humidity), + Condition = DailyWeatherInfo.GetCondition(weatherCode) + }; + } + } + + /// + /// Class DailyWeatherInfo + /// + public class DailyWeatherInfo + { + /// + /// Gets or sets the date. + /// + /// The date. + public string date { get; set; } + /// + /// Gets or sets the precip MM. + /// + /// The precip MM. + public string precipMM { get; set; } + /// + /// Gets or sets the temp max C. + /// + /// The temp max C. + public string tempMaxC { get; set; } + /// + /// Gets or sets the temp max F. + /// + /// The temp max F. + public string tempMaxF { get; set; } + /// + /// Gets or sets the temp min C. + /// + /// The temp min C. + public string tempMinC { get; set; } + /// + /// Gets or sets the temp min F. + /// + /// The temp min F. + public string tempMinF { get; set; } + /// + /// Gets or sets the weather code. + /// + /// The weather code. + public string weatherCode { get; set; } + /// + /// Gets or sets the winddir16 point. + /// + /// The winddir16 point. + public string winddir16Point { get; set; } + /// + /// Gets or sets the winddir degree. + /// + /// The winddir degree. + public string winddirDegree { get; set; } + /// + /// Gets or sets the winddirection. + /// + /// The winddirection. + public string winddirection { get; set; } + /// + /// Gets or sets the windspeed KMPH. + /// + /// The windspeed KMPH. + public string windspeedKmph { get; set; } + /// + /// Gets or sets the windspeed miles. + /// + /// The windspeed miles. + public string windspeedMiles { get; set; } + + /// + /// To the weather forecast. + /// + /// WeatherForecast. + public WeatherForecast ToWeatherForecast() + { + return new WeatherForecast + { + Date = DateTime.Parse(date), + HighTemperatureCelsius = int.Parse(tempMaxC), + HighTemperatureFahrenheit = int.Parse(tempMaxF), + LowTemperatureCelsius = int.Parse(tempMinC), + LowTemperatureFahrenheit = int.Parse(tempMinF), + Condition = GetCondition(weatherCode) + }; + } + + /// + /// Gets the condition. + /// + /// The weather code. + /// WeatherConditions. + public static WeatherConditions GetCondition(string weatherCode) + { + switch (weatherCode) + { + case "362": + case "365": + case "320": + case "317": + case "182": + return WeatherConditions.Sleet; + case "338": + case "335": + case "332": + case "329": + case "326": + case "323": + case "377": + case "374": + case "371": + case "368": + case "395": + case "392": + case "350": + case "227": + case "179": + return WeatherConditions.Snow; + case "314": + case "311": + case "308": + case "305": + case "302": + case "299": + case "296": + case "293": + case "284": + case "281": + case "266": + case "263": + case "359": + case "356": + case "353": + case "185": + case "176": + return WeatherConditions.Rain; + case "260": + case "248": + return WeatherConditions.Fog; + case "389": + case "386": + case "200": + return WeatherConditions.Thunderstorm; + case "230": + return WeatherConditions.Blizzard; + case "143": + return WeatherConditions.Mist; + case "122": + return WeatherConditions.Overcast; + case "119": + return WeatherConditions.Cloudy; + case "115": + return WeatherConditions.PartlyCloudy; + default: + return WeatherConditions.Sunny; + } + } + } +} \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/packages.config b/MediaBrowser.Server.Implementations/packages.config new file mode 100644 index 000000000..106618814 --- /dev/null +++ b/MediaBrowser.Server.Implementations/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/MediaBrowser.Server.Sqlite/MediaBrowser.Server.Sqlite.csproj b/MediaBrowser.Server.Sqlite/MediaBrowser.Server.Sqlite.csproj deleted file mode 100644 index 78ea39dd8..000000000 --- a/MediaBrowser.Server.Sqlite/MediaBrowser.Server.Sqlite.csproj +++ /dev/null @@ -1,98 +0,0 @@ - - - - - Debug - AnyCPU - {8649ED6B-8504-4D00-BFA5-B8C73CC744DB} - Library - Properties - MediaBrowser.Server.Sqlite - MediaBrowser.Server.Sqlite - v4.5 - 512 - ..\ - true - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - Always - - - - - - ..\packages\System.Data.SQLite.1.0.84.0\lib\net45\System.Data.SQLite.dll - - - ..\packages\System.Data.SQLite.1.0.84.0\lib\net45\System.Data.SQLite.Linq.dll - - - - - - - Properties\SharedVersion.cs - - - - - - - - - - - - Always - - - Always - - - - - - - - {9142eefa-7570-41e1-bfcc-468bb571af2f} - MediaBrowser.Common - - - {17e1f4e6-8abd-4fe5-9ecf-43d4b6087ba2} - MediaBrowser.Controller - - - {7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b} - MediaBrowser.Model - - - - - - xcopy "$(TargetPath)" "$(SolutionDir)\MediaBrowser.ServerApplication\CorePlugins\" /y - - - \ No newline at end of file diff --git a/MediaBrowser.Server.Sqlite/Properties/AssemblyInfo.cs b/MediaBrowser.Server.Sqlite/Properties/AssemblyInfo.cs deleted file mode 100644 index 7c69b5544..000000000 --- a/MediaBrowser.Server.Sqlite/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("MediaBrowser.Server.Sqlite")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("MediaBrowser.Server.Sqlite")] -[assembly: AssemblyCopyright("Copyright © 2013")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("f46c9f4b-24ed-49e1-be19-4b6242dd8382")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// \ No newline at end of file diff --git a/MediaBrowser.Server.Sqlite/SQLiteDisplayPreferencesRepository.cs b/MediaBrowser.Server.Sqlite/SQLiteDisplayPreferencesRepository.cs deleted file mode 100644 index 80d389c94..000000000 --- a/MediaBrowser.Server.Sqlite/SQLiteDisplayPreferencesRepository.cs +++ /dev/null @@ -1,149 +0,0 @@ -using MediaBrowser.Controller; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Persistence; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using System; -using System.Collections.Generic; -using System.Data; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Server.Sqlite -{ - /// - /// Class SQLiteDisplayPreferencesRepository - /// - class SQLiteDisplayPreferencesRepository : SqliteRepository, IDisplayPreferencesRepository - { - /// - /// The repository name - /// - public const string RepositoryName = "SQLite"; - - /// - /// Gets the name of the repository - /// - /// The name. - public string Name - { - get - { - return RepositoryName; - } - } - - /// - /// Initializes a new instance of the class. - /// - /// The logger. - public SQLiteDisplayPreferencesRepository(ILogger logger) - : base(logger) - { - } - - /// - /// Opens the connection to the database - /// - /// Task. - public async Task Initialize() - { - var dbFile = Path.Combine(Kernel.Instance.ApplicationPaths.DataPath, "displaypreferences.db"); - - await ConnectToDB(dbFile).ConfigureAwait(false); - - string[] queries = { - - "create table if not exists display_prefs (item_id GUID, user_id GUID, data BLOB)", - "create unique index if not exists idx_display_prefs on display_prefs (item_id, user_id)", - "create table if not exists schema_version (table_name primary key, version)", - //pragmas - "pragma temp_store = memory" - }; - - RunQueries(queries); - } - - /// - /// Save the display preferences associated with an item in the repo - /// - /// The item. - /// The cancellation token. - /// Task. - /// item - public Task SaveDisplayPrefs(Folder item, CancellationToken cancellationToken) - { - if (item == null) - { - throw new ArgumentNullException("item"); - } - - if (cancellationToken == null) - { - throw new ArgumentNullException("cancellationToken"); - } - - cancellationToken.ThrowIfCancellationRequested(); - - return Task.Run(() => - { - var cmd = connection.CreateCommand(); - - cmd.CommandText = "delete from display_prefs where item_id = @guid"; - cmd.AddParam("@guid", item.DisplayPrefsId); - - QueueCommand(cmd); - - if (item.DisplayPrefs != null) - { - foreach (var data in item.DisplayPrefs) - { - cmd = connection.CreateCommand(); - cmd.CommandText = "insert into display_prefs (item_id, user_id, data) values (@1, @2, @3)"; - cmd.AddParam("@1", item.DisplayPrefsId); - cmd.AddParam("@2", data.UserId); - - cmd.AddParam("@3", Kernel.Instance.ProtobufSerializer.SerializeToBytes(data)); - - QueueCommand(cmd); - } - } - }); - } - - /// - /// Gets display preferences for an item - /// - /// The item. - /// IEnumerable{DisplayPreferences}. - /// - public IEnumerable RetrieveDisplayPrefs(Folder item) - { - if (item == null) - { - throw new ArgumentNullException("item"); - } - - var cmd = connection.CreateCommand(); - cmd.CommandText = "select data from display_prefs where item_id = @guid"; - var guidParam = cmd.Parameters.Add("@guid", DbType.Guid); - guidParam.Value = item.DisplayPrefsId; - - using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult)) - { - while (reader.Read()) - { - using (var stream = GetStream(reader, 0)) - { - var data = Kernel.Instance.ProtobufSerializer.DeserializeFromStream(stream); - if (data != null) - { - yield return data; - } - } - } - } - } - } -} diff --git a/MediaBrowser.Server.Sqlite/SQLiteExtensions.cs b/MediaBrowser.Server.Sqlite/SQLiteExtensions.cs deleted file mode 100644 index f9f79a8b7..000000000 --- a/MediaBrowser.Server.Sqlite/SQLiteExtensions.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using System.Data; -using System.Data.SQLite; - -namespace MediaBrowser.Server.Sqlite -{ - /// - /// Class SQLiteExtensions - /// - static class SQLiteExtensions - { - /// - /// Adds the param. - /// - /// The CMD. - /// The param. - /// SQLiteParameter. - /// - public static SQLiteParameter AddParam(this SQLiteCommand cmd, string param) - { - if (string.IsNullOrEmpty(param)) - { - throw new ArgumentNullException(); - } - - var sqliteParam = new SQLiteParameter(param); - cmd.Parameters.Add(sqliteParam); - return sqliteParam; - } - - /// - /// Adds the param. - /// - /// The CMD. - /// The param. - /// The data. - /// SQLiteParameter. - /// - public static SQLiteParameter AddParam(this SQLiteCommand cmd, string param, object data) - { - if (string.IsNullOrEmpty(param)) - { - throw new ArgumentNullException(); - } - - var sqliteParam = AddParam(cmd, param); - sqliteParam.Value = data; - return sqliteParam; - } - - /// - /// Determines whether the specified conn is open. - /// - /// The conn. - /// true if the specified conn is open; otherwise, false. - public static bool IsOpen(this SQLiteConnection conn) - { - return conn.State == ConnectionState.Open; - } - } -} diff --git a/MediaBrowser.Server.Sqlite/SQLiteItemRepository.cs b/MediaBrowser.Server.Sqlite/SQLiteItemRepository.cs deleted file mode 100644 index 58a924bc4..000000000 --- a/MediaBrowser.Server.Sqlite/SQLiteItemRepository.cs +++ /dev/null @@ -1,278 +0,0 @@ -using MediaBrowser.Common.Serialization; -using MediaBrowser.Controller; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Persistence; -using MediaBrowser.Model.Logging; -using System; -using System.Collections.Generic; -using System.Data; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Server.Sqlite -{ - /// - /// Class SQLiteItemRepository - /// - public class SQLiteItemRepository : SqliteRepository, IItemRepository - { - /// - /// The _type mapper - /// - private readonly TypeMapper _typeMapper = new TypeMapper(); - - /// - /// The repository name - /// - public const string RepositoryName = "SQLite"; - - /// - /// Gets the name of the repository - /// - /// The name. - public string Name - { - get - { - return RepositoryName; - } - } - - /// - /// Initializes a new instance of the class. - /// - /// The logger. - public SQLiteItemRepository(ILogger logger) - : base(logger) - { - } - - /// - /// Opens the connection to the database - /// - /// Task. - public async Task Initialize() - { - var dbFile = Path.Combine(Kernel.Instance.ApplicationPaths.DataPath, "library.db"); - - await ConnectToDB(dbFile).ConfigureAwait(false); - - string[] queries = { - - "create table if not exists items (guid GUID primary key, obj_type, data BLOB)", - "create index if not exists idx_items on items(guid)", - "create table if not exists children (guid GUID, child GUID)", - "create unique index if not exists idx_children on children(guid, child)", - "create table if not exists schema_version (table_name primary key, version)", - //triggers - TriggerSql, - //pragmas - "pragma temp_store = memory" - }; - - RunQueries(queries); - } - - //cascade delete triggers - /// - /// The trigger SQL - /// - protected string TriggerSql = - @"CREATE TRIGGER if not exists delete_item - AFTER DELETE - ON items - FOR EACH ROW - BEGIN - DELETE FROM children WHERE children.guid = old.child; - DELETE FROM children WHERE children.child = old.child; - END"; - - /// - /// Save a standard item in the repo - /// - /// The item. - /// The cancellation token. - /// Task. - /// item - public Task SaveItem(BaseItem item, CancellationToken cancellationToken) - { - if (item == null) - { - throw new ArgumentNullException("item"); - } - - if (cancellationToken == null) - { - throw new ArgumentNullException("cancellationToken"); - } - - cancellationToken.ThrowIfCancellationRequested(); - - return Task.Run(() => - { - var serialized = JsonSerializer.SerializeToBytes(item); - - cancellationToken.ThrowIfCancellationRequested(); - - var cmd = connection.CreateCommand(); - cmd.CommandText = "replace into items (guid, obj_type, data) values (@1, @2, @3)"; - cmd.AddParam("@1", item.Id); - cmd.AddParam("@2", item.GetType().FullName); - cmd.AddParam("@3", serialized); - QueueCommand(cmd); - }); - } - - /// - /// Retrieve a standard item from the repo - /// - /// The id. - /// BaseItem. - /// - public BaseItem RetrieveItem(Guid id) - { - if (id == Guid.Empty) - { - throw new ArgumentException(); - } - - return RetrieveItemInternal(id); - } - - /// - /// Internal retrieve from items or users table - /// - /// The id. - /// BaseItem. - /// - protected BaseItem RetrieveItemInternal(Guid id) - { - if (id == Guid.Empty) - { - throw new ArgumentException(); - } - - var cmd = connection.CreateCommand(); - cmd.CommandText = "select obj_type,data from items where guid = @guid"; - var guidParam = cmd.Parameters.Add("@guid", DbType.Guid); - guidParam.Value = id; - - using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow)) - { - if (reader.Read()) - { - var type = reader.GetString(0); - using (var stream = GetStream(reader, 1)) - { - var itemType = _typeMapper.GetType(type); - - if (itemType == null) - { - Logger.Error("Cannot find type {0}. Probably belongs to plug-in that is no longer loaded.", type); - return null; - } - - var item = JsonSerializer.DeserializeFromStream(stream, itemType); - return item as BaseItem; - } - } - } - return null; - } - - /// - /// Retrieve all the children of the given folder - /// - /// The parent. - /// IEnumerable{BaseItem}. - /// - public IEnumerable RetrieveChildren(Folder parent) - { - if (parent == null) - { - throw new ArgumentNullException(); - } - - var cmd = connection.CreateCommand(); - cmd.CommandText = "select obj_type,data from items where guid in (select child from children where guid = @guid)"; - var guidParam = cmd.Parameters.Add("@guid", DbType.Guid); - guidParam.Value = parent.Id; - - using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult)) - { - while (reader.Read()) - { - var type = reader.GetString(0); - - using (var stream = GetStream(reader, 1)) - { - var itemType = _typeMapper.GetType(type); - if (itemType == null) - { - Logger.Error("Cannot find type {0}. Probably belongs to plug-in that is no longer loaded.", type); - continue; - } - var item = JsonSerializer.DeserializeFromStream(stream, itemType) as BaseItem; - if (item != null) - { - item.Parent = parent; - yield return item; - } - } - } - } - } - - /// - /// Save references to all the children for the given folder - /// (Doesn't actually save the child entities) - /// - /// The id. - /// The children. - /// The cancellation token. - /// Task. - /// id - public Task SaveChildren(Guid id, IEnumerable children, CancellationToken cancellationToken) - { - if (id == Guid.Empty) - { - throw new ArgumentNullException("id"); - } - - if (children == null) - { - throw new ArgumentNullException("children"); - } - - if (cancellationToken == null) - { - throw new ArgumentNullException("cancellationToken"); - } - - cancellationToken.ThrowIfCancellationRequested(); - - return Task.Run(() => - { - var cmd = connection.CreateCommand(); - - cmd.CommandText = "delete from children where guid = @guid"; - cmd.AddParam("@guid", id); - - QueueCommand(cmd); - - foreach (var child in children) - { - var guid = child.Id; - cmd = connection.CreateCommand(); - cmd.AddParam("@guid", id); - cmd.CommandText = "replace into children (guid, child) values (@guid, @child)"; - var childParam = cmd.Parameters.Add("@child", DbType.Guid); - - childParam.Value = guid; - QueueCommand(cmd); - } - }); - } - } -} diff --git a/MediaBrowser.Server.Sqlite/SQLiteRepository.cs b/MediaBrowser.Server.Sqlite/SQLiteRepository.cs deleted file mode 100644 index 1caae970e..000000000 --- a/MediaBrowser.Server.Sqlite/SQLiteRepository.cs +++ /dev/null @@ -1,317 +0,0 @@ -using MediaBrowser.Model.Logging; -using System; -using System.Collections.Concurrent; -using System.Data; -using System.Data.Common; -using System.Data.SQLite; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Server.Sqlite -{ - /// - /// Class SqliteRepository - /// - public abstract class SqliteRepository : IDisposable - { - /// - /// The db file name - /// - protected string dbFileName; - /// - /// The connection - /// - protected SQLiteConnection connection; - /// - /// The delayed commands - /// - protected ConcurrentQueue delayedCommands = new ConcurrentQueue(); - /// - /// The flush interval - /// - private const int FlushInterval = 5000; - - /// - /// The flush timer - /// - private Timer FlushTimer; - - /// - /// Gets the logger. - /// - /// The logger. - protected ILogger Logger { get; private set; } - - /// - /// Initializes a new instance of the class. - /// - /// The logger. - /// logger - protected SqliteRepository(ILogger logger) - { - if (logger == null) - { - throw new ArgumentNullException("logger"); - } - - Logger = logger; - } - - /// - /// Connects to DB. - /// - /// The db path. - /// Task{System.Boolean}. - /// dbPath - protected async Task ConnectToDB(string dbPath) - { - if (string.IsNullOrEmpty(dbPath)) - { - throw new ArgumentNullException("dbPath"); - } - - dbFileName = dbPath; - var connectionstr = new SQLiteConnectionStringBuilder - { - PageSize = 4096, - CacheSize = 40960, - SyncMode = SynchronizationModes.Off, - DataSource = dbPath, - JournalMode = SQLiteJournalModeEnum.Memory - }; - - connection = new SQLiteConnection(connectionstr.ConnectionString); - - await connection.OpenAsync().ConfigureAwait(false); - - // Run once - FlushTimer = new Timer(Flush, null, TimeSpan.FromMilliseconds(FlushInterval), TimeSpan.FromMilliseconds(-1)); - } - - /// - /// Runs the queries. - /// - /// The queries. - /// true if XXXX, false otherwise - /// queries - protected void RunQueries(string[] queries) - { - if (queries == null) - { - throw new ArgumentNullException("queries"); - } - - using (var tran = connection.BeginTransaction()) - { - try - { - var cmd = connection.CreateCommand(); - - foreach (var query in queries) - { - cmd.Transaction = tran; - cmd.CommandText = query; - cmd.ExecuteNonQuery(); - } - - tran.Commit(); - } - catch (Exception e) - { - Logger.ErrorException("Error running queries", e); - tran.Rollback(); - throw; - } - } - } - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Releases unmanaged and - optionally - managed resources. - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected virtual void Dispose(bool dispose) - { - if (dispose) - { - Logger.Info("Disposing " + GetType().Name); - - try - { - // If we're not already flushing, do it now - if (!IsFlushing) - { - Flush(null); - } - - // Don't dispose in the middle of a flush - while (IsFlushing) - { - Thread.Sleep(50); - } - - if (FlushTimer != null) - { - FlushTimer.Dispose(); - FlushTimer = null; - } - - if (connection.IsOpen()) - { - connection.Close(); - } - - connection.Dispose(); - } - catch (Exception ex) - { - Logger.ErrorException("Error disposing database", ex); - } - } - } - - /// - /// Queues the command. - /// - /// The CMD. - /// cmd - protected void QueueCommand(SQLiteCommand cmd) - { - if (cmd == null) - { - throw new ArgumentNullException("cmd"); - } - - delayedCommands.Enqueue(cmd); - } - - /// - /// The is flushing - /// - private bool IsFlushing; - - /// - /// Flushes the specified sender. - /// - /// The sender. - private void Flush(object sender) - { - // Cannot call Count on a ConcurrentQueue since it's an O(n) operation - // Use IsEmpty instead - if (delayedCommands.IsEmpty) - { - FlushTimer.Change(TimeSpan.FromMilliseconds(FlushInterval), TimeSpan.FromMilliseconds(-1)); - return; - } - - if (IsFlushing) - { - return; - } - - IsFlushing = true; - var numCommands = 0; - - using (var tran = connection.BeginTransaction()) - { - try - { - while (!delayedCommands.IsEmpty) - { - SQLiteCommand command; - - delayedCommands.TryDequeue(out command); - - command.Connection = connection; - command.Transaction = tran; - - command.ExecuteNonQuery(); - numCommands++; - } - - tran.Commit(); - } - catch (Exception e) - { - Logger.ErrorException("Failed to commit transaction.", e); - tran.Rollback(); - } - } - - Logger.Info("SQL Delayed writer executed " + numCommands + " commands"); - - FlushTimer.Change(TimeSpan.FromMilliseconds(FlushInterval), TimeSpan.FromMilliseconds(-1)); - IsFlushing = false; - } - - /// - /// Executes the command. - /// - /// The CMD. - /// Task. - /// cmd - public async Task ExecuteCommand(DbCommand cmd) - { - if (cmd == null) - { - throw new ArgumentNullException("cmd"); - } - - using (var tran = connection.BeginTransaction()) - { - try - { - cmd.Connection = connection; - cmd.Transaction = tran; - - await cmd.ExecuteNonQueryAsync().ConfigureAwait(false); - - tran.Commit(); - } - catch (Exception e) - { - Logger.ErrorException("Failed to commit transaction.", e); - tran.Rollback(); - } - } - } - - /// - /// Gets a stream from a DataReader at a given ordinal - /// - /// The reader. - /// The ordinal. - /// Stream. - /// reader - protected static Stream GetStream(IDataReader reader, int ordinal) - { - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - var memoryStream = new MemoryStream(); - var num = 0L; - var array = new byte[4096]; - long bytes; - do - { - bytes = reader.GetBytes(ordinal, num, array, 0, array.Length); - memoryStream.Write(array, 0, (int)bytes); - num += bytes; - } - while (bytes > 0L); - memoryStream.Position = 0; - return memoryStream; - } - } -} diff --git a/MediaBrowser.Server.Sqlite/SQLiteUserDataRepository.cs b/MediaBrowser.Server.Sqlite/SQLiteUserDataRepository.cs deleted file mode 100644 index eaa89508a..000000000 --- a/MediaBrowser.Server.Sqlite/SQLiteUserDataRepository.cs +++ /dev/null @@ -1,148 +0,0 @@ -using MediaBrowser.Controller; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Persistence; -using MediaBrowser.Model.Logging; -using System; -using System.Collections.Generic; -using System.Data; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Server.Sqlite -{ - /// - /// Class SQLiteUserDataRepository - /// - public class SQLiteUserDataRepository : SqliteRepository, IUserDataRepository - { - /// - /// The repository name - /// - public const string RepositoryName = "SQLite"; - - /// - /// Gets the name of the repository - /// - /// The name. - public string Name - { - get - { - return RepositoryName; - } - } - - /// - /// Initializes a new instance of the class. - /// - /// The logger. - public SQLiteUserDataRepository(ILogger logger) - : base(logger) - { - } - - /// - /// Opens the connection to the database - /// - /// Task. - public async Task Initialize() - { - var dbFile = Path.Combine(Kernel.Instance.ApplicationPaths.DataPath, "userdata.db"); - - await ConnectToDB(dbFile).ConfigureAwait(false); - - string[] queries = { - - "create table if not exists user_data (item_id GUID, user_id GUID, data BLOB)", - "create unique index if not exists idx_user_data on user_data (item_id, user_id)", - "create table if not exists schema_version (table_name primary key, version)", - //pragmas - "pragma temp_store = memory" - }; - - RunQueries(queries); - } - - /// - /// Save the user specific data associated with an item in the repo - /// - /// The item. - /// The cancellation token. - /// Task. - /// item - public Task SaveUserData(BaseItem item, CancellationToken cancellationToken) - { - if (item == null) - { - throw new ArgumentNullException("item"); - } - - if (cancellationToken == null) - { - throw new ArgumentNullException("cancellationToken"); - } - - return Task.Run(() => - { - cancellationToken.ThrowIfCancellationRequested(); - - var cmd = connection.CreateCommand(); - - cmd.CommandText = "delete from user_data where item_id = @guid"; - cmd.AddParam("@guid", item.UserDataId); - - QueueCommand(cmd); - - if (item.UserData != null) - { - foreach (var data in item.UserData) - { - cmd = connection.CreateCommand(); - cmd.CommandText = "insert into user_data (item_id, user_id, data) values (@1, @2, @3)"; - cmd.AddParam("@1", item.UserDataId); - cmd.AddParam("@2", data.UserId); - - cmd.AddParam("@3", Kernel.Instance.ProtobufSerializer.SerializeToBytes(data)); - - QueueCommand(cmd); - } - } - }); - } - - /// - /// Gets user data for an item - /// - /// The item. - /// IEnumerable{UserItemData}. - /// item - public IEnumerable RetrieveUserData(BaseItem item) - { - if (item == null) - { - throw new ArgumentNullException("item"); - } - - var cmd = connection.CreateCommand(); - cmd.CommandText = "select data from user_data where item_id = @guid"; - var guidParam = cmd.Parameters.Add("@guid", DbType.Guid); - guidParam.Value = item.UserDataId; - - using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult)) - { - while (reader.Read()) - { - using (var stream = GetStream(reader, 0)) - { - var data = Kernel.Instance.ProtobufSerializer.DeserializeFromStream(stream); - if (data != null) - { - yield return data; - } - } - } - } - } - } -} diff --git a/MediaBrowser.Server.Sqlite/SQLiteUserRepository.cs b/MediaBrowser.Server.Sqlite/SQLiteUserRepository.cs deleted file mode 100644 index 6c0de2161..000000000 --- a/MediaBrowser.Server.Sqlite/SQLiteUserRepository.cs +++ /dev/null @@ -1,157 +0,0 @@ -using MediaBrowser.Common.Serialization; -using MediaBrowser.Controller; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Persistence; -using System; -using System.Collections.Generic; -using System.Data; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Model.Logging; - -namespace MediaBrowser.Server.Sqlite -{ - /// - /// Class SQLiteUserRepository - /// - public class SQLiteUserRepository : SqliteRepository, IUserRepository - { - /// - /// The repository name - /// - public const string RepositoryName = "SQLite"; - - /// - /// Gets the name of the repository - /// - /// The name. - public string Name - { - get - { - return RepositoryName; - } - } - - /// - /// Initializes a new instance of the class. - /// - /// The logger. - public SQLiteUserRepository(ILogger logger) - : base(logger) - { - } - - /// - /// Opens the connection to the database - /// - /// Task. - public async Task Initialize() - { - var dbFile = Path.Combine(Kernel.Instance.ApplicationPaths.DataPath, "users.db"); - - await ConnectToDB(dbFile).ConfigureAwait(false); - - string[] queries = { - - "create table if not exists users (guid GUID primary key, data BLOB)", - "create index if not exists idx_users on users(guid)", - "create table if not exists schema_version (table_name primary key, version)", - //pragmas - "pragma temp_store = memory" - }; - - RunQueries(queries); - } - - /// - /// Save a user in the repo - /// - /// The user. - /// The cancellation token. - /// Task. - /// user - public Task SaveUser(User user, CancellationToken cancellationToken) - { - if (user == null) - { - throw new ArgumentNullException("user"); - } - - if (cancellationToken == null) - { - throw new ArgumentNullException("cancellationToken"); - } - - return Task.Run(() => - { - cancellationToken.ThrowIfCancellationRequested(); - - var serialized = JsonSerializer.SerializeToBytes(user); - - cancellationToken.ThrowIfCancellationRequested(); - - var cmd = connection.CreateCommand(); - cmd.CommandText = "replace into users (guid, data) values (@1, @2)"; - cmd.AddParam("@1", user.Id); - cmd.AddParam("@2", serialized); - QueueCommand(cmd); - }); - } - - /// - /// Retrieve all users from the database - /// - /// IEnumerable{User}. - public IEnumerable RetrieveAllUsers() - { - var cmd = connection.CreateCommand(); - cmd.CommandText = "select data from users"; - - using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult)) - { - while (reader.Read()) - { - using (var stream = GetStream(reader, 0)) - { - var user = JsonSerializer.DeserializeFromStream(stream); - yield return user; - } - } - } - } - - /// - /// Deletes the user. - /// - /// The user. - /// The cancellation token. - /// Task. - /// user - public Task DeleteUser(User user, CancellationToken cancellationToken) - { - if (user == null) - { - throw new ArgumentNullException("user"); - } - - if (cancellationToken == null) - { - throw new ArgumentNullException("cancellationToken"); - } - - return Task.Run(() => - { - cancellationToken.ThrowIfCancellationRequested(); - - var cmd = connection.CreateCommand(); - cmd.CommandText = "delete from users where guid=@guid"; - var guidParam = cmd.Parameters.Add("@guid", DbType.Guid); - guidParam.Value = user.Id; - - return ExecuteCommand(cmd); - }); - } - } -} diff --git a/MediaBrowser.Server.Sqlite/packages.config b/MediaBrowser.Server.Sqlite/packages.config deleted file mode 100644 index 106618814..000000000 --- a/MediaBrowser.Server.Sqlite/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/MediaBrowser.Server.WorldWeatherOnline/MediaBrowser.Server.WorldWeatherOnline.csproj b/MediaBrowser.Server.WorldWeatherOnline/MediaBrowser.Server.WorldWeatherOnline.csproj deleted file mode 100644 index 1e97a482e..000000000 --- a/MediaBrowser.Server.WorldWeatherOnline/MediaBrowser.Server.WorldWeatherOnline.csproj +++ /dev/null @@ -1,72 +0,0 @@ - - - - - Debug - AnyCPU - {973CA45C-8362-490B-8327-C68098FD4891} - Library - Properties - MediaBrowser.Server.WorldWeatherOnline - MediaBrowser.Server.WorldWeatherOnline - v4.5 - 512 - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - Always - - - - - - - - - Properties\SharedVersion.cs - - - - - - - {9142eefa-7570-41e1-bfcc-468bb571af2f} - MediaBrowser.Common - - - {17e1f4e6-8abd-4fe5-9ecf-43d4b6087ba2} - MediaBrowser.Controller - - - {7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b} - MediaBrowser.Model - - - - - xcopy "$(TargetPath)" "$(SolutionDir)\MediaBrowser.ServerApplication\CorePlugins\" /y - - - \ No newline at end of file diff --git a/MediaBrowser.Server.WorldWeatherOnline/Properties/AssemblyInfo.cs b/MediaBrowser.Server.WorldWeatherOnline/Properties/AssemblyInfo.cs deleted file mode 100644 index d5d94bafd..000000000 --- a/MediaBrowser.Server.WorldWeatherOnline/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("MediaBrowser.Server.WorldWeatherOnline")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("MediaBrowser.Server.WorldWeatherOnline")] -[assembly: AssemblyCopyright("Copyright © 2013")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("c7b294dc-b3fd-4925-9ac3-8dd16803200c")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// \ No newline at end of file diff --git a/MediaBrowser.Server.WorldWeatherOnline/WeatherProvider.cs b/MediaBrowser.Server.WorldWeatherOnline/WeatherProvider.cs deleted file mode 100644 index c30a30455..000000000 --- a/MediaBrowser.Server.WorldWeatherOnline/WeatherProvider.cs +++ /dev/null @@ -1,332 +0,0 @@ -using MediaBrowser.Common.Serialization; -using MediaBrowser.Controller; -using MediaBrowser.Controller.Weather; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Weather; -using System; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Server.WorldWeatherOnline -{ - /// - /// Based on http://www.worldweatheronline.com/free-weather-feed.aspx - /// The classes in this file are a reproduction of the json output, which will then be converted to our weather model classes - /// - public class WeatherProvider : IWeatherProvider - { - /// - /// Gets or sets the logger. - /// - /// The logger. - private ILogger Logger { get; set; } - - /// - /// Initializes a new instance of the class. - /// - /// The logger. - /// logger - public WeatherProvider(ILogger logger) - { - if (logger == null) - { - throw new ArgumentNullException("logger"); - } - - Logger = logger; - } - - /// - /// The _weather semaphore - /// - private readonly SemaphoreSlim _weatherSemaphore = new SemaphoreSlim(10, 10); - - /// - /// Gets the weather info async. - /// - /// The location. - /// The cancellation token. - /// Task{WeatherInfo}. - /// location - public async Task GetWeatherInfoAsync(string location, CancellationToken cancellationToken) - { - if (string.IsNullOrWhiteSpace(location)) - { - throw new ArgumentNullException("location"); - } - - if (cancellationToken == null) - { - throw new ArgumentNullException("cancellationToken"); - } - - const int numDays = 5; - const string apiKey = "24902f60f1231941120109"; - - var url = "http://free.worldweatheronline.com/feed/weather.ashx?q=" + location + "&format=json&num_of_days=" + numDays + "&key=" + apiKey; - - Logger.Info("Accessing weather from " + url); - - using (var stream = await Kernel.Instance.HttpManager.Get(url, _weatherSemaphore, cancellationToken).ConfigureAwait(false)) - { - var data = JsonSerializer.DeserializeFromStream(stream).data; - - return GetWeatherInfo(data); - } - } - - /// - /// Converst the json output to our WeatherInfo model class - /// - /// The data. - /// WeatherInfo. - private WeatherInfo GetWeatherInfo(WeatherData data) - { - var info = new WeatherInfo(); - - if (data.current_condition != null) - { - var condition = data.current_condition.FirstOrDefault(); - - if (condition != null) - { - info.CurrentWeather = condition.ToWeatherStatus(); - } - } - - if (data.weather != null) - { - info.Forecasts = data.weather.Select(w => w.ToWeatherForecast()).ToArray(); - } - - return info; - } - } - - /// - /// Class WeatherResult - /// - class WeatherResult - { - /// - /// Gets or sets the data. - /// - /// The data. - public WeatherData data { get; set; } - } - - /// - /// Class WeatherData - /// - public class WeatherData - { - /// - /// Gets or sets the current_condition. - /// - /// The current_condition. - public WeatherCondition[] current_condition { get; set; } - /// - /// Gets or sets the weather. - /// - /// The weather. - public DailyWeatherInfo[] weather { get; set; } - } - - /// - /// Class WeatherCondition - /// - public class WeatherCondition - { - /// - /// Gets or sets the temp_ C. - /// - /// The temp_ C. - public string temp_C { get; set; } - /// - /// Gets or sets the temp_ F. - /// - /// The temp_ F. - public string temp_F { get; set; } - /// - /// Gets or sets the humidity. - /// - /// The humidity. - public string humidity { get; set; } - /// - /// Gets or sets the weather code. - /// - /// The weather code. - public string weatherCode { get; set; } - - /// - /// To the weather status. - /// - /// WeatherStatus. - public WeatherStatus ToWeatherStatus() - { - return new WeatherStatus - { - TemperatureCelsius = int.Parse(temp_C), - TemperatureFahrenheit = int.Parse(temp_F), - Humidity = int.Parse(humidity), - Condition = DailyWeatherInfo.GetCondition(weatherCode) - }; - } - } - - /// - /// Class DailyWeatherInfo - /// - public class DailyWeatherInfo - { - /// - /// Gets or sets the date. - /// - /// The date. - public string date { get; set; } - /// - /// Gets or sets the precip MM. - /// - /// The precip MM. - public string precipMM { get; set; } - /// - /// Gets or sets the temp max C. - /// - /// The temp max C. - public string tempMaxC { get; set; } - /// - /// Gets or sets the temp max F. - /// - /// The temp max F. - public string tempMaxF { get; set; } - /// - /// Gets or sets the temp min C. - /// - /// The temp min C. - public string tempMinC { get; set; } - /// - /// Gets or sets the temp min F. - /// - /// The temp min F. - public string tempMinF { get; set; } - /// - /// Gets or sets the weather code. - /// - /// The weather code. - public string weatherCode { get; set; } - /// - /// Gets or sets the winddir16 point. - /// - /// The winddir16 point. - public string winddir16Point { get; set; } - /// - /// Gets or sets the winddir degree. - /// - /// The winddir degree. - public string winddirDegree { get; set; } - /// - /// Gets or sets the winddirection. - /// - /// The winddirection. - public string winddirection { get; set; } - /// - /// Gets or sets the windspeed KMPH. - /// - /// The windspeed KMPH. - public string windspeedKmph { get; set; } - /// - /// Gets or sets the windspeed miles. - /// - /// The windspeed miles. - public string windspeedMiles { get; set; } - - /// - /// To the weather forecast. - /// - /// WeatherForecast. - public WeatherForecast ToWeatherForecast() - { - return new WeatherForecast - { - Date = DateTime.Parse(date), - HighTemperatureCelsius = int.Parse(tempMaxC), - HighTemperatureFahrenheit = int.Parse(tempMaxF), - LowTemperatureCelsius = int.Parse(tempMinC), - LowTemperatureFahrenheit = int.Parse(tempMinF), - Condition = GetCondition(weatherCode) - }; - } - - /// - /// Gets the condition. - /// - /// The weather code. - /// WeatherConditions. - public static WeatherConditions GetCondition(string weatherCode) - { - switch (weatherCode) - { - case "362": - case "365": - case "320": - case "317": - case "182": - return WeatherConditions.Sleet; - case "338": - case "335": - case "332": - case "329": - case "326": - case "323": - case "377": - case "374": - case "371": - case "368": - case "395": - case "392": - case "350": - case "227": - case "179": - return WeatherConditions.Snow; - case "314": - case "311": - case "308": - case "305": - case "302": - case "299": - case "296": - case "293": - case "284": - case "281": - case "266": - case "263": - case "359": - case "356": - case "353": - case "185": - case "176": - return WeatherConditions.Rain; - case "260": - case "248": - return WeatherConditions.Fog; - case "389": - case "386": - case "200": - return WeatherConditions.Thunderstorm; - case "230": - return WeatherConditions.Blizzard; - case "143": - return WeatherConditions.Mist; - case "122": - return WeatherConditions.Overcast; - case "119": - return WeatherConditions.Cloudy; - case "115": - return WeatherConditions.PartlyCloudy; - default: - return WeatherConditions.Sunny; - } - } - } -} \ No newline at end of file diff --git a/MediaBrowser.ServerApplication/App.xaml.cs b/MediaBrowser.ServerApplication/App.xaml.cs index 1b6b6f98e..5808120ad 100644 --- a/MediaBrowser.ServerApplication/App.xaml.cs +++ b/MediaBrowser.ServerApplication/App.xaml.cs @@ -1,26 +1,13 @@ -using BDInfo; -using MediaBrowser.ClickOnce; -using MediaBrowser.Common.IO; +using MediaBrowser.ClickOnce; +using MediaBrowser.Common.Implementations.Serialization; using MediaBrowser.Common.Kernel; -using MediaBrowser.Common.Net; using MediaBrowser.Controller; -using MediaBrowser.IsoMounter; using MediaBrowser.Logging.Nlog; -using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; -using MediaBrowser.Model.MediaInfo; -using MediaBrowser.Model.Updates; -using MediaBrowser.Networking.Management; -using MediaBrowser.Networking.Udp; -using MediaBrowser.Networking.Web; -using MediaBrowser.Networking.WebSocket; using MediaBrowser.Server.Uninstall; -using MediaBrowser.ServerApplication.Implementations; using Microsoft.Win32; -using SimpleInjector; using System; using System.Diagnostics; -using System.IO; using System.Linq; using System.Net.Cache; using System.Threading; @@ -35,7 +22,7 @@ namespace MediaBrowser.ServerApplication /// /// Interaction logic for App.xaml /// - public partial class App : Application, IApplicationHost + public partial class App : Application { /// /// Defines the entry point of the application. @@ -78,16 +65,11 @@ namespace MediaBrowser.ServerApplication protected ILogger Logger { get; set; } /// - /// Gets or sets the log file path. + /// Gets or sets the composition root. /// - /// The log file path. - public string LogFilePath { get; private set; } - - /// - /// The container - /// - private Container _container = new Container(); - + /// The composition root. + protected ApplicationHost CompositionRoot { get; set; } + /// /// Initializes a new instance of the class. /// @@ -135,12 +117,6 @@ namespace MediaBrowser.ServerApplication get { return "MediaBrowser.Server.Uninstall.exe"; } } - /// - /// Gets or sets the iso manager. - /// - /// The iso manager. - private IIsoManager IsoManager { get; set; } - /// /// Gets or sets a value indicating whether [last run at startup value]. /// @@ -198,13 +174,13 @@ namespace MediaBrowser.ServerApplication /// protected async void LoadKernel() { - Kernel = new Kernel(this, Logger); + CompositionRoot = new ApplicationHost(Logger); - RegisterResources(); + Kernel = CompositionRoot.Kernel; try { - new MainWindow(Logger).Show(); + new MainWindow(new JsonSerializer(), Logger).Show(); var now = DateTime.UtcNow; @@ -281,6 +257,7 @@ namespace MediaBrowser.ServerApplication base.OnExit(e); Kernel.Dispose(); + CompositionRoot.Dispose(); } /// @@ -391,17 +368,6 @@ namespace MediaBrowser.ServerApplication Dispatcher.Invoke(Shutdown); } - /// - /// Reloads the logger. - /// - /// - public void ReloadLogger() - { - LogFilePath = Path.Combine(Kernel.ApplicationPaths.LogDirectoryPath, "Server-" + DateTime.Now.Ticks + ".log"); - - NlogManager.AddFileTarget(LogFilePath, Kernel.Configuration.EnableDebugLevelLogging); - } - /// /// Gets the image. /// @@ -477,143 +443,5 @@ namespace MediaBrowser.ServerApplication RenderOptions.SetBitmapScalingMode(bitmap, BitmapScalingMode.Fant); return bitmap; } - - /// - /// Gets or sets a value indicating whether this instance can self update. - /// - /// true if this instance can self update; otherwise, false. - public bool CanSelfUpdate - { - get { return ClickOnceHelper.IsNetworkDeployed; } - } - - /// - /// Checks for update. - /// - /// The cancellation token. - /// The progress. - /// Task{CheckForUpdateResult}. - public Task CheckForApplicationUpdate(CancellationToken cancellationToken, IProgress progress) - { - return new ApplicationUpdateCheck().CheckForApplicationUpdate(cancellationToken, progress); - } - - /// - /// Updates the application. - /// - /// The cancellation token. - /// The progress. - /// Task. - public Task UpdateApplication(CancellationToken cancellationToken, IProgress progress) - { - return new ApplicationUpdater().UpdateApplication(cancellationToken, progress); - } - - /// - /// Registers resources that classes will depend on - /// - private void RegisterResources() - { - RegisterSingleInstance(this); - RegisterSingleInstance(Logger); - - IsoManager = new PismoIsoManager(Logger); - - RegisterSingleInstance(IsoManager); - RegisterSingleInstance(() => new BdInfoExaminer()); - RegisterSingleInstance(() => new NetworkManager()); - RegisterSingleInstance(() => new DotNetZipClient()); - RegisterSingleInstance(() => new AlchemyServer(Logger)); - Register(typeof(IUdpServer), typeof(UdpServer)); - RegisterSingleInstance(() => new HttpServer(this, Kernel, Logger, "Media Browser", "index.html")); - } - - /// - /// Creates an instance of type and resolves all constructor dependancies - /// - /// The type. - /// System.Object. - public object CreateInstance(Type type) - { - try - { - return _container.GetInstance(type); - } - catch - { - Logger.Error("Error creating {0}", type.Name); - - throw; - } - } - - /// - /// Registers the specified obj. - /// - /// - /// The obj. - public void RegisterSingleInstance(T obj) - where T : class - { - _container.RegisterSingle(obj); - } - - /// - /// Registers the specified func. - /// - /// - /// The func. - public void Register(Func func) - where T : class - { - _container.Register(func); - } - - /// - /// Registers the single instance. - /// - /// - /// The func. - public void RegisterSingleInstance(Func func) - where T : class - { - _container.RegisterSingle(func); - } - - /// - /// Resolves this instance. - /// - /// - /// ``0. - public T Resolve() - { - return (T)_container.GetRegistration(typeof(T), true).GetInstance(); - } - - /// - /// Resolves this instance. - /// - /// - /// ``0. - public T TryResolve() - { - var result = _container.GetRegistration(typeof(T), false); - - if (result == null) - { - return default(T); - } - return (T)result.GetInstance(); - } - - /// - /// Registers the specified service type. - /// - /// Type of the service. - /// Type of the concrete. - public void Register(Type serviceType, Type implementation) - { - _container.Register(serviceType, implementation); - } } } diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs new file mode 100644 index 000000000..40e190756 --- /dev/null +++ b/MediaBrowser.ServerApplication/ApplicationHost.cs @@ -0,0 +1,498 @@ +using BDInfo; +using MediaBrowser.ClickOnce; +using MediaBrowser.Common.Implementations.ScheduledTasks; +using MediaBrowser.Common.Implementations.Serialization; +using MediaBrowser.Common.IO; +using MediaBrowser.Common.Kernel; +using MediaBrowser.Common.Net; +using MediaBrowser.Common.ScheduledTasks; +using MediaBrowser.Controller; +using MediaBrowser.IsoMounter; +using MediaBrowser.Logging.Nlog; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.MediaInfo; +using MediaBrowser.Model.Serialization; +using MediaBrowser.Model.System; +using MediaBrowser.Model.Updates; +using MediaBrowser.Networking.Management; +using MediaBrowser.Networking.Udp; +using MediaBrowser.Networking.Web; +using MediaBrowser.Networking.WebSocket; +using MediaBrowser.Server.Implementations; +using MediaBrowser.ServerApplication.Implementations; +using SimpleInjector; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.ServerApplication +{ + /// + /// Class CompositionRoot + /// + public class ApplicationHost : IApplicationHost, IDisposable + { + /// + /// Gets or sets the logger. + /// + /// The logger. + private ILogger Logger { get; set; } + + /// + /// Gets or sets the iso manager. + /// + /// The iso manager. + private IIsoManager IsoManager { get; set; } + + /// + /// Gets or sets the log file path. + /// + /// The log file path. + public string LogFilePath { get; private set; } + + /// + /// The container + /// + private readonly Container _container = new Container(); + + /// + /// Gets or sets the kernel. + /// + /// The kernel. + public Kernel Kernel { get; private set; } + + private readonly List _failedAssemblies = new List(); + /// + /// Gets assemblies that failed to load + /// + public IEnumerable FailedAssemblies + { + get { return _failedAssemblies; } + } + + /// + /// Gets all types within all running assemblies + /// + /// All types. + public Type[] AllTypes { get; private set; } + + /// + /// Gets all concrete types. + /// + /// All concrete types. + public Type[] AllConcreteTypes { get; private set; } + + /// + /// The disposable parts + /// + private readonly List _disposableParts = new List(); + + /// + /// The json serializer + /// + private readonly IJsonSerializer _jsonSerializer = new JsonSerializer(); + + /// + /// The _XML serializer + /// + private readonly IXmlSerializer _xmlSerializer = new XmlSerializer(); + + /// + /// The _application paths + /// + private readonly IServerApplicationPaths _applicationPaths = new ServerApplicationPaths(); + + /// + /// The _task manager + /// + private readonly ITaskManager _taskManager; + + /// + /// Initializes a new instance of the class. + /// + /// The logger. + public ApplicationHost(ILogger logger) + { + Logger = logger; + + _taskManager = new TaskManager(_applicationPaths, _jsonSerializer, Logger); + + Kernel = new Kernel(this, _applicationPaths, _xmlSerializer, _taskManager, Logger); + + RegisterResources(); + + FindParts(); + } + + /// + /// Registers resources that classes will depend on + /// + internal void RegisterResources() + { + DiscoverTypes(); + + RegisterSingleInstance(Kernel); + RegisterSingleInstance(Kernel); + + RegisterSingleInstance(this); + RegisterSingleInstance(Logger); + + IsoManager = new PismoIsoManager(Logger); + + RegisterSingleInstance(_applicationPaths); + RegisterSingleInstance(_applicationPaths); + + RegisterSingleInstance(IsoManager); + RegisterSingleInstance(_taskManager); + RegisterSingleInstance(() => new BdInfoExaminer()); + RegisterSingleInstance(() => new NetworkManager()); + RegisterSingleInstance(() => new DotNetZipClient()); + RegisterSingleInstance(() => new AlchemyServer(Logger)); + RegisterSingleInstance(_jsonSerializer); + RegisterSingleInstance(_xmlSerializer); + RegisterSingleInstance(() => ProtobufSerializer); + Register(typeof(IUdpServer), typeof(UdpServer)); + RegisterSingleInstance(() => ServerFactory.CreateServer(this, Kernel, ProtobufSerializer, Logger, "Media Browser", "index.html")); + } + + /// + /// Discovers the types. + /// + private void DiscoverTypes() + { + _failedAssemblies.Clear(); + + AllTypes = GetComposablePartAssemblies().SelectMany(GetTypes).ToArray(); + + AllConcreteTypes = AllTypes.Where(t => t.IsClass && !t.IsAbstract && !t.IsInterface && !t.IsGenericType).ToArray(); + } + + /// + /// Finds the parts. + /// + private void FindParts() + { + _taskManager.AddTasks(GetExports(false)); + } + + /// + /// 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 + private IEnumerable GetTypes(Assembly assembly) + { + if (assembly == null) + { + throw new ArgumentNullException("assembly"); + } + + try + { + return assembly.GetTypes(); + } + catch (ReflectionTypeLoadException ex) + { + // If it fails we can still get a list of the Types it was able to resolve + return ex.Types.Where(t => t != null); + } + } + + /// + /// The _protobuf serializer initialized + /// + private bool _protobufSerializerInitialized; + /// + /// The _protobuf serializer sync lock + /// + private object _protobufSerializerSyncLock = new object(); + /// + /// Gets a dynamically compiled generated serializer that can serialize protocontracts without reflection + /// + private ProtobufSerializer _protobufSerializer; + /// + /// Gets the protobuf serializer. + /// + /// The protobuf serializer. + public ProtobufSerializer ProtobufSerializer + { + get + { + // Lazy load + LazyInitializer.EnsureInitialized(ref _protobufSerializer, ref _protobufSerializerInitialized, ref _protobufSerializerSyncLock, () => ProtobufSerializer.Create(AllTypes)); + return _protobufSerializer; + } + private set + { + _protobufSerializer = value; + _protobufSerializerInitialized = value != null; + } + } + + /// + /// Creates an instance of type and resolves all constructor dependancies + /// + /// The type. + /// System.Object. + public object CreateInstance(Type type) + { + try + { + return _container.GetInstance(type); + } + catch + { + Logger.Error("Error creating {0}", type.Name); + + throw; + } + } + + /// + /// Registers the specified obj. + /// + /// + /// The obj. + public void RegisterSingleInstance(T obj) + where T : class + { + _container.RegisterSingle(obj); + } + + /// + /// Registers the specified func. + /// + /// + /// The func. + public void Register(Func func) + where T : class + { + _container.Register(func); + } + + /// + /// Registers the single instance. + /// + /// + /// The func. + public void RegisterSingleInstance(Func func) + where T : class + { + _container.RegisterSingle(func); + } + + /// + /// Resolves this instance. + /// + /// + /// ``0. + public T Resolve() + { + return (T)_container.GetRegistration(typeof(T), true).GetInstance(); + } + + /// + /// Resolves this instance. + /// + /// + /// ``0. + public T TryResolve() + { + var result = _container.GetRegistration(typeof(T), false); + + if (result == null) + { + return default(T); + } + return (T)result.GetInstance(); + } + + /// + /// Registers the specified service type. + /// + /// Type of the service. + /// Type of the concrete. + public void Register(Type serviceType, Type implementation) + { + _container.Register(serviceType, implementation); + } + + /// + /// Restarts this instance. + /// + /// + public void Restart() + { + App.Instance.Restart(); + } + + /// + /// Reloads the logger. + /// + /// + public void ReloadLogger() + { + LogFilePath = Path.Combine(Kernel.ApplicationPaths.LogDirectoryPath, "Server-" + DateTime.Now.Ticks + ".log"); + + NlogManager.AddFileTarget(LogFilePath, Kernel.Configuration.EnableDebugLevelLogging); + } + + /// + /// Gets or sets a value indicating whether this instance can self update. + /// + /// true if this instance can self update; otherwise, false. + public bool CanSelfUpdate + { + get { return ClickOnceHelper.IsNetworkDeployed; } + } + + /// + /// Checks for update. + /// + /// The cancellation token. + /// The progress. + /// Task{CheckForUpdateResult}. + public Task CheckForApplicationUpdate(CancellationToken cancellationToken, IProgress progress) + { + return new ApplicationUpdateCheck().CheckForApplicationUpdate(cancellationToken, progress); + } + + /// + /// Updates the application. + /// + /// The cancellation token. + /// The progress. + /// Task. + public Task UpdateApplication(CancellationToken cancellationToken, IProgress progress) + { + return new ApplicationUpdater().UpdateApplication(cancellationToken, progress); + } + + /// + /// Gets the composable part assemblies. + /// + /// IEnumerable{Assembly}. + private IEnumerable GetComposablePartAssemblies() + { + // 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 + foreach (var pluginAssembly in Directory + .EnumerateFiles(Kernel.ApplicationPaths.PluginsPath, "*.dll", SearchOption.TopDirectoryOnly) + .Select(LoadAssembly).Where(a => a != null)) + { + yield return pluginAssembly; + } + + var runningDirectory = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName); + var corePluginDirectory = Path.Combine(runningDirectory, "CorePlugins"); + + // This will prevent the .dll file from getting locked, and allow us to replace it when needed + foreach (var pluginAssembly in Directory + .EnumerateFiles(corePluginDirectory, "*.dll", SearchOption.TopDirectoryOnly) + .Select(LoadAssembly).Where(a => a != null)) + { + yield return pluginAssembly; + } + + // Include composable parts in the Model assembly + yield return typeof(SystemInfo).Assembly; + + // Include composable parts in the Common assembly + yield return typeof(IKernel).Assembly; + + // Include composable parts in the Controller assembly + yield return typeof(Kernel).Assembly; + + // Common implementations + yield return typeof(TaskManager).Assembly; + + // Server implementations + yield return typeof(ServerApplicationPaths).Assembly; + + // Include composable parts in the running assembly + yield return GetType().Assembly; + } + + /// + /// Loads the assembly. + /// + /// The file. + /// Assembly. + private Assembly LoadAssembly(string file) + { + try + { + return Assembly.Load(File.ReadAllBytes((file))); + } + catch (Exception ex) + { + _failedAssemblies.Add(file); + Logger.ErrorException("Error loading assembly {0}", ex, file); + return null; + } + } + + /// + /// Gets the exports. + /// + /// + /// All types. + /// if set to true [manage liftime]. + /// IEnumerable{``0}. + public IEnumerable GetExports(bool manageLiftime = true) + { + var currentType = typeof(T); + + Logger.Info("Composing instances of " + currentType.Name); + + var parts = AllConcreteTypes.Where(currentType.IsAssignableFrom).Select(CreateInstance).Cast().ToArray(); + + if (manageLiftime) + { + _disposableParts.AddRange(parts.OfType()); + } + + return parts; + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + Dispose(true); + } + + /// + /// Releases unmanaged and - optionally - managed resources. + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool dispose) + { + IsoManager.Dispose(); + + foreach (var part in _disposableParts) + { + part.Dispose(); + } + + _disposableParts.Clear(); + } + } + + public class MyClass + { + public MyClass() + { + + } + } +} diff --git a/MediaBrowser.ServerApplication/LibraryExplorer.xaml.cs b/MediaBrowser.ServerApplication/LibraryExplorer.xaml.cs index 02dd6fb9c..59961a941 100644 --- a/MediaBrowser.ServerApplication/LibraryExplorer.xaml.cs +++ b/MediaBrowser.ServerApplication/LibraryExplorer.xaml.cs @@ -1,11 +1,12 @@ -using MediaBrowser.Common.Serialization; -using MediaBrowser.Controller; +using MediaBrowser.Controller; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Localization; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Serialization; using System; using System.Collections.Generic; using System.Globalization; @@ -18,7 +19,6 @@ using System.Windows.Controls; using System.Windows.Data; using System.Windows.Input; using System.Windows.Media.Imaging; -using MediaBrowser.Model.Logging; namespace MediaBrowser.ServerApplication { @@ -27,8 +27,10 @@ namespace MediaBrowser.ServerApplication /// public partial class LibraryExplorer : Window { - private ILogger _logger; + private readonly ILogger _logger; + private readonly IJsonSerializer _jsonSerializer; + /// /// The current user /// @@ -36,9 +38,10 @@ namespace MediaBrowser.ServerApplication /// /// Initializes a new instance of the class. /// - public LibraryExplorer(ILogger logger) + public LibraryExplorer(IJsonSerializer jsonSerializer, ILogger logger) { _logger = logger; + _jsonSerializer = jsonSerializer; InitializeComponent(); lblVersion.Content = "Version: " + Kernel.Instance.ApplicationVersion; @@ -161,7 +164,7 @@ namespace MediaBrowser.ServerApplication lblIndexBy.Visibility = ddlIndexBy.Visibility = ddlSortBy.Visibility = lblSortBy.Visibility = Visibility.Hidden; } - txtData.Text = FormatJson(JsonSerializer.SerializeToString(item)) + trailers + features; + txtData.Text = FormatJson(_jsonSerializer.SerializeToString(item)) + trailers + features; var previews = new List(); await Task.Run(() => diff --git a/MediaBrowser.ServerApplication/MainWindow.xaml.cs b/MediaBrowser.ServerApplication/MainWindow.xaml.cs index 8a312e7ef..332bb1daa 100644 --- a/MediaBrowser.ServerApplication/MainWindow.xaml.cs +++ b/MediaBrowser.ServerApplication/MainWindow.xaml.cs @@ -2,6 +2,7 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Serialization; using MediaBrowser.ServerApplication.Controls; using MediaBrowser.ServerApplication.Logging; using System; @@ -38,6 +39,11 @@ namespace MediaBrowser.ServerApplication /// The new item timer. private Timer NewItemTimer { get; set; } + /// + /// The _json serializer + /// + private readonly IJsonSerializer _jsonSerializer; + /// /// The _logger /// @@ -48,13 +54,18 @@ namespace MediaBrowser.ServerApplication /// /// The logger. /// logger - public MainWindow(ILogger logger) + public MainWindow(IJsonSerializer jsonSerializer, ILogger logger) { + if (jsonSerializer == null) + { + throw new ArgumentNullException("jsonSerializer"); + } if (logger == null) { throw new ArgumentNullException("logger"); } + _jsonSerializer = jsonSerializer; _logger = logger; InitializeComponent(); @@ -282,7 +293,7 @@ namespace MediaBrowser.ServerApplication /// The instance containing the event data. private void cmOpenExplorer_click(object sender, RoutedEventArgs e) { - (new LibraryExplorer(_logger)).Show(); + (new LibraryExplorer(_jsonSerializer, _logger)).Show(); } /// diff --git a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj index b1c57949c..8a3a0b7af 100644 --- a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj +++ b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj @@ -128,36 +128,6 @@ False ..\ThirdParty\UPnP\Libs\Platinum.Managed.dll - - False - ..\packages\ServiceStack.3.9.37\lib\net35\ServiceStack.dll - - - False - ..\packages\ServiceStack.Common.3.9.37\lib\net35\ServiceStack.Common.dll - - - False - ..\packages\ServiceStack.Common.3.9.37\lib\net35\ServiceStack.Interfaces.dll - - - False - ..\packages\ServiceStack.OrmLite.SqlServer.3.9.37\lib\ServiceStack.OrmLite.dll - - - ..\packages\ServiceStack.OrmLite.SqlServer.3.9.37\lib\ServiceStack.OrmLite.SqlServer.dll - - - ..\packages\ServiceStack.Redis.3.9.37\lib\net35\ServiceStack.Redis.dll - - - False - ..\packages\ServiceStack.3.9.37\lib\net35\ServiceStack.ServiceInterface.dll - - - False - ..\packages\ServiceStack.Text.3.9.37\lib\net35\ServiceStack.Text.dll - False ..\packages\SimpleInjector.2.0.0-beta5\lib\net40-client\SimpleInjector.dll @@ -220,6 +190,7 @@ App.xaml Code + ItemUpdateNotification.xaml @@ -279,6 +250,10 @@ {cc96bf3e-0bda-4809-bc4b-bb6d418f4a84} MediaBrowser.ClickOnce + + {c4d2573a-3fd3-441f-81af-174ac4cd4e1d} + MediaBrowser.Common.Implementations + {9142eefa-7570-41e1-bfcc-468bb571af2f} MediaBrowser.Common @@ -303,6 +278,10 @@ {7c11010e-179a-49b7-bfb2-f1656f5e71ad} MediaBrowser.Networking + + {2e781478-814d-4a48-9d80-bff206441a65} + MediaBrowser.Server.Implementations + {5443422f-9548-417a-90dd-2fc91f2b5999} MediaBrowser.Server.Uninstall diff --git a/MediaBrowser.ServerApplication/packages.config b/MediaBrowser.ServerApplication/packages.config index 28d6202f8..e1beae6da 100644 --- a/MediaBrowser.ServerApplication/packages.config +++ b/MediaBrowser.ServerApplication/packages.config @@ -3,11 +3,6 @@ - - - - - \ No newline at end of file diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs index aca931306..b321cc8d9 100644 --- a/MediaBrowser.WebDashboard/Api/DashboardService.cs +++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs @@ -1,7 +1,6 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Common.ScheduledTasks; -using MediaBrowser.Common.ScheduledTasks.Tasks; using MediaBrowser.Controller; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Plugins; @@ -137,7 +136,7 @@ namespace MediaBrowser.WebDashboard.Api .Select(ScheduledTaskHelpers.GetTaskInfo) .ToArray(), - ApplicationUpdateTaskId = taskManager.ScheduledTasks.OfType().First().Id, + ApplicationUpdateTaskId = taskManager.ScheduledTasks.First(t => t.GetType().Name.Equals("SystemUpdateTask", StringComparison.OrdinalIgnoreCase)).Id, ActiveConnections = connections, diff --git a/MediaBrowser.sln b/MediaBrowser.sln index 3f053e2d8..9ce346417 100644 --- a/MediaBrowser.sln +++ b/MediaBrowser.sln @@ -20,8 +20,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Performance19.psess = Performance19.psess EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.ApiInteraction.Javascript", "MediaBrowser.ApiInteraction.Javascript\MediaBrowser.ApiInteraction.Javascript.csproj", "{767B536E-D90C-4D74-A14B-8564B16F3499}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.ApiInteraction", "MediaBrowser.ApiInteraction\MediaBrowser.ApiInteraction.csproj", "{921C0F64-FDA7-4E9F-9E73-0CB0EEDB2422}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.ApiInteraction.Portable", "MediaBrowser.ApiInteraction.Portable\MediaBrowser.ApiInteraction.Portable.csproj", "{52E0C440-85C0-4A99-ACFE-07C87B2600BE}" @@ -41,10 +39,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.IsoMounter", " EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Installer", "MediaBrowser.Installer\MediaBrowser.Installer.csproj", "{3879F78A-D6F6-45E5-B2A8-D8DCF2DABB74}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Server.Sqlite", "MediaBrowser.Server.Sqlite\MediaBrowser.Server.Sqlite.csproj", "{8649ED6B-8504-4D00-BFA5-B8C73CC744DB}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Server.WorldWeatherOnline", "MediaBrowser.Server.WorldWeatherOnline\MediaBrowser.Server.WorldWeatherOnline.csproj", "{973CA45C-8362-490B-8327-C68098FD4891}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Logging.NLog", "MediaBrowser.Logging.NLog\MediaBrowser.Logging.NLog.csproj", "{67310740-0EC4-4DC2-9921-33DF38B20167}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.ClickOnce", "MediaBrowser.ClickOnce\MediaBrowser.ClickOnce.csproj", "{CC96BF3E-0BDA-4809-BC4B-BB6D418F4A84}" @@ -53,6 +47,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Networking", " EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Uninstaller", "MediaBrowser.Uninstaller\MediaBrowser.Uninstaller.csproj", "{FACAF749-3E28-46DD-B613-654FCD434959}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Common.Implementations", "MediaBrowser.Common.Implementations\MediaBrowser.Common.Implementations.csproj", "{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Server.Implementations", "MediaBrowser.Server.Implementations\MediaBrowser.Server.Implementations.csproj", "{2E781478-814D-4A48-9D80-BFF206441A65}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -163,21 +161,6 @@ Global {5624B7B5-B5A7-41D8-9F10-CC5611109619}.Release|x64.ActiveCfg = Release|Any CPU {5624B7B5-B5A7-41D8-9F10-CC5611109619}.Release|x86.ActiveCfg = Release|Any CPU {5624B7B5-B5A7-41D8-9F10-CC5611109619}.Release|x86.Build.0 = Release|Any CPU - {767B536E-D90C-4D74-A14B-8564B16F3499}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {767B536E-D90C-4D74-A14B-8564B16F3499}.Debug|Any CPU.Build.0 = Debug|Any CPU - {767B536E-D90C-4D74-A14B-8564B16F3499}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {767B536E-D90C-4D74-A14B-8564B16F3499}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {767B536E-D90C-4D74-A14B-8564B16F3499}.Debug|Win32.ActiveCfg = Debug|Any CPU - {767B536E-D90C-4D74-A14B-8564B16F3499}.Debug|x64.ActiveCfg = Debug|Any CPU - {767B536E-D90C-4D74-A14B-8564B16F3499}.Debug|x86.ActiveCfg = Debug|Any CPU - {767B536E-D90C-4D74-A14B-8564B16F3499}.Debug|x86.Build.0 = Debug|Any CPU - {767B536E-D90C-4D74-A14B-8564B16F3499}.Release|Any CPU.ActiveCfg = Release|Any CPU - {767B536E-D90C-4D74-A14B-8564B16F3499}.Release|Any CPU.Build.0 = Release|Any CPU - {767B536E-D90C-4D74-A14B-8564B16F3499}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {767B536E-D90C-4D74-A14B-8564B16F3499}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {767B536E-D90C-4D74-A14B-8564B16F3499}.Release|Win32.ActiveCfg = Release|Any CPU - {767B536E-D90C-4D74-A14B-8564B16F3499}.Release|x64.ActiveCfg = Release|Any CPU - {767B536E-D90C-4D74-A14B-8564B16F3499}.Release|x86.ActiveCfg = Release|Any CPU {921C0F64-FDA7-4E9F-9E73-0CB0EEDB2422}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {921C0F64-FDA7-4E9F-9E73-0CB0EEDB2422}.Debug|Any CPU.Build.0 = Debug|Any CPU {921C0F64-FDA7-4E9F-9E73-0CB0EEDB2422}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU @@ -266,34 +249,6 @@ Global {3879F78A-D6F6-45E5-B2A8-D8DCF2DABB74}.Release|Win32.ActiveCfg = Release|Any CPU {3879F78A-D6F6-45E5-B2A8-D8DCF2DABB74}.Release|x64.ActiveCfg = Release|Any CPU {3879F78A-D6F6-45E5-B2A8-D8DCF2DABB74}.Release|x86.ActiveCfg = Release|Any CPU - {8649ED6B-8504-4D00-BFA5-B8C73CC744DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8649ED6B-8504-4D00-BFA5-B8C73CC744DB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8649ED6B-8504-4D00-BFA5-B8C73CC744DB}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {8649ED6B-8504-4D00-BFA5-B8C73CC744DB}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {8649ED6B-8504-4D00-BFA5-B8C73CC744DB}.Debug|Win32.ActiveCfg = Debug|Any CPU - {8649ED6B-8504-4D00-BFA5-B8C73CC744DB}.Debug|x64.ActiveCfg = Debug|Any CPU - {8649ED6B-8504-4D00-BFA5-B8C73CC744DB}.Debug|x86.ActiveCfg = Debug|Any CPU - {8649ED6B-8504-4D00-BFA5-B8C73CC744DB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8649ED6B-8504-4D00-BFA5-B8C73CC744DB}.Release|Any CPU.Build.0 = Release|Any CPU - {8649ED6B-8504-4D00-BFA5-B8C73CC744DB}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {8649ED6B-8504-4D00-BFA5-B8C73CC744DB}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {8649ED6B-8504-4D00-BFA5-B8C73CC744DB}.Release|Win32.ActiveCfg = Release|Any CPU - {8649ED6B-8504-4D00-BFA5-B8C73CC744DB}.Release|x64.ActiveCfg = Release|Any CPU - {8649ED6B-8504-4D00-BFA5-B8C73CC744DB}.Release|x86.ActiveCfg = Release|Any CPU - {973CA45C-8362-490B-8327-C68098FD4891}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {973CA45C-8362-490B-8327-C68098FD4891}.Debug|Any CPU.Build.0 = Debug|Any CPU - {973CA45C-8362-490B-8327-C68098FD4891}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {973CA45C-8362-490B-8327-C68098FD4891}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {973CA45C-8362-490B-8327-C68098FD4891}.Debug|Win32.ActiveCfg = Debug|Any CPU - {973CA45C-8362-490B-8327-C68098FD4891}.Debug|x64.ActiveCfg = Debug|Any CPU - {973CA45C-8362-490B-8327-C68098FD4891}.Debug|x86.ActiveCfg = Debug|Any CPU - {973CA45C-8362-490B-8327-C68098FD4891}.Release|Any CPU.ActiveCfg = Release|Any CPU - {973CA45C-8362-490B-8327-C68098FD4891}.Release|Any CPU.Build.0 = Release|Any CPU - {973CA45C-8362-490B-8327-C68098FD4891}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {973CA45C-8362-490B-8327-C68098FD4891}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {973CA45C-8362-490B-8327-C68098FD4891}.Release|Win32.ActiveCfg = Release|Any CPU - {973CA45C-8362-490B-8327-C68098FD4891}.Release|x64.ActiveCfg = Release|Any CPU - {973CA45C-8362-490B-8327-C68098FD4891}.Release|x86.ActiveCfg = Release|Any CPU {67310740-0EC4-4DC2-9921-33DF38B20167}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {67310740-0EC4-4DC2-9921-33DF38B20167}.Debug|Any CPU.Build.0 = Debug|Any CPU {67310740-0EC4-4DC2-9921-33DF38B20167}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU @@ -350,6 +305,34 @@ Global {FACAF749-3E28-46DD-B613-654FCD434959}.Release|Win32.ActiveCfg = Release|Any CPU {FACAF749-3E28-46DD-B613-654FCD434959}.Release|x64.ActiveCfg = Release|Any CPU {FACAF749-3E28-46DD-B613-654FCD434959}.Release|x86.ActiveCfg = Release|Any CPU + {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Debug|Win32.ActiveCfg = Debug|Any CPU + {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Debug|x64.ActiveCfg = Debug|Any CPU + {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Debug|x86.ActiveCfg = Debug|Any CPU + {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release|Any CPU.Build.0 = Release|Any CPU + {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release|Win32.ActiveCfg = Release|Any CPU + {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release|x64.ActiveCfg = Release|Any CPU + {C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release|x86.ActiveCfg = Release|Any CPU + {2E781478-814D-4A48-9D80-BFF206441A65}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2E781478-814D-4A48-9D80-BFF206441A65}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2E781478-814D-4A48-9D80-BFF206441A65}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {2E781478-814D-4A48-9D80-BFF206441A65}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {2E781478-814D-4A48-9D80-BFF206441A65}.Debug|Win32.ActiveCfg = Debug|Any CPU + {2E781478-814D-4A48-9D80-BFF206441A65}.Debug|x64.ActiveCfg = Debug|Any CPU + {2E781478-814D-4A48-9D80-BFF206441A65}.Debug|x86.ActiveCfg = Debug|Any CPU + {2E781478-814D-4A48-9D80-BFF206441A65}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2E781478-814D-4A48-9D80-BFF206441A65}.Release|Any CPU.Build.0 = Release|Any CPU + {2E781478-814D-4A48-9D80-BFF206441A65}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {2E781478-814D-4A48-9D80-BFF206441A65}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {2E781478-814D-4A48-9D80-BFF206441A65}.Release|Win32.ActiveCfg = Release|Any CPU + {2E781478-814D-4A48-9D80-BFF206441A65}.Release|x64.ActiveCfg = Release|Any CPU + {2E781478-814D-4A48-9D80-BFF206441A65}.Release|x86.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE -- cgit v1.2.3