Skip to content

Commit 8fe6299

Browse files
committed
feat: refactor game scanning logic and enhance UI elements in GameSelectionWindow
1 parent 32611fe commit 8fe6299

3 files changed

Lines changed: 175 additions & 143 deletions

File tree

Source/AutoImport.cs

Lines changed: 44 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,40 @@ public override IEnumerable<GameMetadata> GetGames(LibraryGetGamesArgs args)
3232
return ScanAndSelectGames();
3333
}
3434

35+
private HashSet<string> BuildExistingGamesSet()
36+
{
37+
var existingSet = new HashSet<string>();
38+
try
39+
{
40+
foreach (var game in PlayniteApi.Database.Games)
41+
{
42+
if (game.IsInstalled)
43+
{
44+
if (!string.IsNullOrEmpty(game.InstallDirectory))
45+
existingSet.Add(NormalizePath(game.InstallDirectory));
46+
47+
if (game.GameActions != null)
48+
{
49+
foreach (var action in game.GameActions)
50+
{
51+
if (action.Type == GameActionType.File && !string.IsNullOrEmpty(action.Path))
52+
{
53+
existingSet.Add(NormalizePath(action.Path));
54+
}
55+
}
56+
}
57+
}
58+
}
59+
}
60+
catch (Exception ex)
61+
{
62+
logger.Error(ex, "Failed to build existing games set");
63+
return new HashSet<string>();
64+
}
65+
66+
return existingSet;
67+
}
68+
3569
private string NormalizePath(string path)
3670
{
3771
if (string.IsNullOrEmpty(path)) return string.Empty;
@@ -53,26 +87,7 @@ private List<GameMetadata> ScanAndSelectGames()
5387
}
5488
}
5589

56-
var existingSet = new HashSet<string>();
57-
foreach (var game in PlayniteApi.Database.Games)
58-
{
59-
if (game.IsInstalled)
60-
{
61-
if (!string.IsNullOrEmpty(game.InstallDirectory))
62-
existingSet.Add(NormalizePath(game.InstallDirectory));
63-
64-
if (game.GameActions != null)
65-
{
66-
foreach (var action in game.GameActions)
67-
{
68-
if (action.Type == GameActionType.File && !string.IsNullOrEmpty(action.Path))
69-
{
70-
existingSet.Add(NormalizePath(action.Path));
71-
}
72-
}
73-
}
74-
}
75-
}
90+
var existingSet = BuildExistingGamesSet();
7691

7792
foreach (var folder in settings.Settings.ScanFolders)
7893
{
@@ -129,7 +144,10 @@ private IEnumerable<ScannedGameWrapper> ScanFolderLimited(string rootPath, HashS
129144
results.AddRange(GetExecutablesInDir(subDir, blockedSet, existingSet));
130145
}
131146
}
132-
catch { }
147+
catch (Exception ex)
148+
{
149+
logger.Warn(ex, $"Failed to scan subdirectories in: {rootPath}");
150+
}
133151
return results;
134152
}
135153

@@ -160,7 +178,7 @@ private IEnumerable<ScannedGameWrapper> GetExecutablesInDir(string dirPath, Hash
160178
var metadata = new GameMetadata
161179
{
162180
Name = gameName,
163-
GameId = fileInfo.FullName.GetHashCode().ToString(),
181+
GameId = fileInfo.FullName,
164182
InstallDirectory = fileInfo.DirectoryName,
165183
IsInstalled = true,
166184
Platforms = new HashSet<MetadataProperty> { new MetadataSpecProperty("pc_windows") },
@@ -180,13 +198,15 @@ private IEnumerable<ScannedGameWrapper> GetExecutablesInDir(string dirPath, Hash
180198
}
181199
}
182200
}
183-
catch { }
201+
catch (Exception ex)
202+
{
203+
logger.Warn(ex, $"Failed to scan directory for executables: {dirPath}");
204+
}
184205
return list;
185206
}
186207

187208
private string GetGameNameFromFolderOrExe(string dirPath, FileInfo fileInfo)
188209
{
189-
// Try to get name from folder first
190210
string folderName = Path.GetFileName(dirPath);
191211
if (!string.IsNullOrWhiteSpace(folderName))
192212
{
@@ -197,7 +217,6 @@ private string GetGameNameFromFolderOrExe(string dirPath, FileInfo fileInfo)
197217
}
198218
}
199219

