From 1f9f646f51dcf89d83dc463258467d4e442866f1 Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Sun, 25 Feb 2024 23:38:16 +0600 Subject: [PATCH 1/6] Refactoring. type File struct and type ConvertSetting struct moved to convertor.go. --- kernel/convertor.go | 12 ++++++++++++ kernel/queue.go | 12 ------------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/kernel/convertor.go b/kernel/convertor.go index 8441fa1..0aa24e4 100644 --- a/kernel/convertor.go +++ b/kernel/convertor.go @@ -10,6 +10,18 @@ import ( "strings" ) +type File struct { + Path string + Name string + Ext string +} + +type ConvertSetting struct { + VideoFileInput File + VideoFileOut File + OverwriteOutputFiles bool +} + type ConvertorContract interface { RunConvert(setting ConvertSetting, progress ProgressContract) error GetTotalDuration(file *File) (float64, error) diff --git a/kernel/queue.go b/kernel/queue.go index d820fa6..112ce42 100644 --- a/kernel/queue.go +++ b/kernel/queue.go @@ -10,18 +10,6 @@ type Queue struct { Error error } -type File struct { - Path string - Name string - Ext string -} - -type ConvertSetting struct { - VideoFileInput File - VideoFileOut File - OverwriteOutputFiles bool -} - type StatusContract interface { name() string ordinal() int From e766c6d465bbe7d15b0fd96a5447c536fa2b0a40 Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Tue, 5 Mar 2024 00:58:43 +0500 Subject: [PATCH 2/6] Added the ability to show and hide elements by status type. --- kernel/layout.go | 311 ++++++++++++++++++++++++++++++++++-- kernel/queue.go | 8 +- languages/active.en.toml | 14 +- languages/active.kk.toml | 14 +- languages/active.ru.toml | 8 +- languages/translate.en.toml | 14 +- languages/translate.kk.toml | 14 +- 7 files changed, 341 insertions(+), 42 deletions(-) diff --git a/kernel/layout.go b/kernel/layout.go index 4b740d4..a917312 100644 --- a/kernel/layout.go +++ b/kernel/layout.go @@ -66,6 +66,7 @@ type QueueLayoutObject struct { items map[int]QueueLayoutItem localizerService LocalizerContract layoutLocalizerListener LayoutLocalizerListenerContract + queueStatisticsFormat *queueStatisticsFormat } type QueueLayoutItem struct { @@ -73,20 +74,32 @@ type QueueLayoutItem struct { ProgressBar *widget.ProgressBar StatusMessage *canvas.Text MessageError *canvas.Text + + status *StatusContract } func NewQueueLayoutObject(queue QueueListContract, localizerService LocalizerContract, layoutLocalizerListener LayoutLocalizerListenerContract) *QueueLayoutObject { - title := widget.NewLabel(localizerService.GetMessage(&i18n.LocalizeConfig{MessageID: "queue"}) + ":") + title := widget.NewLabel(localizerService.GetMessage(&i18n.LocalizeConfig{MessageID: "queue"})) title.TextStyle.Bold = true - layoutLocalizerListener.AddItem("queue", title) + layoutLocalizerListener.AddItem("queue", func(text string) { + title.Text = text + title.Refresh() + }) + + items := map[int]QueueLayoutItem{} + queueStatisticsFormat := newQueueStatisticsFormat(localizerService, layoutLocalizerListener, &items) queueLayoutObject := &QueueLayoutObject{ - queue: queue, - container: container.NewVBox(title), - items: map[int]QueueLayoutItem{}, + 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), + ), + items: items, localizerService: localizerService, layoutLocalizerListener: layoutLocalizerListener, + queueStatisticsFormat: queueStatisticsFormat, } queue.AddListener(queueLayoutObject) @@ -118,11 +131,18 @@ func (o QueueLayoutObject) Add(id int, queue *Queue) { canvas.NewLine(theme.FocusColor()), container.NewPadded(), ) + + o.queueStatisticsFormat.addQueue() + if o.queueStatisticsFormat.isChecked(queue.Status) == false { + content.Hide() + } + o.items[id] = QueueLayoutItem{ CanvasObject: content, ProgressBar: progressBar, StatusMessage: statusMessage, MessageError: messageError, + status: &queue.Status, } o.container.Add(content) } @@ -130,6 +150,7 @@ func (o QueueLayoutObject) Add(id int, queue *Queue) { func (o QueueLayoutObject) Remove(id int) { if item, ok := o.items[id]; ok { o.container.Remove(item.CanvasObject) + o.queueStatisticsFormat.removeQueue(*item.status) o.items[id] = QueueLayoutItem{} } } @@ -145,6 +166,12 @@ func (o QueueLayoutObject) ChangeQueueStatus(queueId int, queue *Queue) { item.MessageError.Color = statusColor item.MessageError.Refresh() } + if o.queueStatisticsFormat.isChecked(queue.Status) == false && item.CanvasObject.Visible() == true { + item.CanvasObject.Hide() + } else if item.CanvasObject.Visible() == false { + item.CanvasObject.Show() + } + o.queueStatisticsFormat.changeQueue(queue.Status) } } @@ -161,7 +188,7 @@ func (o QueueLayoutObject) getStatusColor(status StatusContract) color.Color { } func (o QueueLayoutObject) getStatusTitle(status StatusContract) string { - return o.localizerService.GetMessage(&i18n.LocalizeConfig{MessageID: status.name()}) + return o.localizerService.GetMessage(&i18n.LocalizeConfig{MessageID: status.Name() + "Queue"}) } type Progress struct { @@ -256,33 +283,287 @@ func (p Progress) Run(stdOut io.ReadCloser, stdErr io.ReadCloser) error { type LayoutLocalizerItem struct { messageID string - object *widget.Label + callback func(text string) } type LayoutLocalizerListener struct { - itemCurrentId int + itemCurrentId *int items map[int]*LayoutLocalizerItem } type LayoutLocalizerListenerContract interface { - AddItem(messageID string, object *widget.Label) + AddItem(messageID string, callback func(text string)) } func NewLayoutLocalizerListener() *LayoutLocalizerListener { + id := 0 return &LayoutLocalizerListener{ - itemCurrentId: 0, + itemCurrentId: &id, items: map[int]*LayoutLocalizerItem{}, } } -func (l LayoutLocalizerListener) AddItem(messageID string, object *widget.Label) { - l.itemCurrentId += 1 - l.items[l.itemCurrentId] = &LayoutLocalizerItem{messageID: messageID, object: object} +func (l LayoutLocalizerListener) AddItem(messageID string, callback func(text string)) { + *l.itemCurrentId += 1 + l.items[*l.itemCurrentId] = &LayoutLocalizerItem{messageID: messageID, callback: callback} } func (l LayoutLocalizerListener) Change(localizerService LocalizerContract) { for _, item := range l.items { - item.object.Text = localizerService.GetMessage(&i18n.LocalizeConfig{MessageID: item.messageID}) - item.object.Refresh() + item.callback(localizerService.GetMessage(&i18n.LocalizeConfig{MessageID: item.messageID})) + } +} + +type queueStatistics struct { + widget *widget.Check + title string + count *int64 +} +type queueStatisticsFormat struct { + waiting *queueStatistics + inProgress *queueStatistics + completed *queueStatistics + error *queueStatistics + total *queueStatistics +} + +func newQueueStatisticsFormat(localizerService LocalizerContract, layoutLocalizerListener LayoutLocalizerListenerContract, queueItems *map[int]QueueLayoutItem) *queueStatisticsFormat { + checkWaiting := newQueueStatistics("waitingQueue", localizerService, layoutLocalizerListener) + checkInProgress := newQueueStatistics("inProgressQueue", localizerService, layoutLocalizerListener) + checkCompleted := newQueueStatistics("completedQueue", localizerService, layoutLocalizerListener) + checkError := newQueueStatistics("errorQueue", localizerService, layoutLocalizerListener) + checkTotal := newQueueStatistics("total", localizerService, layoutLocalizerListener) + + queueStatisticsFormat := &queueStatisticsFormat{ + waiting: checkWaiting, + inProgress: checkInProgress, + completed: checkCompleted, + error: checkError, + total: checkTotal, + } + + checkTotal.widget.OnChanged = func(b bool) { + if b == true { + queueStatisticsFormat.allCheckboxChecked() + } else { + queueStatisticsFormat.allUnCheckboxChecked() + } + queueStatisticsFormat.redrawingQueueItems(queueItems) + } + + queueStatisticsFormat.waiting.widget.OnChanged = func(b bool) { + if b == true { + queueStatisticsFormat.checkboxChecked() + } else { + queueStatisticsFormat.unCheckboxChecked() + } + queueStatisticsFormat.redrawingQueueItems(queueItems) + } + + queueStatisticsFormat.inProgress.widget.OnChanged = func(b bool) { + if b == true { + queueStatisticsFormat.checkboxChecked() + } else { + queueStatisticsFormat.unCheckboxChecked() + } + queueStatisticsFormat.redrawingQueueItems(queueItems) + } + + queueStatisticsFormat.completed.widget.OnChanged = func(b bool) { + if b == true { + queueStatisticsFormat.checkboxChecked() + } else { + queueStatisticsFormat.unCheckboxChecked() + } + queueStatisticsFormat.redrawingQueueItems(queueItems) + } + + queueStatisticsFormat.error.widget.OnChanged = func(b bool) { + if b == true { + queueStatisticsFormat.checkboxChecked() + } else { + queueStatisticsFormat.unCheckboxChecked() + } + queueStatisticsFormat.redrawingQueueItems(queueItems) + } + + return queueStatisticsFormat +} + +func (f queueStatisticsFormat) redrawingQueueItems(queueItems *map[int]QueueLayoutItem) { + for _, item := range *queueItems { + if f.isChecked(*item.status) == true && item.CanvasObject.Visible() == false { + item.CanvasObject.Show() + continue + } + if f.isChecked(*item.status) == false && item.CanvasObject.Visible() == true { + item.CanvasObject.Hide() + } + } +} + +func (f queueStatisticsFormat) isChecked(status StatusContract) bool { + if status == StatusType(InProgress) { + return f.inProgress.widget.Checked + } + if status == StatusType(Completed) { + return f.completed.widget.Checked + } + if status == StatusType(Error) { + return f.error.widget.Checked + } + if status == StatusType(Waiting) { + return f.waiting.widget.Checked + } + + return true +} + +func (f queueStatisticsFormat) addQueue() { + f.waiting.add() + f.total.add() +} + +func (f queueStatisticsFormat) changeQueue(status StatusContract) { + if status == StatusType(InProgress) { + f.waiting.remove() + f.inProgress.add() + return + } + + if status == StatusType(Completed) { + f.inProgress.remove() + f.completed.add() + return + } + + if status == StatusType(Error) { + f.inProgress.remove() + f.error.add() + return + } +} + +func (f queueStatisticsFormat) removeQueue(status StatusContract) { + f.total.remove() + + if status == StatusType(Completed) { + f.completed.remove() + return + } + + if status == StatusType(Error) { + f.error.remove() + return + } + + if status == StatusType(InProgress) { + f.inProgress.remove() + return + } + + if status == StatusType(Waiting) { + f.waiting.remove() + return + } +} + +func (f queueStatisticsFormat) checkboxChecked() { + if f.total.widget.Checked == true { + return + } + + if f.waiting.widget.Checked == false { + return + } + + if f.inProgress.widget.Checked == false { + return + } + + if f.completed.widget.Checked == false { + return + } + + if f.error.widget.Checked == false { + return + } + + f.total.widget.Checked = true + f.total.widget.Refresh() +} + +func (f queueStatisticsFormat) unCheckboxChecked() { + if f.total.widget.Checked == false { + return + } + + f.total.widget.Checked = false + f.total.widget.Refresh() +} + +func (f queueStatisticsFormat) allCheckboxChecked() { + f.waiting.widget.Checked = true + f.waiting.widget.Refresh() + f.inProgress.widget.Checked = true + f.inProgress.widget.Refresh() + f.completed.widget.Checked = true + f.completed.widget.Refresh() + f.error.widget.Checked = true + f.error.widget.Refresh() +} + +func (f queueStatisticsFormat) allUnCheckboxChecked() { + f.waiting.widget.Checked = false + f.waiting.widget.Refresh() + f.inProgress.widget.Checked = false + f.inProgress.widget.Refresh() + f.completed.widget.Checked = false + f.completed.widget.Refresh() + f.error.widget.Checked = false + f.error.widget.Refresh() +} + +func newQueueStatistics(messaigeID string, localizerService LocalizerContract, layoutLocalizerListener LayoutLocalizerListenerContract) *queueStatistics { + checkbox := widget.NewCheck("", nil) + checkbox.Checked = true + + count := int64(0) + + title := localizerService.GetMessage(&i18n.LocalizeConfig{MessageID: messaigeID}) + queueStatistics := &queueStatistics{ + widget: checkbox, + title: strings.ToLower(title), + count: &count, + } + + queueStatistics.formatText(false) + + layoutLocalizerListener.AddItem(messaigeID, func(text string) { + queueStatistics.title = strings.ToLower(text) + queueStatistics.formatText(true) + queueStatistics.widget.Refresh() + }) + + return queueStatistics +} + +func (s queueStatistics) add() { + *s.count += 1 + s.formatText(true) +} + +func (s queueStatistics) remove() { + if *s.count == 0 { + return + } + *s.count -= 1 + s.formatText(true) +} + +func (s queueStatistics) formatText(refresh bool) { + s.widget.Text = s.title + ": " + strconv.FormatInt(*s.count, 10) + if refresh == true { + s.widget.Refresh() } } diff --git a/kernel/queue.go b/kernel/queue.go index 112ce42..6d2f6c4 100644 --- a/kernel/queue.go +++ b/kernel/queue.go @@ -11,8 +11,8 @@ type Queue struct { } type StatusContract interface { - name() string - ordinal() int + Name() string + Ordinal() int } const ( @@ -31,11 +31,11 @@ var statusTypeStrings = []string{ "error", } -func (status StatusType) name() string { +func (status StatusType) Name() string { return statusTypeStrings[status] } -func (status StatusType) ordinal() int { +func (status StatusType) Ordinal() int { return int(status) } diff --git a/languages/active.en.toml b/languages/active.en.toml index 499b140..357f1c0 100644 --- a/languages/active.en.toml +++ b/languages/active.en.toml @@ -38,7 +38,7 @@ other = "Allow file to be overwritten" hash = "sha1-f60bb5f761024d973834b5e9d25ceebce2c85f94" other = "choose" -[completed] +[completedQueue] hash = "sha1-398c7d4f7b0d522afb930769c0fbb1a9f4b61fbe" other = "Completed" @@ -90,6 +90,10 @@ other = "this is not FFprobe" hash = "sha1-da7b37d7df3fafbd153665b13888413d52b24c17" other = "Failed to determine FFprobe version" +[errorQueue] +hash = "sha1-72aecd9ad85642d84d62dbbf3cf70953c5f696c7" +other = "Error" + [errorSelectedFolderSave] hash = "sha1-83da899677cdc90e4344e3b94ee03c46b51bee4c" other = "You haven't selected a folder to save!" @@ -114,7 +118,7 @@ other = "File for conversion:" hash = "sha1-6a45cef900c668effcb2ab10da05855c1fd10f6f" other = "Help" -[inProgress] +[inProgressQueue] hash = "sha1-eff79c40e2100ae5fadf3a7d99336025edcca8b5" other = "In Progress" @@ -174,10 +178,14 @@ other = "Checking FFmpeg for serviceability..." hash = "sha1-92df86371f6c3a06ca1e4754f113142776a32d49" other = "You can download it from here" +[total] +hash = "sha1-3b5143902e0c5c84459aedf918e17604d9735b94" +other = "Total" + [unzipRun] hash = "sha1-c554dad13026668a1f6adf3171837c5d51bbac36" other = "Unpacked..." -[waiting] +[waitingQueue] hash = "sha1-307429dd84150877080c4bbff2b340d1e7dadff2" other = "Waiting" diff --git a/languages/active.kk.toml b/languages/active.kk.toml index d443d8a..0b2e21b 100644 --- a/languages/active.kk.toml +++ b/languages/active.kk.toml @@ -38,7 +38,7 @@ other = "Файлды қайта жазуға рұқсат беріңіз" hash = "sha1-f60bb5f761024d973834b5e9d25ceebce2c85f94" other = "таңдау" -[completed] +[completedQueue] hash = "sha1-398c7d4f7b0d522afb930769c0fbb1a9f4b61fbe" other = "Дайын" @@ -90,6 +90,10 @@ other = "бұл FFprobe емес" hash = "sha1-da7b37d7df3fafbd153665b13888413d52b24c17" other = "FFprobe нұсқасын анықтау мүмкін болмады" +[errorQueue] +hash = "sha1-72aecd9ad85642d84d62dbbf3cf70953c5f696c7" +other = "Қате" + [errorSelectedFolderSave] hash = "sha1-83da899677cdc90e4344e3b94ee03c46b51bee4c" other = "Сіз сақталатын қалтаны таңдамадыңыз!" @@ -114,7 +118,7 @@ other = "Түрлендіруге арналған файл:" hash = "sha1-6a45cef900c668effcb2ab10da05855c1fd10f6f" other = "Анықтама" -[inProgress] +[inProgressQueue] hash = "sha1-eff79c40e2100ae5fadf3a7d99336025edcca8b5" other = "Орындалуда" @@ -174,10 +178,14 @@ other = "FFmpeg функционалдығы тексерілуде..." hash = "sha1-92df86371f6c3a06ca1e4754f113142776a32d49" other = "Сіз оны осы жерден жүктей аласыз" +[total] +hash = "sha1-3b5143902e0c5c84459aedf918e17604d9735b94" +other = "Барлығы" + [unzipRun] hash = "sha1-c554dad13026668a1f6adf3171837c5d51bbac36" other = "Орамнан шығарылуда..." -[waiting] +[waitingQueue] hash = "sha1-307429dd84150877080c4bbff2b340d1e7dadff2" other = "Күту" diff --git a/languages/active.ru.toml b/languages/active.ru.toml index b163e1b..6053ac2 100644 --- a/languages/active.ru.toml +++ b/languages/active.ru.toml @@ -8,7 +8,7 @@ changeFFPath = "FFmpeg и FFprobe" changeLanguage = "Поменять язык" checkboxOverwriteOutputFilesTitle = "Разрешить перезаписать файл" choose = "выбрать" -completed = "Готово" +completedQueue = "Готово" converterVideoFilesSubmitTitle = "Конвертировать" converterVideoFilesTitle = "Конвертор видео файлов в mp4" download = "Скачать" @@ -21,13 +21,14 @@ errorFFmpeg = "это не FFmpeg" errorFFmpegVersion = "Не смогли определить версию FFmpeg" errorFFprobe = "это не FFprobe" errorFFprobeVersion = "Не смогли определить версию FFprobe" +errorQueue = "Ошибка" errorSelectedFolderSave = "Не выбрали папку для сохранения!" 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)**." fileVideoForConversionTitle = "Файл для ковертации:" help = "Справка" -inProgress = "Выполняется" +inProgressQueue = "Выполняется" languageSelectionFormHead = "Переключить язык" languageSelectionHead = "Выберите язык" licenseLink = "Сведения о лицензии" @@ -42,5 +43,6 @@ selectFFPathTitle = "Укажите путь к FFmpeg и к FFprobe" settings = "Настройки" testFF = "Проверка FFmpeg на работоспособность..." titleDownloadLink = "Скачать можно от сюда" +total = "Всего" unzipRun = "Распаковывается..." -waiting = "В очереди" +waitingQueue = "В очереди" diff --git a/languages/translate.en.toml b/languages/translate.en.toml index acb16ac..5d5b71c 100644 --- a/languages/translate.en.toml +++ b/languages/translate.en.toml @@ -1,15 +1,15 @@ -[completed] +[completedQueue] hash = "sha1-398c7d4f7b0d522afb930769c0fbb1a9f4b61fbe" other = "Completed" -[inProgress] +[errorQueue] +hash = "sha1-72aecd9ad85642d84d62dbbf3cf70953c5f696c7" +other = "Error" + +[inProgressQueue] hash = "sha1-eff79c40e2100ae5fadf3a7d99336025edcca8b5" other = "In Progress" -[queue] -hash = "sha1-aec93b16baeaf55fed871075c9494a460e4a91b8" -other = "Queue" - -[waiting] +[waitingQueue] hash = "sha1-307429dd84150877080c4bbff2b340d1e7dadff2" other = "Waiting" diff --git a/languages/translate.kk.toml b/languages/translate.kk.toml index 93b8581..24640fa 100644 --- a/languages/translate.kk.toml +++ b/languages/translate.kk.toml @@ -1,15 +1,15 @@ -[completed] +[completedQueue] hash = "sha1-398c7d4f7b0d522afb930769c0fbb1a9f4b61fbe" other = "Дайын" -[inProgress] +[errorQueue] +hash = "sha1-72aecd9ad85642d84d62dbbf3cf70953c5f696c7" +other = "Қате" + +[inProgressQueue] hash = "sha1-eff79c40e2100ae5fadf3a7d99336025edcca8b5" other = "Орындалуда" -[queue] -hash = "sha1-aec93b16baeaf55fed871075c9494a460e4a91b8" -other = "Кезек" - -[waiting] +[waitingQueue] hash = "sha1-307429dd84150877080c4bbff2b340d1e7dadff2" other = "Күту" From 1eb7ea4a93c2b088c2e756a4ca3704322a3729d7 Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Tue, 5 Mar 2024 20:13:58 +0500 Subject: [PATCH 3/6] Refactoring LocalizerContract. Instead of AddListener I made AddChangeCallback. And removed unnecessary dependencies. --- handler/menu.go | 67 +++++++++++++---------------------------- kernel/layout.go | 73 ++++++++++++--------------------------------- kernel/localizer.go | 30 ++++++++++--------- main.go | 8 ++--- 4 files changed, 57 insertions(+), 121 deletions(-) diff --git a/handler/menu.go b/handler/menu.go index ca38d52..5ec2ebd 100644 --- a/handler/menu.go +++ b/handler/menu.go @@ -19,7 +19,6 @@ type MenuHandler struct { menuView menu.ViewContract localizerView localizer.ViewContract localizerRepository localizer.RepositoryContract - localizerListener localizerListenerContract } func NewMenuHandler( @@ -28,7 +27,6 @@ func NewMenuHandler( menuView menu.ViewContract, localizerView localizer.ViewContract, localizerRepository localizer.RepositoryContract, - localizerListener localizerListenerContract, ) *MenuHandler { return &MenuHandler{ app: app, @@ -36,7 +34,6 @@ func NewMenuHandler( menuView: menuView, localizerView: localizerView, localizerRepository: localizerRepository, - localizerListener: localizerListener, } } @@ -52,22 +49,31 @@ func (h MenuHandler) getMenuSettings() *fyne.Menu { MessageID: "exit", }), nil) quit.IsQuit = true - h.localizerListener.AddMenuItem("exit", quit) + h.app.GetLocalizerService().AddChangeCallback("exit", func(text string) { + quit.Label = text + }) languageSelection := fyne.NewMenuItem(h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ MessageID: "changeLanguage", }), h.LanguageSelection) - h.localizerListener.AddMenuItem("changeLanguage", languageSelection) + h.app.GetLocalizerService().AddChangeCallback("changeLanguage", func(text string) { + languageSelection.Label = text + }) ffPathSelection := fyne.NewMenuItem(h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ MessageID: "changeFFPath", }), h.convertorHandler.FfPathSelection) - h.localizerListener.AddMenuItem("changeFFPath", ffPathSelection) + h.app.GetLocalizerService().AddChangeCallback("changeFFPath", func(text string) { + ffPathSelection.Label = text + }) settings := fyne.NewMenu(h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ MessageID: "settings", }), languageSelection, ffPathSelection, quit) - h.localizerListener.AddMenu("settings", settings) + h.app.GetLocalizerService().AddChangeCallback("settings", func(text string) { + settings.Label = text + settings.Refresh() + }) return settings } @@ -76,12 +82,17 @@ func (h MenuHandler) getMenuHelp() *fyne.Menu { about := fyne.NewMenuItem(h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ MessageID: "about", }), h.openAbout) - h.localizerListener.AddMenuItem("about", about) + h.app.GetLocalizerService().AddChangeCallback("about", func(text string) { + about.Label = text + }) help := fyne.NewMenu(h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ MessageID: "help", }), about) - h.localizerListener.AddMenu("help", help) + h.app.GetLocalizerService().AddChangeCallback("help", func(text string) { + help.Label = text + help.Refresh() + }) return help } @@ -109,41 +120,3 @@ func (h MenuHandler) LanguageSelection() { h.convertorHandler.MainConvertor() }) } - -type menuItems struct { - menuItem map[string]*fyne.MenuItem - menu map[string]*fyne.Menu -} - -type LocalizerListener struct { - menuItems *menuItems -} - -type localizerListenerContract interface { - AddMenu(messageID string, menu *fyne.Menu) - AddMenuItem(messageID string, menuItem *fyne.MenuItem) -} - -func NewLocalizerListener() *LocalizerListener { - return &LocalizerListener{ - &menuItems{menuItem: map[string]*fyne.MenuItem{}, menu: map[string]*fyne.Menu{}}, - } -} - -func (l LocalizerListener) AddMenu(messageID string, menu *fyne.Menu) { - l.menuItems.menu[messageID] = menu -} - -func (l LocalizerListener) AddMenuItem(messageID string, menuItem *fyne.MenuItem) { - l.menuItems.menuItem[messageID] = menuItem -} - -func (l LocalizerListener) Change(localizerService kernel.LocalizerContract) { - for messageID, menu := range l.menuItems.menuItem { - menu.Label = localizerService.GetMessage(&i18n.LocalizeConfig{MessageID: messageID}) - } - for messageID, menu := range l.menuItems.menu { - menu.Label = localizerService.GetMessage(&i18n.LocalizeConfig{MessageID: messageID}) - menu.Refresh() - } -} diff --git a/kernel/layout.go b/kernel/layout.go index a917312..db691cf 100644 --- a/kernel/layout.go +++ b/kernel/layout.go @@ -61,12 +61,11 @@ type QueueLayoutObjectContract interface { type QueueLayoutObject struct { QueueListContract QueueListContract - queue QueueListContract - container *fyne.Container - items map[int]QueueLayoutItem - localizerService LocalizerContract - layoutLocalizerListener LayoutLocalizerListenerContract - queueStatisticsFormat *queueStatisticsFormat + queue QueueListContract + container *fyne.Container + items map[int]QueueLayoutItem + localizerService LocalizerContract + queueStatisticsFormat *queueStatisticsFormat } type QueueLayoutItem struct { @@ -78,17 +77,17 @@ type QueueLayoutItem struct { status *StatusContract } -func NewQueueLayoutObject(queue QueueListContract, localizerService LocalizerContract, layoutLocalizerListener LayoutLocalizerListenerContract) *QueueLayoutObject { +func NewQueueLayoutObject(queue QueueListContract, localizerService LocalizerContract) *QueueLayoutObject { title := widget.NewLabel(localizerService.GetMessage(&i18n.LocalizeConfig{MessageID: "queue"})) title.TextStyle.Bold = true - layoutLocalizerListener.AddItem("queue", func(text string) { + localizerService.AddChangeCallback("queue", func(text string) { title.Text = text title.Refresh() }) items := map[int]QueueLayoutItem{} - queueStatisticsFormat := newQueueStatisticsFormat(localizerService, layoutLocalizerListener, &items) + queueStatisticsFormat := newQueueStatisticsFormat(localizerService, &items) queueLayoutObject := &QueueLayoutObject{ queue: queue, @@ -96,10 +95,9 @@ func NewQueueLayoutObject(queue QueueListContract, localizerService LocalizerCon container.NewHBox(title, queueStatisticsFormat.completed.widget, queueStatisticsFormat.error.widget), container.NewHBox(queueStatisticsFormat.inProgress.widget, queueStatisticsFormat.waiting.widget, queueStatisticsFormat.total.widget), ), - items: items, - localizerService: localizerService, - layoutLocalizerListener: layoutLocalizerListener, - queueStatisticsFormat: queueStatisticsFormat, + items: items, + localizerService: localizerService, + queueStatisticsFormat: queueStatisticsFormat, } queue.AddListener(queueLayoutObject) @@ -281,39 +279,6 @@ func (p Progress) Run(stdOut io.ReadCloser, stdErr io.ReadCloser) error { return nil } -type LayoutLocalizerItem struct { - messageID string - callback func(text string) -} - -type LayoutLocalizerListener struct { - itemCurrentId *int - items map[int]*LayoutLocalizerItem -} - -type LayoutLocalizerListenerContract interface { - AddItem(messageID string, callback func(text string)) -} - -func NewLayoutLocalizerListener() *LayoutLocalizerListener { - id := 0 - return &LayoutLocalizerListener{ - itemCurrentId: &id, - items: map[int]*LayoutLocalizerItem{}, - } -} - -func (l LayoutLocalizerListener) AddItem(messageID string, callback func(text string)) { - *l.itemCurrentId += 1 - l.items[*l.itemCurrentId] = &LayoutLocalizerItem{messageID: messageID, callback: callback} -} - -func (l LayoutLocalizerListener) Change(localizerService LocalizerContract) { - for _, item := range l.items { - item.callback(localizerService.GetMessage(&i18n.LocalizeConfig{MessageID: item.messageID})) - } -} - type queueStatistics struct { widget *widget.Check title string @@ -327,12 +292,12 @@ type queueStatisticsFormat struct { total *queueStatistics } -func newQueueStatisticsFormat(localizerService LocalizerContract, layoutLocalizerListener LayoutLocalizerListenerContract, queueItems *map[int]QueueLayoutItem) *queueStatisticsFormat { - checkWaiting := newQueueStatistics("waitingQueue", localizerService, layoutLocalizerListener) - checkInProgress := newQueueStatistics("inProgressQueue", localizerService, layoutLocalizerListener) - checkCompleted := newQueueStatistics("completedQueue", localizerService, layoutLocalizerListener) - checkError := newQueueStatistics("errorQueue", localizerService, layoutLocalizerListener) - checkTotal := newQueueStatistics("total", localizerService, layoutLocalizerListener) +func newQueueStatisticsFormat(localizerService LocalizerContract, queueItems *map[int]QueueLayoutItem) *queueStatisticsFormat { + checkWaiting := newQueueStatistics("waitingQueue", localizerService) + checkInProgress := newQueueStatistics("inProgressQueue", localizerService) + checkCompleted := newQueueStatistics("completedQueue", localizerService) + checkError := newQueueStatistics("errorQueue", localizerService) + checkTotal := newQueueStatistics("total", localizerService) queueStatisticsFormat := &queueStatisticsFormat{ waiting: checkWaiting, @@ -524,7 +489,7 @@ func (f queueStatisticsFormat) allUnCheckboxChecked() { f.error.widget.Refresh() } -func newQueueStatistics(messaigeID string, localizerService LocalizerContract, layoutLocalizerListener LayoutLocalizerListenerContract) *queueStatistics { +func newQueueStatistics(messaigeID string, localizerService LocalizerContract) *queueStatistics { checkbox := widget.NewCheck("", nil) checkbox.Checked = true @@ -539,7 +504,7 @@ func newQueueStatistics(messaigeID string, localizerService LocalizerContract, l queueStatistics.formatText(false) - layoutLocalizerListener.AddItem(messaigeID, func(text string) { + localizerService.AddChangeCallback(messaigeID, func(text string) { queueStatistics.title = strings.ToLower(text) queueStatistics.formatText(true) queueStatistics.widget.Refresh() diff --git a/kernel/localizer.go b/kernel/localizer.go index e002125..3823c7d 100644 --- a/kernel/localizer.go +++ b/kernel/localizer.go @@ -16,11 +16,7 @@ type LocalizerContract interface { SetCurrentLanguage(lang Lang) error SetCurrentLanguageByCode(code string) error GetCurrentLanguage() *CurrentLanguage - AddListener(listener LocalizerListenerContract) -} - -type LocalizerListenerContract interface { - Change(localizerService LocalizerContract) + AddChangeCallback(messageID string, callback func(text string)) } type Lang struct { @@ -34,11 +30,16 @@ type CurrentLanguage struct { localizerDefault *i18n.Localizer } +type changeCallback struct { + messageID string + callback func(text string) +} + type Localizer struct { - bundle *i18n.Bundle - languages []Lang - currentLanguage *CurrentLanguage - localizerListener map[int]LocalizerListenerContract + bundle *i18n.Bundle + languages []Lang + currentLanguage *CurrentLanguage + changeCallbacks map[int]*changeCallback } func NewLocalizer(directory string, languageDefault language.Tag) (*Localizer, error) { @@ -63,7 +64,7 @@ func NewLocalizer(directory string, languageDefault language.Tag) (*Localizer, e localizer: localizerDefault, localizerDefault: localizerDefault, }, - localizerListener: map[int]LocalizerListenerContract{}, + changeCallbacks: map[int]*changeCallback{}, }, nil } @@ -123,13 +124,14 @@ func (l Localizer) GetCurrentLanguage() *CurrentLanguage { return l.currentLanguage } -func (l Localizer) AddListener(listener LocalizerListenerContract) { - l.localizerListener[len(l.localizerListener)] = listener +func (l Localizer) AddChangeCallback(messageID string, callback func(text string)) { + l.changeCallbacks[len(l.changeCallbacks)] = &changeCallback{messageID: messageID, callback: callback} } func (l Localizer) eventSetCurrentLanguage() { - for _, listener := range l.localizerListener { - listener.Change(l) + for _, changeCallback := range l.changeCallbacks { + text := l.GetMessage(&i18n.LocalizeConfig{MessageID: changeCallback.messageID}) + changeCallback.callback(text) } } diff --git a/main.go b/main.go index 5b98be8..0c21671 100644 --- a/main.go +++ b/main.go @@ -37,15 +37,13 @@ func init() { ffPathUtilities = &kernel.FFPathUtilities{FFmpeg: "", FFprobe: ""} convertorService := kernel.NewService(ffPathUtilities) - layoutLocalizerListener := kernel.NewLayoutLocalizerListener() - localizerService.AddListener(layoutLocalizerListener) queue := kernel.NewQueueList() application = kernel.NewApp( appMetadata, localizerService, queue, - kernel.NewQueueLayoutObject(queue, localizerService, layoutLocalizerListener), + kernel.NewQueueLayoutObject(queue, localizerService), convertorService, ) } @@ -101,9 +99,7 @@ func main() { localizerRepository := localizer.NewRepository(settingRepository) menuView := menu.NewView(application) - localizerListener := handler.NewLocalizerListener() - application.GetLocalizerService().AddListener(localizerListener) - mainMenu := handler.NewMenuHandler(application, convertorHandler, menuView, localizerView, localizerRepository, localizerListener) + mainMenu := handler.NewMenuHandler(application, convertorHandler, menuView, localizerView, localizerRepository) mainHandler := handler.NewMainHandler(application, convertorHandler, mainMenu, localizerRepository) mainHandler.Start() From 1ece1e443d7e2d1a8422291dcdd820ba8d5b00bb Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Thu, 7 Mar 2024 22:18:35 +0500 Subject: [PATCH 4/6] Added the ability to convert files to different extensions. --- convertor/view.go | 97 ++++++++++++++++++- encoder/apng/encoder.go | 21 +++++ encoder/bmp/encoder.go | 21 +++++ encoder/encoder.go | 79 ++++++++++++++++ encoder/flv/encoder.go | 21 +++++ encoder/gif/encoder.go | 21 +++++ encoder/h264_nvenc/encoder.go | 23 +++++ encoder/libmp3lame/encoder.go | 21 +++++ encoder/libshine/encoder.go | 21 +++++ encoder/libtwolame/encoder.go | 21 +++++ encoder/libvpx/encoder.go | 21 +++++ encoder/libvpx_vp9/encoder.go | 21 +++++ encoder/libwebp/encoder.go | 21 +++++ encoder/libwebp_anim/encoder.go | 21 +++++ encoder/libx264/encoder.go | 21 +++++ encoder/libx265/encoder.go | 21 +++++ encoder/libxvid/encoder.go | 21 +++++ encoder/mjpeg/encoder.go | 21 +++++ encoder/mp2/encoder.go | 21 +++++ encoder/mp2fixed/encoder.go | 21 +++++ encoder/mpeg1video/encoder.go | 21 +++++ encoder/mpeg2video/encoder.go | 21 +++++ encoder/mpeg4/encoder.go | 21 +++++ encoder/msmpeg4/encoder.go | 21 +++++ encoder/msmpeg4v2/encoder.go | 21 +++++ encoder/msvideo1/encoder.go | 21 +++++ encoder/png/encoder.go | 21 +++++ encoder/qtrle/encoder.go | 21 +++++ encoder/sgi/encoder.go | 21 +++++ encoder/tiff/encoder.go | 21 +++++ encoder/wmav1/encoder.go | 21 +++++ encoder/wmav2/encoder.go | 21 +++++ encoder/wmv1/encoder.go | 21 +++++ encoder/wmv2/encoder.go | 21 +++++ encoder/xbm/encoder.go | 21 +++++ handler/convertor.go | 16 +++- kernel/convertor.go | 73 +++++++++++++- kernel/encoder/encoder.go | 84 +++++++++++++++++ kernel/encoder/encoders.go | 74 +++++++++++++++ languages/active.en.toml | 162 ++++++++++++++++++++++++++++++-- languages/active.kk.toml | 160 +++++++++++++++++++++++++++++-- languages/active.ru.toml | 43 ++++++++- languages/translate.en.toml | 148 ++++++++++++++++++++++++++--- languages/translate.kk.toml | 148 ++++++++++++++++++++++++++--- main.go | 4 +- 45 files changed, 1735 insertions(+), 48 deletions(-) create mode 100644 encoder/apng/encoder.go create mode 100644 encoder/bmp/encoder.go create mode 100644 encoder/encoder.go create mode 100644 encoder/flv/encoder.go create mode 100644 encoder/gif/encoder.go create mode 100644 encoder/h264_nvenc/encoder.go create mode 100644 encoder/libmp3lame/encoder.go create mode 100644 encoder/libshine/encoder.go create mode 100644 encoder/libtwolame/encoder.go create mode 100644 encoder/libvpx/encoder.go create mode 100644 encoder/libvpx_vp9/encoder.go create mode 100644 encoder/libwebp/encoder.go create mode 100644 encoder/libwebp_anim/encoder.go create mode 100644 encoder/libx264/encoder.go create mode 100644 encoder/libx265/encoder.go create mode 100644 encoder/libxvid/encoder.go create mode 100644 encoder/mjpeg/encoder.go create mode 100644 encoder/mp2/encoder.go create mode 100644 encoder/mp2fixed/encoder.go create mode 100644 encoder/mpeg1video/encoder.go create mode 100644 encoder/mpeg2video/encoder.go create mode 100644 encoder/mpeg4/encoder.go create mode 100644 encoder/msmpeg4/encoder.go create mode 100644 encoder/msmpeg4v2/encoder.go create mode 100644 encoder/msvideo1/encoder.go create mode 100644 encoder/png/encoder.go create mode 100644 encoder/qtrle/encoder.go create mode 100644 encoder/sgi/encoder.go create mode 100644 encoder/tiff/encoder.go create mode 100644 encoder/wmav1/encoder.go create mode 100644 encoder/wmav2/encoder.go create mode 100644 encoder/wmv1/encoder.go create mode 100644 encoder/wmv2/encoder.go create mode 100644 encoder/xbm/encoder.go create mode 100644 kernel/encoder/encoder.go create mode 100644 kernel/encoder/encoders.go diff --git a/convertor/view.go b/convertor/view.go index 0510768..f6376aa 100644 --- a/convertor/view.go +++ b/convertor/view.go @@ -7,7 +7,9 @@ import ( "fyne.io/fyne/v2/container" "fyne.io/fyne/v2/storage" "fyne.io/fyne/v2/widget" + encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" "git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel/encoder" "github.com/nicksnyder/go-i18n/v2/i18n" "image/color" "path/filepath" @@ -16,6 +18,7 @@ import ( type ViewContract interface { Main( runConvert func(setting HandleConvertSetting), + formats encoder.ConvertorFormatsContract, ) SelectFFPath( ffmpegPath string, @@ -34,6 +37,8 @@ type HandleConvertSetting struct { VideoFileInput kernel.File DirectoryForSave string OverwriteOutputFiles bool + Format string + Encoder encoder2.EncoderContract } type enableFormConversionStruct struct { @@ -50,6 +55,7 @@ func NewView(app kernel.AppContract) *View { func (v View) Main( runConvert func(setting HandleConvertSetting), + formats encoder.ConvertorFormatsContract, ) { form := &widget.Form{} @@ -68,9 +74,11 @@ func (v View) Main( isOverwriteOutputFiles = b }) + selectEncoder := v.getSelectFormat(formats) + form.Items = []*widget.FormItem{ { - Text: v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "fileVideoForConversionTitle"}), + Text: v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "fileForConversionTitle"}), Widget: fileVideoForConversion, }, { @@ -86,6 +94,17 @@ func (v View) Main( { Widget: checkboxOverwriteOutputFiles, }, + { + Widget: selectEncoder.SelectFileType, + }, + { + Text: v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "selectFormat"}), + Widget: selectEncoder.SelectFormat, + }, + { + Text: v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "selectEncoder"}), + Widget: selectEncoder.SelectEncoder, + }, } form.SubmitText = v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ MessageID: "converterVideoFilesSubmitTitle", @@ -105,6 +124,18 @@ func (v View) Main( enableFormConversion(enableFormConversionStruct) return } + if len(selectEncoder.SelectFormat.Selected) == 0 { + showConversionMessage(conversionMessage, errors.New(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ + MessageID: "errorSelectedFormat", + }))) + return + } + if selectEncoder.Encoder == nil { + showConversionMessage(conversionMessage, errors.New(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ + MessageID: "errorSelectedEncoder", + }))) + return + } conversionMessage.Text = "" fileVideoForConversion.Disable() @@ -115,6 +146,8 @@ func (v View) Main( VideoFileInput: *fileInput, DirectoryForSave: *pathToSaveDirectory, OverwriteOutputFiles: isOverwriteOutputFiles, + Format: selectEncoder.SelectFormat.Selected, + Encoder: selectEncoder.Encoder, } runConvert(setting) enableFormConversion(enableFormConversionStruct) @@ -209,6 +242,68 @@ func (v View) getButtonForSelectingDirectoryForSaving() (button *widget.Button, return } +type selectEncoder struct { + SelectFileType *widget.RadioGroup + SelectFormat *widget.Select + SelectEncoder *widget.Select + Encoder encoder2.EncoderContract +} + +func (v View) getSelectFormat(formats encoder.ConvertorFormatsContract) *selectEncoder { + selectEncoder := &selectEncoder{} + + encoders := map[int]encoder2.EncoderDataContract{} + selectEncoder.SelectEncoder = widget.NewSelect([]string{}, func(s string) { + if encoders[selectEncoder.SelectEncoder.SelectedIndex()] == nil { + return + } + selectEncoder.Encoder = encoders[selectEncoder.SelectEncoder.SelectedIndex()].NewEncoder() + }) + + formatSelected := "" + selectEncoder.SelectFormat = widget.NewSelect([]string{}, func(s string) { + if formatSelected == s { + return + } + formatSelected = s + format, err := formats.GetFormat(s) + if err != nil { + return + } + encoderOptions := []string{} + encoders = format.GetEncoders() + for _, e := range encoders { + encoderOptions = append(encoderOptions, v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "encoder_" + e.GetTitle()})) + } + selectEncoder.SelectEncoder.SetOptions(encoderOptions) + selectEncoder.SelectEncoder.SetSelectedIndex(0) + }) + + fileTypeOptions := []string{} + for _, fileType := range encoder2.GetListFileType() { + fileTypeOptions = append(fileTypeOptions, fileType.Name()) + } + selectEncoder.SelectFileType = widget.NewRadioGroup([]string{"video", "audio", "image"}, func(s string) { + formatOptions := []string{} + for _, f := range formats.GetFormats() { + if s != f.GetFileType().Name() { + continue + } + formatOptions = append(formatOptions, f.GetTitle()) + } + selectEncoder.SelectFormat.SetOptions(formatOptions) + if s == encoder2.FileType(encoder2.Video).Name() { + selectEncoder.SelectFormat.SetSelected("mp4") + } else { + selectEncoder.SelectFormat.SetSelectedIndex(0) + } + }) + selectEncoder.SelectFileType.Horizontal = true + selectEncoder.SelectFileType.SetSelected("video") + + return selectEncoder +} + func setStringErrorStyle(text *canvas.Text) { text.Color = color.RGBA{R: 255, G: 0, B: 0, A: 255} text.Refresh() diff --git a/encoder/apng/encoder.go b/encoder/apng/encoder.go new file mode 100644 index 0000000..670f568 --- /dev/null +++ b/encoder/apng/encoder.go @@ -0,0 +1,21 @@ +package apng + +import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" + +type encoder struct { +} + +func NewEncoder() encoder2.EncoderContract { + return &encoder{} +} + +func (e encoder) GetParams() []string { + return []string{"-c:v", "apng"} +} + +func NewData() encoder2.EncoderDataContract { + title := "apng" + formats := []string{"apng"} + fileType := encoder2.FileType(encoder2.Image) + return encoder2.NewData(title, formats, fileType, NewEncoder) +} diff --git a/encoder/bmp/encoder.go b/encoder/bmp/encoder.go new file mode 100644 index 0000000..8a3ed28 --- /dev/null +++ b/encoder/bmp/encoder.go @@ -0,0 +1,21 @@ +package bmp + +import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" + +type encoder struct { +} + +func NewEncoder() encoder2.EncoderContract { + return &encoder{} +} + +func (e encoder) GetParams() []string { + return []string{"-c:v", "bmp"} +} + +func NewData() encoder2.EncoderDataContract { + title := "bmp" + formats := []string{"bmp"} + fileType := encoder2.FileType(encoder2.Image) + return encoder2.NewData(title, formats, fileType, NewEncoder) +} diff --git a/encoder/encoder.go b/encoder/encoder.go new file mode 100644 index 0000000..4bcf835 --- /dev/null +++ b/encoder/encoder.go @@ -0,0 +1,79 @@ +package encoder + +type EncoderContract interface { + GetParams() []string +} + +type EncoderDataContract interface { + GetTitle() string + GetFormats() []string + GetFileType() FileTypeContract + NewEncoder() EncoderContract +} + +type Data struct { + title string + formats []string + fileType FileTypeContract + encoder func() EncoderContract +} + +func NewData(title string, formats []string, fileType FileTypeContract, encoder func() EncoderContract) *Data { + return &Data{ + title: title, + formats: formats, + fileType: fileType, + encoder: encoder, + } +} + +func (data Data) GetTitle() string { + return data.title +} + +func (data Data) GetFormats() []string { + return data.formats +} + +func (data Data) NewEncoder() EncoderContract { + return data.encoder() +} + +func (data Data) GetFileType() FileTypeContract { + return data.fileType +} + +type FileTypeContract interface { + Name() string + Ordinal() int +} + +const ( + Video = iota + Audio + Image +) + +type FileType uint + +var fileTypeStrings = []string{ + "video", + "audio", + "image", +} + +func (fileType FileType) Name() string { + return fileTypeStrings[fileType] +} + +func (fileType FileType) Ordinal() int { + return int(fileType) +} + +func GetListFileType() []FileTypeContract { + return []FileTypeContract{ + FileType(Video), + FileType(Audio), + FileType(Image), + } +} diff --git a/encoder/flv/encoder.go b/encoder/flv/encoder.go new file mode 100644 index 0000000..c581f7c --- /dev/null +++ b/encoder/flv/encoder.go @@ -0,0 +1,21 @@ +package flv + +import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" + +type encoder struct { +} + +func NewEncoder() encoder2.EncoderContract { + return &encoder{} +} + +func (e encoder) GetParams() []string { + return []string{"-c:v", "flv"} +} + +func NewData() encoder2.EncoderDataContract { + title := "flv" + formats := []string{"flv"} + fileType := encoder2.FileType(encoder2.Video) + return encoder2.NewData(title, formats, fileType, NewEncoder) +} diff --git a/encoder/gif/encoder.go b/encoder/gif/encoder.go new file mode 100644 index 0000000..b08eaa8 --- /dev/null +++ b/encoder/gif/encoder.go @@ -0,0 +1,21 @@ +package gif + +import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" + +type encoder struct { +} + +func NewEncoder() encoder2.EncoderContract { + return &encoder{} +} + +func (e encoder) GetParams() []string { + return []string{"-c:v", "gif"} +} + +func NewData() encoder2.EncoderDataContract { + title := "gif" + formats := []string{"gif"} + fileType := encoder2.FileType(encoder2.Image) + return encoder2.NewData(title, formats, fileType, NewEncoder) +} diff --git a/encoder/h264_nvenc/encoder.go b/encoder/h264_nvenc/encoder.go new file mode 100644 index 0000000..d01e6d1 --- /dev/null +++ b/encoder/h264_nvenc/encoder.go @@ -0,0 +1,23 @@ +package h264_nvenc + +import ( + encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" +) + +type encoder struct { +} + +func NewEncoder() encoder2.EncoderContract { + return &encoder{} +} + +func (e encoder) GetParams() []string { + return []string{"-c:v", "h264_nvenc"} +} + +func NewData() encoder2.EncoderDataContract { + title := "h264_nvenc" + formats := []string{"mp4"} + fileType := encoder2.FileType(encoder2.Video) + return encoder2.NewData(title, formats, fileType, NewEncoder) +} diff --git a/encoder/libmp3lame/encoder.go b/encoder/libmp3lame/encoder.go new file mode 100644 index 0000000..0c1d047 --- /dev/null +++ b/encoder/libmp3lame/encoder.go @@ -0,0 +1,21 @@ +package libmp3lame + +import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" + +type encoder struct { +} + +func NewEncoder() encoder2.EncoderContract { + return &encoder{} +} + +func (e encoder) GetParams() []string { + return []string{"-c:a", "libmp3lame"} +} + +func NewData() encoder2.EncoderDataContract { + title := "libmp3lame" + formats := []string{"mp3"} + fileType := encoder2.FileType(encoder2.Audio) + return encoder2.NewData(title, formats, fileType, NewEncoder) +} diff --git a/encoder/libshine/encoder.go b/encoder/libshine/encoder.go new file mode 100644 index 0000000..9db1a6c --- /dev/null +++ b/encoder/libshine/encoder.go @@ -0,0 +1,21 @@ +package libshine + +import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" + +type encoder struct { +} + +func NewEncoder() encoder2.EncoderContract { + return &encoder{} +} + +func (e encoder) GetParams() []string { + return []string{"-c:a", "libshine"} +} + +func NewData() encoder2.EncoderDataContract { + title := "libshine" + formats := []string{"mp3"} + fileType := encoder2.FileType(encoder2.Audio) + return encoder2.NewData(title, formats, fileType, NewEncoder) +} diff --git a/encoder/libtwolame/encoder.go b/encoder/libtwolame/encoder.go new file mode 100644 index 0000000..958e601 --- /dev/null +++ b/encoder/libtwolame/encoder.go @@ -0,0 +1,21 @@ +package libtwolame + +import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" + +type encoder struct { +} + +func NewEncoder() encoder2.EncoderContract { + return &encoder{} +} + +func (e encoder) GetParams() []string { + return []string{"-c:a", "libtwolame"} +} + +func NewData() encoder2.EncoderDataContract { + title := "libtwolame" + formats := []string{"mp2"} + fileType := encoder2.FileType(encoder2.Audio) + return encoder2.NewData(title, formats, fileType, NewEncoder) +} diff --git a/encoder/libvpx/encoder.go b/encoder/libvpx/encoder.go new file mode 100644 index 0000000..2573e72 --- /dev/null +++ b/encoder/libvpx/encoder.go @@ -0,0 +1,21 @@ +package libvpx + +import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" + +type encoder struct { +} + +func NewEncoder() encoder2.EncoderContract { + return &encoder{} +} + +func (e encoder) GetParams() []string { + return []string{"-c:v", "libvpx"} +} + +func NewData() encoder2.EncoderDataContract { + title := "libvpx" + formats := []string{"webm", "mkv"} + fileType := encoder2.FileType(encoder2.Video) + return encoder2.NewData(title, formats, fileType, NewEncoder) +} diff --git a/encoder/libvpx_vp9/encoder.go b/encoder/libvpx_vp9/encoder.go new file mode 100644 index 0000000..657b8c2 --- /dev/null +++ b/encoder/libvpx_vp9/encoder.go @@ -0,0 +1,21 @@ +package libvpx_vp9 + +import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" + +type encoder struct { +} + +func NewEncoder() encoder2.EncoderContract { + return &encoder{} +} + +func (e encoder) GetParams() []string { + return []string{"-c:v", "libvpx-vp9"} +} + +func NewData() encoder2.EncoderDataContract { + title := "libvpx-vp9" + formats := []string{"webm", "mkv"} + fileType := encoder2.FileType(encoder2.Video) + return encoder2.NewData(title, formats, fileType, NewEncoder) +} diff --git a/encoder/libwebp/encoder.go b/encoder/libwebp/encoder.go new file mode 100644 index 0000000..b181f90 --- /dev/null +++ b/encoder/libwebp/encoder.go @@ -0,0 +1,21 @@ +package libwebp + +import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" + +type encoder struct { +} + +func NewEncoder() encoder2.EncoderContract { + return &encoder{} +} + +func (e encoder) GetParams() []string { + return []string{"-c:v", "libwebp"} +} + +func NewData() encoder2.EncoderDataContract { + title := "libwebp" + formats := []string{"webp"} + fileType := encoder2.FileType(encoder2.Image) + return encoder2.NewData(title, formats, fileType, NewEncoder) +} diff --git a/encoder/libwebp_anim/encoder.go b/encoder/libwebp_anim/encoder.go new file mode 100644 index 0000000..ce3fd2d --- /dev/null +++ b/encoder/libwebp_anim/encoder.go @@ -0,0 +1,21 @@ +package libwebp_anim + +import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" + +type encoder struct { +} + +func NewEncoder() encoder2.EncoderContract { + return &encoder{} +} + +func (e encoder) GetParams() []string { + return []string{"-c:v", "libwebp_anim"} +} + +func NewData() encoder2.EncoderDataContract { + title := "libwebp_anim" + formats := []string{"webp"} + fileType := encoder2.FileType(encoder2.Image) + return encoder2.NewData(title, formats, fileType, NewEncoder) +} diff --git a/encoder/libx264/encoder.go b/encoder/libx264/encoder.go new file mode 100644 index 0000000..fad2501 --- /dev/null +++ b/encoder/libx264/encoder.go @@ -0,0 +1,21 @@ +package libx264 + +import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" + +type encoder struct { +} + +func NewEncoder() encoder2.EncoderContract { + return &encoder{} +} + +func (e encoder) GetParams() []string { + return []string{"-c:v", "libx264"} +} + +func NewData() encoder2.EncoderDataContract { + title := "libx264" + formats := []string{"mp4"} + fileType := encoder2.FileType(encoder2.Video) + return encoder2.NewData(title, formats, fileType, NewEncoder) +} diff --git a/encoder/libx265/encoder.go b/encoder/libx265/encoder.go new file mode 100644 index 0000000..2179c22 --- /dev/null +++ b/encoder/libx265/encoder.go @@ -0,0 +1,21 @@ +package libx265 + +import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" + +type encoder struct { +} + +func NewEncoder() encoder2.EncoderContract { + return &encoder{} +} + +func (e encoder) GetParams() []string { + return []string{"-c:v", "libx265"} +} + +func NewData() encoder2.EncoderDataContract { + title := "libx265" + formats := []string{"mp4"} + fileType := encoder2.FileType(encoder2.Video) + return encoder2.NewData(title, formats, fileType, NewEncoder) +} diff --git a/encoder/libxvid/encoder.go b/encoder/libxvid/encoder.go new file mode 100644 index 0000000..3f7c135 --- /dev/null +++ b/encoder/libxvid/encoder.go @@ -0,0 +1,21 @@ +package libxvid + +import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" + +type encoder struct { +} + +func NewEncoder() encoder2.EncoderContract { + return &encoder{} +} + +func (e encoder) GetParams() []string { + return []string{"-c:v", "libxvid"} +} + +func NewData() encoder2.EncoderDataContract { + title := "libxvid" + formats := []string{"avi"} + fileType := encoder2.FileType(encoder2.Video) + return encoder2.NewData(title, formats, fileType, NewEncoder) +} diff --git a/encoder/mjpeg/encoder.go b/encoder/mjpeg/encoder.go new file mode 100644 index 0000000..ba44bdb --- /dev/null +++ b/encoder/mjpeg/encoder.go @@ -0,0 +1,21 @@ +package mjpeg + +import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" + +type encoder struct { +} + +func NewEncoder() encoder2.EncoderContract { + return &encoder{} +} + +func (e encoder) GetParams() []string { + return []string{"-c:v", "mjpeg"} +} + +func NewData() encoder2.EncoderDataContract { + title := "mjpeg" + formats := []string{"jpg"} + fileType := encoder2.FileType(encoder2.Image) + return encoder2.NewData(title, formats, fileType, NewEncoder) +} diff --git a/encoder/mp2/encoder.go b/encoder/mp2/encoder.go new file mode 100644 index 0000000..2e976c9 --- /dev/null +++ b/encoder/mp2/encoder.go @@ -0,0 +1,21 @@ +package mp2 + +import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" + +type encoder struct { +} + +func NewEncoder() encoder2.EncoderContract { + return &encoder{} +} + +func (e encoder) GetParams() []string { + return []string{"-c:a", "mp2"} +} + +func NewData() encoder2.EncoderDataContract { + title := "mp2" + formats := []string{"mp2"} + fileType := encoder2.FileType(encoder2.Audio) + return encoder2.NewData(title, formats, fileType, NewEncoder) +} diff --git a/encoder/mp2fixed/encoder.go b/encoder/mp2fixed/encoder.go new file mode 100644 index 0000000..f9bbcb8 --- /dev/null +++ b/encoder/mp2fixed/encoder.go @@ -0,0 +1,21 @@ +package mp2fixed + +import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" + +type encoder struct { +} + +func NewEncoder() encoder2.EncoderContract { + return &encoder{} +} + +func (e encoder) GetParams() []string { + return []string{"-c:a", "mp2fixed"} +} + +func NewData() encoder2.EncoderDataContract { + title := "mp2fixed" + formats := []string{"mp2"} + fileType := encoder2.FileType(encoder2.Audio) + return encoder2.NewData(title, formats, fileType, NewEncoder) +} diff --git a/encoder/mpeg1video/encoder.go b/encoder/mpeg1video/encoder.go new file mode 100644 index 0000000..4ee5eb8 --- /dev/null +++ b/encoder/mpeg1video/encoder.go @@ -0,0 +1,21 @@ +package mpeg1video + +import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" + +type encoder struct { +} + +func NewEncoder() encoder2.EncoderContract { + return &encoder{} +} + +func (e encoder) GetParams() []string { + return []string{"-c:v", "mpeg1video"} +} + +func NewData() encoder2.EncoderDataContract { + title := "mpeg1video" + formats := []string{"mpg", "mpeg"} + fileType := encoder2.FileType(encoder2.Video) + return encoder2.NewData(title, formats, fileType, NewEncoder) +} diff --git a/encoder/mpeg2video/encoder.go b/encoder/mpeg2video/encoder.go new file mode 100644 index 0000000..0f2eee4 --- /dev/null +++ b/encoder/mpeg2video/encoder.go @@ -0,0 +1,21 @@ +package mpeg2video + +import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" + +type encoder struct { +} + +func NewEncoder() encoder2.EncoderContract { + return &encoder{} +} + +func (e encoder) GetParams() []string { + return []string{"-c:v", "mpeg2video"} +} + +func NewData() encoder2.EncoderDataContract { + title := "mpeg2video" + formats := []string{"mpg", "mpeg"} + fileType := encoder2.FileType(encoder2.Video) + return encoder2.NewData(title, formats, fileType, NewEncoder) +} diff --git a/encoder/mpeg4/encoder.go b/encoder/mpeg4/encoder.go new file mode 100644 index 0000000..b57fe31 --- /dev/null +++ b/encoder/mpeg4/encoder.go @@ -0,0 +1,21 @@ +package mpeg4 + +import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" + +type encoder struct { +} + +func NewEncoder() encoder2.EncoderContract { + return &encoder{} +} + +func (e encoder) GetParams() []string { + return []string{"-c:v", "mpeg4"} +} + +func NewData() encoder2.EncoderDataContract { + title := "mpeg4" + formats := []string{"avi"} + fileType := encoder2.FileType(encoder2.Video) + return encoder2.NewData(title, formats, fileType, NewEncoder) +} diff --git a/encoder/msmpeg4/encoder.go b/encoder/msmpeg4/encoder.go new file mode 100644 index 0000000..5f792a0 --- /dev/null +++ b/encoder/msmpeg4/encoder.go @@ -0,0 +1,21 @@ +package msmpeg4 + +import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" + +type encoder struct { +} + +func NewEncoder() encoder2.EncoderContract { + return &encoder{} +} + +func (e encoder) GetParams() []string { + return []string{"-c:v", "msmpeg4"} +} + +func NewData() encoder2.EncoderDataContract { + title := "msmpeg4" + formats := []string{"avi"} + fileType := encoder2.FileType(encoder2.Video) + return encoder2.NewData(title, formats, fileType, NewEncoder) +} diff --git a/encoder/msmpeg4v2/encoder.go b/encoder/msmpeg4v2/encoder.go new file mode 100644 index 0000000..5d5a420 --- /dev/null +++ b/encoder/msmpeg4v2/encoder.go @@ -0,0 +1,21 @@ +package msmpeg4v2 + +import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" + +type encoder struct { +} + +func NewEncoder() encoder2.EncoderContract { + return &encoder{} +} + +func (e encoder) GetParams() []string { + return []string{"-c:v", "msmpeg4v2"} +} + +func NewData() encoder2.EncoderDataContract { + title := "msmpeg4v2" + formats := []string{"avi"} + fileType := encoder2.FileType(encoder2.Video) + return encoder2.NewData(title, formats, fileType, NewEncoder) +} diff --git a/encoder/msvideo1/encoder.go b/encoder/msvideo1/encoder.go new file mode 100644 index 0000000..ef9c1a1 --- /dev/null +++ b/encoder/msvideo1/encoder.go @@ -0,0 +1,21 @@ +package msvideo1 + +import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" + +type encoder struct { +} + +func NewEncoder() encoder2.EncoderContract { + return &encoder{} +} + +func (e encoder) GetParams() []string { + return []string{"-c:v", "msvideo1"} +} + +func NewData() encoder2.EncoderDataContract { + title := "msvideo1" + formats := []string{"avi"} + fileType := encoder2.FileType(encoder2.Video) + return encoder2.NewData(title, formats, fileType, NewEncoder) +} diff --git a/encoder/png/encoder.go b/encoder/png/encoder.go new file mode 100644 index 0000000..ec6d6d7 --- /dev/null +++ b/encoder/png/encoder.go @@ -0,0 +1,21 @@ +package png + +import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" + +type encoder struct { +} + +func NewEncoder() encoder2.EncoderContract { + return &encoder{} +} + +func (e encoder) GetParams() []string { + return []string{"-c:v", "png"} +} + +func NewData() encoder2.EncoderDataContract { + title := "png" + formats := []string{"png"} + fileType := encoder2.FileType(encoder2.Image) + return encoder2.NewData(title, formats, fileType, NewEncoder) +} diff --git a/encoder/qtrle/encoder.go b/encoder/qtrle/encoder.go new file mode 100644 index 0000000..f055e8c --- /dev/null +++ b/encoder/qtrle/encoder.go @@ -0,0 +1,21 @@ +package qtrle + +import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" + +type encoder struct { +} + +func NewEncoder() encoder2.EncoderContract { + return &encoder{} +} + +func (e encoder) GetParams() []string { + return []string{"-c:v", "qtrle"} +} + +func NewData() encoder2.EncoderDataContract { + title := "qtrle" + formats := []string{"mov"} + fileType := encoder2.FileType(encoder2.Video) + return encoder2.NewData(title, formats, fileType, NewEncoder) +} diff --git a/encoder/sgi/encoder.go b/encoder/sgi/encoder.go new file mode 100644 index 0000000..ee29bc9 --- /dev/null +++ b/encoder/sgi/encoder.go @@ -0,0 +1,21 @@ +package sgi + +import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" + +type encoder struct { +} + +func NewEncoder() encoder2.EncoderContract { + return &encoder{} +} + +func (e encoder) GetParams() []string { + return []string{"-c:v", "sgi"} +} + +func NewData() encoder2.EncoderDataContract { + title := "sgi" + formats := []string{"sgi"} + fileType := encoder2.FileType(encoder2.Image) + return encoder2.NewData(title, formats, fileType, NewEncoder) +} diff --git a/encoder/tiff/encoder.go b/encoder/tiff/encoder.go new file mode 100644 index 0000000..0d920bf --- /dev/null +++ b/encoder/tiff/encoder.go @@ -0,0 +1,21 @@ +package tiff + +import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" + +type encoder struct { +} + +func NewEncoder() encoder2.EncoderContract { + return &encoder{} +} + +func (e encoder) GetParams() []string { + return []string{"-c:v", "tiff"} +} + +func NewData() encoder2.EncoderDataContract { + title := "tiff" + formats := []string{"tiff"} + fileType := encoder2.FileType(encoder2.Image) + return encoder2.NewData(title, formats, fileType, NewEncoder) +} diff --git a/encoder/wmav1/encoder.go b/encoder/wmav1/encoder.go new file mode 100644 index 0000000..f158823 --- /dev/null +++ b/encoder/wmav1/encoder.go @@ -0,0 +1,21 @@ +package wmav1 + +import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" + +type encoder struct { +} + +func NewEncoder() encoder2.EncoderContract { + return &encoder{} +} + +func (e encoder) GetParams() []string { + return []string{"-c:a", "wmav1"} +} + +func NewData() encoder2.EncoderDataContract { + title := "wmav1" + formats := []string{"wma"} + fileType := encoder2.FileType(encoder2.Audio) + return encoder2.NewData(title, formats, fileType, NewEncoder) +} diff --git a/encoder/wmav2/encoder.go b/encoder/wmav2/encoder.go new file mode 100644 index 0000000..0a73f36 --- /dev/null +++ b/encoder/wmav2/encoder.go @@ -0,0 +1,21 @@ +package wmav2 + +import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" + +type encoder struct { +} + +func NewEncoder() encoder2.EncoderContract { + return &encoder{} +} + +func (e encoder) GetParams() []string { + return []string{"-c:a", "wmav2"} +} + +func NewData() encoder2.EncoderDataContract { + title := "wmav2" + formats := []string{"wma"} + fileType := encoder2.FileType(encoder2.Audio) + return encoder2.NewData(title, formats, fileType, NewEncoder) +} diff --git a/encoder/wmv1/encoder.go b/encoder/wmv1/encoder.go new file mode 100644 index 0000000..a3c4587 --- /dev/null +++ b/encoder/wmv1/encoder.go @@ -0,0 +1,21 @@ +package wmv1 + +import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" + +type encoder struct { +} + +func NewEncoder() encoder2.EncoderContract { + return &encoder{} +} + +func (e encoder) GetParams() []string { + return []string{"-c:v", "wmv1"} +} + +func NewData() encoder2.EncoderDataContract { + title := "wmv1" + formats := []string{"wmv"} + fileType := encoder2.FileType(encoder2.Video) + return encoder2.NewData(title, formats, fileType, NewEncoder) +} diff --git a/encoder/wmv2/encoder.go b/encoder/wmv2/encoder.go new file mode 100644 index 0000000..4ab35e2 --- /dev/null +++ b/encoder/wmv2/encoder.go @@ -0,0 +1,21 @@ +package wmv2 + +import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" + +type encoder struct { +} + +func NewEncoder() encoder2.EncoderContract { + return &encoder{} +} + +func (e encoder) GetParams() []string { + return []string{"-c:v", "wmv2"} +} + +func NewData() encoder2.EncoderDataContract { + title := "wmv2" + formats := []string{"wmv"} + fileType := encoder2.FileType(encoder2.Video) + return encoder2.NewData(title, formats, fileType, NewEncoder) +} diff --git a/encoder/xbm/encoder.go b/encoder/xbm/encoder.go new file mode 100644 index 0000000..dff7699 --- /dev/null +++ b/encoder/xbm/encoder.go @@ -0,0 +1,21 @@ +package xbm + +import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" + +type encoder struct { +} + +func NewEncoder() encoder2.EncoderContract { + return &encoder{} +} + +func (e encoder) GetParams() []string { + return []string{"-c:v", "xbm"} +} + +func NewData() encoder2.EncoderDataContract { + title := "xbm" + formats := []string{"xbm"} + fileType := encoder2.FileType(encoder2.Image) + return encoder2.NewData(title, formats, fileType, NewEncoder) +} diff --git a/handler/convertor.go b/handler/convertor.go index 909e908..559d357 100644 --- a/handler/convertor.go +++ b/handler/convertor.go @@ -3,6 +3,7 @@ package handler import ( "errors" "git.kor-elf.net/kor-elf/gui-for-ffmpeg/convertor" + error2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/error" "git.kor-elf.net/kor-elf/gui-for-ffmpeg/helper" "git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel" "github.com/nicksnyder/go-i18n/v2/i18n" @@ -18,24 +19,32 @@ type ConvertorHandlerContract interface { type ConvertorHandler struct { app kernel.AppContract convertorView convertor.ViewContract + errorView error2.ViewContract convertorRepository convertor.RepositoryContract } func NewConvertorHandler( app kernel.AppContract, convertorView convertor.ViewContract, + errorView error2.ViewContract, convertorRepository convertor.RepositoryContract, ) *ConvertorHandler { return &ConvertorHandler{ app: app, convertorView: convertorView, + errorView: errorView, convertorRepository: convertorRepository, } } func (h ConvertorHandler) MainConvertor() { if h.checkingFFPathUtilities() == true { - h.convertorView.Main(h.runConvert) + formats, err := h.app.GetConvertorService().GetSupportFormats() + if err != nil { + h.errorView.PanicError(err) + return + } + h.convertorView.Main(h.runConvert, formats) return } h.convertorView.SelectFFPath("", "", h.saveSettingFFPath, nil, h.downloadFFmpeg) @@ -59,11 +68,12 @@ func (h ConvertorHandler) runConvert(setting convertor.HandleConvertSetting) { h.app.GetQueue().Add(&kernel.ConvertSetting{ VideoFileInput: setting.VideoFileInput, VideoFileOut: kernel.File{ - Path: setting.DirectoryForSave + helper.PathSeparator() + setting.VideoFileInput.Name + ".mp4", + Path: setting.DirectoryForSave + helper.PathSeparator() + setting.VideoFileInput.Name + "." + setting.Format, Name: setting.VideoFileInput.Name, - Ext: ".mp4", + Ext: "." + setting.Format, }, OverwriteOutputFiles: setting.OverwriteOutputFiles, + Encoder: setting.Encoder, }) } diff --git a/kernel/convertor.go b/kernel/convertor.go index 0aa24e4..f0843bc 100644 --- a/kernel/convertor.go +++ b/kernel/convertor.go @@ -1,8 +1,11 @@ package kernel import ( + "bufio" "errors" + encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" "git.kor-elf.net/kor-elf/gui-for-ffmpeg/helper" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel/encoder" "io" "os/exec" "regexp" @@ -20,6 +23,7 @@ type ConvertSetting struct { VideoFileInput File VideoFileOut File OverwriteOutputFiles bool + Encoder encoder2.EncoderContract } type ConvertorContract interface { @@ -30,6 +34,7 @@ type ConvertorContract interface { ChangeFFmpegPath(path string) (bool, error) ChangeFFprobePath(path string) (bool, error) GetRunningProcesses() map[int]*exec.Cmd + GetSupportFormats() (encoder.ConvertorFormatsContract, error) } type ProgressContract interface { @@ -68,7 +73,9 @@ func (s Convertor) RunConvert(setting ConvertSetting, progress ProgressContract) if setting.OverwriteOutputFiles == true { overwriteOutputFiles = "-y" } - args := []string{overwriteOutputFiles, "-i", setting.VideoFileInput.Path, "-c:v", "libx264", "-progress", progress.GetProtocole(), setting.VideoFileOut.Path} + args := []string{overwriteOutputFiles, "-i", setting.VideoFileInput.Path} + args = append(args, setting.Encoder.GetParams()...) + args = append(args, "-progress", progress.GetProtocole(), setting.VideoFileOut.Path) cmd := exec.Command(s.ffPathUtilities.FFmpeg, args...) helper.PrepareBackgroundCommand(cmd) @@ -115,7 +122,30 @@ func (s Convertor) GetTotalDuration(file *File) (duration float64, err error) { } return 0, err } - return strconv.ParseFloat(strings.TrimSpace(string(out)), 64) + frames := strings.TrimSpace(string(out)) + if len(frames) == 0 { + return s.getAlternativeTotalDuration(file) + } + return strconv.ParseFloat(frames, 64) +} + +func (s Convertor) getAlternativeTotalDuration(file *File) (duration float64, err error) { + args := []string{"-v", "error", "-select_streams", "a:0", "-count_packets", "-show_entries", "stream=nb_read_packets", "-of", "csv=p=0", file.Path} + cmd := exec.Command(s.ffPathUtilities.FFprobe, args...) + helper.PrepareBackgroundCommand(cmd) + out, err := cmd.CombinedOutput() + if err != nil { + errString := strings.TrimSpace(string(out)) + if len(errString) > 1 { + return 0, errors.New(errString) + } + return 0, err + } + frames := strings.TrimSpace(string(out)) + if len(frames) == 0 { + return 0, errors.New("error getting number of frames") + } + return strconv.ParseFloat(frames, 64) } func (s Convertor) GetFFmpegVesrion() (string, error) { @@ -168,6 +198,45 @@ func (s Convertor) ChangeFFprobePath(path string) (bool, error) { return true, nil } +func (s Convertor) GetSupportFormats() (encoder.ConvertorFormatsContract, error) { + formats := encoder.NewConvertorFormats() + cmd := exec.Command(s.ffPathUtilities.FFmpeg, "-encoders") + + stdOut, err := cmd.StdoutPipe() + if err != nil { + return formats, err + } + + err = cmd.Start() + if err != nil { + return formats, err + } + + scannerErr := bufio.NewReader(stdOut) + for { + line, _, err := scannerErr.ReadLine() + if err != nil { + if err == io.EOF { + break + } + continue + } + text := strings.Split(strings.TrimSpace(string(line)), " ") + encoderType := string(text[0][0]) + if len(text) < 2 || (encoderType != "V" && encoderType != "A") { + continue + } + formats.NewEncoder(text[1]) + } + + err = cmd.Wait() + if err != nil { + return formats, err + } + + return formats, nil +} + func (s Convertor) GetRunningProcesses() map[int]*exec.Cmd { return s.runningProcesses.items } diff --git a/kernel/encoder/encoder.go b/kernel/encoder/encoder.go new file mode 100644 index 0000000..db4e25f --- /dev/null +++ b/kernel/encoder/encoder.go @@ -0,0 +1,84 @@ +package encoder + +import ( + "errors" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" +) + +type ConvertorFormatContract interface { + GetTitle() string + AddEncoder(encoder encoder.EncoderDataContract) + GetFileType() encoder.FileTypeContract + GetEncoders() map[int]encoder.EncoderDataContract +} + +type ConvertorFormat struct { + title string + fileType encoder.FileTypeContract + encoders map[int]encoder.EncoderDataContract +} + +func NewConvertorFormat(title string, fileType encoder.FileTypeContract) *ConvertorFormat { + return &ConvertorFormat{ + title: title, + fileType: fileType, + encoders: map[int]encoder.EncoderDataContract{}, + } +} + +func (f ConvertorFormat) GetTitle() string { + return f.title +} + +func (f ConvertorFormat) AddEncoder(encoder encoder.EncoderDataContract) { + f.encoders[len(f.encoders)] = encoder +} + +func (f ConvertorFormat) GetEncoders() map[int]encoder.EncoderDataContract { + return f.encoders +} + +func (f ConvertorFormat) GetFileType() encoder.FileTypeContract { + return f.fileType +} + +type ConvertorFormatsContract interface { + NewEncoder(encoderName string) bool + GetFormats() map[string]ConvertorFormatContract + GetFormat(format string) (ConvertorFormatContract, error) +} + +type ConvertorFormats struct { + formats map[string]ConvertorFormatContract +} + +func NewConvertorFormats() *ConvertorFormats { + return &ConvertorFormats{ + formats: map[string]ConvertorFormatContract{}, + } +} + +func (f ConvertorFormats) NewEncoder(encoderName string) bool { + if supportEncoders[encoderName] == nil { + return false + } + data := supportEncoders[encoderName]() + for _, format := range data.GetFormats() { + if f.formats[format] == nil { + f.formats[format] = NewConvertorFormat(format, data.GetFileType()) + } + f.formats[format].AddEncoder(data) + } + return true +} + +func (f ConvertorFormats) GetFormats() map[string]ConvertorFormatContract { + return f.formats +} + +func (f ConvertorFormats) GetFormat(format string) (ConvertorFormatContract, error) { + if f.formats[format] == nil { + return ConvertorFormat{}, errors.New("not found ConvertorFormat") + } + return f.formats[format], nil +} diff --git a/kernel/encoder/encoders.go b/kernel/encoder/encoders.go new file mode 100644 index 0000000..d9330f4 --- /dev/null +++ b/kernel/encoder/encoders.go @@ -0,0 +1,74 @@ +package encoder + +import ( + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/apng" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/bmp" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/flv" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/gif" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/h264_nvenc" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/libmp3lame" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/libshine" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/libtwolame" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/libvpx" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/libvpx_vp9" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/libwebp" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/libwebp_anim" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/libx264" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/libx265" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/libxvid" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/mjpeg" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/mp2" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/mp2fixed" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/mpeg1video" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/mpeg2video" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/mpeg4" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/msmpeg4" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/msmpeg4v2" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/msvideo1" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/png" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/qtrle" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/sgi" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/tiff" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/wmav1" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/wmav2" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/wmv1" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/wmv2" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/xbm" +) + +var supportEncoders = map[string]func() encoder.EncoderDataContract{ + "libx264": libx264.NewData, + "h264_nvenc": h264_nvenc.NewData, + "libx265": libx265.NewData, + "png": png.NewData, + "gif": gif.NewData, + "flv": flv.NewData, + "apng": apng.NewData, + "bmp": bmp.NewData, + "mjpeg": mjpeg.NewData, + "mpeg1video": mpeg1video.NewData, + "mpeg2video": mpeg2video.NewData, + "mpeg4": mpeg4.NewData, + "libxvid": libxvid.NewData, + "msmpeg4v2": msmpeg4v2.NewData, + "msmpeg4": msmpeg4.NewData, + "msvideo1": msvideo1.NewData, + "qtrle": qtrle.NewData, + "tiff": tiff.NewData, + "sgi": sgi.NewData, + "libvpx": libvpx.NewData, + "libvpx-vp9": libvpx_vp9.NewData, + "libwebp_anim": libwebp_anim.NewData, + "libwebp": libwebp.NewData, + "wmv1": wmv1.NewData, + "wmv2": wmv2.NewData, + "xbm": xbm.NewData, + "mp2": mp2.NewData, + "mp2fixed": mp2fixed.NewData, + "libtwolame": libtwolame.NewData, + "libmp3lame": libmp3lame.NewData, + "libshine": libshine.NewData, + "wmav1": wmav1.NewData, + "wmav2": wmav2.NewData, +} diff --git a/languages/active.en.toml b/languages/active.en.toml index 357f1c0..7d573b0 100644 --- a/languages/active.en.toml +++ b/languages/active.en.toml @@ -47,8 +47,8 @@ hash = "sha1-7ac460f3c24c9952082f2db6e4d62f752598709c" other = "Convert" [converterVideoFilesTitle] -hash = "sha1-4d972809e4c7f9c9ff2c110a126bbc183c9429ce" -other = "Converter video files to mp4" +hash = "sha1-1ab29597cc9dfefab08e54ea5442e7ffa15f0394" +other = "Video, audio and picture converter" [download] hash = "sha1-fe8f79f29da457de2f6bc9531de6e536e0c426ad" @@ -62,6 +62,138 @@ other = "Will be downloaded from the site:" hash = "sha1-55f87f114628fa2d5d8e67d1e1cda22c0e4f9271" other = "Downloading..." +[encoder_apng] +hash = "sha1-1cbd9abfef96d5614a7e569161b41bd6ad87bbaf" +other = "APNG image" + +[encoder_bmp] +hash = "sha1-e0b9c16b016961a5abdc2217e8ffd1ba7ddebc40" +other = "BMP image" + +[encoder_flv] +hash = "sha1-3602bbf1cc90e48254f81975c7879b5fc0c4d602" +other = "FLV" + +[encoder_gif] +hash = "sha1-d092a779172291b5215aa095390a5b11659128a4" +other = "GIF image" + +[encoder_h264_nvenc] +hash = "sha1-169389f8c4a2518410159c363378ab5c978c32e5" +other = "H.264 with NVIDIA support" + +[encoder_libmp3lame] +hash = "sha1-cd2c8d6f246c8bc18554b7105cb50b78d3cb2b98" +other = "libmp3lame MP3 (MPEG audio layer 3)" + +[encoder_libshine] +hash = "sha1-891d56c85857e5d83ef5a1fe077c1f1540788f49" +other = "libshine MP3 (MPEG audio layer 3)" + +[encoder_libtwolame] +hash = "sha1-b2f53be810b74edc3c454ac75de7ddecfee322ca" +other = "libtwolame MP2 (MPEG audio layer 2)" + +[encoder_libvpx] +hash = "sha1-b85c923aecfb48de0e87e71b6a21bfc2c547c70e" +other = "libvpx VP8 (codec vp8)" + +[encoder_libvpx-vp9] +hash = "sha1-3106417bd89bee87daa691e87614caf78cb934fe" +other = "libvpx VP9 (codec vp9)" + +[encoder_libwebp] +hash = "sha1-1d590d47d46f7880246061fce0e0de6d743db39e" +other = "libwebp WebP image" + +[encoder_libwebp_anim] +hash = "sha1-f141a9c8f23d79c13d44c30d8f34e05b363771ad" +other = "libwebp_anim WebP image" + +[encoder_libx264] +hash = "sha1-6d764ac459c0bf3c819d76618418cdfbb7a749eb" +other = "H.264 libx264" + +[encoder_libx265] +hash = "sha1-55544c166b1e15fd71a58096518e528109599eea" +other = "H.265 libx265" + +[encoder_libxvid] +hash = "sha1-d4bed46d6cdd2bfa8fd1689801164a83ab10c3f5" +other = "libxvidcore MPEG-4 part 2" + +[encoder_mjpeg] +hash = "sha1-94ba63a322b493a04da65e566781fe1cf8bb0d50" +other = "MJPEG (Motion JPEG)" + +[encoder_mp2] +hash = "sha1-a9154b7203349e5d6fbfd67d1ea97715f54b2065" +other = "MP2 (MPEG audio layer 2)" + +[encoder_mp2fixed] +hash = "sha1-dd2ee670d8bc8a60a96a717ebd26f16b5748cf3f" +other = "MP2 fixed point (MPEG audio layer 2)" + +[encoder_mpeg1video] +hash = "sha1-30043660719a3cb19dab5c33450665a8a9cc1c01" +other = "MPEG-1" + +[encoder_mpeg2video] +hash = "sha1-ccb2dcd8510cfdc9d52e5258af1863e5f2c51e77" +other = "MPEG-2" + +[encoder_mpeg4] +hash = "sha1-67fe42f18421b2f6c90fcdc579f9199bfca4b182" +other = "MPEG-4 part 2" + +[encoder_msmpeg4] +hash = "sha1-313ee597e4f0d9bd63a2bc6ac1618f028aef76f4" +other = "MPEG-4 part 2 Microsoft variant version 3" + +[encoder_msmpeg4v2] +hash = "sha1-adc442ce88f2717693b2da3010a1937d77ee522f" +other = "MPEG-4 part 2 Microsoft variant version 2" + +[encoder_msvideo1] +hash = "sha1-00f43ac0dc162bca10e0d98d6b70c0c6a902f66f" +other = "Microsoft Video-1" + +[encoder_png] +hash = "sha1-6715d4b82f5d9dfe3e53e30b402ffa1a6fbf30a5" +other = "PNG image" + +[encoder_qtrle] +hash = "sha1-31bf155cffaf6842ebc54084e4337ca08fdd9848" +other = "QuickTime Animation (RLE) video" + +[encoder_sgi] +hash = "sha1-f4510e237f7fc3c02caa728f9e500f4b069f9c11" +other = "SGI image" + +[encoder_tiff] +hash = "sha1-ed09d78c38e0b17ed695f35740c756dd7340eeac" +other = "TIFF image" + +[encoder_wmav1] +hash = "sha1-cd4a4c5eeac694b6699d55d0f9b477b3b50f18c7" +other = "Windows Media Audio 1" + +[encoder_wmav2] +hash = "sha1-eb2e5306cb33a702577ecfbdca0461862c66c053" +other = "Windows Media Audio 2" + +[encoder_wmv1] +hash = "sha1-f9b748554c590c36a56bcba2cd317196b7bdeddb" +other = "Windows Media Video 7" + +[encoder_wmv2] +hash = "sha1-5b21c87f5c6104797ead60b488b2948428f6b1b7" +other = "Windows Media Video 8" + +[encoder_xbm] +hash = "sha1-2dfc35881da62e9a1379d8238cf7839b24f79566" +other = "XBM (X BitMap) image" + [error] hash = "sha1-a7df8f8b5d754f226ac4cb320577fe692b33e483" other = "An error has occurred!" @@ -94,9 +226,17 @@ other = "Failed to determine FFprobe version" hash = "sha1-72aecd9ad85642d84d62dbbf3cf70953c5f696c7" other = "Error" +[errorSelectedEncoder] +hash = "sha1-33ed1aaf4cb3c2ee9d8f8c325b9b75d16ddf9979" +other = "Converter not selected" + [errorSelectedFolderSave] -hash = "sha1-83da899677cdc90e4344e3b94ee03c46b51bee4c" -other = "You haven't selected a folder to save!" +hash = "sha1-16f3ef93ee36813fdd79d8fb9bb7fc02acbb94a8" +other = "No save folder selected!" + +[errorSelectedFormat] +hash = "sha1-cda92c56a1ef1aabc92bbfc405ede8ab13087e66" +other = "File extension not selected" [exit] hash = "sha1-c42457057d1ab7950cea00719cbe0b078891775f" @@ -110,9 +250,9 @@ other = "This software uses libraries from the **FFmpeg** project under the **[L hash = "sha1-45f772b2eca5098cd6d31f2d1dc6edec1987a617" other = "**FFmpeg** is a trademark of **[Fabrice Bellard](http://bellard.org/)**, originator of the **[FFmpeg](https://ffmpeg.org/about.html)** project." -[fileVideoForConversionTitle] -hash = "sha1-5e727d4a2ff3f21080e51e81641595b2e668f3be" -other = "File for conversion:" +[fileForConversionTitle] +hash = "sha1-981bf090072a487e538d00780d337b47ca877372" +other = "File to convert:" [help] hash = "sha1-6a45cef900c668effcb2ab10da05855c1fd10f6f" @@ -162,10 +302,18 @@ other = "Queue" hash = "sha1-4864057d626a868fa60f999bed3191d61d045ddc" other = "Save" +[selectEncoder] +hash = "sha1-07beb734a090830bedf8e038e3b45c73d64b5696" +other = "Select a converter" + [selectFFPathTitle] hash = "sha1-95581446a28d968ff1a027c623159a7eb08654cf" other = "Specify the path to FFmpeg and FFprobe" +[selectFormat] +hash = "sha1-5e59b9376d321a49e73063eb7355b555d89bdd55" +other = "Select file extension" + [settings] hash = "sha1-7f17c7c62a7fd8d1a508481f4778688927734c2f" other = "Settings" diff --git a/languages/active.kk.toml b/languages/active.kk.toml index 0b2e21b..6785d10 100644 --- a/languages/active.kk.toml +++ b/languages/active.kk.toml @@ -47,8 +47,8 @@ hash = "sha1-7ac460f3c24c9952082f2db6e4d62f752598709c" other = "Файлды түрлендіру" [converterVideoFilesTitle] -hash = "sha1-4d972809e4c7f9c9ff2c110a126bbc183c9429ce" -other = "Бейне файлдарын mp4 форматына түрлендіру" +hash = "sha1-1ab29597cc9dfefab08e54ea5442e7ffa15f0394" +other = "Бейне, аудио және суретті түрлендіргіш" [download] hash = "sha1-fe8f79f29da457de2f6bc9531de6e536e0c426ad" @@ -62,6 +62,138 @@ other = "Сайттан жүктеледі:" hash = "sha1-55f87f114628fa2d5d8e67d1e1cda22c0e4f9271" other = "Жүктеп алынуда..." +[encoder_apng] +hash = "sha1-1cbd9abfef96d5614a7e569161b41bd6ad87bbaf" +other = "APNG image" + +[encoder_bmp] +hash = "sha1-e0b9c16b016961a5abdc2217e8ffd1ba7ddebc40" +other = "BMP image" + +[encoder_flv] +hash = "sha1-3602bbf1cc90e48254f81975c7879b5fc0c4d602" +other = "FLV" + +[encoder_gif] +hash = "sha1-d092a779172291b5215aa095390a5b11659128a4" +other = "GIF image" + +[encoder_h264_nvenc] +hash = "sha1-169389f8c4a2518410159c363378ab5c978c32e5" +other = "NVIDIA қолдауымен H.264" + +[encoder_libmp3lame] +hash = "sha1-cd2c8d6f246c8bc18554b7105cb50b78d3cb2b98" +other = "libmp3lame MP3 (MPEG audio layer 3)" + +[encoder_libshine] +hash = "sha1-891d56c85857e5d83ef5a1fe077c1f1540788f49" +other = "libshine MP3 (MPEG audio layer 3)" + +[encoder_libtwolame] +hash = "sha1-b2f53be810b74edc3c454ac75de7ddecfee322ca" +other = "libtwolame MP2 (MPEG audio layer 2)" + +[encoder_libvpx] +hash = "sha1-b85c923aecfb48de0e87e71b6a21bfc2c547c70e" +other = "libvpx VP8 (codec vp8)" + +[encoder_libvpx-vp9] +hash = "sha1-3106417bd89bee87daa691e87614caf78cb934fe" +other = "libvpx VP9 (codec vp9)" + +[encoder_libwebp] +hash = "sha1-1d590d47d46f7880246061fce0e0de6d743db39e" +other = "libwebp WebP image" + +[encoder_libwebp_anim] +hash = "sha1-f141a9c8f23d79c13d44c30d8f34e05b363771ad" +other = "libwebp_anim WebP image" + +[encoder_libx264] +hash = "sha1-6d764ac459c0bf3c819d76618418cdfbb7a749eb" +other = "H.264 libx264" + +[encoder_libx265] +hash = "sha1-55544c166b1e15fd71a58096518e528109599eea" +other = "H.265 libx265" + +[encoder_libxvid] +hash = "sha1-d4bed46d6cdd2bfa8fd1689801164a83ab10c3f5" +other = "libxvidcore MPEG-4 part 2" + +[encoder_mjpeg] +hash = "sha1-94ba63a322b493a04da65e566781fe1cf8bb0d50" +other = "MJPEG (Motion JPEG)" + +[encoder_mp2] +hash = "sha1-a9154b7203349e5d6fbfd67d1ea97715f54b2065" +other = "MP2 (MPEG audio layer 2)" + +[encoder_mp2fixed] +hash = "sha1-dd2ee670d8bc8a60a96a717ebd26f16b5748cf3f" +other = "MP2 fixed point (MPEG audio layer 2)" + +[encoder_mpeg1video] +hash = "sha1-30043660719a3cb19dab5c33450665a8a9cc1c01" +other = "MPEG-1" + +[encoder_mpeg2video] +hash = "sha1-ccb2dcd8510cfdc9d52e5258af1863e5f2c51e77" +other = "MPEG-2" + +[encoder_mpeg4] +hash = "sha1-67fe42f18421b2f6c90fcdc579f9199bfca4b182" +other = "MPEG-4 part 2" + +[encoder_msmpeg4] +hash = "sha1-313ee597e4f0d9bd63a2bc6ac1618f028aef76f4" +other = "MPEG-4 part 2 Microsoft variant version 3" + +[encoder_msmpeg4v2] +hash = "sha1-adc442ce88f2717693b2da3010a1937d77ee522f" +other = "MPEG-4 part 2 Microsoft variant version 2" + +[encoder_msvideo1] +hash = "sha1-00f43ac0dc162bca10e0d98d6b70c0c6a902f66f" +other = "Microsoft Video-1" + +[encoder_png] +hash = "sha1-6715d4b82f5d9dfe3e53e30b402ffa1a6fbf30a5" +other = "PNG image" + +[encoder_qtrle] +hash = "sha1-31bf155cffaf6842ebc54084e4337ca08fdd9848" +other = "QuickTime Animation (RLE) video" + +[encoder_sgi] +hash = "sha1-f4510e237f7fc3c02caa728f9e500f4b069f9c11" +other = "SGI image" + +[encoder_tiff] +hash = "sha1-ed09d78c38e0b17ed695f35740c756dd7340eeac" +other = "TIFF image" + +[encoder_wmav1] +hash = "sha1-cd4a4c5eeac694b6699d55d0f9b477b3b50f18c7" +other = "Windows Media Audio 1" + +[encoder_wmav2] +hash = "sha1-eb2e5306cb33a702577ecfbdca0461862c66c053" +other = "Windows Media Audio 2" + +[encoder_wmv1] +hash = "sha1-f9b748554c590c36a56bcba2cd317196b7bdeddb" +other = "Windows Media Video 7" + +[encoder_wmv2] +hash = "sha1-5b21c87f5c6104797ead60b488b2948428f6b1b7" +other = "Windows Media Video 8" + +[encoder_xbm] +hash = "sha1-2dfc35881da62e9a1379d8238cf7839b24f79566" +other = "XBM (X BitMap) image" + [error] hash = "sha1-a7df8f8b5d754f226ac4cb320577fe692b33e483" other = "Қате орын алды!" @@ -94,9 +226,17 @@ other = "FFprobe нұсқасын анықтау мүмкін болмады" hash = "sha1-72aecd9ad85642d84d62dbbf3cf70953c5f696c7" other = "Қате" +[errorSelectedEncoder] +hash = "sha1-33ed1aaf4cb3c2ee9d8f8c325b9b75d16ddf9979" +other = "Түрлендіргіш таңдалмаған" + [errorSelectedFolderSave] -hash = "sha1-83da899677cdc90e4344e3b94ee03c46b51bee4c" -other = "Сіз сақталатын қалтаны таңдамадыңыз!" +hash = "sha1-16f3ef93ee36813fdd79d8fb9bb7fc02acbb94a8" +other = "Сақтау қалтасы таңдалмаған!" + +[errorSelectedFormat] +hash = "sha1-cda92c56a1ef1aabc92bbfc405ede8ab13087e66" +other = "Файл кеңейтімі таңдалмаған" [exit] hash = "sha1-c42457057d1ab7950cea00719cbe0b078891775f" @@ -110,8 +250,8 @@ other = "Бұл бағдарламалық құрал **[LGPLv2.1](http://www.gn hash = "sha1-45f772b2eca5098cd6d31f2d1dc6edec1987a617" other = "FFmpeg — **[FFmpeg](https://ffmpeg.org/about.html)** жобасын жасаушы **[Fabrice Bellard](http://bellard.org/)** сауда белгісі." -[fileVideoForConversionTitle] -hash = "sha1-5e727d4a2ff3f21080e51e81641595b2e668f3be" +[fileForConversionTitle] +hash = "sha1-981bf090072a487e538d00780d337b47ca877372" other = "Түрлендіруге арналған файл:" [help] @@ -162,10 +302,18 @@ other = "Кезек" hash = "sha1-4864057d626a868fa60f999bed3191d61d045ddc" other = "Сақтау" +[selectEncoder] +hash = "sha1-07beb734a090830bedf8e038e3b45c73d64b5696" +other = "Түрлендіргішті таңдаңыз" + [selectFFPathTitle] hash = "sha1-95581446a28d968ff1a027c623159a7eb08654cf" other = "FFmpeg және FFprobe жолын көрсетіңіз" +[selectFormat] +hash = "sha1-5e59b9376d321a49e73063eb7355b555d89bdd55" +other = "Файл кеңейтімін таңдаңыз" + [settings] hash = "sha1-7f17c7c62a7fd8d1a508481f4778688927734c2f" other = "Параметрлер" diff --git a/languages/active.ru.toml b/languages/active.ru.toml index 6053ac2..bc9dd1e 100644 --- a/languages/active.ru.toml +++ b/languages/active.ru.toml @@ -10,10 +10,43 @@ checkboxOverwriteOutputFilesTitle = "Разрешить перезаписать choose = "выбрать" completedQueue = "Готово" converterVideoFilesSubmitTitle = "Конвертировать" -converterVideoFilesTitle = "Конвертор видео файлов в mp4" +converterVideoFilesTitle = "Конвертер видео, аудио и картинок" download = "Скачать" downloadFFmpegFromSite = "Будет скачано с сайта:" downloadRun = "Скачивается..." +encoder_apng = "APNG image" +encoder_bmp = "BMP image" +encoder_flv = "FLV" +encoder_gif = "GIF image" +encoder_h264_nvenc = "H.264 с поддержкой NVIDIA" +encoder_libmp3lame = "libmp3lame MP3 (MPEG audio layer 3)" +encoder_libshine = "libshine MP3 (MPEG audio layer 3)" +encoder_libtwolame = "libtwolame MP2 (MPEG audio layer 2)" +encoder_libvpx = "libvpx VP8 (codec vp8)" +encoder_libvpx-vp9 = "libvpx VP9 (codec vp9)" +encoder_libwebp = "libwebp WebP image" +encoder_libwebp_anim = "libwebp_anim WebP image" +encoder_libx264 = "H.264 libx264" +encoder_libx265 = "H.265 libx265" +encoder_libxvid = "libxvidcore MPEG-4 part 2" +encoder_mjpeg = "MJPEG (Motion JPEG)" +encoder_mp2 = "MP2 (MPEG audio layer 2)" +encoder_mp2fixed = "MP2 fixed point (MPEG audio layer 2)" +encoder_mpeg1video = "MPEG-1" +encoder_mpeg2video = "MPEG-2" +encoder_mpeg4 = "MPEG-4 part 2" +encoder_msmpeg4 = "MPEG-4 part 2 Microsoft variant version 3" +encoder_msmpeg4v2 = "MPEG-4 part 2 Microsoft variant version 2" +encoder_msvideo1 = "Microsoft Video-1" +encoder_png = "PNG image" +encoder_qtrle = "QuickTime Animation (RLE) video" +encoder_sgi = "SGI image" +encoder_tiff = "TIFF image" +encoder_wmav1 = "Windows Media Audio 1" +encoder_wmav2 = "Windows Media Audio 2" +encoder_wmv1 = "Windows Media Video 7" +encoder_wmv2 = "Windows Media Video 8" +encoder_xbm = "XBM (X BitMap) image" error = "Произошла ошибка!" errorConverter = "не смогли отконвертировать видео" errorDatabase = "не смогли создать файл 'database' в папке 'data'" @@ -22,11 +55,13 @@ errorFFmpegVersion = "Не смогли определить версию FFmpeg errorFFprobe = "это не FFprobe" errorFFprobeVersion = "Не смогли определить версию FFprobe" errorQueue = "Ошибка" -errorSelectedFolderSave = "Не выбрали папку для сохранения!" +errorSelectedEncoder = "Конвертер не выбран" +errorSelectedFolderSave = "Папка для сохранения не выбрана!" +errorSelectedFormat = "Расширение файла не выбрана" 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)**." -fileVideoForConversionTitle = "Файл для ковертации:" +fileForConversionTitle = "Файл для конвертации:" help = "Справка" inProgressQueue = "Выполняется" languageSelectionFormHead = "Переключить язык" @@ -39,7 +74,9 @@ programmLink = "Сайт проекта" programmVersion = "**Версия программы:** {{.Version}}" queue = "Очередь" save = "Сохранить" +selectEncoder = "Выберите конвертер" selectFFPathTitle = "Укажите путь к FFmpeg и к FFprobe" +selectFormat = "Выберите расширение файла" settings = "Настройки" testFF = "Проверка FFmpeg на работоспособность..." titleDownloadLink = "Скачать можно от сюда" diff --git a/languages/translate.en.toml b/languages/translate.en.toml index 5d5b71c..92dc6bc 100644 --- a/languages/translate.en.toml +++ b/languages/translate.en.toml @@ -1,15 +1,139 @@ -[completedQueue] -hash = "sha1-398c7d4f7b0d522afb930769c0fbb1a9f4b61fbe" -other = "Completed" +[encoder_apng] +hash = "sha1-1cbd9abfef96d5614a7e569161b41bd6ad87bbaf" +other = "APNG image" -[errorQueue] -hash = "sha1-72aecd9ad85642d84d62dbbf3cf70953c5f696c7" -other = "Error" +[encoder_bmp] +hash = "sha1-e0b9c16b016961a5abdc2217e8ffd1ba7ddebc40" +other = "BMP image" -[inProgressQueue] -hash = "sha1-eff79c40e2100ae5fadf3a7d99336025edcca8b5" -other = "In Progress" +[encoder_flv] +hash = "sha1-3602bbf1cc90e48254f81975c7879b5fc0c4d602" +other = "FLV" -[waitingQueue] -hash = "sha1-307429dd84150877080c4bbff2b340d1e7dadff2" -other = "Waiting" +[encoder_gif] +hash = "sha1-d092a779172291b5215aa095390a5b11659128a4" +other = "GIF image" + +[encoder_h264_nvenc] +hash = "sha1-169389f8c4a2518410159c363378ab5c978c32e5" +other = "H.264 with NVIDIA support" + +[encoder_libmp3lame] +hash = "sha1-cd2c8d6f246c8bc18554b7105cb50b78d3cb2b98" +other = "libmp3lame MP3 (MPEG audio layer 3)" + +[encoder_libshine] +hash = "sha1-891d56c85857e5d83ef5a1fe077c1f1540788f49" +other = "libshine MP3 (MPEG audio layer 3)" + +[encoder_libtwolame] +hash = "sha1-b2f53be810b74edc3c454ac75de7ddecfee322ca" +other = "libtwolame MP2 (MPEG audio layer 2)" + +[encoder_libvpx] +hash = "sha1-b85c923aecfb48de0e87e71b6a21bfc2c547c70e" +other = "libvpx VP8 (codec vp8)" + +[encoder_libvpx-vp9] +hash = "sha1-3106417bd89bee87daa691e87614caf78cb934fe" +other = "libvpx VP9 (codec vp9)" + +[encoder_libwebp] +hash = "sha1-1d590d47d46f7880246061fce0e0de6d743db39e" +other = "libwebp WebP image" + +[encoder_libwebp_anim] +hash = "sha1-f141a9c8f23d79c13d44c30d8f34e05b363771ad" +other = "libwebp_anim WebP image" + +[encoder_libx264] +hash = "sha1-6d764ac459c0bf3c819d76618418cdfbb7a749eb" +other = "H.264 libx264" + +[encoder_libx265] +hash = "sha1-55544c166b1e15fd71a58096518e528109599eea" +other = "H.265 libx265" + +[encoder_libxvid] +hash = "sha1-d4bed46d6cdd2bfa8fd1689801164a83ab10c3f5" +other = "libxvidcore MPEG-4 part 2" + +[encoder_mjpeg] +hash = "sha1-94ba63a322b493a04da65e566781fe1cf8bb0d50" +other = "MJPEG (Motion JPEG)" + +[encoder_mp2] +hash = "sha1-a9154b7203349e5d6fbfd67d1ea97715f54b2065" +other = "MP2 (MPEG audio layer 2)" + +[encoder_mp2fixed] +hash = "sha1-dd2ee670d8bc8a60a96a717ebd26f16b5748cf3f" +other = "MP2 fixed point (MPEG audio layer 2)" + +[encoder_mpeg1video] +hash = "sha1-30043660719a3cb19dab5c33450665a8a9cc1c01" +other = "MPEG-1" + +[encoder_mpeg2video] +hash = "sha1-ccb2dcd8510cfdc9d52e5258af1863e5f2c51e77" +other = "MPEG-2" + +[encoder_mpeg4] +hash = "sha1-67fe42f18421b2f6c90fcdc579f9199bfca4b182" +other = "MPEG-4 part 2" + +[encoder_msmpeg4] +hash = "sha1-313ee597e4f0d9bd63a2bc6ac1618f028aef76f4" +other = "MPEG-4 part 2 Microsoft variant version 3" + +[encoder_msmpeg4v2] +hash = "sha1-adc442ce88f2717693b2da3010a1937d77ee522f" +other = "MPEG-4 part 2 Microsoft variant version 2" + +[encoder_msvideo1] +hash = "sha1-00f43ac0dc162bca10e0d98d6b70c0c6a902f66f" +other = "Microsoft Video-1" + +[encoder_png] +hash = "sha1-6715d4b82f5d9dfe3e53e30b402ffa1a6fbf30a5" +other = "PNG image" + +[encoder_qtrle] +hash = "sha1-31bf155cffaf6842ebc54084e4337ca08fdd9848" +other = "QuickTime Animation (RLE) video" + +[encoder_sgi] +hash = "sha1-f4510e237f7fc3c02caa728f9e500f4b069f9c11" +other = "SGI image" + +[encoder_tiff] +hash = "sha1-ed09d78c38e0b17ed695f35740c756dd7340eeac" +other = "TIFF image" + +[encoder_wmav1] +hash = "sha1-cd4a4c5eeac694b6699d55d0f9b477b3b50f18c7" +other = "Windows Media Audio 1" + +[encoder_wmav2] +hash = "sha1-eb2e5306cb33a702577ecfbdca0461862c66c053" +other = "Windows Media Audio 2" + +[encoder_wmv1] +hash = "sha1-f9b748554c590c36a56bcba2cd317196b7bdeddb" +other = "Windows Media Video 7" + +[encoder_wmv2] +hash = "sha1-5b21c87f5c6104797ead60b488b2948428f6b1b7" +other = "Windows Media Video 8" + +[encoder_xbm] +hash = "sha1-2dfc35881da62e9a1379d8238cf7839b24f79566" +other = "XBM (X BitMap) image" + +[errorSelectedFormat] +hash = "sha1-cda92c56a1ef1aabc92bbfc405ede8ab13087e66" +other = "File extension not selected" + +[selectFormat] +hash = "sha1-5e59b9376d321a49e73063eb7355b555d89bdd55" +other = "Select file extension" diff --git a/languages/translate.kk.toml b/languages/translate.kk.toml index 24640fa..fc61d08 100644 --- a/languages/translate.kk.toml +++ b/languages/translate.kk.toml @@ -1,15 +1,139 @@ -[completedQueue] -hash = "sha1-398c7d4f7b0d522afb930769c0fbb1a9f4b61fbe" -other = "Дайын" +[encoder_apng] +hash = "sha1-1cbd9abfef96d5614a7e569161b41bd6ad87bbaf" +other = "APNG image" -[errorQueue] -hash = "sha1-72aecd9ad85642d84d62dbbf3cf70953c5f696c7" -other = "Қате" +[encoder_bmp] +hash = "sha1-e0b9c16b016961a5abdc2217e8ffd1ba7ddebc40" +other = "BMP image" -[inProgressQueue] -hash = "sha1-eff79c40e2100ae5fadf3a7d99336025edcca8b5" -other = "Орындалуда" +[encoder_flv] +hash = "sha1-3602bbf1cc90e48254f81975c7879b5fc0c4d602" +other = "FLV" -[waitingQueue] -hash = "sha1-307429dd84150877080c4bbff2b340d1e7dadff2" -other = "Күту" +[encoder_gif] +hash = "sha1-d092a779172291b5215aa095390a5b11659128a4" +other = "GIF image" + +[encoder_h264_nvenc] +hash = "sha1-169389f8c4a2518410159c363378ab5c978c32e5" +other = "NVIDIA қолдауымен H.264" + +[encoder_libmp3lame] +hash = "sha1-cd2c8d6f246c8bc18554b7105cb50b78d3cb2b98" +other = "libmp3lame MP3 (MPEG audio layer 3)" + +[encoder_libshine] +hash = "sha1-891d56c85857e5d83ef5a1fe077c1f1540788f49" +other = "libshine MP3 (MPEG audio layer 3)" + +[encoder_libtwolame] +hash = "sha1-b2f53be810b74edc3c454ac75de7ddecfee322ca" +other = "libtwolame MP2 (MPEG audio layer 2)" + +[encoder_libvpx] +hash = "sha1-b85c923aecfb48de0e87e71b6a21bfc2c547c70e" +other = "libvpx VP8 (codec vp8)" + +[encoder_libvpx-vp9] +hash = "sha1-3106417bd89bee87daa691e87614caf78cb934fe" +other = "libvpx VP9 (codec vp9)" + +[encoder_libwebp] +hash = "sha1-1d590d47d46f7880246061fce0e0de6d743db39e" +other = "libwebp WebP image" + +[encoder_libwebp_anim] +hash = "sha1-f141a9c8f23d79c13d44c30d8f34e05b363771ad" +other = "libwebp_anim WebP image" + +[encoder_libx264] +hash = "sha1-6d764ac459c0bf3c819d76618418cdfbb7a749eb" +other = "H.264 libx264" + +[encoder_libx265] +hash = "sha1-55544c166b1e15fd71a58096518e528109599eea" +other = "H.265 libx265" + +[encoder_libxvid] +hash = "sha1-d4bed46d6cdd2bfa8fd1689801164a83ab10c3f5" +other = "libxvidcore MPEG-4 part 2" + +[encoder_mjpeg] +hash = "sha1-94ba63a322b493a04da65e566781fe1cf8bb0d50" +other = "MJPEG (Motion JPEG)" + +[encoder_mp2] +hash = "sha1-a9154b7203349e5d6fbfd67d1ea97715f54b2065" +other = "MP2 (MPEG audio layer 2)" + +[encoder_mp2fixed] +hash = "sha1-dd2ee670d8bc8a60a96a717ebd26f16b5748cf3f" +other = "MP2 fixed point (MPEG audio layer 2)" + +[encoder_mpeg1video] +hash = "sha1-30043660719a3cb19dab5c33450665a8a9cc1c01" +other = "MPEG-1" + +[encoder_mpeg2video] +hash = "sha1-ccb2dcd8510cfdc9d52e5258af1863e5f2c51e77" +other = "MPEG-2" + +[encoder_mpeg4] +hash = "sha1-67fe42f18421b2f6c90fcdc579f9199bfca4b182" +other = "MPEG-4 part 2" + +[encoder_msmpeg4] +hash = "sha1-313ee597e4f0d9bd63a2bc6ac1618f028aef76f4" +other = "MPEG-4 part 2 Microsoft variant version 3" + +[encoder_msmpeg4v2] +hash = "sha1-adc442ce88f2717693b2da3010a1937d77ee522f" +other = "MPEG-4 part 2 Microsoft variant version 2" + +[encoder_msvideo1] +hash = "sha1-00f43ac0dc162bca10e0d98d6b70c0c6a902f66f" +other = "Microsoft Video-1" + +[encoder_png] +hash = "sha1-6715d4b82f5d9dfe3e53e30b402ffa1a6fbf30a5" +other = "PNG image" + +[encoder_qtrle] +hash = "sha1-31bf155cffaf6842ebc54084e4337ca08fdd9848" +other = "QuickTime Animation (RLE) video" + +[encoder_sgi] +hash = "sha1-f4510e237f7fc3c02caa728f9e500f4b069f9c11" +other = "SGI image" + +[encoder_tiff] +hash = "sha1-ed09d78c38e0b17ed695f35740c756dd7340eeac" +other = "TIFF image" + +[encoder_wmav1] +hash = "sha1-cd4a4c5eeac694b6699d55d0f9b477b3b50f18c7" +other = "Windows Media Audio 1" + +[encoder_wmav2] +hash = "sha1-eb2e5306cb33a702577ecfbdca0461862c66c053" +other = "Windows Media Audio 2" + +[encoder_wmv1] +hash = "sha1-f9b748554c590c36a56bcba2cd317196b7bdeddb" +other = "Windows Media Video 7" + +[encoder_wmv2] +hash = "sha1-5b21c87f5c6104797ead60b488b2948428f6b1b7" +other = "Windows Media Video 8" + +[encoder_xbm] +hash = "sha1-2dfc35881da62e9a1379d8238cf7839b24f79566" +other = "XBM (X BitMap) image" + +[errorSelectedFormat] +hash = "sha1-cda92c56a1ef1aabc92bbfc405ede8ab13087e66" +other = "Файл кеңейтімі таңдалмаған" + +[selectFormat] +hash = "sha1-5e59b9376d321a49e73063eb7355b555d89bdd55" +other = "Файл кеңейтімін таңдаңыз" diff --git a/main.go b/main.go index 0c21671..35624bb 100644 --- a/main.go +++ b/main.go @@ -26,7 +26,7 @@ func init() { appMetadata := &fyne.AppMetadata{ ID: "net.kor-elf.projects.gui-for-ffmpeg", Name: "GUI for FFmpeg", - Version: "0.4.0", + Version: "0.5.0", Icon: iconResource, } @@ -95,7 +95,7 @@ func main() { localizerView := localizer.NewView(application) convertorView := convertor.NewView(application) - convertorHandler := handler.NewConvertorHandler(application, convertorView, convertorRepository) + convertorHandler := handler.NewConvertorHandler(application, convertorView, errorView, convertorRepository) localizerRepository := localizer.NewRepository(settingRepository) menuView := menu.NewView(application) From 68d9c4bb6682000d8f1e83c2086331bfb69721e3 Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Thu, 7 Mar 2024 22:50:00 +0500 Subject: [PATCH 5/6] I fixed it in OS Windows so that the console window would not appear. --- kernel/convertor.go | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/convertor.go b/kernel/convertor.go index f0843bc..6754874 100644 --- a/kernel/convertor.go +++ b/kernel/convertor.go @@ -201,6 +201,7 @@ func (s Convertor) ChangeFFprobePath(path string) (bool, error) { func (s Convertor) GetSupportFormats() (encoder.ConvertorFormatsContract, error) { formats := encoder.NewConvertorFormats() cmd := exec.Command(s.ffPathUtilities.FFmpeg, "-encoders") + helper.PrepareBackgroundCommand(cmd) stdOut, err := cmd.StdoutPipe() if err != nil { From a95692196e8922a7b9c2737418cc95caced2100e Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Thu, 7 Mar 2024 23:17:55 +0500 Subject: [PATCH 6/6] Changed screenshot-gui-for-ffmpeg.png. --- images/screenshot-gui-for-ffmpeg.png | Bin 46461 -> 65303 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/images/screenshot-gui-for-ffmpeg.png b/images/screenshot-gui-for-ffmpeg.png index 2183563904e30764ec1cdaa0b5dd5aa8e5f8e71c..fba50e6f0c3d5970f3e6bd1dda2a00f2c0ef86c2 100644 GIT binary patch literal 65303 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