Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
163 changes: 163 additions & 0 deletions source/Generic/PlayNotes/Models/ExistingNotesImporter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
using HtmlAgilityPack;
using Playnite.SDK;
using ReverseMarkdown;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using Markdig;

namespace PlayNotes.Models
{
public class ExistingNotesImporter : ObservableObject
{
private static readonly ILogger logger = LogManager.GetLogger();
private readonly IPlayniteAPI _playniteApi;
private bool clearBaseNotes = false;
public bool ClearBaseNotes { get => clearBaseNotes; set => SetValue(ref clearBaseNotes, value); }
private bool useOverride = false;
public bool UseOverride { get => useOverride; set => SetValue(ref useOverride, value); }
private string overrideFilePath;
public string OverrideFilePath { get => overrideFilePath; set => SetValue(ref overrideFilePath, value); }
private PlayNotesSettings _settings;

public ExistingNotesImporter(IPlayniteAPI playniteApi, PlayNotesSettings settings)
{
_playniteApi = playniteApi;
_settings = settings;
}

public void ResetValues()
{
ClearBaseNotes = false;
}

public bool ImportExistingNotes(MarkdownDatabaseItem databaseItem, CancellationToken cancelToken)
{
if (_playniteApi.MainView.SelectedGames?.Any() != true)
{
return false;
}

string gameName = _playniteApi.MainView.SelectedGames.First().Name;
gameName = Regex.Replace(gameName, @"[^a-zA-Z0-9\s]", "").Trim();

string path;
if (!UseOverride)
{
path = Path.Combine(_settings.ExistingNotesFolderPath, gameName + ".md").Trim();
}
else
{
path = OverrideFilePath;
}

if (string.IsNullOrEmpty(path) ||
!File.Exists(path))
{
_playniteApi.Dialogs.ShowErrorMessage(
$"Could not find the notes file, {path}",
"Invalid file path");
return false;
}

if (!Path.GetExtension(path).Equals(".md", StringComparison.OrdinalIgnoreCase))
{
_playniteApi.Dialogs.ShowErrorMessage(
"Extension not supported",
$"Invalid file extension. Only markdown files are supported.");
return false;
}

try
{
string markdownContent = File.ReadAllText(path);
Dictionary<string, string> sections = ExtractHeadingsAndSections(markdownContent, gameName);

var notes = new List<PlayNote>();
foreach (var section in sections)
{
notes.Add(new PlayNote(section.Key, section.Value));
}

if (!notes.HasItems())
{
logger.Debug($"Notes not found");
_playniteApi.Dialogs.ShowErrorMessage(
"Could not import notes",
"Could not import notes");
return false;
}

if (ClearBaseNotes)
{
databaseItem.Notes.Clear();
}

foreach (var note in notes)
{
databaseItem.Notes.Add(note);
}
}
catch (Exception ex)
{
logger.Debug($"Exception reading file: {path}, exception: {ex}");
_playniteApi.Dialogs.ShowErrorMessage($"Error while reading file {path}");
return false;
}

return true;
}
private static Dictionary<string, string> ExtractHeadingsAndSections(string markdown, string gameName)
{
Dictionary<string, string> sections = new Dictionary<string, string>();

// Regex to match H1 and H2 headings and their content
Regex regex = new Regex(@"^(#{1,2})\s+(.*?)\n([\s\S]*?)(?=\n#{1,2} |\z)", RegexOptions.Multiline);

// Check for the first chunk of text before any headings
string firstChunk = null;
int firstHeadingIndex = markdown.IndexOf("#"); // Find where the first heading starts

if (firstHeadingIndex == -1)
{
if (!string.IsNullOrEmpty(markdown))
{
sections[gameName] = markdown.Trim();
}
}
else
{
if (firstHeadingIndex > 0)
{
firstChunk = markdown.Substring(0, firstHeadingIndex).Trim(); // Content before any heading
if (!string.IsNullOrEmpty(firstChunk))
{
if (!string.IsNullOrEmpty(firstChunk))
{
sections["First"] = firstChunk;
}
}
}

// Extract and store sections for H1 and H2 headings
foreach (Match match in regex.Matches(markdown))
{
string headingType = match.Groups[1].Value.Trim(); // # for H1, ## for H2
string heading = match.Groups[2].Value.Trim();
string content = match.Groups[3].Value.Trim();

if (!string.IsNullOrEmpty(content))
{
sections[heading] = content;
}
}
}

return sections;
}
}
}
2 changes: 1 addition & 1 deletion source/Generic/PlayNotes/PlayNotes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public PlayNotes(IPlayniteAPI api) : base(api)
Settings = new PlayNotesSettingsViewModel(this);
Properties = new GenericPluginProperties
{
HasSettings = false
HasSettings = true
};

