Introduce progress bar updates and queue processing logic
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.
This commit is contained in:
parent
1b1cdd5c22
commit
2909ef7cea
@ -5,6 +5,7 @@ import (
|
|||||||
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/application/convertor"
|
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/application/convertor"
|
||||||
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/application/setting"
|
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/application/setting"
|
||||||
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg"
|
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AppContract interface {
|
type AppContract interface {
|
||||||
@ -16,6 +17,8 @@ type AppContract interface {
|
|||||||
GetItemsToConvert() convertor.ItemsToConvertContract
|
GetItemsToConvert() convertor.ItemsToConvertContract
|
||||||
GetQueueService() convertor.QueueListContract
|
GetQueueService() convertor.QueueListContract
|
||||||
Run()
|
Run()
|
||||||
|
AfterClosing()
|
||||||
|
RunConvertor()
|
||||||
}
|
}
|
||||||
|
|
||||||
type application struct {
|
type application struct {
|
||||||
@ -79,3 +82,56 @@ func (a *application) GetConvertorService() convertor.ConvertorContract {
|
|||||||
func (a *application) Run() {
|
func (a *application) Run() {
|
||||||
a.fyneApp.Run()
|
a.fyneApp.Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *application) RunConvertor() {
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
time.Sleep(time.Millisecond * 3000)
|
||||||
|
queueId, queue := a.queueService.Next()
|
||||||
|
if queue == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
queue.Status = convertor.StatusType(convertor.InProgress)
|
||||||
|
a.queueService.EventChangeQueue(queueId, queue)
|
||||||
|
|
||||||
|
if a.progressBarService.GetContainer().Hidden {
|
||||||
|
a.progressBarService.GetContainer().Show()
|
||||||
|
}
|
||||||
|
|
||||||
|
totalDuration := float64(0)
|
||||||
|
ffprobe, err := a.ffmpegService.GetFFprobe()
|
||||||
|
if err == nil {
|
||||||
|
totalDuration, err = ffprobe.GetTotalDuration(&queue.Setting.FileInput)
|
||||||
|
if err != nil {
|
||||||
|
totalDuration = float64(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
progress := a.progressBarService.GetProgressbar(
|
||||||
|
totalDuration,
|
||||||
|
queue.Setting.FileInput.Path,
|
||||||
|
)
|
||||||
|
|
||||||
|
err = a.convertorService.RunConvert(*queue.Setting, progress)
|
||||||
|
if err != nil {
|
||||||
|
queue.Status = convertor.StatusType(convertor.Error)
|
||||||
|
queue.Error = err
|
||||||
|
a.queueService.EventChangeQueue(queueId, queue)
|
||||||
|
a.progressBarService.ProcessEndedWithError(err.Error())
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
queue.Status = convertor.StatusType(convertor.Completed)
|
||||||
|
a.queueService.EventChangeQueue(queueId, queue)
|
||||||
|
a.progressBarService.ProcessEndedWithSuccess(&queue.Setting.FileOut)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *application) AfterClosing() {
|
||||||
|
for _, cmd := range a.convertorService.GetRunningProcesses() {
|
||||||
|
_ = cmd.Process.Kill()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -5,25 +5,54 @@ import (
|
|||||||
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/application/convertor/encoder"
|
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/application/convertor/encoder"
|
||||||
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg"
|
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/ffmpeg"
|
||||||
"io"
|
"io"
|
||||||
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ConvertorContract interface {
|
type ConvertorContract interface {
|
||||||
|
RunConvert(setting ffmpeg.ConvertSetting, progress ffmpeg.ProgressContract) error
|
||||||
GetSupportFormats() (encoder.ConvertorFormatsContract, error)
|
GetSupportFormats() (encoder.ConvertorFormatsContract, error)
|
||||||
|
GetRunningProcesses() map[int]*exec.Cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
type runningProcesses struct {
|
||||||
|
items map[int]*exec.Cmd
|
||||||
|
numberOfStarts int
|
||||||
}
|
}
|
||||||
|
|
||||||
type convertor struct {
|
type convertor struct {
|
||||||
ffmpegService ffmpeg.UtilitiesContract
|
ffmpegService ffmpeg.UtilitiesContract
|
||||||
|
runningProcesses *runningProcesses
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewConvertor(
|
func NewConvertor(
|
||||||
ffmpegService ffmpeg.UtilitiesContract,
|
ffmpegService ffmpeg.UtilitiesContract,
|
||||||
) ConvertorContract {
|
) ConvertorContract {
|
||||||
return &convertor{
|
return &convertor{
|
||||||
ffmpegService: ffmpegService,
|
ffmpegService: ffmpegService,
|
||||||
|
runningProcesses: &runningProcesses{items: map[int]*exec.Cmd{}, numberOfStarts: 0},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *convertor) RunConvert(setting ffmpeg.ConvertSetting, progress ffmpeg.ProgressContract) error {
|
||||||
|
ffmpegService, err := c.ffmpegService.GetFFmpeg()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
index := c.runningProcesses.numberOfStarts
|
||||||
|
beforeWait := func(cmd *exec.Cmd) {
|
||||||
|
c.runningProcesses.numberOfStarts++
|
||||||
|
c.runningProcesses.items[index] = cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
afterWait := func(cmd *exec.Cmd) {
|
||||||
|
delete(c.runningProcesses.items, index)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ffmpegService.RunConvert(setting, progress, beforeWait, afterWait)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *convertor) GetSupportFormats() (encoder.ConvertorFormatsContract, error) {
|
func (c *convertor) GetSupportFormats() (encoder.ConvertorFormatsContract, error) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
@ -52,3 +81,7 @@ func (c *convertor) GetSupportFormats() (encoder.ConvertorFormatsContract, error
|
|||||||
|
|
||||||
return formats, err
|
return formats, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *convertor) GetRunningProcesses() map[int]*exec.Cmd {
|
||||||
|
return c.runningProcesses.items
|
||||||
|
}
|
||||||
|
@ -1,15 +1,27 @@
|
|||||||
package convertor
|
package convertor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
|
"errors"
|
||||||
"fyne.io/fyne/v2"
|
"fyne.io/fyne/v2"
|
||||||
"fyne.io/fyne/v2/canvas"
|
"fyne.io/fyne/v2/canvas"
|
||||||
"fyne.io/fyne/v2/container"
|
"fyne.io/fyne/v2/container"
|
||||||
|
"fyne.io/fyne/v2/lang"
|
||||||
"fyne.io/fyne/v2/theme"
|
"fyne.io/fyne/v2/theme"
|
||||||
"fyne.io/fyne/v2/widget"
|
"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 {
|
type ProgressBarContract interface {
|
||||||
GetContainer() *fyne.Container
|
GetContainer() *fyne.Container
|
||||||
|
GetProgressbar(totalDuration float64, filePath string) ffmpeg.ProgressContract
|
||||||
|
ProcessEndedWithError(errorText string)
|
||||||
|
ProcessEndedWithSuccess(file *ffmpeg.File)
|
||||||
}
|
}
|
||||||
|
|
||||||
type progressBar struct {
|
type progressBar struct {
|
||||||
@ -20,9 +32,10 @@ type progressBar struct {
|
|||||||
messageError *canvas.Text
|
messageError *canvas.Text
|
||||||
statusMessage *canvas.Text
|
statusMessage *canvas.Text
|
||||||
buttonPlay *widget.Button
|
buttonPlay *widget.Button
|
||||||
|
ffmpegService ffmpeg.UtilitiesContract
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewProgressBar() ProgressBarContract {
|
func NewProgressBar(ffmpegService ffmpeg.UtilitiesContract) ProgressBarContract {
|
||||||
label := widget.NewLabel("")
|
label := widget.NewLabel("")
|
||||||
progressbar := widget.NewProgressBar()
|
progressbar := widget.NewProgressBar()
|
||||||
|
|
||||||
@ -55,9 +68,150 @@ func NewProgressBar() ProgressBarContract {
|
|||||||
messageError: messageError,
|
messageError: messageError,
|
||||||
statusMessage: statusMessage,
|
statusMessage: statusMessage,
|
||||||
buttonPlay: buttonPlay,
|
buttonPlay: buttonPlay,
|
||||||
|
ffmpegService: ffmpegService,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *progressBar) GetContainer() *fyne.Container {
|
func (p *progressBar) GetContainer() *fyne.Container {
|
||||||
return p.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
|
||||||
|
}
|
||||||
|
6
main.go
6
main.go
@ -20,9 +20,9 @@ func main() {
|
|||||||
}
|
}
|
||||||
app.SetMetadata(appMetadata)
|
app.SetMetadata(appMetadata)
|
||||||
fyneApp := app.New()
|
fyneApp := app.New()
|
||||||
progressBarService := convertor.NewProgressBar()
|
|
||||||
appSetting := setting.NewSetting(fyneApp)
|
appSetting := setting.NewSetting(fyneApp)
|
||||||
ffmpegService := ffmpeg.NewUtilities(appSetting)
|
ffmpegService := ffmpeg.NewUtilities(appSetting)
|
||||||
|
progressBarService := convertor.NewProgressBar(ffmpegService)
|
||||||
convertorService := convertor.NewConvertor(ffmpegService)
|
convertorService := convertor.NewConvertor(ffmpegService)
|
||||||
itemsToConvert := convertor.NewItemsToConvert(ffmpegService)
|
itemsToConvert := convertor.NewItemsToConvert(ffmpegService)
|
||||||
queue := convertor.NewQueueList()
|
queue := convertor.NewQueueList()
|
||||||
@ -37,5 +37,9 @@ func main() {
|
|||||||
)
|
)
|
||||||
mainController := controller.NewController(myApp)
|
mainController := controller.NewController(myApp)
|
||||||
mainController.Start()
|
mainController.Start()
|
||||||
|
|
||||||
|
myApp.RunConvertor()
|
||||||
|
defer myApp.AfterClosing()
|
||||||
|
|
||||||
myApp.Run()
|
myApp.Run()
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user