WIP: icon database

This commit is contained in:
Ray 2021-09-21 13:28:52 +01:00
parent 682af23f55
commit 4dd399f537
9 changed files with 342 additions and 46 deletions

View File

@ -33,13 +33,12 @@ namespace FizzyLauncher
private System.ComponentModel.IContainer components; private System.ComponentModel.IContainer components;
protected bool isBusy = false; protected bool isBusy = false;
protected string faviconAddress = null;
protected WebParser webParser = null; protected WebParser webParser = null;
protected WebClient webClient = null; protected WebClient webClient = null;
public BookmarkForm(BookmarkItem model) : base() public BookmarkForm(BookmarkItem model, Image icon) : base()
{ {
InitializeComponent(); InitializeComponent();
@ -47,7 +46,7 @@ namespace FizzyLauncher
textBox2.Text = model.SiteAddress?.Trim() ?? string.Empty; textBox2.Text = model.SiteAddress?.Trim() ?? string.Empty;
memoBox1.Text = model.SiteDescription?.Trim() ?? string.Empty; memoBox1.Text = model.SiteDescription?.Trim() ?? string.Empty;
memoBox2.Text = model.Notes?.Trim() ?? string.Empty; memoBox2.Text = model.Notes?.Trim() ?? string.Empty;
faviconAddress = model.FaviconAddress; pictureBox1.Image = icon;
textBox2.SetTooltipText(toolTip1, "Retrieve web page information"); textBox2.SetTooltipText(toolTip1, "Retrieve web page information");
} }
@ -308,19 +307,21 @@ namespace FizzyLauncher
} }
public BookmarkItem Model public BookmarkResult Model
{ {
get => new BookmarkItem() get => new BookmarkResult()
{
Item = new BookmarkItem()
{ {
SiteName = textBox1.Text?.Trim() ?? string.Empty, SiteName = textBox1.Text?.Trim() ?? string.Empty,
SiteAddress = textBox2.Text?.Trim() ?? string.Empty, SiteAddress = textBox2.Text?.Trim() ?? string.Empty,
SiteDescription = memoBox1.Text?.Trim() ?? string.Empty, SiteDescription = memoBox1.Text?.Trim() ?? string.Empty,
Notes = memoBox2.Text?.Trim() ?? string.Empty, Notes = memoBox2.Text?.Trim() ?? string.Empty
FaviconAddress = faviconAddress },
Icon = pictureBox1.Image
}; };
} }
public Image Favicon { get => pictureBox1.Image; }
protected bool IsBusy protected bool IsBusy
{ {
@ -362,23 +363,25 @@ namespace FizzyLauncher
return; return;
} }
BookmarkItem rs = webParser.RetrieveDetails(textBox2.Text); BookmarkResult rs = webParser.RetrieveDetails(textBox2.Text);
if (rs == null) if (rs == null)
{ {
IsBusy = false; IsBusy = false;
return; return;
} }
if (!string.IsNullOrWhiteSpace(rs.SiteName)) ThreadControl.SetText(textBox1, rs.SiteName); if (rs.Item == null) rs.Item = new BookmarkItem();
if (!string.IsNullOrWhiteSpace(rs.SiteDescription)) ThreadControl.SetText(memoBox1, rs.SiteDescription);
if (string.IsNullOrWhiteSpace(rs.FaviconAddress)) if (!string.IsNullOrWhiteSpace(rs.Item.SiteName)) ThreadControl.SetText(textBox1, rs.Item.SiteName);
if (!string.IsNullOrWhiteSpace(rs.Item.SiteDescription)) ThreadControl.SetText(memoBox1, rs.Item.SiteDescription);
if (string.IsNullOrWhiteSpace(rs.IconURL))
{ {
ThreadControl.Clear(pictureBox1); ThreadControl.Clear(pictureBox1);
} }
else else
{ {
ThreadControl.SetValue(pictureBox1, RetrieveImage(rs.FaviconAddress)); ThreadControl.SetValue(pictureBox1, RetrieveImage(rs.IconURL));
} }
IsBusy = false; IsBusy = false;

View File

@ -142,6 +142,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="HtmlAgilityPack" Version="1.11.34" /> <PackageReference Include="HtmlAgilityPack" Version="1.11.34" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="5.0.10" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="SharpZipLib" Version="1.3.2" /> <PackageReference Include="SharpZipLib" Version="1.3.2" />
</ItemGroup> </ItemGroup>

15
Classes/BookmarkResult.cs Normal file
View File