AddCustomElementSupport(new AddCustomElementSupportArgs
Expand Down
11 changes: 9 additions & 2 deletions source/Generic/PlayNotes/PlayNotes.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@
<Reference Include="LiteDB, Version=4.1.4.0, Culture=neutral, PublicKeyToken=4ee40123013c9f27, processorArchitecture=MSIL">
<HintPath>..\..\packages\LiteDB.4.1.4\lib\net40\LiteDB.dll</HintPath>
</Reference>
<Reference Include="Markdig, Version=0.40.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Markdig.0.40.0\lib\net462\Markdig.dll</HintPath>
</Reference>
<Reference Include="MdXaml, Version=1.26.0.0, Culture=neutral, PublicKeyToken=9f8c7afb435b7edc, processorArchitecture=MSIL">
<HintPath>..\..\packages\MdXaml.1.26.0\lib\net462\MdXaml.dll</HintPath>
</Reference>
Expand Down Expand Up @@ -72,6 +75,9 @@
<Reference Include="Microsoft.Extensions.Primitives, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\..\packages\Microsoft.Extensions.Primitives.6.0.0\lib\net461\Microsoft.Extensions.Primitives.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="Playnite.SDK, Version=6.11.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\PlayniteSDK.6.11.0\lib\net462\Playnite.SDK.dll</HintPath>
</Reference>
Expand All @@ -89,8 +95,8 @@
<Reference Include="System.Diagnostics.DiagnosticSource, Version=6.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\..\packages\System.Diagnostics.DiagnosticSource.6.0.0\lib\net461\System.Diagnostics.DiagnosticSource.dll</HintPath>
</Reference>
<Reference Include="System.Memory, Version=4.0.1.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll</HintPath>
<Reference Include="System.Memory, Version=4.0.1.2, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\..\packages\System.Memory.4.5.5\lib\net461\System.Memory.dll</HintPath>
</Reference>
<Reference Include="System.Numerics" />
<Reference Include="System.Numerics.Vectors, Version=4.1.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
Expand Down Expand Up @@ -229,6 +235,7 @@
<Link>Shared\DatabaseCommon\LiteDbRepository.cs</Link>
</Compile>
<Compile Include="Converters\MarkdownUnescapeConverter.cs" />
<Compile Include="Models\ExistingNotesImporter.cs" />
<Compile Include="Models\SteamGuideImporter.cs" />
<Compile Include="Models\SteamHtmlTransformDefinition.cs" />
<Compile Include="PlayniteControls\MdEngineCommands.cs" />
Expand Down
25 changes: 24 additions & 1 deletion source/Generic/PlayNotes/PlayNotesSettings.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using MdXaml;
using Newtonsoft.Json;
using Playnite.SDK;
using Playnite.SDK.Data;
using System;
Expand All @@ -10,6 +11,7 @@
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Forms;
using System.Windows.Media;

namespace PlayNotes
Expand All @@ -20,7 +22,11 @@ public class PlayNotesSettings : ObservableObject
public bool IsControlVisible { get => _isControlVisible; set => SetValue(ref _isControlVisible, value); }

private Style _markdownStyle;
[JsonIgnore]
public Style MarkdownStyle { get => _markdownStyle; set => SetValue(ref _markdownStyle, value); }

private string _existingNotesFolderPath;
public string ExistingNotesFolderPath { get => _existingNotesFolderPath; set => SetValue(ref _existingNotesFolderPath, value); }
}

