From 6239b16987a6b52ce50eebf92fd3a0134cf2180f Mon Sep 17 00:00:00 2001 From: Jaex Date: Tue, 8 Jul 2025 08:40:18 +0300 Subject: [PATCH] Added HistoryManagerSQLite --- ShareX.HistoryLib/HistoryItem.cs | 2 + ShareX.HistoryLib/HistoryManager.cs | 6 +- ShareX.HistoryLib/HistoryManagerJSON.cs | 2 +- ShareX.HistoryLib/HistoryManagerMock.cs | 2 +- ShareX.HistoryLib/HistoryManagerSQLite.cs | 310 +++++++++++++++++++++ ShareX.HistoryLib/HistoryManagerXML.cs | 2 +- ShareX.HistoryLib/ShareX.HistoryLib.csproj | 3 + 7 files changed, 321 insertions(+), 6 deletions(-) create mode 100644 ShareX.HistoryLib/HistoryManagerSQLite.cs diff --git a/ShareX.HistoryLib/HistoryItem.cs b/ShareX.HistoryLib/HistoryItem.cs index de8c80bb7..143b16081 100644 --- a/ShareX.HistoryLib/HistoryItem.cs +++ b/ShareX.HistoryLib/HistoryItem.cs @@ -32,6 +32,8 @@ namespace ShareX.HistoryLib { public class HistoryItem { + [JsonIgnore] + public long Id { get; set; } public string FileName { get; set; } public string FilePath { get; set; } public DateTime DateTime { get; set; } diff --git a/ShareX.HistoryLib/HistoryManager.cs b/ShareX.HistoryLib/HistoryManager.cs index 671475c76..a2ee25072 100644 --- a/ShareX.HistoryLib/HistoryManager.cs +++ b/ShareX.HistoryLib/HistoryManager.cs @@ -65,7 +65,7 @@ namespace ShareX.HistoryLib public async Task> GetHistoryItemsAsync() { - return await Task.Run(() => GetHistoryItems()); + return await Task.Run(GetHistoryItems); } public bool AppendHistoryItem(HistoryItem historyItem) @@ -93,12 +93,12 @@ namespace ShareX.HistoryLib (!string.IsNullOrEmpty(historyItem.URL) || !string.IsNullOrEmpty(historyItem.FilePath)); } - protected List Load() + internal List Load() { return Load(FilePath); } - protected abstract List Load(string filePath); + internal abstract List Load(string filePath); protected bool Append(IEnumerable historyItems) { diff --git a/ShareX.HistoryLib/HistoryManagerJSON.cs b/ShareX.HistoryLib/HistoryManagerJSON.cs index 370c008c8..aeb77c94f 100644 --- a/ShareX.HistoryLib/HistoryManagerJSON.cs +++ b/ShareX.HistoryLib/HistoryManagerJSON.cs @@ -40,7 +40,7 @@ namespace ShareX.HistoryLib { } - protected override List Load(string filePath) + internal override List Load(string filePath) { if (!string.IsNullOrEmpty(filePath) && File.Exists(filePath)) { diff --git a/ShareX.HistoryLib/HistoryManagerMock.cs b/ShareX.HistoryLib/HistoryManagerMock.cs index 215129633..d442b1e8f 100644 --- a/ShareX.HistoryLib/HistoryManagerMock.cs +++ b/ShareX.HistoryLib/HistoryManagerMock.cs @@ -38,7 +38,7 @@ namespace ShareX.HistoryLib { } - protected override List Load(string filePath) + internal override List Load(string filePath) { List items = new List(); diff --git a/ShareX.HistoryLib/HistoryManagerSQLite.cs b/ShareX.HistoryLib/HistoryManagerSQLite.cs new file mode 100644 index 000000000..0723d423d --- /dev/null +++ b/ShareX.HistoryLib/HistoryManagerSQLite.cs @@ -0,0 +1,310 @@ +#region License Information (GPL v3) + +/* + ShareX - A program that allows you to take screenshots and share any file type + Copyright (c) 2007-2025 ShareX Team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + Optionally you can also view the license at . +*/ + +#endregion License Information (GPL v3) + +using ShareX.HelpersLib; +using System; +using System.Collections.Generic; +using System.Data.SQLite; + +namespace ShareX.HistoryLib +{ + public class HistoryManagerSQLite : HistoryManager + { + private readonly string connectionString; + private static readonly object dbLock = new object(); + + public HistoryManagerSQLite(string dbFilePath) : base(dbFilePath) + { + FileHelpers.CreateDirectoryFromFilePath(dbFilePath); + connectionString = $"Data Source={dbFilePath};Version=3;"; + EnsureDatabase(); + } + + private void EnsureDatabase() + { + lock (dbLock) + { + using (SQLiteConnection connection = new SQLiteConnection(connectionString)) + { + connection.Open(); + + using (SQLiteCommand cmd = connection.CreateCommand()) + { + cmd.CommandText = @" +CREATE TABLE IF NOT EXISTS History ( + Id INTEGER PRIMARY KEY AUTOINCREMENT, + FileName TEXT, + FilePath TEXT, + DateTime TEXT, + Type TEXT, + Host TEXT, + URL TEXT, + ThumbnailURL TEXT, + DeletionURL TEXT, + ShortenedURL TEXT +); + +CREATE TABLE IF NOT EXISTS Tags ( + HistoryId INTEGER, + Key TEXT, + Value TEXT, + FOREIGN KEY(HistoryId) REFERENCES History(Id) +); +"; + cmd.ExecuteNonQuery(); + } + } + } + } + + internal override List Load(string dbPath) + { + List items = new List(); + + lock (dbLock) + { + using (SQLiteConnection connection = new SQLiteConnection(connectionString)) + { + connection.Open(); + + string sql = @"SELECT * FROM History;"; + using (SQLiteCommand cmd = new SQLiteCommand(sql, connection)) + using (SQLiteDataReader reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + HistoryItem item = new HistoryItem() + { + Id = (long)reader["Id"], + FileName = reader["FileName"].ToString(), + FilePath = reader["FilePath"].ToString(), + DateTime = DateTime.Parse(reader["DateTime"].ToString()), + Type = reader["Type"].ToString(), + Host = reader["Host"].ToString(), + URL = reader["URL"].ToString(), + ThumbnailURL = reader["ThumbnailURL"].ToString(), + DeletionURL = reader["DeletionURL"].ToString(), + ShortenedURL = reader["ShortenedURL"].ToString(), + Tags = new Dictionary() + }; + + items.Add(item); + } + } + + string tagSql = @"SELECT HistoryId, Key, Value FROM Tags;"; + using (SQLiteCommand cmd = new SQLiteCommand(tagSql, connection)) + using (SQLiteDataReader reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + long historyId = (long)reader["HistoryId"]; + string key = reader["Key"].ToString(); + string value = reader["Value"].ToString(); + + HistoryItem item = items.Find(i => i.Id == historyId); + + if (item != null) + { + item.Tags[key] = value; + } + } + } + } + } + + return items; + } + + protected override bool Append(string dbPath, IEnumerable historyItems) + { + lock (dbLock) + { + using (SQLiteConnection connection = new SQLiteConnection(connectionString)) + { + connection.Open(); + + using (SQLiteTransaction transaction = connection.BeginTransaction()) + { + foreach (HistoryItem item in historyItems) + { + long newId; + + using (SQLiteCommand cmd = connection.CreateCommand()) + { + cmd.CommandText = @" +INSERT INTO History +(FileName, FilePath, DateTime, Type, Host, URL, ThumbnailURL, DeletionURL, ShortenedURL) +VALUES (@FileName, @FilePath, @DateTime, @Type, @Host, @URL, @ThumbnailURL, @DeletionURL, @ShortenedURL); +SELECT last_insert_rowid();"; + cmd.Parameters.AddWithValue("@FileName", item.FileName); + cmd.Parameters.AddWithValue("@FilePath", item.FilePath); + cmd.Parameters.AddWithValue("@DateTime", item.DateTime.ToString("o")); + cmd.Parameters.AddWithValue("@Type", item.Type); + cmd.Parameters.AddWithValue("@Host", item.Host); + cmd.Parameters.AddWithValue("@URL", item.URL); + cmd.Parameters.AddWithValue("@ThumbnailURL", item.ThumbnailURL); + cmd.Parameters.AddWithValue("@DeletionURL", item.DeletionURL); + cmd.Parameters.AddWithValue("@ShortenedURL", item.ShortenedURL); + newId = (long)cmd.ExecuteScalar(); + } + + if (item.Tags != null) + { + foreach (KeyValuePair kvp in item.Tags) + { + using (SQLiteCommand tagCmd = connection.CreateCommand()) + { + tagCmd.CommandText = @" +INSERT INTO Tags (HistoryId, Key, Value) VALUES (@HistoryId, @Key, @Value);"; + tagCmd.Parameters.AddWithValue("@HistoryId", newId); + tagCmd.Parameters.AddWithValue("@Key", kvp.Key); + tagCmd.Parameters.AddWithValue("@Value", kvp.Value); + tagCmd.ExecuteNonQuery(); + } + } + } + } + + transaction.Commit(); + } + } + } + + return true; + } + + public void Edit(HistoryItem item) + { + lock (dbLock) + { + using (SQLiteConnection connection = new SQLiteConnection(connectionString)) + { + connection.Open(); + + using (SQLiteTransaction transaction = connection.BeginTransaction()) + { + using (SQLiteCommand cmd = connection.CreateCommand()) + { + cmd.CommandText = @" +UPDATE History SET +FileName = @FileName, +FilePath = @FilePath, +DateTime = @DateTime, +Type = @Type, +Host = @Host, +URL = @URL, +ThumbnailURL = @ThumbnailURL, +DeletionURL = @DeletionURL, +ShortenedURL = @ShortenedURL +WHERE Id = @Id;"; + cmd.Parameters.AddWithValue("@FileName", item.FileName); + cmd.Parameters.AddWithValue("@FilePath", item.FilePath); + cmd.Parameters.AddWithValue("@DateTime", item.DateTime.ToString("o")); + cmd.Parameters.AddWithValue("@Type", item.Type); + cmd.Parameters.AddWithValue("@Host", item.Host); + cmd.Parameters.AddWithValue("@URL", item.URL); + cmd.Parameters.AddWithValue("@ThumbnailURL", item.ThumbnailURL); + cmd.Parameters.AddWithValue("@DeletionURL", item.DeletionURL); + cmd.Parameters.AddWithValue("@ShortenedURL", item.ShortenedURL); + cmd.Parameters.AddWithValue("@Id", item.Id); + cmd.ExecuteNonQuery(); + } + + using (SQLiteCommand cmd = connection.CreateCommand()) + { + cmd.CommandText = "DELETE FROM Tags WHERE HistoryId = @HistoryId;"; + cmd.Parameters.AddWithValue("@HistoryId", item.Id); + cmd.ExecuteNonQuery(); + } + + if (item.Tags != null) + { + foreach (KeyValuePair kvp in item.Tags) + { + using (SQLiteCommand cmd = connection.CreateCommand()) + { + cmd.CommandText = "INSERT INTO Tags (HistoryId, Key, Value) VALUES (@HistoryId, @Key, @Value);"; + cmd.Parameters.AddWithValue("@HistoryId", item.Id); + cmd.Parameters.AddWithValue("@Key", kvp.Key); + cmd.Parameters.AddWithValue("@Value", kvp.Value); + cmd.ExecuteNonQuery(); + } + } + } + + transaction.Commit(); + } + } + } + } + + public void Delete(HistoryItem item) + { + lock (dbLock) + { + using (SQLiteConnection connection = new SQLiteConnection(connectionString)) + { + connection.Open(); + + using (SQLiteTransaction transaction = connection.BeginTransaction()) + { + using (SQLiteCommand cmd = connection.CreateCommand()) + { + cmd.CommandText = "DELETE FROM Tags WHERE HistoryId = @HistoryId;"; + cmd.Parameters.AddWithValue("@HistoryId", item.Id); + cmd.ExecuteNonQuery(); + } + + using (SQLiteCommand cmd = connection.CreateCommand()) + { + cmd.CommandText = "DELETE FROM History WHERE Id = @Id;"; + cmd.Parameters.AddWithValue("@Id", item.Id); + cmd.ExecuteNonQuery(); + } + + transaction.Commit(); + } + } + } + } + + public static string MigrateFromJSON(string jsonFilePath) + { + string dbFilePath = FileHelpers.ChangeFileNameExtension(jsonFilePath, "db"); + + HistoryManagerJSON jsonManager = new HistoryManagerJSON(jsonFilePath); + List items = jsonManager.Load(jsonFilePath); + + if (items.Count > 0) + { + HistoryManagerSQLite historyManagerSQLite = new HistoryManagerSQLite(dbFilePath); + historyManagerSQLite.Append(items); + } + + return dbFilePath; + } + } +} \ No newline at end of file diff --git a/ShareX.HistoryLib/HistoryManagerXML.cs b/ShareX.HistoryLib/HistoryManagerXML.cs index 68f24cc13..cfdfd456f 100644 --- a/ShareX.HistoryLib/HistoryManagerXML.cs +++ b/ShareX.HistoryLib/HistoryManagerXML.cs @@ -41,7 +41,7 @@ namespace ShareX.HistoryLib { } - protected override List Load(string filePath) + internal override List Load(string filePath) { List historyItemList = new List(); diff --git a/ShareX.HistoryLib/ShareX.HistoryLib.csproj b/ShareX.HistoryLib/ShareX.HistoryLib.csproj index 64c572f4d..c5cba6322 100644 --- a/ShareX.HistoryLib/ShareX.HistoryLib.csproj +++ b/ShareX.HistoryLib/ShareX.HistoryLib.csproj @@ -4,6 +4,9 @@ Library true + + +