Skip to content

Conversation

@jjsilvan
Copy link

@jjsilvan jjsilvan commented Jan 30, 2026

PR Checklist

  • Functionality has been tested, no obvious bugs
  • Code style follows project conventions
  • Documentation/comments updated (if applicable)

Brief Description of Changes

Please briefly describe the main changes in this PR:
Build on Visual Studio Community 2026 (v145)
.net Framework 4.8.1

Related Issue (if any)

Please provide related issue numbers:
wix doesn't work on v145

Additional Notes

Add any extra notes here:
video plugin uses native video, doesn't use lavfilters

Summary by Sourcery

Add Windows Arm64 runtime support across the core app and video viewer plugin, including architecture detection and native integrations.

New Features:

  • Introduce an Arm64-native QuickLook.Native library and wire it into selection and window-type detection.
  • Add an Arm64-specific playback path in the video viewer using a WPF media element and separate load-and-play entry point.
  • Add converters and resources required for time display and bindings in the new Arm64 video viewer UI.

Bug Fixes:

  • Prevent crashes in the text viewer more menu when the current path is null or invalid by safely handling extension extraction.
  • Ensure looping playback toggling correctly resumes playback on both legacy and Arm64 video players.
  • Avoid using the blocked state instead of the busy state when deciding whether to show the default loading text viewer.

Enhancements:

  • Centralize runtime architecture checks (including a new IsArm64 flag) and use them to select appropriate native and MediaInfo binaries.
  • Adjust the video viewer glass layer to dynamically bind its blurred element to the correct media element depending on architecture.
  • Improve volume handling, lyrics timing, and playback state management to work consistently between legacy and Arm64 video elements.
  • Refine disposal and timer management in the video viewer to stop timers and close media elements cleanly.

Build:

  • Add an Arm64 native project and update solution and project files to include Arm64 builds for the core app and plugins.

@sourcery-ai
Copy link

sourcery-ai bot commented Jan 30, 2026

Reviewer's Guide

Adds Windows Arm64 support across the QuickLook core app, native bridge, and key plugins (especially the VideoViewer) by detecting Arm64 at runtime, routing to new Arm64 native DLLs and media pipelines, and updating projects/solution to build against the new architecture and toolset.

Sequence diagram for video preview on Arm64 vs x64

sequenceDiagram
    actor User
    participant ViewerWindow
    participant Plugin
    participant ViewerPanel
    participant MediaElementWPF
    participant MediaElementUri

    User->>ViewerWindow: Request preview for file path
    ViewerWindow->>Plugin: View(path, context)
    Plugin->>Plugin: isArm64 = RuntimeInformation.ProcessArchitecture == Architecture.Arm64
    alt Arm64
        Plugin->>ViewerPanel: LoadAndPlayWPF(path, mediaInfo)
        ViewerPanel->>ViewerPanel: InitializeArm64()
        ViewerPanel->>MediaElementWPF: Source = new Uri(path)
        ViewerPanel->>MediaElementWPF: Play()
    else x64 or x86
        Plugin->>ViewerPanel: LoadAndPlay(path, mediaInfo)
        ViewerPanel->>ViewerPanel: InitializeDefault()
        ViewerPanel->>MediaElementUri: MediaUriPlayer.LAVFilterDirectory = LAVFilters-x64 or LAVFilters-x86
        ViewerPanel->>MediaElementUri: Play()
    end
Loading

Class diagram for ViewerPanel with Arm64-specific video pipeline

