feat: add database
This commit is contained in:
parent
ada2003ac8
commit
1e50cfe32d
43
Command.cs
43
Command.cs
|
@ -67,15 +67,44 @@ internal class Command : IDisposable {
|
|||
}
|
||||
|
||||
string hash;
|
||||
await using (var stream = new Blake3Stream(this.OpenFile(ext, meta))) {
|
||||
var (inner, path) = this.OpenFile(ext, meta);
|
||||
await using (var stream = new Blake3Stream(inner)) {
|
||||
await stream.WriteAsync(imageData);
|
||||
hash = Convert.ToHexString(stream.ComputeHash().AsSpan());
|
||||
}
|
||||
|
||||
meta.Blake3Hash = hash;
|
||||
var json = JsonConvert.SerializeObject(meta, Formatting.Indented);
|
||||
// TODO: save this info to database
|
||||
Plugin.Log.Info(json);
|
||||
var saved = new SavedMetadata {
|
||||
Path = path,
|
||||
Blake3Hash = hash,
|
||||
Metadata = meta,
|
||||
};
|
||||
|
||||
this.Plugin.Database.Execute(
|
||||
"""
|
||||
insert into screenshots
|
||||
(hash, path, active_character, location, location_sub, area, area_sub, territory_type, world, world_id, captured_at_local, captured_at_utc, eorzea_time, weather, ward, plot, visible_characters)
|
||||
values ($hash, $path, jsonb($active_character), $location, $location_sub, $area, $area_sub, $territory_type, $world, $world_id, $captured_at_local, $captured_at_utc, $eorzea_time, $weather, $ward, $plot, jsonb($visible_characters))
|
||||
""",
|
||||
new Dictionary<string, object?> {
|
||||
["$hash"] = saved.Blake3Hash,
|
||||
["$path"] = saved.Path,
|
||||
["$active_character"] = JsonConvert.SerializeObject(saved.Metadata.ActiveCharacter),
|
||||
["$location"] = saved.Metadata.Location,
|
||||
["$location_sub"] = saved.Metadata.LocationSub,
|
||||
["$area"] = saved.Metadata.Area,
|
||||
["$area_sub"] = saved.Metadata.AreaSub,
|
||||
["$territory_type"] = saved.Metadata.TerritoryType,
|
||||
["$world"] = saved.Metadata.World,
|
||||
["$world_id"] = saved.Metadata.WorldId,
|
||||
["$captured_at_local"] = saved.Metadata.CapturedAtLocal,
|
||||
["$captured_at_utc"] = saved.Metadata.CapturedAtUtc,
|
||||
["$eorzea_time"] = saved.Metadata.EorzeaTime,
|
||||
["$weather"] = saved.Metadata.Weather,
|
||||
["$ward"] = saved.Metadata.Ward,
|
||||
["$plot"] = saved.Metadata.Plot,
|
||||
["$visible_characters"] = JsonConvert.SerializeObject(saved.Metadata.VisibleCharacters),
|
||||
}
|
||||
);
|
||||
|
||||
var message = new SeStringBuilder()
|
||||
.AddText("Screenshot saved. [")
|
||||
|
@ -114,7 +143,7 @@ internal class Command : IDisposable {
|
|||
return (stream.ToArray(), ext);
|
||||
}
|
||||
|
||||
private FileStream OpenFile(string ext, ScreenshotMetadata meta) {
|
||||
private (FileStream, string) OpenFile(string ext, ScreenshotMetadata meta) {
|
||||
Directory.CreateDirectory(this.Plugin.Config.SaveDirectory);
|
||||
|
||||
var fileName = this.Plugin.Config.SaveFileNameTemplate.Render(meta).ReplaceLineEndings(" ");
|
||||
|
@ -125,6 +154,6 @@ internal class Command : IDisposable {
|
|||
var parent = Path.Join(path, "..");
|
||||
Directory.CreateDirectory(parent);
|
||||
|
||||
return new FileStream(path, FileMode.Create, FileAccess.Write);
|
||||
return (new FileStream(path, FileMode.Create, FileAccess.Write), path);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
using System.Data.SQLite;
|
||||
|
||||
namespace Screenie;
|
||||
|
||||
internal class Database : IDisposable {
|
||||
private Plugin Plugin { get; }
|
||||
private SQLiteConnection Connection { get; }
|
||||
|
||||
internal Database(Plugin plugin) {
|
||||
this.Plugin = plugin;
|
||||
|
||||
var dir = this.Plugin.Interface.GetPluginConfigDirectory();
|
||||
Directory.CreateDirectory(dir);
|
||||
var dbPath = Path.Join(dir, "database.sqlite");
|
||||
|
||||
this.Connection = new SQLiteConnection($"Data Source={dbPath}");
|
||||
this.Connection.Open();
|
||||
this.RunMigrations();
|
||||
}
|
||||
|
||||
internal Database(string dir) {
|
||||
Directory.CreateDirectory(dir);
|
||||
var dbPath = Path.Join(dir, "database.sqlite");
|
||||
|
||||
this.Connection = new SQLiteConnection($"Data Source={dbPath}");
|
||||
this.Connection.Open();
|
||||
this.RunMigrations();
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
this.Connection.Close();
|
||||
this.Connection.Dispose();
|
||||
}
|
||||
|
||||
private void RunMigrations() {
|
||||
using var t = this.Connection.BeginTransaction();
|
||||
this.Execute("create table if not exists _migrations (id int not null primary key, query text not null)");
|
||||
|
||||
// get latest migration
|
||||
long max;
|
||||
{
|
||||
using var reader = this.Query("select coalesce(max(id), 0) from _migrations");
|
||||
if (!reader.Read()) {
|
||||
throw new Exception("could not get max migration");
|
||||
}
|
||||
|
||||
max = reader.GetFieldValue<long>(0);
|
||||
}
|
||||
|
||||
if (max < 1) {
|
||||
const string migrationOne = """
|
||||
create table screenshots (
|
||||
hash text not null primary key,
|
||||
path text not null,
|
||||
active_character jsonb not null,
|
||||
location text,
|
||||
location_sub text,
|
||||
area text,
|
||||
area_sub text,
|
||||
territory_type int not null,
|
||||
world text,
|
||||
world_id int not null,
|
||||
captured_at_local timestamp,
|
||||
captured_at_utc timestamp,
|
||||
eorzea_time text not null,
|
||||
weather text,
|
||||
ward int,
|
||||
plot int,
|
||||
visible_characters jsonb not null
|
||||
)
|
||||
""";
|
||||
this.Execute(migrationOne);
|
||||
this.Execute("insert into _migrations (id, query) values (1, $query)", new Dictionary<string, object?> {
|
||||
["$query"] = migrationOne,
|
||||
});
|
||||
}
|
||||
|
||||
t.Commit();
|
||||
}
|
||||
|
||||
private SQLiteCommand GetCommand(string query, Dictionary<string, object?>? parameters = null) {
|
||||
var command = this.Connection.CreateCommand();
|
||||
command.CommandText = query;
|
||||
if (parameters != null) {
|
||||
foreach (var (key, value) in parameters) {
|
||||
command.Parameters.AddWithValue(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
internal SQLiteDataReader Query(string query, Dictionary<string, object?>? parameters = null) {
|
||||
using var command = this.GetCommand(query, parameters);
|
||||
return command.ExecuteReader();
|
||||
}
|
||||
|
||||
internal int Execute(string query, Dictionary<string, object?>? parameters = null) {
|
||||
using var command = this.GetCommand(query, parameters);
|
||||
return command.ExecuteNonQuery();
|
||||
}
|
||||
}
|
|
@ -36,6 +36,7 @@ public class Plugin : IDalamudPlugin {
|
|||
internal IObjectTable ObjectTable { get; init; }
|
||||
|
||||
internal Configuration Config { get; }
|
||||
internal Database Database { get; }
|
||||
internal LinkHandlers LinkHandlers { get; }
|
||||
internal PluginUi Ui { get; }
|
||||
|
||||
|
@ -43,6 +44,7 @@ public class Plugin : IDalamudPlugin {
|
|||
|
||||
public Plugin() {
|
||||
this.Config = this.Interface!.GetPluginConfig() as Configuration ?? new Configuration();
|
||||
this.Database = new Database(this);
|
||||
this.LinkHandlers = new LinkHandlers(this);
|
||||
this.Ui = new PluginUi(this);
|
||||
this.Command = new Command(this);
|
||||
|
@ -52,9 +54,14 @@ public class Plugin : IDalamudPlugin {
|
|||
this.Command.Dispose();
|
||||
this.Ui.Dispose();
|
||||
this.LinkHandlers.Dispose();
|
||||
this.Database.Dispose();
|
||||
}
|
||||
|
||||
internal void SaveConfig() {
|
||||
this.Interface.SavePluginConfig(this.Config);
|
||||
}
|
||||
|
||||
public static void Main() {
|
||||
using var db = new Database("/home/anna/code/Screenie");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,12 +56,15 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Blake3" Version="1.0.0" />
|
||||
<PackageReference Include="Blake3" Version="1.0.0"/>
|
||||
<PackageReference Include="DalamudPackager" Version="2.1.12"/>
|
||||
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.49-beta">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Scriban" Version="5.9.1" />
|
||||
<PackageReference Include="Penumbra.Api" Version="1.0.14"/>
|
||||
<PackageReference Include="Scriban" Version="5.9.1"/>
|
||||
<PackageReference Include="SQLite" Version="3.13.0"/>
|
||||
<PackageReference Include="System.Data.SQLite.Core" Version="1.0.118"/>
|
||||
<PackageReference Include="System.Drawing.Common" Version="8.0.2"/>
|
||||
<PackageReference Include="WebP_Net" Version="1.1.1"/>
|
||||
</ItemGroup>
|
||||
|
|
|
@ -13,8 +13,14 @@ using Map = Lumina.Excel.GeneratedSheets.Map;
|
|||
namespace Screenie;
|
||||
|
||||
[Serializable]
|
||||
public class ScreenshotMetadata {
|
||||
public class SavedMetadata {
|
||||
public required string Blake3Hash;
|
||||
public required string Path;
|
||||
public required ScreenshotMetadata Metadata;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class ScreenshotMetadata {
|
||||
public required Character? ActiveCharacter;
|
||||
public required string? Location;
|
||||
public required string? LocationSub;
|
||||
|
@ -105,7 +111,6 @@ public class ScreenshotMetadata {
|
|||
.ToArray();
|
||||
|
||||
return new ScreenshotMetadata {
|
||||
Blake3Hash = "",
|
||||
ActiveCharacter = active,
|
||||
Location = map?.PlaceName.Value?.Name.ToDalamudString().TextValue.WhitespaceToNull()
|
||||
?? territory?.PlaceName.Value?.Name.ToDalamudString().TextValue.WhitespaceToNull(),
|
||||
|
|
|
@ -95,7 +95,15 @@ internal class PluginUi : IDisposable {
|
|||
|
||||
ImGui.TextUnformatted("Filename format");
|
||||
ImGui.SetNextItemWidth(-1);
|
||||
anyChanged |= ImGui.InputTextMultiline("##filename-format", ref this.Plugin.Config.SaveFileNameFormat, 1024, new Vector2(-1, 150));
|
||||
ImGui.PushFont(UiBuilder.MonoFont);
|
||||
using (new OnDispose(ImGui.PopFont)) {
|
||||
anyChanged |= ImGui.InputTextMultiline(
|
||||
"##filename-format",
|
||||
ref this.Plugin.Config.SaveFileNameFormat,
|
||||
2048,
|
||||
new Vector2(-1, 150)
|
||||
);
|
||||
}
|
||||
|
||||
var template = this.Plugin.Config.SaveFileNameTemplate;
|
||||
if (template.HasErrors) {
|
||||
|
@ -142,14 +150,18 @@ internal class PluginUi : IDisposable {
|
|||
ImGui.SameLine();
|
||||
|
||||
if (ImGuiComponents.IconButton(FontAwesomeIcon.Folder)) {
|
||||
this.FileDialogManager.OpenFolderDialog("Choose screenshots folder", (b, s) => {
|
||||
if (!b) {
|
||||
return;
|
||||
}
|
||||
this.FileDialogManager.OpenFolderDialog(
|
||||
"Choose screenshots folder",
|
||||
(b, s) => {
|
||||
if (!b) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.Plugin.Config.SaveDirectory = s;
|
||||
this.Plugin.SaveConfig();
|
||||
});
|
||||
this.Plugin.Config.SaveDirectory = s;
|
||||
this.Plugin.SaveConfig();
|
||||
},
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)
|
||||
);
|
||||
}
|
||||
|
||||
return changed;
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"sdk": {
|
||||
"version": "8.0.0",
|
||||
"rollForward": "latestMajor",
|
||||
"allowPrerelease": true
|
||||
}
|
||||
}
|
|
@ -27,12 +27,33 @@
|
|||
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
|
||||
}
|
||||
},
|
||||
"Penumbra.Api": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.0.14, )",
|
||||
"resolved": "1.0.14",
|
||||
"contentHash": "KqUuD4d30uhczvfH2YtcbwBxGnovBQAIudZ99eRxK0NeHwnQ3vdKe2BO2gZ2ZzT3JV6lu0NvosXXqqcQ0ZtqLw=="
|
||||
},
|
||||
"Scriban": {
|
||||
"type": "Direct",
|
||||
"requested": "[5.9.1, )",
|
||||
"resolved": "5.9.1",
|
||||
"contentHash": "Er0jZCXrHXtk+nnzmHVEnmz1pjfU+VL3GppO0UjtCMoZ0Se1plyPe1OLb6gM7ToSRA7nu/QIcdRFr29x8w8rQQ=="
|
||||
},
|
||||
"SQLite": {
|
||||
"type": "Direct",
|
||||
"requested": "[3.13.0, )",
|
||||
"resolved": "3.13.0",
|
||||
"contentHash": "MJfRiz2p6aMVOxrxGMdVzhpzI0oxTgZSwC8eVuOpV8L7yYaFUu8q/OFYwv9P0/aZ/pdEu24O6gma6wZJMTun9A=="
|
||||
},
|
||||
"System.Data.SQLite.Core": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.0.118, )",
|
||||
"resolved": "1.0.118",
|
||||
"contentHash": "2V1PsfBeqWlZxF/VtB8lQKPfDBayCU8zD5Xc3Mq7cILOa2ZqpPDSwMP0fTfk1gtGSStSk//DxKiGy6zwCQs7Uw==",
|
||||
"dependencies": {
|
||||
"Stub.System.Data.SQLite.Core.NetStandard": "[1.0.118]"
|
||||
}
|
||||
},
|
||||
"System.Drawing.Common": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.0.2, )",
|
||||
|
@ -74,6 +95,11 @@
|
|||
"Microsoft.Windows.SDK.Win32Metadata": "55.0.45-preview"
|
||||
}
|
||||
},
|
||||
"Stub.System.Data.SQLite.Core.NetStandard": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.0.118",
|
||||
"contentHash": "4TS8IZvDj0ud6utxfXI6zv9Ditk4U9Kt9KqLyAIQGcU3GXp5oGBHgGZifq+APcqRCayuN/MSE8t9ZZmygtU28A=="
|
||||
},
|
||||
"System.Memory": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.5",
|
||||
|
|
Loading…
Reference in New Issue