Compare commits

...

55 Commits
0.1.1 ... main

Author SHA1 Message Date
Leonid Nikitin 40848a70a5 Merge pull request 'Версия 0.7.0' (#8) from develop into main
Reviewed-on: #8
2024-04-28 14:57:07 +05:00
Leonid Nikitin 9aaee4c183
Changed the version to 0.7.0 2024-04-28 14:52:18 +05:00
Leonid Nikitin 2017617614
Added preset option for libx265.
Added preset option for h264_nvenc.
2024-04-28 14:43:18 +05:00
Leonid Nikitin f17104595d Merge pull request 'Версия 0.6.0' (#7) from develop into main
Reviewed-on: #7
2024-03-17 20:52:37 +05:00
Leonid Nikitin 21d4afcedb
Made it possible for each encoder to add its own parameters.
Added preset option for libx264.
2024-03-17 20:28:35 +05:00
Leonid Nikitin 8347e9fbb2
Changed Readme.md.
Updated instructions for working with translations.
2024-03-17 00:46:39 +05:00
Leonid Nikitin 8d17a52f00
Added .gitignore for fyne-cross/*. 2024-03-17 00:39:56 +05:00
Leonid Nikitin 4668da3223
Added .gitignore for translate.*.toml. 2024-03-17 00:39:32 +05:00
Leonid Nikitin a3b30c4543
Deleted temporary languages/translate.*.toml files. 2024-03-17 00:32:21 +05:00
Leonid Nikitin 3f358c8b7d
Refactoring.
I have moved the conversion form to a separate conversion file.
2024-03-17 00:30:02 +05:00
Leonid Nikitin 7b7c15ad27
Fixed bug related to incorrect window size.
Sometimes when starting a program the program window was small.
2024-03-16 21:45:14 +05:00
Leonid Nikitin 24d80779ee Merge pull request 'Версия 0.5.0' (#6) from develop into main
Reviewed-on: #6
2024-03-08 00:59:41 +05:00
Leonid Nikitin a95692196e
Changed screenshot-gui-for-ffmpeg.png. 2024-03-07 23:17:55 +05:00
Leonid Nikitin 68d9c4bb66
I fixed it in OS Windows so that the console window would not appear. 2024-03-07 22:50:00 +05:00
Leonid Nikitin 1ece1e443d
Added the ability to convert files to different extensions. 2024-03-07 22:18:35 +05:00
Leonid Nikitin 1eb7ea4a93
Refactoring LocalizerContract.
Instead of AddListener I made AddChangeCallback. And removed unnecessary dependencies.
2024-03-05 20:13:58 +05:00
Leonid Nikitin e766c6d465
Added the ability to show and hide elements by status type. 2024-03-05 00:58:43 +05:00
Leonid Nikitin 1f9f646f51
Refactoring.
type File struct and type ConvertSetting struct moved to convertor.go.
2024-02-25 23:38:16 +06:00
Leonid Nikitin d88586739a
Edited README.md.
src/icon.png to icon.png
src/data to data
src/languages ​​to languages
2024-02-17 21:22:20 +06:00
Leonid Nikitin a3db2b8f89 Merge pull request 'Версия 0.4.0.' (#5) from develop into main
Reviewed-on: #5
2024-02-17 20:33:18 +06:00
Leonid Nikitin d0539f5e90
Fixed an error when building an assembly in fyne-cross. 2024-02-17 20:06:09 +06:00
Leonid Nikitin 240ae7aa96
Windows OS fix. 2024-02-17 20:04:11 +06:00
Leonid Nikitin 0d05fdb307
Changed the screenshot in README.md. 2024-02-17 19:48:50 +06:00
Leonid Nikitin 8e6fb90482
Code improvement.
And added a comment to the code.
2024-02-17 19:45:28 +06:00
Leonid Nikitin bab8c1f383
Bug fixed.
When starting the program, sometimes the window was displayed incorrectly.
2024-02-17 19:18:25 +06:00
Leonid Nikitin a1c9143685
Added queues.
Reworked the architecture.
2024-02-17 19:08:58 +06:00
Leonid Nikitin c4ec958576
Moved the code from src to the root. 2024-02-12 22:21:47 +06:00
Leonid Nikitin 359db74251 Merge pull request 'Версия 0.3.1' (#4) from develop into main
Reviewed-on: #4
2024-02-10 21:35:08 +06:00
Leonid Nikitin 154cd1c7dd
Fixed convertor_windows.go.
Changed "git.kor-elf.net/kor-elf/gui-for-ffmpeg/convertor" to "git.kor-elf.net/kor-elf/gui-for-ffmpeg/src/convertor".
2024-02-10 20:32:29 +06:00
Leonid Nikitin a617635911
Changed the version to 0.3.1. 2024-02-10 20:23:41 +06:00
Leonid Nikitin 48f8c577c0
Fixed a buffer overflow error when converting videos when the video is more than 2 hours long. 2024-02-10 20:13:17 +06:00
Leonid Nikitin ee0a305972
I made sure that the current folder was saved. 2024-02-10 20:11:26 +06:00
Leonid Nikitin 3dc59344cb
Fix command "fyne get ..."
Renamed the module to git.kor-elf.net/kor-elf/gui-for-ffmpeg/src.
2024-02-04 21:00:55 +06:00
Leonid Nikitin f631c55eae Merge pull request '0.3.0' (#3) from develop into main
Reviewed-on: #3
2024-02-04 20:50:54 +06:00
Leonid Nikitin d46d642e61
Added the ability to automatically download FFmpeg from the site https://github.com/BtbN/FFmpeg-Builds/releases. 2024-02-04 20:16:15 +06:00
Leonid Nikitin a82d283c1a
Added licensed third party. 2024-02-04 15:57:37 +06:00
Leonid Nikitin b7c363faaa
Изменил файл README.md.
Added installation instructions and other useful information.
2024-02-03 20:16:16 +06:00
Leonid Nikitin 2c0e20210f
Переименовал модуль в git.kor-elf.net/kor-elf/gui-for-ffmpeg. 2024-02-03 18:40:44 +06:00
Leonid Nikitin eec298bfd7
Added a window with information about the program. 2024-02-03 17:47:32 +06:00
Leonid Nikitin 2afc5f5b1a
Fixed minor errors reported by the analyzer. 2024-02-01 00:31:28 +06:00
Leonid Nikitin fd7ce5fa08
Fixed minor errors reported by the analyzer. 2024-02-01 00:29:11 +06:00
Leonid Nikitin 05553f06b8
Fixed minor errors reported by the analyzer. 2024-02-01 00:28:06 +06:00
Leonid Nikitin 0d5cfab38d
Made it possible to change the path to FFmpeg and FFprobe. 2024-02-01 00:23:28 +06:00
Leonid Nikitin d86c0d37af
Updated some dependencies. 2024-01-31 22:27:50 +06:00
Leonid Nikitin f09dd01b6d
Fixed minor errors reported by the analyzer. 2024-01-31 21:22:38 +06:00
Leonid Nikitin 6358d5d8cc
The language selection is remembered.
Added a setting for changing the language.
2024-01-31 21:02:12 +06:00
Leonid Nikitin 5025807b14
Changed a.New Window.
Changed "GUI FFMpeg!" to "FFMpeg GUI!".
2024-01-30 20:38:12 +06:00
Leonid Nikitin 0cd32bb0f0 Merge pull request 'Версия 0.2.0' (#2) from develop into main
Reviewed-on: kor-elf/ffmpeg-gui#2
2024-01-28 22:45:56 +06:00
Leonid Nikitin f3e034356b
Merge branch 'main' into develop 2024-01-28 22:10:24 +06:00
Leonid Nikitin 6f0bbf7e29
Windows OS fix.
Added import "ffmpeg Gui/convertor" to convertor_windows.go
2024-01-28 22:08:40 +06:00
Leonid Nikitin 3c563d1966
Added the ability to select a language. 2024-01-28 22:01:16 +06:00
Leonid Nikitin 6df775955f
Minor optimization.
Raised "progress=end" after data:= Scanner Out.Text().
2024-01-27 21:17:04 +06:00
Leonid Nikitin d68382e418
Fixed a bug when the video was fully converted, but the progress bar showed not 100, but, for example, 99. 2024-01-27 21:08:27 +06:00
Leonid Nikitin 846986279c
Added icon. 2024-01-23 21:33:30 +06:00
Leonid Nikitin adf9bc9c27
Code refactoring.
Added files with functions for various OS (//go:build windows, //go:build !windows).
2024-01-23 21:33:01 +06:00
91 changed files with 7459 additions and 673 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
fyne-cross/*

1887
LICENSE-3RD-PARTY.txt Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,45 @@
# ffmpeg-gui
# GUI for FFmpeg
Простенький интерфейс к программе ffmpeg.
<p>Простенький интерфейс для консольной утилиты FFmpeg. Но я <strong>не являюсь</strong> автором самой утилиты <strong>FFmpeg</strong>.</p>
<p><strong>FFmpeg</strong> — торговая марка <strong><a href="http://bellard.org/" target="_blank">Fabrice Bellard</a></strong>, создателя проекта <strong><a href="https://ffmpeg.org/about.html" target="_blank">FFmpeg</a></strong>.</p>
<img src="images/screenshot-ffmpeg-gui.png">
<p>Программное обеспечение является MIT (см. <a href="https://git.kor-elf.net/kor-elf/gui-for-ffmpeg/src/branch/main/LICENSE">LICENSE</a>) и использует сторонние библиотеки, которые распространяются на их собственных условиях (см. <a href="https://git.kor-elf.net/kor-elf/gui-for-ffmpeg/src/branch/main/LICENSE-3RD-PARTY.txt">LICENSE-3RD-PARTY.txt</a>).</p>
<img src="images/screenshot-gui-for-ffmpeg.png">
<p>Скачать скомпилированные готовые версии можно тут: <a href="https://git.kor-elf.net/kor-elf/gui-for-ffmpeg/releases">https://git.kor-elf.net/kor-elf/gui-for-ffmpeg/releases</a>.</p>
## Установка через fyne:
1. go install fyne.io/fyne/v2/cmd/fyne@latest
2. fyne get git.kor-elf.net/kor-elf/gui-for-ffmpeg
## Скомпилировать через исходники:
1. git clone https://git.kor-elf.net/kor-elf/gui-for-ffmpeg.git
2. Переходим в папку проекта и там переходим в папку src: **cd gui-for-ffmpeg**
3. Ознакамливаемся, что нужно ещё установить для Вашей ОС для простого запуска (через go run) тут: https://docs.fyne.io/started/
4. *(не обязательный шаг)* Просто запустить можно так: **go run main.go**
5. go install github.com/fyne-io/fyne-cross@latest
* У Вас так же должен быть установлен docker
* О fyne-cross можно по подробней почитать тут: https://github.com/fyne-io/fyne-cross
6. * fyne-cross windows --icon icon.png --app-id "." -name "gui-for-ffmpeg"
* fyne-cross linux --icon icon.png --app-id "." -name "gui-for-ffmpeg"
7. Создаться папка **fyne-cross/bin** и там будет созданна папка с тем названием под которую Вы компилировали приложения (linux-amd64 или windows-amd64).
8. В папку **fyne-cross/bin/linux-amd64** или **fyne-cross/bin/windows-amd64** копируете:
* icon.png
* data
* languages
* LICENSE
* LICENSE-3RD-PARTY.txt
<p><strong>Структура должна получиться такая:</strong></p>
<img src="images/screenshot-folder-structure.png">
## Работа с переводами:
1. go install -v github.com/nicksnyder/go-i18n/v2/goi18n@latest
3. Создаём файл languages/translate.\*.toml
4. goi18n merge -sourceLanguage ru -outdir languages languages/active.\*.toml languages/translate.\*.toml
5. В файлах **languages/translate.\*.toml** переводим текст на нужный язык
6. goi18n merge -sourceLanguage ru -outdir languages languages/active.\*.toml languages/translate.\*.toml
___где * подставляем нужный язык___
Более подробно можно почитать тут: https://github.com/nicksnyder/go-i18n

36
convertor/repository.go Normal file
View File

@ -0,0 +1,36 @@
package convertor
import (
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/setting"
)
type RepositoryContract interface {
GetPathFfmpeg() (string, error)
SavePathFfmpeg(code string) (setting.Setting, error)
GetPathFfprobe() (string, error)
SavePathFfprobe(code string) (setting.Setting, error)
}
type Repository struct {
settingRepository setting.RepositoryContract
}
func NewRepository(settingRepository setting.RepositoryContract) *Repository {
return &Repository{settingRepository: settingRepository}
}
func (r Repository) GetPathFfmpeg() (string, error) {
return r.settingRepository.GetValue("ffmpeg")
}
func (r Repository) SavePathFfmpeg(path string) (setting.Setting, error) {
return r.settingRepository.CreateOrUpdate("ffmpeg", path)
}
func (r Repository) GetPathFfprobe() (string, error) {
return r.settingRepository.GetValue("ffprobe")
}
func (r Repository) SavePathFfprobe(path string) (setting.Setting, error) {
return r.settingRepository.CreateOrUpdate("ffprobe", path)
}

52
convertor/view.go Normal file
View File

@ -0,0 +1,52 @@
package convertor
import (
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/convertor/view"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel"
"github.com/nicksnyder/go-i18n/v2/i18n"
"image/color"
)
type ViewContract interface {
Main(
formConversion view.ConversionContract,
)
SelectFFPath(
ffmpegPath string,
ffprobePath string,
save func(ffmpegPath string, ffprobePath string) error,
cancel func(),
donwloadFFmpeg func(progressBar *widget.ProgressBar, progressMessage *canvas.Text) error,
)
}
type View struct {
app kernel.AppContract
}
func NewView(app kernel.AppContract) *View {
return &View{
app: app,
}
}
func (v View) Main(formConversion view.ConversionContract) {
converterVideoFilesTitle := v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "converterVideoFilesTitle",
})
v.app.GetWindow().SetContent(widget.NewCard(converterVideoFilesTitle, "", container.NewVScroll(formConversion.GetContent())))
formConversion.AfterViewContent()
}
func setStringErrorStyle(text *canvas.Text) {
text.Color = color.RGBA{R: 255, G: 0, B: 0, A: 255}
text.Refresh()
}
func setStringSuccessStyle(text *canvas.Text) {
text.Color = color.RGBA{R: 49, G: 127, B: 114, A: 255}
text.Refresh()
}

View File

@ -0,0 +1,444 @@
package view
import (
"errors"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/storage"
"fyne.io/fyne/v2/widget"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/convertor/view/form_items"
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"
)
type ConversionContract interface {
GetContent() fyne.CanvasObject
AfterViewContent()
}
type Conversion struct {
app kernel.AppContract
form *form
conversionMessage *canvas.Text
fileForConversion *fileForConversion
directoryForSaving *directoryForSaving
overwriteOutputFiles *overwriteOutputFiles
selectEncoder *selectEncoder
runConvert func(setting HandleConvertSetting)
}
type HandleConvertSetting struct {
FileInput kernel.File
DirectoryForSave string
OverwriteOutputFiles bool
Format string
Encoder encoder2.EncoderContract
}
func NewConversion(app kernel.AppContract, formats encoder.ConvertorFormatsContract, runConvert func(setting HandleConvertSetting)) *Conversion {
conversionMessage := canvas.NewText("", color.RGBA{R: 255, G: 0, B: 0, A: 255})
conversionMessage.TextSize = 16
conversionMessage.TextStyle = fyne.TextStyle{Bold: true}
fileForConversion := newFileForConversion(app)
directoryForSaving := newDirectoryForSaving(app)
overwriteOutputFiles := newOverwriteOutputFiles(app)
selectEncoder := newSelectEncoder(app, formats)
items := []*widget.FormItem{
{
Text: app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "fileForConversionTitle"}),
Widget: fileForConversion.button,
},
{
Widget: container.NewHScroll(fileForConversion.message),
},
{
Text: app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "buttonForSelectedDirTitle"}),
Widget: directoryForSaving.button,
},
{
Widget: container.NewHScroll(directoryForSaving.message),
},
{
Widget: overwriteOutputFiles.checkbox,
},
{
Widget: selectEncoder.SelectFileType,
},
{
Text: app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "selectFormat"}),
Widget: selectEncoder.SelectFormat,
},
{
Text: app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "selectEncoder"}),
Widget: selectEncoder.SelectEncoder,
},
}
form := newForm(app, items)
return &Conversion{
app: app,
form: form,
conversionMessage: conversionMessage,
fileForConversion: fileForConversion,
directoryForSaving: directoryForSaving,
overwriteOutputFiles: overwriteOutputFiles,
selectEncoder: selectEncoder,
runConvert: runConvert,
}
}
func (c Conversion) GetContent() fyne.CanvasObject {
c.form.form.OnSubmit = c.submit
c.fileForConversion.AddChangeCallback(c.selectFileForConversion)
c.selectEncoder.AddChangeCallback(c.changeEncoder)
if c.selectEncoder.Encoder != nil {
c.selectEncoder.SelectEncoder.SetSelectedIndex(c.selectEncoder.SelectEncoder.SelectedIndex())
}
return container.NewVBox(
c.form.form,
c.conversionMessage,
)
}
func (c Conversion) changeEncoder(encoder encoder2.EncoderContract) {
items := []*widget.FormItem{}
if form_items.Views[encoder.GetName()] != nil {
items = form_items.Views[encoder.GetName()](encoder, c.app)
}
c.form.ChangeItems(items)
}
func (c Conversion) AfterViewContent() {
c.form.form.Disable()
}
func (c Conversion) selectFileForConversion(err error) {
c.conversionMessage.Text = ""
if err != nil {
c.form.form.Disable()
return
}
c.form.form.Enable()
}
func (c Conversion) submit() {
if len(c.directoryForSaving.path) == 0 {
showConversionMessage(c.conversionMessage, errors.New(c.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "errorSelectedFolderSave",
})))
c.enableFormConversion()
return
}
if len(c.selectEncoder.SelectFormat.Selected) == 0 {
showConversionMessage(c.conversionMessage, errors.New(c.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "errorSelectedFormat",
})))
return
}
if c.selectEncoder.Encoder == nil {
showConversionMessage(c.conversionMessage, errors.New(c.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "errorSelectedEncoder",
})))
return
}
c.conversionMessage.Text = ""
c.fileForConversion.button.Disable()
c.directoryForSaving.button.Disable()
c.form.form.Disable()
setting := HandleConvertSetting{
FileInput: *c.fileForConversion.file,
DirectoryForSave: c.directoryForSaving.path,
OverwriteOutputFiles: c.overwriteOutputFiles.IsChecked(),
Format: c.selectEncoder.SelectFormat.Selected,
Encoder: c.selectEncoder.Encoder,
}
c.runConvert(setting)
c.enableFormConversion()
c.fileForConversion.message.Text = ""
c.form.form.Disable()
}
func (c Conversion) enableFormConversion() {
c.fileForConversion.button.Enable()
c.directoryForSaving.button.Enable()
c.form.form.Enable()
}
type fileForConversion struct {
button *widget.Button
message *canvas.Text
file *kernel.File
changeCallbacks map[int]func(err error)
}
func newFileForConversion(app kernel.AppContract) *fileForConversion {
fileForConversion := &fileForConversion{
file: &kernel.File{},
changeCallbacks: map[int]func(err error){},
}
buttonTitle := app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "choose",
})
fileForConversion.message = canvas.NewText("", color.RGBA{R: 255, G: 0, B: 0, A: 255})
fileForConversion.message.TextSize = 16
fileForConversion.message.TextStyle = fyne.TextStyle{Bold: true}
var locationURI fyne.ListableURI
fileForConversion.button = widget.NewButton(buttonTitle, func() {
app.GetWindow().NewFileOpen(func(r fyne.URIReadCloser, err error) {
if err != nil {
fileForConversion.message.Text = err.Error()
setStringErrorStyle(fileForConversion.message)
fileForConversion.eventSelectFile(err)
return
}
if r == nil {
return
}
fileForConversion.file.Path = r.URI().Path()
fileForConversion.file.Name = r.URI().Name()
fileForConversion.file.Ext = r.URI().Extension()
fileForConversion.message.Text = r.URI().Path()
setStringSuccessStyle(fileForConversion.message)
fileForConversion.eventSelectFile(nil)
listableURI := storage.NewFileURI(filepath.Dir(r.URI().Path()))
locationURI, err = storage.ListerForURI(listableURI)
}, locationURI)
})
return fileForConversion
}
func (c fileForConversion) AddChangeCallback(callback func(err error)) {
c.changeCallbacks[len(c.changeCallbacks)] = callback
}
func (c fileForConversion) eventSelectFile(err error) {
for _, changeCallback := range c.changeCallbacks {
changeCallback(err)
}
}
type directoryForSaving struct {
button *widget.Button
message *canvas.Text
path string
}
func newDirectoryForSaving(app kernel.AppContract) *directoryForSaving {
directoryForSaving := &directoryForSaving{
path: "",
}
directoryForSaving.message = canvas.NewText("", color.RGBA{R: 255, G: 0, B: 0, A: 255})
directoryForSaving.message.TextSize = 16
directoryForSaving.message.TextStyle = fyne.TextStyle{Bold: true}
buttonTitle := app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "choose",
})
var locationURI fyne.ListableURI
directoryForSaving.button = widget.NewButton(buttonTitle, func() {
app.GetWindow().NewFolderOpen(func(r fyne.ListableURI, err error) {
if err != nil {
directoryForSaving.message.Text = err.Error()
setStringErrorStyle(directoryForSaving.message)
return
}
if r == nil {
return
}
directoryForSaving.path = r.Path()
directoryForSaving.message.Text = r.Path()
setStringSuccessStyle(directoryForSaving.message)
locationURI, _ = storage.ListerForURI(r)
}, locationURI)
})
return directoryForSaving
}
type overwriteOutputFiles struct {
checkbox *widget.Check
isChecked bool
}
func newOverwriteOutputFiles(app kernel.AppContract) *overwriteOutputFiles {
overwriteOutputFiles := &overwriteOutputFiles{
isChecked: false,
}
checkboxOverwriteOutputFilesTitle := app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "checkboxOverwriteOutputFilesTitle",
})
overwriteOutputFiles.checkbox = widget.NewCheck(checkboxOverwriteOutputFilesTitle, func(b bool) {
overwriteOutputFiles.isChecked = b
})
return overwriteOutputFiles
}
func (receiver overwriteOutputFiles) IsChecked() bool {
return receiver.isChecked
}
type selectEncoder struct {
SelectFileType *widget.RadioGroup
SelectFormat *widget.Select
SelectEncoder *widget.Select
Encoder encoder2.EncoderContract
changeCallbacks map[int]func(encoder encoder2.EncoderContract)
}
func newSelectEncoder(app kernel.AppContract, formats encoder.ConvertorFormatsContract) *selectEncoder {
selectEncoder := &selectEncoder{
changeCallbacks: map[int]func(encoder encoder2.EncoderContract){},
}
encoders := map[int]encoder2.EncoderDataContract{}
selectEncoder.SelectEncoder = widget.NewSelect([]string{}, func(s string) {
if encoders[selectEncoder.SelectEncoder.SelectedIndex()] == nil {
return
}
selectEncoderData := encoders[selectEncoder.SelectEncoder.SelectedIndex()]
selectEncoder.ChangeEncoder(selectEncoderData.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 = map[int]encoder2.EncoderDataContract{}
for _, e := range format.GetEncoders() {
encoders[len(encoders)] = e
encoderOptions = append(encoderOptions, 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())
}
encoderGroupVideo := app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "encoderGroupVideo"})
encoderGroupAudio := app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "encoderGroupAudio"})
encoderGroupImage := app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "encoderGroupImage"})
encoderGroup := map[string]string{
encoderGroupVideo: "video",
encoderGroupAudio: "audio",
encoderGroupImage: "image",
}
selectEncoder.SelectFileType = widget.NewRadioGroup([]string{encoderGroupVideo, encoderGroupAudio, encoderGroupImage}, func(s string) {
groupCode := encoderGroup[s]
formatOptions := []string{}
for _, f := range formats.GetFormats() {
if groupCode != f.GetFileType().Name() {
continue
}
formatOptions = append(formatOptions, f.GetTitle())
}
selectEncoder.SelectFormat.SetOptions(formatOptions)
if groupCode == encoder2.FileType(encoder2.Video).Name() {
selectEncoder.SelectFormat.SetSelected("mp4")
} else {
selectEncoder.SelectFormat.SetSelectedIndex(0)
}
})
selectEncoder.SelectFileType.Horizontal = true
selectEncoder.SelectFileType.Required = true
selectEncoder.SelectFileType.SetSelected(encoderGroupVideo)
return selectEncoder
}
func (e *selectEncoder) ChangeEncoder(encoder encoder2.EncoderContract) {
e.Encoder = encoder
e.eventSelectEncoder(e.Encoder)
}
func (e *selectEncoder) AddChangeCallback(callback func(encoder encoder2.EncoderContract)) {
e.changeCallbacks[len(e.changeCallbacks)] = callback
}
func (e *selectEncoder) eventSelectEncoder(encoder encoder2.EncoderContract) {
for _, changeCallback := range e.changeCallbacks {
changeCallback(encoder)
}
}
func setStringErrorStyle(text *canvas.Text) {
text.Color = color.RGBA{R: 255, G: 0, B: 0, A: 255}
text.Refresh()
}
func setStringSuccessStyle(text *canvas.Text) {
text.Color = color.RGBA{R: 49, G: 127, B: 114, A: 255}
text.Refresh()
}
func showConversionMessage(conversionMessage *canvas.Text, err error) {
conversionMessage.Text = err.Error()
setStringErrorStyle(conversionMessage)
}
type form struct {
form *widget.Form
items []*widget.FormItem
}
func newForm(app kernel.AppContract, items []*widget.FormItem) *form {
f := widget.NewForm()
f.SubmitText = app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "converterVideoFilesSubmitTitle",
})
f.Items = items
return &form{
form: f,
items: items,
}
}
func (f form) ChangeItems(items []*widget.FormItem) {
f.form.Items = f.items
f.form.Refresh()
f.form.Items = append(f.form.Items, items...)
f.form.Refresh()
}

View File

@ -0,0 +1,16 @@
package form_items
import (
"fyne.io/fyne/v2/widget"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/convertor/view/form_items/h264_nvenc"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/convertor/view/form_items/libx264"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/convertor/view/form_items/libx265"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel"
)
var Views = map[string]func(encoder encoder.EncoderContract, app kernel.AppContract) []*widget.FormItem{
"libx264": libx264.View,
"h264_nvenc": h264_nvenc.View,
"libx265": libx265.View,
}

View File

@ -0,0 +1,65 @@
package h264_nvenc
import (
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/h264_nvenc"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel"
"github.com/nicksnyder/go-i18n/v2/i18n"
)
func View(encoder encoder.EncoderContract, app kernel.AppContract) []*widget.FormItem {
items := []*widget.FormItem{}
items = append(items, presetParameter(encoder, app)...)
return items
}
func presetParameter(encoder encoder.EncoderContract, app kernel.AppContract) []*widget.FormItem {
parameter, err := encoder.GetParameter("preset")
if err != nil {
return nil
}
presets := map[string]string{}
presetsForSelect := []string{}
presetDefault := ""
for _, name := range h264_nvenc.Presets {
title := name
presetsForSelect = append(presetsForSelect, name)
presets[title] = name
if name == parameter.Get() {
presetDefault = title
}
}
elementSelect := widget.NewSelect(presetsForSelect, func(s string) {
if presets[s] == "" {
return
}
parameter.Set(presets[s])
})
elementSelect.SetSelected(presetDefault)
elementSelect.Hide()
checkboxTitle := app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "parameterCheckbox"})
elementCheckbox := widget.NewCheck(checkboxTitle, func(b bool) {
if b == true {
parameter.SetEnable()
elementSelect.Show()
return
}
parameter.SetDisable()
elementSelect.Hide()
})
return []*widget.FormItem{
{
Text: app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "formPreset"}),
Widget: container.NewVBox(elementCheckbox, elementSelect),
},
}
}

View File

@ -0,0 +1,65 @@
package libx264
import (
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/libx264"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel"
"github.com/nicksnyder/go-i18n/v2/i18n"
)
func View(encoder encoder.EncoderContract, app kernel.AppContract) []*widget.FormItem {
items := []*widget.FormItem{}
items = append(items, presetParameter(encoder, app)...)
return items
}
func presetParameter(encoder encoder.EncoderContract, app kernel.AppContract) []*widget.FormItem {
parameter, err := encoder.GetParameter("preset")
if err != nil {
return nil
}
presets := map[string]string{}
presetsForSelect := []string{}
presetDefault := ""
for _, name := range libx264.Presets {
title := app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "preset_" + name})
presetsForSelect = append(presetsForSelect, title)
presets[title] = name
if name == parameter.Get() {
presetDefault = title
}
}
elementSelect := widget.NewSelect(presetsForSelect, func(s string) {
if presets[s] == "" {
return
}
parameter.Set(presets[s])
})
elementSelect.SetSelected(presetDefault)
elementSelect.Hide()
checkboxTitle := app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "parameterCheckbox"})
elementCheckbox := widget.NewCheck(checkboxTitle, func(b bool) {
if b == true {
parameter.SetEnable()
elementSelect.Show()
return
}
parameter.SetDisable()
elementSelect.Hide()
})
return []*widget.FormItem{
{
Text: app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "formPreset"}),
Widget: container.NewVBox(elementCheckbox, elementSelect),
},
}
}

View File

@ -0,0 +1,65 @@
package libx265
import (
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder/libx265"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel"
"github.com/nicksnyder/go-i18n/v2/i18n"
)
func View(encoder encoder.EncoderContract, app kernel.AppContract) []*widget.FormItem {
items := []*widget.FormItem{}
items = append(items, presetParameter(encoder, app)...)
return items
}
func presetParameter(encoder encoder.EncoderContract, app kernel.AppContract) []*widget.FormItem {
parameter, err := encoder.GetParameter("preset")
if err != nil {
return nil
}
presets := map[string]string{}
presetsForSelect := []string{}
presetDefault := ""
for _, name := range libx265.Presets {
title := app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "preset_" + name})
presetsForSelect = append(presetsForSelect, title)
presets[title] = name
if name == parameter.Get() {
presetDefault = title
}
}
elementSelect := widget.NewSelect(presetsForSelect, func(s string) {
if presets[s] == "" {
return
}
parameter.Set(presets[s])
})
elementSelect.SetSelected(presetDefault)
elementSelect.Hide()
checkboxTitle := app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "parameterCheckbox"})
elementCheckbox := widget.NewCheck(checkboxTitle, func(b bool) {
if b == true {
parameter.SetEnable()
elementSelect.Show()
return
}
parameter.SetDisable()
elementSelect.Hide()
})
return []*widget.FormItem{
{
Text: app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "formPreset"}),
Widget: container.NewVBox(elementCheckbox, elementSelect),
},
}
}

130
convertor/view_setting.go Normal file
View File

@ -0,0 +1,130 @@
package convertor
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/storage"
"fyne.io/fyne/v2/widget"
"github.com/nicksnyder/go-i18n/v2/i18n"
"image/color"
"net/url"
"path/filepath"
)
func (v View) SelectFFPath(
currentPathFfmpeg string,
currentPathFfprobe string,
save func(ffmpegPath string, ffprobePath string) error,
cancel func(),
donwloadFFmpeg func(progressBar *widget.ProgressBar, progressMessage *canvas.Text) error,
) {
errorMessage := canvas.NewText("", color.RGBA{R: 255, G: 0, B: 0, A: 255})
errorMessage.TextSize = 16
errorMessage.TextStyle = fyne.TextStyle{Bold: true}
ffmpegPath, buttonFFmpeg, buttonFFmpegMessage := v.getButtonSelectFile(currentPathFfmpeg)
ffprobePath, buttonFFprobe, buttonFFprobeMessage := v.getButtonSelectFile(currentPathFfprobe)
link := widget.NewHyperlink("https://ffmpeg.org/download.html", &url.URL{
Scheme: "https",
Host: "ffmpeg.org",
Path: "download.html",
})
form := &widget.Form{
Items: []*widget.FormItem{
{
Text: v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "titleDownloadLink",
}),
Widget: link,
},
{
Text: v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "pathToFfmpeg",
}),
Widget: buttonFFmpeg,
},
{
Widget: container.NewHScroll(buttonFFmpegMessage),
},
{
Text: v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "pathToFfprobe",
}),
Widget: buttonFFprobe,
},
{
Widget: container.NewHScroll(buttonFFprobeMessage),
},
{
Widget: errorMessage,
},
},
SubmitText: v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "save",
}),
OnSubmit: func() {
err := save(*ffmpegPath, *ffprobePath)
if err != nil {
errorMessage.Text = err.Error()
}
},
}
if cancel != nil {
form.OnCancel = cancel
form.CancelText = v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "cancel",
})
}
selectFFPathTitle := v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "selectFFPathTitle",
})
v.app.GetWindow().SetContent(widget.NewCard(selectFFPathTitle, "", container.NewVBox(
form,
v.blockDownloadFFmpeg(donwloadFFmpeg),
)))
}
func (v View) getButtonSelectFile(path string) (filePath *string, button *widget.Button, buttonMessage *canvas.Text) {
filePath = &path
buttonMessage = canvas.NewText(path, color.RGBA{R: 49, G: 127, B: 114, A: 255})
buttonMessage.TextSize = 16
buttonMessage.TextStyle = fyne.TextStyle{Bold: true}
buttonTitle := v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "choose",
})
var locationURI fyne.ListableURI
if len(path) > 0 {
listableURI := storage.NewFileURI(filepath.Dir(path))
locationURI, _ = storage.ListerForURI(listableURI)
}
button = widget.NewButton(buttonTitle, func() {
v.app.GetWindow().NewFileOpen(func(r fyne.URIReadCloser, err error) {
if err != nil {
buttonMessage.Text = err.Error()
setStringErrorStyle(buttonMessage)
return
}
if r == nil {
return
}
path = r.URI().Path()
buttonMessage.Text = r.URI().Path()
setStringSuccessStyle(buttonMessage)
listableURI := storage.NewFileURI(filepath.Dir(r.URI().Path()))
locationURI, _ = storage.ListerForURI(listableURI)
}, locationURI)
})
return
}

View File

@ -0,0 +1,17 @@
//go:build !windows
// +build !windows
package convertor
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget"
)
func (v View) blockDownloadFFmpeg(
donwloadFFmpeg func(progressBar *widget.ProgressBar, progressMessage *canvas.Text) error,
) *fyne.Container {
return container.NewVBox()
}

View File

@ -0,0 +1,63 @@
//go:build windows
// +build windows
package convertor
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget"
"github.com/nicksnyder/go-i18n/v2/i18n"
"golang.org/x/image/colornames"
"image/color"
)
func (v View) blockDownloadFFmpeg(
donwloadFFmpeg func(progressBar *widget.ProgressBar, progressMessage *canvas.Text) error,
) *fyne.Container {
errorDownloadFFmpegMessage := canvas.NewText("", color.RGBA{R: 255, G: 0, B: 0, A: 255})
errorDownloadFFmpegMessage.TextSize = 16
errorDownloadFFmpegMessage.TextStyle = fyne.TextStyle{Bold: true}
progressDownloadFFmpegMessage := canvas.NewText("", color.RGBA{R: 49, G: 127, B: 114, A: 255})
progressDownloadFFmpegMessage.TextSize = 16
progressDownloadFFmpegMessage.TextStyle = fyne.TextStyle{Bold: true}
progressBar := widget.NewProgressBar()
var buttonDownloadFFmpeg *widget.Button
buttonDownloadFFmpeg = widget.NewButton(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "download",
}), func() {
buttonDownloadFFmpeg.Disable()
err := donwloadFFmpeg(progressBar, progressDownloadFFmpegMessage)
if err != nil {
errorDownloadFFmpegMessage.Text = err.Error()
}
buttonDownloadFFmpeg.Enable()
})
downloadFFmpegFromSiteMessage := v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "downloadFFmpegFromSite",
})
return container.NewVBox(
canvas.NewLine(colornames.Darkgreen),
widget.NewCard(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "buttonDownloadFFmpeg",
}), "", container.NewVBox(
widget.NewRichTextFromMarkdown(
downloadFFmpegFromSiteMessage+" [https://github.com/BtbN/FFmpeg-Builds/releases](https://github.com/BtbN/FFmpeg-Builds/releases)",
),
buttonDownloadFFmpeg,
errorDownloadFFmpegMessage,
progressDownloadFFmpegMessage,
progressBar,
)),
)
}

19
encoder/apng/encoder.go Normal file
View File

@ -0,0 +1,19 @@
package apng
import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder"
func NewEncoder() encoder2.EncoderContract {
parameters := map[string]encoder2.ParameterContract{}
getParams := func(parameters map[string]encoder2.ParameterContract) []string {
return []string{"-c:v", "apng"}
}
return encoder2.NewEncoder("apng", parameters, getParams)
}
func NewData() encoder2.EncoderDataContract {
title := "apng"
formats := []string{"apng"}
fileType := encoder2.FileType(encoder2.Image)
return encoder2.NewData(title, formats, fileType, NewEncoder)
}

19
encoder/bmp/encoder.go Normal file
View File

@ -0,0 +1,19 @@
package bmp
import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder"
func NewEncoder() encoder2.EncoderContract {
parameters := map[string]encoder2.ParameterContract{}
getParams := func(parameters map[string]encoder2.ParameterContract) []string {
return []string{"-c:v", "bmp"}
}
return encoder2.NewEncoder("bmp", parameters, getParams)
}
func NewData() encoder2.EncoderDataContract {
title := "bmp"
formats := []string{"bmp"}
fileType := encoder2.FileType(encoder2.Image)
return encoder2.NewData(title, formats, fileType, NewEncoder)
}

169
encoder/encoder.go Normal file
View File

@ -0,0 +1,169 @@
package encoder
import "errors"
type EncoderContract interface {
GetName() string
GetParams() []string
GetParameter(name string) (ParameterContract, error)
}
type ParameterContract interface {
GetName() string
Set(string) error
Get() string
IsEnabled() bool
SetEnable()
SetDisable()
}
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),
}
}
type Encoder struct {
name string
parameters map[string]ParameterContract
getParams func(parameters map[string]ParameterContract) []string
}
func NewEncoder(name string, parameters map[string]ParameterContract, getParams func(parameters map[string]ParameterContract) []string) *Encoder {
return &Encoder{
name: name,
parameters: parameters,
getParams: getParams,
}
}
func (e *Encoder) GetName() string {
return e.name
}
func (e *Encoder) GetParams() []string {
return e.getParams(e.parameters)
}
func (e *Encoder) GetParameter(name string) (ParameterContract, error) {
if e.parameters[name] == nil {
return nil, errors.New("parameter not found")
}
return e.parameters[name], nil
}
type Parameter struct {
name string
isEnabled bool
parameter string
setParameter func(string) (string, error)
}
func NewParameter(name string, isEnabled bool, defaultParameter string, setParameter func(string) (string, error)) *Parameter {
return &Parameter{
name: name,
isEnabled: isEnabled,
parameter: defaultParameter,
setParameter: setParameter,
}
}
func (p *Parameter) GetName() string {
return p.name
}
func (p *Parameter) Set(s string) (err error) {
if p.setParameter != nil {
s, err = p.setParameter(s)
if err != nil {
return err
}
}
p.parameter = s
return nil
}
func (p *Parameter) Get() string {
return p.parameter
}
func (p *Parameter) IsEnabled() bool {
return p.isEnabled
}
func (p *Parameter) SetEnable() {
p.isEnabled = true
}
func (p *Parameter) SetDisable() {
p.isEnabled = false
}

19
encoder/flv/encoder.go Normal file
View File

@ -0,0 +1,19 @@
package flv
import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder"
func NewEncoder() encoder2.EncoderContract {
parameters := map[string]encoder2.ParameterContract{}
getParams := func(parameters map[string]encoder2.ParameterContract) []string {
return []string{"-c:v", "flv"}
}
return encoder2.NewEncoder("flv", parameters, getParams)
}
func NewData() encoder2.EncoderDataContract {
title := "flv"
formats := []string{"flv"}
fileType := encoder2.FileType(encoder2.Video)
return encoder2.NewData(title, formats, fileType, NewEncoder)
}

19
encoder/gif/encoder.go Normal file
View File

@ -0,0 +1,19 @@
package gif
import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder"
func NewEncoder() encoder2.EncoderContract {
parameters := map[string]encoder2.ParameterContract{}
getParams := func(parameters map[string]encoder2.ParameterContract) []string {
return []string{"-c:v", "gif"}
}
return encoder2.NewEncoder("gif", parameters, getParams)
}
func NewData() encoder2.EncoderDataContract {
title := "gif"
formats := []string{"gif"}
fileType := encoder2.FileType(encoder2.Image)
return encoder2.NewData(title, formats, fileType, NewEncoder)
}

View File

@ -0,0 +1,58 @@
package h264_nvenc
import (
"errors"
encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder"
)
var Presets = []string{
"default",
"slow",
"medium",
"fast",
"hp",
"hq",
"bd",
"ll",
"llhq",
"llhp",
"lossless",
"losslesshp",
}
func NewEncoder() encoder2.EncoderContract {
parameters := map[string]encoder2.ParameterContract{
"preset": newParameterPreset(),
}
getParams := func(parameters map[string]encoder2.ParameterContract) []string {
params := []string{"-c:v", "h264_nvenc"}
if parameters["preset"] != nil && parameters["preset"].IsEnabled() {
params = append(params, "-preset", parameters["preset"].Get())
}
return params
}
return encoder2.NewEncoder("h264_nvenc", parameters, getParams)
}
func NewData() encoder2.EncoderDataContract {
title := "h264_nvenc"
formats := []string{"mp4"}
fileType := encoder2.FileType(encoder2.Video)
return encoder2.NewData(title, formats, fileType, NewEncoder)
}
func newParameterPreset() encoder2.ParameterContract {
setParameter := func(s string) (string, error) {
for _, value := range Presets {
if value == s {
return value, nil
}
}
return "", errors.New("preset not found")
}
return encoder2.NewParameter("preset", false, "default", setParameter)
}

View File

@ -0,0 +1,19 @@
package libmp3lame
import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder"
func NewEncoder() encoder2.EncoderContract {
parameters := map[string]encoder2.ParameterContract{}
getParams := func(parameters map[string]encoder2.ParameterContract) []string {
return []string{"-c:a", "libmp3lame"}
}
return encoder2.NewEncoder("libmp3lame", parameters, getParams)
}
func NewData() encoder2.EncoderDataContract {
title := "libmp3lame"
formats := []string{"mp3"}
fileType := encoder2.FileType(encoder2.Audio)
return encoder2.NewData(title, formats, fileType, NewEncoder)
}

View File

@ -0,0 +1,19 @@
package libshine
import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder"
func NewEncoder() encoder2.EncoderContract {
parameters := map[string]encoder2.ParameterContract{}
getParams := func(parameters map[string]encoder2.ParameterContract) []string {
return []string{"-c:a", "libshine"}
}
return encoder2.NewEncoder("libshine", parameters, getParams)
}
func NewData() encoder2.EncoderDataContract {
title := "libshine"
formats := []string{"mp3"}
fileType := encoder2.FileType(encoder2.Audio)
return encoder2.NewData(title, formats, fileType, NewEncoder)
}

View File

@ -0,0 +1,19 @@
package libtwolame
import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder"
func NewEncoder() encoder2.EncoderContract {
parameters := map[string]encoder2.ParameterContract{}
getParams := func(parameters map[string]encoder2.ParameterContract) []string {
return []string{"-c:a", "libtwolame"}
}
return encoder2.NewEncoder("libtwolame", parameters, getParams)
}
func NewData() encoder2.EncoderDataContract {
title := "libtwolame"
formats := []string{"mp2"}
fileType := encoder2.FileType(encoder2.Audio)
return encoder2.NewData(title, formats, fileType, NewEncoder)
}

19
encoder/libvpx/encoder.go Normal file
View File

@ -0,0 +1,19 @@
package libvpx
import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder"
func NewEncoder() encoder2.EncoderContract {
parameters := map[string]encoder2.ParameterContract{}
getParams := func(parameters map[string]encoder2.ParameterContract) []string {
return []string{"-c:v", "libvpx"}
}
return encoder2.NewEncoder("libvpx", parameters, getParams)
}
func NewData() encoder2.EncoderDataContract {
title := "libvpx"
formats := []string{"webm", "mkv"}
fileType := encoder2.FileType(encoder2.Video)
return encoder2.NewData(title, formats, fileType, NewEncoder)
}

View File

@ -0,0 +1,19 @@
package libvpx_vp9
import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder"
func NewEncoder() encoder2.EncoderContract {
parameters := map[string]encoder2.ParameterContract{}
getParams := func(parameters map[string]encoder2.ParameterContract) []string {
return []string{"-c:v", "libvpx-vp9"}
}
return encoder2.NewEncoder("libvpx_vp9", parameters, getParams)
}
func NewData() encoder2.EncoderDataContract {
title := "libvpx-vp9"
formats := []string{"webm", "mkv"}
fileType := encoder2.FileType(encoder2.Video)
return encoder2.NewData(title, formats, fileType, NewEncoder)
}

View File

@ -0,0 +1,19 @@
package libwebp
import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder"
func NewEncoder() encoder2.EncoderContract {
parameters := map[string]encoder2.ParameterContract{}
getParams := func(parameters map[string]encoder2.ParameterContract) []string {
return []string{"-c:v", "libwebp"}
}
return encoder2.NewEncoder("libwebp", parameters, getParams)
}
func NewData() encoder2.EncoderDataContract {
title := "libwebp"
formats := []string{"webp"}
fileType := encoder2.FileType(encoder2.Image)
return encoder2.NewData(title, formats, fileType, NewEncoder)
}

View File

@ -0,0 +1,19 @@
package libwebp_anim
import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder"
func NewEncoder() encoder2.EncoderContract {
parameters := map[string]encoder2.ParameterContract{}
getParams := func(parameters map[string]encoder2.ParameterContract) []string {
return []string{"-c:v", "libwebp_anim"}
}
return encoder2.NewEncoder("libwebp_anim", parameters, getParams)
}
func NewData() encoder2.EncoderDataContract {
title := "libwebp_anim"
formats := []string{"webp"}
fileType := encoder2.FileType(encoder2.Image)
return encoder2.NewData(title, formats, fileType, NewEncoder)
}

View File

@ -0,0 +1,56 @@
package libx264
import (
"errors"
encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder"
)
var Presets = []string{
"ultrafast",
"superfast",
"veryfast",
"faster",
"fast",
"medium",
"slow",
"slower",
"veryslow",
"placebo",
}
func NewEncoder() encoder2.EncoderContract {
parameters := map[string]encoder2.ParameterContract{
"preset": newParameterPreset(),
}
getParams := func(parameters map[string]encoder2.ParameterContract) []string {
params := []string{"-c:v", "libx264"}
if parameters["preset"] != nil && parameters["preset"].IsEnabled() {
params = append(params, "-preset", parameters["preset"].Get())
}
return params
}
return encoder2.NewEncoder("libx264", parameters, getParams)
}
func NewData() encoder2.EncoderDataContract {
title := "libx264"
formats := []string{"mp4"}
fileType := encoder2.FileType(encoder2.Video)
return encoder2.NewData(title, formats, fileType, NewEncoder)
}
func newParameterPreset() encoder2.ParameterContract {
setParameter := func(s string) (string, error) {
for _, value := range Presets {
if value == s {
return value, nil
}
}
return "", errors.New("preset not found")
}
return encoder2.NewParameter("preset", false, "medium", setParameter)
}

View File

@ -0,0 +1,56 @@
package libx265
import (
"errors"
encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder"
)
var Presets = []string{
"ultrafast",
"superfast",
"veryfast",
"faster",
"fast",
"medium",
"slow",
"slower",
"veryslow",
"placebo",
}
func NewEncoder() encoder2.EncoderContract {
parameters := map[string]encoder2.ParameterContract{
"preset": newParameterPreset(),
}
getParams := func(parameters map[string]encoder2.ParameterContract) []string {
params := []string{"-c:v", "libx265"}
if parameters["preset"] != nil && parameters["preset"].IsEnabled() {
params = append(params, "-preset", parameters["preset"].Get())
}
return params
}
return encoder2.NewEncoder("libx265", parameters, getParams)
}
func NewData() encoder2.EncoderDataContract {
title := "libx265"
formats := []string{"mp4"}
fileType := encoder2.FileType(encoder2.Video)
return encoder2.NewData(title, formats, fileType, NewEncoder)
}
func newParameterPreset() encoder2.ParameterContract {
setParameter := func(s string) (string, error) {
for _, value := range Presets {
if value == s {
return value, nil
}
}
return "", errors.New("preset not found")
}
return encoder2.NewParameter("preset", false, "medium", setParameter)
}

View File

@ -0,0 +1,19 @@
package libxvid
import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder"
func NewEncoder() encoder2.EncoderContract {
parameters := map[string]encoder2.ParameterContract{}
getParams := func(parameters map[string]encoder2.ParameterContract) []string {
return []string{"-c:v", "libxvid"}
}
return encoder2.NewEncoder("libxvid", parameters, getParams)
}
func NewData() encoder2.EncoderDataContract {
title := "libxvid"
formats := []string{"avi"}
fileType := encoder2.FileType(encoder2.Video)
return encoder2.NewData(title, formats, fileType, NewEncoder)
}

19
encoder/mjpeg/encoder.go Normal file
View File

@ -0,0 +1,19 @@
package mjpeg
import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder"
func NewEncoder() encoder2.EncoderContract {
parameters := map[string]encoder2.ParameterContract{}
getParams := func(parameters map[string]encoder2.ParameterContract) []string {
return []string{"-c:v", "mjpeg"}
}
return encoder2.NewEncoder("mjpeg", parameters, getParams)
}
func NewData() encoder2.EncoderDataContract {
title := "mjpeg"
formats := []string{"jpg"}
fileType := encoder2.FileType(encoder2.Image)
return encoder2.NewData(title, formats, fileType, NewEncoder)
}

19
encoder/mp2/encoder.go Normal file
View File

@ -0,0 +1,19 @@
package mp2
import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder"
func NewEncoder() encoder2.EncoderContract {
parameters := map[string]encoder2.ParameterContract{}
getParams := func(parameters map[string]encoder2.ParameterContract) []string {
return []string{"-c:a", "mp2"}
}
return encoder2.NewEncoder("mp2", parameters, getParams)
}
func NewData() encoder2.EncoderDataContract {
title := "mp2"
formats := []string{"mp2"}
fileType := encoder2.FileType(encoder2.Audio)
return encoder2.NewData(title, formats, fileType, NewEncoder)
}

View File

@ -0,0 +1,19 @@
package mp2fixed
import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder"
func NewEncoder() encoder2.EncoderContract {
parameters := map[string]encoder2.ParameterContract{}
getParams := func(parameters map[string]encoder2.ParameterContract) []string {
return []string{"-c:a", "mp2fixed"}
}
return encoder2.NewEncoder("mp2fixed", parameters, getParams)
}
func NewData() encoder2.EncoderDataContract {
title := "mp2fixed"
formats := []string{"mp2"}
fileType := encoder2.FileType(encoder2.Audio)
return encoder2.NewData(title, formats, fileType, NewEncoder)
}

View File

@ -0,0 +1,19 @@
package mpeg1video
import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder"
func NewEncoder() encoder2.EncoderContract {
parameters := map[string]encoder2.ParameterContract{}
getParams := func(parameters map[string]encoder2.ParameterContract) []string {
return []string{"-c:v", "mpeg1video"}
}
return encoder2.NewEncoder("mpeg1video", parameters, getParams)
}
func NewData() encoder2.EncoderDataContract {
title := "mpeg1video"
formats := []string{"mpg", "mpeg"}
fileType := encoder2.FileType(encoder2.Video)
return encoder2.NewData(title, formats, fileType, NewEncoder)
}

View File

@ -0,0 +1,19 @@
package mpeg2video
import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder"
func NewEncoder() encoder2.EncoderContract {
parameters := map[string]encoder2.ParameterContract{}
getParams := func(parameters map[string]encoder2.ParameterContract) []string {
return []string{"-c:v", "mpeg2video"}
}
return encoder2.NewEncoder("mpeg2video", parameters, getParams)
}
func NewData() encoder2.EncoderDataContract {
title := "mpeg2video"
formats := []string{"mpg", "mpeg"}
fileType := encoder2.FileType(encoder2.Video)
return encoder2.NewData(title, formats, fileType, NewEncoder)
}

19
encoder/mpeg4/encoder.go Normal file
View File

@ -0,0 +1,19 @@
package mpeg4
import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder"
func NewEncoder() encoder2.EncoderContract {
parameters := map[string]encoder2.ParameterContract{}
getParams := func(parameters map[string]encoder2.ParameterContract) []string {
return []string{"-c:v", "mpeg4"}
}
return encoder2.NewEncoder("mpeg4", parameters, getParams)
}
func NewData() encoder2.EncoderDataContract {
title := "mpeg4"
formats := []string{"avi"}
fileType := encoder2.FileType(encoder2.Video)
return encoder2.NewData(title, formats, fileType, NewEncoder)
}

View File

@ -0,0 +1,19 @@
package msmpeg4
import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder"
func NewEncoder() encoder2.EncoderContract {
parameters := map[string]encoder2.ParameterContract{}
getParams := func(parameters map[string]encoder2.ParameterContract) []string {
return []string{"-c:v", "msmpeg4"}
}
return encoder2.NewEncoder("msmpeg4", parameters, getParams)
}
func NewData() encoder2.EncoderDataContract {
title := "msmpeg4"
formats := []string{"avi"}
fileType := encoder2.FileType(encoder2.Video)
return encoder2.NewData(title, formats, fileType, NewEncoder)
}

View File

@ -0,0 +1,19 @@
package msmpeg4v2
import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder"
func NewEncoder() encoder2.EncoderContract {
parameters := map[string]encoder2.ParameterContract{}
getParams := func(parameters map[string]encoder2.ParameterContract) []string {
return []string{"-c:v", "msmpeg4v2"}
}
return encoder2.NewEncoder("msmpeg4v2", parameters, getParams)
}
func NewData() encoder2.EncoderDataContract {
title := "msmpeg4v2"
formats := []string{"avi"}
fileType := encoder2.FileType(encoder2.Video)
return encoder2.NewData(title, formats, fileType, NewEncoder)
}

View File

@ -0,0 +1,19 @@
package msvideo1
import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder"
func NewEncoder() encoder2.EncoderContract {
parameters := map[string]encoder2.ParameterContract{}
getParams := func(parameters map[string]encoder2.ParameterContract) []string {
return []string{"-c:v", "msvideo1"}
}
return encoder2.NewEncoder("msvideo1", parameters, getParams)
}
func NewData() encoder2.EncoderDataContract {
title := "msvideo1"
formats := []string{"avi"}
fileType := encoder2.FileType(encoder2.Video)
return encoder2.NewData(title, formats, fileType, NewEncoder)
}

19
encoder/png/encoder.go Normal file
View File

@ -0,0 +1,19 @@
package png
import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder"
func NewEncoder() encoder2.EncoderContract {
parameters := map[string]encoder2.ParameterContract{}
getParams := func(parameters map[string]encoder2.ParameterContract) []string {
return []string{"-c:v", "png"}
}
return encoder2.NewEncoder("png", parameters, getParams)
}
func NewData() encoder2.EncoderDataContract {
title := "png"
formats := []string{"png"}
fileType := encoder2.FileType(encoder2.Image)
return encoder2.NewData(title, formats, fileType, NewEncoder)
}

19
encoder/qtrle/encoder.go Normal file
View File

@ -0,0 +1,19 @@
package qtrle
import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder"
func NewEncoder() encoder2.EncoderContract {
parameters := map[string]encoder2.ParameterContract{}
getParams := func(parameters map[string]encoder2.ParameterContract) []string {
return []string{"-c:v", "qtrle"}
}
return encoder2.NewEncoder("qtrle", parameters, getParams)
}
func NewData() encoder2.EncoderDataContract {
title := "qtrle"
formats := []string{"mov"}
fileType := encoder2.FileType(encoder2.Video)
return encoder2.NewData(title, formats, fileType, NewEncoder)
}

19
encoder/sgi/encoder.go Normal file
View File

@ -0,0 +1,19 @@
package sgi
import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder"
func NewEncoder() encoder2.EncoderContract {
parameters := map[string]encoder2.ParameterContract{}
getParams := func(parameters map[string]encoder2.ParameterContract) []string {
return []string{"-c:v", "sgi"}
}
return encoder2.NewEncoder("sgi", parameters, getParams)
}
func NewData() encoder2.EncoderDataContract {
title := "sgi"
formats := []string{"sgi"}
fileType := encoder2.FileType(encoder2.Image)
return encoder2.NewData(title, formats, fileType, NewEncoder)
}

19
encoder/tiff/encoder.go Normal file
View File

@ -0,0 +1,19 @@
package tiff
import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder"
func NewEncoder() encoder2.EncoderContract {
parameters := map[string]encoder2.ParameterContract{}
getParams := func(parameters map[string]encoder2.ParameterContract) []string {
return []string{"-c:v", "tiff"}
}
return encoder2.NewEncoder("tiff", parameters, getParams)
}
func NewData() encoder2.EncoderDataContract {
title := "tiff"
formats := []string{"tiff"}
fileType := encoder2.FileType(encoder2.Image)
return encoder2.NewData(title, formats, fileType, NewEncoder)
}

19
encoder/wmav1/encoder.go Normal file
View File

@ -0,0 +1,19 @@
package wmav1
import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder"
func NewEncoder() encoder2.EncoderContract {
parameters := map[string]encoder2.ParameterContract{}
getParams := func(parameters map[string]encoder2.ParameterContract) []string {
return []string{"-c:a", "wmav1"}
}
return encoder2.NewEncoder("wmav1", parameters, getParams)
}
func NewData() encoder2.EncoderDataContract {
title := "wmav1"
formats := []string{"wma"}
fileType := encoder2.FileType(encoder2.Audio)
return encoder2.NewData(title, formats, fileType, NewEncoder)
}

19
encoder/wmav2/encoder.go Normal file
View File

@ -0,0 +1,19 @@
package wmav2
import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder"
func NewEncoder() encoder2.EncoderContract {
parameters := map[string]encoder2.ParameterContract{}
getParams := func(parameters map[string]encoder2.ParameterContract) []string {
return []string{"-c:a", "wmav2"}
}
return encoder2.NewEncoder("wmav2", parameters, getParams)
}
func NewData() encoder2.EncoderDataContract {
title := "wmav2"
formats := []string{"wma"}
fileType := encoder2.FileType(encoder2.Audio)
return encoder2.NewData(title, formats, fileType, NewEncoder)
}

19
encoder/wmv1/encoder.go Normal file
View File

@ -0,0 +1,19 @@
package wmv1
import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder"
func NewEncoder() encoder2.EncoderContract {
parameters := map[string]encoder2.ParameterContract{}
getParams := func(parameters map[string]encoder2.ParameterContract) []string {
return []string{"-c:v", "wmv1"}
}
return encoder2.NewEncoder("wmv1", parameters, getParams)
}
func NewData() encoder2.EncoderDataContract {
title := "wmv1"
formats := []string{"wmv"}
fileType := encoder2.FileType(encoder2.Video)
return encoder2.NewData(title, formats, fileType, NewEncoder)
}

19
encoder/wmv2/encoder.go Normal file
View File

@ -0,0 +1,19 @@
package wmv2
import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder"
func NewEncoder() encoder2.EncoderContract {
parameters := map[string]encoder2.ParameterContract{}
getParams := func(parameters map[string]encoder2.ParameterContract) []string {
return []string{"-c:v", "wmv2"}
}
return encoder2.NewEncoder("wmv2", parameters, getParams)
}
func NewData() encoder2.EncoderDataContract {
title := "wmv2"
formats := []string{"wmv"}
fileType := encoder2.FileType(encoder2.Video)
return encoder2.NewData(title, formats, fileType, NewEncoder)
}

19
encoder/xbm/encoder.go Normal file
View File

@ -0,0 +1,19 @@
package xbm
import encoder2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder"
func NewEncoder() encoder2.EncoderContract {
parameters := map[string]encoder2.ParameterContract{}
getParams := func(parameters map[string]encoder2.ParameterContract) []string {
return []string{"-c:v", "xbm"}
}
return encoder2.NewEncoder("xbm", parameters, getParams)
}
func NewData() encoder2.EncoderDataContract {
title := "xbm"
formats := []string{"xbm"}
fileType := encoder2.FileType(encoder2.Image)
return encoder2.NewData(title, formats, fileType, NewEncoder)
}

64
error/view.go Normal file
View File

@ -0,0 +1,64 @@
package error
import (
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/localizer"
"github.com/nicksnyder/go-i18n/v2/i18n"
)
type ViewContract interface {
PanicError(err error)
}
type View struct {
app kernel.AppContract
}
func NewView(app kernel.AppContract) *View {
return &View{
app: app,
}
}
func (v View) PanicError(err error) {
messageHead := v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "error",
})
v.app.GetWindow().SetContent(container.NewBorder(
container.NewVBox(
widget.NewLabel(messageHead),
widget.NewLabel(err.Error()),
),
nil,
nil,
nil,
localizer.LanguageSelectionForm(v.app.GetLocalizerService(), func(lang kernel.Lang) {
v.PanicError(err)
}),
))
}
func (v View) PanicErrorWriteDirectoryData() {
message := v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "errorDatabase",
})
messageHead := v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "error",
})
v.app.GetWindow().SetContent(container.NewBorder(
container.NewVBox(
widget.NewLabel(messageHead),
widget.NewLabel(message),
),
nil,
nil,
nil,
localizer.LanguageSelectionForm(v.app.GetLocalizerService(), func(lang kernel.Lang) {
v.PanicErrorWriteDirectoryData()
}),
))
}

View File

@ -1,18 +1,27 @@
module ffmpegGui
module git.kor-elf.net/kor-elf/gui-for-ffmpeg
go 1.21
require (
fyne.io/fyne/v2 v2.4.3 // indirect
fyne.io/fyne/v2 v2.4.3
github.com/BurntSushi/toml v1.3.2
github.com/mattn/go-sqlite3 v1.14.21
github.com/nicksnyder/go-i18n/v2 v2.4.0
golang.org/x/text v0.14.0
gorm.io/driver/sqlite v1.5.4
gorm.io/gorm v1.25.6
)
require (
fyne.io/systray v1.10.1-0.20231115130155-104f5ef7839e // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fredbi/uri v1.0.0 // indirect
github.com/fredbi/uri v1.1.0 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/fyne-io/gl-js v0.0.0-20220119005834-d2da28d9ccfe // indirect
github.com/fyne-io/glfw-js v0.0.0-20220120001248-ee7290d23504 // indirect
github.com/fyne-io/image v0.0.0-20220602074514-4956b0afb3d2 // indirect
github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 // indirect
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20221017161538-93cebf72946b // indirect
github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71 // indirect
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240118000515-a250818d05e3 // indirect
github.com/go-text/render v0.0.0-20230619120952-35bccb6164b8 // indirect
github.com/go-text/typesetting v0.0.0-20230616162802-9c17dd34aa4a // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
@ -20,7 +29,6 @@ require (
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e // indirect
github.com/mattn/go-sqlite3 v1.14.19 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c // indirect
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef // indirect
@ -29,11 +37,8 @@ require (
github.com/yuin/goldmark v1.5.5 // indirect
golang.org/x/image v0.11.0 // indirect
golang.org/x/mobile v0.0.0-20230531173138-3c911d8e3eda // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/sys v0.13.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/net v0.20.0 // indirect
golang.org/x/sys v0.16.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gorm.io/driver/sqlite v1.5.4 // indirect
gorm.io/gorm v1.25.5 // indirect
honnef.co/go/js/dom v0.0.0-20210725211120-f030747120f2 // indirect
)

View File

@ -42,6 +42,8 @@ fyne.io/fyne/v2 v2.4.3/go.mod h1:1h3BKxmQYRJlr2g+RGVxedzr6vLVQ/AJmFWcF9CJnoQ=
fyne.io/systray v1.10.1-0.20231115130155-104f5ef7839e h1:Hvs+kW2VwCzNToF3FmnIAzmivNgrclwPgoUdVSrjkP8=
fyne.io/systray v1.10.1-0.20231115130155-104f5ef7839e/go.mod h1:oM2AQqGJ1AMo4nNqZFYU8xYygSBZkW2hmdJ7n4yjedE=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
@ -73,6 +75,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fredbi/uri v1.0.0 h1:s4QwUAZ8fz+mbTsukND+4V5f+mJ/wjaTokwstGUAemg=
github.com/fredbi/uri v1.0.0/go.mod h1:1xC40RnIOGCaQzswaOvrzvG/3M3F0hyDVb3aO/1iGy0=
github.com/fredbi/uri v1.1.0 h1:OqLpTXtyRg9ABReqvDGdJPqZUxs8cyBDOMXBbskCaB8=
github.com/fredbi/uri v1.1.0/go.mod h1:aYTUoAXBOq7BLfVJ8GnKmfcuURosB1xyHDIfWeC/iW4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
@ -85,16 +89,22 @@ github.com/fyne-io/image v0.0.0-20220602074514-4956b0afb3d2/go.mod h1:eO7W361vml
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 h1:zDw5v7qm4yH7N8C8uWd+8Ii9rROdgWxQuGoJ9WDXxfk=
github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw=
github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71 h1:5BVwOaUSBTlVZowGO6VZGw2H/zl9nrd3eCZfYV+NfQA=
github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20211213063430-748e38ca8aec/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20221017161538-93cebf72946b h1:GgabKamyOYguHqHjSkDACcgoPIz3w0Dis/zJ1wyHHHU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20221017161538-93cebf72946b/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240118000515-a250818d05e3 h1:nanQfMsOs3gnuKRm0E5jXWomedE/9YIFXdmHJNZYeqc=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240118000515-a250818d05e3/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-text/render v0.0.0-20230619120952-35bccb6164b8 h1:VkKnvzbvHqgEfm351rfr8Uclu5fnwq8HP2ximUzJsBM=
github.com/go-text/render v0.0.0-20230619120952-35bccb6164b8/go.mod h1:h29xCucjNsDcYb7+0rJokxVwYAq+9kQ19WiFuBKkYtc=
github.com/go-text/typesetting v0.0.0-20230616162802-9c17dd34aa4a h1:VjN8ttdfklC0dnAdKbZqGNESdERUxtE3l8a/4Grgarc=
github.com/go-text/typesetting v0.0.0-20230616162802-9c17dd34aa4a/go.mod h1:evDBbvNR/KaVFZ2ZlDSOWWXIUKq0wCOEtzLxRM8SG3k=
github.com/go-text/typesetting-utils v0.0.0-20230616150549-2a7df14b6a22 h1:LBQTFxP2MfsyEDqSKmUBZaDuDHN1vpqDyOZjcqS7MYI=
github.com/go-text/typesetting-utils v0.0.0-20230616150549-2a7df14b6a22/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
@ -204,14 +214,18 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-sqlite3 v1.14.19 h1:fhGleo2h1p8tVChob4I9HpmVFIAkKGpiukdrgQbWfGI=
github.com/mattn/go-sqlite3 v1.14.19/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/mattn/go-sqlite3 v1.14.21 h1:IXocQLOykluc3xPE0Lvy8FtggMz1G+U3mEjg+0zGizc=
github.com/mattn/go-sqlite3 v1.14.21/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
@ -226,6 +240,10 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
github.com/nicksnyder/go-i18n/v2 v2.3.0 h1:2NPsCsNFCVd7i+Su0xYsBrIhS3bE2XMv5gNTft2O+PQ=
github.com/nicksnyder/go-i18n/v2 v2.3.0/go.mod h1:nxYSZE9M0bf3Y70gPQjN9ha7XNHX7gMc814+6wVyEI4=
github.com/nicksnyder/go-i18n/v2 v2.4.0 h1:3IcvPOAvnCKwNm0TB0dLDTuawWEj+ax/RERNC+diLMM=
github.com/nicksnyder/go-i18n/v2 v2.4.0/go.mod h1:nxYSZE9M0bf3Y70gPQjN9ha7XNHX7gMc814+6wVyEI4=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@ -385,6 +403,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -461,6 +481,8 @@ golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
@ -475,8 +497,8 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@ -642,12 +664,14 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
@ -655,10 +679,10 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/sqlite v1.5.4 h1:IqXwXi8M/ZlPzH/947tn5uik3aYQslP9BVveoax0nV0=
gorm.io/driver/sqlite v1.5.4/go.mod h1:qxAuCol+2r6PannQDpOP1FP6ag3mKi4esLnB/jHed+4=
gorm.io/gorm v1.25.2-0.20230530020048-26663ab9bf55 h1:sC1Xj4TYrLqg1n3AN10w871An7wJM0gzgcm8jkIkECQ=
gorm.io/gorm v1.25.2-0.20230530020048-26663ab9bf55/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls=
gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
gorm.io/gorm v1.25.6 h1:V92+vVda1wEISSOMtodHVRcUIOPYa2tgQtyF+DfFx+A=
gorm.io/gorm v1.25.6/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
honnef.co/go/js/dom v0.0.0-20210725211120-f030747120f2 h1:oomkgU6VaQDsV6qZby2uz1Lap0eXmku8+2em3A/l700=
honnef.co/go/js/dom v0.0.0-20210725211120-f030747120f2/go.mod h1:sUMDUKNB2ZcVjt92UnLy3cdGs+wDAcrPdV3JP6sVgA4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

142
handler/convertor.go Normal file
View File

@ -0,0 +1,142 @@
package handler
import (
"errors"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/convertor"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/convertor/view"
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"
)
type ConvertorHandlerContract interface {
MainConvertor()
FfPathSelection()
GetFfmpegVersion() (string, error)
GetFfprobeVersion() (string, error)
}
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 {
formats, err := h.app.GetConvertorService().GetSupportFormats()
if err != nil {
h.errorView.PanicError(err)
return
}
conversion := view.NewConversion(h.app, formats, h.runConvert)
h.convertorView.Main(conversion)
return
}
h.convertorView.SelectFFPath("", "", h.saveSettingFFPath, nil, h.downloadFFmpeg)
}
func (h ConvertorHandler) FfPathSelection() {
ffmpeg, _ := h.convertorRepository.GetPathFfmpeg()
ffprobe, _ := h.convertorRepository.GetPathFfprobe()
h.convertorView.SelectFFPath(ffmpeg, ffprobe, h.saveSettingFFPath, h.MainConvertor, h.downloadFFmpeg)
}
func (h ConvertorHandler) GetFfmpegVersion() (string, error) {
return h.app.GetConvertorService().GetFFmpegVesrion()
}
func (h ConvertorHandler) GetFfprobeVersion() (string, error) {
return h.app.GetConvertorService().GetFFprobeVersion()
}
func (h ConvertorHandler) runConvert(setting view.HandleConvertSetting) {
h.app.GetQueue().Add(&kernel.ConvertSetting{
VideoFileInput: setting.FileInput,
VideoFileOut: kernel.File{
Path: setting.DirectoryForSave + helper.PathSeparator() + setting.FileInput.Name + "." + setting.Format,
Name: setting.FileInput.Name,
Ext: "." + setting.Format,
},
OverwriteOutputFiles: setting.OverwriteOutputFiles,
Encoder: setting.Encoder,
})
}
func (h ConvertorHandler) checkingFFPathUtilities() bool {
if h.checkingFFPath() == true {
return true
}
pathsToFF := getPathsToFF()
for _, item := range pathsToFF {
ffmpegChecking, _ := h.app.GetConvertorService().ChangeFFmpegPath(item.FFmpeg)
if ffmpegChecking == false {
continue
}
ffprobeChecking, _ := h.app.GetConvertorService().ChangeFFprobePath(item.FFprobe)
if ffprobeChecking == false {
continue
}
_, _ = h.convertorRepository.SavePathFfmpeg(item.FFmpeg)
_, _ = h.convertorRepository.SavePathFfprobe(item.FFprobe)
return true
}
return false
}
func (h ConvertorHandler) saveSettingFFPath(ffmpegPath string, ffprobePath string) error {
ffmpegChecking, _ := h.app.GetConvertorService().ChangeFFmpegPath(ffmpegPath)
if ffmpegChecking == false {
errorText := h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "errorFFmpeg",
})
return errors.New(errorText)
}
ffprobeChecking, _ := h.app.GetConvertorService().ChangeFFprobePath(ffprobePath)
if ffprobeChecking == false {
errorText := h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "errorFFprobe",
})
return errors.New(errorText)
}
_, _ = h.convertorRepository.SavePathFfmpeg(ffmpegPath)
_, _ = h.convertorRepository.SavePathFfprobe(ffprobePath)
h.MainConvertor()
return nil
}
func (h ConvertorHandler) checkingFFPath() bool {
_, err := h.app.GetConvertorService().GetFFmpegVesrion()
if err != nil {
return false
}
_, err = h.app.GetConvertorService().GetFFprobeVersion()
if err != nil {
return false
}
return true
}

View File

@ -0,0 +1,18 @@
//go:build !windows
// +build !windows
package handler
import (
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/widget"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel"
)
func getPathsToFF() []kernel.FFPathUtilities {
return []kernel.FFPathUtilities{{"ffmpeg/bin/ffmpeg", "ffmpeg/bin/ffprobe"}, {"ffmpeg", "ffprobe"}}
}
func (h ConvertorHandler) downloadFFmpeg(progressBar *widget.ProgressBar, progressMessage *canvas.Text) (err error) {
return nil
}

View File

@ -0,0 +1,153 @@
//go:build windows
// +build windows
package handler
import (
"archive/zip"
"errors"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/widget"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel"
"github.com/nicksnyder/go-i18n/v2/i18n"
"io"
"net/http"
"os"
"path/filepath"
"strings"
)
func getPathsToFF() []kernel.FFPathUtilities {
return []kernel.FFPathUtilities{{"ffmpeg\\bin\\ffmpeg.exe", "ffmpeg\\bin\\ffprobe.exe"}}
}
func (h ConvertorHandler) downloadFFmpeg(progressBar *widget.ProgressBar, progressMessage *canvas.Text) (err error) {
isDirectoryFFmpeg := isDirectory("ffmpeg")
if isDirectoryFFmpeg == false {
err = os.Mkdir("ffmpeg", 0777)
if err != nil {
return err
}
}
progressMessage.Text = h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "downloadRun",
})
progressMessage.Refresh()
err = downloadFile("ffmpeg/ffmpeg.zip", "https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-master-latest-win64-gpl.zip", progressBar)
if err != nil {
return err
}
progressMessage.Text = h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "unzipRun",
})
progressMessage.Refresh()
err = unZip("ffmpeg/ffmpeg.zip", "ffmpeg")
if err != nil {
return err
}
_ = os.Remove("ffmpeg/ffmpeg.zip")
progressMessage.Text = h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "testFF",
})
progressMessage.Refresh()
err = h.saveSettingFFPath("ffmpeg/ffmpeg-master-latest-win64-gpl/bin/ffmpeg.exe", "ffmpeg/ffmpeg-master-latest-win64-gpl/bin/ffprobe.exe")
if err != nil {
return err
}
return nil
}
func downloadFile(filepath string, url string, progressBar *widget.ProgressBar) (err error) {
progressBar.Value = 0
progressBar.Max = 100
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return err
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
f, err := os.OpenFile(filepath, os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return err
}
defer f.Close()
buf := make([]byte, 32*1024)
var downloaded int64
for {
n, err := resp.Body.Read(buf)
if err != nil {
if err == io.EOF {
break
}
return err
}
if n > 0 {
f.Write(buf[:n])
downloaded += int64(n)
progressBar.Value = float64(downloaded) / float64(resp.ContentLength) * 100
progressBar.Refresh()
}
}
return nil
}
func unZip(fileZip string, directory string) error {
archive, err := zip.OpenReader(fileZip)
if err != nil {
return err
}
defer archive.Close()
for _, f := range archive.File {
filePath := filepath.Join(directory, f.Name)
if !strings.HasPrefix(filePath, filepath.Clean(directory)+string(os.PathSeparator)) {
return errors.New("invalid file path")
}
if f.FileInfo().IsDir() {
os.MkdirAll(filePath, os.ModePerm)
continue
}
if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil {
return err
}
dstFile, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
if err != nil {
return err
}
fileInArchive, err := f.Open()
if err != nil {
return err
}
if _, err := io.Copy(dstFile, fileInArchive); err != nil {
return err
}
dstFile.Close()
fileInArchive.Close()
}
return nil
}
func isDirectory(path string) bool {
fileInfo, err := os.Stat(path)
if err != nil {
return false
}
return fileInfo.IsDir()
}

38
handler/main.go Normal file
View File

@ -0,0 +1,38 @@
package handler
import (
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/localizer"
)
type MainHandler struct {
app kernel.AppContract
convertorHandler ConvertorHandlerContract
menuHandler MenuHandlerContract
localizerRepository localizer.RepositoryContract
}
func NewMainHandler(
app kernel.AppContract,
convertorHandler ConvertorHandlerContract,
menuHandler MenuHandlerContract,
localizerRepository localizer.RepositoryContract,
) *MainHandler {
return &MainHandler{
app: app,
convertorHandler: convertorHandler,
menuHandler: menuHandler,
localizerRepository: localizerRepository,
}
}
func (h MainHandler) Start() {
language, err := h.localizerRepository.GetCode()
if err != nil {
h.menuHandler.LanguageSelection()
return
}
_ = h.app.GetLocalizerService().SetCurrentLanguageByCode(language)
h.convertorHandler.MainConvertor()
}

122
handler/menu.go Normal file
View File

@ -0,0 +1,122 @@
package handler
import (
"fyne.io/fyne/v2"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/localizer"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/menu"
"github.com/nicksnyder/go-i18n/v2/i18n"
)
type MenuHandlerContract interface {
GetMainMenu() *fyne.MainMenu
LanguageSelection()
}
type MenuHandler struct {
app kernel.AppContract
convertorHandler ConvertorHandlerContract
menuView menu.ViewContract
localizerView localizer.ViewContract
localizerRepository localizer.RepositoryContract
}
func NewMenuHandler(
app kernel.AppContract,
convertorHandler ConvertorHandlerContract,
menuView menu.ViewContract,
localizerView localizer.ViewContract,
localizerRepository localizer.RepositoryContract,
) *MenuHandler {
return &MenuHandler{
app: app,
convertorHandler: convertorHandler,
menuView: menuView,
localizerView: localizerView,
localizerRepository: localizerRepository,
}
}
func (h MenuHandler) GetMainMenu() *fyne.MainMenu {
settings := h.getMenuSettings()
help := h.getMenuHelp()
return fyne.NewMainMenu(settings, help)
}
func (h MenuHandler) getMenuSettings() *fyne.Menu {
quit := fyne.NewMenuItem(h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "exit",
}), nil)
quit.IsQuit = true
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.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.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.app.GetLocalizerService().AddChangeCallback("settings", func(text string) {
settings.Label = text
settings.Refresh()
})
return settings
}
func (h MenuHandler) getMenuHelp() *fyne.Menu {
about := fyne.NewMenuItem(h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "about",
}), h.openAbout)
h.app.GetLocalizerService().AddChangeCallback("about", func(text string) {
about.Label = text
})
help := fyne.NewMenu(h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "help",
}), about)
h.app.GetLocalizerService().AddChangeCallback("help", func(text string) {
help.Label = text
help.Refresh()
})
return help
}
func (h MenuHandler) openAbout() {
ffmpeg, err := h.convertorHandler.GetFfmpegVersion()
if err != nil {
ffmpeg = h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "errorFFmpegVersion",
})
}
ffprobe, err := h.convertorHandler.GetFfprobeVersion()
if err != nil {
ffprobe = h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "errorFFprobeVersion",
})
}
h.menuView.About(ffmpeg, ffprobe)
}
func (h MenuHandler) LanguageSelection() {
h.localizerView.LanguageSelection(func(lang kernel.Lang) {
_, _ = h.localizerRepository.Save(lang.Code)
h.convertorHandler.MainConvertor()
})
}

11
helper/helper.go Normal file
View File

@ -0,0 +1,11 @@
package helper
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/dialog"
)
func FileDialogResize(fileDialog *dialog.FileDialog, w fyne.Window) {
contentSize := w.Content().Size()
fileDialog.Resize(fyne.Size{Width: contentSize.Width - 50, Height: contentSize.Height - 50})
}

8
helper/path_separator.go Normal file
View File

@ -0,0 +1,8 @@
//go:build !windows
// +build !windows
package helper
func PathSeparator() string {
return "/"
}

View File

@ -0,0 +1,8 @@
//go:build windows
// +build windows
package helper
func PathSeparator() string {
return "\\"
}

BIN
icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

105
kernel/app.go Normal file
View File

@ -0,0 +1,105 @@
package kernel
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
"time"
)
type AppContract interface {
GetAppFyne() fyne.App
GetWindow() WindowContract
GetQueue() QueueListContract
GetLocalizerService() LocalizerContract
GetConvertorService() ConvertorContract
AfterClosing()
RunConvertor()
}
type App struct {
AppFyne fyne.App
Window WindowContract
Queue QueueListContract
localizerService LocalizerContract
convertorService ConvertorContract
}
func NewApp(
metadata *fyne.AppMetadata,
localizerService LocalizerContract,
queue QueueListContract,
queueLayoutObject QueueLayoutObjectContract,
convertorService ConvertorContract,
) *App {
app.SetMetadata(*metadata)
a := app.New()
return &App{
AppFyne: a,
Window: newWindow(a.NewWindow("GUI for FFmpeg"), NewLayout(queueLayoutObject, localizerService)),
Queue: queue,
localizerService: localizerService,
convertorService: convertorService,
}
}
func (a App) GetAppFyne() fyne.App {
return a.AppFyne
}
func (a App) GetQueue() QueueListContract {
return a.Queue
}
func (a App) GetWindow() WindowContract {
return a.Window
}
func (a App) GetLocalizerService() LocalizerContract {
return a.localizerService
}
func (a App) GetConvertorService() ConvertorContract {
return a.convertorService
}
func (a App) AfterClosing() {
for _, cmd := range a.convertorService.GetRunningProcesses() {
_ = cmd.Process.Kill()
}
}
func (a App) RunConvertor() {
go func() {
for {
time.Sleep(time.Millisecond * 3000)
queueId, queue := a.Queue.Next()
if queue == nil {
continue
}
queue.Status = StatusType(InProgress)
a.Window.GetLayout().ChangeQueueStatus(queueId, queue)
totalDuration, err := a.convertorService.GetTotalDuration(&queue.Setting.VideoFileInput)
if err != nil {
queue.Status = StatusType(Error)
queue.Error = err
a.Window.GetLayout().ChangeQueueStatus(queueId, queue)
continue
}
progress := a.Window.GetLayout().NewProgressbar(queueId, totalDuration)
err = a.convertorService.RunConvert(*queue.Setting, progress)
if err != nil {
queue.Status = StatusType(Error)
queue.Error = err
a.Window.GetLayout().ChangeQueueStatus(queueId, queue)
continue
}
queue.Status = StatusType(Completed)
a.Window.GetLayout().ChangeQueueStatus(queueId, queue)
}
}()
}

View File

@ -1,8 +1,11 @@
package convertor
package kernel
import (
"bufio"
"errors"
"ffmpegGui/helper"
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"
@ -10,7 +13,20 @@ import (
"strings"
)
type ServiceContract interface {
type File struct {
Path string
Name string
Ext string
}
type ConvertSetting struct {
VideoFileInput File
VideoFileOut File
OverwriteOutputFiles bool
Encoder encoder2.EncoderContract
}
type ConvertorContract interface {
RunConvert(setting ConvertSetting, progress ProgressContract) error
GetTotalDuration(file *File) (float64, error)
GetFFmpegVesrion() (string, error)
@ -18,6 +34,7 @@ type ServiceContract interface {
ChangeFFmpegPath(path string) (bool, error)
ChangeFFprobePath(path string) (bool, error)
GetRunningProcesses() map[int]*exec.Cmd
GetSupportFormats() (encoder.ConvertorFormatsContract, error)
}
type ProgressContract interface {
@ -35,40 +52,30 @@ type runningProcesses struct {
numberOfStarts int
}
type Service struct {
type Convertor struct {
ffPathUtilities *FFPathUtilities
runningProcesses runningProcesses
}
type File struct {
Path string
Name string
Ext string
}
type ConvertSetting struct {
VideoFileInput *File
VideoFileOut *File
OverwriteOutputFiles bool
}
type ConvertData struct {
totalDuration float64
}
func NewService(ffPathUtilities FFPathUtilities) *Service {
return &Service{
ffPathUtilities: &ffPathUtilities,
func NewService(ffPathUtilities *FFPathUtilities) *Convertor {
return &Convertor{
ffPathUtilities: ffPathUtilities,
runningProcesses: runningProcesses{items: map[int]*exec.Cmd{}, numberOfStarts: 0},
}
}
func (s Service) RunConvert(setting ConvertSetting, progress ProgressContract) error {
func (s Convertor) RunConvert(setting ConvertSetting, progress ProgressContract) error {
overwriteOutputFiles := "-n"
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)
@ -103,7 +110,7 @@ func (s Service) RunConvert(setting ConvertSetting, progress ProgressContract) e
return nil
}
func (s Service) GetTotalDuration(file *File) (duration float64, err error) {
func (s Convertor) GetTotalDuration(file *File) (duration float64, err error) {
args := []string{"-v", "error", "-select_streams", "v: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)
@ -115,10 +122,33 @@ func (s Service) 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 Service) GetFFmpegVesrion() (string, error) {
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) {
cmd := exec.Command(s.ffPathUtilities.FFmpeg, "-version")
helper.PrepareBackgroundCommand(cmd)
out, err := cmd.CombinedOutput()
@ -129,7 +159,7 @@ func (s Service) GetFFmpegVesrion() (string, error) {
return text[0], nil
}
func (s Service) GetFFprobeVersion() (string, error) {
func (s Convertor) GetFFprobeVersion() (string, error) {
cmd := exec.Command(s.ffPathUtilities.FFprobe, "-version")
helper.PrepareBackgroundCommand(cmd)
out, err := cmd.CombinedOutput()
@ -140,7 +170,7 @@ func (s Service) GetFFprobeVersion() (string, error) {
return text[0], nil
}
func (s Service) ChangeFFmpegPath(path string) (bool, error) {
func (s Convertor) ChangeFFmpegPath(path string) (bool, error) {
cmd := exec.Command(path, "-version")
helper.PrepareBackgroundCommand(cmd)
out, err := cmd.CombinedOutput()
@ -154,7 +184,7 @@ func (s Service) ChangeFFmpegPath(path string) (bool, error) {
return true, nil
}
func (s Service) ChangeFFprobePath(path string) (bool, error) {
func (s Convertor) ChangeFFprobePath(path string) (bool, error) {
cmd := exec.Command(path, "-version")
helper.PrepareBackgroundCommand(cmd)
out, err := cmd.CombinedOutput()
@ -168,6 +198,46 @@ func (s Service) ChangeFFprobePath(path string) (bool, error) {
return true, nil
}
func (s Service) GetRunningProcesses() map[int]*exec.Cmd {
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 {
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
}

84
kernel/encoder/encoder.go Normal file
View File

@ -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
}

View File

@ -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,
}

20
kernel/error.go Normal file
View File

@ -0,0 +1,20 @@
package kernel
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget"
)
func PanicErrorLang(err error, metadata *fyne.AppMetadata) {
app.SetMetadata(*metadata)
a := app.New()
window := a.NewWindow("GUI for FFmpeg")
window.SetContent(container.NewVBox(
widget.NewLabel("Произошла ошибка!"),
widget.NewLabel("произошла ошибка при получении языковых переводах. \n\r"+err.Error()),
))
window.ShowAndRun()
panic(err.Error())
}

534
kernel/layout.go Normal file
View File

@ -0,0 +1,534 @@
package kernel
import (
"bufio"
"errors"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
"github.com/nicksnyder/go-i18n/v2/i18n"
"image/color"
"io"
"regexp"
"strconv"
"strings"
)
type LayoutContract interface {
SetContent(content fyne.CanvasObject) *fyne.Container
NewProgressbar(queueId int, totalDuration float64) ProgressContract
ChangeQueueStatus(queueId int, queue *Queue)
}
type Layout struct {
layout *fyne.Container
queueLayoutObject QueueLayoutObjectContract
localizerService LocalizerContract
}
func NewLayout(queueLayoutObject QueueLayoutObjectContract, localizerService LocalizerContract) *Layout {
layout := container.NewAdaptiveGrid(2, widget.NewLabel(""), container.NewVScroll(queueLayoutObject.GetCanvasObject()))
return &Layout{
layout: layout,
queueLayoutObject: queueLayoutObject,
localizerService: localizerService,
}
}
func (l Layout) SetContent(content fyne.CanvasObject) *fyne.Container {
l.layout.Objects[0] = content
return l.layout
}
func (l Layout) NewProgressbar(queueId int, totalDuration float64) ProgressContract {
progressbar := l.queueLayoutObject.GetProgressbar(queueId)
return NewProgress(totalDuration, progressbar, l.localizerService)
}
func (l Layout) ChangeQueueStatus(queueId int, queue *Queue) {
l.queueLayoutObject.ChangeQueueStatus(queueId, queue)
}
type QueueLayoutObjectContract interface {
GetCanvasObject() fyne.CanvasObject
GetProgressbar(queueId int) *widget.ProgressBar
ChangeQueueStatus(queueId int, queue *Queue)
}
type QueueLayoutObject struct {
QueueListContract QueueListContract
queue QueueListContract
container *fyne.Container
items map[int]QueueLayoutItem
localizerService LocalizerContract
queueStatisticsFormat *queueStatisticsFormat
}
type QueueLayoutItem struct {
CanvasObject fyne.CanvasObject
ProgressBar *widget.ProgressBar
StatusMessage *canvas.Text
MessageError *canvas.Text
status *StatusContract
}
func NewQueueLayoutObject(queue QueueListContract, localizerService LocalizerContract) *QueueLayoutObject {
title := widget.NewLabel(localizerService.GetMessage(&i18n.LocalizeConfig{MessageID: "queue"}))
title.TextStyle.Bold = true
localizerService.AddChangeCallback("queue", func(text string) {
title.Text = text
title.Refresh()
})
items := map[int]QueueLayoutItem{}
queueStatisticsFormat := newQueueStatisticsFormat(localizerService, &items)
queueLayoutObject := &QueueLayoutObject{
queue: queue,
container: container.NewVBox(
container.NewHBox(title, queueStatisticsFormat.completed.widget, queueStatisticsFormat.error.widget),
container.NewHBox(queueStatisticsFormat.inProgress.widget, queueStatisticsFormat.waiting.widget, queueStatisticsFormat.total.widget),
),
items: items,
localizerService: localizerService,
queueStatisticsFormat: queueStatisticsFormat,
}
queue.AddListener(queueLayoutObject)
return queueLayoutObject
}
func (o QueueLayoutObject) GetCanvasObject() fyne.CanvasObject {
return o.container
}
func (o QueueLayoutObject) GetProgressbar(queueId int) *widget.ProgressBar {
if item, ok := o.items[queueId]; ok {
return item.ProgressBar
}
return widget.NewProgressBar()
}
func (o QueueLayoutObject) Add(id int, queue *Queue) {
progressBar := widget.NewProgressBar()
statusMessage := canvas.NewText(o.getStatusTitle(queue.Status), theme.PrimaryColor())
messageError := canvas.NewText("", theme.ErrorColor())
content := container.NewVBox(
container.NewHScroll(widget.NewLabel(queue.Setting.VideoFileInput.Name)),
progressBar,
container.NewHScroll(statusMessage),
container.NewHScroll(messageError),
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)
}
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{}
}
}
func (o QueueLayoutObject) ChangeQueueStatus(queueId int, queue *Queue) {
if item, ok := o.items[queueId]; ok {
statusColor := o.getStatusColor(queue.Status)
item.StatusMessage.Text = o.getStatusTitle(queue.Status)
item.StatusMessage.Color = statusColor
item.StatusMessage.Refresh()
if queue.Error != nil {
item.MessageError.Text = queue.Error.Error()
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)
}
}
func (o QueueLayoutObject) getStatusColor(status StatusContract) color.Color {
if status == StatusType(Error) {
return theme.ErrorColor()
}
if status == StatusType(Completed) {
return color.RGBA{R: 49, G: 127, B: 114, A: 255}
}
return theme.PrimaryColor()
}
func (o QueueLayoutObject) getStatusTitle(status StatusContract) string {
return o.localizerService.GetMessage(&i18n.LocalizeConfig{MessageID: status.Name() + "Queue"})
}
type Progress struct {
totalDuration float64
progressbar *widget.ProgressBar
protocol string
localizerService LocalizerContract
}
func NewProgress(totalDuration float64, progressbar *widget.ProgressBar, localizerService LocalizerContract) Progress {
return Progress{
totalDuration: totalDuration,
progressbar: progressbar,
protocol: "pipe:",
localizerService: localizerService,
}
}
func (p Progress) GetProtocole() string {
return p.protocol
}
func (p Progress) Run(stdOut io.ReadCloser, stdErr io.ReadCloser) error {
isProcessCompleted := false
var errorText string
p.progressbar.Value = 0
p.progressbar.Max = p.totalDuration
p.progressbar.Refresh()
progress := 0.0
go func() {
scannerErr := bufio.NewReader(stdErr)
for {
line, _, err := scannerErr.ReadLine()
if err != nil {
if err == io.EOF {
break
}
continue
}
data := strings.TrimSpace(string(line))
errorText = data
}
}()
scannerOut := bufio.NewReader(stdOut)
for {
line, _, err := scannerOut.ReadLine()
if err != nil {
if err == io.EOF {
break
}
continue
}
data := strings.TrimSpace(string(line))
if strings.Contains(data, "progress=end") {
p.progressbar.Value = p.totalDuration
p.progressbar.Refresh()
isProcessCompleted = true
break
}
re := regexp.MustCompile(`frame=(\d+)`)
a := re.FindAllStringSubmatch(data, -1)
if len(a) > 0 && len(a[len(a)-1]) > 0 {
c, err := strconv.Atoi(a[len(a)-1][len(a[len(a)-1])-1])
if err != nil {
continue
}
progress = float64(c)
}
if p.progressbar.Value != progress {
p.progressbar.Value = progress
p.progressbar.Refresh()
}
}
if isProcessCompleted == false {
if len(errorText) == 0 {
errorText = p.localizerService.GetMessage(&i18n.LocalizeConfig{
MessageID: "errorConverter",
})
}
return errors.New(errorText)
}
return nil
}
type queueStatistics struct {
widget *widget.Check
title string
count *int64
}
type queueStatisticsFormat struct {
waiting *queueStatistics
inProgress *queueStatistics
completed *queueStatistics
error *queueStatistics
total *queueStatistics
}
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,
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) *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)
localizerService.AddChangeCallback(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()
}
}

158
kernel/localizer.go Normal file
View File

@ -0,0 +1,158 @@
package kernel
import (
"github.com/BurntSushi/toml"
"github.com/nicksnyder/go-i18n/v2/i18n"
"golang.org/x/text/cases"
"golang.org/x/text/language"
"golang.org/x/text/language/display"
"path/filepath"
"sort"
)
type LocalizerContract interface {
GetLanguages() []Lang
GetMessage(localizeConfig *i18n.LocalizeConfig) string
SetCurrentLanguage(lang Lang) error
SetCurrentLanguageByCode(code string) error
GetCurrentLanguage() *CurrentLanguage
AddChangeCallback(messageID string, callback func(text string))
}
type Lang struct {
Code string
Title string
}
type CurrentLanguage struct {
Lang Lang
localizer *i18n.Localizer
localizerDefault *i18n.Localizer
}
type changeCallback struct {
messageID string
callback func(text string)
}
type Localizer struct {
bundle *i18n.Bundle
languages []Lang
currentLanguage *CurrentLanguage
changeCallbacks map[int]*changeCallback
}
func NewLocalizer(directory string, languageDefault language.Tag) (*Localizer, error) {
bundle := i18n.NewBundle(languageDefault)
bundle.RegisterUnmarshalFunc("toml", toml.Unmarshal)
languages, err := initLanguages(directory, bundle)
if err != nil {
return nil, err
}
localizerDefault := i18n.NewLocalizer(bundle, languageDefault.String())
return &Localizer{
bundle: bundle,
languages: languages,
currentLanguage: &CurrentLanguage{
Lang: Lang{
Code: languageDefault.String(),
Title: cases.Title(languageDefault).String(display.Self.Name(languageDefault)),
},
localizer: localizerDefault,
localizerDefault: localizerDefault,
},
changeCallbacks: map[int]*changeCallback{},
}, nil
}
func initLanguages(directory string, bundle *i18n.Bundle) ([]Lang, error) {
var languages []Lang
files, err := filepath.Glob(directory + "/active.*.toml")
if err != nil {
return nil, err
}
for _, file := range files {
lang, err := bundle.LoadMessageFile(file)
if err != nil {
return nil, err
}
title := cases.Title(lang.Tag).String(display.Self.Name(lang.Tag))
languages = append(languages, Lang{Code: lang.Tag.String(), Title: title})
}
sort.Sort(languagesSort(languages))
return languages, nil
}
func (l Localizer) GetLanguages() []Lang {
return l.languages
}
func (l Localizer) GetMessage(localizeConfig *i18n.LocalizeConfig) string {
message, err := l.GetCurrentLanguage().localizer.Localize(localizeConfig)
if err != nil {
message, err = l.GetCurrentLanguage().localizerDefault.Localize(localizeConfig)
if err != nil {
return err.Error()
}
}
return message
}
func (l Localizer) SetCurrentLanguage(lang Lang) error {
l.currentLanguage.Lang = lang
l.currentLanguage.localizer = i18n.NewLocalizer(l.bundle, lang.Code)
l.eventSetCurrentLanguage()
return nil
}
func (l Localizer) SetCurrentLanguageByCode(code string) error {
lang, err := language.Parse(code)
if err != nil {
return err
}
title := cases.Title(lang).String(display.Self.Name(lang))
return l.SetCurrentLanguage(Lang{Code: lang.String(), Title: title})
}
func (l Localizer) GetCurrentLanguage() *CurrentLanguage {
return l.currentLanguage
}
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 _, changeCallback := range l.changeCallbacks {
text := l.GetMessage(&i18n.LocalizeConfig{MessageID: changeCallback.messageID})
changeCallback.callback(text)
}
}
type languagesSort []Lang
func (l languagesSort) Len() int { return len(l) }
func (l languagesSort) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
func (l languagesSort) Less(i, j int) bool {
return languagePriority(l[i]) < languagePriority(l[j])
}
func languagePriority(l Lang) int {
priority := 0
switch l.Code {
case "ru":
priority = -3
case "kk":
priority = -2
case "en":
priority = -1
}
return priority
}

125
kernel/queue.go Normal file
View File

@ -0,0 +1,125 @@
package kernel
import (
"errors"
)
type Queue struct {
Setting *ConvertSetting
Status StatusContract
Error error
}
type StatusContract interface {
Name() string
Ordinal() int
}
const (
Waiting = iota
InProgress
Completed
Error
)
type StatusType uint
var statusTypeStrings = []string{
"waiting",
"inProgress",
"completed",
"error",
}
func (status StatusType) Name() string {
return statusTypeStrings[status]
}
func (status StatusType) Ordinal() int {
return int(status)
}
type QueueListenerContract interface {
Add(key int, queue *Queue)
Remove(key int)
}
type QueueListContract interface {
AddListener(queueListener QueueListenerContract)
GetItems() map[int]*Queue
Add(setting *ConvertSetting)
Remove(key int)
GetItem(key int) (*Queue, error)
Next() (key int, queue *Queue)
}
type QueueList struct {
currentKey *int
items map[int]*Queue
queueListener map[int]QueueListenerContract
}
func NewQueueList() *QueueList {
currentKey := 0
return &QueueList{
currentKey: &currentKey,
items: map[int]*Queue{},
queueListener: map[int]QueueListenerContract{},
}
}
func (l QueueList) GetItems() map[int]*Queue {
return l.items
}
func (l QueueList) Add(setting *ConvertSetting) {
queue := Queue{
Setting: setting,
Status: StatusType(Waiting),
}
*l.currentKey += 1
l.items[*l.currentKey] = &queue
l.eventAdd(*l.currentKey, &queue)
}
func (l QueueList) Remove(key int) {
if _, ok := l.items[key]; ok {
delete(l.items, key)
l.eventRemove(key)
}
}
func (l QueueList) GetItem(key int) (*Queue, error) {
if item, ok := l.items[key]; ok {
return item, nil
}
return nil, errors.New("key not found")
}
func (l QueueList) AddListener(queueListener QueueListenerContract) {
l.queueListener[len(l.queueListener)] = queueListener
}
func (l QueueList) eventAdd(key int, queue *Queue) {
for _, listener := range l.queueListener {
listener.Add(key, queue)
}
}
func (l QueueList) eventRemove(key int) {
for _, listener := range l.queueListener {
listener.Remove(key)
}
}
func (l QueueList) Next() (key int, queue *Queue) {
statusWaiting := StatusType(Waiting)
for key, item := range l.items {
if item.Status == statusWaiting {
return key, item
}
}
return -1, nil
}

80
kernel/window.go Normal file
View File

@ -0,0 +1,80 @@
package kernel
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/dialog"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/helper"
"time"
)
type WindowContract interface {
SetContent(content fyne.CanvasObject)
SetMainMenu(menu *fyne.MainMenu)
NewFileOpen(callback func(fyne.URIReadCloser, error), location fyne.ListableURI) *dialog.FileDialog
NewFolderOpen(callback func(fyne.ListableURI, error), location fyne.ListableURI) *dialog.FileDialog
ShowAndRun()
GetLayout() LayoutContract
}
type Window struct {
windowFyne fyne.Window
layout LayoutContract
}
func newWindow(w fyne.Window, layout LayoutContract) Window {
windowSize := fyne.Size{Width: 1039, Height: 599}
w.Resize(windowSize)
w.CenterOnScreen()
go func() {
/**
* Bug fixed.
* When starting the program, sometimes the window was displayed incorrectly.
*/
windowSize.Width += 1
windowSize.Height += 1
time.Sleep(time.Millisecond * 500)
w.Resize(windowSize)
}()
return Window{
windowFyne: w,
layout: layout,
}
}
func (w Window) SetContent(content fyne.CanvasObject) {
w.windowFyne.SetContent(w.layout.SetContent(content))
}
func (w Window) NewFileOpen(callback func(fyne.URIReadCloser, error), location fyne.ListableURI) *dialog.FileDialog {
fileDialog := dialog.NewFileOpen(callback, w.windowFyne)
helper.FileDialogResize(fileDialog, w.windowFyne)
fileDialog.Show()
if location != nil {
fileDialog.SetLocation(location)
}
return fileDialog
}
func (w Window) NewFolderOpen(callback func(fyne.ListableURI, error), location fyne.ListableURI) *dialog.FileDialog {
fileDialog := dialog.NewFolderOpen(callback, w.windowFyne)
helper.FileDialogResize(fileDialog, w.windowFyne)
fileDialog.Show()
if location != nil {
fileDialog.SetLocation(location)
}
return fileDialog
}
func (w Window) SetMainMenu(menu *fyne.MainMenu) {
w.windowFyne.SetMainMenu(menu)
}
func (w Window) ShowAndRun() {
w.windowFyne.ShowAndRun()
}
func (w Window) GetLayout() LayoutContract {
return w.layout
}