classDiagram
    class ViewerPanel {
        -ContextObject _context
        -BitmapSource _coverArt
        -DispatcherTimer _lyricTimer
        -DispatcherTimer timer
        -bool IsSeeked
        -bool _isPlaying
        -bool _wasPlaying
        -bool _shouldLoop
        -bool isArm64
        +bool HasVideo
        +bool IsPlaying
        +bool ShouldLoop
        +double LinearVolume
        +ViewerPanel(ContextObject context)
        -void InitializeDefault()
        -void InitializeArm64()
        -void Seek_Timer(object sender, EventArgs e)
        -void Seek_Drag_Started(object sender, DragStartedEventArgs e)
        -void Seek_Drag_Completed(object sender, DragCompletedEventArgs e)
        -void Seek_Value_Changed(object sender, RoutedPropertyChangedEventArgs~double~ e)
        -void MediaOpened(object o, RoutedEventArgs args)
        -void MediaEnded(object sender, RoutedEventArgs e)
        -void MediaFailed(object sender, MediaFailedEventArgs e)
        -void UpdateMeta(string path, MediaInfoLib info)
        -void ChangeVolume(double delta)
        -void TogglePlayPause(object sender, EventArgs e)
        -void ToggleShouldLoop(object sender, EventArgs e)
        +void LoadAndPlay(string path, MediaInfoLib info)
        +void LoadAndPlayWPF(string path, MediaInfoLib info)
        +void Dispose()
    }

    class MediaElementUri {
        +bool IsPlaying
        +bool HasVideo
        +long MediaPosition
        +double Volume
        +MediaUriPlayer MediaUriPlayer
        +void Play()
        +void Pause()
        +void Close()
    }

    class MediaElementWPF {
        +Uri Source
        +bool HasVideo
        +TimeSpan Position
        +Duration NaturalDuration
        +double Volume
        +void Play()
        +void Pause()
        +void Close()
    }

    class MediaUriPlayer {
        +string LAVFilterDirectory
        +void Dispose()
    }

    class ContextObject {
        +bool IsBusy
        +object ViewerContent
        +string Title
    }

    ViewerPanel --> MediaElementUri : uses mediaElement
    ViewerPanel --> MediaElementWPF : uses mediaElementWPF
    MediaElementUri --> MediaUriPlayer
    ViewerPanel --> ContextObject
Loading

Class diagram for QuickLook native bridge with Arm64 DLLs

classDiagram
    class App {
        +string AppFullPath
        +string AppPath
        +bool Is64Bit
        +bool IsArm64
        +bool IsUWP
        +bool IsWin11
        +bool IsWin10
    }

    class QuickLookNative {
        -void Init_32()
        -void Init_64()
        -void Init_arm64()
        -FocusedWindowType GetFocusedWindowTypeNative_32()
        -FocusedWindowType GetFocusedWindowTypeNative_64()
        -FocusedWindowType GetFocusedWindowTypeNative_arm64()
        -void GetCurrentSelectionNative_32(StringBuilder sb)
        -void GetCurrentSelectionNative_64(StringBuilder sb)
        -void GetCurrentSelectionNative_arm64(StringBuilder sb)
        +void Init()
        +FocusedWindowType GetFocusedWindowType()
        +string GetCurrentSelection()
    }

    class FocusedWindowType {
    }

    App <.. QuickLookNative : uses IsArm64
    QuickLookNative --> FocusedWindowType
Loading

Class diagram for VideoViewer Plugin and MediaInfo selection

classDiagram
    class Plugin {
        -static MediaInfoLib _mediaInfo
        -ViewerPanel _vp
        -static bool isArm64
        +int Priority
        +Plugin()
        +void View(string path, ContextObject context)
        +void Cleanup()
    }

    class MediaInfoLib {
        +MediaInfoLib(string path)
        +string Get(StreamKind kind, int index, string parameter)
        +string Option(string option, string value)
    }

    class StreamKind {
    }

    Plugin --> ViewerPanel
    Plugin --> MediaInfoLib : selects MediaInfo-arm64 or x86/x64
    MediaInfoLib --> StreamKind
Loading

Class diagram for new time converters in VideoViewer

classDiagram
    class TimeToLongConverter {
        +object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        +object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    }

    class TimeToShortStringConverter {
        +object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        +object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    }

    class IValueConverter {
        <<interface>>
        +object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        +object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    }

    class DependencyObject {
    }

    TimeToLongConverter ..|> IValueConverter
    TimeToShortStringConverter ..|> IValueConverter
    TimeToLongConverter --|> DependencyObject
    TimeToShortStringConverter --|> DependencyObject
Loading

File-Level Changes

Change Details Files
Introduce runtime Arm64 detection and route core native calls to a new QuickLook.NativeArm64 DLL.
  • Add App.IsArm64 flag using RuntimeInformation.ProcessArchitecture == Architecture.Arm64
  • Declare Init_arm64, GetFocusedWindowTypeNative_arm64, and GetCurrentSelectionNative_arm64 P/Invoke signatures in the QuickLook native wrapper
  • Update Init, GetFocusedWindowType, and GetCurrentSelection to branch on App.IsArm64 before 64/32-bit code paths
  • Add new QuickLook.NativeArm64 C++ project and filters to the native solution
