Make it possible to drag and drop multiple files

It is now possible to add multiple files before sending them to the processing queue.
This commit is contained in:
Leonid Nikitin 2025-05-25 01:25:40 +05:00
parent 82167f042f
commit 84b36dd29e
Signed by: kor-elf
GPG Key ID: DAB5355A11C22541
11 changed files with 741 additions and 255 deletions

View File

@ -32,22 +32,22 @@ type Conversion struct {
overwriteOutputFiles *overwriteOutputFiles overwriteOutputFiles *overwriteOutputFiles
selectEncoder *selectEncoder selectEncoder *selectEncoder
runConvert func(setting HandleConvertSetting) runConvert func(setting HandleConvertSetting)
itemsToConvertService kernel.ItemsToConvertContract
} }
type HandleConvertSetting struct { type HandleConvertSetting struct {
FileInput kernel.File
DirectoryForSave string DirectoryForSave string
OverwriteOutputFiles bool OverwriteOutputFiles bool
Format string Format string
Encoder encoder2.EncoderContract Encoder encoder2.EncoderContract
} }
func NewConversion(app kernel.AppContract, formats encoder.ConvertorFormatsContract, runConvert func(setting HandleConvertSetting), settingDirectoryForSaving setting.DirectoryForSavingContract) *Conversion { func NewConversion(app kernel.AppContract, formats encoder.ConvertorFormatsContract, runConvert func(setting HandleConvertSetting), settingDirectoryForSaving setting.DirectoryForSavingContract, itemsToConvertService kernel.ItemsToConvertContract) *Conversion {
conversionMessage := canvas.NewText("", color.RGBA{R: 255, G: 0, B: 0, A: 255}) conversionMessage := canvas.NewText("", color.RGBA{R: 255, G: 0, B: 0, A: 255})
conversionMessage.TextSize = 16 conversionMessage.TextSize = 16
conversionMessage.TextStyle = fyne.TextStyle{Bold: true} conversionMessage.TextStyle = fyne.TextStyle{Bold: true}
fileForConversion := newFileForConversion(app) fileForConversion := newFileForConversion(app, itemsToConvertService)
directoryForSaving := newDirectoryForSaving(app, settingDirectoryForSaving) directoryForSaving := newDirectoryForSaving(app, settingDirectoryForSaving)
overwriteOutputFiles := newOverwriteOutputFiles(app) overwriteOutputFiles := newOverwriteOutputFiles(app)
selectEncoder := newSelectEncoder(app, formats) selectEncoder := newSelectEncoder(app, formats)
@ -93,6 +93,7 @@ func NewConversion(app kernel.AppContract, formats encoder.ConvertorFormatsContr
overwriteOutputFiles: overwriteOutputFiles, overwriteOutputFiles: overwriteOutputFiles,
selectEncoder: selectEncoder, selectEncoder: selectEncoder,
runConvert: runConvert, runConvert: runConvert,
itemsToConvertService: itemsToConvertService,
} }
} }
@ -121,20 +122,32 @@ func (c Conversion) changeEncoder(encoder encoder2.EncoderContract) {
} }
func (c Conversion) AfterViewContent() { func (c Conversion) AfterViewContent() {
if len(c.itemsToConvertService.GetItems()) == 0 {
c.form.form.Disable() c.form.form.Disable()
}
} }
func (c Conversion) selectFileForConversion(err error) { func (c Conversion) selectFileForConversion(err error) {
c.conversionMessage.Text = "" c.conversionMessage.Text = ""
if len(c.itemsToConvertService.GetItems()) == 0 {
if err != nil { if err != nil {
c.form.form.Disable() c.form.form.Disable()
return return
} }
}
c.form.form.Enable() c.form.form.Enable()
} }
func (c Conversion) submit() { func (c Conversion) submit() {
if len(c.itemsToConvertService.GetItems()) == 0 {
showConversionMessage(c.conversionMessage, errors.New(c.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "errorNoFilesAddedForConversion",
})))
c.enableFormConversion()
return
}
if len(c.directoryForSaving.path) == 0 { if len(c.directoryForSaving.path) == 0 {
showConversionMessage(c.conversionMessage, errors.New(c.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ showConversionMessage(c.conversionMessage, errors.New(c.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "errorSelectedFolderSave", MessageID: "errorSelectedFolderSave",
@ -160,18 +173,17 @@ func (c Conversion) submit() {
c.directoryForSaving.button.Disable() c.directoryForSaving.button.Disable()
c.form.form.Disable() c.form.form.Disable()
setting := HandleConvertSetting{ c.runConvert(HandleConvertSetting{
FileInput: *c.fileForConversion.file,
DirectoryForSave: c.directoryForSaving.path, DirectoryForSave: c.directoryForSaving.path,
OverwriteOutputFiles: c.overwriteOutputFiles.IsChecked(), OverwriteOutputFiles: c.overwriteOutputFiles.IsChecked(),
Format: c.selectEncoder.SelectFormat.Selected, Format: c.selectEncoder.SelectFormat.Selected,
Encoder: c.selectEncoder.Encoder, Encoder: c.selectEncoder.Encoder,
} })
c.runConvert(setting)
c.enableFormConversion() c.enableFormConversion()
c.fileForConversion.message.Text = "" if len(c.itemsToConvertService.GetItems()) == 0 {
c.form.form.Disable() c.form.form.Disable()
}
} }
func (c Conversion) enableFormConversion() { func (c Conversion) enableFormConversion() {
@ -188,44 +200,49 @@ type fileForConversion struct {
changeCallbacks map[int]func(err error) changeCallbacks map[int]func(err error)
} }
func newFileForConversion(app kernel.AppContract) *fileForConversion { func newFileForConversion(app kernel.AppContract, itemsToConvertService kernel.ItemsToConvertContract) *fileForConversion {
message := canvas.NewText("", color.RGBA{R: 255, G: 0, B: 0, A: 255})
fileForConversion := &fileForConversion{ fileForConversion := &fileForConversion{
file: &kernel.File{}, message: message,
changeCallbacks: map[int]func(err error){}, changeCallbacks: map[int]func(err error){},
} }
buttonTitle := app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ buttonTitle := app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "choose", MessageID: "choose",
}) + "\n\r\n\r" + app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ }) + "\n" + app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "or", MessageID: "or",
}) + "\n\r\n\r" + app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ }) + "\n" + app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "dragAndDrop1File", MessageID: "dragAndDropFiles",
}) })
fileForConversion.message = canvas.NewText("", color.RGBA{R: 255, G: 0, B: 0, A: 255})
fileForConversion.message.TextSize = 16
fileForConversion.message.TextStyle = fyne.TextStyle{Bold: true}
var locationURI fyne.ListableURI var locationURI fyne.ListableURI
fileForConversion.button = widget.NewButton(buttonTitle, func() { fileForConversion.button = widget.NewButton(buttonTitle, func() {
app.GetWindow().NewFileOpen(func(r fyne.URIReadCloser, err error) { app.GetWindow().NewFileOpen(func(r fyne.URIReadCloser, err error) {
fyne.Do(func() {
fileForConversion.message.Text = ""
fileForConversion.message.Refresh()
})
if err != nil { if err != nil {
fyne.Do(func() {
fileForConversion.message.Text = err.Error() fileForConversion.message.Text = err.Error()
setStringErrorStyle(fileForConversion.message) fileForConversion.message.Refresh()
})
fileForConversion.eventSelectFile(err) fileForConversion.eventSelectFile(err)
return return
} }
if r == nil { if r == nil {
return return
} }
app.GetWindow().GetLayout().GetRightTabs().SelectAddedFilesTab()
fileForConversion.file.Path = r.URI().Path() itemsToConvertService.Add(&kernel.File{
fileForConversion.file.Name = r.URI().Name() Path: r.URI().Path(),
fileForConversion.file.Ext = r.URI().Extension() Name: r.URI().Name(),
Ext: r.URI().Extension(),
fileForConversion.message.Text = r.URI().Path() })
setStringSuccessStyle(fileForConversion.message)
fileForConversion.eventSelectFile(nil) fileForConversion.eventSelectFile(nil)
@ -239,43 +256,41 @@ func newFileForConversion(app kernel.AppContract) *fileForConversion {
return return
} }
if len(uris) > 1 { isError := false
fileForConversion.message.Text = app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ for _, uri := range uris {
MessageID: "errorDragAndDrop1File",
})
setStringErrorStyle(fileForConversion.message)
fileForConversion.eventSelectFile(errors.New(fileForConversion.message.Text))
return
}
uri := uris[0]
info, err := os.Stat(uri.Path()) info, err := os.Stat(uri.Path())
if err != nil { if err != nil {
fileForConversion.message.Text = err.Error() isError = true
setStringErrorStyle(fileForConversion.message) continue
fileForConversion.eventSelectFile(err)
return
} }
if info.IsDir() { if info.IsDir() {
fileForConversion.message.Text = app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ isError = true
MessageID: "errorIsFolder", continue
})
setStringErrorStyle(fileForConversion.message)
fileForConversion.eventSelectFile(errors.New(fileForConversion.message.Text))
return
} }
fileForConversion.file.Path = uri.Path() itemsToConvertService.Add(&kernel.File{
fileForConversion.file.Name = uri.Name() Path: uri.Path(),
fileForConversion.file.Ext = uri.Extension() Name: uri.Name(),
Ext: uri.Extension(),
fileForConversion.message.Text = uri.Path() })
setStringSuccessStyle(fileForConversion.message)
fileForConversion.eventSelectFile(nil) fileForConversion.eventSelectFile(nil)
listableURI := storage.NewFileURI(filepath.Dir(uri.Path())) listableURI := storage.NewFileURI(filepath.Dir(uri.Path()))
locationURI, _ = storage.ListerForURI(listableURI) locationURI, _ = storage.ListerForURI(listableURI)
}
if isError {
fileForConversion.message.Text = app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "errorDragAndDropFile",
})
setStringErrorStyle(fileForConversion.message)
fileForConversion.eventSelectFile(errors.New(fileForConversion.message.Text))
} else {
fyne.Do(func() {
fileForConversion.message.Text = ""
fileForConversion.message.Refresh()
})
}
}) })
return fileForConversion return fileForConversion