1
languages/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
translate.*.toml

399
languages/active.en.toml Normal file
View File

@ -0,0 +1,399 @@
[AlsoUsedProgram]
hash = "sha1-a72be72e7808bb8a0144ed7a93acb29c568b1ed4"
other = "The program also uses:"
[about]
hash = "sha1-3da0b9ef719fd707f443ac00404447f29445976f"
other = "About"
[aboutText]
hash = "sha1-8bd565814118ba8b90c40eb5b62acf8d2676e7d6"
other = "A simple interface for the FFmpeg console utility. \nBut I am not the author of the FFmpeg utility itself."
[buttonDownloadFFmpeg]
hash = "sha1-c223c2e15171156192bc3146aee91e6094bb475b"
other = "Download FFmpeg automatically"
[buttonForSelectedDirTitle]
hash = "sha1-8cbe5c67bcf89e4624635a79cbea104227faedda"
other = "Save to folder:"
[cancel]
hash = "sha1-0ec753be8df955a117404fb634b01b45eb386e2a"
other = "Cancel"
[changeFFPath]
hash = "sha1-46793a2844600d0eb19fa3540fb9564ee5705491"
other = "FFmpeg and FFprobe"
[changeLanguage]
hash = "sha1-8b276eaf378d485c769fb3d5dcc06dfc25b0c01b"
other = "Change language"
[checkboxOverwriteOutputFilesTitle]
hash = "sha1-5860124bb781e7ef680f573fa93977e96328d4e7"
other = "Allow file to be overwritten"
[choose]
hash = "sha1-f60bb5f761024d973834b5e9d25ceebce2c85f94"
other = "choose"
[completedQueue]
hash = "sha1-398c7d4f7b0d522afb930769c0fbb1a9f4b61fbe"
other = "Completed"
[converterVideoFilesSubmitTitle]
hash = "sha1-7ac460f3c24c9952082f2db6e4d62f752598709c"
other = "Convert"
[converterVideoFilesTitle]
hash = "sha1-1ab29597cc9dfefab08e54ea5442e7ffa15f0394"
other = "Video, audio and picture converter"
[download]
hash = "sha1-fe8f79f29da457de2f6bc9531de6e536e0c426ad"
other = "Download"
[downloadFFmpegFromSite]
hash = "sha1-0889c95aa3a8659d8d903b4dab7097699c4d8aa4"
other = "Will be downloaded from the site:"
[downloadRun]
hash = "sha1-55f87f114628fa2d5d8e67d1e1cda22c0e4f9271"
other = "Downloading..."
[encoderGroupAudio]
hash = "sha1-24321cb5400df96be8f3e2131918bebdb3a01bba"
other = "Audio"
[encoderGroupImage]
hash = "sha1-a7e528bc7ac9538aec87d1593c38b80be95d4745"
other = "Images"
[encoderGroupVideo]
hash = "sha1-8e7b9894c7ef0f57ac0bf910f6a8aac1c8a53683"
other = "Video"
[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!"
[errorConverter]
hash = "sha1-55ebddceddb8b044e33cc3893ec2eba7bbd9fcf9"
other = "Couldn't convert video"
[errorDatabase]
hash = "sha1-531abc3f0d12727e542df6e5a22de91098380fc1"
other = "could not create file 'database' in folder 'data'"
[errorFFmpeg]
hash = "sha1-ccf0b95c0d1b392dc215258d917eb4e5d0b88ed0"
other = "this is not FFmpeg"
[errorFFmpegVersion]
hash = "sha1-9a4148d42186b6b32cf83bef726e23022c53283f"
other = "Could not determine FFmpeg version"
[errorFFprobe]
hash = "sha1-86d1b0b4c4ccd6a4f71e758fc67ce11aff4ba9b8"
other = "this is not FFprobe"
[errorFFprobeVersion]
hash = "sha1-da7b37d7df3fafbd153665b13888413d52b24c17"
other = "Failed to determine FFprobe version"
[errorQueue]
hash = "sha1-72aecd9ad85642d84d62dbbf3cf70953c5f696c7"
other = "Error"
[errorSelectedEncoder]
hash = "sha1-33ed1aaf4cb3c2ee9d8f8c325b9b75d16ddf9979"
other = "Converter not selected"
[errorSelectedFolderSave]
hash = "sha1-16f3ef93ee36813fdd79d8fb9bb7fc02acbb94a8"
other = "No save folder selected!"
[errorSelectedFormat]
hash = "sha1-cda92c56a1ef1aabc92bbfc405ede8ab13087e66"
other = "File extension not selected"
[exit]
hash = "sha1-c42457057d1ab7950cea00719cbe0b078891775f"
other = "Exit"
[ffmpegLGPL]
hash = "sha1-d395b16cc8f8eab98a8a970307c5b010ba22dde6"
other = "This software uses libraries from the **FFmpeg** project under the **[LGPLv2.1](http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html)**."
[ffmpegTrademark]
hash = "sha1-45f772b2eca5098cd6d31f2d1dc6edec1987a617"
other = "**FFmpeg** is a trademark of **[Fabrice Bellard](http://bellard.org/)**, originator of the **[FFmpeg](https://ffmpeg.org/about.html)** project."
[fileForConversionTitle]
hash = "sha1-96ac799e1086b31fd8f5f8d4c801829d6c853f08"
other = "File:"
[formPreset]
hash = "sha1-7759891ba1ef9f7adc70defc7ac18fbf149c1a68"
other = "Preset"
[help]
hash = "sha1-6a45cef900c668effcb2ab10da05855c1fd10f6f"
other = "Help"
[inProgressQueue]
hash = "sha1-eff79c40e2100ae5fadf3a7d99336025edcca8b5"
other = "In Progress"
[languageSelectionFormHead]
hash = "sha1-0ff5fa82cf684112660128cba1711297acf11003"
other = "Switch language"
[languageSelectionHead]
hash = "sha1-daf1108fc10d3b1a908288d611f749b3cc651e4b"
other = "Choose language"
[licenseLink]
hash = "sha1-ea18ab849f0eea030d770da82c2a6b3484a7bd13"
other = "License information"
[licenseLinkOther]
hash = "sha1-359fff328717c05104e51a2d29f05bf1875d26b7"
other = "Licenses from other products used in the program"
[parameterCheckbox]
hash = "sha1-9e35221d454870996fd51d576249cf47d1784a3c"
other = "Enable option"
[pathToFfmpeg]
hash = "sha1-fafc50f1db0f720fe83a96cd70a9e1ad824e96b6"
other = "Path to FFmpeg:"
[pathToFfprobe]
hash = "sha1-b872edc9633a2e81ef678dc46fe46a7e91732024"
other = "Path to FFprobe:"
[preset_fast]
hash = "sha1-935e1ac9d3c8ba4478326c909ba66662acb0540e"
other = "fast (slower than \"faster\", but the file will weigh less)"
[preset_faster]
hash = "sha1-98620b73c896440c39ea6ec4b9b19d41301c9a7e"
other = "faster (slower than \"veryfast\", but the file will weigh less)"
[preset_medium]
hash = "sha1-f7d1c30135c22c2f07c247075c0df103bb3c3ea5"
other = "medium (slower than \"fast\", but the file will weigh less)"
[preset_placebo]
hash = "sha1-7bcff099104bb192881139e6404981bd426b3f91"
other = "placebo (not recommended)"
[preset_slow]
hash = "sha1-681bf587275a45b48af49bb2ad8f0947919530e7"
other = "slow (slower than \"medium\", but the file will weigh less)"
[preset_slower]
hash = "sha1-d1c692ee2b7643ae2c71a48bea880327a3c6b1e3"
other = "slower (slower than \"slow\", but the file will weigh less)"
[preset_superfast]
hash = "sha1-41c39959e8f1547cc9259a5b459c4ccbf368cc23"
other = "superfast (slower than \"ultrafast\", but the file will weigh less)"
[preset_ultrafast]
hash = "sha1-dfed981573ac2046832f9a9450bc9388958753fa"
other = "ultrafast (fast, but the file will weigh a lot)"
[preset_veryfast]
hash = "sha1-370b82509887d02d7a2ef9b110df4616b16123ce"
other = "veryfast (slower than \"superfast\", but the file will weigh less)"
[preset_veryslow]
hash = "sha1-d428bfa6deea9dd5c7c1f80ceba24e123ae96d0d"
other = "veryslow (slower than \"slower\", but the file will weigh less)"
[programmLink]
hash = "sha1-18f9a3fad6aacefe1b05eed23122800b391ff5ca"
other = "Project website"
[programmVersion]
hash = "sha1-fa2e4994a301bb24bc2a8fa166e5486ea95a7475"
other = "**Program version:** {{.Version}}"
[queue]
hash = "sha1-aec93b16baeaf55fed871075c9494a460e4a91b8"
other = "Queue"
[save]
hash = "sha1-4864057d626a868fa60f999bed3191d61d045ddc"
other = "Save"
[selectEncoder]
hash = "sha1-88f3670b09758a3336057520a215058d61006abd"
other = "Encoder:"
[selectFFPathTitle]
hash = "sha1-95581446a28d968ff1a027c623159a7eb08654cf"
other = "Specify the path to FFmpeg and FFprobe"
[selectFormat]
hash = "sha1-f3809b0b48886570cd4cf1d7099de6da5b6d4524"
other = "File extension:"
[settings]
hash = "sha1-7f17c7c62a7fd8d1a508481f4778688927734c2f"
other = "Settings"
[testFF]
hash = "sha1-f5b8ed88e9609963035d2235be0a79bbec619976"
other = "Checking FFmpeg for serviceability..."
[titleDownloadLink]
hash = "sha1-92df86371f6c3a06ca1e4754f113142776a32d49"
other = "You can download it from here"
[total]
hash = "sha1-3b5143902e0c5c84459aedf918e17604d9735b94"
other = "Total"
[unzipRun]
hash = "sha1-c554dad13026668a1f6adf3171837c5d51bbac36"
other = "Unpacked..."
[waitingQueue]
hash = "sha1-307429dd84150877080c4bbff2b340d1e7dadff2"
other = "Waiting"

399
languages/active.kk.toml Normal file
View File

@ -0,0 +1,399 @@
[AlsoUsedProgram]
hash = "sha1-a72be72e7808bb8a0144ed7a93acb29c568b1ed4"
other = "Бағдарлама сонымен қатар пайдаланады:"
[about]
hash = "sha1-3da0b9ef719fd707f443ac00404447f29445976f"
other = "Бағдарлама туралы"
[aboutText]
hash = "sha1-8bd565814118ba8b90c40eb5b62acf8d2676e7d6"
other = "FFmpeg консоль утилитасы үшін қарапайым интерфейс. \nБірақ мен FFmpeg утилитасының авторы емеспін."
[buttonDownloadFFmpeg]
hash = "sha1-c223c2e15171156192bc3146aee91e6094bb475b"
other = "FFmpeg автоматты түрде жүктеп алыңыз"
[buttonForSelectedDirTitle]
hash = "sha1-8cbe5c67bcf89e4624635a79cbea104227faedda"
other = "Қалтаға сақтаңыз:"
[cancel]
hash = "sha1-0ec753be8df955a117404fb634b01b45eb386e2a"
other = "Болдырмау"
[changeFFPath]
hash = "sha1-46793a2844600d0eb19fa3540fb9564ee5705491"
other = "FFmpeg және FFprobe"
[changeLanguage]
hash = "sha1-8b276eaf378d485c769fb3d5dcc06dfc25b0c01b"
other = "Тілді өзгерту"
[checkboxOverwriteOutputFilesTitle]
hash = "sha1-5860124bb781e7ef680f573fa93977e96328d4e7"
other = "Файлды қайта жазуға рұқсат беріңіз"
[choose]
hash = "sha1-f60bb5f761024d973834b5e9d25ceebce2c85f94"
other = "таңдау"
[completedQueue]
hash = "sha1-398c7d4f7b0d522afb930769c0fbb1a9f4b61fbe"
other = "Дайын"
[converterVideoFilesSubmitTitle]
hash = "sha1-7ac460f3c24c9952082f2db6e4d62f752598709c"
other = "Файлды түрлендіру"
[converterVideoFilesTitle]
hash = "sha1-1ab29597cc9dfefab08e54ea5442e7ffa15f0394"
other = "Бейне, аудио және суретті түрлендіргіш"
[download]
hash = "sha1-fe8f79f29da457de2f6bc9531de6e536e0c426ad"
other = "Жүктеп алу"
[downloadFFmpegFromSite]
hash = "sha1-0889c95aa3a8659d8d903b4dab7097699c4d8aa4"
other = "Сайттан жүктеледі:"
[downloadRun]
hash = "sha1-55f87f114628fa2d5d8e67d1e1cda22c0e4f9271"
other = "Жүктеп алынуда..."
[encoderGroupAudio]
hash = "sha1-24321cb5400df96be8f3e2131918bebdb3a01bba"
other = "Аудио"
[encoderGroupImage]
hash = "sha1-a7e528bc7ac9538aec87d1593c38b80be95d4745"
other = "Суреттер"
[encoderGroupVideo]
hash = "sha1-8e7b9894c7ef0f57ac0bf910f6a8aac1c8a53683"
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 = "Қате орын алды!"
[errorConverter]
hash = "sha1-55ebddceddb8b044e33cc3893ec2eba7bbd9fcf9"
other = "Бейнені түрлендіру мүмкін болмады"
[errorDatabase]
hash = "sha1-531abc3f0d12727e542df6e5a22de91098380fc1"
other = "'data' қалтасында 'database' файлын жасау мүмкін болмады"
[errorFFmpeg]
hash = "sha1-ccf0b95c0d1b392dc215258d917eb4e5d0b88ed0"
other = "бұл FFmpeg емес"
[errorFFmpegVersion]
hash = "sha1-9a4148d42186b6b32cf83bef726e23022c53283f"
other = "FFmpeg нұсқасын анықтау мүмкін болмады"
[errorFFprobe]
hash = "sha1-86d1b0b4c4ccd6a4f71e758fc67ce11aff4ba9b8"
other = "бұл FFprobe емес"
[errorFFprobeVersion]
hash = "sha1-da7b37d7df3fafbd153665b13888413d52b24c17"
other = "FFprobe нұсқасын анықтау мүмкін болмады"
[errorQueue]
hash = "sha1-72aecd9ad85642d84d62dbbf3cf70953c5f696c7"
other = "Қате"
[errorSelectedEncoder]
hash = "sha1-33ed1aaf4cb3c2ee9d8f8c325b9b75d16ddf9979"
other = "Түрлендіргіш таңдалмаған"
[errorSelectedFolderSave]
hash = "sha1-16f3ef93ee36813fdd79d8fb9bb7fc02acbb94a8"
other = "Сақтау қалтасы таңдалмаған!"
[errorSelectedFormat]
hash = "sha1-cda92c56a1ef1aabc92bbfc405ede8ab13087e66"
other = "Файл кеңейтімі таңдалмаған"
[exit]
hash = "sha1-c42457057d1ab7950cea00719cbe0b078891775f"
other = "Шығу"
[ffmpegLGPL]
hash = "sha1-d395b16cc8f8eab98a8a970307c5b010ba22dde6"
other = "Бұл бағдарламалық құрал **[LGPLv2.1](http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html)** астында **FFmpeg** жобасының кітапханаларын пайдаланады."
[ffmpegTrademark]
hash = "sha1-45f772b2eca5098cd6d31f2d1dc6edec1987a617"
other = "FFmpeg — **[FFmpeg](https://ffmpeg.org/about.html)** жобасын жасаушы **[Fabrice Bellard](http://bellard.org/)** сауда белгісі."
[fileForConversionTitle]
hash = "sha1-96ac799e1086b31fd8f5f8d4c801829d6c853f08"
other = "Файл:"
[formPreset]
hash = "sha1-7759891ba1ef9f7adc70defc7ac18fbf149c1a68"
other = "Алдын ала орнатылған"
[help]
hash = "sha1-6a45cef900c668effcb2ab10da05855c1fd10f6f"
other = "Анықтама"
[inProgressQueue]
hash = "sha1-eff79c40e2100ae5fadf3a7d99336025edcca8b5"
other = "Орындалуда"
[languageSelectionFormHead]
hash = "sha1-0ff5fa82cf684112660128cba1711297acf11003"
other = "Тілді ауыстыру"
[languageSelectionHead]
hash = "sha1-daf1108fc10d3b1a908288d611f749b3cc651e4b"
other = "Тілді таңдаңыз"
[licenseLink]
hash = "sha1-ea18ab849f0eea030d770da82c2a6b3484a7bd13"
other = "Лицензия туралы ақпарат"
[licenseLinkOther]
hash = "sha1-359fff328717c05104e51a2d29f05bf1875d26b7"
other = "Бағдарламада пайдаланылатын басқа өнімдердің лицензиялары"
[parameterCheckbox]
hash = "sha1-9e35221d454870996fd51d576249cf47d1784a3c"
other = "Опцияны қосу"
[pathToFfmpeg]
hash = "sha1-fafc50f1db0f720fe83a96cd70a9e1ad824e96b6"
other = "FFmpeg жол:"
[pathToFfprobe]
hash = "sha1-b872edc9633a2e81ef678dc46fe46a7e91732024"
other = "FFprobe жол:"
[preset_fast]
hash = "sha1-935e1ac9d3c8ba4478326c909ba66662acb0540e"
other = "fast («faster» қарағанда баяуырақ, бірақ файлдың салмағы аз болады)"
[preset_faster]
hash = "sha1-98620b73c896440c39ea6ec4b9b19d41301c9a7e"
other = "faster («veryfast» қарағанда баяуырақ, бірақ файлдың салмағы аз болады)"
[preset_medium]
hash = "sha1-f7d1c30135c22c2f07c247075c0df103bb3c3ea5"
other = "medium («fast» қарағанда баяуырақ, бірақ файлдың салмағы аз болады)"
[preset_placebo]
hash = "sha1-7bcff099104bb192881139e6404981bd426b3f91"
other = "placebo (ұсынылмайды)"
[preset_slow]
hash = "sha1-681bf587275a45b48af49bb2ad8f0947919530e7"
other = "slow («medium» қарағанда баяуырақ, бірақ файлдың салмағы аз болады)"
[preset_slower]
hash = "sha1-d1c692ee2b7643ae2c71a48bea880327a3c6b1e3"
other = "slower («slow» қарағанда баяуырақ, бірақ файлдың салмағы аз болады)"
[preset_superfast]
hash = "sha1-41c39959e8f1547cc9259a5b459c4ccbf368cc23"
other = "superfast («ultrafast» қарағанда баяуырақ, бірақ файлдың салмағы аз болады)"
[preset_ultrafast]
hash = "sha1-dfed981573ac2046832f9a9450bc9388958753fa"
other = "ultrafast (жылдам, бірақ файлдың салмағы көп болады)"
[preset_veryfast]
hash = "sha1-370b82509887d02d7a2ef9b110df4616b16123ce"
other = "veryfast («superfast» қарағанда баяуырақ, бірақ файлдың салмағы аз болады)"
[preset_veryslow]
hash = "sha1-d428bfa6deea9dd5c7c1f80ceba24e123ae96d0d"
other = "veryslow («slower» қарағанда баяуырақ, бірақ файлдың салмағы аз болады)"
[programmLink]
hash = "sha1-18f9a3fad6aacefe1b05eed23122800b391ff5ca"
other = "Жобаның веб-сайты"
[programmVersion]
hash = "sha1-fa2e4994a301bb24bc2a8fa166e5486ea95a7475"
other = "**Бағдарлама нұсқасы:** {{.Version}}"
[queue]
hash = "sha1-aec93b16baeaf55fed871075c9494a460e4a91b8"
other = "Кезек"
[save]
hash = "sha1-4864057d626a868fa60f999bed3191d61d045ddc"
other = "Сақтау"
[selectEncoder]
hash = "sha1-88f3670b09758a3336057520a215058d61006abd"
other = "Кодировщик:"
[selectFFPathTitle]
hash = "sha1-95581446a28d968ff1a027c623159a7eb08654cf"
other = "FFmpeg және FFprobe жолын көрсетіңіз"
[selectFormat]
hash = "sha1-f3809b0b48886570cd4cf1d7099de6da5b6d4524"
other = "Файл кеңейтімі:"
[settings]
hash = "sha1-7f17c7c62a7fd8d1a508481f4778688927734c2f"
other = "Параметрлер"
[testFF]
hash = "sha1-f5b8ed88e9609963035d2235be0a79bbec619976"
other = "FFmpeg функционалдығы тексерілуде..."
[titleDownloadLink]
hash = "sha1-92df86371f6c3a06ca1e4754f113142776a32d49"
other = "Сіз оны осы жерден жүктей аласыз"
[total]
hash = "sha1-3b5143902e0c5c84459aedf918e17604d9735b94"
other = "Барлығы"
[unzipRun]
hash = "sha1-c554dad13026668a1f6adf3171837c5d51bbac36"
other = "Орамнан шығарылуда..."
[waitingQueue]
hash = "sha1-307429dd84150877080c4bbff2b340d1e7dadff2"
other = "Күту"

100
languages/active.ru.toml Normal file
View File

@ -0,0 +1,100 @@
AlsoUsedProgram = "Также в программе используется:"
about = "О программе"
aboutText = "Простенький интерфейс для консольной утилиты FFmpeg. \nНо я не являюсь автором самой утилиты FFmpeg."
buttonDownloadFFmpeg = "Скачать автоматически FFmpeg"
buttonForSelectedDirTitle = "Сохранить в папку:"
cancel = "Отмена"
changeFFPath = "FFmpeg и FFprobe"
changeLanguage = "Поменять язык"
checkboxOverwriteOutputFilesTitle = "Разрешить перезаписать файл"
choose = "выбрать"
completedQueue = "Готово"
converterVideoFilesSubmitTitle = "Конвертировать"
converterVideoFilesTitle = "Конвертер видео, аудио и картинок"
download = "Скачать"
downloadFFmpegFromSite = "Будет скачано с сайта:"
downloadRun = "Скачивается..."
encoderGroupAudio = "Аудио"
encoderGroupImage = "Картинки"
encoderGroupVideo = "Видео"
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'"
errorFFmpeg = "это не FFmpeg"
errorFFmpegVersion = "Не смогли определить версию FFmpeg"
errorFFprobe = "это не FFprobe"
errorFFprobeVersion = "Не смогли определить версию FFprobe"
errorQueue = "Ошибка"
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)**."
fileForConversionTitle = "Файл:"
formPreset = "Предустановка"
help = "Справка"
inProgressQueue = "Выполняется"
languageSelectionFormHead = "Переключить язык"
languageSelectionHead = "Выберите язык"
licenseLink = "Сведения о лицензии"
licenseLinkOther = "Лицензии от других продуктов, которые используются в программе"
parameterCheckbox = "Включить параметр"
pathToFfmpeg = "Путь к FFmpeg:"
pathToFfprobe = "Путь к FFprobe:"
preset_fast = "fast (медленней чем faster, но будет файл и меньше весить)"
preset_faster = "faster (медленней чем veryfast, но будет файл и меньше весить)"
preset_medium = "medium (медленней чем fast, но будет файл и меньше весить)"
preset_placebo = "placebo (не рекомендуется)"
preset_slow = "slow (медленней чем medium, но будет файл и меньше весить)"
preset_slower = "slower (медленней чем slow, но будет файл и меньше весить)"
preset_superfast = "superfast (медленней чем ultrafast, но будет файл и меньше весить)"
preset_ultrafast = "ultrafast (быстро, но файл будет много весить)"
preset_veryfast = "veryfast (медленней чем superfast, но будет файл и меньше весить)"
preset_veryslow = "veryslow (медленней чем slower, но будет файл и меньше весить)"
programmLink = "Сайт проекта"
programmVersion = "**Версия программы:** {{.Version}}"
queue = "Очередь"
save = "Сохранить"
selectEncoder = "Кодировщик:"
selectFFPathTitle = "Укажите путь к FFmpeg и к FFprobe"
selectFormat = "Расширение файла:"
settings = "Настройки"
testFF = "Проверка FFmpeg на работоспособность..."
titleDownloadLink = "Скачать можно от сюда"
total = "Всего"
unzipRun = "Распаковывается..."
waitingQueue = "В очереди"