QuickLook/App.xaml.cs
QuickLook/NativeMethods/QuickLook.cs
QuickLook.Native/QuickLook.NativeArm64/QuickLook.NativeArm64.vcxproj
QuickLook.Native/QuickLook.NativeArm64/QuickLook.NativeArm64.vcxproj.filters
QuickLook.Native/QuickLook.Native32/QuickLook.Native32.vcxproj
QuickLook.Native/QuickLook.Native64/QuickLook.Native64.vcxproj
QuickLook.sln
QuickLook.slnx
Add an Arm64-specific playback path to VideoViewer with a WPF MediaElement pipeline, while preserving the existing LAVFilters-based path for x86/x64.
  • Introduce isArm64 flag and split ViewerPanel initialization into InitializeDefault (LAVFilters) and InitializeArm64 (WPF mediaElementWPF) paths
  • Wire Arm64 media events and playback controls (play/pause, loop, volume, seek) against mediaElementWPF, including a DispatcherTimer-driven seek bar and drag handlers
  • Guard shared behaviors (MediaOpened, MediaEnded, lyrics timing, LinearVolume, TogglePlayPause, looping behavior) with isArm64 checks to choose the correct media element
  • Expose a new LoadAndPlayWPF path that mirrors LoadAndPlay but targets mediaElementWPF and reuses metadata/MIDI handling
  • Update GlassLayer blur bindings to choose mediaElementWPF vs mediaElement based on isArm64
QuickLook.Plugin/QuickLook.Plugin.VideoViewer/ViewerPanel.xaml.cs
QuickLook.Plugin/QuickLook.Plugin.VideoViewer/ViewerPanel.GlassLayer.cs
QuickLook.Plugin/QuickLook.Plugin.VideoViewer/ViewerPanel.xaml
Make the VideoViewer plugin load Arm64-specific MediaInfo binaries and delegate to the correct ViewerPanel load method.
  • Add isArm64 static flag in the VideoViewer Plugin class
  • Update MediaInfoLib path selection to use a new MediaInfo-arm64 folder when running on Arm64, otherwise fall back to x64/x86
  • Change View() to call LoadAndPlayWPF on Arm64 and LoadAndPlay on other architectures
QuickLook.Plugin/QuickLook.Plugin.VideoViewer/Plugin.cs
Enhance VideoViewer bindings with time conversion helpers for Arm64/WPF playback UI.
  • Add TimeToLongConverter for TimeSpan <-> long tick conversions
  • Add TimeToShortStringConverter for formatting TimeSpan as a short time string
  • Wire these converters into the VideoViewer XAML (e.g., progress/time display) for the new WPF pipeline
QuickLook.Plugin/QuickLook.Plugin.VideoViewer/Converters.cs
QuickLook.Plugin/QuickLook.Plugin.VideoViewer/ViewerPanel.xaml
Harden TextViewer More menu against invalid or missing paths to avoid exceptions on Arm64 or other environments.
  • Wrap Path.GetExtension(_currentPath) in a try/catch and fall back to an empty extension on error before computing the reopen menu options
QuickLook.Plugin/QuickLook.Plugin.TextViewer/Plugin.MoreMenu.cs
Adjust viewer window behavior to treat busy state as a reason to show the 'busy' content instead of existing content.
  • Change the BeginShow guard to check ContextObject.IsBusy instead of ContextObject.IsBlocked before displaying a busy TextBlock
QuickLook/ViewerWindow.Actions.cs
Add auto-generated resources/settings scaffolding for the VideoViewer plugin and update projects/solution for the new toolset and Arm64 config.
  • Introduce auto-generated Resources.Designer.cs, Settings.Designer.cs, Resources.resx, and Settings.settings for VideoViewer
  • Update multiple .csproj files (core app and plugins) and the solution metadata for the Visual Studio 2026 toolset / .NET Framework 4.8.1 / Arm64 build configurations