View File

@ -25,6 +25,7 @@ type ConvertorHandler struct {
errorView error2.ViewContract errorView error2.ViewContract
convertorRepository convertor.RepositoryContract convertorRepository convertor.RepositoryContract
settingDirectoryForSaving setting.DirectoryForSavingContract settingDirectoryForSaving setting.DirectoryForSavingContract
itemsToConvertService kernel.ItemsToConvertContract
} }
func NewConvertorHandler( func NewConvertorHandler(
@ -33,6 +34,7 @@ func NewConvertorHandler(
errorView error2.ViewContract, errorView error2.ViewContract,
convertorRepository convertor.RepositoryContract, convertorRepository convertor.RepositoryContract,
settingDirectoryForSaving setting.DirectoryForSavingContract, settingDirectoryForSaving setting.DirectoryForSavingContract,
itemsToConvertService kernel.ItemsToConvertContract,
) *ConvertorHandler { ) *ConvertorHandler {
return &ConvertorHandler{ return &ConvertorHandler{
app: app, app: app,
@ -40,6 +42,7 @@ func NewConvertorHandler(
errorView: errorView, errorView: errorView,
convertorRepository: convertorRepository, convertorRepository: convertorRepository,
settingDirectoryForSaving: settingDirectoryForSaving, settingDirectoryForSaving: settingDirectoryForSaving,
itemsToConvertService: itemsToConvertService,
} }
} }
@ -50,7 +53,7 @@ func (h ConvertorHandler) MainConvertor() {
h.errorView.PanicError(err) h.errorView.PanicError(err)
return return
} }
conversion := view.NewConversion(h.app, formats, h.runConvert, h.settingDirectoryForSaving) conversion := view.NewConversion(h.app, formats, h.runConvert, h.settingDirectoryForSaving, h.itemsToConvertService)
h.convertorView.Main(conversion) h.convertorView.Main(conversion)
return return
} }
@ -77,16 +80,26 @@ func (h ConvertorHandler) GetFfplayVersion() (string, error) {
} }
func (h ConvertorHandler) runConvert(setting view.HandleConvertSetting) { func (h ConvertorHandler) runConvert(setting view.HandleConvertSetting) {
h.app.GetWindow().GetLayout().GetRightTabs().SelectFileQueueTab()
for _, item := range h.itemsToConvertService.GetItems() {
file := item.GetFile()
if file == nil {
continue
}
h.app.GetQueue().Add(&kernel.ConvertSetting{ h.app.GetQueue().Add(&kernel.ConvertSetting{
VideoFileInput: setting.FileInput, VideoFileInput: *file,
VideoFileOut: kernel.File{ VideoFileOut: kernel.File{
Path: setting.DirectoryForSave + helper.PathSeparator() + setting.FileInput.Name + "." + setting.Format, Path: setting.DirectoryForSave + helper.PathSeparator() + file.Name + "." + setting.Format,
Name: setting.FileInput.Name, Name: file.Name,
Ext: "." + setting.Format, Ext: "." + setting.Format,
}, },
OverwriteOutputFiles: setting.OverwriteOutputFiles, OverwriteOutputFiles: setting.OverwriteOutputFiles,
Encoder: setting.Encoder, Encoder: setting.Encoder,
}) })
}
h.itemsToConvertService.AfterAddingQueue()
} }
func (h ConvertorHandler) checkingFFPathUtilities() bool { func (h ConvertorHandler) checkingFFPathUtilities() bool {

View File

@ -12,6 +12,7 @@ type AppContract interface {
GetQueue() QueueListContract GetQueue() QueueListContract
GetLocalizerService() LocalizerContract GetLocalizerService() LocalizerContract
GetConvertorService() ConvertorContract GetConvertorService() ConvertorContract
GetFFplayService() FFplayContract
AfterClosing() AfterClosing()
RunConvertor() RunConvertor()
} }
@ -23,25 +24,34 @@ type App struct {
localizerService LocalizerContract localizerService LocalizerContract
convertorService ConvertorContract convertorService ConvertorContract
blockProgressbarService BlockProgressbarContract
ffplayService FFplayContract
} }
func NewApp( func NewApp(
metadata *fyne.AppMetadata, metadata *fyne.AppMetadata,
localizerService LocalizerContract, localizerService LocalizerContract,
queue QueueListContract, queue QueueListContract,
queueLayoutObject QueueLayoutObjectContract, ffplayService FFplayContract,
convertorService ConvertorContract, convertorService ConvertorContract,
) *App { ) *App {
app.SetMetadata(*metadata) app.SetMetadata(*metadata)
a := app.New() a := app.New()
statusesText := GetBlockProgressbarStatusesText(localizerService)
blockProgressbarService := NewBlockProgressbar(statusesText, ffplayService)
rightTabsService := NewRightTabs(localizerService)
queueLayoutObject := NewQueueLayoutObject(queue, localizerService, ffplayService, rightTabsService, blockProgressbarService.GetContainer())
return &App{ return &App{
AppFyne: a, AppFyne: a,
Window: newWindow(a.NewWindow("GUI for FFmpeg"), NewLayout(queueLayoutObject, localizerService)), Window: newWindow(a.NewWindow("GUI for FFmpeg"), NewLayout(queueLayoutObject, localizerService, rightTabsService)),
Queue: queue, Queue: queue,
localizerService: localizerService, localizerService: localizerService,
convertorService: convertorService, convertorService: convertorService,
blockProgressbarService: blockProgressbarService,
ffplayService: ffplayService,
} }
} }
@ -65,6 +75,10 @@ func (a App) GetConvertorService() ConvertorContract {
return a.convertorService return a.convertorService
} }
func (a App) GetFFplayService() FFplayContract {
return a.ffplayService
}
func (a App) AfterClosing() { func (a App) AfterClosing() {
for _, cmd := range a.convertorService.GetRunningProcesses() { for _, cmd := range a.convertorService.GetRunningProcesses() {
_ = cmd.Process.Kill() _ = cmd.Process.Kill()
@ -81,22 +95,33 @@ func (a App) RunConvertor() {
} }
queue.Status = StatusType(InProgress) queue.Status = StatusType(InProgress)
a.Window.GetLayout().ChangeQueueStatus(queueId, queue) a.Window.GetLayout().ChangeQueueStatus(queueId, queue)
if a.blockProgressbarService.GetContainer().Hidden {
a.blockProgressbarService.GetContainer().Show()
}
totalDuration, err := a.convertorService.GetTotalDuration(&queue.Setting.VideoFileInput) totalDuration, err := a.convertorService.GetTotalDuration(&queue.Setting.VideoFileInput)
if err != nil { if err != nil {
totalDuration = 0 totalDuration = 0
} }
progress := a.Window.GetLayout().NewProgressbar(queueId, totalDuration)
progress := a.blockProgressbarService.GetProgressbar(
totalDuration,
queue.Setting.VideoFileInput.Path,
a.localizerService,
)
err = a.convertorService.RunConvert(*queue.Setting, progress) err = a.convertorService.RunConvert(*queue.Setting, progress)
if err != nil { if err != nil {
queue.Status = StatusType(Error) queue.Status = StatusType(Error)
queue.Error = err queue.Error = err
a.Window.GetLayout().ChangeQueueStatus(queueId, queue) a.Window.GetLayout().ChangeQueueStatus(queueId, queue)
a.blockProgressbarService.ProcessEndedWithError(err.Error())
continue continue
} }
queue.Status = StatusType(Completed) queue.Status = StatusType(Completed)
a.Window.GetLayout().ChangeQueueStatus(queueId, queue) a.Window.GetLayout().ChangeQueueStatus(queueId, queue)
a.blockProgressbarService.ProcessEndedWithSuccess(queue.Setting.VideoFileOut.Path)
} }
}() }()
} }