public class PlayNotesSettingsViewModel : ObservableObject, ISettings
Expand Down Expand Up @@ -75,7 +81,7 @@ public PlayNotesSettingsViewModel(PlayNotes plugin)
imageStyle.Setters.Add(new Setter(Image.StretchProperty, Stretch.Uniform));
imageStyle.Setters.Add(new Setter(Image.StretchDirectionProperty, StretchDirection.DownOnly));

var maxWidthBinding = new Binding("ActualWidth");
var maxWidthBinding = new System.Windows.Data.Binding("ActualWidth");
var relativeSource = new RelativeSource(RelativeSourceMode.FindAncestor)
{
AncestorType = typeof(MarkdownScrollViewer)
Expand Down Expand Up @@ -136,5 +142,22 @@ public bool VerifySettings(out List<string> errors)
errors = new List<string>();
return true;
}

public RelayCommand BrowseFolder
{
get => new RelayCommand(() =>
{
using (FolderBrowserDialog folderDialog = new FolderBrowserDialog())
{
folderDialog.Description = "Select a folder";
folderDialog.ShowNewFolderButton = true;

if (folderDialog.ShowDialog() == DialogResult.OK)
{
Settings.ExistingNotesFolderPath = folderDialog.SelectedPath; // Updates the bound property
}
}
});
}
}
}
9 changes: 5 additions & 4 deletions source/Generic/PlayNotes/PlayNotesSettingsView.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@
mc:Ignorable="d"
d:DesignHeight="400" d:DesignWidth="600">
<StackPanel>
<TextBlock Text="Description for Option1:"/>
<TextBox Text="{Binding Settings.Option1}"/>
<TextBlock Text="Description for Option2:"/>
<CheckBox IsChecked="{Binding Settings.Option2}"/>
<DockPanel Margin="0,10,0,0" LastChildFill="True">
<TextBlock Text="Notes folder path: " DockPanel.Dock="Left" VerticalAlignment="Center" />
<TextBox Width="330" Margin="10,0,0,0" DockPanel.Dock="Left" Text="{Binding Settings.ExistingNotesFolderPath}" />
<Button Content="Browse" Width="75" Command="{Binding BrowseFolder}" />
</DockPanel>
</StackPanel>
</UserControl>
19 changes: 19 additions & 0 deletions source/Generic/PlayNotes/PlayniteControls/NotesViewerControl.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,25 @@
<Separator Margin="0,10,0,0" />
</StackPanel>
</TabItem>
<TabItem Header="Import Existing Notes">
<StackPanel>
<DockPanel Margin="0,10,0,0" LastChildFill="True">
<TextBlock Text="File path override: " DockPanel.Dock="Left" VerticalAlignment="Center" />
<TextBox Width="330" Margin="10,0,0,0" DockPanel.Dock="Left" Text="{Binding ExistingNotesImporter.OverrideFilePath}" />
<Button Content="Browse" Width="75" Command="{Binding BrowseFile}" />
</DockPanel>

<CheckBox Margin="0,10,0,0" IsChecked="{Binding ExistingNotesImporter.UseOverride}"
Content="Use override" />
<CheckBox Margin="0,10,0,0" IsChecked="{Binding ExistingNotesImporter.ClearBaseNotes}"
Content="{DynamicResource PlayNotes_SteamGuideImporterClearExistingNotesNotesLabel}" />
<Button Margin="0,15,0,0" HorizontalAlignment="Left"
Content="{DynamicResource PlayNotes_ImportLabel}"
Command="{Binding ImportExistingNotesCommand}" />

<Separator Margin="0,10,0,0" />
</StackPanel>
</TabItem>
</TabControl>
</StackPanel>

Expand Down
Loading