15 Commits

Author SHA1 Message Date
43d794373a Embed application icon as a resource and refactor icon usage
Replaced the external `icon.png` file with an embedded resource using Go's embed functionality. Updated all references to use the new resource, eliminating the need for the standalone icon file.
2025-06-01 19:58:28 +05:00
3241b88158 Refactor localization system and migrate to Fyne's built-in support
Replaced the `i18n` and `toml` dependencies with Fyne's built-in language system for localization management. Updated the `Localizer` implementation to handle translations using JSON files and embed functionality. Simplified language selection and persisted settings via Fyne's preferences API.
2025-06-01 15:20:33 +05:00
d69767f5e9 Remove bbolt database dependency
Replaced bbolt-based database handling with Fyne built-in preferences for storing application settings. Deleted migration logic, database initialization, and error handling related to bbolt, simplifying the codebase and reducing external dependencies.
2025-05-30 00:34:33 +05:00
7340f43d6e Remove unused helper function from ffplay.go
The `PrepareBackgroundCommand` function has been removed because it prevents the program window from being displayed on Windows.
2025-05-25 22:51:59 +05:00
5ab11922b9 Select "Added Files" tab after dragging and dropping files.
Ensure the "Added Files" tab is automatically selected after users drag and drop files for conversion. This improves user experience by guiding them to the newly added content.
2025-05-25 21:46:19 +05:00
5f72ce8c56 Update screenshot for GUI in FFmpeg documentation
Replaced the GUI screenshot to reflect the latest interface changes. This ensures alignment with the current functionality and improves user clarity.
2025-05-25 01:48:33 +05:00
5b15848048 Update version to 0.9.0
Bump the application version from 0.8.0 to 0.9.0 in both `main.go` and `FyneApp.toml`. This reflects the latest changes and prepares the app for the next release.
2025-05-25 01:29:14 +05:00
84b36dd29e Make it possible to drag and drop multiple files
It is now possible to add multiple files before sending them to the processing queue.
2025-05-25 01:25:40 +05:00
82167f042f Add theme management functionality to the application
Implemented a theme management system allowing users to select and persist themes (default, light, dark) in the settings menu.
2025-05-23 20:18:05 +05:00
712ec2f182 Remove language selection to a new settings section. 2025-05-22 21:42:45 +05:00
883bf376b0 Add FFplay help feature and keyboard shortcut guide
Introduced a new "Help FFplay" section in the help menu to provide information about FFplay player keyboard shortcuts and actions.
2025-05-21 00:22:42 +05:00
306383449a Add FFplay support to the application
Integrated FFplay functionality across the application. This includes support for setting up the FFplay path and invoking FFplay for media playback.
2025-05-19 22:49:09 +05:00
a831d56d93 Add localized error handling for database timeout
Introduced a new localized error message "errorDatabaseTimeout" in multiple languages (English, Kazakh, Russian) and updated the `PanicError` method to handle database timeout errors more gracefully. This improves user feedback by providing context-specific error messages.
2025-05-18 19:32:57 +05:00
9d46db43c2 Default language
I made it so that if the OS language matches the language into which there is a translation, it would be used by default. And if not, then I would suggest choosing which language to use.
2025-05-18 19:31:59 +05:00
46d210d6d5 Added return after kernel.PanicErrorLang(err, appMetadata) to avoid unpredictable results during an error. 2025-05-18 19:28:16 +05:00
57 changed files with 2183 additions and 1826 deletions

View File

@@ -2,8 +2,5 @@
Icon = "icon.png" Icon = "icon.png"
Name = "GUI for FFmpeg" Name = "GUI for FFmpeg"
ID = "net.kor-elf.projects.gui-for-ffmpeg" ID = "net.kor-elf.projects.gui-for-ffmpeg"
Version = "0.8.0" Version = "0.9.0"
Build = 4 Build = 11
[Migrations]
fyneDo = true

View File

@@ -25,21 +25,7 @@
* fyne-cross linux --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). 7. Создаться папка **fyne-cross/bin** и там будет созданна папка с тем названием под которую Вы компилировали приложения (linux-amd64 или windows-amd64).
8. В папку **fyne-cross/bin/linux-amd64** или **fyne-cross/bin/windows-amd64** копируете: 8. В папку **fyne-cross/bin/linux-amd64** или **fyne-cross/bin/windows-amd64** копируете:
* icon.png
* data
* languages
* LICENSE * LICENSE
* LICENSE-3RD-PARTY.txt * LICENSE-3RD-PARTY.txt
<p><strong>Структура должна получиться такая:</strong></p> <p><strong>Структура должна получиться такая:</strong></p>
<img src="images/screenshot-folder-structure.png"> <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

View File

@@ -5,10 +5,12 @@ import (
) )
type RepositoryContract interface { type RepositoryContract interface {
GetPathFfmpeg() (string, error) GetPathFfmpeg() string
SavePathFfmpeg(code string) (setting.Setting, error) SavePathFfmpeg(code string) setting.Setting
GetPathFfprobe() (string, error) GetPathFfprobe() string
SavePathFfprobe(code string) (setting.Setting, error) SavePathFfprobe(code string) setting.Setting
GetPathFfplay() string
SavePathFfplay(code string) setting.Setting
} }
type Repository struct { type Repository struct {
@@ -19,18 +21,26 @@ func NewRepository(settingRepository setting.RepositoryContract) *Repository {
return &Repository{settingRepository: settingRepository} return &Repository{settingRepository: settingRepository}
} }
func (r Repository) GetPathFfmpeg() (string, error) { func (r Repository) GetPathFfmpeg() string {
return r.settingRepository.GetValue("ffmpeg") return r.settingRepository.GetValue("ffmpeg")
} }
func (r Repository) SavePathFfmpeg(path string) (setting.Setting, error) { func (r Repository) SavePathFfmpeg(path string) setting.Setting {
return r.settingRepository.CreateOrUpdate("ffmpeg", path) return r.settingRepository.CreateOrUpdate("ffmpeg", path)
} }
func (r Repository) GetPathFfprobe() (string, error) { func (r Repository) GetPathFfprobe() string {
return r.settingRepository.GetValue("ffprobe") return r.settingRepository.GetValue("ffprobe")
} }
func (r Repository) SavePathFfprobe(path string) (setting.Setting, error) { func (r Repository) SavePathFfprobe(path string) setting.Setting {
return r.settingRepository.CreateOrUpdate("ffprobe", path) return r.settingRepository.CreateOrUpdate("ffprobe", path)
} }
func (r Repository) GetPathFfplay() string {
return r.settingRepository.GetValue("ffplay")
}
func (r Repository) SavePathFfplay(path string) setting.Setting {
return r.settingRepository.CreateOrUpdate("ffplay", path)
}

View File

@@ -7,7 +7,6 @@ import (
"fyne.io/fyne/v2/widget" "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/convertor/view"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel" "git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel"
"github.com/nicksnyder/go-i18n/v2/i18n"
"image/color" "image/color"
) )
@@ -18,7 +17,8 @@ type ViewContract interface {
SelectFFPath( SelectFFPath(
ffmpegPath string, ffmpegPath string,
ffprobePath string, ffprobePath string,
save func(ffmpegPath string, ffprobePath string) error, ffplayPath string,
save func(ffmpegPath string, ffprobePath string, ffplayPath string) error,
cancel func(), cancel func(),
donwloadFFmpeg func(progressBar *widget.ProgressBar, progressMessage *canvas.Text) error, donwloadFFmpeg func(progressBar *widget.ProgressBar, progressMessage *canvas.Text) error,
) )
@@ -44,9 +44,7 @@ func NewView(app kernel.AppContract) *View {
} }
func (v View) Main(formConversion view.ConversionContract) { func (v View) Main(formConversion view.ConversionContract) {
converterVideoFilesTitle := v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ converterVideoFilesTitle := v.app.GetLocalizerService().GetMessage("converterVideoFilesTitle")
MessageID: "converterVideoFilesTitle",
})
v.app.GetWindow().SetContent(widget.NewCard(converterVideoFilesTitle, "", container.NewVScroll(formConversion.GetContent()))) v.app.GetWindow().SetContent(widget.NewCard(converterVideoFilesTitle, "", container.NewVScroll(formConversion.GetContent())))
formConversion.AfterViewContent() formConversion.AfterViewContent()
} }

View File

