gui-for-ffmpeg/kernel/progressbar.go
Leonid Nikitin 84b36dd29e
Make it possible to drag and drop multiple files
It is now possible to add multiple files before sending them to the processing queue.
2025-05-25 01:25:40 +05:00

255 lines
6.1 KiB
Go

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
}