@@ -86,6 +86,7 @@ module App =
8686 | Search of bool
8787 | SearchResult of VideoSearchResult
8888 | SearchProgress of BatchProgress
89+ | ProgressChanged of float
8990 | SearchCompleted
9091
9192 | AttachedToVisualTreeChanged of VisualTreeAttachmentEventArgs
@@ -169,10 +170,12 @@ module App =
169170 fun dispatch ->
170171 async {
171172 let command = mapToSearchCommand model
172- CommandValidator.PrevalidateSearchCommand command
173173 let cacheFolder = Folder.GetPath Folders.cache
174174 let dataStore = JsonFileDataStore cacheFolder
175175 let youtube = Youtube( dataStore, VideoIndexRepository cacheFolder)
176+ let dispatchProgress = Cmd.debounce 100 ( fun progress -> SearchProgress progress)
177+ command.SetProgressReporter( Progress< BatchProgress>( fun progress -> dispatchProgress progress |> List.iter ( fun effect -> effect dispatch)))
178+ CommandValidator.PrevalidateSearchCommand command
176179 use cts = new CancellationTokenSource()
177180 do ! CommandValidator.ValidateScopesAsync( command, youtube, dataStore, cts.Token) |> Async.AwaitTask
178181
@@ -308,7 +311,6 @@ module App =
308311
309312 | Search on -> { model with Searching = on; SearchResults = [] }, ( if on then searchCmd model else Cmd.none)
310313 | SearchResult result -> { model with SearchResults = result:: model.SearchResults }, Cmd.none
311- | SearchCompleted -> { model with Searching = false }, Notify " search completed" |> Cmd.ofMsg
312314
313315 | SearchProgress progress ->
314316 let scopes = model.Scopes |> List.map( fun s ->
@@ -320,15 +322,20 @@ module App =
320322 | _ -> failwith " quark"
321323
322324 let scopeProgress = progress.VideoLists |> Seq.tryFind ( fun pair -> equals s pair.Key) |> Option.map ( fun pair -> pair.Value)
323- if scopeProgress.IsSome then { s with Progress = scopeProgress } else s )
325+ if scopeProgress.IsSome
326+ then { s with Progress = scopeProgress }
327+ else s )
324328
325329 { model with Scopes = scopes }, Cmd.none
326330
331+ | ProgressChanged _ value -> model, Cmd.none
332+ | SearchCompleted -> { model with Searching = false }, notifyInfo model.Notifier " search completed"
333+
327334 | AttachedToVisualTreeChanged args ->
328335 let notifier = FabApplication.Current.WindowNotificationManager
329336 notifier.Position <- NotificationPosition.BottomRight
330337 { model with Notifier = notifier }, Cmd.none
331-
338+
332339 | Notify title -> model, notifyInfo model.Notifier title
333340 | OpenUrl url -> model, ( fun _ -> ShellCommands.OpenUri( url); Cmd.none)()
334341 | CopyingToClipboard _ args -> model, Cmd.none
@@ -461,37 +468,45 @@ module App =
461468 Label " in"
462469
463470 for scope in model.Scopes do
464- Label( displayScope scope.Type)
465-
466- TextBox( scope.Aliases, fun value -> AliasesUpdated( scope, value))
467- .watermark( " by " + ( if scope.Type = Scopes.videos then " space-separated IDs or URLs"
468- elif scope.Type = Scopes.playlist then " ID or URL"
469- else " handle, slug, user name, ID or URL" ))
470-
471- ( HStack( 5 ) {
472- Label " search top"
473- NumericUpDown( 0 , float UInt16.MaxValue, scope.Top, fun value -> TopChanged( scope, value))
474- .formatString( " F0" )
475- .tip( ToolTip( " number of videos to search" ))
476- Label " videos"
477- }) .centerHorizontal() .isVisible( scope.DisplaysSettings)
478-
479- ( HStack( 5 ) {
480- Label " and look for new ones after"
481- NumericUpDown( 0 , float UInt16.MaxValue, scope.CacheHours, fun value -> CacheHoursChanged( scope, value))
482- .formatString( " F0" )
483- .tip( ToolTip( " The info about which videos are in a playlist or channel is cached locally to speed up future searches."
484- + " This controls after how many hours such a cache is considered stale."
485- + Environment.NewLine + Environment.NewLine
486- + " Note that this doesn't concern the video data caches,"
487- + " which are not expected to change often and are stored until you explicitly clear them." ))
488- Label " hours"
489- }) .centerHorizontal() .isVisible( scope.DisplaysSettings)
490-
491- ToggleButton( " ⚙" , scope.DisplaysSettings, fun display -> DisplaySettingsChanged( scope, display))
492- .tip( ToolTip( " display settings" ))
493-
494- Button( " ❌" , RemoveScope scope) .tip( ToolTip( " remove this scope" ))
471+ VStack( 5 ){
472+ HStack( 5 ){
473+ Label( displayScope scope.Type)
474+
475+ TextBox( scope.Aliases, fun value -> AliasesUpdated( scope, value))
476+ .watermark( " by " + ( if scope.Type = Scopes.videos then " space-separated IDs or URLs"
477+ elif scope.Type = Scopes.playlist then " ID or URL"
478+ else " handle, slug, user name, ID or URL" ))
479+
480+ ( HStack( 5 ) {
481+ Label " search top"
482+ NumericUpDown( 0 , float UInt16.MaxValue, scope.Top, fun value -> TopChanged( scope, value))
483+ .formatString( " F0" )
484+ .tip( ToolTip( " number of videos to search" ))
485+ Label " videos"
486+ }) .centerHorizontal() .isVisible( scope.DisplaysSettings)
487+
488+ ( HStack( 5 ) {
489+ Label " and look for new ones after"
490+ NumericUpDown( 0 , float UInt16.MaxValue, scope.CacheHours, fun value -> CacheHoursChanged( scope, value))
491+ .formatString( " F0" )
492+ .tip( ToolTip( " The info about which videos are in a playlist or channel is cached locally to speed up future searches."
493+ + " This controls after how many hours such a cache is considered stale."
494+ + Environment.NewLine + Environment.NewLine
495+ + " Note that this doesn't concern the video data caches,"
496+ + " which are not expected to change often and are stored until you explicitly clear them." ))
497+ Label " hours"
498+ }) .centerHorizontal() .isVisible( scope.DisplaysSettings)
499+
500+ ToggleButton( " ⚙" , scope.DisplaysSettings, fun display -> DisplaySettingsChanged( scope, display))
501+ .tip( ToolTip( " display settings" ))
502+
503+ Button( " ❌" , RemoveScope scope) .tip( ToolTip( " remove this scope" ))
504+ }
505+
506+ if scope.Progress.IsSome then
507+ ProgressBar( 0 , scope.Progress.Value.AllJobs, scope.Progress.Value.CompletedJobs, ProgressChanged)
508+ .progressTextFormat( scope.Progress.Value.ToString()) .showProgressText( true )
509+ }
495510 }
496511
497512 HStack( 5 ){
0 commit comments