1
+ Add-Type - Name Window - Namespace Console - MemberDefinition '
2
+ [DllImport("Kernel32.dll")]
3
+ public static extern IntPtr GetConsoleWindow();
4
+
5
+ [DllImport("user32.dll")]
6
+ public static extern bool ShowWindow(IntPtr hWnd, Int32 nCmdShow);'
7
+
8
+ [Console.Window ]::ShowWindow([Console.Window ]::GetConsoleWindow(), 0 )
9
+ $global :python = $false
10
+ $global :ffmpeg = $false
11
+ $global :gopro2gpx = $false
12
+ $global :choco = $false
13
+ $global :git = $false
14
+ $global :singlemode = $true
15
+ $global :singlevid = " "
16
+ $global :filepath = " "
17
+ Add-Type - AssemblyName PresentationCore, PresentationFramework
18
+
19
+ Function New-WPFMessageBox {
20
+
21
+ # For examples for use, see my blog:
22
+ # https://smsagent.wordpress.com/2017/08/24/a-customisable-wpf-messagebox-for-powershell/
23
+
24
+ # CHANGES
25
+ # 2017-09-11 - Added some required assemblies in the dynamic parameters to avoid errors when run from the PS console host.
26
+
27
+ # Define Parameters
28
+ [CmdletBinding ()]
29
+ Param
30
+ (
31
+ # The popup Content
32
+ [Parameter (Mandatory = $True , Position = 0 )]
33
+ [Object ]$Content ,
34
+
35
+ # The window title
36
+ [Parameter (Mandatory = $false , Position = 1 )]
37
+ [string ]$Title ,
38
+
39
+ # The buttons to add
40
+ [Parameter (Mandatory = $false , Position = 2 )]
41
+ [ValidateSet (' OK' , ' OK-Cancel' , ' Abort-Retry-Ignore' , ' Yes-No-Cancel' , ' Yes-No' , ' Retry-Cancel' , ' Cancel-TryAgain-Continue' , ' None' )]
42
+ [array ]$ButtonType = ' OK' ,
43
+
44
+ # The buttons to add
45
+ [Parameter (Mandatory = $false , Position = 3 )]
46
+ [array ]$CustomButtons ,
47
+
48
+ # Content font size
49
+ [Parameter (Mandatory = $false , Position = 4 )]
50
+ [int ]$ContentFontSize = 14 ,
51
+
52
+ # Title font size
53
+ [Parameter (Mandatory = $false , Position = 5 )]
54
+ [int ]$TitleFontSize = 14 ,
55
+
56
+ # BorderThickness
57
+ [Parameter (Mandatory = $false , Position = 6 )]
58
+ [int ]$BorderThickness = 0 ,
59
+
60
+ # CornerRadius
61
+ [Parameter (Mandatory = $false , Position = 7 )]
62
+ [int ]$CornerRadius = 8 ,
63
+
64
+ # ShadowDepth
65
+ [Parameter (Mandatory = $false , Position = 8 )]
66
+ [int ]$ShadowDepth = 3 ,
67
+
68
+ # BlurRadius
69
+ [Parameter (Mandatory = $false , Position = 9 )]
70
+ [int ]$BlurRadius = 20 ,
71
+
72
+ # WindowHost
73
+ [Parameter (Mandatory = $false , Position = 10 )]
74
+ [object ]$WindowHost ,
75
+
76
+ # Timeout in seconds,
77
+ [Parameter (Mandatory = $false , Position = 11 )]
78
+ [int ]$Timeout ,
79
+
80
+ # Code for Window Loaded event,
81
+ [Parameter (Mandatory = $false , Position = 12 )]
82
+ [scriptblock ]$OnLoaded ,
83
+
84
+ # Code for Window Closed event,
85
+ [Parameter (Mandatory = $false , Position = 13 )]
86
+ [scriptblock ]$OnClosed
87
+
88
+ )
89
+
90
+ # Dynamically Populated parameters
91
+ DynamicParam {
92
+
93
+ # Add assemblies for use in PS Console
94
+ Add-Type - AssemblyName System.Drawing, PresentationCore
95
+
96
+ # ContentBackground
97
+ $ContentBackground = ' ContentBackground'
98
+ $AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute ]
99
+ $ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
100
+ $ParameterAttribute.Mandatory = $False
101
+ $AttributeCollection.Add ($ParameterAttribute )
102
+ $RuntimeParameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
103
+ $arrSet = [System.Drawing.Brushes ] | Get-Member - Static - MemberType Property | Select - ExpandProperty Name
104
+ $ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($arrSet )
105
+ $AttributeCollection.Add ($ValidateSetAttribute )
106
+ $PSBoundParameters.ContentBackground = " White"
107
+ $RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($ContentBackground , [string ], $AttributeCollection )
108
+ $RuntimeParameterDictionary.Add ($ContentBackground , $RuntimeParameter )
109
+
110
+
111
+ # FontFamily
112
+ $FontFamily = ' FontFamily'
113
+ $AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute ]
114
+ $ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
115
+ $ParameterAttribute.Mandatory = $False
116
+ $AttributeCollection.Add ($ParameterAttribute )
117
+ $arrSet = [System.Drawing.FontFamily ]::Families.Name | Select - Skip 1
118
+ $ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($arrSet )
119
+ $AttributeCollection.Add ($ValidateSetAttribute )
120
+ $RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($FontFamily , [string ], $AttributeCollection )
121
+ $RuntimeParameterDictionary.Add ($FontFamily , $RuntimeParameter )
122
+ $PSBoundParameters.FontFamily = " Segoe UI"
123
+
124
+ # TitleFontWeight
125
+ $TitleFontWeight = ' TitleFontWeight'
126
+ $AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute ]
127
+ $ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
128
+ $ParameterAttribute.Mandatory = $False
129
+ $AttributeCollection.Add ($ParameterAttribute )
130
+ $arrSet = [System.Windows.FontWeights ] | Get-Member - Static - MemberType Property | Select - ExpandProperty Name
131
+ $ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($arrSet )
132
+ $AttributeCollection.Add ($ValidateSetAttribute )
133
+ $PSBoundParameters.TitleFontWeight = " Normal"
134
+ $RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($TitleFontWeight , [string ], $AttributeCollection )
135
+ $RuntimeParameterDictionary.Add ($TitleFontWeight , $RuntimeParameter )
136
+
137
+ # ContentFontWeight
138
+ $ContentFontWeight = ' ContentFontWeight'
139
+ $AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute ]
140
+ $ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
141
+ $ParameterAttribute.Mandatory = $False
142
+ $AttributeCollection.Add ($ParameterAttribute )
143
+ $arrSet = [System.Windows.FontWeights ] | Get-Member - Static - MemberType Property | Select - ExpandProperty Name
144
+ $ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($arrSet )
145
+ $AttributeCollection.Add ($ValidateSetAttribute )
146
+ $PSBoundParameters.ContentFontWeight = " Normal"
147
+ $RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($ContentFontWeight , [string ], $AttributeCollection )
148
+ $RuntimeParameterDictionary.Add ($ContentFontWeight , $RuntimeParameter )
149
+
150
+
151
+ # ContentTextForeground
152
+ $ContentTextForeground = ' ContentTextForeground'
153
+ $AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute ]
154
+ $ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
155
+ $ParameterAttribute.Mandatory = $False
156
+ $AttributeCollection.Add ($ParameterAttribute )
157
+ $arrSet = [System.Drawing.Brushes ] | Get-Member - Static - MemberType Property | Select - ExpandProperty Name
158
+ $ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($arrSet )
159
+ $AttributeCollection.Add ($ValidateSetAttribute )
160
+ $PSBoundParameters.ContentTextForeground = " Black"
161
+ $RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($ContentTextForeground , [string ], $AttributeCollection )
162
+ $RuntimeParameterDictionary.Add ($ContentTextForeground , $RuntimeParameter )
163
+
164
+ # TitleTextForeground
165
+ $TitleTextForeground = ' TitleTextForeground'
166
+ $AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute ]
167
+ $ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
168
+ $ParameterAttribute.Mandatory = $False
169
+ $AttributeCollection.Add ($ParameterAttribute )
170
+ $arrSet = [System.Drawing.Brushes ] | Get-Member - Static - MemberType Property | Select - ExpandProperty Name
171
+ $ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($arrSet )
172
+ $AttributeCollection.Add ($ValidateSetAttribute )
173
+ $PSBoundParameters.TitleTextForeground = " Black"
174
+ $RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($TitleTextForeground , [string ], $AttributeCollection )
175
+ $RuntimeParameterDictionary.Add ($TitleTextForeground , $RuntimeParameter )
176
+
177
+ # BorderBrush
178
+ $BorderBrush = ' BorderBrush'
179
+ $AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute ]
180
+ $ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
181
+ $ParameterAttribute.Mandatory = $False
182
+ $AttributeCollection.Add ($ParameterAttribute )
183
+ $arrSet = [System.Drawing.Brushes ] | Get-Member - Static - MemberType Property | Select - ExpandProperty Name
184
+ $ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($arrSet )
185
+ $AttributeCollection.Add ($ValidateSetAttribute )
186
+ $PSBoundParameters.BorderBrush = " Black"
187
+ $RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($BorderBrush , [string ], $AttributeCollection )
188
+ $RuntimeParameterDictionary.Add ($BorderBrush , $RuntimeParameter )
189
+
190
+
191
+ # TitleBackground
192
+ $TitleBackground = ' TitleBackground'
193
+ $AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute ]
194
+ $ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
195
+ $ParameterAttribute.Mandatory = $False
196
+ $AttributeCollection.Add ($ParameterAttribute )
197
+ $arrSet = [System.Drawing.Brushes ] | Get-Member - Static - MemberType Property | Select - ExpandProperty Name
198
+ $ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($arrSet )
199
+ $AttributeCollection.Add ($ValidateSetAttribute )
200
+ $PSBoundParameters.TitleBackground = " White"
201
+ $RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($TitleBackground , [string ], $AttributeCollection )
202
+ $RuntimeParameterDictionary.Add ($TitleBackground , $RuntimeParameter )
203
+
204
+ # ButtonTextForeground
205
+ $ButtonTextForeground = ' ButtonTextForeground'
206
+ $AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute ]
207
+ $ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
208
+ $ParameterAttribute.Mandatory = $False
209
+ $AttributeCollection.Add ($ParameterAttribute )
210
+ $arrSet = [System.Drawing.Brushes ] | Get-Member - Static - MemberType Property | Select - ExpandProperty Name
211
+ $ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($arrSet )
212
+ $AttributeCollection.Add ($ValidateSetAttribute )
213
+ $PSBoundParameters.ButtonTextForeground = " Black"
214
+ $RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($ButtonTextForeground , [string ], $AttributeCollection )
215
+ $RuntimeParameterDictionary.Add ($ButtonTextForeground , $RuntimeParameter )
216
+
217
+ # Sound
218
+ $Sound = ' Sound'
219
+ $AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute ]
220
+ $ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
221
+ $ParameterAttribute.Mandatory = $False
222
+ # $ParameterAttribute.Position = 14
223
+ $AttributeCollection.Add ($ParameterAttribute )
224
+ $arrSet = (Get-ChildItem " $env: SystemDrive \Windows\Media" - Filter Windows* | Select - ExpandProperty Name).Replace(' .wav' , ' ' )
225
+ $ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($arrSet )
226
+ $AttributeCollection.Add ($ValidateSetAttribute )
227
+ $RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($Sound , [string ], $AttributeCollection )
228
+ $RuntimeParameterDictionary.Add ($Sound , $RuntimeParameter )
229
+
230
+ return $RuntimeParameterDictionary
231
+ }
232
+
233
+ Begin {
234
+ Add-Type - AssemblyName PresentationFramework
235
+ }
236
+
237
+ Process {
238
+
239
+ # Define the XAML markup
240
+ [XML ]$Xaml = @"
241
+ <Window
242
+ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
243
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
244
+ x:Name="Window" Title="" SizeToContent="WidthAndHeight" WindowStartupLocation="CenterScreen" WindowStyle="None" ResizeMode="NoResize" AllowsTransparency="True" Background="Transparent" Opacity="1">
245
+ <Window.Resources>
246
+ <Style TargetType="{x:Type Button}">
247
+ <Setter Property="Template">
248
+ <Setter.Value>
249
+ <ControlTemplate TargetType="Button">
250
+ <Border>
251
+ <Grid Background="{TemplateBinding Background}">
252
+ <ContentPresenter />
253
+ </Grid>
254
+ </Border>
255
+ </ControlTemplate>
256
+ </Setter.Value>
257
+ </Setter>
258
+ </Style>
259
+ </Window.Resources>
260
+ <Border x:Name="MainBorder" Margin="10" CornerRadius="$CornerRadius " BorderThickness="$BorderThickness " BorderBrush="$ ( $PSBoundParameters.BorderBrush ) " Padding="0" >
261
+ <Border.Effect>
262
+ <DropShadowEffect x:Name="DSE" Color="Black" Direction="270" BlurRadius="$BlurRadius " ShadowDepth="$ShadowDepth " Opacity="0.6" />
263
+ </Border.Effect>
264
+ <Border.Triggers>
265
+ <EventTrigger RoutedEvent="Window.Loaded">
266
+ <BeginStoryboard>
267
+ <Storyboard>
268
+ <DoubleAnimation Storyboard.TargetName="DSE" Storyboard.TargetProperty="ShadowDepth" From="0" To="$ShadowDepth " Duration="0:0:1" AutoReverse="False" />
269
+ <DoubleAnimation Storyboard.TargetName="DSE" Storyboard.TargetProperty="BlurRadius" From="0" To="$BlurRadius " Duration="0:0:1" AutoReverse="False" />
270
+ </Storyboard>
271
+ </BeginStoryboard>
272
+ </EventTrigger>
273
+ </Border.Triggers>
274
+ <Grid >
275
+ <Border Name="Mask" CornerRadius="$CornerRadius " Background="$ ( $PSBoundParameters.ContentBackground ) " />
276
+ <Grid x:Name="Grid" Background="$ ( $PSBoundParameters.ContentBackground ) ">
277
+ <Grid.OpacityMask>
278
+ <VisualBrush Visual="{Binding ElementName=Mask}"/>
279
+ </Grid.OpacityMask>
280
+ <StackPanel Name="StackPanel" >
281
+ <TextBox Name="TitleBar" IsReadOnly="True" IsHitTestVisible="False" Text="$Title " Padding="10" FontFamily="$ ( $PSBoundParameters.FontFamily ) " FontSize="$TitleFontSize " Foreground="$ ( $PSBoundParameters.TitleTextForeground ) " FontWeight="$ ( $PSBoundParameters.TitleFontWeight ) " Background="$ ( $PSBoundParameters.TitleBackground ) " HorizontalAlignment="Stretch" VerticalAlignment="Center" Width="Auto" HorizontalContentAlignment="Center" BorderThickness="0"/>
282
+ <DockPanel Name="ContentHost" Margin="0,10,0,10" >
283
+ </DockPanel>
284
+ <DockPanel Name="ButtonHost" LastChildFill="False" HorizontalAlignment="Center" >
285
+ </DockPanel>
286
+ </StackPanel>
287
+ </Grid>
288
+ </Grid>
289
+ </Border>
290
+ </Window>
291
+ "@
292
+
293
+ [XML ]$ButtonXaml = @"
294
+ <Button xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="Auto" Height="30" FontFamily="Segui" FontSize="16" Background="Transparent" Foreground="White" BorderThickness="1" Margin="10" Padding="20,0,20,0" HorizontalAlignment="Right" Cursor="Hand"/>
295
+ "@
296
+
297
+ [XML ]$ButtonTextXaml = @"
298
+ <TextBlock xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" FontFamily="$ ( $PSBoundParameters.FontFamily ) " FontSize="16" Background="Transparent" Foreground="$ ( $PSBoundParameters.ButtonTextForeground ) " Padding="20,5,20,5" HorizontalAlignment="Center" VerticalAlignment="Center"/>
299
+ "@
300
+
301
+ [XML ]$ContentTextXaml = @"
302
+ <TextBlock xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Text="$Content " Foreground="$ ( $PSBoundParameters.ContentTextForeground ) " DockPanel.Dock="Right" HorizontalAlignment="Center" VerticalAlignment="Center" FontFamily="$ ( $PSBoundParameters.FontFamily ) " FontSize="$ContentFontSize " FontWeight="$ ( $PSBoundParameters.ContentFontWeight ) " TextWrapping="Wrap" Height="Auto" MaxWidth="500" MinWidth="50" Padding="10"/>
303
+ "@
304
+
305
+ # Load the window from XAML
306
+ $Window = [Windows.Markup.XamlReader ]::Load((New-Object - TypeName System.Xml.XmlNodeReader - ArgumentList $xaml ))
307
+
308
+ # Custom function to add a button
309
+ Function Add-Button {
310
+ Param ($Content )
311
+ $Button = [Windows.Markup.XamlReader ]::Load((New-Object - TypeName System.Xml.XmlNodeReader - ArgumentList $ButtonXaml ))
312
+ $ButtonText = [Windows.Markup.XamlReader ]::Load((New-Object - TypeName System.Xml.XmlNodeReader - ArgumentList $ButtonTextXaml ))
313
+ $ButtonText.Text = " $Content "
314
+ $Button.Content = $ButtonText
315
+ $Button.Add_MouseEnter ({
316
+ $This.Content.FontSize = " 17"
317
+ })
318
+ $Button.Add_MouseLeave ({
319
+ $This.Content.FontSize = " 16"
320
+ })
321
+ $Button.Add_Click ({
322
+ New-Variable - Name WPFMessageBoxOutput - Value $ ($This.Content.Text ) - Option ReadOnly - Scope Script - Force
323
+ $Window.Close ()
324
+ })
325
+ $Window.FindName (' ButtonHost' ).AddChild($Button )
326
+ }
327
+
328
+ # Add buttons
329
+ If ($ButtonType -eq " OK" )
330
+ {
331
+ Add-Button - Content " OK"
332
+ }
333
+
334
+ If ($ButtonType -eq " OK-Cancel" )
335
+ {
336
+ Add-Button - Content " OK"
337
+ Add-Button - Content " Cancel"
338
+ }
339
+
340
+ If ($ButtonType -eq " Abort-Retry-Ignore" )
341
+ {
342
+ Add-Button - Content " Abort"
343
+ Add-Button - Content " Retry"
344
+ Add-Button - Content " Ignore"
345
+ }
346
+
347
+ If ($ButtonType -eq " Yes-No-Cancel" )
348
+ {
349
+ Add-Button - Content " Yes"
350
+ Add-Button - Content " No"
351
+ Add-Button - Content " Cancel"
352
+ }
353
+
354
+ If ($ButtonType -eq " Yes-No" )
355
+ {
356
+ Add-Button - Content " Yes"
357
+ Add-Button - Content " No"
358
+ }
359
+
360
+ If ($ButtonType -eq " Retry-Cancel" )
361
+ {
362
+ Add-Button - Content " Retry"
363
+ Add-Button - Content " Cancel"
364
+ }
365
+
366
+ If ($ButtonType -eq " Cancel-TryAgain-Continue" )
367
+ {
368
+ Add-Button - Content " Cancel"
369
+ Add-Button - Content " TryAgain"
370
+ Add-Button - Content " Continue"
371
+ }
372
+
373
+ If ($ButtonType -eq " None" -and $CustomButtons )
374
+ {
375
+ Foreach ($CustomButton in $CustomButtons )
376
+ {
377
+ Add-Button - Content " $CustomButton "
378
+ }
379
+ }
380
+
381
+ # Remove the title bar if no title is provided
382
+ If ($Title -eq " " )
383
+ {
384
+ $TitleBar = $Window.FindName (' TitleBar' )
385
+ $Window.FindName (' StackPanel' ).Children.Remove($TitleBar )
386
+ }
387
+
388
+ # Add the Content
389
+ If ($Content -is [String ])
390
+ {
391
+ # Replace double quotes with single to avoid quote issues in strings
392
+ If ($Content -match ' "' )
393
+ {
394
+ $Content = $Content.Replace (' "' , " '" )
395
+ }
396
+
397
+ # Use a text box for a string value...
398
+ $ContentTextBox = [Windows.Markup.XamlReader ]::Load((New-Object - TypeName System.Xml.XmlNodeReader - ArgumentList $ContentTextXaml ))
399
+ $Window.FindName (' ContentHost' ).AddChild($ContentTextBox )
400
+ }
401
+ Else
402
+ {
403
+ # ...or add a WPF element as a child
404
+ Try
405
+ {
406
+ $Window.FindName (' ContentHost' ).AddChild($Content )
407
+ }
408
+ Catch
409
+ {
410
+ $_
411
+ }
412
+ }
413
+
414
+ # Enable window to move when dragged
415
+ $Window.FindName (' Grid' ).Add_MouseLeftButtonDown({
416
+ $Window.DragMove ()
417
+ })
418
+
419
+ # Activate the window on loading
420
+ If ($OnLoaded )
421
+ {
422
+ $Window.Add_Loaded ({
423
+ $This.Activate ()
424
+ Invoke-Command $OnLoaded
425
+ })
426
+ }
427
+ Else
428
+ {
429
+ $Window.Add_Loaded ({
430
+ $This.Activate ()
431
+ })
432
+ }
433
+
434
+
435
+ # Stop the dispatcher timer if exists
436
+ If ($OnClosed )
437
+ {
438
+ $Window.Add_Closed ({
439
+ If ($DispatcherTimer )
440
+ {
441
+ $DispatcherTimer.Stop ()
442
+ }
443
+ Invoke-Command $OnClosed
444
+ })
445
+ }
446
+ Else
447
+ {
448
+ $Window.Add_Closed ({
449
+ If ($DispatcherTimer )
450
+ {
451
+ $DispatcherTimer.Stop ()
452
+ }
453
+ })
454
+ }
455
+
456
+
457
+ # If a window host is provided assign it as the owner
458
+ If ($WindowHost )
459
+ {
460
+ $Window.Owner = $WindowHost
461
+ $Window.WindowStartupLocation = " CenterOwner"
462
+ }
463
+
464
+ # If a timeout value is provided, use a dispatcher timer to close the window when timeout is reached
465
+ If ($Timeout )
466
+ {
467
+ $Stopwatch = New-object System.Diagnostics.Stopwatch
468
+ $TimerCode = {
469
+ If ($Stopwatch.Elapsed.TotalSeconds -ge $Timeout )
470
+ {
471
+ $Stopwatch.Stop ()
472
+ $Window.Close ()
473
+ }
474
+ }
475
+ $DispatcherTimer = New-Object - TypeName System.Windows.Threading.DispatcherTimer
476
+ $DispatcherTimer.Interval = [TimeSpan ]::FromSeconds(1 )
477
+ $DispatcherTimer.Add_Tick ($TimerCode )
478
+ $Stopwatch.Start ()
479
+ $DispatcherTimer.Start ()
480
+ }
481
+
482
+ # Play a sound
483
+ If ($ ($PSBoundParameters.Sound ))
484
+ {
485
+ $SoundFile = " $env: SystemDrive \Windows\Media\$ ( $PSBoundParameters.Sound ) .wav"
486
+ $SoundPlayer = New-Object System.Media.SoundPlayer - ArgumentList $SoundFile
487
+ $SoundPlayer.Add_LoadCompleted ({
488
+ $This.Play ()
489
+ $This.Dispose ()
490
+ })
491
+ $SoundPlayer.LoadAsync ()
492
+ }
493
+
494
+ # Display the window
495
+ $null = $window.Dispatcher.InvokeAsync {$window.ShowDialog ()}.Wait()
496
+
497
+ }
498
+ }
499
+ function Run-Elevated ($scriptblock )
500
+ {
501
+ # TODO: make -NoExit a parameter
502
+ # TODO: just open PS (no -Command parameter) if $scriptblock -eq ''
503
+ $sh = new-object - com ' Shell.Application'
504
+ $sh.ShellExecute (' powershell' , " -NoExit -Command $scriptblock " , ' ' , ' runas' )
505
+ }
506
+ function Create-WPFWindow {
507
+ Param ($global :hash )
508
+
509
+ $Window = New-Object System.Windows.Window
510
+ $Window.SizeToContent = [System.Windows.SizeToContent ]::WidthAndHeight
511
+ $window.title = " GoPro Metaxtractor"
512
+ $window.WindowStartupLocation = [System.Windows.WindowStartupLocation ]::CenterScreen
513
+ $window.ResizeMode = [System.Windows.ResizeMode ]::NoResize
514
+
515
+ $IntroBlock = New-object System.Windows.Controls.TextBlock
516
+ $introblock.HorizontalAlignment = " Left"
517
+ $IntroBlock.VerticalAlignment = " Center"
518
+ $IntroBlock.TextWrapping = " Wrap"
519
+ $introblock.Height = 100
520
+ $introblock.Width = 700
521
+ $global :hash.IntroBlock = $IntroBlock
522
+
523
+ $singleFileRadio = new-object System.Windows.Controls.RadioButton
524
+ $singleFileRadio.GroupName = " selectorGroup"
525
+ $singleFileRadio.Content = " Process Single File"
526
+ $singleFileRadio.IsChecked = $True
527
+ $global :hash.singleFileRadio = $singleFileRadio
528
+
529
+ $folderRadio = new-object System.Windows.Controls.RadioButton
530
+ $folderRadio.GroupName = " selectorGroup"
531
+ $folderRadio.Content = " Process Folder of Files"
532
+ $folderRadio.IsChecked = $false
533
+ $global :hash.folderRadio = $folderRadio
534
+
535
+ $singleFileText = new-object System.Windows.Controls.TextBox
536
+ $singleFileText.Height = 20
537
+ $singleFileText.Width = 500
538
+ $singleFileText.AllowDrop = $True
539
+ $global :hash.singleFileText = $singleFileText
540
+
541
+ $folderFileText = new-object System.Windows.Controls.TextBlock
542
+ $folderFileText.Height = 20
543
+ $folderFileText.Width = 500
544
+
545
+ $global :hash.folderFileText = $folderFileText
546
+
547
+ $fileBrowseButton = new-object System.Windows.Controls.Button
548
+ $fileBrowseButton.Content = " Browse for File"
549
+ $fileBrowseButton.Height = 24
550
+ $fileBrowseButton.Width = 120
551
+ $fileBrowseButton.Margin = 5
552
+ $global :hash.fileBrowseButton = $fileBrowseButton
553
+
554
+ $statusBar = new-object System.Windows.Controls.ProgressBar
555
+ $statusBar.Height = 24
556
+ $statusBar.Value = 0
557
+ $statusBar.Width = 600
558
+ $global :hash.statusbar = $statusbar
559
+
560
+ $statusText = new-object System.Windows.Controls.TextBlock
561
+ $statusText.Height = 24
562
+ $statustext.Width = 150
563
+ $statustext.VerticalAlignment = " Center"
564
+ $statusText.HorizontalAlignment = " Left"
565
+ $global :hash.statusText = $statusText
566
+
567
+ $folderBrowseButton = new-object System.Windows.Controls.Button
568
+ $folderbrowsebutton.Content = " Browse for Folder"
569
+ $folderBrowseButton.Height = 24
570
+ $folderBrowseButton.width = 120
571
+ $folderBrowseButton.Margin = 5
572
+ $folderBrowseButton.IsEnabled = $false
573
+ $global :hash.folderBrowseButton = $folderBrowseButton
574
+
575
+ $goButton = new-object System.Windows.Controls.Button
576
+ $goButton.Height = 48
577
+ $goButton.width = 120
578
+ $goButton.Content = " Begin Processing"
579
+ $goButton.Margin = 5
580
+ $goButton.IsEnabled = $false
581
+ $global :hash.goButton = $goButton
582
+
583
+ $singleFileStack = new-object System.Windows.Controls.StackPanel
584
+ $singleFileStack.Orientation = " Horizontal"
585
+ $singleFileStack.Margin = " 5,5,5,5"
586
+ $singleFileStack.AddChild ($fileBrowseButton )
587
+ $singleFileStack.AddChild ($singleFileText )
588
+
589
+ $folderStack = new-object System.Windows.Controls.StackPanel
590
+ $folderStack.Orientation = " Horizontal"
591
+ $folderStack.Margin = " 5,5,5,5"
592
+ $folderStack.AddChild ($folderBrowseButton )
593
+ $folderStack.AddChild ($folderFileText )
594
+
595
+ $statusStack = new-object System.Windows.Controls.StackPanel
596
+ $statusStack.Orientation = " Horizontal"
597
+ $statusStack.AddChild ($statusText )
598
+ $statusStack.AddChild ($statusBar )
599
+
600
+ $aboutButton = new-object System.Windows.Controls.Button
601
+ $aboutButton.Height = 24
602
+ $aboutbutton.Content = " About"
603
+ $aboutbutton.Width = 60
604
+ $global :hash.aboutButton = $aboutButton
605
+
606
+ $introStack = new-object System.Windows.Controls.StackPanel
607
+ $introStack.Orientation = " Horizontal"
608
+ $introStack.AddChild ($introblock )
609
+ $introStack.AddChild ($aboutButton )
610
+
611
+
612
+ $vStackPanel = new-object system.windows.controls.StackPanel
613
+ $vStackPanel.Orientation = " Vertical"
614
+ $vStackPanel.Margin = " 5,5,5,5"
615
+ $vStackPanel.AddChild ($introStack )
616
+ $vStackPanel.AddChild ($singleFileRadio )
617
+ $vStackPanel.AddChild ($folderRadio )
618
+ $vStackPanel.AddChild ($singleFileStack )
619
+ $vStackPanel.AddChild ($folderStack )
620
+ $vStackPanel.AddChild ($goButton )
621
+ $vstackPanel.AddChild ($statusStack )
622
+ $window.AddChild ($vStackPanel )
623
+ $global :hash.Window = $Window
624
+
625
+
626
+ }
627
+
628
+ function Check-Dependencies {
629
+ $WarningParams = @ {
630
+ Title = " WARNING"
631
+ TitleFontSize = 20
632
+ TitleBackground = ' Orange'
633
+ TitleTextForeground = ' Black'
634
+ }
635
+ $list = @ ()
636
+
637
+
638
+
639
+ try {
640
+ choco - v
641
+ $global :choco = $true
642
+ }
643
+ catch {
644
+ $global :choco = $false
645
+ $list += " choco"
646
+ }
647
+ try {
648
+ ffmpeg
649
+ $global :ffmpeg = $true
650
+ }
651
+ catch {
652
+ $global :ffmpeg = $false
653
+ $list += " ffmpeg"
654
+ }
655
+ try {
656
+ python - V
657
+ $global :python = $true
658
+ }catch {
659
+ $global :python = $false
660
+ $list += " python"
661
+ }
662
+ try {
663
+ gopro2gpx
664
+ $global :gopro2gpx = $true
665
+ }catch {
666
+ $global :gopro2gpx = $false
667
+ $list += " gopro2gpx"
668
+ }
669
+ try {
670
+ git
671
+ $global :git = $true
672
+ }catch {
673
+ $global :git = $false
674
+ $list += " git"
675
+ }
676
+ if ($list.Count -ne 0 ){
677
+ $listdisplay = $list | out-string
678
+ New-WPFMessageBox @WarningParams - Content " Looks like we're missing the following packages on this system: $listdisplay
679
+
680
+ Would you like me to install these for you?" - ButtonType Yes- No
681
+ if ($WPFMessageBoxOutput -eq " Yes" ){
682
+ Install-Dependencies
683
+ }
684
+ }else {
685
+ New-WPFMessageBox - Title " We're good!" - Content " Just go ahead and run this app again!" - ButtonType ' OK' - Timeout 10
686
+ echo " Made ya look!" > ./ GoPro- Metaxtractor.ini
687
+ Exit
688
+ }
689
+
690
+ }
691
+
692
+ Function Install-Dependencies {
693
+
694
+ $chocoblock = @"
695
+ Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
696
+ exit
697
+ "@
698
+
699
+ $pythonblock = @"
700
+ winget install --id Python.Python.3.12 --accept-package-agreements --accept-source-agreements
701
+ exit
702
+ "@
703
+ $ffmpegblock = @"
704
+ choco install ffmpeg -y
705
+ exit
706
+ "@
707
+ $gopro2gpxblock = @"
708
+ pip3 install git+https://github.com/juanmcasillas/gopro2gpx
709
+ exit
710
+ "@
711
+ $gitblock = @"
712
+ choco install git -y
713
+ exit
714
+ "@
715
+
716
+ $installerparams = @ {
717
+ Title = " Installing... this might take a few..."
718
+ ContentBackground = " WhiteSmoke"
719
+ ButtonType = ' None'
720
+ Timeout = 60
721
+ }
722
+
723
+ New-WPFMessageBox @installerparams - content " It may look like it's doing nothing. Please don't close or interact with any windows until installs are completed" - ButtonType OK - Timeout 10
724
+ try {
725
+ if (! $global :choco ){
726
+ $code = {
727
+ Run- Elevated $chocoblock
728
+ }
729
+ New-WPFMessageBox - Title " Installing... This might take a few.." - ContentBackground " WhiteSmoke" - ButtonType " None" - Timeout 60 - content " Installing Choco.." - OnLoaded $code
730
+ }
731
+ if (! $global :python ){
732
+ $code = {
733
+ Run- Elevated $pythonblock
734
+ }
735
+ New-WPFMessageBox - Title " Installing... This might take a few.." - ContentBackground " WhiteSmoke" - ButtonType " None" - Timeout 60 - Content " Installing Python.." - OnLoaded $code
736
+ }
737
+ if (! $global :ffmpeg ){
738
+ $code = {
739
+ Run- Elevated $ffmpegblock
740
+ }
741
+ New-WPFMessageBox - Title " Installing... This might take a few.." - ContentBackground " WhiteSmoke" - ButtonType ' None' - Timeout 15 - Content " Installing ffmpeg" - OnLoaded $code
742
+ }
743
+ if (! $global :git ){
744
+ $code = {
745
+ Run- Elevated $gitblock
746
+ }
747
+ New-WPFMessageBox - Content " Installing git" - Title " Installing... This might take a few.." - ContentBackground " WhiteSmoke" - ButtonType ' None' - Timeout 20 - OnLoaded $code
748
+ }
749
+ if (! $global :gopro2gpx ){
750
+ $code = {
751
+ Run- Elevated $gopro2gpxblock
752
+ }
753
+ New-WPFMessageBox - Title " Installing... This might take a few.." - ContentBackground " WhiteSmoke" - ButtonType " None" - Timeout 5 - Content " Installing GoPro2GPX" - OnLoaded $code
754
+ }
755
+ echo " Made ya look!" > ./ GoPro- Metaxtractor.ini
756
+ $InfoParams = @ {
757
+ Title = " SUCCESS!"
758
+ TitleFontSize = 20
759
+ TitleBackground = ' LightSkyBlue'
760
+ TitleTextForeground = ' Black'
761
+ }
762
+ New-WPFMessageBox @InfoParams - Content " Looks like everything is already installed or installed ok. The app will now exit, please run it again."
763
+ Exit
764
+ }catch {
765
+ write-host $Error
766
+ $ErrorParams = @ {
767
+ FontFamily = ' Verdana'
768
+ Title = " :("
769
+ TitleFontSize = 80
770
+ TitleTextForeground = ' White'
771
+ TitleBackground = ' SteelBlue'
772
+ ButtonType = ' OK'
773
+ ContentFontSize = 16
774
+ ContentTextForeground = ' White'
775
+ ContentBackground = ' SteelBlue'
776
+ ButtonTextForeground = ' White'
777
+ BorderThickness = 0
778
+ }
779
+ New-WPFMessageBox @ErrorParams - Content " Something went pearshaped during install.. You're going to have to troubleshoot this yourself. I'm just a guy. Sorry...
780
+
781
+ 0x80042069 -- Not a real error code."
782
+ exit
783
+ }
784
+ Function global :Update-Status ($percent , $status ){
785
+ $global :hash.statusbar.Value = $percent
786
+ $global :hash.statusText.Text = $status
787
+ }
788
+ }
789
+ $DependencyParams = @ {
790
+ Content = " Looks like this is your first run or you're missing critical dependencies. I can install them for you if you like or you can refer to my github page for the list of dependencies if you'd rather install them yourself. Install them now?"
791
+ Title = " First Run Setup"
792
+ TitleFontSize = 20
793
+ TitleBackground = ' Green'
794
+ TitleTextForeground = ' White'
795
+ ButtonType = ' yes-no'
796
+
797
+ }
798
+ $DoneParams = @ {
799
+ Content = " Job has completed"
800
+ Title = " Glorious Success!"
801
+ TitleFontSize = 20
802
+ TitleBackground = ' Green'
803
+ TitleTextForeground = ' White'
804
+ ButtonType = ' Ok'
805
+
806
+ }
807
+
808
+ if (! (Test-Path - path ./ GoPro- Metaxtractor.ini)){
809
+ New-WPFMessageBox @DependencyParams
810
+ if ($WPFMessageBoxOutput -eq " Yes" ){
811
+ Check- Dependencies
812
+ }else {Exit }
813
+ }
814
+ $global :hash = @ {}
815
+ Create- WPFWindow $global :hash
816
+
817
+ $global :hash.IntroBlock.Text = " Welcome to GoPro Metaxtractor. This will extract GPX files from GoPro camera video files. It's kinda garbage right now but hopefully someone will find it useful as the current alternatives are some paid stuff on that website or get dirty with python, java or some other shizz. Iono. Do what you want, I'm not the police. Enjoy!!"
818
+ $global :hash.statustext.Text = " Ready.."
819
+ $global :hash.statusbar.Value = 0
820
+ $global :hash.singleFileRadio.Add_Click {
821
+ $global :hash.folderBrowseButton.IsEnabled = $false ;
822
+ $global :hash.singleFileText.IsEnabled = $true ;
823
+ $global :hash.fileBrowseButton.IsEnabled = $true ;
824
+ $global :singlemode = $true
825
+ }
826
+ $global :hash.folderRadio.Add_Click {
827
+ $global :hash.singleFileText.IsEnabled = $false ;
828
+ $global :singlemode = $false
829
+ $global :hash.fileBrowseButton.IsEnabled = $false ;
830
+ $global :hash.folderBrowseButton.IsEnabled = $true ;
831
+ }
832
+
833
+ $global :hash.fileBrowseButton.Add_Click {
834
+ Add-Type - AssemblyName System.Windows.Forms
835
+ $FileBrowser = New-Object System.Windows.Forms.OpenFileDialog - Property @ {
836
+ InitialDirectory = [Environment ]::GetFolderPath(' Desktop' )
837
+ Filter = ' GoPro MP4 (*.mp4)|*.mp4|GoPro MOV (*.mov)|*.mov'
838
+ }
839
+ $null = $FileBrowser.ShowDialog ()
840
+ if ($filebrowser.filename -ne " " ){
841
+ $global :hash.singleFileText.Text = $filebrowser.FileName
842
+ $global :singlevid = $filebrowser.FileName
843
+ $global :hash.goButton.IsEnabled = $true
844
+ }
845
+
846
+
847
+ }
848
+ $global :hash.aboutButton.Add_Click {
849
+ New-WPFMessageBox - title " About" - Content " GoPro Metaxtractor v0.1.0b
850
+ Created by Joe Hancuff
851
+ Look, I know this is a heaping pile of shit but I wanted to take a stab at making something relatively EASY for Windows users that they didn't have to manually install a bunch of libraries or other apps to get this hot buttery garbage working. I'm working on it, but I can't gaurantee that I can devote a bunch of time to this. If it worked for you, awesome. If not, well it was free, don't bitch at me too loudly about it.
852
+
853
+ So yeah... Watch this space I guess, I dunno. ¯\_(ツ)_/¯" - TitleFontSize 30 - TitleBackground ' Navy' - TitleTextForeground ' White' - CornerRadius 80 - ShadowDepth 10 - BlurRadius 10 - BorderThickness 1 - BorderBrush ' Black'
854
+ }
855
+ $global :hash.goButton.Add_Click {
856
+
857
+ $global :hash.statusbar.Value = 0
858
+ $global :hash.statusText.Text = " Processing..."
859
+ if ($global :singlemode ){
860
+ $vidfile = $global :singlevid
861
+ $vidfilePath = $vidfile.substring (0 , $vidfile.length - 12 )
862
+ $noext = $vidfile.Substring (0 , $vidfile.Length - 4 )
863
+ $binfile = $noext + " .bin"
864
+ $lonefile = $noext.Substring ($vidfile.Length - 12 )
865
+ ffmpeg - y - i " $vidfile " - codec copy - map 0 :3 -f rawvideo " $binfile "
866
+ gopro2gpx - s - b " $binfile " $lonefile
867
+ $gpx = $lonefile + " .gpx"
868
+ mv " ./$gpx " " $vidfilepath "
869
+ rm $binfile
870
+ $global :hash.statusbar.Value = 100
871
+ $global :hash.statusText.Text = " Done!"
872
+ New-WPFMessageBox - Content " Job has completed" - Title " Glorious Success!" - Titlefontsize 20 - TitleBackground ' Green' - TitleTextForeground ' White' - ButtonType ' Ok'
873
+ }
874
+ else {
875
+ $path = $global :filepath
876
+ $vidfiles = get-childitem " $path " - Filter * .MP4
877
+ $numfiles = $vidfiles.Length
878
+ $donefiles = 0
879
+ foreach ($vidfile in $vidfiles.fullname ){
880
+ $noext = $vidfile.Substring (0 , $vidfile.Length - 4 )
881
+ $binfile = $noext + " .bin"
882
+ $lonefile = $noext.Substring ($vidfile.Length - 12 )
883
+ ffmpeg - y - i " $vidfile " - codec copy - map 0 :3 -f rawvideo " $binfile "
884
+ start-sleep - seconds 1
885
+ gopro2gpx - s - b " $binfile " $lonefile
886
+ $gpx = $lonefile + " .gpx"
887
+ mv " ./$gpx " " $path "
888
+ rm $binfile
889
+ $donefiles ++
890
+ $global :hash.statusbar.Value = $ (($donefiles / $numfiles )* 100 )
891
+ }
892
+ $global :hash.statusbar.Value = 100
893
+ $global :hash.statusText.Text = " Done!"
894
+ New-WPFMessageBox - Content " Job has completed" - Title " Glorious Success!" - Titlefontsize 20 - TitleBackground ' Green' - TitleTextForeground ' White' - ButtonType ' Ok'
895
+
896
+ }
897
+
898
+ }
899
+
900
+ $global :hash.folderBrowseButton.Add_Click {
901
+ Add-Type - AssemblyName system.windows.Forms
902
+ $folder = new-object System.Windows.Forms.FolderBrowserDialog
903
+ $null = $folder.ShowDialog ()
904
+ if ($folder.SelectedPath -ne " " ){
905
+ $global :hash.folderFileText.Text = $folder.SelectedPath
906
+ $global :filepath = $folder.SelectedPath
907
+ $global :hash.goButton.IsEnabled = $true
908
+ }
909
+ }
910
+ [void ]$global :hash.Window.Dispatcher.InvokeAsync {$global :hash.Window.ShowDialog ()}.Wait()
0 commit comments