Implemented progress bar integration with `ProgressBarContract` for real-time conversion tracking and status updates. Added queue management functionality to process files sequentially with error and completion handling. Extended `ConvertorContract` and `FFmpegContract` to support tracking of running processes and conversion progress.
218 lines
4.8 KiB
Go
218 lines
4.8 KiB
Go
package convertor
|
|
|
|
import (
|
|
"bufio"
|
|
"errors"
|
|
"fyne.io/fyne/v2"
|
|
"fyne.io/fyne/v2/canvas"
|
|
"fyne.io/fyne/v2/container"
|
|
"fyne.io/fyne/v2/lang"
|
|
"fyne.io/fyne/v2/theme"
|
|
"fyne.io/fyne/v2/widget"
|
|
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg"
|
|
"image/color"
|
|
"io"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
type ProgressBarContract interface {
|
|
GetContainer() *fyne.Container
|
|
GetProgressbar(totalDuration float64, filePath string) ffmpeg.ProgressContract
|
|
ProcessEndedWithError(errorText string)
|
|
ProcessEndedWithSuccess(file *ffmpeg.File)
|
|
}
|
|
|
|
type progressBar struct {
|
|
container *fyne.Container
|
|
label *widget.Label
|
|
progressbar *widget.ProgressBar
|
|
errorBlock *container.Scroll
|
|
messageError *canvas.Text
|
|
statusMessage *canvas.Text
|
|
buttonPlay *widget.Button
|
|
ffmpegService ffmpeg.UtilitiesContract
|
|
}
|
|
|
|
func NewProgressBar(ffmpegService ffmpeg.UtilitiesContract) ProgressBarContract {
|
|
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 &progressBar{
|
|
container: content,
|
|
label: label,
|
|
progressbar: progressbar,
|
|
errorBlock: errorBlock,
|
|
messageError: messageError,
|
|
statusMessage: statusMessage,
|
|
buttonPlay: buttonPlay,
|
|
ffmpegService: ffmpegService,
|
|
}
|
|
}
|
|
|
|
func (p *progressBar) GetContainer() *fyne.Container {
|
|
return p.container
|
|
}
|
|
|
|
func (p *progressBar) GetProgressbar(totalDuration float64, filePath string) ffmpeg.ProgressContract {
|
|
p.label.Text = filePath
|
|
p.statusMessage.Color = theme.Color(theme.ColorNamePrimary)
|
|
p.statusMessage.Text = lang.L("inProgressQueue")
|
|
p.messageError.Text = ""
|
|
fyne.Do(func() {
|
|
p.buttonPlay.Hide()
|
|
if p.errorBlock.Visible() {
|
|
p.errorBlock.Hide()
|
|
}
|
|
p.statusMessage.Refresh()
|
|
p.container.Refresh()
|
|
p.errorBlock.Refresh()
|
|
})
|
|
|
|
p.progressbar.Value = 0
|
|
return NewProgress(totalDuration, p.progressbar)
|
|
}
|
|
|
|
func (p *progressBar) ProcessEndedWithError(errorText string) {
|
|
fyne.Do(func() {
|
|
p.statusMessage.Color = theme.Color(theme.ColorNameError)
|
|
p.statusMessage.Text = lang.L("errorQueue")
|
|
p.messageError.Text = errorText
|
|
p.errorBlock.Show()
|
|
})
|
|
}
|
|
|
|
func (p *progressBar) ProcessEndedWithSuccess(file *ffmpeg.File) {
|
|
fyne.Do(func() {
|
|
p.statusMessage.Color = color.RGBA{R: 49, G: 127, B: 114, A: 255}
|
|
p.statusMessage.Text = lang.L("completedQueue")
|
|
p.buttonPlay.Show()
|
|
p.buttonPlay.OnTapped = func() {
|
|
p.buttonPlay.Disable()
|
|
go func() {
|
|
ffplay, err := p.ffmpegService.GetFFplay()
|
|
if err == nil {
|
|
_ = ffplay.Play(file)
|
|
}
|
|
|
|
fyne.Do(func() {
|
|
p.buttonPlay.Enable()
|
|
})
|
|
}()
|
|
}
|
|
})
|
|
}
|
|
|
|
type Progress struct {
|
|
totalDuration float64
|
|
progressbar *widget.ProgressBar
|
|
protocol string
|
|
}
|
|
|
|
func NewProgress(totalDuration float64, progressbar *widget.ProgressBar) ffmpeg.ProgressContract {
|
|
return &Progress{
|
|
totalDuration: totalDuration,
|
|
progressbar: progressbar,
|
|
protocol: "pipe:",
|
|
}
|
|
}
|
|
|
|
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 = lang.L("errorConverter")
|
|
}
|
|
return errors.New(errorText)
|
|
}
|
|
|
|
return nil
|
|
}
|