151
kernel/items_to_convert.go Normal file
View File

@ -0,0 +1,151 @@
package kernel
import (
"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"
)
type ItemsToConvertContract interface {
Add(file *File)
GetItems() map[int]ItemToConvertContract
AfterAddingQueue()
}
type ItemsToConvert struct {
nextId int
items map[int]ItemToConvertContract
itemsContainer *fyne.Container
ffplayService FFplayContract
isAutoRemove bool
}
func NewItemsToConvert(itemsContainer *fyne.Container, ffplayService FFplayContract, localizerService LocalizerContract) *ItemsToConvert {
containerForItems := container.NewVBox()
ItemsToConvert := &ItemsToConvert{
nextId: 0,
items: map[int]ItemToConvertContract{},
itemsContainer: containerForItems,
ffplayService: ffplayService,
isAutoRemove: true,
}
line := canvas.NewLine(theme.Color(theme.ColorNameFocus))
line.StrokeWidth = 5
checkboxAutoRemove := widget.NewCheck(localizerService.GetMessage(&i18n.LocalizeConfig{
MessageID: "autoClearAfterAddingToQueue",
}), func(checked bool) {
ItemsToConvert.isAutoRemove = checked
})
checkboxAutoRemove.SetChecked(ItemsToConvert.isAutoRemove)
localizerService.AddChangeCallback("autoClearAfterAddingToQueue", func(text string) {
checkboxAutoRemove.Text = text
})
buttonClear := widget.NewButton(localizerService.GetMessage(&i18n.LocalizeConfig{
MessageID: "clearAll",
}), func() {
ItemsToConvert.clear()
})
buttonClear.Importance = widget.DangerImportance
localizerService.AddChangeCallback("clearAll", func(text string) {
buttonClear.Text = text
})
itemsContainer.Add(container.NewVBox(
container.NewPadded(),
container.NewBorder(nil, nil, nil, buttonClear, container.NewHScroll(checkboxAutoRemove)),
container.NewPadded(),
line,
container.NewPadded(),
containerForItems,
))
return ItemsToConvert
}
func (items *ItemsToConvert) Add(file *File) {
nextId := items.nextId
var content *fyne.Container
var buttonPlay *widget.Button
buttonPlay = widget.NewButtonWithIcon("", theme.Icon(theme.IconNameMediaPlay), func() {
buttonPlay.Disable()
go func() {
_ = items.ffplayService.Run(FFplaySetting{
PathToFile: file.Path,
})
fyne.Do(func() {
buttonPlay.Enable()
})
}()
})
buttonRemove := widget.NewButtonWithIcon("", theme.Icon(theme.IconNameDelete), func() {
items.itemsContainer.Remove(content)
items.itemsContainer.Refresh()
delete(items.items, nextId)
})
buttonRemove.Importance = widget.DangerImportance
content = container.NewVBox(
container.NewBorder(
nil,
nil,
buttonPlay,
buttonRemove,
container.NewHScroll(widget.NewLabel(file.Name)),
),
container.NewHScroll(widget.NewLabel(file.Path)),
container.NewPadded(),
canvas.NewLine(theme.Color(theme.ColorNameFocus)),
container.NewPadded(),
)
items.itemsContainer.Add(content)
items.items[nextId] = NewItemToConvert(file, content)
items.nextId++
}
func (items *ItemsToConvert) GetItems() map[int]ItemToConvertContract {
return items.items
}
func (items *ItemsToConvert) AfterAddingQueue() {
if items.isAutoRemove {
items.clear()
}
}
func (items *ItemsToConvert) clear() {
items.itemsContainer.RemoveAll()
items.items = map[int]ItemToConvertContract{}
}
type ItemToConvertContract interface {
GetFile() *File
GetContent() *fyne.Container
}
type ItemToConvert struct {
file *File
content *fyne.Container
}
func NewItemToConvert(file *File, content *fyne.Container) *ItemToConvert {
return &ItemToConvert{
file: file,
content: content,
}
}
func (item ItemToConvert) GetFile() *File {
return item.file
}
func (item ItemToConvert) GetContent() *fyne.Container {
return item.content
}

