package kernel import ( "bufio" "errors" "fyne.io/fyne/v2" "fyne.io/fyne/v2/canvas" "fyne.io/fyne/v2/container" "fyne.io/fyne/v2/theme" "fyne.io/fyne/v2/widget" "github.com/nicksnyder/go-i18n/v2/i18n" "image/color" "io" "regexp" "strconv" "strings" ) type BlockProgressbarContract interface { GetContainer() *fyne.Container GetProgressbar(totalDuration float64, filePath string, localizerService LocalizerContract) Progress ProcessEndedWithError(errorText string) ProcessEndedWithSuccess(filePath string) } type BlockProgressbar struct { container *fyne.Container label *widget.Label progressbar *widget.ProgressBar errorBlock *container.Scroll messageError *canvas.Text statusMessage *canvas.Text buttonPlay *widget.Button statusesText *BlockProgressbarStatusesText ffplayService FFplayContract } func NewBlockProgressbar(statusesText *BlockProgressbarStatusesText, ffplayService FFplayContract) *BlockProgressbar { label := widget.NewLabel("") progressbar := widget.NewProgressBar() statusMessage := canvas.NewText("", theme.Color(theme.ColorNamePrimary)) messageError := canvas.NewText("", theme.Color(theme.ColorNameError)) buttonPlay := widget.NewButtonWithIcon("", theme.Icon(theme.IconNameMediaPlay), func() { }) buttonPlay.Hide() errorBlock := container.NewHScroll(messageError) errorBlock.Hide() content := container.NewVBox( container.NewHScroll(label), progressbar, container.NewHScroll(container.NewHBox( buttonPlay, statusMessage, )), errorBlock, ) content.Hide() return &BlockProgressbar{ container: content, label: label, progressbar: progressbar, errorBlock: errorBlock, messageError: messageError, statusMessage: statusMessage, buttonPlay: buttonPlay, statusesText: statusesText, ffplayService: ffplayService, } } func (block BlockProgressbar) GetContainer() *fyne.Container { return block.container } func (block BlockProgressbar) GetProgressbar(totalDuration float64, filePath string, localizerService LocalizerContract) Progress { block.label.Text = filePath block.statusMessage.Color = theme.Color(theme.ColorNamePrimary) block.statusMessage.Text = block.statusesText.inProgress block.messageError.Text = "" fyne.Do(func() { block.buttonPlay.Hide() if block.errorBlock.Visible() { block.errorBlock.Hide() } block.statusMessage.Refresh() block.container.Refresh() block.errorBlock.Refresh() }) block.progressbar.Value = 0 return NewProgress(totalDuration, block.progressbar, localizerService) } func (block BlockProgressbar) ProcessEndedWithError(errorText string) { fyne.Do(func() { block.statusMessage.Color = theme.Color(theme.ColorNameError) block.statusMessage.Text = block.statusesText.error block.messageError.Text = errorText block.errorBlock.Show() }) } func (block BlockProgressbar) ProcessEndedWithSuccess(filePath string) { fyne.Do(func() { block.statusMessage.Color = color.RGBA{R: 49, G: 127, B: 114, A: 255} block.statusMessage.Text = block.statusesText.completed block.buttonPlay.Show() block.buttonPlay.OnTapped = func() { block.buttonPlay.Disable() go func() { _ = block.ffplayService.Run(FFplaySetting{ PathToFile: filePath, }) fyne.Do(func() { block.buttonPlay.Enable() }) }() } }) } type Progress struct { totalDuration float64 progressbar *widget.ProgressBar protocol string localizerService LocalizerContract } func NewProgress(totalDuration float64, progressbar *widget.ProgressBar, localizerService LocalizerContract) Progress { return Progress{ totalDuration: totalDuration, progressbar: progressbar, protocol: "pipe:", localizerService: localizerService, } } func (p Progress) GetProtocole() string { return p.protocol } func (p Progress) Run(stdOut io.ReadCloser, stdErr io.ReadCloser) error { isProcessCompleted := false var errorText string p.progressbar.Value = 0 p.progressbar.Max = p.totalDuration fyne.Do(func() { p.progressbar.Refresh() }) progress := 0.0 go func() { scannerErr := bufio.NewReader(stdErr) for { line, _, err := scannerErr.ReadLine() if err != nil { if err == io.EOF { break } continue } data := strings.TrimSpace(string(line)) errorText = data } }() scannerOut := bufio.NewReader(stdOut) for { line, _, err := scannerOut.ReadLine() if err != nil { if err == io.EOF { break } continue } data := strings.TrimSpace(string(line)) if strings.Contains(data, "progress=end") { p.progressbar.Value = p.totalDuration fyne.Do(func() { p.progressbar.Refresh() }) isProcessCompleted = true break } re := regexp.MustCompile(`frame=(\d+)`) a := re.FindAllStringSubmatch(data, -1) if len(a) > 0 && len(a[len(a)-1]) > 0 { c, err := strconv.Atoi(a[len(a)-1][len(a[len(a)-1])-1]) if err != nil { continue } progress = float64(c) } if p.progressbar.Value != progress { p.progressbar.Value = progress fyne.Do(func() { p.progressbar.Refresh() }) } } if isProcessCompleted == false { if len(errorText) == 0 { errorText = p.localizerService.GetMessage(&i18n.LocalizeConfig{ MessageID: "errorConverter", }) } return errors.New(errorText) } return nil } type BlockProgressbarStatusesText struct { inProgress string completed string error string } func GetBlockProgressbarStatusesText(localizerService LocalizerContract) *BlockProgressbarStatusesText { statusesText := &BlockProgressbarStatusesText{ inProgress: localizerService.GetMessage(&i18n.LocalizeConfig{ MessageID: "inProgressQueue", }), completed: localizerService.GetMessage(&i18n.LocalizeConfig{ MessageID: "completedQueue", }), error: localizerService.GetMessage(&i18n.LocalizeConfig{ MessageID: "errorQueue", }), } localizerService.AddChangeCallback("inProgressQueue", func(text string) { statusesText.inProgress = text }) localizerService.AddChangeCallback("completedQueue", func(text string) { statusesText.completed = text }) localizerService.AddChangeCallback("errorQueue", func(text string) { statusesText.error = text }) return statusesText }