@@ -12,7 +12,6 @@ import (
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel" "git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel/encoder" "git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel/encoder"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/setting" "git.kor-elf.net/kor-elf/gui-for-ffmpeg/setting"
"github.com/nicksnyder/go-i18n/v2/i18n"
"image/color" "image/color"
"os" "os"
"path/filepath" "path/filepath"
@@ -32,36 +31,36 @@ type Conversion struct {
overwriteOutputFiles *overwriteOutputFiles overwriteOutputFiles *overwriteOutputFiles
selectEncoder *selectEncoder selectEncoder *selectEncoder
runConvert func(setting HandleConvertSetting) runConvert func(setting HandleConvertSetting)
itemsToConvertService kernel.ItemsToConvertContract
} }
type HandleConvertSetting struct { type HandleConvertSetting struct {
FileInput kernel.File
DirectoryForSave string DirectoryForSave string
OverwriteOutputFiles bool OverwriteOutputFiles bool
Format string Format string
Encoder encoder2.EncoderContract Encoder encoder2.EncoderContract
} }
func NewConversion(app kernel.AppContract, formats encoder.ConvertorFormatsContract, runConvert func(setting HandleConvertSetting), settingDirectoryForSaving setting.DirectoryForSavingContract) *Conversion { func NewConversion(app kernel.AppContract, formats encoder.ConvertorFormatsContract, runConvert func(setting HandleConvertSetting), settingDirectoryForSaving setting.DirectoryForSavingContract, itemsToConvertService kernel.ItemsToConvertContract) *Conversion {
conversionMessage := canvas.NewText("", color.RGBA{R: 255, G: 0, B: 0, A: 255}) conversionMessage := canvas.NewText("", color.RGBA{R: 255, G: 0, B: 0, A: 255})
conversionMessage.TextSize = 16 conversionMessage.TextSize = 16
conversionMessage.TextStyle = fyne.TextStyle{Bold: true} conversionMessage.TextStyle = fyne.TextStyle{Bold: true}
fileForConversion := newFileForConversion(app) fileForConversion := newFileForConversion(app, itemsToConvertService)
directoryForSaving := newDirectoryForSaving(app, settingDirectoryForSaving) directoryForSaving := newDirectoryForSaving(app, settingDirectoryForSaving)
overwriteOutputFiles := newOverwriteOutputFiles(app) overwriteOutputFiles := newOverwriteOutputFiles(app)
selectEncoder := newSelectEncoder(app, formats) selectEncoder := newSelectEncoder(app, formats)
items := []*widget.FormItem{ items := []*widget.FormItem{
{ {
Text: app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "fileForConversionTitle"}), Text: app.GetLocalizerService().GetMessage("fileForConversionTitle"),
Widget: fileForConversion.button, Widget: fileForConversion.button,
}, },
{ {
Widget: container.NewHScroll(fileForConversion.message), Widget: container.NewHScroll(fileForConversion.message),
}, },
{ {
Text: app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "buttonForSelectedDirTitle"}), Text: app.GetLocalizerService().GetMessage("buttonForSelectedDirTitle"),
Widget: directoryForSaving.button, Widget: directoryForSaving.button,
}, },
{ {
@@ -74,11 +73,11 @@ func NewConversion(app kernel.AppContract, formats encoder.ConvertorFormatsContr
Widget: selectEncoder.SelectFileType, Widget: selectEncoder.SelectFileType,
}, },
{ {
Text: app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "selectFormat"}), Text: app.GetLocalizerService().GetMessage("selectFormat"),
Widget: selectEncoder.SelectFormat, Widget: selectEncoder.SelectFormat,
}, },
{ {
Text: app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "selectEncoder"}), Text: app.GetLocalizerService().GetMessage("selectEncoder"),
Widget: selectEncoder.SelectEncoder, Widget: selectEncoder.SelectEncoder,
}, },
} }
@@ -93,6 +92,7 @@ func NewConversion(app kernel.AppContract, formats encoder.ConvertorFormatsContr
overwriteOutputFiles: overwriteOutputFiles, overwriteOutputFiles: overwriteOutputFiles,
selectEncoder: selectEncoder, selectEncoder: selectEncoder,
runConvert: runConvert, runConvert: runConvert,
itemsToConvertService: itemsToConvertService,
} }
} }
@@ -121,37 +121,41 @@ func (c Conversion) changeEncoder(encoder encoder2.EncoderContract) {
} }
func (c Conversion) AfterViewContent() { func (c Conversion) AfterViewContent() {
if len(c.itemsToConvertService.GetItems()) == 0 {
c.form.form.Disable() c.form.form.Disable()
} }
}
func (c Conversion) selectFileForConversion(err error) { func (c Conversion) selectFileForConversion(err error) {
c.conversionMessage.Text = "" c.conversionMessage.Text = ""
if len(c.itemsToConvertService.GetItems()) == 0 {
if err != nil { if err != nil {
c.form.form.Disable() c.form.form.Disable()
return return
} }
}
c.form.form.Enable() c.form.form.Enable()
} }
func (c Conversion) submit() { func (c Conversion) submit() {
if len(c.itemsToConvertService.GetItems()) == 0 {
showConversionMessage(c.conversionMessage, errors.New(c.app.GetLocalizerService().GetMessage("errorNoFilesAddedForConversion")))
c.enableFormConversion()
return
}
if len(c.directoryForSaving.path) == 0 { if len(c.directoryForSaving.path) == 0 {
showConversionMessage(c.conversionMessage, errors.New(c.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ showConversionMessage(c.conversionMessage, errors.New(c.app.GetLocalizerService().GetMessage("errorSelectedFolderSave")))
MessageID: "errorSelectedFolderSave",
})))
c.enableFormConversion() c.enableFormConversion()
return return
} }
if len(c.selectEncoder.SelectFormat.Selected) == 0 { if len(c.selectEncoder.SelectFormat.Selected) == 0 {
showConversionMessage(c.conversionMessage, errors.New(c.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ showConversionMessage(c.conversionMessage, errors.New(c.app.GetLocalizerService().GetMessage("errorSelectedFormat")))
MessageID: "errorSelectedFormat",
})))
return return
} }
if c.selectEncoder.Encoder == nil { if c.selectEncoder.Encoder == nil {
showConversionMessage(c.conversionMessage, errors.New(c.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ showConversionMessage(c.conversionMessage, errors.New(c.app.GetLocalizerService().GetMessage("errorSelectedEncoder")))
MessageID: "errorSelectedEncoder",
})))
return return
} }
c.conversionMessage.Text = "" c.conversionMessage.Text = ""
@@ -160,19 +164,18 @@ func (c Conversion) submit() {
c.directoryForSaving.button.Disable() c.directoryForSaving.button.Disable()
c.form.form.Disable() c.form.form.Disable()
setting := HandleConvertSetting{ c.runConvert(HandleConvertSetting{
FileInput: *c.fileForConversion.file,
DirectoryForSave: c.directoryForSaving.path, DirectoryForSave: c.directoryForSaving.path,
OverwriteOutputFiles: c.overwriteOutputFiles.IsChecked(), OverwriteOutputFiles: c.overwriteOutputFiles.IsChecked(),
Format: c.selectEncoder.SelectFormat.Selected, Format: c.selectEncoder.SelectFormat.Selected,
Encoder: c.selectEncoder.Encoder, Encoder: c.selectEncoder.Encoder,
} })
c.runConvert(setting)
c.enableFormConversion() c.enableFormConversion()
c.fileForConversion.message.Text = "" if len(c.itemsToConvertService.GetItems()) == 0 {
c.form.form.Disable() c.form.form.Disable()
} }
}
func (c Conversion) enableFormConversion() { func (c Conversion) enableFormConversion() {
c.fileForConversion.button.Enable() c.fileForConversion.button.Enable()
@@ -188,44 +191,45 @@ type fileForConversion struct {
changeCallbacks map[int]func(err error) changeCallbacks map[int]func(err error)
} }
func newFileForConversion(app kernel.AppContract) *fileForConversion { func newFileForConversion(app kernel.AppContract, itemsToConvertService kernel.ItemsToConvertContract) *fileForConversion {
message := canvas.NewText("", color.RGBA{R: 255, G: 0, B: 0, A: 255})
fileForConversion := &fileForConversion{ fileForConversion := &fileForConversion{
file: &kernel.File{}, message: message,
changeCallbacks: map[int]func(err error){}, changeCallbacks: map[int]func(err error){},
} }
buttonTitle := app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ buttonTitle := app.GetLocalizerService().GetMessage("choose") + "\n" +
MessageID: "choose", app.GetLocalizerService().GetMessage("or") + "\n" +
}) + "\n\r\n\r" + app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ app.GetLocalizerService().GetMessage("dragAndDropFiles")
MessageID: "or",
}) + "\n\r\n\r" + app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "dragAndDrop1File",
})
fileForConversion.message = canvas.NewText("", color.RGBA{R: 255, G: 0, B: 0, A: 255})
fileForConversion.message.TextSize = 16
fileForConversion.message.TextStyle = fyne.TextStyle{Bold: true}
var locationURI fyne.ListableURI var locationURI fyne.ListableURI
fileForConversion.button = widget.NewButton(buttonTitle, func() { fileForConversion.button = widget.NewButton(buttonTitle, func() {
app.GetWindow().NewFileOpen(func(r fyne.URIReadCloser, err error) { app.GetWindow().NewFileOpen(func(r fyne.URIReadCloser, err error) {
fyne.Do(func() {
fileForConversion.message.Text = ""
fileForConversion.message.Refresh()
})
if err != nil { if err != nil {
fyne.Do(func() {
fileForConversion.message.Text = err.Error() fileForConversion.message.Text = err.Error()
setStringErrorStyle(fileForConversion.message) fileForConversion.message.Refresh()
})
fileForConversion.eventSelectFile(err) fileForConversion.eventSelectFile(err)
return return
} }
if r == nil { if r == nil {
return return
} }
app.GetWindow().GetLayout().GetRightTabs().SelectAddedFilesTab()
fileForConversion.file.Path = r.URI().Path() itemsToConvertService.Add(&kernel.File{
fileForConversion.file.Name = r.URI().Name() Path: r.URI().Path(),
fileForConversion.file.Ext = r.URI().Extension() Name: r.URI().Name(),
Ext: r.URI().Extension(),
fileForConversion.message.Text = r.URI().Path() })
setStringSuccessStyle(fileForConversion.message)
fileForConversion.eventSelectFile(nil) fileForConversion.eventSelectFile(nil)
@@ -239,43 +243,40 @@ func newFileForConversion(app kernel.AppContract) *fileForConversion {
return return
} }
if len(uris) > 1 { isError := false
fileForConversion.message.Text = app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ for _, uri := range uris {
MessageID: "errorDragAndDrop1File",
})
setStringErrorStyle(fileForConversion.message)
fileForConversion.eventSelectFile(errors.New(fileForConversion.message.Text))
return
}
uri := uris[0]
info, err := os.Stat(uri.Path()) info, err := os.Stat(uri.Path())
if err != nil { if err != nil {
fileForConversion.message.Text = err.Error() isError = true
setStringErrorStyle(fileForConversion.message) continue
fileForConversion.eventSelectFile(err)
return
} }
if info.IsDir() { if info.IsDir() {
fileForConversion.message.Text = app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ isError = true
MessageID: "errorIsFolder", continue
})
setStringErrorStyle(fileForConversion.message)
fileForConversion.eventSelectFile(errors.New(fileForConversion.message.Text))
return
} }
fileForConversion.file.Path = uri.Path() itemsToConvertService.Add(&kernel.File{
fileForConversion.file.Name = uri.Name() Path: uri.Path(),
fileForConversion.file.Ext = uri.Extension() Name: uri.Name(),
Ext: uri.Extension(),
fileForConversion.message.Text = uri.Path() })
setStringSuccessStyle(fileForConversion.message)
fileForConversion.eventSelectFile(nil) fileForConversion.eventSelectFile(nil)
listableURI := storage.NewFileURI(filepath.Dir(uri.Path())) listableURI := storage.NewFileURI(filepath.Dir(uri.Path()))
locationURI, _ = storage.ListerForURI(listableURI) locationURI, _ = storage.ListerForURI(listableURI)
}
app.GetWindow().GetLayout().GetRightTabs().SelectAddedFilesTab()
if isError {
fileForConversion.message.Text = app.GetLocalizerService().GetMessage("errorDragAndDropFile")
setStringErrorStyle(fileForConversion.message)
fileForConversion.eventSelectFile(errors.New(fileForConversion.message.Text))
} else {
fyne.Do(func() {
fileForConversion.message.Text = ""
fileForConversion.message.Refresh()
})
}
}) })
return fileForConversion return fileForConversion
@@ -306,9 +307,7 @@ func newDirectoryForSaving(app kernel.AppContract, settingDirectoryForSaving set
directoryForSaving.message.TextSize = 16 directoryForSaving.message.TextSize = 16
directoryForSaving.message.TextStyle = fyne.TextStyle{Bold: true} directoryForSaving.message.TextStyle = fyne.TextStyle{Bold: true}
buttonTitle := app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ buttonTitle := app.GetLocalizerService().GetMessage("choose")
MessageID: "choose",
})
var locationURI fyne.ListableURI var locationURI fyne.ListableURI
@@ -337,7 +336,7 @@ func newDirectoryForSaving(app kernel.AppContract, settingDirectoryForSaving set
locationURI, err = storage.ListerForURI(r) locationURI, err = storage.ListerForURI(r)
if err == nil { if err == nil {
_, _ = settingDirectoryForSaving.SaveDirectoryForSaving(locationURI.Path()) _ = settingDirectoryForSaving.SaveDirectoryForSaving(locationURI.Path())
} }
}, locationURI) }, locationURI)
@@ -347,10 +346,7 @@ func newDirectoryForSaving(app kernel.AppContract, settingDirectoryForSaving set
} }
func getDirectoryForSaving(settingDirectoryForSaving setting.DirectoryForSavingContract) (fyne.ListableURI, error) { func getDirectoryForSaving(settingDirectoryForSaving setting.DirectoryForSavingContract) (fyne.ListableURI, error) {
path, err := settingDirectoryForSaving.GetDirectoryForSaving() path := settingDirectoryForSaving.GetDirectoryForSaving()
if err != nil {
return nil, err
}
if len(path) > 0 { if len(path) > 0 {
path = "file://" + path path = "file://" + path
@@ -373,9 +369,7 @@ func newOverwriteOutputFiles(app kernel.AppContract) *overwriteOutputFiles {
overwriteOutputFiles := &overwriteOutputFiles{ overwriteOutputFiles := &overwriteOutputFiles{
isChecked: false, isChecked: false,
} }
checkboxOverwriteOutputFilesTitle := app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ checkboxOverwriteOutputFilesTitle := app.GetLocalizerService().GetMessage("checkboxOverwriteOutputFilesTitle")
MessageID: "checkboxOverwriteOutputFilesTitle",
})
overwriteOutputFiles.checkbox = widget.NewCheck(checkboxOverwriteOutputFilesTitle, func(b bool) { overwriteOutputFiles.checkbox = widget.NewCheck(checkboxOverwriteOutputFilesTitle, func(b bool) {
overwriteOutputFiles.isChecked = b overwriteOutputFiles.isChecked = b
}) })
@@ -424,7 +418,7 @@ func newSelectEncoder(app kernel.AppContract, formats encoder.ConvertorFormatsCo
encoders = map[int]encoder2.EncoderDataContract{} encoders = map[int]encoder2.EncoderDataContract{}
for _, e := range format.GetEncoders() { for _, e := range format.GetEncoders() {
encoders[len(encoders)] = e encoders[len(encoders)] = e
encoderOptions = append(encoderOptions, app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "encoder_" + e.GetTitle()})) encoderOptions = append(encoderOptions, app.GetLocalizerService().GetMessage("encoder_"+e.GetTitle()))
} }
selectEncoder.SelectEncoder.SetOptions(encoderOptions) selectEncoder.SelectEncoder.SetOptions(encoderOptions)
selectEncoder.SelectEncoder.SetSelectedIndex(0) selectEncoder.SelectEncoder.SetSelectedIndex(0)
@@ -435,9 +429,9 @@ func newSelectEncoder(app kernel.AppContract, formats encoder.ConvertorFormatsCo
fileTypeOptions = append(fileTypeOptions, fileType.Name()) fileTypeOptions = append(fileTypeOptions, fileType.Name())
} }
encoderGroupVideo := app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "encoderGroupVideo"}) encoderGroupVideo := app.GetLocalizerService().GetMessage("encoderGroupVideo")
encoderGroupAudio := app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "encoderGroupAudio"}) encoderGroupAudio := app.GetLocalizerService().GetMessage("encoderGroupAudio")
encoderGroupImage := app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "encoderGroupImage"}) encoderGroupImage := app.GetLocalizerService().GetMessage("encoderGroupImage")
encoderGroup := map[string]string{ encoderGroup := map[string]string{
encoderGroupVideo: "video", encoderGroupVideo: "video",
encoderGroupAudio: "audio", encoderGroupAudio: "audio",
@@ -504,9 +498,7 @@ type form struct {
func newForm(app kernel.AppContract, items []*widget.FormItem) *form { func newForm(app kernel.AppContract, items []*widget.FormItem) *form {
f := widget.NewForm() f := widget.NewForm()
f.SubmitText = app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ f.SubmitText = app.GetLocalizerService().GetMessage("converterVideoFilesSubmitTitle")
MessageID: "converterVideoFilesSubmitTitle",
})
f.Items = items f.Items = items
return &form{ return &form{

View File

@@ -6,7 +6,6 @@ import (
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" "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/encoder/h264_nvenc"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel" "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 { func View(encoder encoder.EncoderContract, app kernel.AppContract) []*widget.FormItem {
@@ -45,7 +44,7 @@ func presetParameter(encoder encoder.EncoderContract, app kernel.AppContract) []
elementSelect.SetSelected(presetDefault) elementSelect.SetSelected(presetDefault)
elementSelect.Hide() elementSelect.Hide()
checkboxTitle := app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "parameterCheckbox"}) checkboxTitle := app.GetLocalizerService().GetMessage("parameterCheckbox")
elementCheckbox := widget.NewCheck(checkboxTitle, func(b bool) { elementCheckbox := widget.NewCheck(checkboxTitle, func(b bool) {
if b == true { if b == true {
parameter.SetEnable() parameter.SetEnable()
@@ -58,7 +57,7 @@ func presetParameter(encoder encoder.EncoderContract, app kernel.AppContract) []
return []*widget.FormItem{ return []*widget.FormItem{
{ {
Text: app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "formPreset"}), Text: app.GetLocalizerService().GetMessage("formPreset"),
Widget: container.NewVBox(elementCheckbox, elementSelect), Widget: container.NewVBox(elementCheckbox, elementSelect),
}, },
} }

View File

@@ -6,7 +6,6 @@ import (
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" "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/encoder/libx264"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel" "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 { func View(encoder encoder.EncoderContract, app kernel.AppContract) []*widget.FormItem {
@@ -28,7 +27,7 @@ func presetParameter(encoder encoder.EncoderContract, app kernel.AppContract) []
presetDefault := "" presetDefault := ""
for _, name := range libx264.Presets { for _, name := range libx264.Presets {
title := app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "preset_" + name}) title := app.GetLocalizerService().GetMessage("preset_" + name)
presetsForSelect = append(presetsForSelect, title) presetsForSelect = append(presetsForSelect, title)
presets[title] = name presets[title] = name
if name == parameter.Get() { if name == parameter.Get() {
@@ -45,7 +44,7 @@ func presetParameter(encoder encoder.EncoderContract, app kernel.AppContract) []
elementSelect.SetSelected(presetDefault) elementSelect.SetSelected(presetDefault)
elementSelect.Hide() elementSelect.Hide()
checkboxTitle := app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "parameterCheckbox"}) checkboxTitle := app.GetLocalizerService().GetMessage("parameterCheckbox")
elementCheckbox := widget.NewCheck(checkboxTitle, func(b bool) { elementCheckbox := widget.NewCheck(checkboxTitle, func(b bool) {
if b == true { if b == true {
parameter.SetEnable() parameter.SetEnable()
@@ -58,7 +57,7 @@ func presetParameter(encoder encoder.EncoderContract, app kernel.AppContract) []
return []*widget.FormItem{ return []*widget.FormItem{
{ {
Text: app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "formPreset"}), Text: app.GetLocalizerService().GetMessage("formPreset"),
Widget: container.NewVBox(elementCheckbox, elementSelect), Widget: container.NewVBox(elementCheckbox, elementSelect),
}, },
} }

View File

@@ -6,7 +6,6 @@ import (
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/encoder" "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/encoder/libx265"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel" "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 { func View(encoder encoder.EncoderContract, app kernel.AppContract) []*widget.FormItem {
@@ -28,7 +27,7 @@ func presetParameter(encoder encoder.EncoderContract, app kernel.AppContract) []
presetDefault := "" presetDefault := ""
for _, name := range libx265.Presets { for _, name := range libx265.Presets {
title := app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "preset_" + name}) title := app.GetLocalizerService().GetMessage("preset_" + name)
presetsForSelect = append(presetsForSelect, title) presetsForSelect = append(presetsForSelect, title)
presets[title] = name presets[title] = name
if name == parameter.Get() { if name == parameter.Get() {
@@ -45,7 +44,7 @@ func presetParameter(encoder encoder.EncoderContract, app kernel.AppContract) []
elementSelect.SetSelected(presetDefault) elementSelect.SetSelected(presetDefault)
elementSelect.Hide() elementSelect.Hide()
checkboxTitle := app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "parameterCheckbox"}) checkboxTitle := app.GetLocalizerService().GetMessage("parameterCheckbox")
elementCheckbox := widget.NewCheck(checkboxTitle, func(b bool) { elementCheckbox := widget.NewCheck(checkboxTitle, func(b bool) {
if b == true { if b == true {
parameter.SetEnable() parameter.SetEnable()
@@ -58,7 +57,7 @@ func presetParameter(encoder encoder.EncoderContract, app kernel.AppContract) []
return []*widget.FormItem{ return []*widget.FormItem{
{ {
Text: app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{MessageID: "formPreset"}), Text: app.GetLocalizerService().GetMessage("formPreset"),
Widget: container.NewVBox(elementCheckbox, elementSelect), Widget: container.NewVBox(elementCheckbox, elementSelect),
}, },
} }

View File

@@ -6,7 +6,6 @@ import (
"fyne.io/fyne/v2/container" "fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/storage" "fyne.io/fyne/v2/storage"
"fyne.io/fyne/v2/widget" "fyne.io/fyne/v2/widget"
"github.com/nicksnyder/go-i18n/v2/i18n"
"image/color" "image/color"
"net/url" "net/url"
"path/filepath" "path/filepath"
@@ -15,7 +14,8 @@ import (
func (v View) SelectFFPath( func (v View) SelectFFPath(
currentPathFfmpeg string, currentPathFfmpeg string,
currentPathFfprobe string, currentPathFfprobe string,
save func(ffmpegPath string, ffprobePath string) error, currentPathFfplay string,
save func(ffmpegPath string, ffprobePath string, ffplayPath string) error,
cancel func(), cancel func(),
donwloadFFmpeg func(progressBar *widget.ProgressBar, progressMessage *canvas.Text) error, donwloadFFmpeg func(progressBar *widget.ProgressBar, progressMessage *canvas.Text) error,
) { ) {
@@ -25,6 +25,7 @@ func (v View) SelectFFPath(
ffmpegPath, buttonFFmpeg, buttonFFmpegMessage := v.getButtonSelectFile(currentPathFfmpeg) ffmpegPath, buttonFFmpeg, buttonFFmpegMessage := v.getButtonSelectFile(currentPathFfmpeg)
ffprobePath, buttonFFprobe, buttonFFprobeMessage := v.getButtonSelectFile(currentPathFfprobe) ffprobePath, buttonFFprobe, buttonFFprobeMessage := v.getButtonSelectFile(currentPathFfprobe)
ffplayPath, buttonFFplay, buttonFFplayMessage := v.getButtonSelectFile(currentPathFfplay)
link := widget.NewHyperlink("https://ffmpeg.org/download.html", &url.URL{ link := widget.NewHyperlink("https://ffmpeg.org/download.html", &url.URL{
Scheme: "https", Scheme: "https",
@@ -35,38 +36,37 @@ func (v View) SelectFFPath(
form := &widget.Form{ form := &widget.Form{
Items: []*widget.FormItem{ Items: []*widget.FormItem{
{ {
Text: v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ Text: v.app.GetLocalizerService().GetMessage("titleDownloadLink"),
MessageID: "titleDownloadLink",
}),
Widget: link, Widget: link,
}, },
{ {
Text: v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ Text: v.app.GetLocalizerService().GetMessage("pathToFfmpeg"),
MessageID: "pathToFfmpeg",
}),
Widget: buttonFFmpeg, Widget: buttonFFmpeg,
}, },
{ {
Widget: container.NewHScroll(buttonFFmpegMessage), Widget: container.NewHScroll(buttonFFmpegMessage),
}, },
{ {
Text: v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ Text: v.app.GetLocalizerService().GetMessage("pathToFfprobe"),
MessageID: "pathToFfprobe",
}),
Widget: buttonFFprobe, Widget: buttonFFprobe,
}, },
{ {
Widget: container.NewHScroll(buttonFFprobeMessage), Widget: container.NewHScroll(buttonFFprobeMessage),
}, },
{
Text: v.app.GetLocalizerService().GetMessage("pathToFfplay"),
Widget: buttonFFplay,
},
{
Widget: container.NewHScroll(buttonFFplayMessage),
},
{ {
Widget: errorMessage, Widget: errorMessage,
}, },
}, },
SubmitText: v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ SubmitText: v.app.GetLocalizerService().GetMessage("save"),
MessageID: "save",
}),
OnSubmit: func() { OnSubmit: func() {
err := save(*ffmpegPath, *ffprobePath) err := save(*ffmpegPath, *ffprobePath, *ffplayPath)
if err != nil { if err != nil {
errorMessage.Text = err.Error() errorMessage.Text = err.Error()
} }
@@ -74,13 +74,9 @@ func (v View) SelectFFPath(
} }
if cancel != nil { if cancel != nil {
form.OnCancel = cancel form.OnCancel = cancel
form.CancelText = v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ form.CancelText = v.app.GetLocalizerService().GetMessage("cancel")
MessageID: "cancel",
})
} }
selectFFPathTitle := v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ selectFFPathTitle := v.app.GetLocalizerService().GetMessage("selectFFPathTitle")
MessageID: "selectFFPathTitle",
})
if v.downloadFFmpeg.blockDownloadFFmpegContainer == nil { if v.downloadFFmpeg.blockDownloadFFmpegContainer == nil {
v.downloadFFmpeg.blockDownloadFFmpegContainer = v.blockDownloadFFmpeg(donwloadFFmpeg) v.downloadFFmpeg.blockDownloadFFmpegContainer = v.blockDownloadFFmpeg(donwloadFFmpeg)
@@ -99,9 +95,7 @@ func (v View) getButtonSelectFile(path string) (filePath *string, button *widget
buttonMessage.TextSize = 16 buttonMessage.TextSize = 16
buttonMessage.TextStyle = fyne.TextStyle{Bold: true} buttonMessage.TextStyle = fyne.TextStyle{Bold: true}
buttonTitle := v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ buttonTitle := v.app.GetLocalizerService().GetMessage("choose")
MessageID: "choose",
})
var locationURI fyne.ListableURI var locationURI fyne.ListableURI
if len(path) > 0 { if len(path) > 0 {

View File

@@ -8,7 +8,6 @@ import (
"fyne.io/fyne/v2/canvas" "fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container" "fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget" "fyne.io/fyne/v2/widget"
"github.com/nicksnyder/go-i18n/v2/i18n"
"golang.org/x/image/colornames" "golang.org/x/image/colornames"
"image/color" "image/color"
) )
@@ -29,9 +28,7 @@ func (v View) blockDownloadFFmpeg(
var buttonDownloadFFmpeg *widget.Button var buttonDownloadFFmpeg *widget.Button
buttonDownloadFFmpeg = widget.NewButton(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ buttonDownloadFFmpeg = widget.NewButton(v.app.GetLocalizerService().GetMessage("download"), func() {
MessageID: "download",
}), func() {
fyne.Do(func() { fyne.Do(func() {
buttonDownloadFFmpeg.Disable() buttonDownloadFFmpeg.Disable()
}) })
@@ -47,15 +44,11 @@ func (v View) blockDownloadFFmpeg(
}) })
downloadFFmpegFromSiteMessage := v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ downloadFFmpegFromSiteMessage := v.app.GetLocalizerService().GetMessage("downloadFFmpegFromSite")
MessageID: "downloadFFmpegFromSite",
})
return container.NewVBox( return container.NewVBox(
canvas.NewLine(colornames.Darkgreen), canvas.NewLine(colornames.Darkgreen),
widget.NewCard(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ widget.NewCard(v.app.GetLocalizerService().GetMessage("buttonDownloadFFmpeg"), "", container.NewVBox(
MessageID: "buttonDownloadFFmpeg",
}), "", container.NewVBox(
widget.NewRichTextFromMarkdown( widget.NewRichTextFromMarkdown(
downloadFFmpegFromSiteMessage+" [https://github.com/BtbN/FFmpeg-Builds/releases](https://github.com/BtbN/FFmpeg-Builds/releases)", downloadFFmpegFromSiteMessage+" [https://github.com/BtbN/FFmpeg-Builds/releases](https://github.com/BtbN/FFmpeg-Builds/releases)",
), ),

View File

@@ -8,7 +8,6 @@ import (
"fyne.io/fyne/v2/canvas" "fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container" "fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget" "fyne.io/fyne/v2/widget"
"github.com/nicksnyder/go-i18n/v2/i18n"
"golang.org/x/image/colornames" "golang.org/x/image/colornames"
"image/color" "image/color"
) )
@@ -29,9 +28,7 @@ func (v View) blockDownloadFFmpeg(
var buttonDownloadFFmpeg *widget.Button var buttonDownloadFFmpeg *widget.Button
buttonDownloadFFmpeg = widget.NewButton(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ buttonDownloadFFmpeg = widget.NewButton(v.app.GetLocalizerService().GetMessage("download"), func() {
MessageID: "download",
}), func() {
go func() { go func() {
fyne.Do(func() { fyne.Do(func() {
@@ -47,15 +44,11 @@ func (v View) blockDownloadFFmpeg(
}() }()
}) })
downloadFFmpegFromSiteMessage := v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ downloadFFmpegFromSiteMessage := v.app.GetLocalizerService().GetMessage("downloadFFmpegFromSite")
MessageID: "downloadFFmpegFromSite",
})
return container.NewVBox( return container.NewVBox(
canvas.NewLine(colornames.Darkgreen), canvas.NewLine(colornames.Darkgreen),
widget.NewCard(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ widget.NewCard(v.app.GetLocalizerService().GetMessage("buttonDownloadFFmpeg"), "", container.NewVBox(
MessageID: "buttonDownloadFFmpeg",
}), "", container.NewVBox(
widget.NewRichTextFromMarkdown( widget.NewRichTextFromMarkdown(
downloadFFmpegFromSiteMessage+" [https://github.com/BtbN/FFmpeg-Builds/releases](https://github.com/BtbN/FFmpeg-Builds/releases)", downloadFFmpegFromSiteMessage+" [https://github.com/BtbN/FFmpeg-Builds/releases](https://github.com/BtbN/FFmpeg-Builds/releases)",
), ),

2
data/.gitignore vendored
View File

@@ -1,2 +0,0 @@
*
!.gitignore

View File

@@ -1,7 +0,0 @@
package db
import "errors"
var (
ErrRecordNotFound = errors.New("record not found")
)

View File

@@ -5,7 +5,6 @@ import (
"fyne.io/fyne/v2/widget" "fyne.io/fyne/v2/widget"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel" "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/localizer"
"github.com/nicksnyder/go-i18n/v2/i18n"
) )
type ViewContract interface { type ViewContract interface {
@@ -23,9 +22,7 @@ func NewView(app kernel.AppContract) *View {
} }
func (v View) PanicError(err error) { func (v View) PanicError(err error) {
messageHead := v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ messageHead := v.app.GetLocalizerService().GetMessage("error")
MessageID: "error",
})
v.app.GetWindow().SetContent(container.NewBorder( v.app.GetWindow().SetContent(container.NewBorder(
container.NewVBox( container.NewVBox(
@@ -40,25 +37,3 @@ func (v View) PanicError(err error) {
}), }),
)) ))
} }
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()
}),
))
}

7
go.mod
View File

@@ -6,16 +6,13 @@ toolchain go1.23.9
require ( require (
fyne.io/fyne/v2 v2.6.1 fyne.io/fyne/v2 v2.6.1
github.com/BurntSushi/toml v1.5.0
github.com/nicksnyder/go-i18n/v2 v2.6.0
github.com/ulikunitz/xz v0.5.12 github.com/ulikunitz/xz v0.5.12
go.etcd.io/bbolt v1.4.0
golang.org/x/image v0.27.0 golang.org/x/image v0.27.0
golang.org/x/text v0.25.0
) )
require ( require (
fyne.io/systray v1.11.0 // indirect fyne.io/systray v1.11.0 // indirect
github.com/BurntSushi/toml v1.5.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fredbi/uri v1.1.0 // indirect github.com/fredbi/uri v1.1.0 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect
@@ -34,6 +31,7 @@ require (
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 // indirect github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 // indirect
github.com/kr/text v0.2.0 // indirect github.com/kr/text v0.2.0 // indirect
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
github.com/nicksnyder/go-i18n/v2 v2.6.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rymdport/portal v0.4.1 // indirect github.com/rymdport/portal v0.4.1 // indirect
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c // indirect github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c // indirect
@@ -42,5 +40,6 @@ require (
github.com/yuin/goldmark v1.7.11 // indirect github.com/yuin/goldmark v1.7.11 // indirect
golang.org/x/net v0.40.0 // indirect golang.org/x/net v0.40.0 // indirect
golang.org/x/sys v0.33.0 // indirect golang.org/x/sys v0.33.0 // indirect
golang.org/x/text v0.25.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
) )

4
go.sum
View File

@@ -67,14 +67,10 @@ github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/yuin/goldmark v1.7.11 h1:ZCxLyDMtz0nT2HFfsYG8WZ47Trip2+JyLysKcMYE5bo= github.com/yuin/goldmark v1.7.11 h1:ZCxLyDMtz0nT2HFfsYG8WZ47Trip2+JyLysKcMYE5bo=
github.com/yuin/goldmark v1.7.11/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= github.com/yuin/goldmark v1.7.11/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk=
go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk=
golang.org/x/image v0.27.0 h1:C8gA4oWU/tKkdCfYT6T2u4faJu3MeNS5O8UPWlPF61w= golang.org/x/image v0.27.0 h1:C8gA4oWU/tKkdCfYT6T2u4faJu3MeNS5O8UPWlPF61w=
golang.org/x/image v0.27.0/go.mod h1:xbdrClrAUway1MUTEZDq9mz/UpRwYAkFFNUslZtcB+g= golang.org/x/image v0.27.0/go.mod h1:xbdrClrAUway1MUTEZDq9mz/UpRwYAkFFNUslZtcB+g=
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=

View File

@@ -8,7 +8,6 @@ import (
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/helper" "git.kor-elf.net/kor-elf/gui-for-ffmpeg/helper"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel" "git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/setting" "git.kor-elf.net/kor-elf/gui-for-ffmpeg/setting"
"github.com/nicksnyder/go-i18n/v2/i18n"
) )
type ConvertorHandlerContract interface { type ConvertorHandlerContract interface {
@@ -16,6 +15,7 @@ type ConvertorHandlerContract interface {
FfPathSelection() FfPathSelection()
GetFfmpegVersion() (string, error) GetFfmpegVersion() (string, error)
GetFfprobeVersion() (string, error) GetFfprobeVersion() (string, error)
GetFfplayVersion() (string, error)
} }
type ConvertorHandler struct { type ConvertorHandler struct {
@@ -24,6 +24,7 @@ type ConvertorHandler struct {
errorView error2.ViewContract errorView error2.ViewContract
convertorRepository convertor.RepositoryContract convertorRepository convertor.RepositoryContract
settingDirectoryForSaving setting.DirectoryForSavingContract settingDirectoryForSaving setting.DirectoryForSavingContract
itemsToConvertService kernel.ItemsToConvertContract
} }
func NewConvertorHandler( func NewConvertorHandler(
@@ -32,6 +33,7 @@ func NewConvertorHandler(
errorView error2.ViewContract, errorView error2.ViewContract,
convertorRepository convertor.RepositoryContract, convertorRepository convertor.RepositoryContract,
settingDirectoryForSaving setting.DirectoryForSavingContract, settingDirectoryForSaving setting.DirectoryForSavingContract,
itemsToConvertService kernel.ItemsToConvertContract,
) *ConvertorHandler { ) *ConvertorHandler {
return &ConvertorHandler{ return &ConvertorHandler{
app: app, app: app,
@@ -39,6 +41,7 @@ func NewConvertorHandler(
errorView: errorView, errorView: errorView,
convertorRepository: convertorRepository, convertorRepository: convertorRepository,
settingDirectoryForSaving: settingDirectoryForSaving, settingDirectoryForSaving: settingDirectoryForSaving,
itemsToConvertService: itemsToConvertService,
} }
} }
@@ -49,17 +52,18 @@ func (h ConvertorHandler) MainConvertor() {
h.errorView.PanicError(err) h.errorView.PanicError(err)
return return
} }
conversion := view.NewConversion(h.app, formats, h.runConvert, h.settingDirectoryForSaving) conversion := view.NewConversion(h.app, formats, h.runConvert, h.settingDirectoryForSaving, h.itemsToConvertService)
h.convertorView.Main(conversion) h.convertorView.Main(conversion)
return return
} }
h.convertorView.SelectFFPath("", "", h.saveSettingFFPath, nil, h.downloadFFmpeg) h.convertorView.SelectFFPath("", "", "", h.saveSettingFFPath, nil, h.downloadFFmpeg)
} }
func (h ConvertorHandler) FfPathSelection() { func (h ConvertorHandler) FfPathSelection() {
ffmpeg, _ := h.convertorRepository.GetPathFfmpeg() ffmpeg := h.convertorRepository.GetPathFfmpeg()
ffprobe, _ := h.convertorRepository.GetPathFfprobe() ffprobe := h.convertorRepository.GetPathFfprobe()
h.convertorView.SelectFFPath(ffmpeg, ffprobe, h.saveSettingFFPath, h.MainConvertor, h.downloadFFmpeg) ffplay := h.convertorRepository.GetPathFfplay()
h.convertorView.SelectFFPath(ffmpeg, ffprobe, ffplay, h.saveSettingFFPath, h.MainConvertor, h.downloadFFmpeg)
} }
func (h ConvertorHandler) GetFfmpegVersion() (string, error) { func (h ConvertorHandler) GetFfmpegVersion() (string, error) {
@@ -70,18 +74,32 @@ func (h ConvertorHandler) GetFfprobeVersion() (string, error) {
return h.app.GetConvertorService().GetFFprobeVersion() return h.app.GetConvertorService().GetFFprobeVersion()
} }
func (h ConvertorHandler) GetFfplayVersion() (string, error) {
return h.app.GetConvertorService().GetFFplayVersion()
}
func (h ConvertorHandler) runConvert(setting view.HandleConvertSetting) { func (h ConvertorHandler) runConvert(setting view.HandleConvertSetting) {
h.app.GetWindow().GetLayout().GetRightTabs().SelectFileQueueTab()
for _, item := range h.itemsToConvertService.GetItems() {
file := item.GetFile()
if file == nil {
continue
}
h.app.GetQueue().Add(&kernel.ConvertSetting{ h.app.GetQueue().Add(&kernel.ConvertSetting{
VideoFileInput: setting.FileInput, VideoFileInput: *file,
VideoFileOut: kernel.File{ VideoFileOut: kernel.File{
Path: setting.DirectoryForSave + helper.PathSeparator() + setting.FileInput.Name + "." + setting.Format, Path: setting.DirectoryForSave + helper.PathSeparator() + file.Name + "." + setting.Format,
Name: setting.FileInput.Name, Name: file.Name,
Ext: "." + setting.Format, Ext: "." + setting.Format,
}, },
OverwriteOutputFiles: setting.OverwriteOutputFiles, OverwriteOutputFiles: setting.OverwriteOutputFiles,
Encoder: setting.Encoder, Encoder: setting.Encoder,
}) })
} }
h.itemsToConvertService.AfterAddingQueue()
}
func (h ConvertorHandler) checkingFFPathUtilities() bool { func (h ConvertorHandler) checkingFFPathUtilities() bool {
if h.checkingFFPath() == true { if h.checkingFFPath() == true {
@@ -98,33 +116,42 @@ func (h ConvertorHandler) checkingFFPathUtilities() bool {
if ffprobeChecking == false { if ffprobeChecking == false {
continue continue
} }
_, _ = h.convertorRepository.SavePathFfmpeg(item.FFmpeg)
_, _ = h.convertorRepository.SavePathFfprobe(item.FFprobe) ffplayChecking, _ := h.app.GetConvertorService().ChangeFFplayPath(item.FFplay)
if ffplayChecking == false {
continue
}
_ = h.convertorRepository.SavePathFfmpeg(item.FFmpeg)
_ = h.convertorRepository.SavePathFfprobe(item.FFprobe)
_ = h.convertorRepository.SavePathFfplay(item.FFplay)
return true return true
} }
return false return false
} }
func (h ConvertorHandler) saveSettingFFPath(ffmpegPath string, ffprobePath string) error { func (h ConvertorHandler) saveSettingFFPath(ffmpegPath string, ffprobePath string, ffplayPath string) error {
ffmpegChecking, _ := h.app.GetConvertorService().ChangeFFmpegPath(ffmpegPath) ffmpegChecking, _ := h.app.GetConvertorService().ChangeFFmpegPath(ffmpegPath)
if ffmpegChecking == false { if ffmpegChecking == false {
errorText := h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ errorText := h.app.GetLocalizerService().GetMessage("errorFFmpeg")
MessageID: "errorFFmpeg",
})
return errors.New(errorText) return errors.New(errorText)
} }
ffprobeChecking, _ := h.app.GetConvertorService().ChangeFFprobePath(ffprobePath) ffprobeChecking, _ := h.app.GetConvertorService().ChangeFFprobePath(ffprobePath)
if ffprobeChecking == false { if ffprobeChecking == false {
errorText := h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ errorText := h.app.GetLocalizerService().GetMessage("errorFFprobe")
MessageID: "errorFFprobe",
})
return errors.New(errorText) return errors.New(errorText)
} }
_, _ = h.convertorRepository.SavePathFfmpeg(ffmpegPath) ffplayChecking, _ := h.app.GetConvertorService().ChangeFFplayPath(ffplayPath)
_, _ = h.convertorRepository.SavePathFfprobe(ffprobePath) if ffplayChecking == false {
errorText := h.app.GetLocalizerService().GetMessage("errorFFplay")
return errors.New(errorText)
}
_ = h.convertorRepository.SavePathFfmpeg(ffmpegPath)
_ = h.convertorRepository.SavePathFfprobe(ffprobePath)
_ = h.convertorRepository.SavePathFfplay(ffplayPath)
h.MainConvertor() h.MainConvertor()
@@ -142,5 +169,10 @@ func (h ConvertorHandler) checkingFFPath() bool {
return false return false
} }
_, err = h.app.GetConvertorService().GetFFplayVersion()
if err != nil {
return false
}
return true return true
} }

View File

@@ -10,7 +10,7 @@ import (
) )
func getPathsToFF() []kernel.FFPathUtilities { func getPathsToFF() []kernel.FFPathUtilities {
return []kernel.FFPathUtilities{{"ffmpeg/bin/ffmpeg", "ffmpeg/bin/ffprobe"}, {"ffmpeg", "ffprobe"}} return []kernel.FFPathUtilities{{FFmpeg: "ffmpeg/bin/ffmpeg", FFprobe: "ffmpeg/bin/ffprobe", FFplay: "ffmpeg/bin/ffplay"}, {FFmpeg: "ffmpeg", FFprobe: "ffprobe", FFplay: "ffplay"}}
} }
func (h ConvertorHandler) downloadFFmpeg(progressBar *widget.ProgressBar, progressMessage *canvas.Text) (err error) { func (h ConvertorHandler) downloadFFmpeg(progressBar *widget.ProgressBar, progressMessage *canvas.Text) (err error) {

View File

@@ -10,7 +10,6 @@ import (
"fyne.io/fyne/v2/canvas" "fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/widget" "fyne.io/fyne/v2/widget"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel" "git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel"
"github.com/nicksnyder/go-i18n/v2/i18n"
"github.com/ulikunitz/xz" "github.com/ulikunitz/xz"
"io" "io"
"net/http" "net/http"
@@ -19,7 +18,7 @@ import (
) )
func getPathsToFF() []kernel.FFPathUtilities { func getPathsToFF() []kernel.FFPathUtilities {
return []kernel.FFPathUtilities{{"ffmpeg/bin/ffmpeg", "ffmpeg/bin/ffprobe"}, {"ffmpeg", "ffprobe"}} return []kernel.FFPathUtilities{{FFmpeg: "ffmpeg/bin/ffmpeg", FFprobe: "ffmpeg/bin/ffprobe", FFplay: "ffmpeg/bin/ffplay"}, {FFmpeg: "ffmpeg", FFprobe: "ffprobe", FFplay: "ffplay"}}
} }
func (h ConvertorHandler) downloadFFmpeg(progressBar *widget.ProgressBar, progressMessage *canvas.Text) (err error) { func (h ConvertorHandler) downloadFFmpeg(progressBar *widget.ProgressBar, progressMessage *canvas.Text) (err error) {
@@ -30,9 +29,7 @@ func (h ConvertorHandler) downloadFFmpeg(progressBar *widget.ProgressBar, progre
return err return err
} }
} }
progressMessage.Text = h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ progressMessage.Text = h.app.GetLocalizerService().GetMessage("downloadRun")
MessageID: "downloadRun",
})
fyne.Do(func() { fyne.Do(func() {
progressMessage.Refresh() progressMessage.Refresh()
}) })
@@ -41,9 +38,7 @@ func (h ConvertorHandler) downloadFFmpeg(progressBar *widget.ProgressBar, progre
return err return err
} }
progressMessage.Text = h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ progressMessage.Text = h.app.GetLocalizerService().GetMessage("unzipRun")
MessageID: "unzipRun",
})
fyne.Do(func() { fyne.Do(func() {
progressMessage.Refresh() progressMessage.Refresh()
}) })
@@ -53,21 +48,21 @@ func (h ConvertorHandler) downloadFFmpeg(progressBar *widget.ProgressBar, progre
} }
_ = os.Remove("ffmpeg/ffmpeg.tar.xz") _ = os.Remove("ffmpeg/ffmpeg.tar.xz")
progressMessage.Text = h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ progressMessage.Text = h.app.GetLocalizerService().GetMessage("testFF")
MessageID: "testFF",
})
fyne.Do(func() { fyne.Do(func() {
progressMessage.Refresh() progressMessage.Refresh()
}) })
err = h.saveSettingFFPath("ffmpeg/ffmpeg-master-latest-linux64-gpl/bin/ffmpeg", "ffmpeg/ffmpeg-master-latest-linux64-gpl/bin/ffprobe") err = h.saveSettingFFPath(
"ffmpeg/ffmpeg-master-latest-linux64-gpl/bin/ffmpeg",
"ffmpeg/ffmpeg-master-latest-linux64-gpl/bin/ffprobe",
"ffmpeg/ffmpeg-master-latest-linux64-gpl/bin/ffplay",
)
if err != nil { if err != nil {
return err return err
} }
progressMessage.Text = h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ progressMessage.Text = h.app.GetLocalizerService().GetMessage("completedQueue")
MessageID: "completedQueue",
})
fyne.Do(func() { fyne.Do(func() {
progressMessage.Refresh() progressMessage.Refresh()
}) })
@@ -217,6 +212,12 @@ func unTarXz(fileTar string, directory string, progressBar *widget.ProgressBar)
return err return err
} }
ffplayPath := filepath.Join(directory, "ffmpeg-master-latest-linux64-gpl", "bin", "ffplay")
err = os.Chmod(ffplayPath, 0755)
if err != nil {
return err
}
return nil return nil
} }

View File

@@ -10,7 +10,6 @@ import (
"fyne.io/fyne/v2/canvas" "fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/widget" "fyne.io/fyne/v2/widget"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel" "git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel"
"github.com/nicksnyder/go-i18n/v2/i18n"
"io" "io"
"net/http" "net/http"
"os" "os"
@@ -19,7 +18,7 @@ import (
) )
func getPathsToFF() []kernel.FFPathUtilities { func getPathsToFF() []kernel.FFPathUtilities {
return []kernel.FFPathUtilities{{"ffmpeg\\bin\\ffmpeg.exe", "ffmpeg\\bin\\ffprobe.exe"}} return []kernel.FFPathUtilities{{FFmpeg: "ffmpeg\\bin\\ffmpeg.exe", FFprobe: "ffmpeg\\bin\\ffprobe.exe", FFplay: "ffmpeg\\bin\\ffplay.exe"}}
} }
func (h ConvertorHandler) downloadFFmpeg(progressBar *widget.ProgressBar, progressMessage *canvas.Text) (err error) { func (h ConvertorHandler) downloadFFmpeg(progressBar *widget.ProgressBar, progressMessage *canvas.Text) (err error) {
@@ -30,9 +29,7 @@ func (h ConvertorHandler) downloadFFmpeg(progressBar *widget.ProgressBar, progre
return err return err
} }
} }
progressMessage.Text = h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ progressMessage.Text = h.app.GetLocalizerService().GetMessage("downloadRun")
MessageID: "downloadRun",
})
fyne.Do(func() { fyne.Do(func() {
progressMessage.Refresh() progressMessage.Refresh()
}) })
@@ -41,9 +38,7 @@ func (h ConvertorHandler) downloadFFmpeg(progressBar *widget.ProgressBar, progre
return err return err
} }
progressMessage.Text = h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ progressMessage.Text = h.app.GetLocalizerService().GetMessage("unzipRun")
MessageID: "unzipRun",
})
fyne.Do(func() { fyne.Do(func() {
progressMessage.Refresh() progressMessage.Refresh()
}) })
@@ -53,20 +48,20 @@ func (h ConvertorHandler) downloadFFmpeg(progressBar *widget.ProgressBar, progre
} }
_ = os.Remove("ffmpeg/ffmpeg.zip") _ = os.Remove("ffmpeg/ffmpeg.zip")
progressMessage.Text = h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ progressMessage.Text = h.app.GetLocalizerService().GetMessage("testFF")
MessageID: "testFF",
})
fyne.Do(func() { fyne.Do(func() {
progressMessage.Refresh() progressMessage.Refresh()
}) })
err = h.saveSettingFFPath("ffmpeg/ffmpeg-master-latest-win64-gpl/bin/ffmpeg.exe", "ffmpeg/ffmpeg-master-latest-win64-gpl/bin/ffprobe.exe") err = h.saveSettingFFPath(
"ffmpeg/ffmpeg-master-latest-win64-gpl/bin/ffmpeg.exe",
"ffmpeg/ffmpeg-master-latest-win64-gpl/bin/ffprobe.exe",
"ffmpeg/ffmpeg-master-latest-win64-gpl/bin/ffplay.exe",
)
if err != nil { if err != nil {
return err return err
} }
progressMessage.Text = h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ progressMessage.Text = h.app.GetLocalizerService().GetMessage("completedQueue")
MessageID: "completedQueue",
})
fyne.Do(func() { fyne.Do(func() {
progressMessage.Refresh() progressMessage.Refresh()
}) })

