using System.Security.Cryptography; using Microsoft.Data.Sqlite; namespace ytLive.Services; public partial class LayoutStore : IDisposable { public byte[]? GetAssetBytes(string assetId) { using var cmd = _connection.CreateCommand(); cmd.CommandText = "SELECT Data FROM Asset WHERE Id = $id"; cmd.Parameters.AddWithValue("$id", assetId); return cmd.ExecuteScalar() as byte[]; } public string UpsertAsset(byte[] data, int pixelWidth, int pixelHeight) { var hash = Convert.ToHexString(SHA256.HashData(data)); var id = Guid.NewGuid().ToString(); using var cmd = _connection.CreateCommand(); cmd.CommandText = """ INSERT INTO Asset (Id, Hash, Data, PixelWidth, PixelHeight) VALUES ($id, $hash, $data, $w, $h) ON CONFLICT(Hash) DO NOTHING; """; cmd.Parameters.AddWithValue("$id", id); cmd.Parameters.AddWithValue("$hash", hash); cmd.Parameters.AddWithValue("$data", data); cmd.Parameters.AddWithValue("$w", pixelWidth); cmd.Parameters.AddWithValue("$h", pixelHeight); cmd.ExecuteNonQuery(); cmd.CommandText = "SELECT Id FROM Asset WHERE Hash = $hash;"; return (string)cmd.ExecuteScalar()!; } // ─── License (TASK 10 — Polar billing) ─── /// The saved license key, or null if never entered. }