@ -0,0 +1,15 @@
using bzit.bomg.Models;
using System.Drawing;
namespace BookmarkManager
{
public class BookmarkResult
{
public BookmarkItem Item { get; set; } = null;
public string IconURL { get; set; } = null;
public Image Icon { get; set; } = null;
}
}

22
Classes/Crypto.cs Normal file
View File

@ -0,0 +1,22 @@
using System;
using System.Security.Cryptography;
using System.Text;
namespace BookmarkManager
{
public class Crypto
{
public static string GetSHA256Hash(string text)
{
if (string.IsNullOrWhiteSpace(text)) return string.Empty;
using (var sha = new SHA256Managed())
{
byte[] hash = sha.ComputeHash(Encoding.UTF8.GetBytes(text));
return BitConverter.ToString(hash).Replace("-", string.Empty) + "+" + text.Length.ToString();
}
}
}
}

244
Classes/IconDatabase.cs Normal file
View File

@ -0,0 +1,244 @@
using Microsoft.Data.Sqlite;
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
namespace BookmarkManager
{
public class IconDatabase
{
protected string dbFilename = null;
protected SqliteConnection dbConnection = null;
public IconDatabase(string filename)
{
if (string.IsNullOrWhiteSpace(filename)) return;
dbFilename = filename;
bool isNew = !File.Exists(dbFilename);
SqliteConnectionStringBuilder builder = new SqliteConnectionStringBuilder();
builder.DataSource = dbFilename;
builder.Cache = SqliteCacheMode.Private;
builder.Mode = SqliteOpenMode.ReadWriteCreate;
dbConnection = new SqliteConnection(builder.ToString());
dbConnection.Open();
if (isNew) Initialise();
//if (File.Exists(filename))
//{
// File.Delete("test.db3");
//}
}
public bool AddIcon(string id, Image image)
{
if (dbConnection == null) return false;
if (string.IsNullOrWhiteSpace(id)) return false;
if (image == null) return DeleteIcon(id);
if (HasIcon(id)) return UpdateIcon(id, image);
SqliteCommand command = new SqliteCommand();
command.Connection = dbConnection;
command.CommandText = "INSERT INTO icons (id, content) VALUES (@id, @content)";
command.Parameters.Add("@id", SqliteType.Text).Value = id;
command.Parameters.Add("@content", SqliteType.Blob, 20).Value = ImageToBytes(image);
try
{
command.ExecuteNonQuery();
}
catch (Exception)
{
return false;
}
return true;
}
public void Close()
{
if (dbConnection == null) return;
dbConnection.Close();
dbConnection.Dispose();
dbConnection = null;
}
public bool DeleteIcon(string id)
{
if (dbConnection == null) return false;
if (string.IsNullOrWhiteSpace(id)) return false;
SqliteCommand command = new SqliteCommand();
command.Connection = dbConnection;
command.CommandText = "DELETE FROM icons WHERE id = @id";
command.Parameters.Add("@id", SqliteType.Text).Value = id;
try
{
command.ExecuteNonQuery();
}
catch (Exception)
{
return false;
}
return true;
}
public Image FindIcon(string id)
{
if (dbConnection == null) return null;
if (string.IsNullOrWhiteSpace(id)) return null;
byte[] iconBytes = null;
SqliteCommand command = new SqliteCommand();
command.Connection = dbConnection;
command.CommandText = "SELECT content FROM icons WHERE @id";
command.Parameters.Add("@id", SqliteType.Text).Value = id;
try
{
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
if (reader["content"] != null && !Convert.IsDBNull(reader["content"]))
{
iconBytes = (byte[])reader["content"];
}
}
}
command.Dispose();
}
catch (Exception)
{
return null;
}
if (iconBytes == null)
{
return null;
}
Image img = null;
try
{
img = Image.FromStream(new MemoryStream(iconBytes));
}
catch (Exception)
{
return null;
}
return new Bitmap(img, 16, 16);
}
public bool HasIcon(string id)
{
if (dbConnection == null) return false;
if (string.IsNullOrWhiteSpace(id)) return false;
int rv = 0;
SqliteCommand command = new SqliteCommand();
command.Connection = dbConnection;
command.CommandText = "SELECT COUNT(1) AS count FROM icons WHERE @id";
command.Parameters.Add("@id", SqliteType.Text).Value = id;
try
{
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
if (reader["count"] != null && !Convert.IsDBNull(reader["count"]))
{
rv = (int)reader["count"];
}
}
}
command.Dispose();
}
catch (Exception)
{
return false;
}
return (rv > 0);
}
public bool UpdateIcon(string id, Image image)
{
if (dbConnection == null) return false;
if (string.IsNullOrWhiteSpace(id)) return false;
if (image == null) return DeleteIcon(id);
if (!HasIcon(id)) return AddIcon(id, image);
SqliteCommand command = new SqliteCommand();
command.Connection = dbConnection;
command.CommandText = "UPDATE icons SET content = @content WHERE id = @id";
command.Parameters.Add("@id", SqliteType.Text).Value = id;
command.Parameters.Add("@content", SqliteType.Blob, 20).Value = ImageToBytes(image);
try
{
command.ExecuteNonQuery();
}
catch (Exception)
{
return false;
}
return true;
}
protected bool Initialise()
{
if (dbConnection == null) return false;
try
{
SqliteCommand command = new SqliteCommand("CREATE TABLE icons (id TEXT PRIMARY KEY, content BLOB)", dbConnection);
command.ExecuteNonQuery();
command.Dispose();
}
catch (Exception)
{
return false;
}
return true;
}
protected byte[] ImageToBytes(Image image)
{
MemoryStream stream = new MemoryStream();
//image.Save(stream, ((image.RawFormat == null) ? ImageFormat.Bmp : new ImageFormat(image.RawFormat.Guid)));
image.Save(stream, ImageFormat.Bmp);
stream.Close();
return stream.ToArray();
}
}
}

