diff --git a/DBMerger/DBMerger.csproj b/DBMerger/DBMerger.csproj index f1d76bdc..562eb8af 100644 --- a/DBMerger/DBMerger.csproj +++ b/DBMerger/DBMerger.csproj @@ -1,4 +1,4 @@ - + ..\build\Cef\ @@ -37,13 +37,6 @@ false - - true - true - false - false - - diff --git a/Dotnet/Cef/CefService.cs b/Dotnet/Cef/CefService.cs index d0985bbb..69eabc90 100644 --- a/Dotnet/Cef/CefService.cs +++ b/Dotnet/Cef/CefService.cs @@ -89,11 +89,18 @@ namespace VRCX if (File.Exists(vueDevtoolsCrxPath)) { var vueDevtoolsPath = Path.Join(extensionsPath, "Vue-js-devtools"); - if (!Directory.Exists(vueDevtoolsPath)) + try { + if (Directory.Exists(vueDevtoolsPath)) + Directory.Delete(vueDevtoolsPath, true); + Directory.CreateDirectory(vueDevtoolsPath); ZipFile.ExtractToDirectory(vueDevtoolsCrxPath, vueDevtoolsPath); } + catch (Exception ex) + { + logger.Error(ex, "Failed to extract Vue Devtools"); + } } // load extensions diff --git a/Dotnet/Cef/JavascriptBindings.cs b/Dotnet/Cef/JavascriptBindings.cs index d9cd4ec9..f6e32d62 100644 --- a/Dotnet/Cef/JavascriptBindings.cs +++ b/Dotnet/Cef/JavascriptBindings.cs @@ -10,7 +10,7 @@ namespace VRCX repository.Register("AppApi", Program.AppApiInstance); repository.Register("WebApi", WebApi.Instance); repository.Register("VRCXStorage", VRCXStorage.Instance); - repository.Register("SQLite", SQLiteLegacy.Instance); + repository.Register("SQLite", SQLite.Instance); repository.Register("LogWatcher", LogWatcher.Instance); repository.Register("Discord", Discord.Instance); repository.Register("AssetBundleManager", AssetBundleManager.Instance); diff --git a/Dotnet/Program.cs b/Dotnet/Program.cs index 6eccf791..26bfa6be 100644 --- a/Dotnet/Program.cs +++ b/Dotnet/Program.cs @@ -230,7 +230,7 @@ namespace VRCX logger.Info("Launch Command: {0}", StartupArgs.LaunchArguments.LaunchCommand); logger.Debug("Wine detection: {0}", Wine.GetIfWine()); - SQLiteLegacy.Instance.Init(); + SQLite.Instance.Init(); AppApiInstance = new AppApiCef(); AppApiVrInstance = new AppApiVrCef(); @@ -262,7 +262,7 @@ namespace VRCX Discord.Instance.Exit(); SystemMonitorCef.Instance.Exit(); VRCXStorage.Instance.Save(); - SQLiteLegacy.Instance.Exit(); + SQLite.Instance.Exit(); ProcessMonitor.Instance.Exit(); } #else diff --git a/Dotnet/SQLiteLegacy.cs b/Dotnet/SQLite.cs similarity index 95% rename from Dotnet/SQLiteLegacy.cs rename to Dotnet/SQLite.cs index 08762a9b..32a69747 100644 --- a/Dotnet/SQLiteLegacy.cs +++ b/Dotnet/SQLite.cs @@ -8,18 +8,18 @@ using System.Text.Json; namespace VRCX { - public class SQLiteLegacy + public class SQLite { - public static SQLiteLegacy Instance; + public static SQLite Instance; private readonly ReaderWriterLockSlim m_ConnectionLock; private SQLiteConnection m_Connection; - static SQLiteLegacy() + static SQLite() { - Instance = new SQLiteLegacy(); + Instance = new SQLite(); } - public SQLiteLegacy() + public SQLite() { m_ConnectionLock = new ReaderWriterLockSlim(); } diff --git a/Dotnet/ScreenshotMetadata/ScreenshotMetadataDatabase.cs b/Dotnet/ScreenshotMetadata/ScreenshotMetadataDatabase.cs index 65470a58..af76c5e0 100644 --- a/Dotnet/ScreenshotMetadata/ScreenshotMetadataDatabase.cs +++ b/Dotnet/ScreenshotMetadata/ScreenshotMetadataDatabase.cs @@ -1,23 +1,14 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using SQLite; +using System.Data.SQLite; namespace VRCX { - [Table("cache")] public class MetadataCache { - [PrimaryKey, AutoIncrement] - [Column("id")] public int Id { get; set; } - [Column("file_path"), NotNull, Indexed] public string FilePath { get; set; } - [Column("metadata")] - public string Metadata { get; set; } - [Column("cached_at"), NotNull] + public string? Metadata { get; set; } public DateTimeOffset CachedAt { get; set; } } @@ -25,61 +16,135 @@ namespace VRCX // Couldn't be me... oh wait internal class ScreenshotMetadataDatabase { - private SQLiteConnection sqlite; + private readonly SQLiteConnection _sqlite; public ScreenshotMetadataDatabase(string databaseLocation) { - var options = new SQLiteConnectionString(databaseLocation, true); - sqlite = new SQLiteConnection(options); - - sqlite.CreateTable(); + _sqlite = new SQLiteConnection($"Data Source=\"{databaseLocation}\";Version=3;PRAGMA locking_mode=NORMAL;PRAGMA busy_timeout=5000;PRAGMA journal_mode=WAL;PRAGMA optimize=0x10002;", true); + _sqlite.Open(); + using var cmd = new SQLiteCommand(_sqlite); + cmd.CommandText = @"CREATE TABLE IF NOT EXISTS cache ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + file_path TEXT NOT NULL UNIQUE, + metadata TEXT, + cached_at INTEGER NOT NULL + );"; + cmd.ExecuteNonQuery(); } public void AddMetadataCache(string filePath, string metadata) { + // old table schema didn't have filePath as unique + var isFileCached = IsFileCached(filePath); + if (isFileCached != -1) + return; + var cache = new MetadataCache() { FilePath = filePath, Metadata = metadata, CachedAt = DateTimeOffset.Now }; - sqlite.Insert(cache); + const string sql = "INSERT OR REPLACE INTO cache (file_path, metadata, cached_at) VALUES (@FilePath, @Metadata, @CachedAt);"; + using var command = new SQLiteCommand(sql, _sqlite); + command.Parameters.AddWithValue("@FilePath", cache.FilePath); + command.Parameters.AddWithValue("@Metadata", cache.Metadata); + command.Parameters.AddWithValue("@CachedAt", cache.CachedAt.Ticks); + command.ExecuteNonQuery(); } public void BulkAddMetadataCache(IEnumerable cache) { - sqlite.InsertAll(cache, runInTransaction: true); + using var transaction = _sqlite.BeginTransaction(); + using var command = new SQLiteCommand(_sqlite); + const string sql = "INSERT OR REPLACE INTO cache (file_path, metadata, cached_at) VALUES (@FilePath, @Metadata, @CachedAt);"; + command.CommandText = sql; + var filePathParam = command.Parameters.Add("@FilePath", System.Data.DbType.String); + var metadataParam = command.Parameters.Add("@Metadata", System.Data.DbType.String); + var cachedAtParam = command.Parameters.Add("@CachedAt", System.Data.DbType.Int64); + foreach (var item in cache) + { + var isFileCached = IsFileCached(item.FilePath); + if (isFileCached != -1) + continue; + + filePathParam.Value = item.FilePath; + metadataParam.Value = item.Metadata; + cachedAtParam.Value = item.CachedAt.Ticks; + command.ExecuteNonQuery(); + } + transaction.Commit(); } public int IsFileCached(string filePath) { - var query = sqlite.Table().Where(c => c.FilePath == filePath).Select(c => c.Id); - - if (query.Any()) + const string sql = "SELECT id FROM cache WHERE file_path = @FilePath;"; + using var command = new SQLiteCommand(sql, _sqlite); + command.Parameters.AddWithValue("@FilePath", filePath); + using var reader = command.ExecuteReader(); + var result = new List(); + while (reader.Read()) { - return query.First(); + result.Add(reader.GetInt32(0)); } - else + if (result.Count > 0) { - return -1; + return result[0]; } + return -1; } - public string GetMetadata(string filePath) + public string? GetMetadata(string filePath) { - var query = sqlite.Table().Where(c => c.FilePath == filePath).Select(c => c.Metadata); - return query.FirstOrDefault(); + const string sql = "SELECT id, file_path, metadata, cached_at FROM cache WHERE file_path = @FilePath;"; + using var command = new SQLiteCommand(sql, _sqlite); + command.Parameters.AddWithValue("@FilePath", filePath); + using var reader = command.ExecuteReader(); + var result = new List(); + while (reader.Read()) + { + result.Add(new MetadataCache() + { + Id = reader.GetInt32(0), + FilePath = reader.GetString(1), + Metadata = reader.IsDBNull(2) ? null : reader.GetString(2), + CachedAt = new DateTime(reader.GetInt64(3)) + }); + } + if (result.Count > 0) + { + return result[0].Metadata; + } + return null; } - public string GetMetadataById(int id) + public string? GetMetadataById(int id) { - var query = sqlite.Table().Where(c => c.Id == id).Select(c => c.Metadata); - return query.FirstOrDefault(); + const string sql = "SELECT id, file_path, metadata, cached_at FROM cache WHERE id = @Id;"; + using var command = new SQLiteCommand(sql, _sqlite); + command.Parameters.AddWithValue("@Id", id); + using var reader = command.ExecuteReader(); + var result = new List(); + while (reader.Read()) + { + result.Add(new MetadataCache() + { + Id = reader.GetInt32(0), + FilePath = reader.GetString(1), + Metadata = reader.IsDBNull(2) ? null : reader.GetString(2), + CachedAt = new DateTime(reader.GetInt64(3)) + }); + } + if (result.Count > 0) + { + return result[0].Metadata; + } + return null; } public void Close() { - sqlite.Close(); + _sqlite.Close(); } } } diff --git a/Dotnet/VRCX-Cef.csproj b/Dotnet/VRCX-Cef.csproj index 79769a39..b4ffaaf8 100644 --- a/Dotnet/VRCX-Cef.csproj +++ b/Dotnet/VRCX-Cef.csproj @@ -55,12 +55,7 @@ app.manifest - - true - true - false - false - + @@ -103,12 +98,10 @@ - - + - diff --git a/Dotnet/VRCX-Electron.csproj b/Dotnet/VRCX-Electron.csproj index 60bae5fe..2bbc981f 100644 --- a/Dotnet/VRCX-Electron.csproj +++ b/Dotnet/VRCX-Electron.csproj @@ -54,12 +54,6 @@ false - - true - true - false - false - libs\Blake2Sharp.dll @@ -79,14 +73,6 @@ PreserveNewest openvr_api.dll - - PreserveNewest - SQLite.Interop.dll - - - PreserveNewest - SQLite.Interop.dll - PreserveNewest @@ -98,19 +84,14 @@ - - - - - + - diff --git a/Dotnet/WebApi.cs b/Dotnet/WebApi.cs index 8760610e..c8461bca 100644 --- a/Dotnet/WebApi.cs +++ b/Dotnet/WebApi.cs @@ -121,8 +121,8 @@ namespace VRCX private void LoadCookies() { - SQLiteLegacy.Instance.ExecuteNonQuery("CREATE TABLE IF NOT EXISTS `cookies` (`key` TEXT PRIMARY KEY, `value` TEXT)"); - var values = SQLiteLegacy.Instance.Execute("SELECT `value` FROM `cookies` WHERE `key` = @key", + SQLite.Instance.ExecuteNonQuery("CREATE TABLE IF NOT EXISTS `cookies` (`key` TEXT PRIMARY KEY, `value` TEXT)"); + var values = SQLite.Instance.Execute("SELECT `value` FROM `cookies` WHERE `key` = @key", new Dictionary { { "@key", "default" } @@ -190,7 +190,7 @@ namespace VRCX var cookies = GetAllCookies(); using var memoryStream = new MemoryStream(); System.Text.Json.JsonSerializer.Serialize(memoryStream, cookies); - SQLiteLegacy.Instance.ExecuteNonQuery( + SQLite.Instance.ExecuteNonQuery( "INSERT OR REPLACE INTO `cookies` (`key`, `value`) VALUES (@key, @value)", new Dictionary() { {"@key", "default"}, diff --git a/Dotnet/libs/linux/SQLite.Interop.dll b/Dotnet/libs/linux/SQLite.Interop.dll deleted file mode 100644 index 6cc6b465..00000000 Binary files a/Dotnet/libs/linux/SQLite.Interop.dll and /dev/null differ diff --git a/Dotnet/libs/macos/SQLite.Interop.dll b/Dotnet/libs/macos/SQLite.Interop.dll deleted file mode 100644 index e320817f..00000000 Binary files a/Dotnet/libs/macos/SQLite.Interop.dll and /dev/null differ diff --git a/src-electron/main.js b/src-electron/main.js index 07181568..1c13211f 100644 --- a/src-electron/main.js +++ b/src-electron/main.js @@ -101,7 +101,7 @@ const version = getVersion(); interopApi.getDotNetObject('ProgramElectron').PreInit(version, args); interopApi.getDotNetObject('VRCXStorage').Load(); interopApi.getDotNetObject('ProgramElectron').Init(); -interopApi.getDotNetObject('SQLiteLegacy').Init(); +interopApi.getDotNetObject('SQLite').Init(); interopApi.getDotNetObject('AppApiElectron').Init(); interopApi.getDotNetObject('Discord').Init(); interopApi.getDotNetObject('WebApi').Init(); diff --git a/src/plugin/ipc.js b/src/plugin/ipc.js index a79257bc..fc80b32a 100644 --- a/src/plugin/ipc.js +++ b/src/plugin/ipc.js @@ -16,9 +16,9 @@ if (WINDOWS) { window.AppApi = InteropApi.AppApiElectron; window.WebApi = InteropApi.WebApi; window.VRCXStorage = InteropApi.VRCXStorage; - window.SQLite = InteropApi.SQLiteLegacy; + window.SQLite = InteropApi.SQLite; window.LogWatcher = InteropApi.LogWatcher; window.Discord = InteropApi.Discord; window.AssetBundleManager = InteropApi.AssetBundleManager; window.AppApiVrElectron = InteropApi.AppApiVrElectron; -} \ No newline at end of file +}