From 3e0319dd213867224a70b6e07874b254bcbca174 Mon Sep 17 00:00:00 2001 From: Parth Sethi Date: Thu, 6 Feb 2025 12:11:10 +0530 Subject: [PATCH 1/4] Add support for importing existing notes --- .../PlayNotes/Models/ExistingNotesImporter.cs | 137 ++++++++++++++++++ source/Generic/PlayNotes/PlayNotes.cs | 2 +- source/Generic/PlayNotes/PlayNotes.csproj | 8 +- source/Generic/PlayNotes/PlayNotesSettings.cs | 22 ++- .../PlayNotes/PlayNotesSettingsView.xaml | 9 +- source/Generic/PlayNotes/app.config | 4 + source/Generic/PlayNotes/packages.config | 3 +- source/PlayniteExtensions.sln | 4 +- 8 files changed, 178 insertions(+), 11 deletions(-) create mode 100644 source/Generic/PlayNotes/Models/ExistingNotesImporter.cs diff --git a/source/Generic/PlayNotes/Models/ExistingNotesImporter.cs b/source/Generic/PlayNotes/Models/ExistingNotesImporter.cs new file mode 100644 index 0000000000..fd7b246d44 --- /dev/null +++ b/source/Generic/PlayNotes/Models/ExistingNotesImporter.cs @@ -0,0 +1,137 @@ +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 readonly List _contentTransformElems; + private readonly Dictionary _unsupportedElemsRegexFormatters; + private bool clearBaseNotes = false; + public bool ClearBaseNotes { get => clearBaseNotes; set => SetValue(ref clearBaseNotes, value); } + + private PlayNotesSettings _settings; + + public ExistingNotesImporter(IPlayniteAPI playniteApi, PlayNotesSettings settings) + { + _playniteApi = playniteApi; + _settings = settings; + _contentTransformElems = new List() + { + new SteamHtmlTransformDefinition("span", "bb_strike", "strike"), + new SteamHtmlTransformDefinition("div", "bb_h1", "h1"), + new SteamHtmlTransformDefinition("div", "bb_h2", "h2"), + new SteamHtmlTransformDefinition("div", "bb_h3", "h3"), + new SteamHtmlTransformDefinition("div", "bb_h4", "h4"), + new SteamHtmlTransformDefinition("div", "bb_h5", "h5") + }; + + _unsupportedElemsRegexFormatters = new Dictionary + { + {@"((.|\n)*?)", "~~$1~~" }, + {@"((.|\n)*?)", "*$1*" } + }; + } + + public void ResetValues() + { + ClearBaseNotes = false; + } + + public bool ImportExistingNotes(MarkdownDatabaseItem databaseItem, CancellationToken cancelToken) + { + if (_playniteApi.MainView.SelectedGames?.Any() != true) + { + return false; + } + + string path = Path.Combine(_settings.ExistingNotesFolderPath, _playniteApi.MainView.SelectedGames.First().Name + ".md"); + path = path.Trim(); + + 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 sections = ExtractH2Sections(markdownContent); + + var notes = new List(); + 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 ExtractH2Sections(string markdown) + { + Dictionary sections = new Dictionary(); + + // Regex to match H2 headings and their content + Regex regex = new Regex(@"##\s+(.*?)\n([\s\S]*?)(?=\n## |\z)", RegexOptions.Multiline); + + // Extract and store sections + foreach (Match match in regex.Matches(markdown)) + { + string heading = match.Groups[1].Value.Trim(); + string content = match.Groups[2].Value.Trim(); + sections[heading] = content; + } + + return sections; + } + } +} \ No newline at end of file diff --git a/source/Generic/PlayNotes/PlayNotes.cs b/source/Generic/PlayNotes/PlayNotes.cs index 3c2b0e0860..815c671ee8 100644 --- a/source/Generic/PlayNotes/PlayNotes.cs +++ b/source/Generic/PlayNotes/PlayNotes.cs @@ -31,7 +31,7 @@ public PlayNotes(IPlayniteAPI api) : base(api) Settings = new PlayNotesSettingsViewModel(this); Properties = new GenericPluginProperties { - HasSettings = false + HasSettings = true }; AddCustomElementSupport(new AddCustomElementSupportArgs diff --git a/source/Generic/PlayNotes/PlayNotes.csproj b/source/Generic/PlayNotes/PlayNotes.csproj index cd780d6f6d..b7ad3ccb14 100644 --- a/source/Generic/PlayNotes/PlayNotes.csproj +++ b/source/Generic/PlayNotes/PlayNotes.csproj @@ -42,6 +42,9 @@ ..\..\packages\LiteDB.4.1.4\lib\net40\LiteDB.dll + + ..\..\packages\Markdig.0.40.0\lib\net462\Markdig.dll + ..\..\packages\MdXaml.1.26.0\lib\net462\MdXaml.dll @@ -89,8 +92,8 @@ ..\..\packages\System.Diagnostics.DiagnosticSource.6.0.0\lib\net461\System.Diagnostics.DiagnosticSource.dll - - ..\..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll + + ..\..\packages\System.Memory.4.5.5\lib\net461\System.Memory.dll @@ -229,6 +232,7 @@ Shared\DatabaseCommon\LiteDbRepository.cs + diff --git a/source/Generic/PlayNotes/PlayNotesSettings.cs b/source/Generic/PlayNotes/PlayNotesSettings.cs index 4c8839d5c2..3ed21dfdac 100644 --- a/source/Generic/PlayNotes/PlayNotesSettings.cs +++ b/source/Generic/PlayNotes/PlayNotesSettings.cs @@ -10,6 +10,7 @@ using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; +using System.Windows.Forms; using System.Windows.Media; namespace PlayNotes @@ -21,6 +22,9 @@ public class PlayNotesSettings : ObservableObject private Style _markdownStyle; 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 @@ -75,7 +79,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) @@ -136,5 +140,21 @@ public bool VerifySettings(out List errors) errors = new List(); 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 + } + } + }); + } } } \ No newline at end of file diff --git a/source/Generic/PlayNotes/PlayNotesSettingsView.xaml b/source/Generic/PlayNotes/PlayNotesSettingsView.xaml index a38cc77f31..4691ed462d 100644 --- a/source/Generic/PlayNotes/PlayNotesSettingsView.xaml +++ b/source/Generic/PlayNotes/PlayNotesSettingsView.xaml @@ -6,9 +6,10 @@ mc:Ignorable="d" d:DesignHeight="400" d:DesignWidth="600"> - - - - + + + +