QuickLook.Plugin/QuickLook.Plugin.VideoViewer/Properties/Resources.Designer.cs
QuickLook.Plugin/QuickLook.Plugin.VideoViewer/Properties/Resources.resx
QuickLook.Plugin/QuickLook.Plugin.VideoViewer/Properties/Settings.Designer.cs
QuickLook.Plugin/QuickLook.Plugin.VideoViewer/Properties/Settings.settings
QuickLook/QuickLook.csproj
QuickLook.Plugin/QuickLook.Plugin.VideoViewer/QuickLook.Plugin.VideoViewer.csproj
QuickLook.Plugin/QuickLook.Plugin.AppViewer/QuickLook.Plugin.AppViewer.csproj
QuickLook.Plugin/QuickLook.Plugin.ArchiveViewer/QuickLook.Plugin.ArchiveViewer.csproj
QuickLook.Plugin/QuickLook.Plugin.CLSIDViewer/QuickLook.Plugin.CLSIDViewer.csproj
QuickLook.Plugin/QuickLook.Plugin.CertViewer/QuickLook.Plugin.CertViewer.csproj
QuickLook.Plugin/QuickLook.Plugin.CsvViewer/QuickLook.Plugin.CsvViewer.csproj
QuickLook.Plugin/QuickLook.Plugin.ELFViewer/QuickLook.Plugin.ELFViewer.csproj
QuickLook.Plugin/QuickLook.Plugin.FontViewer/QuickLook.Plugin.FontViewer.csproj
QuickLook.Plugin/QuickLook.Plugin.HelixViewer/QuickLook.Plugin.HelixViewer.csproj
QuickLook.Plugin/QuickLook.Plugin.HtmlViewer/QuickLook.Plugin.HtmlViewer.csproj
QuickLook.Plugin/QuickLook.Plugin.ImageViewer/QuickLook.Plugin.ImageViewer.csproj
QuickLook.Plugin/QuickLook.Plugin.MailViewer/QuickLook.Plugin.MailViewer.csproj
QuickLook.Plugin/QuickLook.Plugin.MarkdownViewer/QuickLook.Plugin.MarkdownViewer.csproj
QuickLook.Plugin/QuickLook.Plugin.MediaInfoViewer/QuickLook.Plugin.MediaInfoViewer.csproj
QuickLook.Plugin/QuickLook.Plugin.OfficeViewer/QuickLook.Plugin.OfficeViewer.csproj
QuickLook.Plugin/QuickLook.Plugin.PDFViewer/QuickLook.Plugin.PdfViewer.csproj
QuickLook.Plugin/QuickLook.Plugin.PEViewer/QuickLook.Plugin.PEViewer.csproj
QuickLook.Plugin/QuickLook.Plugin.PluginInstaller/QuickLook.Plugin.PluginInstaller.csproj
QuickLook.Plugin/QuickLook.Plugin.TextViewer/QuickLook.Plugin.TextViewer.csproj
QuickLook.Plugin/QuickLook.Plugin.ThumbnailViewer/QuickLook.Plugin.ThumbnailViewer.csproj

Possibly linked issues

  • #Windows ARM64 native support: PR directly implements native Windows ARM64 support via ARM64 binaries and runtime architecture checks, matching the issue request.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 4 issues, and left some high level feedback:

  • In InitializeArm64, buttonTimeWPF.Click is wired twice (one referencing buttonTimeWPF.Tag and one buttonTime.Tag), which looks like a copy‑paste mistake and may be toggling the wrong control’s state; consider consolidating this into a single handler for the correct button.
  • The new ARM64 slider seek helpers (Seek_Drag_Started, Seek_Drag_Completed, Seek_Value_Changed) are not hooked up to sliderProgressWPF events, so the seek UX may not work as intended; wire these methods to the corresponding Thumb/ValueChanged events or remove them if not needed.
  • The change from ContextObject.IsBlocked to ContextObject.IsBusy in ViewerWindow.Actions.BeginShow alters the gating logic for showing the busy TextBlock; please double‑check that this is intentional and consistent with how these flags are used elsewhere.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `InitializeArm64`, `buttonTimeWPF.Click` is wired twice (one referencing `buttonTimeWPF.Tag` and one `buttonTime.Tag`), which looks like a copy‑paste mistake and may be toggling the wrong control’s state; consider consolidating this into a single handler for the correct button.