View File

@ -11,7 +11,7 @@ namespace BookmarkManager
protected HttpWeb webClient = null; protected HttpWeb webClient = null;
public BookmarkItem RetrieveDetails(string url) public BookmarkResult RetrieveDetails(string url)
{ {
string sourceCode = retrieveSourceCode(url); string sourceCode = retrieveSourceCode(url);
if (string.IsNullOrWhiteSpace(sourceCode)) if (string.IsNullOrWhiteSpace(sourceCode))
@ -19,24 +19,25 @@ namespace BookmarkManager
return null; return null;
} }
BookmarkItem rs = new BookmarkItem(); BookmarkResult rs = new BookmarkResult();
rs.Item = new BookmarkItem();
HtmlDocument document = new HtmlDocument(); HtmlDocument document = new HtmlDocument();
document.LoadHtml(sourceCode); document.LoadHtml(sourceCode);
rs.SiteName = parseSiteTitle(document); rs.Item.SiteName = parseSiteTitle(document);
rs.SiteAddress = url; rs.Item.SiteAddress = url;
rs.SiteDescription = parseSiteDescription(document); rs.Item.SiteDescription = parseSiteDescription(document);
rs.FaviconAddress = parseSiteIcon(document); rs.IconURL = parseSiteIcon(document);
// resolve relative URL // resolve relative URL
if (!string.IsNullOrWhiteSpace(rs.FaviconAddress)) if (!string.IsNullOrWhiteSpace(rs.IconURL))
{ {
Uri iconAddressURI; Uri iconAddressURI;
bool rv = Uri.TryCreate(new Uri(url), rs.FaviconAddress, out iconAddressURI); bool rv = Uri.TryCreate(new Uri(url), rs.IconURL, out iconAddressURI);
if (rv) if (rv)
{ {
rs.FaviconAddress = iconAddressURI.ToString(); rs.IconURL = iconAddressURI.ToString();
} }
} }

View File

