Skip to content

Commit

Permalink
Initial Commit
Browse files Browse the repository at this point in the history
  • Loading branch information
TBM13 committed Jan 23, 2023
0 parents commit bd265b4
Show file tree
Hide file tree
Showing 9 changed files with 744 additions and 0 deletions.
401 changes: 401 additions & 0 deletions .gitignore

Large diffs are not rendered by default.

25 changes: 25 additions & 0 deletions BrowserSearch.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.33312.197
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BrowserSearch", "BrowserSearch\BrowserSearch.csproj", "{EF807AEB-094D-449A-8FF3-B34E56FB05C6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{EF807AEB-094D-449A-8FF3-B34E56FB05C6}.Debug|x64.ActiveCfg = Debug|x64
{EF807AEB-094D-449A-8FF3-B34E56FB05C6}.Debug|x64.Build.0 = Debug|x64
{EF807AEB-094D-449A-8FF3-B34E56FB05C6}.Release|x64.ActiveCfg = Release|x64
{EF807AEB-094D-449A-8FF3-B34E56FB05C6}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {6BD530DB-D589-4E04-A00B-864C0233829A}
EndGlobalSection
EndGlobal
37 changes: 37 additions & 0 deletions BrowserSearch/BrowserSearch.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net7.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<UseWPF>true</UseWPF>
<AssemblyName>Community.PowerToys.Run.Plugin.$(MSBuildProjectName)</AssemblyName>
<Platforms>AnyCPU;x64</Platforms>
</PropertyGroup>

<!-- These libraries can be copied from any installation of PowerToys -->
<!-- They are located at <PowerToys Path>\modules\launcher -->
<ItemGroup>
<Reference Include="Wox.Plugin">
<HintPath>libs\Wox.Plugin.dll</HintPath>
</Reference>
<Reference Include="Wox.Infrastructure">
<HintPath>libs\Wox.Infrastructure.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Data.Sqlite.Core">
<HintPath>libs\Microsoft.Data.Sqlite.dll</HintPath>
</Reference>
</ItemGroup>

<ItemGroup>
<None Update="Images\BrowserSearch.dark.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Images\BrowserSearch.light.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="plugin.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>
131 changes: 131 additions & 0 deletions BrowserSearch/Chrome.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
using Microsoft.Data.Sqlite;
using System;
using System.Collections.Generic;
using System.IO;
using Wox.Infrastructure;
using Wox.Plugin;
using Wox.Plugin.Logger;
using BrowserInfo = Wox.Plugin.Common.DefaultBrowserInfo;

namespace BrowserSearch
{
internal class Chrome : IBrowser
{
private SqliteConnection? _historyDbConnection, _predictorDbConnection;

private readonly List<Result> _history = new();
public string Name { get; } = "Google Chrome";
public Dictionary<string, string> Predictions { get; } = new();

void IBrowser.Init()
{
CopyDatabases();
if (_historyDbConnection is null || _predictorDbConnection is null)
{
throw new ArgumentNullException(nameof(_historyDbConnection));
}
if (_predictorDbConnection is null)
{
throw new ArgumentNullException(nameof(_predictorDbConnection));
}

PopulatePredictions();
PopulateHistory();

_historyDbConnection.Close();
_predictorDbConnection.Close();
}

private void CopyDatabases()
{
string historyCopy = Path.GetTempPath() + @"\BrowserSearch_Chrome_History";
string predictorCopy = Path.GetTempPath() + @"\BrowserSearch_Chrome_ActionPredictor";

// We need to copy the databases. If we don't, we won't be able to open them
// while Chrome is running
string localappdata = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
File.Copy(localappdata + @"\Google\Chrome\User Data\Default\History", historyCopy, true);
File.Copy(localappdata + @"\Google\Chrome\User Data\Default\Network Action Predictor", predictorCopy, true);

_historyDbConnection = new($"Data Source={historyCopy}");
_predictorDbConnection = new($"Data Source={predictorCopy}");
}

private SqliteDataReader ExecuteCmd(SqliteConnection connection, SqliteCommand cmd)
{
cmd.Connection = connection;
connection.Open();

return cmd.ExecuteReader();
}

private void PopulatePredictions()
{
if (_predictorDbConnection is null)
{
throw new ArgumentNullException(nameof(_predictorDbConnection));
}

Dictionary<string, long> _predictionHits = new();

using SqliteCommand cmd = new("SELECT user_text, url, number_of_hits FROM network_action_predictor");
using SqliteDataReader reader = ExecuteCmd(_predictorDbConnection, cmd);
while (reader.Read())
{
string text = (string)reader[0]; // Query
string url = (string)reader[1]; // Predicted URL for that query
long hits = (long)reader[2]; // Amount of times the prediction was correct and the user selected it

// There can be multiples predictions for the same query
// So lets make sure to only select the one with the most hits
if (_predictionHits.GetValueOrDefault(text, -1) < hits)
{
_predictionHits[text] = hits;
Predictions[text] = url;
}
}
}

private void PopulateHistory()
{
if (_historyDbConnection is null)
{
throw new ArgumentNullException(nameof(_historyDbConnection));
}

using SqliteCommand historyReadCmd = new("SELECT url, title FROM urls ORDER BY visit_count DESC");
using SqliteDataReader reader = ExecuteCmd(_historyDbConnection, historyReadCmd);
while (reader.Read())
{
string url = (string)reader[0];
string title = (string)reader[1];

Result result = new()
{
QueryTextDisplay = url,
Title = title,
SubTitle = url,
IcoPath = BrowserInfo.IconPath,
Action = action =>
{
// Open URL in default browser
if (!Helper.OpenInShell(url))
{
Log.Error($"Couldn't open '{url}'", typeof(Chrome));
return false;
}

return true;
},
};

_history.Add(result);
}
}

List<Result> IBrowser.GetHistory()
{
return _history ?? new List<Result>();
}
}
}
14 changes: 14 additions & 0 deletions BrowserSearch/IBrowser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using Wox.Plugin;

namespace BrowserSearch
{
internal interface IBrowser
{
string Name { get; }
Dictionary<string, string> Predictions { get; }
void Init();
List<Result> GetHistory();
}
}
Binary file added BrowserSearch/Images/BrowserSearch.dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added BrowserSearch/Images/BrowserSearch.light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
123 changes: 123 additions & 0 deletions BrowserSearch/Main.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
using BrowserSearch;
using System;
using System.Collections.Generic;
using System.Linq;
using Wox.Plugin;
using Wox.Plugin.Logger;
using BrowserInfo = Wox.Plugin.Common.DefaultBrowserInfo;

namespace Community.Powertoys.Run.Plugin.BrowserSearch
{
public class Main : IPlugin, IReloadable
{
private PluginInitContext? _context;

private readonly List<IBrowser> _supportedBrowsers = new()
{
new Chrome()
};
private IBrowser? _defaultBrowser;

public string Name => "Browser Search";
public string Description => "Search in your browser's history.";

public void Init(PluginInitContext context)
{
_context = context ?? throw new ArgumentNullException(nameof(context));

// Retrieve default browser info
BrowserInfo.UpdateIfTimePassed();

foreach (IBrowser b in _supportedBrowsers)
{
if (b.Name == BrowserInfo.Name)
{
_defaultBrowser = b;
break;
}
}

if (_defaultBrowser is null)
{
Log.Error($"Unsupported/unrecognized default browser '{BrowserInfo.Name}'", typeof(Main));
return;
}

_defaultBrowser.Init();
}

private int CalculateScore(string query, string title, string url, string? predictionUrl)
{
// Since PT Run's FuzzySearch is too slow, and the history usually has a lot of entries,
// lets calculate the scores manually.
float titleScore = title.Contains(query, StringComparison.InvariantCultureIgnoreCase)
? ((float)query.Length / (float)title.Length * 100f)
: 0;
float urlScore = url.Contains(query, StringComparison.InvariantCultureIgnoreCase)
? ((float)query.Length / (float)url.Length * 100f)
: 0;

float score = new[] { titleScore, urlScore }.Max();

// If the browser has a prediction for this specific query,
// we want to give a higher score to the entry that has
// the prediction's url
if (predictionUrl is not null && predictionUrl == url)
{
score += 50;
}

return (int)score;
}

public List<Result> Query(Query query)
{
if (query is null)
{
throw new ArgumentNullException(nameof(query));
}
if (_defaultBrowser is null)
{
return new List<Result>();
}

List<Result> history = _defaultBrowser.GetHistory();

// This happens when the user only typed this plugin's ActionKeyword ("b?")
if (string.IsNullOrEmpty(query.Search))
{
return history;
}

List<Result> results = new();
// Get the Browser's prediction for this specific query, if it has one
_defaultBrowser.Predictions.TryGetValue(query.Search, out string? prediction);

for (int i = 0; i < history.Count; i++)
{
Result r = history[i];

int score = CalculateScore(query.Search, r.Title, r.SubTitle, prediction);
if (score <= 0)
{
continue;
}

r.Score = score;
results.Add(r);
}

return results;
}

public void ReloadData()
{
if (_context is null)
{
return;
}

BrowserInfo.UpdateIfTimePassed();
}
}
}
13 changes: 13 additions & 0 deletions BrowserSearch/plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"ID": "E5A9FC7A3F7F4320BE612DA95C57C32D",
"ActionKeyword": "b?",
"IsGlobal": true,
"Name": "Browser Search",
"Author": "TBM13",
"Version": "1.0.0",
"Language": "csharp",
"Website": "https://github.com/TBM13/BrowserSearch",
"ExecuteFileName": "Community.PowerToys.Run.Plugin.BrowserSearch.dll",
"IcoPathDark": "Images\\BrowserSearch.dark.png",
"IcoPathLight": "Images\\BrowserSearch.light.png"
}

0 comments on commit bd265b4

Please sign in to comment.