From a5f9dc1bfc6c8d37f915d2ea33e495013bd83ba5 Mon Sep 17 00:00:00 2001 From: LukePulverenti Date: Wed, 20 Feb 2013 23:37:50 -0500 Subject: isolated sqlite dependancy --- .../ApiClient.js | 24 +- MediaBrowser.Common/Kernel/BaseKernel.cs | 26 +- MediaBrowser.Common/Kernel/IKernel.cs | 2 +- MediaBrowser.Common/UI/BaseApplication.cs | 13 +- MediaBrowser.Controller/Kernel.cs | 26 +- .../MediaBrowser.Controller.csproj | 20 -- .../SQLite/SQLiteDisplayPreferencesRepository.cs | 139 ---------- .../Persistence/SQLite/SQLiteExtensions.cs | 61 ----- .../Persistence/SQLite/SQLiteItemRepository.cs | 268 ------------------ .../Persistence/SQLite/SQLiteRepository.cs | 301 --------------------- .../Persistence/SQLite/SQLiteUserDataRepository.cs | 138 ---------- .../Persistence/SQLite/SQLiteUserRepository.cs | 147 ---------- MediaBrowser.Controller/packages.config | 1 - MediaBrowser.IsoMounter/IsoManager.cs | 200 -------------- MediaBrowser.IsoMounter/IsoMount.cs | 92 ------- .../MediaBrowser.IsoMounter.csproj | 4 +- MediaBrowser.IsoMounter/PismoIsoManager.cs | 201 ++++++++++++++ MediaBrowser.IsoMounter/PismoMount.cs | 92 +++++++ MediaBrowser.Model/DTO/BaseItemPerson.cs | 5 +- .../MediaBrowser.Plugins.DefaultTheme.csproj | 2 +- .../MediaBrowser.Server.Sqlite.csproj | 99 +++++++ .../Properties/AssemblyInfo.cs | 36 +++ .../SQLiteDisplayPreferencesRepository.cs | 141 ++++++++++ MediaBrowser.Server.Sqlite/SQLiteExtensions.cs | 61 +++++ MediaBrowser.Server.Sqlite/SQLiteItemRepository.cs | 270 ++++++++++++++++++ MediaBrowser.Server.Sqlite/SQLiteRepository.cs | 301 +++++++++++++++++++++ .../SQLiteUserDataRepository.cs | 140 ++++++++++ MediaBrowser.Server.Sqlite/SQLiteUserRepository.cs | 149 ++++++++++ MediaBrowser.Server.Sqlite/packages.config | 4 + MediaBrowser.ServerApplication/App.xaml.cs | 12 +- .../MediaBrowser.ServerApplication.csproj | 3 + MediaBrowser.UI/App.xaml.cs | 14 +- MediaBrowser.UI/Controller/UIKernel.cs | 27 +- MediaBrowser.UI/MediaBrowser.UI.csproj | 2 +- MediaBrowser.sln | 16 ++ 35 files changed, 1573 insertions(+), 1464 deletions(-) delete mode 100644 MediaBrowser.Controller/Persistence/SQLite/SQLiteDisplayPreferencesRepository.cs delete mode 100644 MediaBrowser.Controller/Persistence/SQLite/SQLiteExtensions.cs delete mode 100644 MediaBrowser.Controller/Persistence/SQLite/SQLiteItemRepository.cs delete mode 100644 MediaBrowser.Controller/Persistence/SQLite/SQLiteRepository.cs delete mode 100644 MediaBrowser.Controller/Persistence/SQLite/SQLiteUserDataRepository.cs delete mode 100644 MediaBrowser.Controller/Persistence/SQLite/SQLiteUserRepository.cs delete mode 100644 MediaBrowser.IsoMounter/IsoManager.cs delete mode 100644 MediaBrowser.IsoMounter/IsoMount.cs create mode 100644 MediaBrowser.IsoMounter/PismoIsoManager.cs create mode 100644 MediaBrowser.IsoMounter/PismoMount.cs create mode 100644 MediaBrowser.Server.Sqlite/MediaBrowser.Server.Sqlite.csproj create mode 100644 MediaBrowser.Server.Sqlite/Properties/AssemblyInfo.cs create mode 100644 MediaBrowser.Server.Sqlite/SQLiteDisplayPreferencesRepository.cs create mode 100644 MediaBrowser.Server.Sqlite/SQLiteExtensions.cs create mode 100644 MediaBrowser.Server.Sqlite/SQLiteItemRepository.cs create mode 100644 MediaBrowser.Server.Sqlite/SQLiteRepository.cs create mode 100644 MediaBrowser.Server.Sqlite/SQLiteUserDataRepository.cs create mode 100644 MediaBrowser.Server.Sqlite/SQLiteUserRepository.cs create mode 100644 MediaBrowser.Server.Sqlite/packages.config diff --git a/MediaBrowser.ApiInteraction.Javascript/ApiClient.js b/MediaBrowser.ApiInteraction.Javascript/ApiClient.js index de8f443d8..0b185a55e 100644 --- a/MediaBrowser.ApiInteraction.Javascript/ApiClient.js +++ b/MediaBrowser.ApiInteraction.Javascript/ApiClient.js @@ -815,8 +815,8 @@ var ApiClient = { } // Don't put these on the query string - options.type = null; - options.index = null; + delete options.type; + delete options.index; return ApiClient.getUrl(url, options); }, @@ -849,8 +849,8 @@ var ApiClient = { } // Don't put these on the query string - options.type = null; - options.index = null; + delete options.type; + delete options.index; return ApiClient.getUrl(url, options); }, @@ -883,8 +883,8 @@ var ApiClient = { } // Don't put these on the query string - options.type = null; - options.index = null; + delete options.type; + delete options.index; return ApiClient.getUrl(url, options); }, @@ -917,8 +917,8 @@ var ApiClient = { } // Don't put these on the query string - options.type = null; - options.index = null; + delete options.type; + delete options.index; return ApiClient.getUrl(url, options); }, @@ -951,8 +951,8 @@ var ApiClient = { } // Don't put these on the query string - options.type = null; - options.index = null; + delete options.type; + delete options.index; return ApiClient.getUrl(url, options); }, @@ -987,8 +987,8 @@ var ApiClient = { } // Don't put these on the query string - options.type = null; - options.index = null; + delete options.type; + delete options.index; return ApiClient.getUrl(url, options); }, diff --git a/MediaBrowser.Common/Kernel/BaseKernel.cs b/MediaBrowser.Common/Kernel/BaseKernel.cs index 616a0305c..95e6d05d6 100644 --- a/MediaBrowser.Common/Kernel/BaseKernel.cs +++ b/MediaBrowser.Common/Kernel/BaseKernel.cs @@ -116,7 +116,7 @@ namespace MediaBrowser.Common.Kernel /// The new version. public void OnApplicationUpdated(Version newVersion) { - EventHelper.QueueEventIfNotNull(ApplicationUpdated, this, new GenericEventArgs {Argument = newVersion}); + EventHelper.QueueEventIfNotNull(ApplicationUpdated, this, new GenericEventArgs { Argument = newVersion }); NotifyPendingRestart(); } @@ -258,7 +258,7 @@ namespace MediaBrowser.Common.Kernel /// The rest services. [ImportMany(typeof(IRestfulService))] public IEnumerable RestServices { get; private set; } - + /// /// The _protobuf serializer initialized /// @@ -348,14 +348,20 @@ namespace MediaBrowser.Common.Kernel public Assembly[] Assemblies { get; private set; } /// - /// Initializes the Kernel + /// Initializes a new instance of the class. /// /// The iso manager. - /// Task. - public async Task Init(IIsoManager isoManager) + protected BaseKernel(IIsoManager isoManager) { IsoManager = isoManager; + } + /// + /// Initializes the Kernel + /// + /// Task. + public async Task Init() + { Logger = Logging.LogManager.GetLogger(GetType().Name); ApplicationPaths = new TApplicationPathsType(); @@ -539,10 +545,10 @@ namespace MediaBrowser.Common.Kernel { yield return pluginAssembly; } - + // Include composable parts in the Model assembly - yield return typeof (SystemInfo).Assembly; - + yield return typeof(SystemInfo).Assembly; + // Include composable parts in the Common assembly yield return Assembly.GetExecutingAssembly(); @@ -636,7 +642,7 @@ namespace MediaBrowser.Common.Kernel IsoManager = null; } } - + /// /// Disposes the TCP manager. /// @@ -760,7 +766,7 @@ namespace MediaBrowser.Common.Kernel /// The _save lock /// private readonly object _configurationSaveLock = new object(); - + /// /// Saves the current configuration /// diff --git a/MediaBrowser.Common/Kernel/IKernel.cs b/MediaBrowser.Common/Kernel/IKernel.cs index 1a68dd320..5358f3ab0 100644 --- a/MediaBrowser.Common/Kernel/IKernel.cs +++ b/MediaBrowser.Common/Kernel/IKernel.cs @@ -51,7 +51,7 @@ namespace MediaBrowser.Common.Kernel /// Inits this instance. /// /// Task. - Task Init(IIsoManager isoManager); + Task Init(); /// /// Reloads this instance. diff --git a/MediaBrowser.Common/UI/BaseApplication.cs b/MediaBrowser.Common/UI/BaseApplication.cs index a6e13e3af..72056b59d 100644 --- a/MediaBrowser.Common/UI/BaseApplication.cs +++ b/MediaBrowser.Common/UI/BaseApplication.cs @@ -1,5 +1,4 @@ -using MediaBrowser.Common.IO; -using MediaBrowser.Common.Kernel; +using MediaBrowser.Common.Kernel; using MediaBrowser.Common.Logging; using MediaBrowser.Common.Updates; using MediaBrowser.Model.Logging; @@ -9,7 +8,6 @@ using System.Collections.Generic; using System.ComponentModel; using System.Deployment.Application; using System.Net.Cache; -using System.Reflection; using System.Threading; using System.Threading.Tasks; using System.Windows; @@ -83,13 +81,6 @@ namespace MediaBrowser.Common.UI /// The logger. protected ILogger Logger { get; set; } - /// - /// Instantiates the iso manager. - /// - /// The kernel. - /// IIsoManager. - protected abstract IIsoManager InstantiateIsoManager(IKernel kernel); - /// /// Initializes a new instance of the class. /// @@ -176,7 +167,7 @@ namespace MediaBrowser.Common.UI var now = DateTime.UtcNow; - await Kernel.Init(InstantiateIsoManager(Kernel)); + await Kernel.Init(); var done = (DateTime.UtcNow - now); Logger.Info("Kernel.Init completed in {0}{1} minutes and {2} seconds.", done.Hours > 0 ? done.Hours + " Hours " : "", done.Minutes, done.Seconds); diff --git a/MediaBrowser.Controller/Kernel.cs b/MediaBrowser.Controller/Kernel.cs index 803c4774e..c4d8f3e6c 100644 --- a/MediaBrowser.Controller/Kernel.cs +++ b/MediaBrowser.Controller/Kernel.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Common.Kernel; +using MediaBrowser.Common.IO; +using MediaBrowser.Common.Kernel; using MediaBrowser.Common.Plugins; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Entities; @@ -6,7 +7,6 @@ using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaInfo; using MediaBrowser.Controller.Persistence; -using MediaBrowser.Controller.Persistence.SQLite; using MediaBrowser.Controller.Playback; using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Providers; @@ -19,10 +19,7 @@ using MediaBrowser.Model.System; using System; using System.Collections.Generic; using System.ComponentModel.Composition; -using System.Diagnostics; -using System.IO; using System.Linq; -using System.Reflection; using System.Threading; using System.Threading.Tasks; @@ -303,8 +300,8 @@ namespace MediaBrowser.Controller /// /// Creates a kernel based on a Data path, which is akin to our current programdata path /// - public Kernel() - : base() + public Kernel(IIsoManager isoManager) + : base(isoManager) { Instance = this; } @@ -385,19 +382,19 @@ namespace MediaBrowser.Controller await base.OnComposablePartsLoaded().ConfigureAwait(false); // Get the current item repository - ItemRepository = GetRepository(ItemRepositories, Configuration.ItemRepository, SQLiteItemRepository.RepositoryName); + ItemRepository = GetRepository(ItemRepositories, Configuration.ItemRepository); var itemRepoTask = ItemRepository.Initialize(); // Get the current user repository - UserRepository = GetRepository(UserRepositories, Configuration.UserRepository, SQLiteUserRepository.RepositoryName); + UserRepository = GetRepository(UserRepositories, Configuration.UserRepository); var userRepoTask = UserRepository.Initialize(); // Get the current item repository - UserDataRepository = GetRepository(UserDataRepositories, Configuration.UserDataRepository, SQLiteUserDataRepository.RepositoryName); + UserDataRepository = GetRepository(UserDataRepositories, Configuration.UserDataRepository); var userDataRepoTask = UserDataRepository.Initialize(); // Get the current display preferences repository - DisplayPreferencesRepository = GetRepository(DisplayPreferencesRepositories, Configuration.DisplayPreferencesRepository, SQLiteDisplayPreferencesRepository.RepositoryName); + DisplayPreferencesRepository = GetRepository(DisplayPreferencesRepositories, Configuration.DisplayPreferencesRepository); var displayPreferencesRepoTask = DisplayPreferencesRepository.Initialize(); // Sort the resolvers by priority @@ -418,15 +415,14 @@ namespace MediaBrowser.Controller /// /// The repositories. /// The name. - /// The default name. /// ``0. - private T GetRepository(IEnumerable repositories, string name, string defaultName) + private T GetRepository(IEnumerable repositories, string name) where T : class, IRepository { var enumerable = repositories as T[] ?? repositories.ToArray(); - return enumerable.FirstOrDefault(r => r.Name.Equals(name ?? defaultName, StringComparison.OrdinalIgnoreCase)) ?? - enumerable.First(r => r.Name.Equals(defaultName, StringComparison.OrdinalIgnoreCase)); + return enumerable.FirstOrDefault(r => string.Equals(r.Name, name, StringComparison.OrdinalIgnoreCase)) ?? + enumerable.FirstOrDefault(); } /// diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 88e5ed551..5730b56ed 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -69,14 +69,6 @@ - - False - ..\packages\System.Data.SQLite.1.0.84.0\lib\net45\System.Data.SQLite.dll - - - False - ..\packages\System.Data.SQLite.1.0.84.0\lib\net45\System.Data.SQLite.Linq.dll - @@ -149,12 +141,6 @@ - - - - - - @@ -251,12 +237,6 @@ - - Always - - - Always - diff --git a/MediaBrowser.Controller/Persistence/SQLite/SQLiteDisplayPreferencesRepository.cs b/MediaBrowser.Controller/Persistence/SQLite/SQLiteDisplayPreferencesRepository.cs deleted file mode 100644 index db1535b34..000000000 --- a/MediaBrowser.Controller/Persistence/SQLite/SQLiteDisplayPreferencesRepository.cs +++ /dev/null @@ -1,139 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Model.Entities; -using System; -using System.Collections.Generic; -using System.ComponentModel.Composition; -using System.Data; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Controller.Persistence.SQLite -{ - /// - /// Class SQLiteDisplayPreferencesRepository - /// - [Export(typeof(IDisplayPreferencesRepository))] - 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; - } - } - - /// - /// 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.Controller/Persistence/SQLite/SQLiteExtensions.cs b/MediaBrowser.Controller/Persistence/SQLite/SQLiteExtensions.cs deleted file mode 100644 index f1ed77492..000000000 --- a/MediaBrowser.Controller/Persistence/SQLite/SQLiteExtensions.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using System.Data; -using System.Data.SQLite; - -namespace MediaBrowser.Controller.Persistence.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.Controller/Persistence/SQLite/SQLiteItemRepository.cs b/MediaBrowser.Controller/Persistence/SQLite/SQLiteItemRepository.cs deleted file mode 100644 index 08527f9c1..000000000 --- a/MediaBrowser.Controller/Persistence/SQLite/SQLiteItemRepository.cs +++ /dev/null @@ -1,268 +0,0 @@ -using MediaBrowser.Common.Serialization; -using MediaBrowser.Controller.Entities; -using System; -using System.Collections.Generic; -using System.ComponentModel.Composition; -using System.Data; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Controller.Persistence.SQLite -{ - /// - /// Class SQLiteItemRepository - /// - [Export(typeof(IItemRepository))] - 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; - } - } - - /// - /// 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.Controller/Persistence/SQLite/SQLiteRepository.cs b/MediaBrowser.Controller/Persistence/SQLite/SQLiteRepository.cs deleted file mode 100644 index 5cf57541b..000000000 --- a/MediaBrowser.Controller/Persistence/SQLite/SQLiteRepository.cs +++ /dev/null @@ -1,301 +0,0 @@ -using MediaBrowser.Common.Logging; -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.Controller.Persistence.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; - - protected ILogger Logger { get; private set; } - - /// - /// Connects to DB. - /// - /// The db path. - /// Task{System.Boolean}. - /// - protected async Task ConnectToDB(string dbPath) - { - if (string.IsNullOrEmpty(dbPath)) - { - throw new ArgumentNullException("dbPath"); - } - - Logger = LogManager.GetLogger(GetType().Name); - - 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 - /// - 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. - /// - 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. - /// - 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. - /// - 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.Controller/Persistence/SQLite/SQLiteUserDataRepository.cs b/MediaBrowser.Controller/Persistence/SQLite/SQLiteUserDataRepository.cs deleted file mode 100644 index a027e8475..000000000 --- a/MediaBrowser.Controller/Persistence/SQLite/SQLiteUserDataRepository.cs +++ /dev/null @@ -1,138 +0,0 @@ -using MediaBrowser.Controller.Entities; -using System; -using System.Collections.Generic; -using System.ComponentModel.Composition; -using System.Data; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Controller.Persistence.SQLite -{ - /// - /// Class SQLiteUserDataRepository - /// - [Export(typeof(IUserDataRepository))] - 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; - } - } - - /// - /// 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}. - /// - 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.Controller/Persistence/SQLite/SQLiteUserRepository.cs b/MediaBrowser.Controller/Persistence/SQLite/SQLiteUserRepository.cs deleted file mode 100644 index 9fe5e5624..000000000 --- a/MediaBrowser.Controller/Persistence/SQLite/SQLiteUserRepository.cs +++ /dev/null @@ -1,147 +0,0 @@ -using System.Threading; -using MediaBrowser.Common.Serialization; -using MediaBrowser.Controller.Entities; -using System; -using System.Collections.Generic; -using System.ComponentModel.Composition; -using System.Data; -using System.IO; -using System.Threading.Tasks; - -namespace MediaBrowser.Controller.Persistence.SQLite -{ - /// - /// Class SQLiteUserRepository - /// - [Export(typeof(IUserRepository))] - 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; - } - } - - /// - /// 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.Controller/packages.config b/MediaBrowser.Controller/packages.config index 8a94d35ff..c9c7cab8a 100644 --- a/MediaBrowser.Controller/packages.config +++ b/MediaBrowser.Controller/packages.config @@ -6,5 +6,4 @@ - \ No newline at end of file diff --git a/MediaBrowser.IsoMounter/IsoManager.cs b/MediaBrowser.IsoMounter/IsoManager.cs deleted file mode 100644 index 22f09addf..000000000 --- a/MediaBrowser.IsoMounter/IsoManager.cs +++ /dev/null @@ -1,200 +0,0 @@ -using MediaBrowser.Common.IO; -using MediaBrowser.Common.Kernel; -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.IsoMounter -{ - /// - /// Class IsoManager - /// - public class IsoManager : BaseManager, IIsoManager - { - /// - /// The mount semaphore - limit to four at a time. - /// - private readonly SemaphoreSlim _mountSemaphore = new SemaphoreSlim(4,4); - - /// - /// The PFM API - /// - private PfmApi _pfmApi; - /// - /// The _PFM API initialized - /// - private bool _pfmApiInitialized; - /// - /// The _PFM API sync lock - /// - private object _pfmApiSyncLock = new object(); - /// - /// Gets the display prefs. - /// - /// The display prefs. - private PfmApi PfmApi - { - get - { - LazyInitializer.EnsureInitialized(ref _pfmApi, ref _pfmApiInitialized, ref _pfmApiSyncLock, () => - { - var err = PfmStatic.InstallCheck(); - - if (err != PfmInst.installed) - { - throw new Exception("Pismo File Mount Audit Package is not installed"); - } - - PfmApi pfmApi; - - err = PfmStatic.ApiFactory(out pfmApi); - - if (err != 0) - { - throw new IOException("Unable to open PFM Api. Pismo File Mount Audit Package is probably not installed."); - } - - return pfmApi; - }); - return _pfmApi; - } - } - - /// - /// The _has initialized - /// - private bool _hasInitialized; - - /// - /// Initializes a new instance of the class. - /// - /// The kernel. - public IsoManager(IKernel kernel) - : base(kernel) - { - } - - /// - /// The _my PFM file mount UI - /// - private readonly MyPfmFileMountUi _myPfmFileMountUi = new MyPfmFileMountUi(); - - /// - /// Mounts the specified iso path. - /// - /// The iso path. - /// The cancellation token. - /// if set to true [visible to all processes]. - /// IsoMount. - /// isoPath - /// Unable to create mount. - public async Task Mount(string isoPath, CancellationToken cancellationToken, bool visibleToAllProcesses = true) - { - if (string.IsNullOrEmpty(isoPath)) - { - throw new ArgumentNullException("isoPath"); - } - - PfmFileMount mount; - var err = PfmApi.FileMountCreate(out mount); - - if (err != 0) - { - throw new IOException("Unable to create mount for " + isoPath); - } - - _hasInitialized = true; - - var fmp = new PfmFileMountCreateParams { }; - - fmp.ui = _myPfmFileMountUi; - - fmp.fileMountFlags |= PfmFileMountFlag.inProcess; - - if (visibleToAllProcesses) - { - fmp.visibleProcessId = PfmVisibleProcessId.all; - } - - fmp.mountFileName = isoPath; - - // unc only - fmp.mountFlags |= PfmMountFlag.uncOnly; - fmp.mountFlags |= PfmMountFlag.noShellNotify; - fmp.mountFlags |= PfmMountFlag.readOnly; - - Logger.Info("Mounting {0}", isoPath); - - await _mountSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); - - err = mount.Start(fmp); - - if (err != 0) - { - _mountSemaphore.Release(); - mount.Dispose(); - throw new IOException("Unable to start mount for " + isoPath); - } - - err = mount.WaitReady(); - - if (err != 0) - { - _mountSemaphore.Release(); - mount.Dispose(); - throw new IOException("Unable to start mount for " + isoPath); - } - - return new IsoMount(mount, isoPath, this); - } - - /// - /// 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) - { - if (dispose) - { - if (_hasInitialized) - { - Logger.Info("Disposing PfmPapi"); - _pfmApi.Dispose(); - - Logger.Info("PfmStatic.ApiUnload"); - PfmStatic.ApiUnload(); - } - } - - base.Dispose(dispose); - } - - /// - /// Gets a value indicating whether this instance can mount. - /// - /// The path. - /// true if this instance can mount the specified path; otherwise, false. - /// true if this instance can mount; otherwise, false. - public bool CanMount(string path) - { - try - { - return string.Equals(Path.GetExtension(path), ".iso", StringComparison.OrdinalIgnoreCase) && PfmApi != null; - } - catch - { - return false; - } - } - - /// - /// Called when [unmount]. - /// - /// The mount. - internal void OnUnmount(IsoMount mount) - { - _mountSemaphore.Release(); - } - } -} diff --git a/MediaBrowser.IsoMounter/IsoMount.cs b/MediaBrowser.IsoMounter/IsoMount.cs deleted file mode 100644 index 6524fd234..000000000 --- a/MediaBrowser.IsoMounter/IsoMount.cs +++ /dev/null @@ -1,92 +0,0 @@ -using MediaBrowser.Common.IO; -using MediaBrowser.Common.Logging; -using MediaBrowser.Model.Logging; -using System; - -namespace MediaBrowser.IsoMounter -{ - /// - /// Class IsoMount - /// - internal class IsoMount : IIsoMount - { - /// - /// The logger - /// - private static readonly ILogger Logger = LogManager.GetLogger("IsoMount"); - - /// - /// Gets or sets the iso path. - /// - /// The iso path. - public string IsoPath { get; internal set; } - - /// - /// Gets the mounted path. - /// - /// The mounted path. - public string MountedPath { get; internal set; } - - /// - /// The PFM file mount - /// - private PfmFileMount _pfmFileMount; - - /// - /// The _iso manager - /// - private readonly IsoManager _isoManager; - - /// - /// Prevents a default instance of the class from being created. - /// - /// The mount. - /// The iso path. - /// The iso manager. - internal IsoMount(PfmFileMount mount, string isoPath, IsoManager isoManager) - { - _pfmFileMount = mount; - IsoPath = isoPath; - _isoManager = isoManager; - - MountedPath = mount.GetMount().GetUncName(); - } - - /// - /// 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) - { - UnMount(); - } - - /// - /// Uns the mount. - /// - private void UnMount() - { - if (_pfmFileMount != null) - { - Logger.Info("Unmounting {0}", IsoPath); - - _pfmFileMount.Cancel(); - _pfmFileMount.Detach(); - - _isoManager.OnUnmount(this); - - _pfmFileMount.Dispose(); - _pfmFileMount = null; - } - } - } -} diff --git a/MediaBrowser.IsoMounter/MediaBrowser.IsoMounter.csproj b/MediaBrowser.IsoMounter/MediaBrowser.IsoMounter.csproj index 2d2fe5bae..2dd1fa8b6 100644 --- a/MediaBrowser.IsoMounter/MediaBrowser.IsoMounter.csproj +++ b/MediaBrowser.IsoMounter/MediaBrowser.IsoMounter.csproj @@ -62,8 +62,8 @@ - - + + diff --git a/MediaBrowser.IsoMounter/PismoIsoManager.cs b/MediaBrowser.IsoMounter/PismoIsoManager.cs new file mode 100644 index 000000000..b3a1889f0 --- /dev/null +++ b/MediaBrowser.IsoMounter/PismoIsoManager.cs @@ -0,0 +1,201 @@ +using MediaBrowser.Common.IO; +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Common.Logging; +using MediaBrowser.Model.Logging; + +namespace MediaBrowser.IsoMounter +{ + /// + /// Class IsoManager + /// + public class PismoIsoManager : IIsoManager + { + private ILogger Logger = LogManager.GetLogger("IsoManager"); + + /// + /// The mount semaphore - limit to four at a time. + /// + private readonly SemaphoreSlim _mountSemaphore = new SemaphoreSlim(4,4); + + /// + /// The PFM API + /// + private PfmApi _pfmApi; + /// + /// The _PFM API initialized + /// + private bool _pfmApiInitialized; + /// + /// The _PFM API sync lock + /// + private object _pfmApiSyncLock = new object(); + /// + /// Gets the display prefs. + /// + /// The display prefs. + private PfmApi PfmApi + { + get + { + LazyInitializer.EnsureInitialized(ref _pfmApi, ref _pfmApiInitialized, ref _pfmApiSyncLock, () => + { + var err = PfmStatic.InstallCheck(); + + if (err != PfmInst.installed) + { + throw new Exception("Pismo File Mount Audit Package is not installed"); + } + + PfmApi pfmApi; + + err = PfmStatic.ApiFactory(out pfmApi); + + if (err != 0) + { + throw new IOException("Unable to open PFM Api. Pismo File Mount Audit Package is probably not installed."); + } + + return pfmApi; + }); + return _pfmApi; + } + } + + /// + /// The _has initialized + /// + private bool _hasInitialized; + + public PismoIsoManager() + { + } + + /// + /// The _my PFM file mount UI + /// + private readonly MyPfmFileMountUi _myPfmFileMountUi = new MyPfmFileMountUi(); + + /// + /// Mounts the specified iso path. + /// + /// The iso path. + /// The cancellation token. + /// if set to true [visible to all processes]. + /// IsoMount. + /// isoPath + /// Unable to create mount. + public async Task Mount(string isoPath, CancellationToken cancellationToken, bool visibleToAllProcesses = true) + { + if (string.IsNullOrEmpty(isoPath)) + { + throw new ArgumentNullException("isoPath"); + } + + PfmFileMount mount; + var err = PfmApi.FileMountCreate(out mount); + + if (err != 0) + { + throw new IOException("Unable to create mount for " + isoPath); + } + + _hasInitialized = true; + + var fmp = new PfmFileMountCreateParams { }; + + fmp.ui = _myPfmFileMountUi; + + fmp.fileMountFlags |= PfmFileMountFlag.inProcess; + + if (visibleToAllProcesses) + { + fmp.visibleProcessId = PfmVisibleProcessId.all; + } + + fmp.mountFileName = isoPath; + + // unc only + fmp.mountFlags |= PfmMountFlag.uncOnly; + fmp.mountFlags |= PfmMountFlag.noShellNotify; + fmp.mountFlags |= PfmMountFlag.readOnly; + + Logger.Info("Mounting {0}", isoPath); + + await _mountSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); + + err = mount.Start(fmp); + + if (err != 0) + { + _mountSemaphore.Release(); + mount.Dispose(); + throw new IOException("Unable to start mount for " + isoPath); + } + + err = mount.WaitReady(); + + if (err != 0) + { + _mountSemaphore.Release(); + mount.Dispose(); + throw new IOException("Unable to start mount for " + isoPath); + } + + return new PismoMount(mount, isoPath, this); + } + + 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) + { + if (_hasInitialized) + { + Logger.Info("Disposing PfmPapi"); + _pfmApi.Dispose(); + + Logger.Info("PfmStatic.ApiUnload"); + PfmStatic.ApiUnload(); + } + } + } + + /// + /// Gets a value indicating whether this instance can mount. + /// + /// The path. + /// true if this instance can mount the specified path; otherwise, false. + /// true if this instance can mount; otherwise, false. + public bool CanMount(string path) + { + try + { + return string.Equals(Path.GetExtension(path), ".iso", StringComparison.OrdinalIgnoreCase) && PfmApi != null; + } + catch + { + return false; + } + } + + /// + /// Called when [unmount]. + /// + /// The mount. + internal void OnUnmount(PismoMount mount) + { + _mountSemaphore.Release(); + } + } +} diff --git a/MediaBrowser.IsoMounter/PismoMount.cs b/MediaBrowser.IsoMounter/PismoMount.cs new file mode 100644 index 000000000..1e3dbb881 --- /dev/null +++ b/MediaBrowser.IsoMounter/PismoMount.cs @@ -0,0 +1,92 @@ +using MediaBrowser.Common.IO; +using MediaBrowser.Common.Logging; +using MediaBrowser.Model.Logging; +using System; + +namespace MediaBrowser.IsoMounter +{ + /// + /// Class IsoMount + /// + internal class PismoMount : IIsoMount + { + /// + /// The logger + /// + private static readonly ILogger Logger = LogManager.GetLogger("IsoMount"); + + /// + /// Gets or sets the iso path. + /// + /// The iso path. + public string IsoPath { get; internal set; } + + /// + /// Gets the mounted path. + /// + /// The mounted path. + public string MountedPath { get; internal set; } + + /// + /// The PFM file mount + /// + private PfmFileMount _pfmFileMount; + + /// + /// The _iso manager + /// + private readonly PismoIsoManager _isoManager; + + /// + /// Prevents a default instance of the class from being created. + /// + /// The mount. + /// The iso path. + /// The iso manager. + internal PismoMount(PfmFileMount mount, string isoPath, PismoIsoManager isoManager) + { + _pfmFileMount = mount; + IsoPath = isoPath; + _isoManager = isoManager; + + MountedPath = mount.GetMount().GetUncName(); + } + + /// + /// 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) + { + UnMount(); + } + + /// + /// Uns the mount. + /// + private void UnMount() + { + if (_pfmFileMount != null) + { + Logger.Info("Unmounting {0}", IsoPath); + + _pfmFileMount.Cancel(); + _pfmFileMount.Detach(); + + _isoManager.OnUnmount(this); + + _pfmFileMount.Dispose(); + _pfmFileMount = null; + } + } + } +} diff --git a/MediaBrowser.Model/DTO/BaseItemPerson.cs b/MediaBrowser.Model/DTO/BaseItemPerson.cs index 6bb78541d..442dfa8a4 100644 --- a/MediaBrowser.Model/DTO/BaseItemPerson.cs +++ b/MediaBrowser.Model/DTO/BaseItemPerson.cs @@ -46,7 +46,10 @@ namespace MediaBrowser.Model.DTO [IgnoreDataMember] public bool HasPrimaryImage { - get { return PrimaryImageTag.HasValue; } + get + { + return PrimaryImageTag.HasValue; + } } /// diff --git a/MediaBrowser.Plugins.DefaultTheme/MediaBrowser.Plugins.DefaultTheme.csproj b/MediaBrowser.Plugins.DefaultTheme/MediaBrowser.Plugins.DefaultTheme.csproj index b915f55c1..0ba40e814 100644 --- a/MediaBrowser.Plugins.DefaultTheme/MediaBrowser.Plugins.DefaultTheme.csproj +++ b/MediaBrowser.Plugins.DefaultTheme/MediaBrowser.Plugins.DefaultTheme.csproj @@ -342,7 +342,7 @@ - xcopy "$(TargetPath)" "$(SolutionDir)\MediaBrowser.UI\" /y + xcopy "$(TargetPath)" "$(SolutionDir)\MediaBrowser.UI\CorePlugins\" /y + \ No newline at end of file diff --git a/MediaBrowser.Server.Sqlite/Properties/AssemblyInfo.cs b/MediaBrowser.Server.Sqlite/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..905af0e8c --- /dev/null +++ b/MediaBrowser.Server.Sqlite/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +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 +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/MediaBrowser.Server.Sqlite/SQLiteDisplayPreferencesRepository.cs b/MediaBrowser.Server.Sqlite/SQLiteDisplayPreferencesRepository.cs new file mode 100644 index 000000000..be45a82dd --- /dev/null +++ b/MediaBrowser.Server.Sqlite/SQLiteDisplayPreferencesRepository.cs @@ -0,0 +1,141 @@ +using MediaBrowser.Controller; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Persistence; +using MediaBrowser.Model.Entities; +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.Data; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Server.Sqlite +{ + /// + /// Class SQLiteDisplayPreferencesRepository + /// + [Export(typeof(IDisplayPreferencesRepository))] + 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; + } + } + + /// + /// 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 new file mode 100644 index 000000000..f9f79a8b7 --- /dev/null +++ b/MediaBrowser.Server.Sqlite/SQLiteExtensions.cs @@ -0,0 +1,61 @@ +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 new file mode 100644 index 000000000..c381de85b --- /dev/null +++ b/MediaBrowser.Server.Sqlite/SQLiteItemRepository.cs @@ -0,0 +1,270 @@ +using MediaBrowser.Common.Serialization; +using MediaBrowser.Controller; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Persistence; +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.Data; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Server.Sqlite +{ + /// + /// Class SQLiteItemRepository + /// + [Export(typeof(IItemRepository))] + 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; + } + } + + /// + /// 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 new file mode 100644 index 000000000..b84b336dc --- /dev/null +++ b/MediaBrowser.Server.Sqlite/SQLiteRepository.cs @@ -0,0 +1,301 @@ +using MediaBrowser.Common.Logging; +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; + + protected ILogger Logger { get; private set; } + + /// + /// Connects to DB. + /// + /// The db path. + /// Task{System.Boolean}. + /// + protected async Task ConnectToDB(string dbPath) + { + if (string.IsNullOrEmpty(dbPath)) + { + throw new ArgumentNullException("dbPath"); + } + + Logger = LogManager.GetLogger(GetType().Name); + + 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 + /// + 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. + /// + 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. + /// + 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. + /// + 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 new file mode 100644 index 000000000..c9d4db3ba --- /dev/null +++ b/MediaBrowser.Server.Sqlite/SQLiteUserDataRepository.cs @@ -0,0 +1,140 @@ +using MediaBrowser.Controller; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Persistence; +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.Data; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Server.Sqlite +{ + /// + /// Class SQLiteUserDataRepository + /// + [Export(typeof(IUserDataRepository))] + 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; + } + } + + /// + /// 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}. + /// + 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 new file mode 100644 index 000000000..f300f5177 --- /dev/null +++ b/MediaBrowser.Server.Sqlite/SQLiteUserRepository.cs @@ -0,0 +1,149 @@ +using MediaBrowser.Common.Serialization; +using MediaBrowser.Controller; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Persistence; +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.Data; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Server.Sqlite +{ + /// + /// Class SQLiteUserRepository + /// + [Export(typeof(IUserRepository))] + 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; + } + } + + /// + /// 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 new file mode 100644 index 000000000..106618814 --- /dev/null +++ b/MediaBrowser.Server.Sqlite/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/MediaBrowser.ServerApplication/App.xaml.cs b/MediaBrowser.ServerApplication/App.xaml.cs index 387ae9577..487b60485 100644 --- a/MediaBrowser.ServerApplication/App.xaml.cs +++ b/MediaBrowser.ServerApplication/App.xaml.cs @@ -75,16 +75,6 @@ namespace MediaBrowser.ServerApplication get { return "MediaBrowser.Server.Uninstall.exe"; } } - /// - /// Instantiates the iso manager. - /// - /// The kernel. - /// IIsoManager. - protected override IIsoManager InstantiateIsoManager(IKernel kernel) - { - return new IsoManager(kernel); - } - /// /// Called when [second instance launched]. /// @@ -180,7 +170,7 @@ namespace MediaBrowser.ServerApplication /// IKernel. protected override IKernel InstantiateKernel() { - return new Kernel(); + return new Kernel(new PismoIsoManager()); } /// diff --git a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj index f7799557e..74e437564 100644 --- a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj +++ b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj @@ -320,6 +320,9 @@ Always + + Always + Always diff --git a/MediaBrowser.UI/App.xaml.cs b/MediaBrowser.UI/App.xaml.cs index 7d627e0a9..bae133ab7 100644 --- a/MediaBrowser.UI/App.xaml.cs +++ b/MediaBrowser.UI/App.xaml.cs @@ -244,7 +244,7 @@ namespace MediaBrowser.UI /// IKernel. protected override IKernel InstantiateKernel() { - return new UIKernel(); + return new UIKernel(new PismoIsoManager()); } /// @@ -258,16 +258,6 @@ namespace MediaBrowser.UI return HiddenWindow; } - /// - /// Instantiates the iso manager. - /// - /// The kernel. - /// IIsoManager. - protected override IIsoManager InstantiateIsoManager(IKernel kernel) - { - return new IsoManager(kernel); - } - /// /// Shows the application window. /// @@ -368,7 +358,7 @@ namespace MediaBrowser.UI { var now = DateTime.UtcNow; - await Kernel.Init(InstantiateIsoManager(Kernel)); + await Kernel.Init(); Logger.Info("Kernel.Init completed in {0} seconds.", (DateTime.UtcNow - now).TotalSeconds); diff --git a/MediaBrowser.UI/Controller/UIKernel.cs b/MediaBrowser.UI/Controller/UIKernel.cs index be313e153..2c06e7b93 100644 --- a/MediaBrowser.UI/Controller/UIKernel.cs +++ b/MediaBrowser.UI/Controller/UIKernel.cs @@ -1,7 +1,5 @@ -using System.Net; -using System.Net.Cache; -using System.Net.Http; -using MediaBrowser.ApiInteraction; +using MediaBrowser.ApiInteraction; +using MediaBrowser.Common.IO; using MediaBrowser.Common.Kernel; using MediaBrowser.Common.Logging; using MediaBrowser.Model.Connectivity; @@ -14,6 +12,9 @@ using System.ComponentModel.Composition; using System.Diagnostics; using System.IO; using System.Linq; +using System.Net; +using System.Net.Cache; +using System.Net.Http; using System.Reflection; using System.Threading.Tasks; @@ -45,8 +46,8 @@ namespace MediaBrowser.UI.Controller /// /// Initializes a new instance of the class. /// - public UIKernel() - : base() + public UIKernel(IIsoManager isoManager) + : base(isoManager) { Instance = this; } @@ -147,20 +148,6 @@ namespace MediaBrowser.UI.Controller return base.ReloadInternal(); } - /// - /// Gets the composable part assemblies. - /// - /// IEnumerable{Assembly}. - protected override IEnumerable GetComposablePartAssemblies() - { - var runningDirectory = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName); - - return base.GetComposablePartAssemblies().Concat(new[] { - - Assembly.Load(File.ReadAllBytes(Path.Combine(runningDirectory, "MediaBrowser.Plugins.DefaultTheme.dll"))) - }); - } - /// /// Called when [composable parts loaded]. /// diff --git a/MediaBrowser.UI/MediaBrowser.UI.csproj b/MediaBrowser.UI/MediaBrowser.UI.csproj index d2db097aa..8d6432ebb 100644 --- a/MediaBrowser.UI/MediaBrowser.UI.csproj +++ b/MediaBrowser.UI/MediaBrowser.UI.csproj @@ -397,7 +397,7 @@ Always - + Always diff --git a/MediaBrowser.sln b/MediaBrowser.sln index 1e9f922a9..9e35fdc9e 100644 --- a/MediaBrowser.sln +++ b/MediaBrowser.sln @@ -45,6 +45,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Plugins.Dlna", 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 Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -287,6 +289,20 @@ 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 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE -- cgit v1.2.3