From 84b36dd29e863893bd92d3030f8cf2736228ecfd Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Sun, 25 May 2025 01:25:40 +0500 Subject: [PATCH] Make it possible to drag and drop multiple files It is now possible to add multiple files before sending them to the processing queue. --- convertor/view/conversion.go | 169 ++++++++++++----------- handler/convertor.go | 35 +++-- kernel/app.go | 39 +++++- kernel/items_to_convert.go | 151 +++++++++++++++++++++ kernel/layout.go | 185 +++++++------------------ kernel/progressbar.go | 254 +++++++++++++++++++++++++++++++++++ kernel/right_tabs.go | 76 +++++++++++ languages/active.en.toml | 34 +++-- languages/active.kk.toml | 34 +++-- languages/active.ru.toml | 10 +- main.go | 9 +- 11 files changed, 741 insertions(+), 255 deletions(-) create mode 100644 kernel/items_to_convert.go create mode 100644 kernel/progressbar.go create mode 100644 kernel/right_tabs.go diff --git a/convertor/view/conversion.go b/convertor/view/conversion.go index 5d95e18..546f273 100644 --- a/convertor/view/conversion.go +++ b/convertor/view/conversion.go @@ -24,30 +24,30 @@ type ConversionContract interface { } type Conversion struct { - app kernel.AppContract - form *form - conversionMessage *canvas.Text - fileForConversion *fileForConversion - directoryForSaving *directoryForSaving - overwriteOutputFiles *overwriteOutputFiles - selectEncoder *selectEncoder - runConvert func(setting HandleConvertSetting) + app kernel.AppContract + form *form + conversionMessage *canvas.Text + fileForConversion *fileForConversion + directoryForSaving *directoryForSaving + overwriteOutputFiles *overwriteOutputFiles + selectEncoder *selectEncoder + runConvert func(setting HandleConvertSetting) + itemsToConvertService kernel.ItemsToConvertContract } type HandleConvertSetting struct { - FileInput kernel.File DirectoryForSave string OverwriteOutputFiles bool Format string 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.TextSize = 16 conversionMessage.TextStyle = fyne.TextStyle{Bold: true} - fileForConversion := newFileForConversion(app) + fileForConversion := newFileForConversion(app, itemsToConvertService) directoryForSaving := newDirectoryForSaving(app, settingDirectoryForSaving) overwriteOutputFiles := newOverwriteOutputFiles(app) selectEncoder := newSelectEncoder(app, formats) @@ -85,14 +85,15 @@ func NewConversion(app kernel.AppContract, formats encoder.ConvertorFormatsContr form := newForm(app, items) return &Conversion{ - app: app, - form: form, - conversionMessage: conversionMessage, - fileForConversion: fileForConversion, - directoryForSaving: directoryForSaving, - overwriteOutputFiles: overwriteOutputFiles, - selectEncoder: selectEncoder, - runConvert: runConvert, + app: app, + form: form, + conversionMessage: conversionMessage, + fileForConversion: fileForConversion, + directoryForSaving: directoryForSaving, + overwriteOutputFiles: overwriteOutputFiles, + selectEncoder: selectEncoder, + runConvert: runConvert, + itemsToConvertService: itemsToConvertService, } } @@ -121,20 +122,32 @@ func (c Conversion) changeEncoder(encoder encoder2.EncoderContract) { } func (c Conversion) AfterViewContent() { - c.form.form.Disable() + if len(c.itemsToConvertService.GetItems()) == 0 { + c.form.form.Disable() + } } func (c Conversion) selectFileForConversion(err error) { c.conversionMessage.Text = "" - if err != nil { - c.form.form.Disable() - return + if len(c.itemsToConvertService.GetItems()) == 0 { + if err != nil { + c.form.form.Disable() + return + } } c.form.form.Enable() } 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 { showConversionMessage(c.conversionMessage, errors.New(c.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ MessageID: "errorSelectedFolderSave", @@ -160,18 +173,17 @@ func (c Conversion) submit() { c.directoryForSaving.button.Disable() c.form.form.Disable() - setting := HandleConvertSetting{ - FileInput: *c.fileForConversion.file, + c.runConvert(HandleConvertSetting{ DirectoryForSave: c.directoryForSaving.path, OverwriteOutputFiles: c.overwriteOutputFiles.IsChecked(), Format: c.selectEncoder.SelectFormat.Selected, Encoder: c.selectEncoder.Encoder, - } - c.runConvert(setting) + }) c.enableFormConversion() - c.fileForConversion.message.Text = "" - c.form.form.Disable() + if len(c.itemsToConvertService.GetItems()) == 0 { + c.form.form.Disable() + } } func (c Conversion) enableFormConversion() { @@ -188,44 +200,49 @@ type fileForConversion struct { 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{ - file: &kernel.File{}, + message: message, + changeCallbacks: map[int]func(err error){}, } buttonTitle := app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ MessageID: "choose", - }) + "\n\r\n\r" + app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ + }) + "\n" + app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ MessageID: "or", - }) + "\n\r\n\r" + app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "dragAndDrop1File", + }) + "\n" + app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ + 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 fileForConversion.button = widget.NewButton(buttonTitle, func() { app.GetWindow().NewFileOpen(func(r fyne.URIReadCloser, err error) { + fyne.Do(func() { + fileForConversion.message.Text = "" + fileForConversion.message.Refresh() + }) + if err != nil { - fileForConversion.message.Text = err.Error() - setStringErrorStyle(fileForConversion.message) + fyne.Do(func() { + fileForConversion.message.Text = err.Error() + fileForConversion.message.Refresh() + }) fileForConversion.eventSelectFile(err) return } if r == nil { return } + app.GetWindow().GetLayout().GetRightTabs().SelectAddedFilesTab() - fileForConversion.file.Path = r.URI().Path() - fileForConversion.file.Name = r.URI().Name() - fileForConversion.file.Ext = r.URI().Extension() - - fileForConversion.message.Text = r.URI().Path() - setStringSuccessStyle(fileForConversion.message) + itemsToConvertService.Add(&kernel.File{ + Path: r.URI().Path(), + Name: r.URI().Name(), + Ext: r.URI().Extension(), + }) fileForConversion.eventSelectFile(nil) @@ -239,43 +256,41 @@ func newFileForConversion(app kernel.AppContract) *fileForConversion { return } - if len(uris) > 1 { + isError := false + for _, uri := range uris { + info, err := os.Stat(uri.Path()) + if err != nil { + isError = true + continue + } + if info.IsDir() { + isError = true + continue + } + + itemsToConvertService.Add(&kernel.File{ + Path: uri.Path(), + Name: uri.Name(), + Ext: uri.Extension(), + }) + + fileForConversion.eventSelectFile(nil) + + listableURI := storage.NewFileURI(filepath.Dir(uri.Path())) + locationURI, _ = storage.ListerForURI(listableURI) + } + if isError { fileForConversion.message.Text = app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "errorDragAndDrop1File", + MessageID: "errorDragAndDropFile", }) setStringErrorStyle(fileForConversion.message) fileForConversion.eventSelectFile(errors.New(fileForConversion.message.Text)) - return - } - - uri := uris[0] - info, err := os.Stat(uri.Path()) - if err != nil { - fileForConversion.message.Text = err.Error() - setStringErrorStyle(fileForConversion.message) - fileForConversion.eventSelectFile(err) - return - } - if info.IsDir() { - fileForConversion.message.Text = app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ - MessageID: "errorIsFolder", + } else { + fyne.Do(func() { + fileForConversion.message.Text = "" + fileForConversion.message.Refresh() }) - setStringErrorStyle(fileForConversion.message) - fileForConversion.eventSelectFile(errors.New(fileForConversion.message.Text)) - return } - - fileForConversion.file.Path = uri.Path() - fileForConversion.file.Name = uri.Name() - fileForConversion.file.Ext = uri.Extension() - - fileForConversion.message.Text = uri.Path() - setStringSuccessStyle(fileForConversion.message) - - fileForConversion.eventSelectFile(nil) - - listableURI := storage.NewFileURI(filepath.Dir(uri.Path())) - locationURI, _ = storage.ListerForURI(listableURI) }) return fileForConversion diff --git a/handler/convertor.go b/handler/convertor.go index e67fa81..a0cd29b 100644 --- a/handler/convertor.go +++ b/handler/convertor.go @@ -25,6 +25,7 @@ type ConvertorHandler struct { errorView error2.ViewContract convertorRepository convertor.RepositoryContract settingDirectoryForSaving setting.DirectoryForSavingContract + itemsToConvertService kernel.ItemsToConvertContract } func NewConvertorHandler( @@ -33,6 +34,7 @@ func NewConvertorHandler( errorView error2.ViewContract, convertorRepository convertor.RepositoryContract, settingDirectoryForSaving setting.DirectoryForSavingContract, + itemsToConvertService kernel.ItemsToConvertContract, ) *ConvertorHandler { return &ConvertorHandler{ app: app, @@ -40,6 +42,7 @@ func NewConvertorHandler( errorView: errorView, convertorRepository: convertorRepository, settingDirectoryForSaving: settingDirectoryForSaving, + itemsToConvertService: itemsToConvertService, } } @@ -50,7 +53,7 @@ func (h ConvertorHandler) MainConvertor() { h.errorView.PanicError(err) 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) return } @@ -77,16 +80,26 @@ func (h ConvertorHandler) GetFfplayVersion() (string, error) { } func (h ConvertorHandler) runConvert(setting view.HandleConvertSetting) { - h.app.GetQueue().Add(&kernel.ConvertSetting{ - VideoFileInput: setting.FileInput, - VideoFileOut: kernel.File{ - Path: setting.DirectoryForSave + helper.PathSeparator() + setting.FileInput.Name + "." + setting.Format, - Name: setting.FileInput.Name, - Ext: "." + setting.Format, - }, - OverwriteOutputFiles: setting.OverwriteOutputFiles, - Encoder: setting.Encoder, - }) + 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{ + VideoFileInput: *file, + VideoFileOut: kernel.File{ + Path: setting.DirectoryForSave + helper.PathSeparator() + file.Name + "." + setting.Format, + Name: file.Name, + Ext: "." + setting.Format, + }, + OverwriteOutputFiles: setting.OverwriteOutputFiles, + Encoder: setting.Encoder, + }) + } + h.itemsToConvertService.AfterAddingQueue() } func (h ConvertorHandler) checkingFFPathUtilities() bool { diff --git a/kernel/app.go b/kernel/app.go index e1ec4f9..3babb14 100644 --- a/kernel/app.go +++ b/kernel/app.go @@ -12,6 +12,7 @@ type AppContract interface { GetQueue() QueueListContract GetLocalizerService() LocalizerContract GetConvertorService() ConvertorContract + GetFFplayService() FFplayContract AfterClosing() RunConvertor() } @@ -21,27 +22,36 @@ type App struct { Window WindowContract Queue QueueListContract - localizerService LocalizerContract - convertorService ConvertorContract + localizerService LocalizerContract + convertorService ConvertorContract + blockProgressbarService BlockProgressbarContract + ffplayService FFplayContract } func NewApp( metadata *fyne.AppMetadata, localizerService LocalizerContract, queue QueueListContract, - queueLayoutObject QueueLayoutObjectContract, + ffplayService FFplayContract, convertorService ConvertorContract, ) *App { app.SetMetadata(*metadata) a := app.New() + statusesText := GetBlockProgressbarStatusesText(localizerService) + blockProgressbarService := NewBlockProgressbar(statusesText, ffplayService) + rightTabsService := NewRightTabs(localizerService) + queueLayoutObject := NewQueueLayoutObject(queue, localizerService, ffplayService, rightTabsService, blockProgressbarService.GetContainer()) + return &App{ 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, - localizerService: localizerService, - convertorService: convertorService, + localizerService: localizerService, + convertorService: convertorService, + blockProgressbarService: blockProgressbarService, + ffplayService: ffplayService, } } @@ -65,6 +75,10 @@ func (a App) GetConvertorService() ConvertorContract { return a.convertorService } +func (a App) GetFFplayService() FFplayContract { + return a.ffplayService +} + func (a App) AfterClosing() { for _, cmd := range a.convertorService.GetRunningProcesses() { _ = cmd.Process.Kill() @@ -81,22 +95,33 @@ func (a App) RunConvertor() { } queue.Status = StatusType(InProgress) a.Window.GetLayout().ChangeQueueStatus(queueId, queue) + if a.blockProgressbarService.GetContainer().Hidden { + a.blockProgressbarService.GetContainer().Show() + } totalDuration, err := a.convertorService.GetTotalDuration(&queue.Setting.VideoFileInput) if err != nil { 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) if err != nil { queue.Status = StatusType(Error) queue.Error = err a.Window.GetLayout().ChangeQueueStatus(queueId, queue) + a.blockProgressbarService.ProcessEndedWithError(err.Error()) + continue } queue.Status = StatusType(Completed) a.Window.GetLayout().ChangeQueueStatus(queueId, queue) + a.blockProgressbarService.ProcessEndedWithSuccess(queue.Setting.VideoFileOut.Path) } }() } diff --git a/kernel/items_to_convert.go b/kernel/items_to_convert.go new file mode 100644 index 0000000..0adbc3d --- /dev/null +++ b/kernel/items_to_convert.go @@ -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 +} diff --git a/kernel/layout.go b/kernel/layout.go index 94ec22a..3d34179 100644 --- a/kernel/layout.go +++ b/kernel/layout.go @@ -1,8 +1,6 @@ package kernel import ( - "bufio" - "errors" "fyne.io/fyne/v2" "fyne.io/fyne/v2/canvas" "fyne.io/fyne/v2/container" @@ -10,31 +8,31 @@ import ( "fyne.io/fyne/v2/widget" "github.com/nicksnyder/go-i18n/v2/i18n" "image/color" - "io" - "regexp" "strconv" "strings" ) type LayoutContract interface { SetContent(content fyne.CanvasObject) *fyne.Container - NewProgressbar(queueId int, totalDuration float64) ProgressContract ChangeQueueStatus(queueId int, queue *Queue) + GetRightTabs() RightTabsContract } type Layout struct { layout *fyne.Container queueLayoutObject QueueLayoutObjectContract localizerService LocalizerContract + rightTabsService RightTabsContract } -func NewLayout(queueLayoutObject QueueLayoutObjectContract, localizerService LocalizerContract) *Layout { - layout := container.NewAdaptiveGrid(2, widget.NewLabel(""), container.NewVScroll(queueLayoutObject.GetCanvasObject())) +func NewLayout(queueLayoutObject QueueLayoutObjectContract, localizerService LocalizerContract, rightTabsService RightTabsContract) *Layout { + layout := container.NewAdaptiveGrid(2, widget.NewLabel(""), queueLayoutObject.GetCanvasObject()) return &Layout{ layout: layout, queueLayoutObject: queueLayoutObject, localizerService: localizerService, + rightTabsService: rightTabsService, } } @@ -43,18 +41,16 @@ func (l Layout) SetContent(content fyne.CanvasObject) *fyne.Container { 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) { l.queueLayoutObject.ChangeQueueStatus(queueId, queue) } +func (l Layout) GetRightTabs() RightTabsContract { + return l.rightTabsService +} + type QueueLayoutObjectContract interface { GetCanvasObject() fyne.CanvasObject - GetProgressbar(queueId int) *widget.ProgressBar ChangeQueueStatus(queueId int, queue *Queue) } @@ -63,6 +59,7 @@ type QueueLayoutObject struct { queue QueueListContract container *fyne.Container + containerItems *fyne.Container items map[int]QueueLayoutItem localizerService LocalizerContract queueStatisticsFormat *queueStatisticsFormat @@ -70,16 +67,16 @@ type QueueLayoutObject struct { } type QueueLayoutItem struct { - CanvasObject fyne.CanvasObject - ProgressBar *widget.ProgressBar - StatusMessage *canvas.Text - MessageError *canvas.Text - buttonPlay *widget.Button + CanvasObject fyne.CanvasObject + BlockMessageError *container.Scroll + StatusMessage *canvas.Text + MessageError *canvas.Text + buttonPlay *widget.Button 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.TextStyle.Bold = true @@ -91,12 +88,27 @@ func NewQueueLayoutObject(queue QueueListContract, localizerService LocalizerCon items := map[int]QueueLayoutItem{} queueStatisticsFormat := newQueueStatisticsFormat(localizerService, &items) + line := canvas.NewLine(theme.Color(theme.ColorNameFocus)) + line.StrokeWidth = 5 + + rightTabsService.GetFileQueueContainer().Add(container.NewVBox( + container.NewPadded(), + container.NewHBox(title, queueStatisticsFormat.completed.widget, queueStatisticsFormat.error.widget), + container.NewHBox(queueStatisticsFormat.inProgress.widget, queueStatisticsFormat.waiting.widget, queueStatisticsFormat.total.widget), + container.NewPadded(), + line, + container.NewPadded(), + )) queueLayoutObject := &QueueLayoutObject{ queue: queue, - container: container.NewVBox( - container.NewHBox(title, queueStatisticsFormat.completed.widget, queueStatisticsFormat.error.widget), - container.NewHBox(queueStatisticsFormat.inProgress.widget, queueStatisticsFormat.waiting.widget, queueStatisticsFormat.total.widget), + container: container.NewBorder( + container.NewVBox( + blockProgressbar, + widget.NewSeparator(), + ), + nil, nil, nil, container.NewVScroll(rightTabsService.GetTabs()), ), + containerItems: rightTabsService.GetFileQueueContainer(), items: items, localizerService: localizerService, queueStatisticsFormat: queueStatisticsFormat, @@ -112,31 +124,24 @@ func (o QueueLayoutObject) GetCanvasObject() fyne.CanvasObject { 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) { - progressBar := widget.NewProgressBar() - statusMessage := canvas.NewText(o.getStatusTitle(queue.Status), theme.Color(theme.ColorNamePrimary)) messageError := canvas.NewText("", theme.Color(theme.ColorNameError)) buttonPlay := widget.NewButtonWithIcon("", theme.Icon(theme.IconNameMediaPlay), func() { }) buttonPlay.Hide() + blockMessageError := container.NewHScroll(messageError) + blockMessageError.Hide() content := container.NewVBox( container.NewHScroll(widget.NewLabel(queue.Setting.VideoFileInput.Name)), - progressBar, - container.NewHScroll(container.NewHBox( + container.NewHBox( buttonPlay, statusMessage, - )), - container.NewHScroll(messageError), + ), + blockMessageError, + container.NewPadded(), canvas.NewLine(theme.Color(theme.ColorNameFocus)), container.NewPadded(), ) @@ -147,14 +152,14 @@ func (o QueueLayoutObject) Add(id int, queue *Queue) { } o.items[id] = QueueLayoutItem{ - CanvasObject: content, - ProgressBar: progressBar, - StatusMessage: statusMessage, - MessageError: messageError, - buttonPlay: buttonPlay, - status: &queue.Status, + CanvasObject: content, + StatusMessage: statusMessage, + BlockMessageError: blockMessageError, + MessageError: messageError, + buttonPlay: buttonPlay, + status: &queue.Status, } - o.container.Add(content) + o.containerItems.Add(content) } 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.Color = statusColor fyne.Do(func() { + item.BlockMessageError.Show() item.MessageError.Refresh() }) } @@ -219,101 +225,6 @@ func (o QueueLayoutObject) getStatusTitle(status StatusContract) string { 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 { widget *widget.Check title string diff --git a/kernel/progressbar.go b/kernel/progressbar.go new file mode 100644 index 0000000..48b8485 --- /dev/null +++ b/kernel/progressbar.go @@ -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 +} diff --git a/kernel/right_tabs.go b/kernel/right_tabs.go new file mode 100644 index 0000000..16f8de1 --- /dev/null +++ b/kernel/right_tabs.go @@ -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) + }) +} diff --git a/languages/active.en.toml b/languages/active.en.toml index e89a35c..c5c158d 100644 --- a/languages/active.en.toml +++ b/languages/active.en.toml @@ -10,6 +10,14 @@ other = "About" hash = "sha1-8bd565814118ba8b90c40eb5b62acf8d2676e7d6" 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] hash = "sha1-c223c2e15171156192bc3146aee91e6094bb475b" other = "Download FFmpeg automatically" @@ -38,6 +46,10 @@ other = "Allow file to be overwritten" hash = "sha1-f60bb5f761024d973834b5e9d25ceebce2c85f94" other = "choose" +[clearAll] +hash = "sha1-f32702d79ac206432400ac6b041695d020f6fa77" +other = "Clear List" + [completedQueue] hash = "sha1-398c7d4f7b0d522afb930769c0fbb1a9f4b61fbe" other = "Completed" @@ -62,9 +74,9 @@ other = "Will be downloaded from the site:" hash = "sha1-55f87f114628fa2d5d8e67d1e1cda22c0e4f9271" other = "Downloading..." -[dragAndDrop1File] -hash = "sha1-7259670822df1cc92ef5f06ed3c0e9407746975a" -other = "drag and drop 1 file" +[dragAndDropFiles] +hash = "sha1-07bb747cc7590d7a51cdf96dff49a74139097766" +other = "drag and drop files" [encoderGroupAudio] hash = "sha1-24321cb5400df96be8f3e2131918bebdb3a01bba" @@ -226,9 +238,9 @@ other = "could not create file 'database' in folder 'data'" hash = "sha1-f8153516ac2442d19be4b6daccce839d204ff09f" other = "Could not open configuration file.\nMake sure another copy of the program is not running!" -[errorDragAndDrop1File] -hash = "sha1-a8edb5cbd622f3ce4ec07a2377e22ec5fad4491b" -other = "You can only drag and drop 1 file." +[errorDragAndDropFile] +hash = "sha1-863cf1ad9c820d5b0c2006ceeaa29e25f81c1714" +other = "Not all files were added" [errorFFmpeg] hash = "sha1-ccf0b95c0d1b392dc215258d917eb4e5d0b88ed0" @@ -254,9 +266,9 @@ other = "this is not FFprobe" hash = "sha1-da7b37d7df3fafbd153665b13888413d52b24c17" other = "Failed to determine FFprobe version" -[errorIsFolder] -hash = "sha1-f937d090b6e320957514d850657cdf2f911dc6aa" -other = "You can only drag and drop a file" +[errorNoFilesAddedForConversion] +hash = "sha1-5cf1f65bef15cb0382e56be98f44c6abde56a314" +other = "There are no files to convert" [errorQueue] hash = "sha1-72aecd9ad85642d84d62dbbf3cf70953c5f696c7" @@ -290,6 +302,10 @@ other = "**FFmpeg** is a trademark of **[Fabrice Bellard](http://bellard.org/)** hash = "sha1-96ac799e1086b31fd8f5f8d4c801829d6c853f08" other = "File:" +[fileQueueTitle] +hash = "sha1-aec93b16baeaf55fed871075c9494a460e4a91b8" +other = "Queue" + [formPreset] hash = "sha1-7759891ba1ef9f7adc70defc7ac18fbf149c1a68" other = "Preset" diff --git a/languages/active.kk.toml b/languages/active.kk.toml index 72e3e16..2074652 100644 --- a/languages/active.kk.toml +++ b/languages/active.kk.toml @@ -10,6 +10,14 @@ other = "Бағдарлама туралы" hash = "sha1-8bd565814118ba8b90c40eb5b62acf8d2676e7d6" other = "FFmpeg консоль утилитасы үшін қарапайым интерфейс. \nБірақ мен FFmpeg утилитасының авторы емеспін." +[addedFilesTitle] +hash = "sha1-8ba0f6e477b0d78df2cc06f1d8b41b888623b851" +other = "Қосылған файлдар" + +[autoClearAfterAddingToQueue] +hash = "sha1-b3781695a4c35380d2cd075bb52f27a2a6d8f19c" +other = "Кезекке қосқаннан кейін тазалаңыз" + [buttonDownloadFFmpeg] hash = "sha1-c223c2e15171156192bc3146aee91e6094bb475b" other = "FFmpeg автоматты түрде жүктеп алыңыз" @@ -38,6 +46,10 @@ other = "Файлды қайта жазуға рұқсат беріңіз" hash = "sha1-f60bb5f761024d973834b5e9d25ceebce2c85f94" other = "таңдау" +[clearAll] +hash = "sha1-f32702d79ac206432400ac6b041695d020f6fa77" +other = "Тізімді өшіру" + [completedQueue] hash = "sha1-398c7d4f7b0d522afb930769c0fbb1a9f4b61fbe" other = "Дайын" @@ -62,9 +74,9 @@ other = "Сайттан жүктеледі:" hash = "sha1-55f87f114628fa2d5d8e67d1e1cda22c0e4f9271" other = "Жүктеп алынуда..." -[dragAndDrop1File] -hash = "sha1-7259670822df1cc92ef5f06ed3c0e9407746975a" -other = "1 файлды сүйреңіз" +[dragAndDropFiles] +hash = "sha1-07bb747cc7590d7a51cdf96dff49a74139097766" +other = "файлдарды сүйреп апарыңыз" [encoderGroupAudio] hash = "sha1-24321cb5400df96be8f3e2131918bebdb3a01bba" @@ -226,9 +238,9 @@ other = "'data' қалтасында 'database' файлын жасау мүмк hash = "sha1-f8153516ac2442d19be4b6daccce839d204ff09f" other = "Конфигурация файлын аша алмады.\nБағдарламаның басқа көшірмесі іске қосылмағанына көз жеткізіңіз!" -[errorDragAndDrop1File] -hash = "sha1-a8edb5cbd622f3ce4ec07a2377e22ec5fad4491b" -other = "Тек 1 файлды сүйреп апаруға болады" +[errorDragAndDropFile] +hash = "sha1-863cf1ad9c820d5b0c2006ceeaa29e25f81c1714" +other = "Барлық файлдар қосылмаған" [errorFFmpeg] hash = "sha1-ccf0b95c0d1b392dc215258d917eb4e5d0b88ed0" @@ -254,9 +266,9 @@ other = "бұл FFprobe емес" hash = "sha1-da7b37d7df3fafbd153665b13888413d52b24c17" other = "FFprobe нұсқасын анықтау мүмкін болмады" -[errorIsFolder] -hash = "sha1-f937d090b6e320957514d850657cdf2f911dc6aa" -other = "Тек файлды сүйреп апаруға болады" +[errorNoFilesAddedForConversion] +hash = "sha1-5cf1f65bef15cb0382e56be98f44c6abde56a314" +other = "Түрлендіруге арналған файлдар жоқ" [errorQueue] hash = "sha1-72aecd9ad85642d84d62dbbf3cf70953c5f696c7" @@ -290,6 +302,10 @@ other = "FFmpeg — **[FFmpeg](https://ffmpeg.org/about.html)** жобасын hash = "sha1-96ac799e1086b31fd8f5f8d4c801829d6c853f08" other = "Файл:" +[fileQueueTitle] +hash = "sha1-aec93b16baeaf55fed871075c9494a460e4a91b8" +other = "Кезек" + [formPreset] hash = "sha1-7759891ba1ef9f7adc70defc7ac18fbf149c1a68" other = "Алдын ала орнатылған" diff --git a/languages/active.ru.toml b/languages/active.ru.toml index 638bde7..2383c00 100644 --- a/languages/active.ru.toml +++ b/languages/active.ru.toml @@ -1,6 +1,8 @@ AlsoUsedProgram = "Также в программе используется:" about = "О программе" aboutText = "Простенький интерфейс для консольной утилиты FFmpeg. \nНо я не являюсь автором самой утилиты FFmpeg." +addedFilesTitle = "Добавленные файлы" +autoClearAfterAddingToQueue = "Очищать после добавления в очередь" buttonDownloadFFmpeg = "Скачать автоматически FFmpeg" buttonForSelectedDirTitle = "Сохранить в папку:" cancel = "Отмена" @@ -8,13 +10,14 @@ changeFFPath = "FFmpeg, FFprobe и FFplay" changeLanguage = "Поменять язык" checkboxOverwriteOutputFilesTitle = "Разрешить перезаписать файл" choose = "выбрать" +clearAll = "Очистить список" completedQueue = "Готово" converterVideoFilesSubmitTitle = "Конвертировать" converterVideoFilesTitle = "Конвертер видео, аудио и картинок" download = "Скачать" downloadFFmpegFromSite = "Будет скачано с сайта:" downloadRun = "Скачивается..." -dragAndDrop1File = "перетащить 1 файл" +dragAndDropFiles = "перетащить файлы" encoderGroupAudio = "Аудио" encoderGroupImage = "Картинки" encoderGroupVideo = "Видео" @@ -55,14 +58,14 @@ error = "Произошла ошибка!" errorConverter = "не смогли отконвертировать видео" errorDatabase = "не смогли создать файл 'database' в папке 'data'" errorDatabaseTimeout = "Не смогли открыть файл конфигурации.\nУбедитесь, что другая копия программы не запущена!" -errorDragAndDrop1File = "Можно перетащить только 1 файл" +errorDragAndDropFile = "Не все файлы добавились" errorFFmpeg = "это не FFmpeg" errorFFmpegVersion = "Не смогли определить версию FFmpeg" errorFFplay = "это не FFplay" errorFFplayVersion = "Не смогли определить версию FFplay" errorFFprobe = "это не FFprobe" errorFFprobeVersion = "Не смогли определить версию FFprobe" -errorIsFolder = "Можно перетаскивать только файл" +errorNoFilesAddedForConversion = "Нет файлов для конвертации" errorQueue = "Ошибка" errorSelectedEncoder = "Конвертер не выбран" errorSelectedFolderSave = "Папка для сохранения не выбрана!" @@ -71,6 +74,7 @@ exit = "Выход" 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)**." fileForConversionTitle = "Файл:" +fileQueueTitle = "Очередь" formPreset = "Предустановка" gratitude = "Благодарность" gratitudeText = "Я искренне благодарю вас за неоценимую\n\rи своевременную помощь:" diff --git a/main.go b/main.go index 18d96ff..c681288 100644 --- a/main.go +++ b/main.go @@ -46,7 +46,7 @@ func init() { appMetadata, localizerService, queue, - kernel.NewQueueLayoutObject(queue, localizerService, ffplayService), + ffplayService, convertorService, ) } @@ -108,7 +108,12 @@ func main() { localizerView := localizer.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) themeService := theme.NewTheme(application, themeRepository)