@ -1,11 +1,13 @@
using BookmarkManager; using BookmarkManager;
using bzit.bomg.Models; using bzit.bomg.Models;
using FizzyLauncher.Models; using FizzyLauncher.Models;
using Microsoft.Data.Sqlite;
using Newtonsoft.Json; using Newtonsoft.Json;
using RyzStudio.Windows.Forms; using RyzStudio.Windows.Forms;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Drawing;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
@ -23,7 +25,7 @@ namespace FizzyLauncher
New New
} }
protected IconDatabase iconDatabase = null;
protected OptionsForm optionsForm = null; protected OptionsForm optionsForm = null;
protected FindForm findForm = null; protected FindForm findForm = null;
@ -39,13 +41,16 @@ namespace FizzyLauncher
{ {
InitializeComponent(); InitializeComponent();
jsonfigFilename = Path.ChangeExtension(Application.ExecutablePath, "jsonfig");
if (iconDatabase == null) iconDatabase = new IconDatabase(Path.ChangeExtension(Application.ExecutablePath, "db"));
iconDatabase.AddIcon(Guid.NewGuid().ToString(), UIResource.arrow_down);
this.AutoScaleMode = AutoScaleMode.None; this.AutoScaleMode = AutoScaleMode.None;
this.StartPosition = FormStartPosition.WindowsDefaultLocation; this.StartPosition = FormStartPosition.WindowsDefaultLocation;
this.ClientSize = new System.Drawing.Size(300, 580); this.ClientSize = new System.Drawing.Size(300, 580);
//this.Visible = false; //this.Visible = false;
jsonfigFilename = Path.ChangeExtension(Application.ExecutablePath, "jsonfig");
treeView1.RootContextMenu = rootContextMenu; treeView1.RootContextMenu = rootContextMenu;
treeView1.FolderContextMenu = folderContextMenu; treeView1.FolderContextMenu = folderContextMenu;
treeView1.PageContextMenu = pageContextMenu; treeView1.PageContextMenu = pageContextMenu;
@ -71,6 +76,7 @@ namespace FizzyLauncher
//ThreadControl.SetVisible(this, false); //ThreadControl.SetVisible(this, false);
await LoadAppSession(jsonfigFilename); await LoadAppSession(jsonfigFilename);
//await InitialiseIconDB(iconDBFilename);
InvalidateAppSession(); InvalidateAppSession();
@ -107,6 +113,7 @@ namespace FizzyLauncher
sessionPassword = null; sessionPassword = null;
treeView1.Clear(); treeView1.Clear();
iconDatabase?.Close();
ApplicationMode = AppMode.Clear; ApplicationMode = AppMode.Clear;
@ -680,10 +687,16 @@ namespace FizzyLauncher
case Keys.F2: case Keys.F2:
if (nodeType == BookmarkTreeView.NodeType.Page) if (nodeType == BookmarkTreeView.NodeType.Page)
{ {
BookmarkForm bookmarkForm = new BookmarkForm(treeView1.GetNodeModel()); BookmarkForm bookmarkForm = new BookmarkForm(treeView1.GetNodeModel(), null);
if (bookmarkForm.ShowDialog() == DialogResult.OK) if (bookmarkForm.ShowDialog() == DialogResult.OK)
{ {
treeView1.UpdateItem(treeView1.SelectedNode, bookmarkForm.Model, bookmarkForm.Favicon); string iconID = Crypto.GetSHA256Hash(bookmarkForm.Model.Item?.SiteName);
if (!string.IsNullOrWhiteSpace(iconID))
{
iconDatabase.AddIcon(iconID, bookmarkForm.Model.Icon);
}
treeView1.UpdateItem(treeView1.SelectedNode, bookmarkForm.Model.Item);
} }
} }
@ -708,10 +721,16 @@ namespace FizzyLauncher
node.EnsureVisible(); node.EnsureVisible();
treeView1.SelectedNode = node; treeView1.SelectedNode = node;
BookmarkForm bookmarkForm = new BookmarkForm(treeView1.GetNodeModel()); BookmarkForm bookmarkForm = new BookmarkForm(treeView1.GetNodeModel(), null);
if (bookmarkForm.ShowDialog() == DialogResult.OK) if (bookmarkForm.ShowDialog() == DialogResult.OK)
{ {
treeView1.UpdateItem(treeView1.SelectedNode, bookmarkForm.Model, bookmarkForm.Favicon); string iconID = Crypto.GetSHA256Hash(bookmarkForm.Model.Item?.SiteName);
if (!string.IsNullOrWhiteSpace(iconID))
{
iconDatabase.AddIcon(iconID, bookmarkForm.Model.Icon);
}
treeView1.UpdateItem(treeView1.SelectedNode, bookmarkForm.Model.Item);
} }
else else
{ {

View File

@ -11,23 +11,10 @@ namespace bzit.bomg.Models
public string SiteDescription { get; set; } public string SiteDescription { get; set; }
public string FaviconAddress { get; set; }
public string TreeviewPath { get; set; } public string TreeviewPath { get; set; }
public string Notes { get; set; } public string Notes { get; set; }
//public BookmarkItemModel ToModel()
//{
// return new BookmarkItemModel()
// {
// SiteName = this.SiteName,
// SiteAddress = this.SiteAddress,
// SiteDescription = this.SiteDescription,
// FaviconAddress = this.FaviconAddress,
// TreeviewPath = this.TreeviewPath
// };
//}
public new string ToString() public new string ToString()
{ {

View File

@ -1,4 +1,5 @@
using bzit.bomg; using BookmarkManager;
using bzit.bomg;
using bzit.bomg.Models; using bzit.bomg.Models;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -519,6 +520,9 @@ namespace RyzStudio.Windows.Forms
} }
} }
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public IconDatabase IconDatabase { get; set; } = null;
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public ContextMenuStrip RootContextMenu { get; set; } = null; public ContextMenuStrip RootContextMenu { get; set; } = null;