26
localizer/repository.go Normal file
View File

@ -0,0 +1,26 @@
package localizer
import (
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/setting"
)
type RepositoryContract interface {
GetCode() (string, error)
Save(code string) (setting.Setting, error)
}
type Repository struct {
settingRepository setting.RepositoryContract
}
func NewRepository(settingRepository setting.RepositoryContract) *Repository {
return &Repository{settingRepository: settingRepository}
}
func (r Repository) GetCode() (string, error) {
return r.settingRepository.GetValue("language")
}
func (r Repository) Save(code string) (setting.Setting, error) {
return r.settingRepository.CreateOrUpdate("language", code)
}

74
localizer/view.go Normal file
View File

@ -0,0 +1,74 @@
package localizer
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/widget"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel"
"github.com/nicksnyder/go-i18n/v2/i18n"
)
type ViewContract interface {
LanguageSelection(funcSelected func(lang kernel.Lang))
}
type View struct {
app kernel.AppContract
}
func NewView(app kernel.AppContract) *View {
return &View{
app: app,
}
}
func (v View) LanguageSelection(funcSelected func(lang kernel.Lang)) {
languages := v.app.GetLocalizerService().GetLanguages()
listView := widget.NewList(
func() int {
return len(languages)
},
func() fyne.CanvasObject {
return widget.NewLabel("template")
},
func(i widget.ListItemID, o fyne.CanvasObject) {
block := o.(*widget.Label)
block.SetText(languages[i].Title)
})
listView.OnSelected = func(id widget.ListItemID) {
_ = v.app.GetLocalizerService().SetCurrentLanguage(languages[id])
funcSelected(languages[id])
}
messageHead := v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "languageSelectionHead",
})
v.app.GetWindow().SetContent(widget.NewCard(messageHead, "", listView))
}
func LanguageSelectionForm(localizerService kernel.LocalizerContract, funcSelected func(lang kernel.Lang)) fyne.CanvasObject {
languages := localizerService.GetLanguages()
currentLanguage := localizerService.GetCurrentLanguage()
listView := widget.NewList(
func() int {
return len(languages)
},
func() fyne.CanvasObject {
return widget.NewLabel("template")
},
func(i widget.ListItemID, o fyne.CanvasObject) {
block := o.(*widget.Label)
block.SetText(languages[i].Title)
if languages[i].Code == currentLanguage.Lang.Code {
block.TextStyle = fyne.TextStyle{Bold: true}
}
})
listView.OnSelected = func(id widget.ListItemID) {
_ = localizerService.SetCurrentLanguage(languages[id])
funcSelected(languages[id])
}
messageHead := localizerService.GetMessage(&i18n.LocalizeConfig{
MessageID: "languageSelectionFormHead",
})
return widget.NewCard(messageHead, "", listView)
}