200-
// Fall back to exe file name
201220
string rawExeName = Path.GetFileNameWithoutExtension(fileInfo.Name);
202221
return CleanGameName(rawExeName);
203222
}
@@ -206,10 +225,8 @@ private bool IsValidGameName(string name)
206225
{
207226
if (string.IsNullOrWhiteSpace(name)) return false;
208227

209-
// Check if name is too short (likely not a real game name)
210228
if (name.Length < 2) return false;
211229

212-
// Check for generic folder names that shouldn't be used
213230
string lowerName = name.ToLowerInvariant();
214231
string[] genericNames = { "bin", "game", "games", "exe", "exes", "program", "programs",
215232
"application", "applications", "software", "tools", "util",
Lines changed: 130 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -1,129 +1,145 @@
11
<Window x:Class="AutoImportPlugin.GameSelectionWindow"
22
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
33
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4-
Title="Select Games to Import"
4+
Title="Select Games to Import"
55
Height="550" Width="900"
66
WindowStartupLocation="CenterOwner"
77
ShowInTaskbar="False"
8-
Background="{DynamicResource WindowBackgroundBrush}"
9-
Foreground="{DynamicResource TextBrush}">
8+
Background="#1E1E1E"
9+
Foreground="#F0F0F0">
1010

11-
<Window.Resources>
12-
<Style TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
13-
<Setter Property="Padding" Value="20,12"/>
14-
<Setter Property="Margin" Value="0,0,10,0"/>
15-
<Setter Property="Background" Value="{DynamicResource ControlBackgroundBrush}"/>
16-
<Setter Property="Foreground" Value="{DynamicResource TextBrush}"/>
17-
<Setter Property="BorderBrush" Value="{DynamicResource NormalBorderBrush}"/>
18-
<Setter Property="BorderThickness" Value="1"/>
19-
<Setter Property="Template">
20-
<Setter.Value>
21-
<ControlTemplate TargetType="Button">
22-
<Border Background="{TemplateBinding Background}"
23-
BorderBrush="{TemplateBinding BorderBrush}"
24-
BorderThickness="{TemplateBinding BorderThickness}"
25-
CornerRadius="3">
26-
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
27-
</Border>
28-
<ControlTemplate.Triggers>
29-
<Trigger Property="IsMouseOver" Value="True">
30-
<Setter Property="Background" Value="{DynamicResource GlyphBrush}"/>
31-
<Setter Property="Foreground" Value="{DynamicResource WindowBackgroundBrush}"/>
32-
</Trigger>
33-
</ControlTemplate.Triggers>
34-
</ControlTemplate>
35-
</Setter.Value>
36-
</Setter>
11+
<Window.Resources>
12+
<Style TargetType="Button">
13+
<Setter Property="Background" Value="#333333"/>
14+
<Setter Property="Foreground" Value="White"/>
15+
<Setter Property="BorderBrush" Value="#454545"/>
16+
<Setter Property="BorderThickness" Value="1"/>
17+
<Setter Property="Padding" Value="15,6"/>
18+
<Setter Property="FontSize" Value="13"/>
19+
<Setter Property="Margin" Value="0,0,10,0"/>
20+
<Setter Property="Template">
21+
<Setter.Value>
22+
<ControlTemplate TargetType="Button">
23+
<Border Background="{TemplateBinding Background}"
24+
BorderBrush="{TemplateBinding BorderBrush}"
25+
BorderThickness="{TemplateBinding BorderThickness}"
26+
CornerRadius="4">
27+
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
28+
</Border>
29+
<ControlTemplate.Triggers>
30+
<Trigger Property="IsMouseOver" Value="True">
31+
<Setter Property="Background" Value="#454545"/>
32+
</Trigger>
33+
<Trigger Property="IsPressed" Value="True">
34+
<Setter Property="Background" Value="#252525"/>
35+
</Trigger>
36+
</ControlTemplate.Triggers>
37+
</ControlTemplate>
38+
</Setter.Value>
39+
</Setter>
40+
</Style>
41+
42+
<Style TargetType="CheckBox">
43+
<Setter Property="HorizontalAlignment" Value="Center"/>
44+
<Setter Property="VerticalAlignment" Value="Center"/>
45+
</Style>
46+
</Window.Resources>
47+
48+
<Grid Margin="15">
49+
<Grid.RowDefinitions>
50+
<RowDefinition Height="Auto"/>
51+
<RowDefinition Height="*"/>
52+
<RowDefinition Height="Auto"/>
53+
</Grid.RowDefinitions>
54+
55+
<StackPanel Grid.Row="0" Margin="0,0,0,15">
56+
<TextBlock Text="Select 'Import' to add to library. Select 'Ignore' to block from future scans."
57+
FontSize="14"
58+
Foreground="#AAAAAA"
59+
Margin="0,5,0,0"/>
60+
</StackPanel>
61+
62+
<DataGrid x:Name="GridGames"
63+
Grid.Row="1"
64+
AutoGenerateColumns="False"
65+
CanUserAddRows="False"
66+
HeadersVisibility="Column"
67+
SelectionMode="Single"
68+
Background="#202020"
69+
BorderBrush="#3E3E3E"
70+
BorderThickness="1"
71+
GridLinesVisibility="Horizontal"
72+
HorizontalGridLinesBrush="#303030"
73+
VerticalGridLinesBrush="#303030"
74+
RowBackground="Transparent"
75+
Foreground="#DDDDDD">
76+
77+
<DataGrid.Resources>
78+
<Style TargetType="DataGridColumnHeader">
79+
<Setter Property="Background" Value="#2D2D2D"/>
80+
<Setter Property="Foreground" Value="White"/>
81+
<Setter Property="Padding" Value="10,8"/>
82+
<Setter Property="BorderBrush" Value="#3E3E3E"/>
83+
<Setter Property="BorderThickness" Value="0,0,1,1"/>
84+
<Setter Property="FontWeight" Value="SemiBold"/>
3785
</Style>
38-
</Window.Resources>
3986

40-
<Grid Margin="15">
41-
<Grid.RowDefinitions>
42-
<RowDefinition Height="Auto"/>
43-
<RowDefinition Height="*"/>
44-
<RowDefinition Height="Auto"/>
45-
</Grid.RowDefinitions>
87+
<Style TargetType="DataGridCell">
88+
<Setter Property="BorderThickness" Value="0"/>
89+
<Setter Property="Padding" Value="5"/>
90+
<Setter Property="Template">
91+
<Setter.Value>
92+
<ControlTemplate TargetType="DataGridCell">
93+
<Border Padding="{TemplateBinding Padding}"
94+
Background="{TemplateBinding Background}"
95+
BorderBrush="{TemplateBinding BorderBrush}"
96+
BorderThickness="{TemplateBinding BorderThickness}">
97+
<ContentPresenter VerticalAlignment="Center"/>
98+
</Border>
99+
</ControlTemplate>
100+
</Setter.Value>
101+
</Setter>
102+
<Style.Triggers>
103+
<Trigger Property="IsSelected" Value="True">
104+
<Setter Property="Background" Value="#353535"/>
105+
<Setter Property="Foreground" Value="White"/>
106+
</Trigger>
107+
</Style.Triggers>
108+
</Style>
109+
</DataGrid.Resources>
46110

47-
<StackPanel Grid.Row="0" Margin="0,0,0,15">
48-
<TextBlock Text="Select 'Import' to add to library. Select 'Ignore' to block from future scans."
49-
FontSize="14"
50-
Foreground="{DynamicResource TextBrush}"
51-
Opacity="0.7"
52-
Margin="0,5,0,0"/>
53-
</StackPanel>
111+
<DataGrid.Columns>
112+
<DataGridTemplateColumn Header="Import" Width="60">
113+
<DataGridTemplateColumn.CellTemplate>
114+
<DataTemplate>
115+
<CheckBox IsChecked="{Binding IsSelected, UpdateSourceTrigger=PropertyChanged}" />
116+
</DataTemplate>
117+
</DataGridTemplateColumn.CellTemplate>
118+
</DataGridTemplateColumn>
54119

55-
<DataGrid x:Name="GridGames"
56-
Grid.Row="1"
57-
AutoGenerateColumns="False"
58-
CanUserAddRows="False"
59-
HeadersVisibility="Column"
60-
GridLinesVisibility="Horizontal"
61-
SelectionMode="Single"
62-
Background="{DynamicResource ControlBackgroundBrush}"
63-
Foreground="{DynamicResource TextBrush}"
64-
BorderBrush="{DynamicResource PanelSeparatorBrush}"
65-
BorderThickness="1"
66-
HorizontalGridLinesBrush="{DynamicResource PanelSeparatorBrush}"
67-
VerticalGridLinesBrush="{DynamicResource PanelSeparatorBrush}"
68-
RowBackground="Transparent">
69-
70-
<DataGrid.Resources>
71-
<Style TargetType="DataGridColumnHeader">
72-
<Setter Property="Background" Value="{DynamicResource WindowBackgroundBrush}"/>
73-
<Setter Property="Foreground" Value="{DynamicResource TextBrush}"/>
74-
<Setter Property="Padding" Value="10,8"/>
75-
<Setter Property="BorderBrush" Value="{DynamicResource PanelSeparatorBrush}"/>
76-
<Setter Property="BorderThickness" Value="0,0,0,1"/>
77-
<Setter Property="FontWeight" Value="SemiBold"/>
78-
</Style>
79-
80-
<Style TargetType="DataGridCell">
81-
<Setter Property="Padding" Value="5"/>
82-
<Setter Property="Template">
83-
<Setter.Value>
84-
<ControlTemplate TargetType="DataGridCell">
85-
<Border Padding="{TemplateBinding Padding}"
86-
Background="{TemplateBinding Background}"
87-
BorderBrush="{TemplateBinding BorderBrush}"
88-
BorderThickness="{TemplateBinding BorderThickness}">
89-
<ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
90-
</Border>
91-
</ControlTemplate>
92-
</Setter.Value>
93-
</Setter>
94-
<Style.Triggers>
95-
<Trigger Property="IsSelected" Value="True">
96-
<Setter Property="Background" Value="{DynamicResource GlyphBrush}"/>
97-
<Setter Property="Foreground" Value="{DynamicResource WindowBackgroundBrush}"/>
98-
</Trigger>
99-
</Style.Triggers>
100-
</Style>
101-
</DataGrid.Resources>
120+
<DataGridTemplateColumn Header="Ignore" Width="60">
121+
<DataGridTemplateColumn.CellTemplate>
122+
<DataTemplate>
123+
<CheckBox IsChecked="{Binding IsIgnored, UpdateSourceTrigger=PropertyChanged}" />
124+
</DataTemplate>
125+
</DataGridTemplateColumn.CellTemplate>
126+
</DataGridTemplateColumn>
102127

103-
<DataGrid.Columns>
104-
<DataGridCheckBoxColumn Binding="{Binding IsSelected, UpdateSourceTrigger=PropertyChanged}"
105-
Header="Import"
106-
Width="60"/>
107-
108-
<DataGridCheckBoxColumn Binding="{Binding IsIgnored, UpdateSourceTrigger=PropertyChanged}"
109-
Header="Ignore"
110-
Width="60"/>
128+
<DataGridTextColumn Binding="{Binding Name}"
129+
Header="Name"
130+
Width="300"
131+
IsReadOnly="True"/>
111132

112-
<DataGridTextColumn Binding="{Binding Name}"
113-
Header="Name"
114-
Width="300"
115-
IsReadOnly="True"/>
116-
117-
<DataGridTextColumn Binding="{Binding ExecutablePath}"
118-
Header="Executable Path"
119-
Width="*"
120-
IsReadOnly="True"/>
121-
</DataGrid.Columns>
122-
</DataGrid>
133+
<DataGridTextColumn Binding="{Binding ExecutablePath}"
134+
Header="Executable Path"
135+
Width="*"
136+
IsReadOnly="True"/>
137+
</DataGrid.Columns>
138+
</DataGrid>
123139

124-
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,15,0,0">
125-
<Button Content="Import Selected" Click="BtnImport_Click"/>
126-
<Button Content="Cancel" Click="BtnCancel_Click" Margin="0"/>
127-
</StackPanel>
128-
</Grid>
140+
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,15,0,0">
141+
<Button Content="Import Selected" Click="BtnImport_Click" IsDefault="True"/>
142+
<Button Content="Cancel" Click="BtnCancel_Click" IsCancel="True" Margin="0"/>
143+
</StackPanel>
144+
</Grid>
129145
</Window>

0 commit comments

Comments
 (0)