- The new ARM64 slider seek helpers (`Seek_Drag_Started`, `Seek_Drag_Completed`, `Seek_Value_Changed`) are not hooked up to `sliderProgressWPF` events, so the seek UX may not work as intended; wire these methods to the corresponding `Thumb`/`ValueChanged` events or remove them if not needed.
- The change from `ContextObject.IsBlocked` to `ContextObject.IsBusy` in `ViewerWindow.Actions.BeginShow` alters the gating logic for showing the busy TextBlock; please double‑check that this is intentional and consistent with how these flags are used elsewhere.

## Individual Comments

### Comment 1
<location> `QuickLook.Plugin/QuickLook.Plugin.VideoViewer/ViewerPanel.xaml.cs:129-138` </location>
<code_context>
+        mediaElementWPF.MediaOpened += MediaOpened;
+        mediaElementWPF.MediaEnded += MediaEnded;
+
+        buttonTimeWPF.Click += (_, _) => buttonTimeWPF.Tag = (string)buttonTimeWPF.Tag == "Time" ? "Length" : "Time";
+
+
+        timer = new DispatcherTimer
+        {
+            Interval = TimeSpan.FromSeconds(1)
+        };
+        timer.Tick += Seek_Timer;
+        timer.Start();
+
+        buttonTimeWPF.Click += (_, _) => buttonTime.Tag = (string)buttonTime.Tag == "Time" ? "Length" : "Time";
+
+        sliderProgressWPF.PreviewMouseDown += (_, e) =>
</code_context>

<issue_to_address>
**issue (bug_risk):** Duplicate click handler toggles both buttonTimeWPF and buttonTime tags, likely a copy-paste error.

The second `buttonTimeWPF.Click` handler updates `buttonTime.Tag`, unintentionally coupling the WPF button to the non-arm64 control and causing their states to diverge or alter a hidden control. Please remove or correct this handler so only the appropriate button’s `Tag` is changed.
</issue_to_address>

### Comment 2
<location> `QuickLook.Plugin/QuickLook.Plugin.VideoViewer/ViewerPanel.xaml.cs:210-218` </location>
<code_context>
             if (value == _shouldLoop) return;
             _shouldLoop = value;
             OnPropertyChanged();
+            if (!IsPlaying)
+            {
+                IsPlaying = true;
+
+                if (isArm64) mediaElementWPF.Play(); else mediaElement.Play();
+            }
         }
</code_context>

<issue_to_address>
**suggestion (bug_risk):** Changing ShouldLoop now auto-starts playback, which is a surprising side effect.

The setter now forces `IsPlaying = true` and calls `Play()` whenever looping is enabled while not playing, whereas previously it didn’t alter playback state. This hidden side effect can break callers that only want to configure looping (e.g., set options before starting). Please either constrain this behavior to a clearly intended context (e.g., only when resuming previously playing media) or keep `ShouldLoop` as a pure configuration flag and let existing playback controls start playback.

```suggestion
            if (value == _shouldLoop) return;
            _shouldLoop = value;
            OnPropertyChanged();
```
</issue_to_address>

### Comment 3
<location> `QuickLook/ViewerWindow.Actions.cs:204` </location>
<code_context>
         }