125
main.go Normal file
View File

@ -0,0 +1,125 @@
package main
import (
"errors"
"fyne.io/fyne/v2"
"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/handler"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/localizer"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/menu"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/migration"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/setting"
_ "github.com/mattn/go-sqlite3"
"golang.org/x/text/language"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"os"
)
var application kernel.AppContract
var ffPathUtilities *kernel.FFPathUtilities
func init() {
iconResource, _ := fyne.LoadResourceFromPath("icon.png")
appMetadata := &fyne.AppMetadata{
ID: "net.kor-elf.projects.gui-for-ffmpeg",
Name: "GUI for FFmpeg",
Version: "0.7.0",
Icon: iconResource,
}
localizerService, err := kernel.NewLocalizer("languages", language.Russian)
if err != nil {
kernel.PanicErrorLang(err, appMetadata)
}
ffPathUtilities = &kernel.FFPathUtilities{FFmpeg: "", FFprobe: ""}
convertorService := kernel.NewService(ffPathUtilities)
queue := kernel.NewQueueList()
application = kernel.NewApp(
appMetadata,
localizerService,
queue,
kernel.NewQueueLayoutObject(queue, localizerService),
convertorService,
)
}
func main() {
errorView := error2.NewView(application)
if canCreateFile("data/database") != true {
errorView.PanicErrorWriteDirectoryData()
application.GetWindow().ShowAndRun()
return
}
db, err := gorm.Open(sqlite.Open("data/database"), &gorm.Config{})
if err != nil {
errorView.PanicError(err)
application.GetWindow().ShowAndRun()
return
}
defer appCloseWithDb(db)
err = migration.Run(db)
if err != nil {
errorView.PanicError(err)
application.GetWindow().ShowAndRun()
return
}
settingRepository := setting.NewRepository(db)
convertorRepository := convertor.NewRepository(settingRepository)
pathFFmpeg, err := convertorRepository.GetPathFfmpeg()
if err != nil && errors.Is(err, gorm.ErrRecordNotFound) == false {
errorView.PanicError(err)
application.GetWindow().ShowAndRun()
return
}
ffPathUtilities.FFmpeg = pathFFmpeg
pathFFprobe, err := convertorRepository.GetPathFfprobe()
if err != nil && errors.Is(err, gorm.ErrRecordNotFound) == false {
errorView.PanicError(err)
application.GetWindow().ShowAndRun()
return
}
ffPathUtilities.FFprobe = pathFFprobe
application.RunConvertor()
defer application.AfterClosing()
localizerView := localizer.NewView(application)
convertorView := convertor.NewView(application)
convertorHandler := handler.NewConvertorHandler(application, convertorView, errorView, convertorRepository)
localizerRepository := localizer.NewRepository(settingRepository)
menuView := menu.NewView(application)
mainMenu := handler.NewMenuHandler(application, convertorHandler, menuView, localizerView, localizerRepository)
mainHandler := handler.NewMainHandler(application, convertorHandler, mainMenu, localizerRepository)
mainHandler.Start()
application.GetWindow().SetMainMenu(mainMenu.GetMainMenu())
application.GetWindow().ShowAndRun()
}
func appCloseWithDb(db *gorm.DB) {
sqlDB, err := db.DB()
if err == nil {
_ = sqlDB.Close()
}
}
func canCreateFile(path string) bool {
file, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0666)
if err != nil {
return false
}
_ = file.Close()
return true
}

