-
-
Notifications
You must be signed in to change notification settings - Fork 246
/
Copy pathWatcher.vb
317 lines (218 loc) · 11.3 KB
/
Watcher.vb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
Imports System.Collections.ObjectModel
Imports System.Text.Json
Imports Microsoft.Toolkit.Mvvm.ComponentModel
Imports CompactGUI.Core
Imports System.Threading
Imports System.Collections.Specialized
Imports System.Runtime
Imports System.ComponentModel
<PropertyChanged.AddINotifyPropertyChangedInterface>
Public Class Watcher : Inherits ObservableObject
<PropertyChanged.AlsoNotifyFor(NameOf(TotalSaved))>
Public Property FolderMonitors As New List(Of FolderMonitor)
<PropertyChanged.AlsoNotifyFor(NameOf(TotalSaved))>
Public Property WatchedFolders As New ObservableCollection(Of WatchedFolder)
<PropertyChanged.AlsoNotifyFor(NameOf(TotalSaved))>
Public Property LastAnalysed As DateTime
Public Shared Property IsWatchingEnabled As Boolean = True
Public Shared Property IsBackgroundCompactingEnabled As Boolean = True
Private ReadOnly _DataFolder As New IO.DirectoryInfo(IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "IridiumIO", "CompactGUI"))
Private ReadOnly Property WatcherJSONFile As IO.FileInfo = New IO.FileInfo(IO.Path.Combine(_DataFolder.FullName, "watcher.json"))
Public Property BGCompactor As BackgroundCompactor
Private ReadOnly _parseWatchersSemaphore As New SemaphoreSlim(1, 1)
Private Const LAST_SYSTEM_MODIFIED_TIME_THRESHOLD As Integer = 180 ' 3 minutes
Private _disableCounter As Integer = 0
Private _counterLock As New SemaphoreSlim(1, 1)
Public Async Function DisableBackgrounding() As Task
Await _counterLock.WaitAsync()
Try
_disableCounter += 1
If _disableCounter = 1 Then
Debug.WriteLine("Backgrounding disabled!")
IdleDetector.Paused = True
Await _parseWatchersSemaphore.WaitAsync()
End If
Finally
_counterLock.Release()
End Try
End Function
Public Async Function EnableBackgrounding() As Task
Await _counterLock.WaitAsync()
Try
If _disableCounter > 0 Then
_disableCounter -= 1
If _disableCounter = 0 Then
_parseWatchersSemaphore.Release()
IdleDetector.Paused = False
Debug.WriteLine("Backgrounding enabled!")
End If
End If
Finally
_counterLock.Release()
End Try
End Function
Sub New(excludedFiletypes As String())
IdleDetector.Start()
AddHandler IdleDetector.IsIdle, AddressOf OnSystemIdle
BGCompactor = New BackgroundCompactor(excludedFiletypes)
InitializeWatchedFoldersAsync()
End Sub
Private Async Function InitializeWatchedFoldersAsync() As Task
Dim initialWatchedFolders = Await GetWatchedFoldersFromJson()
If initialWatchedFolders Is Nothing Then Return
WatchedFolders.Clear()
For Each folder In initialWatchedFolders.Where(Function(f) IO.Directory.Exists(f.Folder))
WatchedFolders.Add(folder)
Next
FolderMonitors.AddRange(WatchedFolders.Select(Function(w) New FolderMonitor(w.Folder, w.DisplayName) With {.LastChangedDate = w.LastSystemModifiedDate}))
UpdateRegistryBasedOnWatchedFolders()
End Function
Private Sub UpdateRegistryBasedOnWatchedFolders()
Dim registryKey As Microsoft.Win32.RegistryKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey("Software\Microsoft\Windows\CurrentVersion\Run", True)
If WatchedFolders.Count > 0 Then
registryKey.SetValue("CompactGUI", Environment.ProcessPath & " -tray")
Else
registryKey.DeleteValue("CompactGUI", False)
End If
End Sub
Public Sub AddOrUpdateWatched(item As WatchedFolder, Optional immediateFlushToDisk As Boolean = True)
Dim existingItem = WatchedFolders.FirstOrDefault(Function(f) f.Folder = item.Folder)
If existingItem Is Nothing Then
WatchedFolders.Add(item)
FolderMonitors.Add(New FolderMonitor(item.Folder, item.DisplayName) With {.LastChangedDate = item.LastSystemModifiedDate})
Else
UpdateFolderProperties(existingItem, item)
End If
OnPropertyChanged(NameOf(TotalSaved))
If immediateFlushToDisk Then WriteToFile()
End Sub
Public Sub UpdateWatched(folder As String, ByRef analyser As Analyser, Optional immediateFlushToDisk As Boolean = True)
Dim existingItem = WatchedFolders.FirstOrDefault(Function(f) f.Folder = folder)
If existingItem IsNot Nothing Then
existingItem.LastCheckedDate = DateTime.Now
existingItem.LastCheckedSize = analyser.CompressedBytes
existingItem.LastSystemModifiedDate = FolderMonitors.First(Function(f) f.Folder = folder).LastChangedDate
existingItem.CompressionLevel = analyser.FileCompressionDetailsList.Select(Function(f) f.CompressionMode).Max
FolderMonitors.First(Function(f) f.Folder = folder).HasTargetChanged = False
OnPropertyChanged(NameOf(TotalSaved))
If Not immediateFlushToDisk Then WriteToFile()
End If
End Sub
Private Sub UpdateFolderProperties(existingItem As WatchedFolder, newItem As WatchedFolder)
With existingItem
.Folder = newItem.Folder
.DisplayName = newItem.DisplayName
.IsSteamGame = newItem.IsSteamGame
.LastCompressedSize = newItem.LastCompressedSize
.LastUncompressedSize = newItem.LastUncompressedSize
.LastCompressedDate = DateTime.Now
.LastCheckedDate = DateTime.Now
.LastCheckedSize = newItem.LastCheckedSize
.LastSystemModifiedDate = DateTime.Now
.CompressionLevel = newItem.CompressionLevel
End With
FolderMonitors.First(Function(f) f.Folder = newItem.Folder).HasTargetChanged = False
End Sub
Public Sub RemoveWatched(item As WatchedFolder)
Dim x = FolderMonitors.Find(Function(f) f.Folder = item.Folder)
If x IsNot Nothing Then
x.Dispose()
FolderMonitors.Remove(x)
End If
WatchedFolders.Remove(item)
WriteToFile()
End Sub
Private Async Function GetWatchedFoldersFromJson() As Task(Of ObservableCollection(Of WatchedFolder))
If Not _DataFolder.Exists Then _DataFolder.Create()
If Not WatcherJSONFile.Exists Then Await WatcherJSONFile.Create().DisposeAsync()
Dim ret = DeserializeAndValidateJSON(WatcherJSONFile)
LastAnalysed = ret.Item1
Dim _WatchedFolders = ret.Item2
Return _WatchedFolders
End Function
Private Shared ReadOnly DeserializeOptions As New JsonSerializerOptions With {.IncludeFields = True}
Private Shared ReadOnly SerializeOptions As New JsonSerializerOptions With {.IncludeFields = True, .WriteIndented = True}
Private Shared Function DeserializeAndValidateJSON(inputjsonFile As IO.FileInfo) As (DateTime, ObservableCollection(Of WatchedFolder))
Dim WatcherJSON = IO.File.ReadAllText(inputjsonFile.FullName)
If WatcherJSON = "" Then WatcherJSON = "{}"
Dim validatedResult As (DateTime, ObservableCollection(Of WatchedFolder))
Try
validatedResult = JsonSerializer.Deserialize(Of (DateTime, ObservableCollection(Of WatchedFolder)))(WatcherJSON, DeserializeOptions)
Catch ex As Exception
validatedResult = (DateTime.Now, Nothing)
End Try
Return validatedResult
End Function
Public Sub WriteToFile()
Dim output = JsonSerializer.Serialize((LastAnalysed, WatchedFolders), SerializeOptions)
IO.File.WriteAllText(WatcherJSONFile.FullName, output)
End Sub
Private Async Sub OnSystemIdle()
If Not IsWatchingEnabled Then Return
Dim recentThresholdDate As DateTime = DateTime.Now.AddSeconds(-LAST_SYSTEM_MODIFIED_TIME_THRESHOLD)
If FolderMonitors.Exists(Function(x) x.LastChangedDate > recentThresholdDate) Then Return
If _parseWatchersSemaphore.CurrentCount <> 0 Then
Await ParseWatchers()
End If
If _parseWatchersSemaphore.CurrentCount <> 0 AndAlso IsBackgroundCompactingEnabled Then
Await BackgroundCompact()
End If
End Sub
Public Async Function ParseWatchers(Optional ParseAll As Boolean = False) As Task
Dim acquired = Await _parseWatchersSemaphore.WaitAsync(0)
If Not acquired Then Return
Try
Dim WatchersToCheck = If(ParseAll, FolderMonitors, FolderMonitors.Where(Function(w) w.HasTargetChanged))
If Not WatchersToCheck.Any() Then Return
For Each fsWatcher In WatchersToCheck.OrderBy(Function(f) f.DisplayName)
Await Analyse(fsWatcher.Folder, ParseAll)
Next
LastAnalysed = DateTime.Now
If WatchersToCheck.Any() Then WriteToFile()
Finally
_parseWatchersSemaphore.Release()
End Try
End Function
Public Async Function BackgroundCompact() As Task
Dim acquired = Await _parseWatchersSemaphore.WaitAsync(0)
If Not acquired Then Return
Try
If BGCompactor.isCompactorActive Then Return
If Not WatchedFolders.Any(Function(f) f.DecayPercentage <> 0 AndAlso f.CompressionLevel <> Core.CompressionAlgorithm.NO_COMPRESSION) Then
Return
End If
Await BGCompactor.StartCompactingAsync(WatchedFolders, FolderMonitors)
OnPropertyChanged(NameOf(TotalSaved))
Finally
_parseWatchersSemaphore.Release()
End Try
End Function
Public Async Function Analyse(folder As String, checkDiskModified As Boolean) As Task(Of Boolean)
Debug.WriteLine("Background Analysing: " & folder)
Dim analyser As New Core.Analyser(folder)
Await analyser.AnalyseFolder(Nothing)
Dim watched = WatchedFolders.First(Function(f) f.Folder = folder)
watched.IsWorking = True
watched.LastCheckedDate = DateTime.Now
watched.LastCheckedSize = analyser.CompressedBytes
watched.LastUncompressedSize = analyser.UncompressedBytes
watched.LastSystemModifiedDate = FolderMonitors.First(Function(f) f.Folder = folder).LastChangedDate
Dim mainCompressionLVL = analyser.FileCompressionDetailsList.Select(Function(f) f.CompressionMode).Max
watched.CompressionLevel = mainCompressionLVL
If checkDiskModified Then
Dim lastDiskWriteTime = analyser.FileCompressionDetailsList.Select(Function(fl)
Dim finfo As New IO.FileInfo(fl.FileName)
Return finfo.LastWriteTime
End Function).OrderByDescending(Function(f) f).First
watched.LastSystemModifiedDate = If(watched.LastSystemModifiedDate < lastDiskWriteTime, lastDiskWriteTime, watched.LastSystemModifiedDate)
End If
FolderMonitors.First(Function(f) f.Folder = folder).HasTargetChanged = False
watched.IsWorking = False
Return True
End Function
Public ReadOnly Property TotalSaved As Long
Get
Return WatchedFolders.Sum(Function(f) f.LastUncompressedSize - f.LastCheckedSize)
End Get
End Property
End Class