View File

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

View File

@@ -5,7 +5,7 @@ import (
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel" "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/localizer"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/menu" "git.kor-elf.net/kor-elf/gui-for-ffmpeg/menu"
"github.com/nicksnyder/go-i18n/v2/i18n" "git.kor-elf.net/kor-elf/gui-for-ffmpeg/theme"
) )
type MenuHandlerContract interface { type MenuHandlerContract interface {
@@ -17,23 +17,26 @@ type MenuHandler struct {
app kernel.AppContract app kernel.AppContract
convertorHandler ConvertorHandlerContract convertorHandler ConvertorHandlerContract
menuView menu.ViewContract menuView menu.ViewContract
menuViewSetting menu.ViewSettingContract
localizerView localizer.ViewContract localizerView localizer.ViewContract
localizerRepository localizer.RepositoryContract themeService theme.ThemeContract
} }
func NewMenuHandler( func NewMenuHandler(
app kernel.AppContract, app kernel.AppContract,
convertorHandler ConvertorHandlerContract, convertorHandler ConvertorHandlerContract,
menuView menu.ViewContract, menuView menu.ViewContract,
menuViewSetting menu.ViewSettingContract,
localizerView localizer.ViewContract, localizerView localizer.ViewContract,
localizerRepository localizer.RepositoryContract, themeService theme.ThemeContract,
) *MenuHandler { ) *MenuHandler {
return &MenuHandler{ return &MenuHandler{
app: app, app: app,
convertorHandler: convertorHandler, convertorHandler: convertorHandler,
menuView: menuView, menuView: menuView,
menuViewSetting: menuViewSetting,
localizerView: localizerView, localizerView: localizerView,
localizerRepository: localizerRepository, themeService: themeService,
} }
} }
@@ -45,31 +48,23 @@ func (h MenuHandler) GetMainMenu() *fyne.MainMenu {
} }
func (h MenuHandler) getMenuSettings() *fyne.Menu { func (h MenuHandler) getMenuSettings() *fyne.Menu {
quit := fyne.NewMenuItem(h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ quit := fyne.NewMenuItem(h.app.GetLocalizerService().GetMessage("exit"), nil)
MessageID: "exit",
}), nil)
quit.IsQuit = true quit.IsQuit = true
h.app.GetLocalizerService().AddChangeCallback("exit", func(text string) { h.app.GetLocalizerService().AddChangeCallback("exit", func(text string) {
quit.Label = text quit.Label = text
}) })
languageSelection := fyne.NewMenuItem(h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ settingsSelection := fyne.NewMenuItem(h.app.GetLocalizerService().GetMessage("settings"), h.settingsSelection)
MessageID: "changeLanguage", h.app.GetLocalizerService().AddChangeCallback("settings", func(text string) {
}), h.LanguageSelection) settingsSelection.Label = text
h.app.GetLocalizerService().AddChangeCallback("changeLanguage", func(text string) {
languageSelection.Label = text
}) })
ffPathSelection := fyne.NewMenuItem(h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ ffPathSelection := fyne.NewMenuItem(h.app.GetLocalizerService().GetMessage("changeFFPath"), h.convertorHandler.FfPathSelection)
MessageID: "changeFFPath",
}), h.convertorHandler.FfPathSelection)
h.app.GetLocalizerService().AddChangeCallback("changeFFPath", func(text string) { h.app.GetLocalizerService().AddChangeCallback("changeFFPath", func(text string) {
ffPathSelection.Label = text ffPathSelection.Label = text
}) })
settings := fyne.NewMenu(h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ settings := fyne.NewMenu(h.app.GetLocalizerService().GetMessage("settings"), settingsSelection, ffPathSelection, quit)
MessageID: "settings",
}), languageSelection, ffPathSelection, quit)
h.app.GetLocalizerService().AddChangeCallback("settings", func(text string) { h.app.GetLocalizerService().AddChangeCallback("settings", func(text string) {
settings.Label = text settings.Label = text
settings.Refresh() settings.Refresh()
@@ -79,23 +74,22 @@ func (h MenuHandler) getMenuSettings() *fyne.Menu {
} }
func (h MenuHandler) getMenuHelp() *fyne.Menu { func (h MenuHandler) getMenuHelp() *fyne.Menu {
about := fyne.NewMenuItem(h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ about := fyne.NewMenuItem(h.app.GetLocalizerService().GetMessage("about"), h.openAbout)
MessageID: "about",
}), h.openAbout)
h.app.GetLocalizerService().AddChangeCallback("about", func(text string) { h.app.GetLocalizerService().AddChangeCallback("about", func(text string) {
about.Label = text about.Label = text
}) })
gratitude := fyne.NewMenuItem(h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ gratitude := fyne.NewMenuItem(h.app.GetLocalizerService().GetMessage("gratitude"), h.menuView.Gratitude)
MessageID: "gratitude",
}), h.openGratitude)
h.app.GetLocalizerService().AddChangeCallback("gratitude", func(text string) { h.app.GetLocalizerService().AddChangeCallback("gratitude", func(text string) {
gratitude.Label = text gratitude.Label = text
}) })
help := fyne.NewMenu(h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ helpFFplay := fyne.NewMenuItem(h.app.GetLocalizerService().GetMessage("helpFFplay"), h.menuView.HelpFFplay)
MessageID: "help", h.app.GetLocalizerService().AddChangeCallback("helpFFplay", func(text string) {
}), about, gratitude) helpFFplay.Label = text
})
help := fyne.NewMenu(h.app.GetLocalizerService().GetMessage("help"), helpFFplay, about, gratitude)
h.app.GetLocalizerService().AddChangeCallback("help", func(text string) { h.app.GetLocalizerService().AddChangeCallback("help", func(text string) {
help.Label = text help.Label = text
help.Refresh() help.Refresh()
@@ -107,27 +101,43 @@ func (h MenuHandler) getMenuHelp() *fyne.Menu {
func (h MenuHandler) openAbout() { func (h MenuHandler) openAbout() {
ffmpeg, err := h.convertorHandler.GetFfmpegVersion() ffmpeg, err := h.convertorHandler.GetFfmpegVersion()
if err != nil { if err != nil {
ffmpeg = h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ ffmpeg = h.app.GetLocalizerService().GetMessage("errorFFmpegVersion")
MessageID: "errorFFmpegVersion",
})
} }
ffprobe, err := h.convertorHandler.GetFfprobeVersion() ffprobe, err := h.convertorHandler.GetFfprobeVersion()
if err != nil { if err != nil {
ffprobe = h.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ ffprobe = h.app.GetLocalizerService().GetMessage("errorFFprobeVersion")
MessageID: "errorFFprobeVersion", }
}) ffplay, err := h.convertorHandler.GetFfplayVersion()
if err != nil {
ffplay = h.app.GetLocalizerService().GetMessage("errorFFplayVersion")
} }
h.menuView.About(ffmpeg, ffprobe) h.menuView.About(ffmpeg, ffprobe, ffplay)
}
func (h MenuHandler) openGratitude() {
h.menuView.Gratitude()
} }
func (h MenuHandler) LanguageSelection() { func (h MenuHandler) LanguageSelection() {
h.localizerView.LanguageSelection(func(lang kernel.Lang) { h.localizerView.LanguageSelection(func(lang kernel.Lang) {
_, _ = h.localizerRepository.Save(lang.Code)
h.convertorHandler.MainConvertor() h.convertorHandler.MainConvertor()
}) })
} }
func (h MenuHandler) settingsSelection() {
save := func(setting *menu.SettingForm) error {
err := h.app.GetLocalizerService().SetCurrentLanguage(setting.Language, true)
if err != nil {
return err
}
err = h.themeService.SetCurrentTheme(setting.ThemeInfo)
if err != nil {
return err
}
h.convertorHandler.MainConvertor()
return nil
}
cancel := func() {
h.convertorHandler.MainConvertor()
}
h.menuViewSetting.Main(save, cancel)
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 77 KiB

View File

@@ -0,0 +1,13 @@
package resources
import (
_ "embed"
"fyne.io/fyne/v2"
)
//go:embed icons/logo.png
var iconAppLogo []byte
func IconAppLogoResource() *fyne.StaticResource {
return fyne.NewStaticResource("icon.png", iconAppLogo)
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

View File

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

View File

@@ -32,8 +32,10 @@ type ConvertorContract interface {
GetTotalDuration(file *File) (float64, error) GetTotalDuration(file *File) (float64, error)
GetFFmpegVesrion() (string, error) GetFFmpegVesrion() (string, error)
GetFFprobeVersion() (string, error) GetFFprobeVersion() (string, error)
GetFFplayVersion() (string, error)
ChangeFFmpegPath(path string) (bool, error) ChangeFFmpegPath(path string) (bool, error)
ChangeFFprobePath(path string) (bool, error) ChangeFFprobePath(path string) (bool, error)
ChangeFFplayPath(path string) (bool, error)
GetRunningProcesses() map[int]*exec.Cmd GetRunningProcesses() map[int]*exec.Cmd
GetSupportFormats() (encoder.ConvertorFormatsContract, error) GetSupportFormats() (encoder.ConvertorFormatsContract, error)
} }
@@ -46,6 +48,7 @@ type ProgressContract interface {
type FFPathUtilities struct { type FFPathUtilities struct {
FFmpeg string FFmpeg string
FFprobe string FFprobe string
FFplay string
} }
type runningProcesses struct { type runningProcesses struct {
@@ -177,6 +180,17 @@ func (s Convertor) GetFFprobeVersion() (string, error) {
return text[0], nil return text[0], nil
} }
func (s Convertor) GetFFplayVersion() (string, error) {
cmd := exec.Command(s.ffPathUtilities.FFplay, "-version")
helper.PrepareBackgroundCommand(cmd)
out, err := cmd.CombinedOutput()
if err != nil {
return "", err
}
text := regexp.MustCompile("\r?\n").Split(strings.TrimSpace(string(out)), -1)
return text[0], nil
}
func (s Convertor) ChangeFFmpegPath(path string) (bool, error) { func (s Convertor) ChangeFFmpegPath(path string) (bool, error) {
cmd := exec.Command(path, "-version") cmd := exec.Command(path, "-version")
helper.PrepareBackgroundCommand(cmd) helper.PrepareBackgroundCommand(cmd)
@@ -205,6 +219,20 @@ func (s Convertor) ChangeFFprobePath(path string) (bool, error) {
return true, nil return true, nil
} }
func (s Convertor) ChangeFFplayPath(path string) (bool, error) {
cmd := exec.Command(path, "-version")
helper.PrepareBackgroundCommand(cmd)
out, err := cmd.CombinedOutput()
if err != nil {
return false, err
}
if strings.Contains(strings.TrimSpace(string(out)), "ffplay") == false {
return false, nil
}
s.ffPathUtilities.FFplay = path
return true, nil
}
func (s Convertor) GetSupportFormats() (encoder.ConvertorFormatsContract, error) { func (s Convertor) GetSupportFormats() (encoder.ConvertorFormatsContract, error) {
formats := encoder.NewConvertorFormats() formats := encoder.NewConvertorFormats()
cmd := exec.Command(s.ffPathUtilities.FFmpeg, "-encoders") cmd := exec.Command(s.ffPathUtilities.FFmpeg, "-encoders")

View File

@@ -2,14 +2,11 @@ package kernel
import ( import (
"fyne.io/fyne/v2" "fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/container" "fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget" "fyne.io/fyne/v2/widget"
) )
func PanicErrorLang(err error, metadata *fyne.AppMetadata) { func panicErrorLang(a fyne.App, err error) {
app.SetMetadata(*metadata)
a := app.New()
window := a.NewWindow("GUI for FFmpeg") window := a.NewWindow("GUI for FFmpeg")
window.SetContent(container.NewVBox( window.SetContent(container.NewVBox(
widget.NewLabel("Произошла ошибка!"), widget.NewLabel("Произошла ошибка!"),

28
kernel/ffplay.go Normal file
View File

@@ -0,0 +1,28 @@
package kernel
import (
"os/exec"
)
type FFplay struct {
ffPathUtilities *FFPathUtilities
}
type FFplaySetting struct {
PathToFile string
}
type FFplayContract interface {
Run(setting FFplaySetting) error
}
func NewFFplay(ffPathUtilities *FFPathUtilities) *FFplay {
return &FFplay{ffPathUtilities: ffPathUtilities}
}
func (ffplay FFplay) Run(setting FFplaySetting) error {
args := []string{setting.PathToFile}
cmd := exec.Command(ffplay.ffPathUtilities.FFplay, args...)
return cmd.Start()
}

152
kernel/items_to_convert.go Normal file
View File

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

View File

@@ -1,40 +1,37 @@
package kernel package kernel
import ( import (
"bufio"
"errors"
"fyne.io/fyne/v2" "fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas" "fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container" "fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/theme" "fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget" "fyne.io/fyne/v2/widget"
"github.com/nicksnyder/go-i18n/v2/i18n"
"image/color" "image/color"
"io"
"regexp"
"strconv" "strconv"
"strings" "strings"
) )
type LayoutContract interface { type LayoutContract interface {
SetContent(content fyne.CanvasObject) *fyne.Container SetContent(content fyne.CanvasObject) *fyne.Container
NewProgressbar(queueId int, totalDuration float64) ProgressContract
ChangeQueueStatus(queueId int, queue *Queue) ChangeQueueStatus(queueId int, queue *Queue)
GetRightTabs() RightTabsContract
} }
type Layout struct { type Layout struct {
layout *fyne.Container layout *fyne.Container
queueLayoutObject QueueLayoutObjectContract queueLayoutObject QueueLayoutObjectContract
localizerService LocalizerContract localizerService LocalizerContract
rightTabsService RightTabsContract
} }
func NewLayout(queueLayoutObject QueueLayoutObjectContract, localizerService LocalizerContract) *Layout { func NewLayout(queueLayoutObject QueueLayoutObjectContract, localizerService LocalizerContract, rightTabsService RightTabsContract) *Layout {
layout := container.NewAdaptiveGrid(2, widget.NewLabel(""), container.NewVScroll(queueLayoutObject.GetCanvasObject())) layout := container.NewAdaptiveGrid(2, widget.NewLabel(""), queueLayoutObject.GetCanvasObject())
return &Layout{ return &Layout{
layout: layout, layout: layout,
queueLayoutObject: queueLayoutObject, queueLayoutObject: queueLayoutObject,
localizerService: localizerService, localizerService: localizerService,
rightTabsService: rightTabsService,
} }
} }
@@ -43,18 +40,16 @@ func (l Layout) SetContent(content fyne.CanvasObject) *fyne.Container {
return l.layout return l.layout
} }
func (l Layout) NewProgressbar(queueId int, totalDuration float64) ProgressContract {
progressbar := l.queueLayoutObject.GetProgressbar(queueId)
return NewProgress(totalDuration, progressbar, l.localizerService)
}
func (l Layout) ChangeQueueStatus(queueId int, queue *Queue) { func (l Layout) ChangeQueueStatus(queueId int, queue *Queue) {
l.queueLayoutObject.ChangeQueueStatus(queueId, queue) l.queueLayoutObject.ChangeQueueStatus(queueId, queue)
} }
func (l Layout) GetRightTabs() RightTabsContract {
return l.rightTabsService
}
type QueueLayoutObjectContract interface { type QueueLayoutObjectContract interface {
GetCanvasObject() fyne.CanvasObject GetCanvasObject() fyne.CanvasObject
GetProgressbar(queueId int) *widget.ProgressBar
ChangeQueueStatus(queueId int, queue *Queue) ChangeQueueStatus(queueId int, queue *Queue)
} }
@@ -63,22 +58,25 @@ type QueueLayoutObject struct {
queue QueueListContract queue QueueListContract
container *fyne.Container container *fyne.Container
containerItems *fyne.Container
items map[int]QueueLayoutItem items map[int]QueueLayoutItem
localizerService LocalizerContract localizerService LocalizerContract
queueStatisticsFormat *queueStatisticsFormat queueStatisticsFormat *queueStatisticsFormat
ffplayService FFplayContract
} }
type QueueLayoutItem struct { type QueueLayoutItem struct {
CanvasObject fyne.CanvasObject CanvasObject fyne.CanvasObject
ProgressBar *widget.ProgressBar BlockMessageError *container.Scroll
StatusMessage *canvas.Text StatusMessage *canvas.Text
MessageError *canvas.Text MessageError *canvas.Text
buttonPlay *widget.Button
status *StatusContract status *StatusContract
} }
func NewQueueLayoutObject(queue QueueListContract, localizerService LocalizerContract) *QueueLayoutObject { func NewQueueLayoutObject(queue QueueListContract, localizerService LocalizerContract, ffplayService FFplayContract, rightTabsService RightTabsContract, blockProgressbar *fyne.Container) *QueueLayoutObject {
title := widget.NewLabel(localizerService.GetMessage(&i18n.LocalizeConfig{MessageID: "queue"})) title := widget.NewLabel(localizerService.GetMessage("queue"))
title.TextStyle.Bold = true title.TextStyle.Bold = true
localizerService.AddChangeCallback("queue", func(text string) { localizerService.AddChangeCallback("queue", func(text string) {
@@ -89,15 +87,31 @@ func NewQueueLayoutObject(queue QueueListContract, localizerService LocalizerCon
items := map[int]QueueLayoutItem{} items := map[int]QueueLayoutItem{}
queueStatisticsFormat := newQueueStatisticsFormat(localizerService, &items) queueStatisticsFormat := newQueueStatisticsFormat(localizerService, &items)
queueLayoutObject := &QueueLayoutObject{ line := canvas.NewLine(theme.Color(theme.ColorNameFocus))
queue: queue, line.StrokeWidth = 5
container: container.NewVBox(
rightTabsService.GetFileQueueContainer().Add(container.NewVBox(
container.NewPadded(),
container.NewHBox(title, queueStatisticsFormat.completed.widget, queueStatisticsFormat.error.widget), container.NewHBox(title, queueStatisticsFormat.completed.widget, queueStatisticsFormat.error.widget),
container.NewHBox(queueStatisticsFormat.inProgress.widget, queueStatisticsFormat.waiting.widget, queueStatisticsFormat.total.widget), container.NewHBox(queueStatisticsFormat.inProgress.widget, queueStatisticsFormat.waiting.widget, queueStatisticsFormat.total.widget),
container.NewPadded(),
line,
container.NewPadded(),
))
queueLayoutObject := &QueueLayoutObject{
queue: queue,
container: container.NewBorder(
container.NewVBox(
blockProgressbar,
widget.NewSeparator(),
), ),
nil, nil, nil, container.NewVScroll(rightTabsService.GetTabs()),
),
containerItems: rightTabsService.GetFileQueueContainer(),
items: items, items: items,
localizerService: localizerService, localizerService: localizerService,
queueStatisticsFormat: queueStatisticsFormat, queueStatisticsFormat: queueStatisticsFormat,
ffplayService: ffplayService,
} }
queue.AddListener(queueLayoutObject) queue.AddListener(queueLayoutObject)
@@ -109,24 +123,24 @@ func (o QueueLayoutObject) GetCanvasObject() fyne.CanvasObject {
return o.container return o.container
} }
func (o QueueLayoutObject) GetProgressbar(queueId int) *widget.ProgressBar {
if item, ok := o.items[queueId]; ok {
return item.ProgressBar
}
return widget.NewProgressBar()
}
func (o QueueLayoutObject) Add(id int, queue *Queue) { func (o QueueLayoutObject) Add(id int, queue *Queue) {
progressBar := widget.NewProgressBar()
statusMessage := canvas.NewText(o.getStatusTitle(queue.Status), theme.Color(theme.ColorNamePrimary)) statusMessage := canvas.NewText(o.getStatusTitle(queue.Status), theme.Color(theme.ColorNamePrimary))
messageError := canvas.NewText("", theme.Color(theme.ColorNameError)) messageError := canvas.NewText("", theme.Color(theme.ColorNameError))
buttonPlay := widget.NewButtonWithIcon("", theme.Icon(theme.IconNameMediaPlay), func() {
})
buttonPlay.Hide()
blockMessageError := container.NewHScroll(messageError)
blockMessageError.Hide()
content := container.NewVBox( content := container.NewVBox(
container.NewHScroll(widget.NewLabel(queue.Setting.VideoFileInput.Name)), container.NewHScroll(widget.NewLabel(queue.Setting.VideoFileInput.Name)),
progressBar, container.NewHBox(
container.NewHScroll(statusMessage), buttonPlay,
container.NewHScroll(messageError), statusMessage,
),
blockMessageError,
container.NewPadded(),
canvas.NewLine(theme.Color(theme.ColorNameFocus)), canvas.NewLine(theme.Color(theme.ColorNameFocus)),
container.NewPadded(), container.NewPadded(),
) )
@@ -138,12 +152,13 @@ func (o QueueLayoutObject) Add(id int, queue *Queue) {
o.items[id] = QueueLayoutItem{ o.items[id] = QueueLayoutItem{
CanvasObject: content, CanvasObject: content,
ProgressBar: progressBar,
StatusMessage: statusMessage, StatusMessage: statusMessage,
BlockMessageError: blockMessageError,
MessageError: messageError, MessageError: messageError,
buttonPlay: buttonPlay,
status: &queue.Status, status: &queue.Status,
} }
o.container.Add(content) o.containerItems.Add(content)
} }
func (o QueueLayoutObject) Remove(id int) { func (o QueueLayoutObject) Remove(id int) {
@@ -166,9 +181,24 @@ func (o QueueLayoutObject) ChangeQueueStatus(queueId int, queue *Queue) {
item.MessageError.Text = queue.Error.Error() item.MessageError.Text = queue.Error.Error()
item.MessageError.Color = statusColor item.MessageError.Color = statusColor
fyne.Do(func() { fyne.Do(func() {
item.BlockMessageError.Show()
item.MessageError.Refresh() item.MessageError.Refresh()
}) })
} }
if queue.Status == StatusType(Completed) {
item.buttonPlay.Show()
item.buttonPlay.OnTapped = func() {
item.buttonPlay.Disable()
go func() {
_ = o.ffplayService.Run(FFplaySetting{
PathToFile: queue.Setting.VideoFileOut.Path,
})
fyne.Do(func() {
item.buttonPlay.Enable()
})
}()
}
}
if o.queueStatisticsFormat.isChecked(queue.Status) == false && item.CanvasObject.Visible() == true { if o.queueStatisticsFormat.isChecked(queue.Status) == false && item.CanvasObject.Visible() == true {
item.CanvasObject.Hide() item.CanvasObject.Hide()
} else if item.CanvasObject.Visible() == false { } else if item.CanvasObject.Visible() == false {
@@ -191,102 +221,7 @@ func (o QueueLayoutObject) getStatusColor(status StatusContract) color.Color {
} }
func (o QueueLayoutObject) getStatusTitle(status StatusContract) string { func (o QueueLayoutObject) getStatusTitle(status StatusContract) string {
return o.localizerService.GetMessage(&i18n.LocalizeConfig{MessageID: status.Name() + "Queue"}) return o.localizerService.GetMessage(status.Name() + "Queue")
}
type Progress struct {
totalDuration float64
progressbar *widget.ProgressBar
protocol string
localizerService LocalizerContract
}
func NewProgress(totalDuration float64, progressbar *widget.ProgressBar, localizerService LocalizerContract) Progress {
return Progress{
totalDuration: totalDuration,
progressbar: progressbar,
protocol: "pipe:",
localizerService: localizerService,
}
}
func (p Progress) GetProtocole() string {
return p.protocol
}
func (p Progress) Run(stdOut io.ReadCloser, stdErr io.ReadCloser) error {
isProcessCompleted := false
var errorText string
p.progressbar.Value = 0
p.progressbar.Max = p.totalDuration
fyne.Do(func() {
p.progressbar.Refresh()
})
progress := 0.0
go func() {
scannerErr := bufio.NewReader(stdErr)
for {
line, _, err := scannerErr.ReadLine()
if err != nil {
if err == io.EOF {
break
}
continue
}
data := strings.TrimSpace(string(line))
errorText = data
}
}()
scannerOut := bufio.NewReader(stdOut)
for {
line, _, err := scannerOut.ReadLine()
if err != nil {
if err == io.EOF {
break
}
continue
}
data := strings.TrimSpace(string(line))
if strings.Contains(data, "progress=end") {
p.progressbar.Value = p.totalDuration
fyne.Do(func() {
p.progressbar.Refresh()
})
isProcessCompleted = true
break
}
re := regexp.MustCompile(`frame=(\d+)`)
a := re.FindAllStringSubmatch(data, -1)
if len(a) > 0 && len(a[len(a)-1]) > 0 {
c, err := strconv.Atoi(a[len(a)-1][len(a[len(a)-1])-1])
if err != nil {
continue
}
progress = float64(c)
}
if p.progressbar.Value != progress {
p.progressbar.Value = progress
fyne.Do(func() {
p.progressbar.Refresh()
})
}
}
if isProcessCompleted == false {
if len(errorText) == 0 {
errorText = p.localizerService.GetMessage(&i18n.LocalizeConfig{
MessageID: "errorConverter",
})
}
return errors.New(errorText)
}
return nil
} }
type queueStatistics struct { type queueStatistics struct {
@@ -505,7 +440,7 @@ func newQueueStatistics(messaigeID string, localizerService LocalizerContract) *
count := int64(0) count := int64(0)
title := localizerService.GetMessage(&i18n.LocalizeConfig{MessageID: messaigeID}) title := localizerService.GetMessage(messaigeID)
queueStatistics := &queueStatistics{ queueStatistics := &queueStatistics{
widget: checkbox, widget: checkbox,
title: strings.ToLower(title), title: strings.ToLower(title),

View File

@@ -1,21 +1,28 @@
package kernel package kernel
import ( import (
"github.com/BurntSushi/toml" "embed"
"github.com/nicksnyder/go-i18n/v2/i18n" "encoding/json"
"golang.org/x/text/cases" "fyne.io/fyne/v2"
"fyne.io/fyne/v2/lang"
"golang.org/x/text/language" "golang.org/x/text/language"
"golang.org/x/text/language/display"
"path/filepath"
"sort"
) )
//go:embed translations
var translations embed.FS
var supportedLanguages = map[string]Lang{
"ru": {Code: "ru", Title: "Русский"},
"kk": {Code: "kk", Title: "Қазақ Тілі"},
"en": {Code: "en", Title: "English"},
}
type LocalizerContract interface { type LocalizerContract interface {
IsStartWithLanguageSelection() bool
GetMessage(key string, data ...any) string
GetLanguages() []Lang GetLanguages() []Lang
GetMessage(localizeConfig *i18n.LocalizeConfig) string GetCurrentLanguage() Lang
SetCurrentLanguage(lang Lang) error SetCurrentLanguage(selectLang Lang, isSaveSetting bool) error
SetCurrentLanguageByCode(code string) error
GetCurrentLanguage() *CurrentLanguage
AddChangeCallback(messageID string, callback func(text string)) AddChangeCallback(messageID string, callback func(text string))
} }
@@ -24,135 +31,164 @@ type Lang struct {
Title string Title string
} }
type CurrentLanguage struct {
Lang Lang
localizer *i18n.Localizer
localizerDefault *i18n.Localizer
}
type changeCallback struct { type changeCallback struct {
messageID string messageID string
callback func(text string) callback func(text string)
} }
type Localizer struct { type Localizer struct {
bundle *i18n.Bundle setting SettingLanguageContract
languages []Lang currentLang Lang
currentLanguage *CurrentLanguage
changeCallbacks map[int]*changeCallback changeCallbacks map[int]*changeCallback
isStartWithLanguageSelection bool
} }
func NewLocalizer(directory string, languageDefault language.Tag) (*Localizer, error) { func newLocalizer(app fyne.App) (*Localizer, error) {
bundle := i18n.NewBundle(languageDefault) setting := newSettingLanguage(app)
bundle.RegisterUnmarshalFunc("toml", toml.Unmarshal) currentLanguage, isLanguageNotSupported := setting.GetLang()
languages, err := initLanguages(directory, bundle) localizer := &Localizer{
if err != nil { setting: setting,
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{}, changeCallbacks: map[int]*changeCallback{},
}, nil isStartWithLanguageSelection: isLanguageNotSupported,
} }
func initLanguages(directory string, bundle *i18n.Bundle) ([]Lang, error) { err := localizer.SetCurrentLanguage(currentLanguage, false)
var languages []Lang
files, err := filepath.Glob(directory + "/active.*.toml")
if err != nil { if err != nil {
return nil, err return nil, err
} }
for _, file := range files {
lang, err := bundle.LoadMessageFile(file) return localizer, nil
}
func (l *Localizer) IsStartWithLanguageSelection() bool {
return l.isStartWithLanguageSelection
}
func (l *Localizer) GetMessage(key string, data ...any) string {
return lang.L(key, data...)
}
func (l *Localizer) GetLanguages() []Lang {
return getLanguages()
}
func (l *Localizer) GetCurrentLanguage() Lang {
return l.currentLang
}
func (l *Localizer) SetCurrentLanguage(selectLang Lang, isSaveSetting bool) error {
l.currentLang = selectLang
translationsData, err := l.getTranslations(selectLang)
if err != nil { if err != nil {
return nil, err return 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)) name := lang.SystemLocale().LanguageString()
err = lang.AddTranslations(fyne.NewStaticResource(name+".json", translationsData))
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 { if err != nil {
message, err = l.GetCurrentLanguage().localizerDefault.Localize(localizeConfig) return err
if err != nil { }
return err.Error()
} if isSaveSetting {
} l.setting.SetLang(selectLang)
return message
} }
func (l Localizer) SetCurrentLanguage(lang Lang) error {
l.currentLanguage.Lang = lang
l.currentLanguage.localizer = i18n.NewLocalizer(l.bundle, lang.Code)
l.eventSetCurrentLanguage() l.eventSetCurrentLanguage()
return nil return nil
} }
func (l Localizer) SetCurrentLanguageByCode(code string) error { func (l *Localizer) AddChangeCallback(messageID string, callback func(text string)) {
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} l.changeCallbacks[len(l.changeCallbacks)] = &changeCallback{messageID: messageID, callback: callback}
} }
func (l Localizer) eventSetCurrentLanguage() { func (l *Localizer) eventSetCurrentLanguage() {
for _, changeCallback := range l.changeCallbacks { for _, changeCallback := range l.changeCallbacks {
text := l.GetMessage(&i18n.LocalizeConfig{MessageID: changeCallback.messageID}) text := l.GetMessage(changeCallback.messageID)
changeCallback.callback(text) changeCallback.callback(text)
} }
} }
type languagesSort []Lang func (l *Localizer) getTranslations(language Lang) ([]byte, error) {
baseJson, err := translations.ReadFile("translations/base." + language.Code + ".json")
func (l languagesSort) Len() int { return len(l) } if err != nil {
func (l languagesSort) Swap(i, j int) { l[i], l[j] = l[j], l[i] } return nil, err
func (l languagesSort) Less(i, j int) bool {
return languagePriority(l[i]) < languagePriority(l[j])
} }
func languagePriority(l Lang) int { appJson, err := translations.ReadFile("translations/app." + language.Code + ".json")
priority := 0 if err != nil {
return nil, err
switch l.Code {
case "ru":
priority = -3
case "kk":
priority = -2
case "en":
priority = -1
} }
return priority return l.mergeTranslations(baseJson, appJson)
}
func (l *Localizer) mergeTranslations(baseJson []byte, appJson []byte) ([]byte, error) {
base := map[string]interface{}{}
custom := map[string]interface{}{}
err := json.Unmarshal(baseJson, &base)
if err != nil {
return nil, err
}
err = json.Unmarshal(appJson, &custom)
if err != nil {
return nil, err
}
for k, v := range custom {
base[k] = v
}
return json.Marshal(base)
}
func getLanguages() []Lang {
items := []Lang{}
for _, item := range supportedLanguages {
items = append(items, item)
}
return items
}
type SettingLanguageContract interface {
GetLang() (currentLang Lang, isLanguageNotSupported bool)
SetLang(language Lang)
}
type SettingLanguage struct {
app fyne.App
}
func newSettingLanguage(app fyne.App) *SettingLanguage {
return &SettingLanguage{
app: app,
}
}
func (s *SettingLanguage) GetLang() (currentLang Lang, isLanguageNotSupported bool) {
languageCode := s.app.Preferences().String("language")
currentLang = supportedLanguages["ru"]
if languageCode == "" {
languageTag, err := language.Parse(lang.SystemLocale().LanguageString())
if err != nil {
return currentLang, true
}
base, _ := languageTag.Base()
languageCode = base.String()
}
if findLang, ok := findSupportedLanguage(languageCode); ok {
return findLang, false
}
return currentLang, true
}
func (s *SettingLanguage) SetLang(language Lang) {
s.app.Preferences().SetString("language", language.Code)
}
func findSupportedLanguage(code string) (Lang, bool) {
lang, ok := supportedLanguages[code]
return lang, ok
} }

245
kernel/progressbar.go Normal file
View File

@@ -0,0 +1,245 @@
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"
"image/color"
"io"
"regexp"
"strconv"
"strings"
)
type BlockProgressbarContract interface {
GetContainer() *fyne.Container
GetProgressbar(totalDuration float64, filePath string, localizerService LocalizerContract) Progress
ProcessEndedWithError(errorText string)
ProcessEndedWithSuccess(filePath string)
}
type BlockProgressbar struct {
container *fyne.Container
label *widget.Label
progressbar *widget.ProgressBar
errorBlock *container.Scroll
messageError *canvas.Text
statusMessage *canvas.Text
buttonPlay *widget.Button
statusesText *BlockProgressbarStatusesText
ffplayService FFplayContract
}
func NewBlockProgressbar(statusesText *BlockProgressbarStatusesText, ffplayService FFplayContract) *BlockProgressbar {
label := widget.NewLabel("")
progressbar := widget.NewProgressBar()
statusMessage := canvas.NewText("", theme.Color(theme.ColorNamePrimary))
messageError := canvas.NewText("", theme.Color(theme.ColorNameError))
buttonPlay := widget.NewButtonWithIcon("", theme.Icon(theme.IconNameMediaPlay), func() {
})
buttonPlay.Hide()
errorBlock := container.NewHScroll(messageError)
errorBlock.Hide()
content := container.NewVBox(
container.NewHScroll(label),
progressbar,
container.NewHScroll(container.NewHBox(
buttonPlay,
statusMessage,
)),
errorBlock,
)
content.Hide()
return &BlockProgressbar{
container: content,
label: label,
progressbar: progressbar,
errorBlock: errorBlock,
messageError: messageError,
statusMessage: statusMessage,
buttonPlay: buttonPlay,
statusesText: statusesText,
ffplayService: ffplayService,
}
}
func (block BlockProgressbar) GetContainer() *fyne.Container {
return block.container
}
func (block BlockProgressbar) GetProgressbar(totalDuration float64, filePath string, localizerService LocalizerContract) Progress {
block.label.Text = filePath
block.statusMessage.Color = theme.Color(theme.ColorNamePrimary)
block.statusMessage.Text = block.statusesText.inProgress
block.messageError.Text = ""
fyne.Do(func() {
block.buttonPlay.Hide()
if block.errorBlock.Visible() {
block.errorBlock.Hide()
}
block.statusMessage.Refresh()
block.container.Refresh()
block.errorBlock.Refresh()
})
block.progressbar.Value = 0
return NewProgress(totalDuration, block.progressbar, localizerService)
}
func (block BlockProgressbar) ProcessEndedWithError(errorText string) {
fyne.Do(func() {
block.statusMessage.Color = theme.Color(theme.ColorNameError)
block.statusMessage.Text = block.statusesText.error
block.messageError.Text = errorText
block.errorBlock.Show()
})
}
func (block BlockProgressbar) ProcessEndedWithSuccess(filePath string) {
fyne.Do(func() {
block.statusMessage.Color = color.RGBA{R: 49, G: 127, B: 114, A: 255}
block.statusMessage.Text = block.statusesText.completed
block.buttonPlay.Show()
block.buttonPlay.OnTapped = func() {
block.buttonPlay.Disable()
go func() {
_ = block.ffplayService.Run(FFplaySetting{
PathToFile: filePath,
})
fyne.Do(func() {
block.buttonPlay.Enable()
})
}()
}
})
}
type Progress struct {
totalDuration float64
progressbar *widget.ProgressBar
protocol string
localizerService LocalizerContract
}
func NewProgress(totalDuration float64, progressbar *widget.ProgressBar, localizerService LocalizerContract) Progress {
return Progress{
totalDuration: totalDuration,
progressbar: progressbar,
protocol: "pipe:",
localizerService: localizerService,
}
}
func (p Progress) GetProtocole() string {
return p.protocol
}
func (p Progress) Run(stdOut io.ReadCloser, stdErr io.ReadCloser) error {
isProcessCompleted := false
var errorText string
p.progressbar.Value = 0
p.progressbar.Max = p.totalDuration
fyne.Do(func() {
p.progressbar.Refresh()
})
progress := 0.0
go func() {
scannerErr := bufio.NewReader(stdErr)
for {
line, _, err := scannerErr.ReadLine()
if err != nil {
if err == io.EOF {
break
}
continue
}
data := strings.TrimSpace(string(line))
errorText = data
}
}()
scannerOut := bufio.NewReader(stdOut)
for {
line, _, err := scannerOut.ReadLine()
if err != nil {
if err == io.EOF {
break
}
continue
}
data := strings.TrimSpace(string(line))
if strings.Contains(data, "progress=end") {
p.progressbar.Value = p.totalDuration
fyne.Do(func() {
p.progressbar.Refresh()
})
isProcessCompleted = true
break
}
re := regexp.MustCompile(`frame=(\d+)`)
a := re.FindAllStringSubmatch(data, -1)
if len(a) > 0 && len(a[len(a)-1]) > 0 {
c, err := strconv.Atoi(a[len(a)-1][len(a[len(a)-1])-1])
if err != nil {
continue
}
progress = float64(c)
}
if p.progressbar.Value != progress {
p.progressbar.Value = progress
fyne.Do(func() {
p.progressbar.Refresh()
})
}
}
if isProcessCompleted == false {
if len(errorText) == 0 {
errorText = p.localizerService.GetMessage("errorConverter")
}
return errors.New(errorText)
}
return nil
}
type BlockProgressbarStatusesText struct {
inProgress string
completed string
error string
}
func GetBlockProgressbarStatusesText(localizerService LocalizerContract) *BlockProgressbarStatusesText {
statusesText := &BlockProgressbarStatusesText{
inProgress: localizerService.GetMessage("inProgressQueue"),
completed: localizerService.GetMessage("completedQueue"),
error: localizerService.GetMessage("errorQueue"),
}
localizerService.AddChangeCallback("inProgressQueue", func(text string) {
statusesText.inProgress = text
})
localizerService.AddChangeCallback("completedQueue", func(text string) {
statusesText.completed = text
})
localizerService.AddChangeCallback("errorQueue", func(text string) {
statusesText.error = text
})
return statusesText
}

75
kernel/right_tabs.go Normal file
View File

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

View File

@@ -0,0 +1,143 @@
{
"AlsoUsedProgram": "The program also uses:",
"about": "About",
"aboutText": "A simple interface for the FFmpeg console utility. \nBut I am not the author of the FFmpeg utility itself.",
"addedFilesTitle": "Added files",
"autoClearAfterAddingToQueue": "Auto-clear after adding to queue",
"buttonDownloadFFmpeg": "Download FFmpeg automatically",
"buttonForSelectedDirTitle": "Save to folder:",
"cancel": "Cancel",
"changeFFPath": "FFmpeg, FFprobe and FFplay",
"changeLanguage": "Change language",
"checkboxOverwriteOutputFilesTitle": "Allow file to be overwritten",
"choose": "choose",
"clearAll": "Clear List",
"completedQueue": "Completed",
"converterVideoFilesSubmitTitle": "Convert",
"converterVideoFilesTitle": "Video, audio and picture converter",
"download": "Download",
"downloadFFmpegFromSite": "Will be downloaded from the site:",
"downloadRun": "Downloading...",
"dragAndDropFiles": "drag and drop files",
"encoderGroupAudio": "Audio",
"encoderGroupImage": "Images",
"encoderGroupVideo": "Video",
"encoder_apng": "APNG image",
"encoder_bmp": "BMP image",
"encoder_flv": "FLV",
"encoder_gif": "GIF image",
"encoder_h264_nvenc": "H.264 with NVIDIA support",
"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": "An error has occurred!",
"errorConverter": "Couldn't convert video",
"errorDragAndDropFile": "Not all files were added",
"errorFFmpeg": "this is not FFmpeg",
"errorFFmpegVersion": "Could not determine FFmpeg version",
"errorFFplay": "this is not FFplay",
"errorFFplayVersion": "Could not determine FFplay version",
"errorFFprobe": "this is not FFprobe",
"errorFFprobeVersion": "Failed to determine FFprobe version",
"errorNoFilesAddedForConversion": "There are no files to convert",
"errorQueue": "Error",
"errorSelectedEncoder": "Converter not selected",
"errorSelectedFolderSave": "No save folder selected!",
"errorSelectedFormat": "File extension not selected",
"exit": "Exit",
"ffmpegLGPL": "This software uses libraries from the **FFmpeg** project under the **[LGPLv2.1](http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html)**.",
"ffmpegTrademark": "**FFmpeg** is a trademark of **[Fabrice Bellard](http://bellard.org/)**, originator of the **[FFmpeg](https://ffmpeg.org/about.html)** project.",
"fileForConversionTitle": "File:",
"fileQueueTitle": "Queue",
"formPreset": "Preset",
"gratitude": "Gratitude",
"gratitudeText": "I sincerely thank you for your invaluable\n\r and timely assistance:",
"help": "Help",
"helpFFplay": "FFplay Player Keys",
"helpFFplayActivateFrameStepMode": "Activate frame-by-frame mode.",
"helpFFplayCycleVideoFiltersOrShowModes": "A cycle of video filters or display modes.",
"helpFFplayDecreaseVolume": "Decrease the volume.",
"helpFFplayDescription": "Description",
"helpFFplayDoubleClickLeftMouseButton": "double click\nleft mouse button",
"helpFFplayIncreaseVolume": "Increase the volume.",
"helpFFplayKeyDown": "down",
"helpFFplayKeyHoldS": "hold S",
"helpFFplayKeyLeft": "left",
"helpFFplayKeyRight": "right",
"helpFFplayKeySpace": "SPACE",
"helpFFplayKeyUp": "up",
"helpFFplayKeys": "Keys",
"helpFFplayPause": "Pause or continue playing.",
"helpFFplayQuit": "Close the player.",
"helpFFplaySeekBForward10Minutes": "Fast forward 10 minutes.",
"helpFFplaySeekBForward1Minute": "Fast forward 1 minute.",
"helpFFplaySeekBackward10Minutes": "Rewind 10 minutes.",
"helpFFplaySeekBackward10Seconds": "Rewind 10 seconds.",
"helpFFplaySeekBackward1Minute": "Rewind 1 minute.",
"helpFFplaySeekForward10Seconds": "Fast forward 10 seconds.",
"helpFFplayToggleFullScreen": "Switch to full screen or exit full screen.",
"helpFFplayToggleMute": "Mute or unmute.",
"inProgressQueue": "In Progress",
"languageSelectionFormHead": "Switch language",
"languageSelectionHead": "Choose language",
"licenseLink": "License information",
"licenseLinkOther": "Licenses from other products used in the program",
"menuSettingsLanguage": "Language",
"menuSettingsTheme": "Theme",
"or": "or",
"parameterCheckbox": "Enable option",
"pathToFfmpeg": "Path to FFmpeg:",
"pathToFfplay": "Path to FFplay:",
"pathToFfprobe": "Path to FFprobe:",
"preset_fast": "fast (slower than \"faster\", but the file will weigh less)",
"preset_faster": "faster (slower than \"veryfast\", but the file will weigh less)",
"preset_medium": "medium (slower than \"fast\", but the file will weigh less)",
"preset_placebo": "placebo (not recommended)",
"preset_slow": "slow (slower than \"medium\", but the file will weigh less)",
"preset_slower": "slower (slower than \"slow\", but the file will weigh less)",
"preset_superfast": "superfast (slower than \"ultrafast\", but the file will weigh less)",
"preset_ultrafast": "ultrafast (fast, but the file will weigh a lot)",
"preset_veryfast": "veryfast (slower than \"superfast\", but the file will weigh less)",
"preset_veryslow": "veryslow (slower than \"slower\", but the file will weigh less)",
"programmLink": "Project website",
"programmVersion": "**Program version:** {{.Version}}",
"queue": "Queue",
"save": "Save",
"selectEncoder": "Encoder:",
"selectFFPathTitle": "Specify the path to FFmpeg and FFprobe",
"selectFormat": "File extension:",
"settings": "Settings",
"testFF": "Checking FFmpeg for serviceability...",
"themesNameDark": "Dark",
"themesNameDefault": "Default",
"themesNameLight": "Light",
"titleDownloadLink": "You can download it from here",
"total": "Total",
"unzipRun": "Unpacked...",
"waitingQueue": "Waiting"
}

View File

@@ -0,0 +1,143 @@
{
"AlsoUsedProgram": "Бағдарлама сонымен қатар пайдаланады:",
"about": "Бағдарлама туралы",
"aboutText": "FFmpeg консоль утилитасы үшін қарапайым интерфейс. \nБірақ мен FFmpeg утилитасының авторы емеспін.",
"addedFilesTitle": "Қосылған файлдар",
"autoClearAfterAddingToQueue": "Кезекке қосқаннан кейін тазалаңыз",
"buttonDownloadFFmpeg": "FFmpeg автоматты түрде жүктеп алыңыз",
"buttonForSelectedDirTitle": "Қалтаға сақтаңыз:",
"cancel": "Болдырмау",
"changeFFPath": "FFmpeg, FFprobe және FFplay",
"changeLanguage": "Тілді өзгерту",
"checkboxOverwriteOutputFilesTitle": "Файлды қайта жазуға рұқсат беріңіз",
"choose": "таңдау",
"clearAll": "Тізімді өшіру",
"completedQueue": "Дайын",
"converterVideoFilesSubmitTitle": "Файлды түрлендіру",
"converterVideoFilesTitle": "Бейне, аудио және суретті түрлендіргіш",
"download": "Жүктеп алу",
"downloadFFmpegFromSite": "Сайттан жүктеледі:",
"downloadRun": "Жүктеп алынуда...",
"dragAndDropFiles": "файлдарды сүйреп апарыңыз",
"encoderGroupAudio": "Аудио",
"encoderGroupImage": "Суреттер",
"encoderGroupVideo": "Бейне",
"encoder_apng": "APNG image",
"encoder_bmp": "BMP image",
"encoder_flv": "FLV",
"encoder_gif": "GIF image",
"encoder_h264_nvenc": "NVIDIA қолдауымен H.264",
"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": "Бейнені түрлендіру мүмкін болмады",
"errorDragAndDropFile": "Барлық файлдар қосылмаған",
"errorFFmpeg": "бұл FFmpeg емес",
"errorFFmpegVersion": "FFmpeg нұсқасын анықтау мүмкін болмады",
"errorFFplay": "бұл FFplay емес",
"errorFFplayVersion": "FFplay нұсқасын анықтау мүмкін болмады",
"errorFFprobe": "бұл FFprobe емес",
"errorFFprobeVersion": "FFprobe нұсқасын анықтау мүмкін болмады",
"errorNoFilesAddedForConversion": "Түрлендіруге арналған файлдар жоқ",
"errorQueue": "Қате",
"errorSelectedEncoder": "Түрлендіргіш таңдалмаған",
"errorSelectedFolderSave": "Сақтау қалтасы таңдалмаған!",
"errorSelectedFormat": "Файл кеңейтімі таңдалмаған",
"exit": "Шығу",
"ffmpegLGPL": "Бұл бағдарламалық құрал **[LGPLv2.1](http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html)** астында **FFmpeg** жобасының кітапханаларын пайдаланады.",
"ffmpegTrademark": "FFmpeg — **[FFmpeg](https://ffmpeg.org/about.html)** жобасын жасаушы **[Fabrice Bellard](http://bellard.org/)** сауда белгісі.",
"fileForConversionTitle": "Файл:",
"fileQueueTitle": "Кезек",
"formPreset": "Алдын ала орнатылған",
"gratitude": "Алғыс",
"gratitudeText": "Сізге баға жетпес және уақтылы көмектескеніңіз\n\r үшін шын жүректен алғыс айтамын:",
"help": "Анықтама",
"helpFFplay": "FFplay ойнатқышының пернелері",
"helpFFplayActivateFrameStepMode": "Уақыт аралығын іске қосыңыз.",
"helpFFplayCycleVideoFiltersOrShowModes": "Бейне сүзгілерінің немесе дисплей режимдерінің циклі.",
"helpFFplayDecreaseVolume": "Дыбыс деңгейін төмендетіңіз.",
"helpFFplayDescription": "Сипаттама",
"helpFFplayDoubleClickLeftMouseButton": "тінтуірдің сол жақ\nбатырмасын екі рет басу",
"helpFFplayIncreaseVolume": "Дыбыс деңгейін арттыру.",
"helpFFplayKeyDown": "төмен",
"helpFFplayKeyHoldS": "ұстау S",
"helpFFplayKeyLeft": "сол",
"helpFFplayKeyRight": "құқық",
"helpFFplayKeySpace": "SPACE (пробел)",
"helpFFplayKeyUp": "жоғары",
"helpFFplayKeys": "Кілттер",
"helpFFplayPause": "Кідіртіңіз немесе жоғалтуды жалғастырыңыз.",
"helpFFplayQuit": "Ойнатқышты жабыңыз.",
"helpFFplaySeekBForward10Minutes": "10 минутқа алға айналдырыңыз.",
"helpFFplaySeekBForward1Minute": "1 минутқа алға айналдырыңыз.",
"helpFFplaySeekBackward10Minutes": "10 минутқа артқа айналдырыңыз.",
"helpFFplaySeekBackward10Seconds": "10 секундқа артқа айналдырыңыз.",
"helpFFplaySeekBackward1Minute": "1 минутқа артқа айналдырыңыз.",
"helpFFplaySeekForward10Seconds": "10 секунд алға айналдырыңыз.",
"helpFFplayToggleFullScreen": "Толық экранға ауысу немесе толық экраннан шығу.",
"helpFFplayToggleMute": "Дыбысты өшіріңіз немесе дыбысты қосыңыз.",
"inProgressQueue": "Орындалуда",
"languageSelectionFormHead": "Тілді ауыстыру",
"languageSelectionHead": "Тілді таңдаңыз",
"licenseLink": "Лицензия туралы ақпарат",
"licenseLinkOther": "Бағдарламада пайдаланылатын басқа өнімдердің лицензиялары",
"menuSettingsLanguage": "Тіл",
"menuSettingsTheme": "Тақырып",
"or": "немесе",
"parameterCheckbox": "Опцияны қосу",
"pathToFfmpeg": "FFmpeg жол:",
"pathToFfplay": "FFplay жол:",
"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 функционалдығы тексерілуде...",
"themesNameDark": "Қараңғы тақырып",
"themesNameDefault": "Әдепкі бойынша",
"themesNameLight": "Жеңіл тақырып",
"titleDownloadLink": "Сіз оны осы жерден жүктей аласыз",
"total": "Барлығы",
"unzipRun": "Орамнан шығарылуда...",
"waitingQueue": "Күту"
}

View File

@@ -0,0 +1,143 @@
{
"AlsoUsedProgram": "Также в программе используется:",
"about": "О программе",
"aboutText": "Простенький интерфейс для консольной утилиты FFmpeg. \nНо я не являюсь автором самой утилиты FFmpeg.",
"addedFilesTitle": "Добавленные файлы",
"autoClearAfterAddingToQueue": "Очищать после добавления в очередь",
"buttonDownloadFFmpeg": "Скачать автоматически FFmpeg",
"buttonForSelectedDirTitle": "Сохранить в папку:",
"cancel": "Отмена",
"changeFFPath": "FFmpeg, FFprobe и FFplay",
"changeLanguage": "Поменять язык",
"checkboxOverwriteOutputFilesTitle": "Разрешить перезаписать файл",
"choose": "выбрать",
"clearAll": "Очистить список",
"completedQueue": "Готово",
"converterVideoFilesSubmitTitle": "Конвертировать",
"converterVideoFilesTitle": "Конвертер видео, аудио и картинок",
"download": "Скачать",
"downloadFFmpegFromSite": "Будет скачано с сайта:",
"downloadRun": "Скачивается...",
"dragAndDropFiles": "перетащить файлы",
"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": "не смогли отконвертировать видео",
"errorDragAndDropFile": "Не все файлы добавились",
"errorFFmpeg": "это не FFmpeg",
"errorFFmpegVersion": "Не смогли определить версию FFmpeg",
"errorFFplay": "это не FFplay",
"errorFFplayVersion": "Не смогли определить версию FFplay",
"errorFFprobe": "это не FFprobe",
"errorFFprobeVersion": "Не смогли определить версию FFprobe",
"errorNoFilesAddedForConversion": "Нет файлов для конвертации",
"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": "Файл:",
"fileQueueTitle": "Очередь",
"formPreset": "Предустановка",
"gratitude": "Благодарность",
"gratitudeText": "Я искренне благодарю вас за неоценимую\n\rи своевременную помощь:",
"help": "Справка",
"helpFFplay": "Клавиши проигрывателя FFplay",
"helpFFplayActivateFrameStepMode": "Активировать покадровый режим.",
"helpFFplayCycleVideoFiltersOrShowModes": "Цикл видеофильтров или режимов показа.",
"helpFFplayDecreaseVolume": "Уменьшить громкость.",
"helpFFplayDescription": "Описание",
"helpFFplayDoubleClickLeftMouseButton": "двойной щелчок\nлевой кнопкой мыши",
"helpFFplayIncreaseVolume": "Увеличить громкость.",
"helpFFplayKeyDown": "вниз",
"helpFFplayKeyHoldS": "держать S",
"helpFFplayKeyLeft": "лево",
"helpFFplayKeyRight": "право",
"helpFFplayKeySpace": "SPACE (пробел)",
"helpFFplayKeyUp": "вверх",
"helpFFplayKeys": "Клавиши",
"helpFFplayPause": "Поставить на паузу или продолжить проигрывать.",
"helpFFplayQuit": "Закрыть проигрыватель.",
"helpFFplaySeekBForward10Minutes": "Перемотать вперёд на 10 минут.",
"helpFFplaySeekBForward1Minute": "Перемотать вперёд на 1 минуту.",
"helpFFplaySeekBackward10Minutes": "Перемотать назад на 10 минут.",
"helpFFplaySeekBackward10Seconds": "Перемотать назад на 10 секунд.",
"helpFFplaySeekBackward1Minute": "Перемотать назад на 1 минуту.",
"helpFFplaySeekForward10Seconds": "Перемотать вперёд на 10 секунд.",
"helpFFplayToggleFullScreen": "Переключиться на полный экран или выйти с полного экрана.",
"helpFFplayToggleMute": "Отключить звук или включить звук.",
"inProgressQueue": "Выполняется",
"languageSelectionFormHead": "Переключить язык",
"languageSelectionHead": "Выберите язык",
"licenseLink": "Сведения о лицензии",
"licenseLinkOther": "Лицензии от других продуктов, которые используются в программе",
"menuSettingsLanguage": "Язык",
"menuSettingsTheme": "Тема",
"or": "или",
"parameterCheckbox": "Включить параметр",
"pathToFfmpeg": "Путь к FFmpeg:",
"pathToFfplay": "Путь к FFplay:",
"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 на работоспособность...",
"themesNameDark": "Тёмная",
"themesNameDefault": "По умолчанию",
"themesNameLight": "Светлая",
"titleDownloadLink": "Скачать можно от сюда",
"total": "Всего",
"unzipRun": "Распаковывается...",
"waitingQueue": "В очереди"
}

View File

@@ -0,0 +1,45 @@
{
"Advanced": "Advanced",
"Cancel": "Cancel",
"Confirm": "Confirm",
"Copy": "Copy",
"Create Folder": "Create Folder",
"Cut": "Cut",
"Enter filename": "Enter filename",
"Error": "Error",
"Favourites": "Favourites",
"File": "File",
"Folder": "Folder",
"New Folder": "New Folder",
"No": "No",
"OK": "OK",
"Open": "Open",
"Paste": "Paste",
"Quit": "Quit",
"Redo": "Redo",
"Save": "Save",
"Select all": "Select all",
"Show Hidden Files": "Show Hidden Files",
"Undo": "Undo",
"Yes": "Yes",
"file.name": {
"other": "Name"
},
"file.parent": {
"other": "Parent"
},
"friday": "Friday",
"friday.short": "Fri",
"monday": "Monday",
"monday.short": "Mon",
"saturday": "Saturday",
"saturday.short": "Sat",
"sunday": "Sunday",
"sunday.short": "Sun",
"thursday": "Thursday",
"thursday.short": "Thu",
"tuesday": "Tuesday",
"tuesday.short": "Tue",
"wednesday": "Wednesday",
"wednesday.short": "Wed"
}

View File

@@ -0,0 +1,45 @@
{
"Advanced": "Кеңейтілген",
"Cancel": "Бас тарту",
"Confirm": "Растау",
"Copy": "Көшіру",
"Create Folder": "Қалта жасау",
"Cut": "Кесу",
"Enter filename": "Файл атауын енгізіңіз",
"Error": "Қате",
"Favourites": "Таңдаулылар",
"File": "Файл",
"Folder": "Қалта",
"New Folder": "Жаңа қалта",
"No": "Жоқ",
"OK": "ОК",
"Open": "Ашу",
"Paste": "Кірістіру",
"Quit": "Шығу",
"Redo": "Қайталау",
"Save": "Сақтау",
"Select all": "Барлығын таңдаңыз",
"Show Hidden Files": "Жасырын файлдарды көрсету",
"Undo": "Бас тарту",
"Yes": "Иә",
"file.name": {
"other": "Аты"
},
"file.parent": {
"other": "Жоғары"
},
"friday": "Жұма",
"friday.short": "Жұ",
"monday": "Дүйсенбі",
"monday.short": "Дү",
"saturday": "Сенбі",
"saturday.short": "Сен",
"sunday": "Жексенбі",
"sunday.short": "Же",
"thursday": "Сейсенбі",
"thursday.short": "Се",
"tuesday": "Бейсенбі",
"tuesday.short": "Бе",
"wednesday": "Сәрсенбі",
"wednesday.short": "Сә"
}

View File

@@ -0,0 +1,45 @@
{
"Advanced": "Расширенные",
"Cancel": "Отмена",
"Confirm": "Подтвердить",
"Copy": "Копировать",
"Create Folder": "Создать папку",
"Cut": "Вырезать",
"Enter filename": "Введите имя файла",
"Error": "Ошибка",
"Favourites": "Избранное",
"File": "Файл",
"Folder": "Папка",
"New Folder": "Новая папка",
"No": "Нет",
"OK": "ОК",
"Open": "Открыть",
"Paste": "Вставить",
"Quit": "Выйти",
"Redo": "Повторить",
"Save": "Сохранить",
"Select all": "Выбрать всё",
"Show Hidden Files": "Показать скрытые файлы",
"Undo": "Отменить",
"Yes": "Да",
"file.name": {
"other": "Имя"
},
"file.parent": {
"other": "Вверх"
},
"friday": "Пятница",
"friday.short": "Пт",
"monday": "Понедельник",
"monday.short": "Пн",
"saturday": "Суббота",
"saturday.short": "Сб",
"sunday": "Воскресенье",
"sunday.short": "Вс",
"thursday": "Вторник",
"thursday.short": "Вт",
"tuesday": "Четверг",
"tuesday.short": "Чт",
"wednesday": "Среда",
"wednesday.short": "Ср"
}

View File

@@ -83,5 +83,7 @@ func (w Window) GetLayout() LayoutContract {
} }
func (w Window) SetOnDropped(callback func(position fyne.Position, uris []fyne.URI)) { func (w Window) SetOnDropped(callback func(position fyne.Position, uris []fyne.URI)) {
fyne.Do(func() {
w.windowFyne.SetOnDropped(callback) w.windowFyne.SetOnDropped(callback)
})
} }

View File

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

View File

@@ -1,423 +0,0 @@
[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..."
[dragAndDrop1File]
hash = "sha1-7259670822df1cc92ef5f06ed3c0e9407746975a"
other = "drag and drop 1 file"
[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'"
[errorDragAndDrop1File]
hash = "sha1-a8edb5cbd622f3ce4ec07a2377e22ec5fad4491b"
other = "You can only drag and drop 1 file."
[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"
[errorIsFolder]
hash = "sha1-f937d090b6e320957514d850657cdf2f911dc6aa"
other = "You can only drag and drop a file"
[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"
[gratitude]
hash = "sha1-51968fc38e53a9a11c861126c62404674fd6096f"
other = "Gratitude"
[gratitudeText]
hash = "sha1-cb343e4d39ca31e6da6f72b9394cc915cb7d1258"
other = "I sincerely thank you for your invaluable\n\r and timely assistance:"
[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"
[or]
hash = "sha1-30bb0333ca1583110e4ced513b5d2455b86f529b"
other = "or"
[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"

View File

@@ -1,423 +0,0 @@
[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 = "Жүктеп алынуда..."
[dragAndDrop1File]
hash = "sha1-7259670822df1cc92ef5f06ed3c0e9407746975a"
other = "1 файлды сүйреңіз"
[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' файлын жасау мүмкін болмады"
[errorDragAndDrop1File]
hash = "sha1-a8edb5cbd622f3ce4ec07a2377e22ec5fad4491b"
other = "Тек 1 файлды сүйреп апаруға болады"
[errorFFmpeg]
hash = "sha1-ccf0b95c0d1b392dc215258d917eb4e5d0b88ed0"
other = "бұл FFmpeg емес"
[errorFFmpegVersion]
hash = "sha1-9a4148d42186b6b32cf83bef726e23022c53283f"
other = "FFmpeg нұсқасын анықтау мүмкін болмады"
[errorFFprobe]
hash = "sha1-86d1b0b4c4ccd6a4f71e758fc67ce11aff4ba9b8"
other = "бұл FFprobe емес"
[errorFFprobeVersion]
hash = "sha1-da7b37d7df3fafbd153665b13888413d52b24c17"
other = "FFprobe нұсқасын анықтау мүмкін болмады"
[errorIsFolder]
hash = "sha1-f937d090b6e320957514d850657cdf2f911dc6aa"
other = "Тек файлды сүйреп апаруға болады"
[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 = "Алдын ала орнатылған"
[gratitude]
hash = "sha1-51968fc38e53a9a11c861126c62404674fd6096f"
other = "Алғыс"
[gratitudeText]
hash = "sha1-cb343e4d39ca31e6da6f72b9394cc915cb7d1258"
other = "Сізге баға жетпес және уақтылы көмектескеніңіз\n\r үшін шын жүректен алғыс айтамын:"
[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 = "Бағдарламада пайдаланылатын басқа өнімдердің лицензиялары"
[or]
hash = "sha1-30bb0333ca1583110e4ced513b5d2455b86f529b"
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 = "Күту"

View File

@@ -1,106 +0,0 @@
AlsoUsedProgram = "Также в программе используется:"
about = "О программе"
aboutText = "Простенький интерфейс для консольной утилиты FFmpeg. \nНо я не являюсь автором самой утилиты FFmpeg."
buttonDownloadFFmpeg = "Скачать автоматически FFmpeg"
buttonForSelectedDirTitle = "Сохранить в папку:"
cancel = "Отмена"
changeFFPath = "FFmpeg и FFprobe"
changeLanguage = "Поменять язык"
checkboxOverwriteOutputFilesTitle = "Разрешить перезаписать файл"
choose = "выбрать"
completedQueue = "Готово"
converterVideoFilesSubmitTitle = "Конвертировать"
converterVideoFilesTitle = "Конвертер видео, аудио и картинок"
download = "Скачать"
downloadFFmpegFromSite = "Будет скачано с сайта:"
downloadRun = "Скачивается..."
dragAndDrop1File = "перетащить 1 файл"
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'"
errorDragAndDrop1File = "Можно перетащить только 1 файл"
errorFFmpeg = "это не FFmpeg"
errorFFmpegVersion = "Не смогли определить версию FFmpeg"
errorFFprobe = "это не FFprobe"
errorFFprobeVersion = "Не смогли определить версию FFprobe"
errorIsFolder = "Можно перетаскивать только файл"
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 = "Предустановка"
gratitude = "Благодарность"
gratitudeText = "Я искренне благодарю вас за неоценимую\n\rи своевременную помощь:"
help = "Справка"
inProgressQueue = "Выполняется"
languageSelectionFormHead = "Переключить язык"
languageSelectionHead = "Выберите язык"
licenseLink = "Сведения о лицензии"
licenseLinkOther = "Лицензии от других продуктов, которые используются в программе"
or = "или"
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 = "В очереди"

View File

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

View File

@@ -4,7 +4,6 @@ import (
"fyne.io/fyne/v2" "fyne.io/fyne/v2"
"fyne.io/fyne/v2/widget" "fyne.io/fyne/v2/widget"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel" "git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel"
"github.com/nicksnyder/go-i18n/v2/i18n"
) )
type ViewContract interface { type ViewContract interface {
@@ -35,13 +34,11 @@ func (v View) LanguageSelection(funcSelected func(lang kernel.Lang)) {
block.SetText(languages[i].Title) block.SetText(languages[i].Title)
}) })
listView.OnSelected = func(id widget.ListItemID) { listView.OnSelected = func(id widget.ListItemID) {
_ = v.app.GetLocalizerService().SetCurrentLanguage(languages[id]) _ = v.app.GetLocalizerService().SetCurrentLanguage(languages[id], true)
funcSelected(languages[id]) funcSelected(languages[id])
} }
messageHead := v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ messageHead := v.app.GetLocalizerService().GetMessage("languageSelectionHead")
MessageID: "languageSelectionHead",
})
v.app.GetWindow().SetContent(widget.NewCard(messageHead, "", listView)) v.app.GetWindow().SetContent(widget.NewCard(messageHead, "", listView))
} }
@@ -58,17 +55,15 @@ func LanguageSelectionForm(localizerService kernel.LocalizerContract, funcSelect
func(i widget.ListItemID, o fyne.CanvasObject) { func(i widget.ListItemID, o fyne.CanvasObject) {
block := o.(*widget.Label) block := o.(*widget.Label)
block.SetText(languages[i].Title) block.SetText(languages[i].Title)
if languages[i].Code == currentLanguage.Lang.Code { if languages[i].Code == currentLanguage.Code {
block.TextStyle = fyne.TextStyle{Bold: true} block.TextStyle = fyne.TextStyle{Bold: true}
} }
}) })
listView.OnSelected = func(id widget.ListItemID) { listView.OnSelected = func(id widget.ListItemID) {
_ = localizerService.SetCurrentLanguage(languages[id]) _ = localizerService.SetCurrentLanguage(languages[id], true)
funcSelected(languages[id]) funcSelected(languages[id])
} }
messageHead := localizerService.GetMessage(&i18n.LocalizeConfig{ messageHead := localizerService.GetMessage("languageSelectionFormHead")
MessageID: "languageSelectionFormHead",
})
return widget.NewCard(messageHead, "", listView) return widget.NewCard(messageHead, "", listView)
} }

92
main.go
View File

@@ -1,120 +1,76 @@
package main package main
import ( import (
"errors" _ "embed"
"fyne.io/fyne/v2" "fyne.io/fyne/v2"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/convertor" "git.kor-elf.net/kor-elf/gui-for-ffmpeg/convertor"
dberror "git.kor-elf.net/kor-elf/gui-for-ffmpeg/db"
error2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/error" 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/handler"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/resources"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel" "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/localizer"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/menu" "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" "git.kor-elf.net/kor-elf/gui-for-ffmpeg/setting"
"go.etcd.io/bbolt" "git.kor-elf.net/kor-elf/gui-for-ffmpeg/theme"
"golang.org/x/text/language"
"os"
"time"
) )
var application kernel.AppContract var application kernel.AppContract
var ffPathUtilities *kernel.FFPathUtilities var ffPathUtilities *kernel.FFPathUtilities
func init() { func init() {
iconResource, _ := fyne.LoadResourceFromPath("icon.png")
appMetadata := &fyne.AppMetadata{ appMetadata := &fyne.AppMetadata{
ID: "net.kor-elf.projects.gui-for-ffmpeg", ID: "net.kor-elf.projects.gui-for-ffmpeg",
Name: "GUI for FFmpeg", Name: "GUI for FFmpeg",
Version: "0.8.0", Version: "0.9.0",
Icon: iconResource, Icon: resources.IconAppLogoResource(),
} }
localizerService, err := kernel.NewLocalizer("languages", language.Russian) ffPathUtilities = &kernel.FFPathUtilities{FFmpeg: "", FFprobe: "", FFplay: ""}
if err != nil {
kernel.PanicErrorLang(err, appMetadata)
}
ffPathUtilities = &kernel.FFPathUtilities{FFmpeg: "", FFprobe: ""}
convertorService := kernel.NewService(ffPathUtilities) convertorService := kernel.NewService(ffPathUtilities)
ffplayService := kernel.NewFFplay(ffPathUtilities)
queue := kernel.NewQueueList() queue := kernel.NewQueueList()
application = kernel.NewApp( application = kernel.NewApp(
appMetadata, appMetadata,
localizerService,
queue, queue,
kernel.NewQueueLayoutObject(queue, localizerService), ffplayService,
convertorService, convertorService,
) )
} }
func main() { func main() {
errorView := error2.NewView(application) errorView := error2.NewView(application)
if canCreateFile("data/database.db") != true {
errorView.PanicErrorWriteDirectoryData()
application.GetWindow().ShowAndRun()
return
}
db, err := bbolt.Open("data/database.db", 0600, &bbolt.Options{Timeout: 3 * time.Second}) settingRepository := setting.NewRepository(application.GetAppFyne())
if err != nil {
errorView.PanicError(err)
application.GetWindow().ShowAndRun()
return
}
defer db.Close()
err = migration.Run(db)
if err != nil {
errorView.PanicError(err)
application.GetWindow().ShowAndRun()
return
}
settingRepository := setting.NewRepository(db)
settingDirectoryForSaving := setting.NewSettingDirectoryForSaving(settingRepository) settingDirectoryForSaving := setting.NewSettingDirectoryForSaving(settingRepository)
convertorRepository := convertor.NewRepository(settingRepository) convertorRepository := convertor.NewRepository(settingRepository)
pathFFmpeg, err := convertorRepository.GetPathFfmpeg() ffPathUtilities.FFmpeg = convertorRepository.GetPathFfmpeg()
if err != nil && errors.Is(err, dberror.ErrRecordNotFound) == false { ffPathUtilities.FFprobe = convertorRepository.GetPathFfprobe()
errorView.PanicError(err) ffPathUtilities.FFplay = convertorRepository.GetPathFfplay()
application.GetWindow().ShowAndRun()
return
}
ffPathUtilities.FFmpeg = pathFFmpeg
pathFFprobe, err := convertorRepository.GetPathFfprobe()
if err != nil && errors.Is(err, dberror.ErrRecordNotFound) == false {
errorView.PanicError(err)
application.GetWindow().ShowAndRun()
return
}
ffPathUtilities.FFprobe = pathFFprobe
application.RunConvertor() application.RunConvertor()
defer application.AfterClosing() defer application.AfterClosing()
localizerView := localizer.NewView(application) localizerView := localizer.NewView(application)
convertorView := convertor.NewView(application) convertorView := convertor.NewView(application)
convertorHandler := handler.NewConvertorHandler(application, convertorView, errorView, convertorRepository, settingDirectoryForSaving) itemsToConvertService := kernel.NewItemsToConvert(
application.GetWindow().GetLayout().GetRightTabs().GetAddedFilesContainer(),
application.GetFFplayService(),
application.GetLocalizerService(),
)
convertorHandler := handler.NewConvertorHandler(application, convertorView, errorView, convertorRepository, settingDirectoryForSaving, itemsToConvertService)
themeRepository := theme.NewRepository(settingRepository)
themeService := theme.NewTheme(application, themeRepository)
localizerRepository := localizer.NewRepository(settingRepository)
menuView := menu.NewView(application) menuView := menu.NewView(application)
mainMenu := handler.NewMenuHandler(application, convertorHandler, menuView, localizerView, localizerRepository) menuSettingView := menu.NewViewSetting(application, themeService)
mainMenu := handler.NewMenuHandler(application, convertorHandler, menuView, menuSettingView, localizerView, themeService)
mainHandler := handler.NewMainHandler(application, convertorHandler, mainMenu, localizerRepository) mainHandler := handler.NewMainHandler(application, convertorHandler, mainMenu)
mainHandler.Start() mainHandler.Start()
application.GetWindow().SetMainMenu(mainMenu.GetMainMenu()) application.GetWindow().SetMainMenu(mainMenu.GetMainMenu())
application.GetWindow().ShowAndRun() application.GetWindow().ShowAndRun()
} }
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

@@ -4,16 +4,18 @@ import (
"fyne.io/fyne/v2" "fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas" "fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container" "fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget" "fyne.io/fyne/v2/widget"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/internal/resources"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel" "git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel"
"github.com/nicksnyder/go-i18n/v2/i18n"
"golang.org/x/image/colornames" "golang.org/x/image/colornames"
"net/url" "net/url"
) )
type ViewContract interface { type ViewContract interface {
About(ffmpegVersion string, ffprobeVersion string) About(ffmpegVersion string, ffprobeVersion string, ffplayVersion string)
Gratitude() Gratitude()
HelpFFplay()
} }
type View struct { type View struct {
@@ -27,19 +29,15 @@ func NewView(app kernel.AppContract) *View {
} }
func (v View) Gratitude() { func (v View) Gratitude() {
view := v.app.GetAppFyne().NewWindow(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ view := v.app.GetAppFyne().NewWindow(v.app.GetLocalizerService().GetMessage("gratitude"))
MessageID: "gratitude",
}))
view.Resize(fyne.Size{Width: 500, Height: 400}) view.Resize(fyne.Size{Width: 500, Height: 400})
view.SetFixedSize(true) view.SetFixedSize(true)
image := canvas.NewImageFromFile("icon.png") image := canvas.NewImageFromResource(resources.IconAppLogoResource())
image.SetMinSize(fyne.Size{Width: 100, Height: 100}) image.SetMinSize(fyne.Size{Width: 100, Height: 100})
image.FillMode = canvas.ImageFillContain image.FillMode = canvas.ImageFillContain
gratitude := canvas.NewText(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ gratitude := canvas.NewText(v.app.GetLocalizerService().GetMessage("gratitude"), colornames.Darkgreen)
MessageID: "gratitude",
}), colornames.Darkgreen)
gratitude.TextStyle = fyne.TextStyle{Bold: true} gratitude.TextStyle = fyne.TextStyle{Bold: true}
gratitude.TextSize = 20 gratitude.TextSize = 20
@@ -47,23 +45,19 @@ func (v View) Gratitude() {
container.NewScroll(container.NewVBox( container.NewScroll(container.NewVBox(
container.NewBorder(nil, nil, container.NewVBox(image), nil, container.NewVBox( container.NewBorder(nil, nil, container.NewVBox(image), nil, container.NewVBox(
gratitude, gratitude,
widget.NewLabel(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ widget.NewLabel(v.app.GetLocalizerService().GetMessage("gratitudeText")),
MessageID: "gratitudeText",
})),
widget.NewLabel("Екатерина"), widget.NewLabel("Екатерина"),
widget.NewLabel("Евгений"), widget.NewLabel("Евгений"),
), )),
))), )),
) )
view.CenterOnScreen() view.CenterOnScreen()
view.Show() view.Show()
} }
func (v View) About(ffmpegVersion string, ffprobeVersion string) { func (v View) About(ffmpegVersion string, ffprobeVersion string, ffplayVersion string) {
view := v.app.GetAppFyne().NewWindow(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ view := v.app.GetAppFyne().NewWindow(v.app.GetLocalizerService().GetMessage("about"))
MessageID: "about",
}))
view.Resize(fyne.Size{Width: 793, Height: 550}) view.Resize(fyne.Size{Width: 793, Height: 550})
view.SetFixedSize(true) view.SetFixedSize(true)
@@ -71,54 +65,51 @@ func (v View) About(ffmpegVersion string, ffprobeVersion string) {
programmName.TextStyle = fyne.TextStyle{Bold: true} programmName.TextStyle = fyne.TextStyle{Bold: true}
programmName.TextSize = 20 programmName.TextSize = 20
programmLink := widget.NewHyperlink(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ programmLink := widget.NewHyperlink(
MessageID: "programmLink", v.app.GetLocalizerService().GetMessage("programmLink"),
}), &url.URL{ &url.URL{
Scheme: "https", Scheme: "https",
Host: "gui-for-ffmpeg.projects.kor-elf.net", Host: "gui-for-ffmpeg.projects.kor-elf.net",
Path: "/", Path: "/",
}) },
)
licenseLink := widget.NewHyperlink(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ licenseLink := widget.NewHyperlink(
MessageID: "licenseLink", v.app.GetLocalizerService().GetMessage("licenseLink"),
}), &url.URL{ &url.URL{
Scheme: "https", Scheme: "https",
Host: "git.kor-elf.net", Host: "git.kor-elf.net",
Path: "kor-elf/gui-for-ffmpeg/src/branch/main/LICENSE", Path: "kor-elf/gui-for-ffmpeg/src/branch/main/LICENSE",
}) },
)
licenseLinkOther := widget.NewHyperlink(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ licenseLinkOther := widget.NewHyperlink(v.app.GetLocalizerService().GetMessage(
MessageID: "licenseLinkOther", "licenseLinkOther"),
}), &url.URL{ &url.URL{
Scheme: "https", Scheme: "https",
Host: "git.kor-elf.net", Host: "git.kor-elf.net",
Path: "kor-elf/gui-for-ffmpeg/src/branch/main/LICENSE-3RD-PARTY.txt", 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,
}, },
})) )
programmVersion := widget.NewRichTextFromMarkdown(
v.app.GetLocalizerService().GetMessage(
"programmVersion",
map[string]any{"Version": v.app.GetAppFyne().Metadata().Version},
),
)
aboutText := widget.NewRichText( aboutText := widget.NewRichText(
&widget.TextSegment{ &widget.TextSegment{
Text: v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ Text: v.app.GetLocalizerService().GetMessage("aboutText"),
MessageID: "aboutText",
}),
}, },
) )
image := canvas.NewImageFromFile("icon.png") image := canvas.NewImageFromResource(resources.IconAppLogoResource())
image.SetMinSize(fyne.Size{Width: 100, Height: 100}) image.SetMinSize(fyne.Size{Width: 100, Height: 100})
image.FillMode = canvas.ImageFillContain image.FillMode = canvas.ImageFillContain
ffmpegTrademark := widget.NewRichTextFromMarkdown(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ ffmpegTrademark := widget.NewRichTextFromMarkdown(v.app.GetLocalizerService().GetMessage("ffmpegTrademark"))
MessageID: "ffmpegTrademark", ffmpegLGPL := widget.NewRichTextFromMarkdown(v.app.GetLocalizerService().GetMessage("ffmpegLGPL"))
}))
ffmpegLGPL := widget.NewRichTextFromMarkdown(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{
MessageID: "ffmpegLGPL",
}))
view.SetContent( view.SetContent(
container.NewScroll(container.NewVBox( container.NewScroll(container.NewVBox(
@@ -134,15 +125,111 @@ func (v View) About(ffmpegVersion string, ffprobeVersion string) {
)), )),
v.getAboutFfmpeg(ffmpegVersion), v.getAboutFfmpeg(ffmpegVersion),
v.getAboutFfprobe(ffprobeVersion), v.getAboutFfprobe(ffprobeVersion),
widget.NewCard(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ v.getAboutFfplay(ffplayVersion),
MessageID: "AlsoUsedProgram", widget.NewCard(v.app.GetLocalizerService().GetMessage("AlsoUsedProgram"), "", v.getOther()),
}), "", v.getOther()),
)), )),
) )
view.CenterOnScreen() view.CenterOnScreen()
view.Show() view.Show()
} }
func (v View) HelpFFplay() {
view := v.app.GetAppFyne().NewWindow(v.app.GetLocalizerService().GetMessage("helpFFplay"))
view.Resize(fyne.Size{Width: 800, Height: 550})
view.SetFixedSize(true)
data := [][]string{
[]string{
v.app.GetLocalizerService().GetMessage("helpFFplayKeys"),
v.app.GetLocalizerService().GetMessage("helpFFplayDescription"),
},
[]string{
"Q, ESC",
v.app.GetLocalizerService().GetMessage("helpFFplayQuit"),
},
[]string{
"F, " + v.app.GetLocalizerService().GetMessage("helpFFplayDoubleClickLeftMouseButton"),
v.app.GetLocalizerService().GetMessage("helpFFplayToggleFullScreen"),
},
[]string{
"P, " +
v.app.GetLocalizerService().GetMessage("helpFFplayKeySpace"),
v.app.GetLocalizerService().GetMessage("helpFFplayPause"),
},
[]string{
"M",
v.app.GetLocalizerService().GetMessage("helpFFplayToggleMute"),
},
[]string{
"9, /",
v.app.GetLocalizerService().GetMessage("helpFFplayDecreaseVolume"),
},
[]string{
"0, *",
v.app.GetLocalizerService().GetMessage("helpFFplayIncreaseVolume"),
},
[]string{
v.app.GetLocalizerService().GetMessage("helpFFplayKeyLeft"),
v.app.GetLocalizerService().GetMessage("helpFFplaySeekBackward10Seconds"),
},
[]string{
v.app.GetLocalizerService().GetMessage("helpFFplayKeyRight"),
v.app.GetLocalizerService().GetMessage("helpFFplaySeekForward10Seconds"),
},
[]string{
v.app.GetLocalizerService().GetMessage("helpFFplayKeyDown"),
v.app.GetLocalizerService().GetMessage("helpFFplaySeekBackward1Minute"),
},
[]string{
v.app.GetLocalizerService().GetMessage("helpFFplayKeyUp"),
v.app.GetLocalizerService().GetMessage("helpFFplaySeekBForward1Minute"),
},
[]string{
"Page Down",
v.app.GetLocalizerService().GetMessage("helpFFplaySeekBackward10Minutes"),
},
[]string{
"Page Up",
v.app.GetLocalizerService().GetMessage("helpFFplaySeekBForward10Minutes"),
},
[]string{
"S, " + v.app.GetLocalizerService().GetMessage("helpFFplayKeyHoldS"),
v.app.GetLocalizerService().GetMessage("helpFFplayActivateFrameStepMode"),
},
[]string{
"W",
v.app.GetLocalizerService().GetMessage("helpFFplayCycleVideoFiltersOrShowModes"),
},
}
list := widget.NewTable(
func() (int, int) {
return len(data), len(data[0])
},
func() fyne.CanvasObject {
return widget.NewLabel("")
},
func(i widget.TableCellID, o fyne.CanvasObject) {
if i.Row == 0 {
o.(*widget.Label).TextStyle.Bold = true
o.(*widget.Label).SizeName = theme.SizeNameSubHeadingText
}
if i.Col == 0 {
o.(*widget.Label).TextStyle.Bold = true
}
o.(*widget.Label).SetText(data[i.Row][i.Col])
})
list.SetRowHeight(0, 40)
list.SetColumnWidth(0, 200)
list.SetColumnWidth(1, 585)
list.SetRowHeight(2, 55)
view.SetContent(
container.NewScroll(list),
)
view.CenterOnScreen()
view.Show()
}
func (v View) getCopyright() *widget.RichText { func (v View) getCopyright() *widget.RichText {
return widget.NewRichTextFromMarkdown("Copyright (c) 2024 **[Leonid Nikitin (kor-elf)](https://git.kor-elf.net/kor-elf/)**.") return widget.NewRichTextFromMarkdown("Copyright (c) 2024 **[Leonid Nikitin (kor-elf)](https://git.kor-elf.net/kor-elf/)**.")
} }
@@ -152,17 +239,13 @@ func (v View) getAboutFfmpeg(version string) *fyne.Container {
programmName.TextStyle = fyne.TextStyle{Bold: true} programmName.TextStyle = fyne.TextStyle{Bold: true}
programmName.TextSize = 20 programmName.TextSize = 20
programmLink := widget.NewHyperlink(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ programmLink := widget.NewHyperlink(v.app.GetLocalizerService().GetMessage("programmLink"), &url.URL{
MessageID: "programmLink",
}), &url.URL{
Scheme: "https", Scheme: "https",
Host: "ffmpeg.org", Host: "ffmpeg.org",
Path: "", Path: "",
}) })
licenseLink := widget.NewHyperlink(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ licenseLink := widget.NewHyperlink(v.app.GetLocalizerService().GetMessage("licenseLink"), &url.URL{
MessageID: "licenseLink",
}), &url.URL{
Scheme: "https", Scheme: "https",
Host: "ffmpeg.org", Host: "ffmpeg.org",
Path: "legal.html", Path: "legal.html",
@@ -182,17 +265,39 @@ func (v View) getAboutFfprobe(version string) *fyne.Container {
programmName.TextStyle = fyne.TextStyle{Bold: true} programmName.TextStyle = fyne.TextStyle{Bold: true}
programmName.TextSize = 20 programmName.TextSize = 20
programmLink := widget.NewHyperlink(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ programmLink := widget.NewHyperlink(v.app.GetLocalizerService().GetMessage("programmLink"), &url.URL{
MessageID: "programmLink",
}), &url.URL{
Scheme: "https", Scheme: "https",
Host: "ffmpeg.org", Host: "ffmpeg.org",
Path: "ffprobe.html", Path: "ffprobe.html",
}) })
licenseLink := widget.NewHyperlink(v.app.GetLocalizerService().GetMessage(&i18n.LocalizeConfig{ licenseLink := widget.NewHyperlink(v.app.GetLocalizerService().GetMessage("licenseLink"), &url.URL{
MessageID: "licenseLink", Scheme: "https",
}), &url.URL{ 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) getAboutFfplay(version string) *fyne.Container {
programmName := canvas.NewText(" FFplay", colornames.Darkgreen)
programmName.TextStyle = fyne.TextStyle{Bold: true}
programmName.TextSize = 20
programmLink := widget.NewHyperlink(v.app.GetLocalizerService().GetMessage("programmLink"), &url.URL{
Scheme: "https",
Host: "ffmpeg.org",
Path: "ffplay.html",
})
licenseLink := widget.NewHyperlink(v.app.GetLocalizerService().GetMessage("licenseLink"), &url.URL{
Scheme: "https", Scheme: "https",
Host: "ffmpeg.org", Host: "ffmpeg.org",
Path: "legal.html", Path: "legal.html",

101
menu/view_setting.go Normal file
View File

@@ -0,0 +1,101 @@
package menu
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/widget"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/theme"
"image/color"
)
type ViewSettingContract interface {
Main(
save func(*SettingForm) error,
cancel func(),
)
}
type SettingForm struct {
Language kernel.Lang
ThemeInfo theme.ThemeInfoContract
}
type ViewSetting struct {
app kernel.AppContract
themeService theme.ThemeContract
}
func NewViewSetting(app kernel.AppContract, themeService theme.ThemeContract) *ViewSetting {
return &ViewSetting{
app: app,
themeService: themeService,
}
}
func (v ViewSetting) Main(save func(*SettingForm) error, cancel func()) {
errorMessage := canvas.NewText("", color.RGBA{R: 255, G: 0, B: 0, A: 255})
errorMessage.TextSize = 16
errorMessage.TextStyle = fyne.TextStyle{Bold: true}
viewSettingForm := &SettingForm{
Language: v.app.GetLocalizerService().GetCurrentLanguage(),
ThemeInfo: v.themeService.GetCurrentThemeInfo(),
}
languageItems := []string{}
langByTitle := map[string]kernel.Lang{}
for _, language := range v.app.GetLocalizerService().GetLanguages() {
languageItems = append(languageItems, language.Title)
langByTitle[language.Title] = language
}
selectLanguage := widget.NewSelect(languageItems, func(s string) {
if lang, ok := langByTitle[s]; ok {
viewSettingForm.Language = lang
}
})
selectLanguage.Selected = v.app.GetLocalizerService().GetCurrentLanguage().Title
themeItems := []string{}
themeByTitle := map[string]theme.ThemeInfoContract{}
for _, themeInfo := range v.themeService.List() {
themeItems = append(themeItems, themeInfo.GetTitle())
themeByTitle[themeInfo.GetTitle()] = themeInfo
}
selectTheme := widget.NewSelect(themeItems, func(s string) {
if themeInfo, ok := themeByTitle[s]; ok {
viewSettingForm.ThemeInfo = themeInfo
}
})
selectTheme.Selected = v.themeService.GetCurrentThemeInfo().GetTitle()
form := &widget.Form{
Items: []*widget.FormItem{
{
Text: v.app.GetLocalizerService().GetMessage("menuSettingsLanguage"),
Widget: selectLanguage,
},
{
Text: v.app.GetLocalizerService().GetMessage("menuSettingsTheme"),
Widget: selectTheme,
},
{
Widget: errorMessage,
},
},
SubmitText: v.app.GetLocalizerService().GetMessage("save"),
OnSubmit: func() {
err := save(viewSettingForm)
if err != nil {
errorMessage.Text = err.Error()
}
},
}
if cancel != nil {
form.OnCancel = cancel
form.CancelText = v.app.GetLocalizerService().GetMessage("cancel")
}
messageHead := v.app.GetLocalizerService().GetMessage("settings")
v.app.GetWindow().SetContent(widget.NewCard(messageHead, "", form))
}

View File

@@ -1,12 +0,0 @@
package migration
import (
"go.etcd.io/bbolt"
)
func Run(db *bbolt.DB) error {
return db.Update(func(tx *bbolt.Tx) error {
_, err := tx.CreateBucketIfNotExists([]byte("settings"))
return err
})
}

View File

@@ -1,8 +1,8 @@
package setting package setting
type DirectoryForSavingContract interface { type DirectoryForSavingContract interface {
GetDirectoryForSaving() (string, error) GetDirectoryForSaving() string
SaveDirectoryForSaving(path string) (Setting, error) SaveDirectoryForSaving(path string) Setting
} }
type DirectoryForSaving struct { type DirectoryForSaving struct {
@@ -13,10 +13,10 @@ func NewSettingDirectoryForSaving(settingRepository RepositoryContract) *Directo
return &DirectoryForSaving{settingRepository: settingRepository} return &DirectoryForSaving{settingRepository: settingRepository}
} }
func (setting DirectoryForSaving) GetDirectoryForSaving() (string, error) { func (setting DirectoryForSaving) GetDirectoryForSaving() string {
return setting.settingRepository.GetValue("directoryForSaving") return setting.settingRepository.GetValue("directoryForSaving")
} }
func (setting DirectoryForSaving) SaveDirectoryForSaving(path string) (Setting, error) { func (setting DirectoryForSaving) SaveDirectoryForSaving(path string) Setting {
return setting.settingRepository.CreateOrUpdate("directoryForSaving", path) return setting.settingRepository.CreateOrUpdate("directoryForSaving", path)
} }

View File

@@ -1,6 +1,6 @@
package setting package setting
type Setting struct { type Setting struct {
Code string `json:"code"` Code string
Value string `json:"value"` Value string
} }

View File

@@ -1,82 +1,39 @@
package setting package setting
import ( import (
"encoding/json" "fyne.io/fyne/v2"
"errors"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/db"
"go.etcd.io/bbolt"
) )
type RepositoryContract interface { type RepositoryContract interface {
Create(setting Setting) (Setting, error) Create(setting Setting) Setting
CreateOrUpdate(code string, value string) (Setting, error) CreateOrUpdate(code string, value string) Setting
GetValue(code string) (value string, err error) GetValue(code string) string
} }
type Repository struct { type Repository struct {
db *bbolt.DB app fyne.App
} }
func NewRepository(db *bbolt.DB) *Repository { func NewRepository(app fyne.App) *Repository {
return &Repository{db} return &Repository{
app: app,
}
} }
func (r Repository) GetValue(code string) (value string, err error) { func (r Repository) GetValue(code string) string {
var setting Setting return r.app.Preferences().String(code)
err = r.db.View(func(tx *bbolt.Tx) error {
b := tx.Bucket([]byte("settings"))
if b == nil {
return errors.New("bucket 'settings' not found")
}
val := b.Get([]byte(code))
if val == nil {
return db.ErrRecordNotFound
}
return json.Unmarshal(val, &setting)
})
if err != nil {
return "", err
} }
return setting.Value, nil func (r Repository) Create(setting Setting) Setting {
r.app.Preferences().SetString(setting.Code, setting.Value)
return setting
} }
func (r Repository) Create(setting Setting) (Setting, error) { func (r Repository) CreateOrUpdate(code string, value string) Setting {
err := r.db.Update(func(tx *bbolt.Tx) error {
b := tx.Bucket([]byte("settings"))
if b == nil {
return errors.New("bucket 'settings' not found")
}
data, err := json.Marshal(setting)
if err != nil {
return err
}
return b.Put([]byte(setting.Code), data)
})
return setting, err
}
func (r Repository) CreateOrUpdate(code string, value string) (Setting, error) {
var setting Setting var setting Setting
setting.Code = code setting.Code = code
setting.Value = value setting.Value = value
err := r.db.Update(func(tx *bbolt.Tx) error { r.app.Preferences().SetString(code, value)
b := tx.Bucket([]byte("settings")) return setting
if b == nil {
return errors.New("bucket 'settings' not found")
}
data, err := json.Marshal(setting)
if err != nil {
return err
}
return b.Put([]byte(code), data)
})
return setting, err
} }

28
theme/repository.go Normal file
View File

@@ -0,0 +1,28 @@
package theme
import "git.kor-elf.net/kor-elf/gui-for-ffmpeg/setting"
type RepositoryContract interface {
GetCode() string
Save(code string) setting.Setting
}
type Repository struct {
settingRepository setting.RepositoryContract
}
func NewRepository(settingRepository setting.RepositoryContract) *Repository {
return &Repository{settingRepository: settingRepository}
}
func (r Repository) GetCode() string {
name := r.settingRepository.GetValue("theme")
if len(name) == 0 {
return "default"
}
return name
}
func (r Repository) Save(code string) setting.Setting {
return r.settingRepository.CreateOrUpdate("theme", code)
}

148
theme/theme.go Normal file
View File

@@ -0,0 +1,148 @@
package theme
import (
"fyne.io/fyne/v2"
fyneTheme "fyne.io/fyne/v2/theme"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/kernel"
"image/color"
)
type ThemeContract interface {
List() map[string]ThemeInfoContract
GetCurrentThemeInfo() ThemeInfoContract
SetCurrentTheme(themeInfo ThemeInfoContract) error
}
type theme struct {
app kernel.AppContract
repository RepositoryContract
list map[string]ThemeInfoContract
}
func NewTheme(app kernel.AppContract, repository RepositoryContract) ThemeContract {
theme := &theme{
app: app,
repository: repository,
list: getThemes(app.GetLocalizerService()),
}
theme.init()
return theme
}
func (t theme) init() {
themeInfo := t.GetCurrentThemeInfo()
if themeInfo.GetName() == "default" {
t.app.GetAppFyne().Settings().SetTheme(fyneTheme.DefaultTheme())
return
}
t.app.GetAppFyne().Settings().SetTheme(&forcedVariant{theme: fyneTheme.DefaultTheme(), variant: themeInfo.GetVariant()})
}
func (t theme) GetCurrentThemeInfo() ThemeInfoContract {
themes := t.List()
if themeInfo, ok := themes[t.repository.GetCode()]; ok {
return themeInfo
}
return themes["default"]
}
func (t theme) List() map[string]ThemeInfoContract {
return t.list
}
func (t theme) SetCurrentTheme(themeInfo ThemeInfoContract) error {
_ = t.repository.Save(themeInfo.GetName())
if themeInfo.GetName() == "default" {
t.app.GetAppFyne().Settings().SetTheme(fyneTheme.DefaultTheme())
return nil
}
t.app.GetAppFyne().Settings().SetTheme(&forcedVariant{theme: fyneTheme.DefaultTheme(), variant: themeInfo.GetVariant()})
return nil
}
type ThemeInfoContract interface {
GetName() string
GetTitle() string
GetVariant() fyne.ThemeVariant
}
type themeInfo struct {
name string
title string
variant fyne.ThemeVariant
}
func (inf themeInfo) GetName() string {
return inf.name
}
func (inf themeInfo) GetTitle() string {
return inf.title
}
func (inf themeInfo) GetVariant() fyne.ThemeVariant {
return inf.variant
}
func getThemes(localizer kernel.LocalizerContract) map[string]ThemeInfoContract {
themesNameDefault := &themeInfo{
name: "default",
title: localizer.GetMessage("themesNameDefault"),
}
themesNameLight := &themeInfo{
name: "light",
title: localizer.GetMessage("themesNameLight"),
variant: fyneTheme.VariantLight,
}
themesNameDark := &themeInfo{
name: "dark",
title: localizer.GetMessage("themesNameDark"),
variant: fyneTheme.VariantDark,
}
list := map[string]ThemeInfoContract{
"default": themesNameDefault,
"light": themesNameLight,
"dark": themesNameDark,
}
localizer.AddChangeCallback("themesNameDefault", func(text string) {
themesNameDefault.title = text
})
localizer.AddChangeCallback("themesNameLight", func(text string) {
themesNameLight.title = text
})
localizer.AddChangeCallback("themesNameDark", func(text string) {
themesNameDark.title = text
})
return list
}
type forcedVariant struct {
theme fyne.Theme
variant fyne.ThemeVariant
}
func (f *forcedVariant) Color(name fyne.ThemeColorName, _ fyne.ThemeVariant) color.Color {
return f.theme.Color(name, f.variant)
}
func (f *forcedVariant) Font(style fyne.TextStyle) fyne.Resource {
return fyneTheme.DefaultTheme().Font(style)
}
func (f *forcedVariant) Icon(name fyne.ThemeIconName) fyne.Resource {
return fyneTheme.DefaultTheme().Icon(name)
}
func (f *forcedVariant) Size(name fyne.ThemeSizeName) float32 {
return fyneTheme.DefaultTheme().Size(name)
}