635
menu/view.go Normal file
View File

@ -0,0 +1,635 @@
package menu
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel"
"github.com/nicksnyder/go-i18n/v2/i18n"
"golang.org/x/image/colornames"
"net/url"
)
type ViewContract interface {
About(ffmpegVersion string, ffprobeVersion string)
}
type View struct {
app kernel.AppContract
}
func NewView(app kernel.AppContract) *View {
return &View{
app: app,
}
}
func (v View) About(ffmpegVersion string, ffprobeVersion string) {
view := v.app.GetAppFyne().NewWindow(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "about",
}))
view.Resize(fyne.Size{Width: 793, Height: 550})
view.SetFixedSize(true)
programmName := canvas.NewText(" GUI for FFmpeg", colornames.Darkgreen)
programmName.TextStyle = fyne.TextStyle{Bold: true}
programmName.TextSize = 20
programmLink := widget.NewHyperlink(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "programmLink",
}), &url.URL{
Scheme: "https",
Host: "git.kor-elf.net",
Path: "kor-elf/gui-for-ffmpeg/releases",
})
licenseLink := widget.NewHyperlink(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "licenseLink",
}), &url.URL{
Scheme: "https",
Host: "git.kor-elf.net",
Path: "kor-elf/gui-for-ffmpeg/src/branch/main/LICENSE",
})
licenseLinkOther := widget.NewHyperlink(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "licenseLinkOther",
}), &url.URL{
Scheme: "https",
Host: "git.kor-elf.net",
Path: "kor-elf/gui-for-ffmpeg/src/branch/main/LICENSE-3RD-PARTY.txt",
})
programmVersion := widget.NewRichTextFromMarkdown(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "programmVersion",
TemplateData: map[string]string{
"Version": v.app.GetAppFyne().Metadata().Version,
},
}))
aboutText := widget.NewRichText(
&widget.TextSegment{
Text: v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "aboutText",
}),
},
)
image := canvas.NewImageFromFile("icon.png")
image.SetMinSize(fyne.Size{Width: 100, Height: 100})
image.FillMode = canvas.ImageFillContain
ffmpegTrademark := widget.NewRichTextFromMarkdown(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "ffmpegTrademark",
}))
ffmpegLGPL := widget.NewRichTextFromMarkdown(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "ffmpegLGPL",
}))
view.SetContent(
container.NewScroll(container.NewVBox(
container.NewBorder(nil, nil, container.NewVBox(image), nil, container.NewVBox(
programmName,
programmVersion,
aboutText,
ffmpegTrademark,
ffmpegLGPL,
v.getCopyright(),
container.NewHBox(programmLink, licenseLink),
container.NewHBox(licenseLinkOther),
)),
v.getAboutFfmpeg(ffmpegVersion),
v.getAboutFfprobe(ffprobeVersion),
widget.NewCard(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "AlsoUsedProgram",
}), "", v.getOther()),
)),
)
view.CenterOnScreen()
view.Show()
}
func (v View) getCopyright() *widget.RichText {
return widget.NewRichTextFromMarkdown("Copyright (c) 2024 **[Leonid Nikitin (kor-elf)](https://git.kor-elf.net/kor-elf/)**.")
}
func (v View) getAboutFfmpeg(version string) *fyne.Container {
programmName := canvas.NewText(" FFmpeg", colornames.Darkgreen)
programmName.TextStyle = fyne.TextStyle{Bold: true}
programmName.TextSize = 20
programmLink := widget.NewHyperlink(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "programmLink",
}), &url.URL{
Scheme: "https",
Host: "ffmpeg.org",
Path: "",
})
licenseLink := widget.NewHyperlink(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "licenseLink",
}), &url.URL{
Scheme: "https",
Host: "ffmpeg.org",
Path: "legal.html",
})
return container.NewVBox(
programmName,
widget.NewLabel(version),
widget.NewRichTextFromMarkdown("**FFmpeg** is a trademark of **[Fabrice Bellard](http://bellard.org/)**, originator of the **[FFmpeg](https://ffmpeg.org/about.html)** project."),
widget.NewRichTextFromMarkdown("This software uses libraries from the **FFmpeg** project under the **[LGPLv2.1](http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html)**."),
container.NewHBox(programmLink, licenseLink),
)
}
func (v View) getAboutFfprobe(version string) *fyne.Container {
programmName := canvas.NewText(" FFprobe", colornames.Darkgreen)
programmName.TextStyle = fyne.TextStyle{Bold: true}
programmName.TextSize = 20
programmLink := widget.NewHyperlink(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "programmLink",
}), &url.URL{
Scheme: "https",
Host: "ffmpeg.org",
Path: "ffprobe.html",
})
licenseLink := widget.NewHyperlink(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "licenseLink",
}), &url.URL{
Scheme: "https",
Host: "ffmpeg.org",
Path: "legal.html",
})
return container.NewVBox(
programmName,
widget.NewLabel(version),
widget.NewRichTextFromMarkdown("**FFmpeg** is a trademark of **[Fabrice Bellard](http://bellard.org/)**, originator of the **[FFmpeg](https://ffmpeg.org/about.html)** project."),
widget.NewRichTextFromMarkdown("This software uses libraries from the **FFmpeg** project under the **[LGPLv2.1](http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html)**."),
container.NewHBox(programmLink, licenseLink),
)
}
func (v View) getOther() *fyne.Container {
return container.NewVBox(
canvas.NewLine(colornames.Darkgreen),
container.NewHBox(widget.NewHyperlink("fyne.io/fyne/v2", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "fyne-io/fyne",
})),
container.NewHBox(widget.NewHyperlink("BSD 3-Clause License", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "fyne-io/fyne/blob/master/LICENSE",
})),
widget.NewRichTextFromMarkdown("Copyright (C) 2018 Fyne.io developers (see [AUTHORS](https://github.com/fyne-io/fyne/blob/master/AUTHORS))"),
canvas.NewLine(colornames.Darkgreen),
container.NewHBox(widget.NewHyperlink("fyne.io/systray", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "fyne-io/systray",
})),
container.NewHBox(widget.NewHyperlink("Apache License", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "fyne-io/systray/blob/master/LICENSE",
})),
canvas.NewLine(colornames.Darkgreen),
container.NewHBox(widget.NewHyperlink("github.com/BurntSushi/toml", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "BurntSushi/toml",
})),
container.NewHBox(widget.NewHyperlink("The MIT License (MIT)", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "BurntSushi/toml/blob/master/COPYING",
})),
widget.NewLabel("Copyright (c) 2013 TOML authors"),
canvas.NewLine(colornames.Darkgreen),
container.NewHBox(widget.NewHyperlink("github.com/davecgh/go-spew", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "davecgh/go-spew",
})),
container.NewHBox(widget.NewHyperlink("ISC License", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "davecgh/go-spew/blob/master/LICENSE",
})),
widget.NewLabel("Copyright (c) 2012-2016 Dave Collins <dave@davec.name>"),
canvas.NewLine(colornames.Darkgreen),
container.NewHBox(widget.NewHyperlink("github.com/fredbi/uri", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "fredbi/uri",
})),
container.NewHBox(widget.NewHyperlink("The MIT License (MIT)", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "fredbi/uri/blob/master/LICENSE.md",
})),
widget.NewLabel("Copyright (c) 2018 Frederic Bidon"),
widget.NewLabel("Copyright (c) 2015 Trey Tacon"),
canvas.NewLine(colornames.Darkgreen),
container.NewHBox(widget.NewHyperlink("github.com/fsnotify/fsnotify", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "fsnotify/fsnotify",
})),
container.NewHBox(widget.NewHyperlink("BSD-3-Clause license", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "fsnotify/fsnotify/blob/main/LICENSE",
})),
widget.NewLabel("Copyright © 2012 The Go Authors. All rights reserved."),
widget.NewLabel("Copyright © fsnotify Authors. All rights reserved."),
canvas.NewLine(colornames.Darkgreen),
container.NewHBox(widget.NewHyperlink("github.com/fyne-io/gl-js", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "fyne-io/gl-js",
})),
container.NewHBox(widget.NewHyperlink("BSD-3-Clause license", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "fyne-io/gl-js/blob/master/LICENSE",
})),
widget.NewLabel("Copyright (c) 2009 The Go Authors. All rights reserved."),
canvas.NewLine(colornames.Darkgreen),
container.NewHBox(widget.NewHyperlink("github.com/fyne-io/glfw-js", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "fyne-io/glfw-js",
})),
container.NewHBox(widget.NewHyperlink("MIT License", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "fyne-io/glfw-js/blob/master/LICENSE",
})),
widget.NewLabel("Copyright (c) 2014 Dmitri Shuralyov"),
canvas.NewLine(colornames.Darkgreen),
container.NewHBox(widget.NewHyperlink("github.com/fyne-io/image", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "fyne-io/image",
})),
container.NewHBox(widget.NewHyperlink("BSD 3-Clause License", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "fyne-io/image/blob/main/LICENSE",
})),
widget.NewLabel("Copyright (c) 2022, Fyne.io"),
canvas.NewLine(colornames.Darkgreen),
container.NewHBox(widget.NewHyperlink("github.com/go-gl/gl", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "go-gl/gl",
})),
container.NewHBox(widget.NewHyperlink("The MIT License (MIT)", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "go-gl/gl/blob/master/LICENSE",
})),
widget.NewLabel("Copyright (c) 2014 Eric Woroshow"),
canvas.NewLine(colornames.Darkgreen),
container.NewHBox(widget.NewHyperlink("github.com/go-gl/glfw/v3.3/glfw", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "go-gl/glfw/",
})),
container.NewHBox(widget.NewHyperlink("BSD-3-Clause license", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "go-gl/glfw/blob/master/LICENSE",
})),
widget.NewLabel("Copyright (c) 2012 The glfw3-go Authors. All rights reserved."),
canvas.NewLine(colornames.Darkgreen),
container.NewHBox(widget.NewHyperlink("github.com/go-text/render", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "go-text/render",
})),
container.NewHBox(widget.NewHyperlink("Unlicense OR BSD-3-Clause", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "go-text/render/blob/main/LICENSE",
})),
widget.NewLabel("Copyright 2021 The go-text authors"),
canvas.NewLine(colornames.Darkgreen),
container.NewHBox(widget.NewHyperlink("github.com/go-text/typesetting", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "go-text/typesetting",
})),
container.NewHBox(widget.NewHyperlink("Unlicense OR BSD-3-Clause", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "go-text/typesetting/blob/main/LICENSE",
})),
widget.NewLabel("Copyright 2021 The go-text authors"),
canvas.NewLine(colornames.Darkgreen),
container.NewHBox(widget.NewHyperlink("github.com/godbus/dbus/v5", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "godbus/dbus",
})),
container.NewHBox(widget.NewHyperlink("BSD 2-Clause \"Simplified\" License", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "godbus/dbus/blob/master/LICENSE",
})),
widget.NewLabel("Copyright (c) 2013, Georg Reinke (<guelfey at gmail dot com>), Google"),
canvas.NewLine(colornames.Darkgreen),
container.NewHBox(widget.NewHyperlink("github.com/gopherjs/gopherjs", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "gopherjs/gopherjs",
})),
container.NewHBox(widget.NewHyperlink("BSD 2-Clause \"Simplified\" License", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "gopherjs/gopherjs/blob/master/LICENSE",
})),
widget.NewLabel("Copyright (c) 2013 Richard Musiol. All rights reserved."),
canvas.NewLine(colornames.Darkgreen),
container.NewHBox(widget.NewHyperlink("github.com/jinzhu/inflection", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "jinzhu/inflection",
})),
container.NewHBox(widget.NewHyperlink("The MIT License (MIT)", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "jinzhu/inflection/blob/master/LICENSE",
})),
widget.NewLabel("Copyright (c) 2015 - Jinzhu"),
canvas.NewLine(colornames.Darkgreen),
container.NewHBox(widget.NewHyperlink("github.com/jsummers/gobmp", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "jsummers/gobmp",
})),
container.NewHBox(widget.NewHyperlink("The MIT License (MIT)", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "jsummers/gobmp/blob/master/COPYING.txt",
})),
widget.NewLabel("Copyright (c) 2012-2015 Jason Summers"),
canvas.NewLine(colornames.Darkgreen),
container.NewHBox(widget.NewHyperlink("github.com/mattn/go-sqlite3", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "mattn/go-sqlite3",
})),
container.NewHBox(widget.NewHyperlink("The MIT License (MIT)", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "mattn/go-sqlite3/blob/master/LICENSE",
})),
widget.NewLabel("Copyright (c) 2014 Yasuhiro Matsumoto"),
canvas.NewLine(colornames.Darkgreen),
container.NewHBox(widget.NewHyperlink("github.com/nicksnyder/go-i18n/v2", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "nicksnyder/go-i18n",
})),
container.NewHBox(widget.NewHyperlink("The MIT License (MIT)", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "nicksnyder/go-i18n/blob/main/LICENSE",
})),
widget.NewRichTextFromMarkdown("Copyright (c) 2014 Nick Snyder [https://github.com/nicksnyder](https://github.com/nicksnyder)"),
canvas.NewLine(colornames.Darkgreen),
container.NewHBox(widget.NewHyperlink("github.com/pmezard/go-difflib", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "pmezard/go-difflib",
})),
container.NewHBox(widget.NewHyperlink("License", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "pmezard/go-difflib/blob/master/LICENSE",
})),
widget.NewLabel("Copyright (c) 2013, Patrick Mezard"),
canvas.NewLine(colornames.Darkgreen),
container.NewHBox(widget.NewHyperlink("github.com/srwiley/oksvg", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "srwiley/oksvg",
})),
container.NewHBox(widget.NewHyperlink("BSD 3-Clause License", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "srwiley/oksvg/blob/master/LICENSE",
})),
widget.NewLabel("Copyright (c) 2018, Steven R Wiley"),
canvas.NewLine(colornames.Darkgreen),
container.NewHBox(widget.NewHyperlink("github.com/srwiley/rasterx", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "srwiley/rasterx",
})),
container.NewHBox(widget.NewHyperlink("BSD 3-Clause License", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "srwiley/rasterx/blob/master/LICENSE",
})),
widget.NewLabel("Copyright (c) 2018, Steven R Wiley"),
canvas.NewLine(colornames.Darkgreen),
container.NewHBox(widget.NewHyperlink("github.com/stretchr/testify", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "stretchr/testify",
})),
container.NewHBox(widget.NewHyperlink("MIT License", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "stretchr/testify/blob/master/LICENSE",
})),
widget.NewLabel("Copyright (c) 2012-2020 Mat Ryer, Tyler Bunnell and contributors."),
canvas.NewLine(colornames.Darkgreen),
container.NewHBox(widget.NewHyperlink("github.com/tevino/abool", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "tevino/abool",
})),
container.NewHBox(widget.NewHyperlink("The MIT License (MIT)", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "tevino/abool/blob/master/LICENSE",
})),
widget.NewLabel("Copyright (c) 2016 Tevin Zhang"),
canvas.NewLine(colornames.Darkgreen),
container.NewHBox(widget.NewHyperlink("github.com/yuin/goldmark", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "yuin/goldmark",
})),
container.NewHBox(widget.NewHyperlink("MIT License", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "yuin/goldmark/blob/master/LICENSE",
})),
widget.NewLabel("Copyright (c) 2019 Yusuke Inuzuka"),
canvas.NewLine(colornames.Darkgreen),
container.NewHBox(widget.NewHyperlink("golang.org/x/image", &url.URL{
Scheme: "https",
Host: "pkg.go.dev",
Path: "golang.org/x/image",
})),
container.NewHBox(widget.NewHyperlink("License", &url.URL{
Scheme: "https",
Host: "cs.opensource.google",
Path: "go/x/image/+/master:LICENSE",
})),
widget.NewLabel("Copyright (c) 2009 The Go Authors. All rights reserved."),
canvas.NewLine(colornames.Darkgreen),
container.NewHBox(widget.NewHyperlink("golang.org/x/mobile", &url.URL{
Scheme: "https",
Host: "pkg.go.dev",
Path: "golang.org/x/mobile",
})),
container.NewHBox(widget.NewHyperlink("License", &url.URL{
Scheme: "https",
Host: "cs.opensource.google",
Path: "go/x/mobile/+/master:LICENSE",
})),
widget.NewLabel("Copyright (c) 2009 The Go Authors. All rights reserved."),
canvas.NewLine(colornames.Darkgreen),
container.NewHBox(widget.NewHyperlink("golang.org/x/net", &url.URL{
Scheme: "https",
Host: "pkg.go.dev",
Path: "golang.org/x/net",
})),
container.NewHBox(widget.NewHyperlink("License", &url.URL{
Scheme: "https",
Host: "cs.opensource.google",
Path: "go/x/net/+/master:LICENSE",
})),
widget.NewLabel("Copyright (c) 2009 The Go Authors. All rights reserved."),
canvas.NewLine(colornames.Darkgreen),
container.NewHBox(widget.NewHyperlink("golang.org/x/sys", &url.URL{
Scheme: "https",
Host: "pkg.go.dev",
Path: "golang.org/x/sys",
})),
container.NewHBox(widget.NewHyperlink("License", &url.URL{
Scheme: "https",
Host: "cs.opensource.google",
Path: "go/x/sys/+/master:LICENSE",
})),
widget.NewLabel("Copyright (c) 2009 The Go Authors. All rights reserved."),
canvas.NewLine(colornames.Darkgreen),
container.NewHBox(widget.NewHyperlink("golang.org/x/text", &url.URL{
Scheme: "https",
Host: "pkg.go.dev",
Path: "golang.org/x/text",
})),
container.NewHBox(widget.NewHyperlink("License", &url.URL{
Scheme: "https",
Host: "cs.opensource.google",
Path: "go/x/text/+/master:LICENSE",
})),
widget.NewLabel("Copyright (c) 2009 The Go Authors. All rights reserved."),
canvas.NewLine(colornames.Darkgreen),
container.NewHBox(widget.NewHyperlink("gopkg.in/yaml.v3", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "go-yaml/yaml/tree/v3.0.1",
})),
container.NewHBox(widget.NewHyperlink("Licensed under the Apache License, Version 2.0", &url.URL{
Scheme: "http",
Host: "www.apache.org",
Path: "licenses/LICENSE-2.0",
})),
widget.NewLabel("Copyright 2011-2016 Canonical Ltd."),
canvas.NewLine(colornames.Darkgreen),
container.NewHBox(widget.NewHyperlink("gorm.io/gorm", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "go-gorm/gorm",
})),
container.NewHBox(widget.NewHyperlink("The MIT License (MIT)", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "go-gorm/gorm/blob/master/LICENSE",
})),
widget.NewLabel("Copyright (c) 2013-NOW Jinzhu <wosmvp@gmail.com>"),
canvas.NewLine(colornames.Darkgreen),
container.NewHBox(widget.NewHyperlink("honnef.co/go/js/dom", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "dominikh/go-js-dom",
})),
container.NewHBox(widget.NewHyperlink("The MIT License (MIT)", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "dominikh/go-js-dom/blob/master/LICENSE",
})),
widget.NewLabel("Copyright (c) 2014 Dominik Honnef"),
canvas.NewLine(colornames.Darkgreen),
container.NewHBox(widget.NewHyperlink("github.com/golang/go", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "golang/go",
})),
container.NewHBox(widget.NewHyperlink("BSD 3-Clause \"New\" or \"Revised\" License", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "golang/go/blob/master/LICENSE",
})),
widget.NewLabel("Copyright (c) 2009 The Go Authors. All rights reserved."),
canvas.NewLine(colornames.Darkgreen),
container.NewHBox(widget.NewHyperlink("github.com/golang/go", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "golang/go",
})),
container.NewHBox(widget.NewHyperlink("BSD 3-Clause \"New\" or \"Revised\" License", &url.URL{
Scheme: "https",
Host: "github.com",
Path: "golang/go/blob/master/LICENSE",
})),
widget.NewLabel("Copyright (c) 2009 The Go Authors. All rights reserved."),
canvas.NewLine(colornames.Darkgreen),
)
}

View File

@ -1,7 +1,7 @@
package migration
import (
"ffmpegGui/setting"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/setting"
"gorm.io/gorm"
)

View File

@ -1,11 +1,13 @@
package setting
import (
"errors"
"gorm.io/gorm"
)
type RepositoryContract interface {
Create(setting Setting) (Setting, error)
CreateOrUpdate(code string, value string) (Setting, error)
GetValue(code string) (value string, err error)
}
@ -33,3 +35,21 @@ func (r Repository) Create(setting Setting) (Setting, error) {
}
return setting, err
}
func (r Repository) CreateOrUpdate(code string, value string) (Setting, error) {
var setting Setting
err := r.db.Where("code = ?", code).First(&setting).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) == true {
setting = Setting{Code: code, Value: value}
return r.Create(setting)
} else {
return setting, err
}
}
err = r.db.Model(&setting).UpdateColumn("value", value).Error
if err != nil {
return setting, err
}
return setting, err
}

View File

@ -1,190 +0,0 @@
package convertor
import (
"errors"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/widget"
"image/color"
)
type ViewContract interface {
Main(
runConvert func(setting HandleConvertSetting, progressbar *widget.ProgressBar) error,
)
}
type View struct {
w fyne.Window
}
type HandleConvertSetting struct {
VideoFileInput *File
DirectoryForSave string
OverwriteOutputFiles bool
}
type enableFormConversionStruct struct {
fileVideoForConversion *widget.Button
buttonForSelectedDir *widget.Button
form *widget.Form
}
func NewView(w fyne.Window) *View {
return &View{w}
}
func (v View) Main(
runConvert func(setting HandleConvertSetting, progressbar *widget.ProgressBar) error,
) {
form := &widget.Form{}
conversionMessage := canvas.NewText("", color.RGBA{255, 0, 0, 255})
conversionMessage.TextSize = 16
conversionMessage.TextStyle = fyne.TextStyle{Bold: true}
progress := widget.NewProgressBar()
fileVideoForConversion, fileVideoForConversionMessage, fileInput := v.getButtonFileVideoForConversion(form, progress, conversionMessage)
buttonForSelectedDir, buttonForSelectedDirMessage, pathToSaveDirectory := v.getButtonForSelectingDirectoryForSaving()
isOverwriteOutputFiles := false
checkboxOverwriteOutputFiles := widget.NewCheck("Разрешить перезаписать файл", func(b bool) {
isOverwriteOutputFiles = b
})
form.Items = []*widget.FormItem{
{Text: "Файл для ковертации:", Widget: fileVideoForConversion},
{Widget: fileVideoForConversionMessage},
{Text: "Папка куда будет сохраняться:", Widget: buttonForSelectedDir},
{Widget: buttonForSelectedDirMessage},
{Widget: checkboxOverwriteOutputFiles},
}
form.SubmitText = "Конвертировать"
enableFormConversionStruct := enableFormConversionStruct{
fileVideoForConversion: fileVideoForConversion,
buttonForSelectedDir: buttonForSelectedDir,
form: form,
}
form.OnSubmit = func() {
if len(*pathToSaveDirectory) == 0 {
showConversionMessage(conversionMessage, errors.New("Не выбрали папку для сохранения!"))
enableFormConversion(enableFormConversionStruct)
return
}
conversionMessage.Text = ""
fileVideoForConversion.Disable()
buttonForSelectedDir.Disable()
form.Disable()
setting := HandleConvertSetting{
VideoFileInput: fileInput,
DirectoryForSave: *pathToSaveDirectory,
OverwriteOutputFiles: isOverwriteOutputFiles,
}
err := runConvert(setting, progress)
if err != nil {
showConversionMessage(conversionMessage, err)
enableFormConversion(enableFormConversionStruct)
return
}
enableFormConversion(enableFormConversionStruct)
}
v.w.SetContent(widget.NewCard("Конвертор видео файлов в mp4", "", container.NewVBox(form, conversionMessage, progress)))
form.Disable()
}
func (v View) getButtonFileVideoForConversion(form *widget.Form, progress *widget.ProgressBar, conversionMessage *canvas.Text) (*widget.Button, *canvas.Text, *File) {
fileInput := &File{}
fileVideoForConversionMessage := canvas.NewText("", color.RGBA{255, 0, 0, 255})
fileVideoForConversionMessage.TextSize = 16
fileVideoForConversionMessage.TextStyle = fyne.TextStyle{Bold: true}
button := widget.NewButton("выбрать", func() {
fileDialog := dialog.NewFileOpen(
func(r fyne.URIReadCloser, err error) {
if err != nil {
fileVideoForConversionMessage.Text = err.Error()
setStringErrorStyle(fileVideoForConversionMessage)
return
}
if r == nil {
return
}
fileInput.Path = r.URI().Path()
fileInput.Name = r.URI().Name()
fileInput.Ext = r.URI().Extension()
fileVideoForConversionMessage.Text = r.URI().Path()
setStringSuccessStyle(fileVideoForConversionMessage)
form.Enable()
progress.Value = 0
progress.Refresh()
conversionMessage.Text = ""
}, v.w)
fileDialog.Show()
})
return button, fileVideoForConversionMessage, fileInput
}
func (v View) getButtonForSelectingDirectoryForSaving() (button *widget.Button, buttonMessage *canvas.Text, dirPath *string) {
buttonMessage = canvas.NewText("", color.RGBA{255, 0, 0, 255})
buttonMessage.TextSize = 16
buttonMessage.TextStyle = fyne.TextStyle{Bold: true}
path := ""
dirPath = &path
button = widget.NewButton("выбрать", func() {
fileDialog := dialog.NewFolderOpen(
func(r fyne.ListableURI, err error) {
if err != nil {
buttonMessage.Text = err.Error()
setStringErrorStyle(buttonMessage)
return
}
if r == nil {
return
}
path = r.Path()
buttonMessage.Text = r.Path()
setStringSuccessStyle(buttonMessage)
}, v.w)
fileDialog.Show()
})
return
}
func setStringErrorStyle(text *canvas.Text) {
text.Color = color.RGBA{255, 0, 0, 255}
text.Refresh()
}
func setStringSuccessStyle(text *canvas.Text) {
text.Color = color.RGBA{49, 127, 114, 255}
text.Refresh()
}
func showConversionMessage(conversionMessage *canvas.Text, err error) {
conversionMessage.Text = err.Error()
setStringErrorStyle(conversionMessage)
}
func enableFormConversion(enableFormConversionStruct enableFormConversionStruct) {
enableFormConversionStruct.fileVideoForConversion.Enable()
enableFormConversionStruct.buttonForSelectedDir.Enable()
enableFormConversionStruct.form.Enable()
}

View File

@ -1,26 +0,0 @@
package error
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget"
)
type ViewContract interface {
PanicError(err error)
}
type View struct {
w fyne.Window
}
func NewView(w fyne.Window) *View {
return &View{w}
}
func (v View) PanicError(err error) {
v.w.SetContent(container.NewVBox(
widget.NewLabel("Произошла ошибка!"),
widget.NewLabel("Ошибка: "+err.Error()),
))
}

View File

@ -1,205 +0,0 @@
package handler
import (
"bufio"
"errors"
"ffmpegGui/convertor"
"ffmpegGui/helper"
"ffmpegGui/setting"
"fyne.io/fyne/v2/widget"
"io"
"regexp"
"runtime"
"strconv"
"strings"
)
type ConvertorHandler struct {
convertorService convertor.ServiceContract
convertorView convertor.ViewContract
settingView setting.ViewContract
settingRepository setting.RepositoryContract
}
func NewConvertorHandler(
convertorService convertor.ServiceContract,
convertorView convertor.ViewContract,
settingView setting.ViewContract,
settingRepository setting.RepositoryContract,
) *ConvertorHandler {
return &ConvertorHandler{
convertorService,
convertorView,
settingView,
settingRepository,
}
}
func (h ConvertorHandler) GetConvertor() {
if h.checkingFFPathUtilities() == true {
h.convertorView.Main(h.runConvert)
return
}
h.settingView.SelectFFPath(h.saveSettingFFPath)
}
func (h ConvertorHandler) runConvert(setting convertor.HandleConvertSetting, progressbar *widget.ProgressBar) error {
totalDuration, err := h.convertorService.GetTotalDuration(setting.VideoFileInput)
if err != nil {
return err
}
progress := NewProgress(totalDuration, progressbar)
return h.convertorService.RunConvert(
convertor.ConvertSetting{
VideoFileInput: setting.VideoFileInput,
VideoFileOut: &convertor.File{
Path: setting.DirectoryForSave + helper.PathSeparator() + setting.VideoFileInput.Name + ".mp4",
Name: setting.VideoFileInput.Name,
Ext: ".mp4",
},
OverwriteOutputFiles: setting.OverwriteOutputFiles,
},
progress,
)
}
func (h ConvertorHandler) checkingFFPathUtilities() bool {
if h.checkingFFPath() == true {
return true
}
var pathsToFF []convertor.FFPathUtilities
if runtime.GOOS == "windows" {
pathsToFF = []convertor.FFPathUtilities{{"ffmpeg\\bin\\ffmpeg.exe", "ffmpeg\\bin\\ffprobe.exe"}}
} else {
pathsToFF = []convertor.FFPathUtilities{{"ffmpeg/bin/ffmpeg", "ffmpeg/bin/ffprobe"}, {"ffmpeg", "ffprobe"}}
}
for _, item := range pathsToFF {
ffmpegChecking, _ := h.convertorService.ChangeFFmpegPath(item.FFmpeg)
if ffmpegChecking == false {
continue
}
ffprobeChecking, _ := h.convertorService.ChangeFFprobePath(item.FFprobe)
if ffprobeChecking == false {
continue
}
ffmpegEntity := setting.Setting{Code: "ffmpeg", Value: item.FFmpeg}
_, _ = h.settingRepository.Create(ffmpegEntity)
ffprobeEntity := setting.Setting{Code: "ffprobe", Value: item.FFprobe}
_, _ = h.settingRepository.Create(ffprobeEntity)
return true
}
return false
}
func (h ConvertorHandler) saveSettingFFPath(ffmpegPath string, ffprobePath string) error {
ffmpegChecking, _ := h.convertorService.ChangeFFmpegPath(ffmpegPath)
if ffmpegChecking == false {
return errors.New("это не FFmpeg")
}
ffprobeChecking, _ := h.convertorService.ChangeFFprobePath(ffprobePath)
if ffprobeChecking == false {
return errors.New("это не FFprobe")
}
ffmpegEntity := setting.Setting{Code: "ffmpeg", Value: ffmpegPath}
_, _ = h.settingRepository.Create(ffmpegEntity)
ffprobeEntity := setting.Setting{Code: "ffprobe", Value: ffprobePath}
_, _ = h.settingRepository.Create(ffprobeEntity)
h.GetConvertor()
return nil
}
func (h ConvertorHandler) checkingFFPath() bool {
_, err := h.convertorService.GetFFmpegVesrion()
if err != nil {
return false
}
_, err = h.convertorService.GetFFprobeVersion()
if err != nil {
return false
}
return true
}
type progress struct {
totalDuration float64
progressbar *widget.ProgressBar
protocol string
}
func NewProgress(totalDuration float64, progressbar *widget.ProgressBar) progress {
return progress{
totalDuration: totalDuration,
progressbar: progressbar,
protocol: "pipe:",
}
}
func (p progress) GetProtocole() string {
return p.protocol
}
func (p progress) Run(stdOut io.ReadCloser, stdErr io.ReadCloser) error {
isProcessCompleted := false
var errorText string
p.progressbar.Value = 0
p.progressbar.Max = p.totalDuration
p.progressbar.Refresh()
progress := 0.0
go func() {
scannerErr := bufio.NewScanner(stdErr)
for scannerErr.Scan() {
errorText = scannerErr.Text()
}
if err := scannerErr.Err(); err != nil {
errorText = err.Error()
}
}()
scannerOut := bufio.NewScanner(stdOut)
for scannerOut.Scan() {
if isProcessCompleted != true {
isProcessCompleted = true
}
data := scannerOut.Text()
re := regexp.MustCompile(`frame=(\d+)`)
a := re.FindAllStringSubmatch(data, -1)
if len(a) > 0 && len(a[len(a)-1]) > 0 {
c, err := strconv.Atoi(a[len(a)-1][len(a[len(a)-1])-1])
if err != nil {
continue
}
progress = float64(c)
}
if strings.Contains(data, "progress=end") {
p.progressbar.Value = p.totalDuration
p.progressbar.Refresh()
isProcessCompleted = true
}
if p.progressbar.Value != progress {
p.progressbar.Value = progress
p.progressbar.Refresh()
}
}
if isProcessCompleted == false {
if len(errorText) == 0 {
errorText = "не смогли отконвертировать видео"
}
return errors.New(errorText)
}
return nil
}

View File

@ -1,10 +0,0 @@
package helper
import "runtime"
func PathSeparator() string {
if runtime.GOOS == "windows" {
return "\\"
}
return "/"
}

View File

@ -1,97 +0,0 @@
package main
import (
"errors"
"ffmpegGui/convertor"
myError "ffmpegGui/error"
"ffmpegGui/handler"
"ffmpegGui/migration"
"ffmpegGui/setting"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
_ "github.com/mattn/go-sqlite3"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"os"
)
//const appVersion string = "0.1.1"
func main() {
a := app.New()
w := a.NewWindow("GUI FFMpeg!")
w.Resize(fyne.Size{Width: 800, Height: 600})
w.CenterOnScreen()
errorView := myError.NewView(w)
if canCreateFile("data/database") != true {
errorView.PanicError(errors.New("не смогли создать файл 'database' в папке 'data'"))
w.ShowAndRun()
return
}
db, err := gorm.Open(sqlite.Open("data/database"), &gorm.Config{})
if err != nil {
errorView.PanicError(err)
w.ShowAndRun()
return
}
defer appCloseWithDb(db)
err = migration.Run(db)
if err != nil {
errorView.PanicError(err)
w.ShowAndRun()
return
}
settingRepository := setting.NewRepository(db)
pathFFmpeg, err := settingRepository.GetValue("ffmpeg")
if err != nil && errors.Is(err, gorm.ErrRecordNotFound) == false {
errorView.PanicError(err)
w.ShowAndRun()
return
}
pathFFprobe, err := settingRepository.GetValue("ffprobe")
if err != nil && errors.Is(err, gorm.ErrRecordNotFound) == false {
errorView.PanicError(err)
w.ShowAndRun()
return
}
ffPathUtilities := convertor.FFPathUtilities{FFmpeg: pathFFmpeg, FFprobe: pathFFprobe}
convertorView := convertor.NewView(w)
settingView := setting.NewView(w)
convertorService := convertor.NewService(ffPathUtilities)
defer appCloseWithConvert(convertorService)
mainHandler := handler.NewConvertorHandler(convertorService, convertorView, settingView, settingRepository)
mainHandler.GetConvertor()
w.ShowAndRun()
}
func appCloseWithDb(db *gorm.DB) {
sqlDB, err := db.DB()
if err == nil {
_ = sqlDB.Close()
}
}
func appCloseWithConvert(convertorService convertor.ServiceContract) {
for _, cmd := range convertorService.GetRunningProcesses() {
_ = cmd.Process.Kill()
}
}
func canCreateFile(path string) bool {
file, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0666)
if err != nil {
return false
}
_ = file.Close()
return true
}

View File

@ -1,98 +0,0 @@
package setting
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/widget"
"image/color"
"net/url"
)
type ViewContract interface {
SelectFFPath(func(ffmpegPath string, ffprobePath string) error)
}
type View struct {
w fyne.Window
}
func NewView(w fyne.Window) *View {
return &View{w}
}
func (v View) SelectFFPath(save func(ffmpegPath string, ffprobePath string) error) {
errorMessage := canvas.NewText("", color.RGBA{255, 0, 0, 255})
errorMessage.TextSize = 16
errorMessage.TextStyle = fyne.TextStyle{Bold: true}
ffmpegPath, buttonFFmpeg, buttonFFmpegMessage := v.getButtonSelectFile()
ffprobePath, buttonFFprobe, buttonFFprobeMessage := v.getButtonSelectFile()
link := widget.NewHyperlink("https://ffmpeg.org/download.html", &url.URL{
Scheme: "https",
Host: "ffmpeg.org",
Path: "download.html",
})
form := &widget.Form{
Items: []*widget.FormItem{
{Text: "Скачать можно от сюда", Widget: link},
{Text: "Путь к ffmpeg:", Widget: buttonFFmpeg},
{Widget: buttonFFmpegMessage},
{Text: "Путь к ffprobe:", Widget: buttonFFprobe},
{Widget: buttonFFprobeMessage},
{Widget: errorMessage},
},
SubmitText: "Сохранить",
OnSubmit: func() {
err := save(string(*ffmpegPath), string(*ffprobePath))
if err != nil {
errorMessage.Text = err.Error()
}
},
}
v.w.SetContent(widget.NewCard("Укажите путь к FFmpeg и к FFprobe", "", container.NewVBox(form)))
}
func (v View) getButtonSelectFile() (filePath *string, button *widget.Button, buttonMessage *canvas.Text) {
path := ""
filePath = &path
buttonMessage = canvas.NewText("", color.RGBA{255, 0, 0, 255})
buttonMessage.TextSize = 16
buttonMessage.TextStyle = fyne.TextStyle{Bold: true}
button = widget.NewButton("выбрать", func() {
fileDialog := dialog.NewFileOpen(
func(r fyne.URIReadCloser, err error) {
if err != nil {
buttonMessage.Text = err.Error()
setStringErrorStyle(buttonMessage)
return
}
if r == nil {
return
}
path = r.URI().Path()
buttonMessage.Text = r.URI().Path()
setStringSuccessStyle(buttonMessage)
}, v.w)
fileDialog.Show()
})
return
}
func setStringErrorStyle(text *canvas.Text) {
text.Color = color.RGBA{255, 0, 0, 255}
text.Refresh()
}
func setStringSuccessStyle(text *canvas.Text) {
text.Color = color.RGBA{49, 127, 114, 255}
text.Refresh()
}