View File

@ -1,8 +1,6 @@
package kernel package kernel
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"
@ -10,31 +8,31 @@ import (
"fyne.io/fyne/v2/widget" "fyne.io/fyne/v2/widget"
"github.com/nicksnyder/go-i18n/v2/i18n" "github.com/nicksnyder/go-i18n/v2/i18n"
"image/color" "image/color"
"io"
"regexp"
"strconv" "strconv"
"strings" "strings"
) )
type LayoutContract interface { type LayoutContract interface {
SetContent(content fyne.CanvasObject) *fyne.Container SetContent(content fyne.CanvasObject) *fyne.Container
NewProgressbar(queueId int, totalDuration float64) ProgressContract
ChangeQueueStatus(queueId int, queue *Queue) ChangeQueueStatus(queueId int, queue *Queue)
GetRightTabs() RightTabsContract
} }
type Layout struct { type Layout struct {
layout *fyne.Container layout *fyne.Container
queueLayoutObject QueueLayoutObjectContract queueLayoutObject QueueLayoutObjectContract
localizerService LocalizerContract localizerService LocalizerContract
rightTabsService RightTabsContract
} }
func NewLayout(queueLayoutObject QueueLayoutObjectContract, localizerService LocalizerContract) *Layout { func NewLayout(queueLayoutObject QueueLayoutObjectContract, localizerService LocalizerContract, rightTabsService RightTabsContract) *Layout {
layout := container.NewAdaptiveGrid(2, widget.NewLabel(""), container.NewVScroll(queueLayoutObject.GetCanvasObject())) layout := container.NewAdaptiveGrid(2, widget.NewLabel(""), queueLayoutObject.GetCanvasObject())
return &Layout{ return &Layout{
layout: layout, layout: layout,
queueLayoutObject: queueLayoutObject, queueLayoutObject: queueLayoutObject,
localizerService: localizerService, localizerService: localizerService,
rightTabsService: rightTabsService,
} }
} }
@ -43,18 +41,16 @@ func (l Layout) SetContent(content fyne.CanvasObject) *fyne.Container {
return l.layout return l.layout
} }
func (l Layout) NewProgressbar(queueId int, totalDuration float64) ProgressContract {
progressbar := l.queueLayoutObject.GetProgressbar(queueId)
return NewProgress(totalDuration, progressbar, l.localizerService)
}
func (l Layout) ChangeQueueStatus(queueId int, queue *Queue) { func (l Layout) ChangeQueueStatus(queueId int, queue *Queue) {
l.queueLayoutObject.ChangeQueueStatus(queueId, queue) l.queueLayoutObject.ChangeQueueStatus(queueId, queue)
} }
func (l Layout) GetRightTabs() RightTabsContract {
return l.rightTabsService
}
type QueueLayoutObjectContract interface { type QueueLayoutObjectContract interface {
GetCanvasObject() fyne.CanvasObject GetCanvasObject() fyne.CanvasObject
GetProgressbar(queueId int) *widget.ProgressBar
ChangeQueueStatus(queueId int, queue *Queue) ChangeQueueStatus(queueId int, queue *Queue)
} }
@ -63,6 +59,7 @@ type QueueLayoutObject struct {
queue QueueListContract queue QueueListContract
container *fyne.Container container *fyne.Container
containerItems *fyne.Container
items map[int]QueueLayoutItem items map[int]QueueLayoutItem
localizerService LocalizerContract localizerService LocalizerContract
queueStatisticsFormat *queueStatisticsFormat queueStatisticsFormat *queueStatisticsFormat
@ -71,7 +68,7 @@ type QueueLayoutObject struct {
type QueueLayoutItem struct { type QueueLayoutItem struct {
CanvasObject fyne.CanvasObject CanvasObject fyne.CanvasObject
ProgressBar *widget.ProgressBar BlockMessageError *container.Scroll
StatusMessage *canvas.Text StatusMessage *canvas.Text
MessageError *canvas.Text MessageError *canvas.Text
buttonPlay *widget.Button buttonPlay *widget.Button
@ -79,7 +76,7 @@ type QueueLayoutItem struct {
status *StatusContract status *StatusContract
} }
func NewQueueLayoutObject(queue QueueListContract, localizerService LocalizerContract, ffplayService FFplayContract) *QueueLayoutObject { func NewQueueLayoutObject(queue QueueListContract, localizerService LocalizerContract, ffplayService FFplayContract, rightTabsService RightTabsContract, blockProgressbar *fyne.Container) *QueueLayoutObject {
title := widget.NewLabel(localizerService.GetMessage(&i18n.LocalizeConfig{MessageID: "queue"})) title := widget.NewLabel(localizerService.GetMessage(&i18n.LocalizeConfig{MessageID: "queue"}))
title.TextStyle.Bold = true title.TextStyle.Bold = true
@ -91,12 +88,27 @@ func NewQueueLayoutObject(queue QueueListContract, localizerService LocalizerCon
items := map[int]QueueLayoutItem{} items := map[int]QueueLayoutItem{}
queueStatisticsFormat := newQueueStatisticsFormat(localizerService, &items) queueStatisticsFormat := newQueueStatisticsFormat(localizerService, &items)
queueLayoutObject := &QueueLayoutObject{ line := canvas.NewLine(theme.Color(theme.ColorNameFocus))
queue: queue, line.StrokeWidth = 5
container: container.NewVBox(
rightTabsService.GetFileQueueContainer().Add(container.NewVBox(
container.NewPadded(),
container.NewHBox(title, queueStatisticsFormat.completed.widget, queueStatisticsFormat.error.widget), container.NewHBox(title, queueStatisticsFormat.completed.widget, queueStatisticsFormat.error.widget),
container.NewHBox(queueStatisticsFormat.inProgress.widget, queueStatisticsFormat.waiting.widget, queueStatisticsFormat.total.widget), container.NewHBox(queueStatisticsFormat.inProgress.widget, queueStatisticsFormat.waiting.widget, queueStatisticsFormat.total.widget),
container.NewPadded(),
line,
container.NewPadded(),
))
queueLayoutObject := &QueueLayoutObject{
queue: queue,
container: container.NewBorder(
container.NewVBox(
blockProgressbar,
widget.NewSeparator(),
), ),
nil, nil, nil, container.NewVScroll(rightTabsService.GetTabs()),
),
containerItems: rightTabsService.GetFileQueueContainer(),
items: items, items: items,
localizerService: localizerService, localizerService: localizerService,
queueStatisticsFormat: queueStatisticsFormat, queueStatisticsFormat: queueStatisticsFormat,
@ -112,31 +124,24 @@ func (o QueueLayoutObject) GetCanvasObject() fyne.CanvasObject {
return o.container return o.container
} }
func (o QueueLayoutObject) GetProgressbar(queueId int) *widget.ProgressBar {
if item, ok := o.items[queueId]; ok {
return item.ProgressBar
}
return widget.NewProgressBar()
}
func (o QueueLayoutObject) Add(id int, queue *Queue) { func (o QueueLayoutObject) Add(id int, queue *Queue) {
progressBar := widget.NewProgressBar()
statusMessage := canvas.NewText(o.getStatusTitle(queue.Status), theme.Color(theme.ColorNamePrimary)) statusMessage := canvas.NewText(o.getStatusTitle(queue.Status), theme.Color(theme.ColorNamePrimary))
messageError := canvas.NewText("", theme.Color(theme.ColorNameError)) messageError := canvas.NewText("", theme.Color(theme.ColorNameError))
buttonPlay := widget.NewButtonWithIcon("", theme.Icon(theme.IconNameMediaPlay), func() { buttonPlay := widget.NewButtonWithIcon("", theme.Icon(theme.IconNameMediaPlay), func() {
}) })
buttonPlay.Hide() buttonPlay.Hide()
blockMessageError := container.NewHScroll(messageError)
blockMessageError.Hide()
content := container.NewVBox( content := container.NewVBox(
container.NewHScroll(widget.NewLabel(queue.Setting.VideoFileInput.Name)), container.NewHScroll(widget.NewLabel(queue.Setting.VideoFileInput.Name)),
progressBar, container.NewHBox(
container.NewHScroll(container.NewHBox(
buttonPlay, buttonPlay,
statusMessage, statusMessage,
)), ),
container.NewHScroll(messageError), blockMessageError,
container.NewPadded(),
canvas.NewLine(theme.Color(theme.ColorNameFocus)), canvas.NewLine(theme.Color(theme.ColorNameFocus)),
container.NewPadded(), container.NewPadded(),
) )
@ -148,13 +153,13 @@ func (o QueueLayoutObject) Add(id int, queue *Queue) {
o.items[id] = QueueLayoutItem{ o.items[id] = QueueLayoutItem{
CanvasObject: content, CanvasObject: content,
ProgressBar: progressBar,
StatusMessage: statusMessage, StatusMessage: statusMessage,
BlockMessageError: blockMessageError,
MessageError: messageError, MessageError: messageError,
buttonPlay: buttonPlay, buttonPlay: buttonPlay,
status: &queue.Status, status: &queue.Status,
} }
o.container.Add(content) o.containerItems.Add(content)
} }
func (o QueueLayoutObject) Remove(id int) { func (o QueueLayoutObject) Remove(id int) {
@ -177,6 +182,7 @@ func (o QueueLayoutObject) ChangeQueueStatus(queueId int, queue *Queue) {
item.MessageError.Text = queue.Error.Error() item.MessageError.Text = queue.Error.Error()
item.MessageError.Color = statusColor item.MessageError.Color = statusColor
fyne.Do(func() { fyne.Do(func() {
item.BlockMessageError.Show()
item.MessageError.Refresh() item.MessageError.Refresh()
}) })
} }
@ -219,101 +225,6 @@ func (o QueueLayoutObject) getStatusTitle(status StatusContract) string {
return o.localizerService.GetMessage(&i18n.LocalizeConfig{MessageID: status.Name() + "Queue"}) return o.localizerService.GetMessage(&i18n.LocalizeConfig{MessageID: status.Name() + "Queue"})
} }
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 queueStatistics struct { type queueStatistics struct {
widget *widget.Check widget *widget.Check
title string title string

254
kernel/progressbar.go Normal file
View File

@ -0,0 +1,254 @@
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
}

76
kernel/right_tabs.go Normal file
View File

@ -0,0 +1,76 @@
package kernel
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"github.com/nicksnyder/go-i18n/v2/i18n"
)
type RightTabsContract interface {
GetTabs() *container.AppTabs
GetAddedFilesContainer() *fyne.Container
GetFileQueueContainer() *fyne.Container
SelectFileQueueTab()
SelectAddedFilesTab()
}
type RightTabs struct {
tabs *container.AppTabs
addedFilesContainer *fyne.Container
addedFilesTab *container.TabItem
fileQueueContainer *fyne.Container
fileQueueTab *container.TabItem
}
func NewRightTabs(localizerService LocalizerContract) *RightTabs {
addedFilesContainer := container.NewVBox()
addedFilesTab := container.NewTabItem(localizerService.GetMessage(&i18n.LocalizeConfig{MessageID: "addedFilesTitle"}), addedFilesContainer)
localizerService.AddChangeCallback("addedFilesTitle", func(text string) {
addedFilesTab.Text = text
})
fileQueueContainer := container.NewVBox()
fileQueueTab := container.NewTabItem(localizerService.GetMessage(&i18n.LocalizeConfig{MessageID: "fileQueueTitle"}), fileQueueContainer)
localizerService.AddChangeCallback("fileQueueTitle", func(text string) {
fileQueueTab.Text = text
})
tabs := container.NewAppTabs(
addedFilesTab,
fileQueueTab,
)
return &RightTabs{
tabs: tabs,
addedFilesContainer: addedFilesContainer,
addedFilesTab: addedFilesTab,
fileQueueContainer: fileQueueContainer,
fileQueueTab: fileQueueTab,
}
}
func (t RightTabs) GetTabs() *container.AppTabs {
return t.tabs
}
func (t RightTabs) GetAddedFilesContainer() *fyne.Container {
return t.addedFilesContainer
}
func (t RightTabs) GetFileQueueContainer() *fyne.Container {
return t.fileQueueContainer
}
func (t RightTabs) SelectFileQueueTab() {
fyne.Do(func() {
t.tabs.Select(t.fileQueueTab)
})
}
func (t RightTabs) SelectAddedFilesTab() {
fyne.Do(func() {
t.tabs.Select(t.addedFilesTab)
})
}

View File

@ -10,6 +10,14 @@ other = "About"
hash = "sha1-8bd565814118ba8b90c40eb5b62acf8d2676e7d6" hash = "sha1-8bd565814118ba8b90c40eb5b62acf8d2676e7d6"
other = "A simple interface for the FFmpeg console utility. \nBut I am not the author of the FFmpeg utility itself." other = "A simple interface for the FFmpeg console utility. \nBut I am not the author of the FFmpeg utility itself."
[addedFilesTitle]
hash = "sha1-8ba0f6e477b0d78df2cc06f1d8b41b888623b851"
other = "Added files"
[autoClearAfterAddingToQueue]
hash = "sha1-b3781695a4c35380d2cd075bb52f27a2a6d8f19c"
other = "Auto-clear after adding to queue"
[buttonDownloadFFmpeg] [buttonDownloadFFmpeg]
hash = "sha1-c223c2e15171156192bc3146aee91e6094bb475b" hash = "sha1-c223c2e15171156192bc3146aee91e6094bb475b"
other = "Download FFmpeg automatically" other = "Download FFmpeg automatically"
@ -38,6 +46,10 @@ other = "Allow file to be overwritten"
hash = "sha1-f60bb5f761024d973834b5e9d25ceebce2c85f94" hash = "sha1-f60bb5f761024d973834b5e9d25ceebce2c85f94"
other = "choose" other = "choose"
[clearAll]
hash = "sha1-f32702d79ac206432400ac6b041695d020f6fa77"
other = "Clear List"
[completedQueue] [completedQueue]
hash = "sha1-398c7d4f7b0d522afb930769c0fbb1a9f4b61fbe" hash = "sha1-398c7d4f7b0d522afb930769c0fbb1a9f4b61fbe"
other = "Completed" other = "Completed"
@ -62,9 +74,9 @@ other = "Will be downloaded from the site:"
hash = "sha1-55f87f114628fa2d5d8e67d1e1cda22c0e4f9271" hash = "sha1-55f87f114628fa2d5d8e67d1e1cda22c0e4f9271"
other = "Downloading..." other = "Downloading..."
[dragAndDrop1File] [dragAndDropFiles]
hash = "sha1-7259670822df1cc92ef5f06ed3c0e9407746975a" hash = "sha1-07bb747cc7590d7a51cdf96dff49a74139097766"
other = "drag and drop 1 file" other = "drag and drop files"
[encoderGroupAudio] [encoderGroupAudio]
hash = "sha1-24321cb5400df96be8f3e2131918bebdb3a01bba" hash = "sha1-24321cb5400df96be8f3e2131918bebdb3a01bba"
@ -226,9 +238,9 @@ other = "could not create file 'database' in folder 'data'"
hash = "sha1-f8153516ac2442d19be4b6daccce839d204ff09f" hash = "sha1-f8153516ac2442d19be4b6daccce839d204ff09f"
other = "Could not open configuration file.\nMake sure another copy of the program is not running!" other = "Could not open configuration file.\nMake sure another copy of the program is not running!"
[errorDragAndDrop1File] [errorDragAndDropFile]
hash = "sha1-a8edb5cbd622f3ce4ec07a2377e22ec5fad4491b" hash = "sha1-863cf1ad9c820d5b0c2006ceeaa29e25f81c1714"
other = "You can only drag and drop 1 file." other = "Not all files were added"
[errorFFmpeg] [errorFFmpeg]
hash = "sha1-ccf0b95c0d1b392dc215258d917eb4e5d0b88ed0" hash = "sha1-ccf0b95c0d1b392dc215258d917eb4e5d0b88ed0"
@ -254,9 +266,9 @@ other = "this is not FFprobe"
hash = "sha1-da7b37d7df3fafbd153665b13888413d52b24c17" hash = "sha1-da7b37d7df3fafbd153665b13888413d52b24c17"
other = "Failed to determine FFprobe version" other = "Failed to determine FFprobe version"
[errorIsFolder] [errorNoFilesAddedForConversion]
hash = "sha1-f937d090b6e320957514d850657cdf2f911dc6aa" hash = "sha1-5cf1f65bef15cb0382e56be98f44c6abde56a314"
other = "You can only drag and drop a file" other = "There are no files to convert"
[errorQueue] [errorQueue]
hash = "sha1-72aecd9ad85642d84d62dbbf3cf70953c5f696c7" hash = "sha1-72aecd9ad85642d84d62dbbf3cf70953c5f696c7"
@ -290,6 +302,10 @@ other = "**FFmpeg** is a trademark of **[Fabrice Bellard](http://bellard.org/)**
hash = "sha1-96ac799e1086b31fd8f5f8d4c801829d6c853f08" hash = "sha1-96ac799e1086b31fd8f5f8d4c801829d6c853f08"
other = "File:" other = "File:"
[fileQueueTitle]
hash = "sha1-aec93b16baeaf55fed871075c9494a460e4a91b8"
other = "Queue"
[formPreset] [formPreset]
hash = "sha1-7759891ba1ef9f7adc70defc7ac18fbf149c1a68" hash = "sha1-7759891ba1ef9f7adc70defc7ac18fbf149c1a68"
other = "Preset" other = "Preset"

View File

@ -10,6 +10,14 @@ other = "Бағдарлама туралы"
hash = "sha1-8bd565814118ba8b90c40eb5b62acf8d2676e7d6" hash = "sha1-8bd565814118ba8b90c40eb5b62acf8d2676e7d6"
other = "FFmpeg консоль утилитасы үшін қарапайым интерфейс. \nБірақ мен FFmpeg утилитасының авторы емеспін." other = "FFmpeg консоль утилитасы үшін қарапайым интерфейс. \nБірақ мен FFmpeg утилитасының авторы емеспін."
[addedFilesTitle]
hash = "sha1-8ba0f6e477b0d78df2cc06f1d8b41b888623b851"
other = "Қосылған файлдар"
[autoClearAfterAddingToQueue]
hash = "sha1-b3781695a4c35380d2cd075bb52f27a2a6d8f19c"
other = "Кезекке қосқаннан кейін тазалаңыз"
[buttonDownloadFFmpeg] [buttonDownloadFFmpeg]
hash = "sha1-c223c2e15171156192bc3146aee91e6094bb475b" hash = "sha1-c223c2e15171156192bc3146aee91e6094bb475b"
other = "FFmpeg автоматты түрде жүктеп алыңыз" other = "FFmpeg автоматты түрде жүктеп алыңыз"
@ -38,6 +46,10 @@ other = "Файлды қайта жазуға рұқсат беріңіз"
hash = "sha1-f60bb5f761024d973834b5e9d25ceebce2c85f94" hash = "sha1-f60bb5f761024d973834b5e9d25ceebce2c85f94"
other = "таңдау" other = "таңдау"
[clearAll]
hash = "sha1-f32702d79ac206432400ac6b041695d020f6fa77"
other = "Тізімді өшіру"
[completedQueue] [completedQueue]
hash = "sha1-398c7d4f7b0d522afb930769c0fbb1a9f4b61fbe" hash = "sha1-398c7d4f7b0d522afb930769c0fbb1a9f4b61fbe"
other = "Дайын" other = "Дайын"
@ -62,9 +74,9 @@ other = "Сайттан жүктеледі:"
hash = "sha1-55f87f114628fa2d5d8e67d1e1cda22c0e4f9271" hash = "sha1-55f87f114628fa2d5d8e67d1e1cda22c0e4f9271"
other = "Жүктеп алынуда..." other = "Жүктеп алынуда..."
[dragAndDrop1File] [dragAndDropFiles]
hash = "sha1-7259670822df1cc92ef5f06ed3c0e9407746975a" hash = "sha1-07bb747cc7590d7a51cdf96dff49a74139097766"
other = "1 файлды сүйреңіз" other = "файлдарды сүйреп апарыңыз"
[encoderGroupAudio] [encoderGroupAudio]
hash = "sha1-24321cb5400df96be8f3e2131918bebdb3a01bba" hash = "sha1-24321cb5400df96be8f3e2131918bebdb3a01bba"
@ -226,9 +238,9 @@ other = "'data' қалтасында 'database' файлын жасау мүмк
hash = "sha1-f8153516ac2442d19be4b6daccce839d204ff09f" hash = "sha1-f8153516ac2442d19be4b6daccce839d204ff09f"
other = "Конфигурация файлын аша алмады.\nБағдарламаның басқа көшірмесі іске қосылмағанына көз жеткізіңіз!" other = "Конфигурация файлын аша алмады.\nБағдарламаның басқа көшірмесі іске қосылмағанына көз жеткізіңіз!"
[errorDragAndDrop1File] [errorDragAndDropFile]
hash = "sha1-a8edb5cbd622f3ce4ec07a2377e22ec5fad4491b" hash = "sha1-863cf1ad9c820d5b0c2006ceeaa29e25f81c1714"
other = "Тек 1 файлды сүйреп апаруға болады" other = "Барлық файлдар қосылмаған"
[errorFFmpeg] [errorFFmpeg]
hash = "sha1-ccf0b95c0d1b392dc215258d917eb4e5d0b88ed0" hash = "sha1-ccf0b95c0d1b392dc215258d917eb4e5d0b88ed0"
@ -254,9 +266,9 @@ other = "бұл FFprobe емес"
hash = "sha1-da7b37d7df3fafbd153665b13888413d52b24c17" hash = "sha1-da7b37d7df3fafbd153665b13888413d52b24c17"
other = "FFprobe нұсқасын анықтау мүмкін болмады" other = "FFprobe нұсқасын анықтау мүмкін болмады"
[errorIsFolder] [errorNoFilesAddedForConversion]
hash = "sha1-f937d090b6e320957514d850657cdf2f911dc6aa" hash = "sha1-5cf1f65bef15cb0382e56be98f44c6abde56a314"
other = "Тек файлды сүйреп апаруға болады" other = "Түрлендіруге арналған файлдар жоқ"
[errorQueue] [errorQueue]
hash = "sha1-72aecd9ad85642d84d62dbbf3cf70953c5f696c7" hash = "sha1-72aecd9ad85642d84d62dbbf3cf70953c5f696c7"
@ -290,6 +302,10 @@ other = "FFmpeg — **[FFmpeg](https://ffmpeg.org/about.html)** жобасын
hash = "sha1-96ac799e1086b31fd8f5f8d4c801829d6c853f08" hash = "sha1-96ac799e1086b31fd8f5f8d4c801829d6c853f08"
other = "Файл:" other = "Файл:"
[fileQueueTitle]
hash = "sha1-aec93b16baeaf55fed871075c9494a460e4a91b8"
other = "Кезек"
[formPreset] [formPreset]
hash = "sha1-7759891ba1ef9f7adc70defc7ac18fbf149c1a68" hash = "sha1-7759891ba1ef9f7adc70defc7ac18fbf149c1a68"
other = "Алдын ала орнатылған" other = "Алдын ала орнатылған"

View File

@ -1,6 +1,8 @@
AlsoUsedProgram = "Также в программе используется:" AlsoUsedProgram = "Также в программе используется:"
about = "О программе" about = "О программе"
aboutText = "Простенький интерфейс для консольной утилиты FFmpeg. \nНо я не являюсь автором самой утилиты FFmpeg." aboutText = "Простенький интерфейс для консольной утилиты FFmpeg. \nНо я не являюсь автором самой утилиты FFmpeg."
addedFilesTitle = "Добавленные файлы"
autoClearAfterAddingToQueue = "Очищать после добавления в очередь"
buttonDownloadFFmpeg = "Скачать автоматически FFmpeg" buttonDownloadFFmpeg = "Скачать автоматически FFmpeg"
buttonForSelectedDirTitle = "Сохранить в папку:" buttonForSelectedDirTitle = "Сохранить в папку:"
cancel = "Отмена" cancel = "Отмена"
@ -8,13 +10,14 @@ changeFFPath = "FFmpeg, FFprobe и FFplay"
changeLanguage = "Поменять язык" changeLanguage = "Поменять язык"
checkboxOverwriteOutputFilesTitle = "Разрешить перезаписать файл" checkboxOverwriteOutputFilesTitle = "Разрешить перезаписать файл"
choose = "выбрать" choose = "выбрать"
clearAll = "Очистить список"
completedQueue = "Готово" completedQueue = "Готово"
converterVideoFilesSubmitTitle = "Конвертировать" converterVideoFilesSubmitTitle = "Конвертировать"
converterVideoFilesTitle = "Конвертер видео, аудио и картинок" converterVideoFilesTitle = "Конвертер видео, аудио и картинок"
download = "Скачать" download = "Скачать"
downloadFFmpegFromSite = "Будет скачано с сайта:" downloadFFmpegFromSite = "Будет скачано с сайта:"
downloadRun = "Скачивается..." downloadRun = "Скачивается..."
dragAndDrop1File = "перетащить 1 файл" dragAndDropFiles = "перетащить файлы"
encoderGroupAudio = "Аудио" encoderGroupAudio = "Аудио"
encoderGroupImage = "Картинки" encoderGroupImage = "Картинки"
encoderGroupVideo = "Видео" encoderGroupVideo = "Видео"
@ -55,14 +58,14 @@ error = "Произошла ошибка!"
errorConverter = "не смогли отконвертировать видео" errorConverter = "не смогли отконвертировать видео"
errorDatabase = "не смогли создать файл 'database' в папке 'data'" errorDatabase = "не смогли создать файл 'database' в папке 'data'"
errorDatabaseTimeout = "Не смогли открыть файл конфигурации.\nУбедитесь, что другая копия программы не запущена!" errorDatabaseTimeout = "Не смогли открыть файл конфигурации.\nУбедитесь, что другая копия программы не запущена!"
errorDragAndDrop1File = "Можно перетащить только 1 файл" errorDragAndDropFile = "Не все файлы добавились"
errorFFmpeg = "это не FFmpeg" errorFFmpeg = "это не FFmpeg"
errorFFmpegVersion = "Не смогли определить версию FFmpeg" errorFFmpegVersion = "Не смогли определить версию FFmpeg"
errorFFplay = "это не FFplay" errorFFplay = "это не FFplay"
errorFFplayVersion = "Не смогли определить версию FFplay" errorFFplayVersion = "Не смогли определить версию FFplay"
errorFFprobe = "это не FFprobe" errorFFprobe = "это не FFprobe"
errorFFprobeVersion = "Не смогли определить версию FFprobe" errorFFprobeVersion = "Не смогли определить версию FFprobe"
errorIsFolder = "Можно перетаскивать только файл" errorNoFilesAddedForConversion = "Нет файлов для конвертации"
errorQueue = "Ошибка" errorQueue = "Ошибка"
errorSelectedEncoder = "Конвертер не выбран" errorSelectedEncoder = "Конвертер не выбран"
errorSelectedFolderSave = "Папка для сохранения не выбрана!" errorSelectedFolderSave = "Папка для сохранения не выбрана!"
@ -71,6 +74,7 @@ exit = "Выход"
ffmpegLGPL = "Это программное обеспечение использует библиотеки из проекта **FFmpeg** под **[LGPLv2.1](http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html)**." ffmpegLGPL = "Это программное обеспечение использует библиотеки из проекта **FFmpeg** под **[LGPLv2.1](http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html)**."
ffmpegTrademark = "**FFmpeg** — торговая марка **[Fabrice Bellard](http://bellard.org/)** , создателя проекта **[FFmpeg](https://ffmpeg.org/about.html)**." ffmpegTrademark = "**FFmpeg** — торговая марка **[Fabrice Bellard](http://bellard.org/)** , создателя проекта **[FFmpeg](https://ffmpeg.org/about.html)**."
fileForConversionTitle = "Файл:" fileForConversionTitle = "Файл:"
fileQueueTitle = "Очередь"
formPreset = "Предустановка" formPreset = "Предустановка"
gratitude = "Благодарность" gratitude = "Благодарность"
gratitudeText = "Я искренне благодарю вас за неоценимую\n\rи своевременную помощь:" gratitudeText = "Я искренне благодарю вас за неоценимую\n\rи своевременную помощь:"

View File

@ -46,7 +46,7 @@ func init() {
appMetadata, appMetadata,
localizerService, localizerService,
queue, queue,
kernel.NewQueueLayoutObject(queue, localizerService, ffplayService), ffplayService,
convertorService, convertorService,
) )
} }
@ -108,7 +108,12 @@ func main() {
localizerView := localizer.NewView(application) localizerView := localizer.NewView(application)
convertorView := convertor.NewView(application) convertorView := convertor.NewView(application)
convertorHandler := handler.NewConvertorHandler(application, convertorView, errorView, convertorRepository, settingDirectoryForSaving) itemsToConvertService := kernel.NewItemsToConvert(
application.GetWindow().GetLayout().GetRightTabs().GetAddedFilesContainer(),
application.GetFFplayService(),
application.GetLocalizerService(),
)
convertorHandler := handler.NewConvertorHandler(application, convertorView, errorView, convertorRepository, settingDirectoryForSaving, itemsToConvertService)
themeRepository := theme.NewRepository(settingRepository) themeRepository := theme.NewRepository(settingRepository)
themeService := theme.NewTheme(application, themeRepository) themeService := theme.NewTheme(application, themeRepository)