-        if (ContextObject.IsBlocked)
+        if (ContextObject.IsBusy)
         {
             ContextObject.ViewerContent = new System.Windows.Controls.TextBlock
</code_context>

<issue_to_address>
**issue (bug_risk):** Using IsBusy instead of IsBlocked changes the error handling semantics and may be incorrect.

Previously this branch ran only when `ContextObject.IsBlocked`, which suggests a persistent error or crash state. Changing it to `IsBusy` will show the "crashed plugin" UI while a plugin is just loading, masking a valid busy state. Unless the model semantics changed, this should likely remain `IsBlocked` (or explicitly combine `IsBusy`/`IsBlocked`) to keep error and loading states distinct.
</issue_to_address>

### Comment 4
<location> `QuickLook.Plugin/QuickLook.Plugin.TextViewer/Plugin.MoreMenu.cs:32-40` </location>
<code_context>
     {
         string translationFile = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Translations.config");
-        string extension = Path.GetExtension(_currentPath).ToLower();
+        string extension = "";
+        try
+        {
+            extension = Path.GetExtension(_currentPath).ToLower();
+        } catch (System.Exception)
+        { 
+        }
</code_context>

<issue_to_address>
**suggestion (bug_risk):** Empty catch-all around Path.GetExtension hides the real problem; a targeted null/invalid-path check would be clearer.

Catching `System.Exception` and defaulting to an empty extension will hide real issues. The likely problem here is a null or invalid `_currentPath`. Prefer checking `_currentPath` for null/empty before calling `GetExtension`, or at most catching `ArgumentException`, so unexpected errors still surface.

```suggestion
        string translationFile = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Translations.config");
        string extension = string.Empty;

        if (!string.IsNullOrEmpty(_currentPath))
        {
            try
            {
                extension = Path.GetExtension(_currentPath).ToLowerInvariant();
            }
            catch (ArgumentException)
            {
                // Invalid path format; leave extension as empty.
            }
        }
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +129 to +138
buttonTimeWPF.Click += (_, _) => buttonTimeWPF.Tag = (string)buttonTimeWPF.Tag == "Time" ? "Length" : "Time";


timer = new DispatcherTimer
{
Interval = TimeSpan.FromSeconds(1)
};
timer.Tick += Seek_Timer;
timer.Start();

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Duplicate click handler toggles both buttonTimeWPF and buttonTime tags, likely a copy-paste error.

The second buttonTimeWPF.Click handler updates buttonTime.Tag, unintentionally coupling the WPF button to the non-arm64 control and causing their states to diverge or alter a hidden control. Please remove or correct this handler so only the appropriate button’s Tag is changed.

Comment on lines 210 to +218
if (value == _shouldLoop) return;
_shouldLoop = value;
OnPropertyChanged();
if (!IsPlaying)
{
IsPlaying = true;

if (isArm64) mediaElementWPF.Play(); else mediaElement.Play();
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): Changing ShouldLoop now auto-starts playback, which is a surprising side effect.

The setter now forces IsPlaying = true and calls Play() whenever looping is enabled while not playing, whereas previously it didn’t alter playback state. This hidden side effect can break callers that only want to configure looping (e.g., set options before starting). Please either constrain this behavior to a clearly intended context (e.g., only when resuming previously playing media) or keep ShouldLoop as a pure configuration flag and let existing playback controls start playback.

Suggested change
if (value == _shouldLoop) return;
_shouldLoop = value;
OnPropertyChanged();
if (!IsPlaying)
{
IsPlaying = true;
if (isArm64) mediaElementWPF.Play(); else mediaElement.Play();
}
if (value == _shouldLoop) return;
_shouldLoop = value;
OnPropertyChanged();

}

if (ContextObject.IsBlocked)
if (ContextObject.IsBusy)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Using IsBusy instead of IsBlocked changes the error handling semantics and may be incorrect.

Previously this branch ran only when ContextObject.IsBlocked, which suggests a persistent error or crash state. Changing it to IsBusy will show the "crashed plugin" UI while a plugin is just loading, masking a valid busy state. Unless the model semantics changed, this should likely remain IsBlocked (or explicitly combine IsBusy/IsBlocked) to keep error and loading states distinct.

Comment on lines 32 to +40
string translationFile = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Translations.config");
string extension = Path.GetExtension(_currentPath).ToLower();
string extension = "";
try
{
extension = Path.GetExtension(_currentPath).ToLower();
} catch (System.Exception)
{
}

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): Empty catch-all around Path.GetExtension hides the real problem; a targeted null/invalid-path check would be clearer.

Catching System.Exception and defaulting to an empty extension will hide real issues. The likely problem here is a null or invalid _currentPath. Prefer checking _currentPath for null/empty before calling GetExtension, or at most catching ArgumentException, so unexpected errors still surface.

Suggested change
string translationFile = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Translations.config");
string extension = Path.GetExtension(_currentPath).ToLower();
string extension = "";
try
{
extension = Path.GetExtension(_currentPath).ToLower();
} catch (System.Exception)
{
}
string translationFile = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Translations.config");
string extension = string.Empty;
if (!string.IsNullOrEmpty(_currentPath))
{
try
{
extension = Path.GetExtension(_currentPath).ToLowerInvariant();
}
catch (ArgumentException)
{
// Invalid path format; leave extension as empty.
}
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant