44 Commits

Author SHA1 Message Date
359db74251 Merge pull request 'Версия 0.3.1' (#4) from develop into main
Reviewed-on: #4
2024-02-10 21:35:08 +06:00
154cd1c7dd Fixed convertor_windows.go.
Changed "git.kor-elf.net/kor-elf/gui-for-ffmpeg/convertor" to "git.kor-elf.net/kor-elf/gui-for-ffmpeg/src/convertor".
2024-02-10 20:32:29 +06:00
a617635911 Changed the version to 0.3.1. 2024-02-10 20:23:41 +06:00
48f8c577c0 Fixed a buffer overflow error when converting videos when the video is more than 2 hours long. 2024-02-10 20:13:17 +06:00
ee0a305972 I made sure that the current folder was saved. 2024-02-10 20:11:26 +06:00
3dc59344cb Fix command "fyne get ..."
Renamed the module to git.kor-elf.net/kor-elf/gui-for-ffmpeg/src.
2024-02-04 21:00:55 +06:00
f631c55eae Merge pull request '0.3.0' (#3) from develop into main
Reviewed-on: #3
2024-02-04 20:50:54 +06:00
d46d642e61 Added the ability to automatically download FFmpeg from the site https://github.com/BtbN/FFmpeg-Builds/releases. 2024-02-04 20:16:15 +06:00
a82d283c1a Added licensed third party. 2024-02-04 15:57:37 +06:00
b7c363faaa Изменил файл README.md.
Added installation instructions and other useful information.
2024-02-03 20:16:16 +06:00
2c0e20210f Переименовал модуль в git.kor-elf.net/kor-elf/gui-for-ffmpeg. 2024-02-03 18:40:44 +06:00
eec298bfd7 Added a window with information about the program. 2024-02-03 17:47:32 +06:00
2afc5f5b1a Fixed minor errors reported by the analyzer. 2024-02-01 00:31:28 +06:00
fd7ce5fa08 Fixed minor errors reported by the analyzer. 2024-02-01 00:29:11 +06:00
05553f06b8 Fixed minor errors reported by the analyzer. 2024-02-01 00:28:06 +06:00
0d5cfab38d Made it possible to change the path to FFmpeg and FFprobe. 2024-02-01 00:23:28 +06:00
d86c0d37af Updated some dependencies. 2024-01-31 22:27:50 +06:00
f09dd01b6d Fixed minor errors reported by the analyzer. 2024-01-31 21:22:38 +06:00
6358d5d8cc The language selection is remembered.
Added a setting for changing the language.
2024-01-31 21:02:12 +06:00
5025807b14 Changed a.New Window.
Changed "GUI FFMpeg!" to "FFMpeg GUI!".
2024-01-30 20:38:12 +06:00
0cd32bb0f0 Merge pull request 'Версия 0.2.0' (#2) from develop into main
Reviewed-on: kor-elf/ffmpeg-gui#2
2024-01-28 22:45:56 +06:00
f3e034356b Merge branch 'main' into develop 2024-01-28 22:10:24 +06:00
6f0bbf7e29 Windows OS fix.
Added import "ffmpeg Gui/convertor" to convertor_windows.go
2024-01-28 22:08:40 +06:00
3c563d1966 Added the ability to select a language. 2024-01-28 22:01:16 +06:00
6df775955f Minor optimization.
Raised "progress=end" after data:= Scanner Out.Text().
2024-01-27 21:17:04 +06:00
d68382e418 Fixed a bug when the video was fully converted, but the progress bar showed not 100, but, for example, 99. 2024-01-27 21:08:27 +06:00
846986279c Added icon. 2024-01-23 21:33:30 +06:00
adf9bc9c27 Code refactoring.
Added files with functions for various OS (//go:build windows, //go:build !windows).
2024-01-23 21:33:01 +06:00
c572a3cabe Merge pull request 'Версия 0.1.1' (#1) from develop into main
Reviewed-on: kor-elf/ffmpeg-gui#1
2024-01-20 17:42:02 +06:00
7cc22e1553 Edit Reademe.md.
Added a screenshot from the program.
2024-01-20 17:39:18 +06:00
9bb19a0263 Sometimes the progressbar is not displayed, so I decided not to hide it. 2024-01-20 17:05:16 +06:00
fc38a1b20c Fix after closing the program so that ffmpeg would also stop if it was running. 2024-01-20 14:58:16 +06:00
2596e822bd Fix progress bar not always shown during conversion. 2024-01-20 14:57:22 +06:00
4fa977347c Fix ffmpeg processes not terminating. 2024-01-20 02:24:14 +06:00
77b847dde6 Fix error in OS windows.
Changed the progressbar to the pipe protocol.
2024-01-20 01:53:25 +06:00
ebc8832d4d Made it possible to choose where to save. 2024-01-18 20:23:23 +06:00
5051c65ec6 Fix path to ffmpeg and ffprobe.
Added the ability to select the path to ffmpeg and ffprobe.
2024-01-18 01:39:20 +06:00
10aa917c24 I made it so that an error would be displayed if ffmpeg was not found. 2024-01-16 18:08:50 +06:00
1070b796cc Fix RunConvert.
Fixed an error converting videos with a file name that has spaces.
2024-01-16 00:15:45 +06:00
176189c9d0 Adjusted display of error text when an error occurs during video conversion. 2024-01-16 00:04:50 +06:00
dddbfa65bc Refactor package "convertor" "Main" method.
Moved the button for selecting a video file into a separate function.
2024-01-15 20:28:02 +06:00
97dd0f4b32 Refactor getSockPath.
Removed extra code.
2024-01-14 17:25:35 +06:00
5a4c960201 Working prototype.
A simple working graphical interface with which you can transcode any video to mp4.
2024-01-14 16:31:07 +06:00
e288d5efbb Changing the name on the license. 2024-01-14 16:16:26 +06:00
39 changed files with 5509 additions and 3 deletions

View File

@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2024 kor-elf
Copyright (c) 2024 Leonid Nikitin (kor-elf)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

1887
LICENSE-3RD-PARTY.txt Normal file

File diff suppressed because it is too large Load Diff

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

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

173
src/convertor/service.go Normal file
View File

@@ -0,0 +1,173 @@
package convertor
import (
"errors"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/src/helper"
"io"
"os/exec"
"regexp"
"strconv"
"strings"
)
type ServiceContract interface {
RunConvert(setting ConvertSetting, progress ProgressContract) error
GetTotalDuration(file *File) (float64, error)
GetFFmpegVesrion() (string, error)
GetFFprobeVersion() (string, error)
ChangeFFmpegPath(path string) (bool, error)
ChangeFFprobePath(path string) (bool, error)
GetRunningProcesses() map[int]*exec.Cmd
}
type ProgressContract interface {
GetProtocole() string
Run(stdOut io.ReadCloser, stdErr io.ReadCloser) error
}
type FFPathUtilities struct {
FFmpeg string
FFprobe string
}
type runningProcesses struct {
items map[int]*exec.Cmd
numberOfStarts int
}
type Service struct {
ffPathUtilities *FFPathUtilities
runningProcesses runningProcesses
}
type File struct {
Path string
Name string
Ext string
}
type ConvertSetting struct {
VideoFileInput *File
VideoFileOut *File
OverwriteOutputFiles bool
}
type ConvertData struct {
totalDuration float64
}
func NewService(ffPathUtilities FFPathUtilities) *Service {
return &Service{
ffPathUtilities: &ffPathUtilities,
runningProcesses: runningProcesses{items: map[int]*exec.Cmd{}, numberOfStarts: 0},
}
}
func (s Service) RunConvert(setting ConvertSetting, progress ProgressContract) error {
overwriteOutputFiles := "-n"
if setting.OverwriteOutputFiles == true {
overwriteOutputFiles = "-y"
}
args := []string{overwriteOutputFiles, "-i", setting.VideoFileInput.Path, "-c:v", "libx264", "-progress", progress.GetProtocole(), setting.VideoFileOut.Path}
cmd := exec.Command(s.ffPathUtilities.FFmpeg, args...)
helper.PrepareBackgroundCommand(cmd)
stdOut, err := cmd.StdoutPipe()
if err != nil {
return err
}
stdErr, err := cmd.StderrPipe()
if err != nil {
return err
}
err = cmd.Start()
if err != nil {
return err
}
index := s.runningProcesses.numberOfStarts
s.runningProcesses.numberOfStarts++
s.runningProcesses.items[index] = cmd
errProgress := progress.Run(stdOut, stdErr)
err = cmd.Wait()
delete(s.runningProcesses.items, index)
if errProgress != nil {
return errProgress
}
if err != nil {
return err
}
return nil
}
func (s Service) GetTotalDuration(file *File) (duration float64, err error) {
args := []string{"-v", "error", "-select_streams", "v:0", "-count_packets", "-show_entries", "stream=nb_read_packets", "-of", "csv=p=0", file.Path}
cmd := exec.Command(s.ffPathUtilities.FFprobe, args...)
helper.PrepareBackgroundCommand(cmd)
out, err := cmd.CombinedOutput()
if err != nil {
errString := strings.TrimSpace(string(out))
if len(errString) > 1 {
return 0, errors.New(errString)
}
return 0, err
}
return strconv.ParseFloat(strings.TrimSpace(string(out)), 64)
}
func (s Service) GetFFmpegVesrion() (string, error) {
cmd := exec.Command(s.ffPathUtilities.FFmpeg, "-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 Service) GetFFprobeVersion() (string, error) {
cmd := exec.Command(s.ffPathUtilities.FFprobe, "-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 Service) ChangeFFmpegPath(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)), "ffmpeg") == false {
return false, nil
}
s.ffPathUtilities.FFmpeg = path
return true, nil
}
func (s Service) ChangeFFprobePath(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)), "ffprobe") == false {
return false, nil
}
s.ffPathUtilities.FFprobe = path
return true, nil
}
func (s Service) GetRunningProcesses() map[int]*exec.Cmd {
return s.runningProcesses.items
}

253
src/convertor/view.go Normal file
View File

@@ -0,0 +1,253 @@
package convertor
import (
"errors"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/storage"
"fyne.io/fyne/v2/widget"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/src/helper"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/src/localizer"
"github.com/nicksnyder/go-i18n/v2/i18n"
"image/color"
"path/filepath"
)
type ViewContract interface {
Main(
runConvert func(setting HandleConvertSetting, progressbar *widget.ProgressBar) error,
)
SelectFFPath(
ffmpegPath string,
ffprobePath string,
save func(ffmpegPath string, ffprobePath string) error,
cancel func(),
donwloadFFmpeg func(progressBar *widget.ProgressBar, progressMessage *canvas.Text) error,
)
}
type View struct {
w fyne.Window
localizerService localizer.ServiceContract
}
type HandleConvertSetting struct {
VideoFileInput *File
DirectoryForSave string
OverwriteOutputFiles bool
}
type enableFormConversionStruct struct {
fileVideoForConversion *widget.Button
buttonForSelectedDir *widget.Button
form *widget.Form
}
func NewView(w fyne.Window, localizerService localizer.ServiceContract) *View {
return &View{
w: w,
localizerService: localizerService,
}
}
func (v View) Main(
runConvert func(setting HandleConvertSetting, progressbar *widget.ProgressBar) error,
) {
form := &widget.Form{}
conversionMessage := canvas.NewText("", color.RGBA{R: 255, G: 0, B: 0, A: 255})
conversionMessage.TextSize = 16
conversionMessage.TextStyle = fyne.TextStyle{Bold: true}
progress := widget.NewProgressBar()
fileVideoForConversion, fileVideoForConversionMessage, fileInput := v.getButtonFileVideoForConversion(form, progress, conversionMessage)
buttonForSelectedDir, buttonForSelectedDirMessage, pathToSaveDirectory := v.getButtonForSelectingDirectoryForSaving()
isOverwriteOutputFiles := false
checkboxOverwriteOutputFilesTitle := v.localizerService.GetMessage(&i18n.LocalizeConfig{
MessageID: "checkboxOverwriteOutputFilesTitle",
})
checkboxOverwriteOutputFiles := widget.NewCheck(checkboxOverwriteOutputFilesTitle, func(b bool) {
isOverwriteOutputFiles = b
})
form.Items = []*widget.FormItem{
{
Text: v.localizerService.GetMessage(&i18n.LocalizeConfig{MessageID: "fileVideoForConversionTitle"}),
Widget: fileVideoForConversion,
},
{
Widget: container.NewHScroll(fileVideoForConversionMessage),
},
{
Text: v.localizerService.GetMessage(&i18n.LocalizeConfig{MessageID: "buttonForSelectedDirTitle"}),
Widget: buttonForSelectedDir,
},
{
Widget: container.NewHScroll(buttonForSelectedDirMessage),
},
{
Widget: checkboxOverwriteOutputFiles,
},
}
form.SubmitText = v.localizerService.GetMessage(&i18n.LocalizeConfig{
MessageID: "converterVideoFilesSubmitTitle",
})
enableFormConversionStruct := enableFormConversionStruct{
fileVideoForConversion: fileVideoForConversion,
buttonForSelectedDir: buttonForSelectedDir,
form: form,
}
form.OnSubmit = func() {
if len(*pathToSaveDirectory) == 0 {
showConversionMessage(conversionMessage, errors.New(v.localizerService.GetMessage(&i18n.LocalizeConfig{
MessageID: "errorSelectedFolderSave",
})))
enableFormConversion(enableFormConversionStruct)
return
}
conversionMessage.Text = ""
fileVideoForConversion.Disable()
buttonForSelectedDir.Disable()
form.Disable()
setting := HandleConvertSetting{
VideoFileInput: fileInput,
DirectoryForSave: *pathToSaveDirectory,
OverwriteOutputFiles: isOverwriteOutputFiles,
}
err := runConvert(setting, progress)
if err != nil {
showConversionMessage(conversionMessage, err)
enableFormConversion(enableFormConversionStruct)
return
}
enableFormConversion(enableFormConversionStruct)
}
converterVideoFilesTitle := v.localizerService.GetMessage(&i18n.LocalizeConfig{
MessageID: "converterVideoFilesTitle",
})
v.w.SetContent(widget.NewCard(converterVideoFilesTitle, "", container.NewVBox(form, conversionMessage, progress)))
form.Disable()
}
func (v View) getButtonFileVideoForConversion(form *widget.Form, progress *widget.ProgressBar, conversionMessage *canvas.Text) (*widget.Button, *canvas.Text, *File) {
fileInput := &File{}
fileVideoForConversionMessage := canvas.NewText("", color.RGBA{R: 255, G: 0, B: 0, A: 255})
fileVideoForConversionMessage.TextSize = 16
fileVideoForConversionMessage.TextStyle = fyne.TextStyle{Bold: true}
buttonTitle := v.localizerService.GetMessage(&i18n.LocalizeConfig{
MessageID: "choose",
})
var locationURI fyne.ListableURI
button := widget.NewButton(buttonTitle, func() {
fileDialog := dialog.NewFileOpen(
func(r fyne.URIReadCloser, err error) {
if err != nil {
fileVideoForConversionMessage.Text = err.Error()
setStringErrorStyle(fileVideoForConversionMessage)
return
}
if r == nil {
return
}
fileInput.Path = r.URI().Path()
fileInput.Name = r.URI().Name()
fileInput.Ext = r.URI().Extension()
fileVideoForConversionMessage.Text = r.URI().Path()
setStringSuccessStyle(fileVideoForConversionMessage)
form.Enable()
progress.Value = 0
progress.Refresh()
conversionMessage.Text = ""
listableURI := storage.NewFileURI(filepath.Dir(r.URI().Path()))
locationURI, err = storage.ListerForURI(listableURI)
}, v.w)
helper.FileDialogResize(fileDialog, v.w)
fileDialog.Show()
if locationURI != nil {
fileDialog.SetLocation(locationURI)
}
})
return button, fileVideoForConversionMessage, fileInput
}
func (v View) getButtonForSelectingDirectoryForSaving() (button *widget.Button, buttonMessage *canvas.Text, dirPath *string) {
buttonMessage = canvas.NewText("", color.RGBA{R: 255, G: 0, B: 0, A: 255})
buttonMessage.TextSize = 16
buttonMessage.TextStyle = fyne.TextStyle{Bold: true}
path := ""
dirPath = &path
buttonTitle := v.localizerService.GetMessage(&i18n.LocalizeConfig{
MessageID: "choose",
})
var locationURI fyne.ListableURI
button = widget.NewButton(buttonTitle, func() {
fileDialog := dialog.NewFolderOpen(
func(r fyne.ListableURI, err error) {
if err != nil {
buttonMessage.Text = err.Error()
setStringErrorStyle(buttonMessage)
return
}
if r == nil {
return
}
path = r.Path()
buttonMessage.Text = r.Path()
setStringSuccessStyle(buttonMessage)
locationURI, _ = storage.ListerForURI(r)
}, v.w)
helper.FileDialogResize(fileDialog, v.w)
fileDialog.Show()
if locationURI != nil {
fileDialog.SetLocation(locationURI)
}
})
return
}
func setStringErrorStyle(text *canvas.Text) {
text.Color = color.RGBA{R: 255, G: 0, B: 0, A: 255}
text.Refresh()
}
func setStringSuccessStyle(text *canvas.Text) {
text.Color = color.RGBA{R: 49, G: 127, B: 114, A: 255}
text.Refresh()
}
func showConversionMessage(conversionMessage *canvas.Text, err error) {
conversionMessage.Text = err.Error()
setStringErrorStyle(conversionMessage)
}
func enableFormConversion(enableFormConversionStruct enableFormConversionStruct) {
enableFormConversionStruct.fileVideoForConversion.Enable()
enableFormConversionStruct.buttonForSelectedDir.Enable()
enableFormConversionStruct.form.Enable()
}

View File

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

View File

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

View File

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

2
src/data/.gitignore vendored Normal file
View File

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

66
src/error/view.go Normal file
View File

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

44
src/go.mod Normal file
View File

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

697
src/go.sum Normal file
View File

@@ -0,0 +1,697 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
fyne.io/fyne/v2 v2.4.3 h1:v2wncjEAcwXZ8UNmTCWTGL9+sGyPc5RuzBvM96GcC78=
fyne.io/fyne/v2 v2.4.3/go.mod h1:1h3BKxmQYRJlr2g+RGVxedzr6vLVQ/AJmFWcF9CJnoQ=
fyne.io/systray v1.10.1-0.20231115130155-104f5ef7839e h1:Hvs+kW2VwCzNToF3FmnIAzmivNgrclwPgoUdVSrjkP8=
fyne.io/systray v1.10.1-0.20231115130155-104f5ef7839e/go.mod h1:oM2AQqGJ1AMo4nNqZFYU8xYygSBZkW2hmdJ7n4yjedE=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fredbi/uri v1.0.0 h1:s4QwUAZ8fz+mbTsukND+4V5f+mJ/wjaTokwstGUAemg=
github.com/fredbi/uri v1.0.0/go.mod h1:1xC40RnIOGCaQzswaOvrzvG/3M3F0hyDVb3aO/1iGy0=
github.com/fredbi/uri v1.1.0 h1:OqLpTXtyRg9ABReqvDGdJPqZUxs8cyBDOMXBbskCaB8=
github.com/fredbi/uri v1.1.0/go.mod h1:aYTUoAXBOq7BLfVJ8GnKmfcuURosB1xyHDIfWeC/iW4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/fyne-io/gl-js v0.0.0-20220119005834-d2da28d9ccfe h1:A/wiwvQ0CAjPkuJytaD+SsXkPU0asQ+guQEIg1BJGX4=
github.com/fyne-io/gl-js v0.0.0-20220119005834-d2da28d9ccfe/go.mod h1:d4clgH0/GrRwWjRzJJQXxT/h1TyuNSfF/X64zb/3Ggg=
github.com/fyne-io/glfw-js v0.0.0-20220120001248-ee7290d23504 h1:+31CdF/okdokeFNoy9L/2PccG3JFidQT3ev64/r4pYU=
github.com/fyne-io/glfw-js v0.0.0-20220120001248-ee7290d23504/go.mod h1:gLRWYfYnMA9TONeppRSikMdXlHQ97xVsPojddUv3b/E=
github.com/fyne-io/image v0.0.0-20220602074514-4956b0afb3d2 h1:hnLq+55b7Zh7/2IRzWCpiTcAvjv/P8ERF+N7+xXbZhk=
github.com/fyne-io/image v0.0.0-20220602074514-4956b0afb3d2/go.mod h1:eO7W361vmlPOrykIg+Rsh1SZ3tQBaOsfzZhsIOb/Lm0=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 h1:zDw5v7qm4yH7N8C8uWd+8Ii9rROdgWxQuGoJ9WDXxfk=
github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw=
github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71 h1:5BVwOaUSBTlVZowGO6VZGw2H/zl9nrd3eCZfYV+NfQA=
github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20211213063430-748e38ca8aec/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20221017161538-93cebf72946b h1:GgabKamyOYguHqHjSkDACcgoPIz3w0Dis/zJ1wyHHHU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20221017161538-93cebf72946b/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240118000515-a250818d05e3 h1:nanQfMsOs3gnuKRm0E5jXWomedE/9YIFXdmHJNZYeqc=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240118000515-a250818d05e3/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-text/render v0.0.0-20230619120952-35bccb6164b8 h1:VkKnvzbvHqgEfm351rfr8Uclu5fnwq8HP2ximUzJsBM=
github.com/go-text/render v0.0.0-20230619120952-35bccb6164b8/go.mod h1:h29xCucjNsDcYb7+0rJokxVwYAq+9kQ19WiFuBKkYtc=
github.com/go-text/typesetting v0.0.0-20230616162802-9c17dd34aa4a h1:VjN8ttdfklC0dnAdKbZqGNESdERUxtE3l8a/4Grgarc=
github.com/go-text/typesetting v0.0.0-20230616162802-9c17dd34aa4a/go.mod h1:evDBbvNR/KaVFZ2ZlDSOWWXIUKq0wCOEtzLxRM8SG3k=
github.com/go-text/typesetting-utils v0.0.0-20230616150549-2a7df14b6a22 h1:LBQTFxP2MfsyEDqSKmUBZaDuDHN1vpqDyOZjcqS7MYI=
github.com/go-text/typesetting-utils v0.0.0-20230616150549-2a7df14b6a22/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20211219123610-ec9572f70e60/go.mod h1:cz9oNYuRUWGdHmLF2IodMLkAhcPtXeULvcBNagUrxTI=
github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g=
github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k=
github.com/goxjs/gl v0.0.0-20210104184919-e3fafc6f8f2a/go.mod h1:dy/f2gjY09hwVfIyATps4G2ai7/hLwLkc5TrPqONuXY=
github.com/goxjs/glfw v0.0.0-20191126052801-d2efb5f20838/go.mod h1:oS8P8gVOT4ywTcjV6wZlOU4GuVFQ8F5328KY3MJ79CY=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e h1:LvL4XsI70QxOGHed6yhQtAU34Kx3Qq2wwBzGFKY8zKk=
github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e/go.mod h1:kLgvv7o6UM+0QSf0QjAse3wReFDsb9qbZJdfexWlrQw=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-sqlite3 v1.14.19 h1:fhGleo2h1p8tVChob4I9HpmVFIAkKGpiukdrgQbWfGI=
github.com/mattn/go-sqlite3 v1.14.19/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/mattn/go-sqlite3 v1.14.21 h1:IXocQLOykluc3xPE0Lvy8FtggMz1G+U3mEjg+0zGizc=
github.com/mattn/go-sqlite3 v1.14.21/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
github.com/nicksnyder/go-i18n/v2 v2.3.0 h1:2NPsCsNFCVd7i+Su0xYsBrIhS3bE2XMv5gNTft2O+PQ=
github.com/nicksnyder/go-i18n/v2 v2.3.0/go.mod h1:nxYSZE9M0bf3Y70gPQjN9ha7XNHX7gMc814+6wVyEI4=
github.com/nicksnyder/go-i18n/v2 v2.4.0 h1:3IcvPOAvnCKwNm0TB0dLDTuawWEj+ax/RERNC+diLMM=
github.com/nicksnyder/go-i18n/v2 v2.4.0/go.mod h1:nxYSZE9M0bf3Y70gPQjN9ha7XNHX7gMc814+6wVyEI4=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shurcooL/go v0.0.0-20200502201357-93f07166e636/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c h1:km8GpoQut05eY3GiYWEedbTT0qnSxrCjsVbb7yKY1KE=
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c/go.mod h1:cNQ3dwVJtS5Hmnjxy6AgTPd0Inb3pW05ftPSX7NZO7Q=
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef h1:Ch6Q+AZUxDBCVqdkI8FSpFyZDtCVBc2VmejdNrm5rRQ=
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef/go.mod h1:nXTWP6+gD5+LUJ8krVhhoeHjvHTutPxMYl5SvkcnJNE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tevino/abool v1.2.0 h1:heAkClL8H6w+mK5md9dzsuohKeXHUpY7Vw0ZCKW+huA=
github.com/tevino/abool v1.2.0/go.mod h1:qc66Pna1RiIsPa7O4Egxxs9OqkuxDX55zznh9K07Tzg=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.5.5 h1:IJznPe8wOzfIKETmMkd06F8nXkmlhaHqFRM9l1hAGsU=
github.com/yuin/goldmark v1.5.5/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.11.0 h1:ds2RoQvBvYTiJkwpSFDwCcDFNX7DqjL2WsUgTNk0Ooo=
golang.org/x/image v0.11.0/go.mod h1:bglhjqbqVuEb9e9+eNR45Jfu7D+T4Qan+NhQk8Ck2P8=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mobile v0.0.0-20211207041440-4e6c2922fdee/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ=
golang.org/x/mobile v0.0.0-20230531173138-3c911d8e3eda h1:O+EUvnBNPwI4eLthn8W5K+cS8zQZfgTABPLNm6Bna34=
golang.org/x/mobile v0.0.0-20230531173138-3c911d8e3eda/go.mod h1:aAjjkJNdrh3PMckS4B10TGS2nag27cbKR1y2BpUxsiY=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.8-0.20211022200916-316ba0b74098/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/sqlite v1.5.4 h1:IqXwXi8M/ZlPzH/947tn5uik3aYQslP9BVveoax0nV0=
gorm.io/driver/sqlite v1.5.4/go.mod h1:qxAuCol+2r6PannQDpOP1FP6ag3mKi4esLnB/jHed+4=
gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls=
gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
gorm.io/gorm v1.25.6 h1:V92+vVda1wEISSOMtodHVRcUIOPYa2tgQtyF+DfFx+A=
gorm.io/gorm v1.25.6/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
honnef.co/go/js/dom v0.0.0-20210725211120-f030747120f2 h1:oomkgU6VaQDsV6qZby2uz1Lap0eXmku8+2em3A/l700=
honnef.co/go/js/dom v0.0.0-20210725211120-f030747120f2/go.mod h1:sUMDUKNB2ZcVjt92UnLy3cdGs+wDAcrPdV3JP6sVgA4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=

238
src/handler/convertor.go Normal file
View File

@@ -0,0 +1,238 @@
package handler
import (
"bufio"
"errors"
"fyne.io/fyne/v2/widget"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/src/convertor"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/src/helper"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/src/localizer"
"github.com/nicksnyder/go-i18n/v2/i18n"
"io"
"regexp"
"strconv"
"strings"
)
type ConvertorHandlerContract interface {
MainConvertor()
FfPathSelection()
GetFfmpegVersion() (string, error)
GetFfprobeVersion() (string, error)
}
type ConvertorHandler struct {
convertorService convertor.ServiceContract
convertorView convertor.ViewContract
convertorRepository convertor.RepositoryContract
localizerService localizer.ServiceContract
}
func NewConvertorHandler(
convertorService convertor.ServiceContract,
convertorView convertor.ViewContract,
convertorRepository convertor.RepositoryContract,
localizerService localizer.ServiceContract,
) *ConvertorHandler {
return &ConvertorHandler{
convertorService: convertorService,
convertorView: convertorView,
convertorRepository: convertorRepository,
localizerService: localizerService,
}
}
func (h ConvertorHandler) MainConvertor() {
if h.checkingFFPathUtilities() == true {
h.convertorView.Main(h.runConvert)
return
}
h.convertorView.SelectFFPath("", "", h.saveSettingFFPath, nil, h.downloadFFmpeg)
}
func (h ConvertorHandler) FfPathSelection() {
ffmpeg, _ := h.convertorRepository.GetPathFfmpeg()
ffprobe, _ := h.convertorRepository.GetPathFfprobe()
h.convertorView.SelectFFPath(ffmpeg, ffprobe, h.saveSettingFFPath, h.MainConvertor, h.downloadFFmpeg)
}
func (h ConvertorHandler) GetFfmpegVersion() (string, error) {
return h.convertorService.GetFFmpegVesrion()
}
func (h ConvertorHandler) GetFfprobeVersion() (string, error) {
return h.convertorService.GetFFprobeVersion()
}
func (h ConvertorHandler) runConvert(setting convertor.HandleConvertSetting, progressbar *widget.ProgressBar) error {
totalDuration, err := h.convertorService.GetTotalDuration(setting.VideoFileInput)
if err != nil {
return err
}
progress := NewProgress(totalDuration, progressbar, h.localizerService)
return h.convertorService.RunConvert(
convertor.ConvertSetting{
VideoFileInput: setting.VideoFileInput,
VideoFileOut: &convertor.File{
Path: setting.DirectoryForSave + helper.PathSeparator() + setting.VideoFileInput.Name + ".mp4",
Name: setting.VideoFileInput.Name,
Ext: ".mp4",
},
OverwriteOutputFiles: setting.OverwriteOutputFiles,
},
progress,
)
}
func (h ConvertorHandler) checkingFFPathUtilities() bool {
if h.checkingFFPath() == true {
return true
}
pathsToFF := getPathsToFF()
for _, item := range pathsToFF {
ffmpegChecking, _ := h.convertorService.ChangeFFmpegPath(item.FFmpeg)
if ffmpegChecking == false {
continue
}
ffprobeChecking, _ := h.convertorService.ChangeFFprobePath(item.FFprobe)
if ffprobeChecking == false {
continue
}
_, _ = h.convertorRepository.SavePathFfmpeg(item.FFmpeg)
_, _ = h.convertorRepository.SavePathFfprobe(item.FFprobe)
return true
}
return false
}
func (h ConvertorHandler) saveSettingFFPath(ffmpegPath string, ffprobePath string) error {
ffmpegChecking, _ := h.convertorService.ChangeFFmpegPath(ffmpegPath)
if ffmpegChecking == false {
errorText := h.localizerService.GetMessage(&i18n.LocalizeConfig{
MessageID: "errorFFmpeg",
})
return errors.New(errorText)
}
ffprobeChecking, _ := h.convertorService.ChangeFFprobePath(ffprobePath)
if ffprobeChecking == false {
errorText := h.localizerService.GetMessage(&i18n.LocalizeConfig{
MessageID: "errorFFprobe",
})
return errors.New(errorText)
}
_, _ = h.convertorRepository.SavePathFfmpeg(ffmpegPath)
_, _ = h.convertorRepository.SavePathFfprobe(ffprobePath)
h.MainConvertor()
return nil
}
func (h ConvertorHandler) checkingFFPath() bool {
_, err := h.convertorService.GetFFmpegVesrion()
if err != nil {
return false
}
_, err = h.convertorService.GetFFprobeVersion()
if err != nil {
return false
}
return true
}
type Progress struct {
totalDuration float64
progressbar *widget.ProgressBar
protocol string
localizerService localizer.ServiceContract
}
func NewProgress(totalDuration float64, progressbar *widget.ProgressBar, localizerService localizer.ServiceContract) Progress {
return Progress{
totalDuration: totalDuration,
progressbar: progressbar,
protocol: "pipe:",
localizerService: localizerService,
}
}
func (p Progress) GetProtocole() string {
return p.protocol
}
func (p Progress) Run(stdOut io.ReadCloser, stdErr io.ReadCloser) error {
isProcessCompleted := false
var errorText string
p.progressbar.Value = 0
p.progressbar.Max = p.totalDuration
p.progressbar.Refresh()
progress := 0.0
go func() {
scannerErr := bufio.NewReader(stdErr)
for {
line, _, err := scannerErr.ReadLine()
if err != nil {
if err == io.EOF {
break
}
continue
}
data := strings.TrimSpace(string(line))
errorText = data
}
}()
scannerOut := bufio.NewReader(stdOut)
for {
line, _, err := scannerOut.ReadLine()
if err != nil {
if err == io.EOF {
break
}
continue
}
data := strings.TrimSpace(string(line))
if strings.Contains(data, "progress=end") {
p.progressbar.Value = p.totalDuration
p.progressbar.Refresh()
isProcessCompleted = true
break
}
re := regexp.MustCompile(`frame=(\d+)`)
a := re.FindAllStringSubmatch(data, -1)
if len(a) > 0 && len(a[len(a)-1]) > 0 {
c, err := strconv.Atoi(a[len(a)-1][len(a[len(a)-1])-1])
if err != nil {
continue
}
progress = float64(c)
}
if p.progressbar.Value != progress {
p.progressbar.Value = progress
p.progressbar.Refresh()
}
}
if isProcessCompleted == false {
if len(errorText) == 0 {
errorText = p.localizerService.GetMessage(&i18n.LocalizeConfig{
MessageID: "errorConverter",
})
}
return errors.New(errorText)
}
return nil
}

View File

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

View File

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

37
src/handler/main.go Normal file
View File

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

125
src/handler/menu.go Normal file
View File

@@ -0,0 +1,125 @@
package handler
import (
"fyne.io/fyne/v2"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/src/localizer"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/src/menu"
"github.com/nicksnyder/go-i18n/v2/i18n"
)
type MenuHandlerContract interface {
GetMainMenu() *fyne.MainMenu
LanguageSelection()
}
type menuItems struct {
menuItem map[string]*fyne.MenuItem
menu map[string]*fyne.Menu
}
type MenuHandler struct {
convertorHandler ConvertorHandlerContract
menuView menu.ViewContract
localizerService localizer.ServiceContract
localizerView localizer.ViewContract
localizerRepository localizer.RepositoryContract
menuItems *menuItems
}
func NewMenuHandler(
convertorHandler ConvertorHandlerContract,
menuView menu.ViewContract,
localizerService localizer.ServiceContract,
localizerView localizer.ViewContract,
localizerRepository localizer.RepositoryContract,
) *MenuHandler {
return &MenuHandler{
convertorHandler: convertorHandler,
menuView: menuView,
localizerService: localizerService,
localizerView: localizerView,
localizerRepository: localizerRepository,
menuItems: &menuItems{menuItem: map[string]*fyne.MenuItem{}, menu: map[string]*fyne.Menu{}},
}
}
func (h MenuHandler) GetMainMenu() *fyne.MainMenu {
settings := h.getMenuSettings()
help := h.getMenuHelp()
return fyne.NewMainMenu(settings, help)
}
func (h MenuHandler) getMenuSettings() *fyne.Menu {
quit := fyne.NewMenuItem(h.localizerService.GetMessage(&i18n.LocalizeConfig{
MessageID: "exit",
}), nil)
quit.IsQuit = true
h.menuItems.menuItem["exit"] = quit
languageSelection := fyne.NewMenuItem(h.localizerService.GetMessage(&i18n.LocalizeConfig{
MessageID: "changeLanguage",
}), h.LanguageSelection)
h.menuItems.menuItem["changeLanguage"] = languageSelection
ffPathSelection := fyne.NewMenuItem(h.localizerService.GetMessage(&i18n.LocalizeConfig{
MessageID: "changeFFPath",
}), h.convertorHandler.FfPathSelection)
h.menuItems.menuItem["changeFFPath"] = ffPathSelection
settings := fyne.NewMenu(h.localizerService.GetMessage(&i18n.LocalizeConfig{
MessageID: "settings",
}), languageSelection, ffPathSelection, quit)
h.menuItems.menu["settings"] = settings
return settings
}
func (h MenuHandler) getMenuHelp() *fyne.Menu {
about := fyne.NewMenuItem(h.localizerService.GetMessage(&i18n.LocalizeConfig{
MessageID: "about",
}), h.openAbout)
h.menuItems.menuItem["about"] = about
help := fyne.NewMenu(h.localizerService.GetMessage(&i18n.LocalizeConfig{
MessageID: "help",
}), about)
h.menuItems.menu["help"] = help
return help
}
func (h MenuHandler) openAbout() {
ffmpeg, err := h.convertorHandler.GetFfmpegVersion()
if err != nil {
ffmpeg = h.localizerService.GetMessage(&i18n.LocalizeConfig{
MessageID: "errorFFmpegVersion",
})
}
ffprobe, err := h.convertorHandler.GetFfprobeVersion()
if err != nil {
ffprobe = h.localizerService.GetMessage(&i18n.LocalizeConfig{
MessageID: "errorFFprobeVersion",
})
}
h.menuView.About(ffmpeg, ffprobe)
}
func (h MenuHandler) LanguageSelection() {
h.localizerView.LanguageSelection(func(lang localizer.Lang) {
_, _ = h.localizerRepository.Save(lang.Code)
h.menuMessageReload()
h.convertorHandler.MainConvertor()
})
}
func (h MenuHandler) menuMessageReload() {
for messageID, menu := range h.menuItems.menuItem {
menu.Label = h.localizerService.GetMessage(&i18n.LocalizeConfig{MessageID: messageID})
}
for messageID, menu := range h.menuItems.menu {
menu.Label = h.localizerService.GetMessage(&i18n.LocalizeConfig{MessageID: messageID})
menu.Refresh()
}
}

11
src/helper/helper.go Normal file
View File

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

View File

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

View File

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

View File

@@ -0,0 +1,12 @@
//go:build !windows
// +build !windows
package helper
import (
"os/exec"
)
func PrepareBackgroundCommand(cmd *exec.Cmd) {
}

View File

@@ -0,0 +1,13 @@
//go:build windows
// +build windows
package helper
import (
"os/exec"
"syscall"
)
func PrepareBackgroundCommand(cmd *exec.Cmd) {
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
}

BIN
src/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

View File

@@ -0,0 +1,167 @@
[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-52b13f1b13e82d22e8c4102332db5d4ec551247b"
other = "Folder where it will be saved:"
[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"
[converterVideoFilesSubmitTitle]
hash = "sha1-7ac460f3c24c9952082f2db6e4d62f752598709c"
other = "Convert"
[converterVideoFilesTitle]
hash = "sha1-4d972809e4c7f9c9ff2c110a126bbc183c9429ce"
other = "Converter video files to mp4"
[download]
hash = "sha1-fe8f79f29da457de2f6bc9531de6e536e0c426ad"
other = "Download"
[downloadFFmpegFromSite]
hash = "sha1-0889c95aa3a8659d8d903b4dab7097699c4d8aa4"
other = "Will be downloaded from the site:"
[downloadRun]
hash = "sha1-55f87f114628fa2d5d8e67d1e1cda22c0e4f9271"
other = "Downloading..."
[error]
hash = "sha1-a7df8f8b5d754f226ac4cb320577fe692b33e483"
other = "An error has occurred!"
[errorConverter]
hash = "sha1-55ebddceddb8b044e33cc3893ec2eba7bbd9fcf9"
other = "Couldn't convert video"
[errorDatabase]
hash = "sha1-531abc3f0d12727e542df6e5a22de91098380fc1"
other = "could not create file 'database' in folder 'data'"
[errorFFmpeg]
hash = "sha1-ccf0b95c0d1b392dc215258d917eb4e5d0b88ed0"
other = "this is not FFmpeg"
[errorFFmpegVersion]
hash = "sha1-9a4148d42186b6b32cf83bef726e23022c53283f"
other = "Could not determine FFmpeg version"
[errorFFprobe]
hash = "sha1-86d1b0b4c4ccd6a4f71e758fc67ce11aff4ba9b8"
other = "this is not FFprobe"
[errorFFprobeVersion]
hash = "sha1-da7b37d7df3fafbd153665b13888413d52b24c17"
other = "Failed to determine FFprobe version"
[errorSelectedFolderSave]
hash = "sha1-83da899677cdc90e4344e3b94ee03c46b51bee4c"
other = "You haven't selected a folder to save!"
[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."
[fileVideoForConversionTitle]
hash = "sha1-5e727d4a2ff3f21080e51e81641595b2e668f3be"
other = "File for conversion:"
[help]
hash = "sha1-6a45cef900c668effcb2ab10da05855c1fd10f6f"
other = "Help"
[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"
[pathToFfmpeg]
hash = "sha1-fafc50f1db0f720fe83a96cd70a9e1ad824e96b6"
other = "Path to FFmpeg:"
[pathToFfprobe]
hash = "sha1-b872edc9633a2e81ef678dc46fe46a7e91732024"
other = "Path to FFprobe:"
[programmLink]
hash = "sha1-18f9a3fad6aacefe1b05eed23122800b391ff5ca"
other = "Project website"
[programmVersion]
hash = "sha1-fa2e4994a301bb24bc2a8fa166e5486ea95a7475"
other = "**Program version:** {{.Version}}"
[save]
hash = "sha1-4864057d626a868fa60f999bed3191d61d045ddc"
other = "Save"
[selectFFPathTitle]
hash = "sha1-95581446a28d968ff1a027c623159a7eb08654cf"
other = "Specify the path to FFmpeg and FFprobe"
[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"
[unzipRun]
hash = "sha1-c554dad13026668a1f6adf3171837c5d51bbac36"
other = "Unpacked..."

View File

@@ -0,0 +1,167 @@
[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-52b13f1b13e82d22e8c4102332db5d4ec551247b"
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 = "таңдау"
[converterVideoFilesSubmitTitle]
hash = "sha1-7ac460f3c24c9952082f2db6e4d62f752598709c"
other = "Файлды түрлендіру"
[converterVideoFilesTitle]
hash = "sha1-4d972809e4c7f9c9ff2c110a126bbc183c9429ce"
other = "Бейне файлдарын mp4 форматына түрлендіру"
[download]
hash = "sha1-fe8f79f29da457de2f6bc9531de6e536e0c426ad"
other = "Жүктеп алу"
[downloadFFmpegFromSite]
hash = "sha1-0889c95aa3a8659d8d903b4dab7097699c4d8aa4"
other = "Сайттан жүктеледі:"
[downloadRun]
hash = "sha1-55f87f114628fa2d5d8e67d1e1cda22c0e4f9271"
other = "Жүктеп алынуда..."
[error]
hash = "sha1-a7df8f8b5d754f226ac4cb320577fe692b33e483"
other = "Қате орын алды!"
[errorConverter]
hash = "sha1-55ebddceddb8b044e33cc3893ec2eba7bbd9fcf9"
other = "Бейнені түрлендіру мүмкін болмады"
[errorDatabase]
hash = "sha1-531abc3f0d12727e542df6e5a22de91098380fc1"
other = "'data' қалтасында 'database' файлын жасау мүмкін болмады"
[errorFFmpeg]
hash = "sha1-ccf0b95c0d1b392dc215258d917eb4e5d0b88ed0"
other = "бұл FFmpeg емес"
[errorFFmpegVersion]
hash = "sha1-9a4148d42186b6b32cf83bef726e23022c53283f"
other = "FFmpeg нұсқасын анықтау мүмкін болмады"
[errorFFprobe]
hash = "sha1-86d1b0b4c4ccd6a4f71e758fc67ce11aff4ba9b8"
other = "бұл FFprobe емес"
[errorFFprobeVersion]
hash = "sha1-da7b37d7df3fafbd153665b13888413d52b24c17"
other = "FFprobe нұсқасын анықтау мүмкін болмады"
[errorSelectedFolderSave]
hash = "sha1-83da899677cdc90e4344e3b94ee03c46b51bee4c"
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/)** сауда белгісі."
[fileVideoForConversionTitle]
hash = "sha1-5e727d4a2ff3f21080e51e81641595b2e668f3be"
other = "Түрлендіруге арналған файл:"
[help]
hash = "sha1-6a45cef900c668effcb2ab10da05855c1fd10f6f"
other = "Анықтама"
[languageSelectionFormHead]
hash = "sha1-0ff5fa82cf684112660128cba1711297acf11003"
other = "Тілді ауыстыру"
[languageSelectionHead]
hash = "sha1-daf1108fc10d3b1a908288d611f749b3cc651e4b"
other = "Тілді таңдаңыз"
[licenseLink]
hash = "sha1-ea18ab849f0eea030d770da82c2a6b3484a7bd13"
other = "Лицензия туралы ақпарат"
[licenseLinkOther]
hash = "sha1-359fff328717c05104e51a2d29f05bf1875d26b7"
other = "Бағдарламада пайдаланылатын басқа өнімдердің лицензиялары"
[pathToFfmpeg]
hash = "sha1-fafc50f1db0f720fe83a96cd70a9e1ad824e96b6"
other = "FFmpeg жол:"
[pathToFfprobe]
hash = "sha1-b872edc9633a2e81ef678dc46fe46a7e91732024"
other = "FFprobe жол:"
[programmLink]
hash = "sha1-18f9a3fad6aacefe1b05eed23122800b391ff5ca"
other = "Жобаның веб-сайты"
[programmVersion]
hash = "sha1-fa2e4994a301bb24bc2a8fa166e5486ea95a7475"
other = "**Бағдарлама нұсқасы:** {{.Version}}"
[save]
hash = "sha1-4864057d626a868fa60f999bed3191d61d045ddc"
other = "Сақтау"
[selectFFPathTitle]
hash = "sha1-95581446a28d968ff1a027c623159a7eb08654cf"
other = "FFmpeg және FFprobe жолын көрсетіңіз"
[settings]
hash = "sha1-7f17c7c62a7fd8d1a508481f4778688927734c2f"
other = "Параметрлер"
[testFF]
hash = "sha1-f5b8ed88e9609963035d2235be0a79bbec619976"
other = "FFmpeg функционалдығы тексерілуде..."
[titleDownloadLink]
hash = "sha1-92df86371f6c3a06ca1e4754f113142776a32d49"
other = "Сіз оны осы жерден жүктей аласыз"
[unzipRun]
hash = "sha1-c554dad13026668a1f6adf3171837c5d51bbac36"
other = "Орамнан шығарылуда..."

View File

@@ -0,0 +1,42 @@
AlsoUsedProgram = "Также в программе используется:"
about = "О программе"
aboutText = "Простенький интерфейс для консольной утилиты FFmpeg. \nНо я не являюсь автором самой утилиты FFmpeg."
buttonDownloadFFmpeg = "Скачать автоматически FFmpeg"
buttonForSelectedDirTitle = "Папка куда будет сохраняться:"
cancel = "Отмена"
changeFFPath = "FFmpeg и FFprobe"
changeLanguage = "Поменять язык"
checkboxOverwriteOutputFilesTitle = "Разрешить перезаписать файл"
choose = "выбрать"
converterVideoFilesSubmitTitle = "Конвертировать"
converterVideoFilesTitle = "Конвертор видео файлов в mp4"
download = "Скачать"
downloadFFmpegFromSite = "Будет скачано с сайта:"
downloadRun = "Скачивается..."
error = "Произошла ошибка!"
errorConverter = "не смогли отконвертировать видео"
errorDatabase = "не смогли создать файл 'database' в папке 'data'"
errorFFmpeg = "это не FFmpeg"
errorFFmpegVersion = "Не смогли определить версию FFmpeg"
errorFFprobe = "это не FFprobe"
errorFFprobeVersion = "Не смогли определить версию FFprobe"
errorSelectedFolderSave = "Не выбрали папку для сохранения!"
exit = "Выход"
ffmpegLGPL = "Это программное обеспечение использует библиотеки из проекта **FFmpeg** под **[LGPLv2.1](http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html)**."
ffmpegTrademark = "**FFmpeg** — торговая марка **[Fabrice Bellard](http://bellard.org/)** , создателя проекта **[FFmpeg](https://ffmpeg.org/about.html)**."
fileVideoForConversionTitle = "Файл для ковертации:"
help = "Справка"
languageSelectionFormHead = "Переключить язык"
languageSelectionHead = "Выберите язык"
licenseLink = "Сведения о лицензии"
licenseLinkOther = "Лицензии от других продуктов, которые используются в программе"
pathToFfmpeg = "Путь к FFmpeg:"
pathToFfprobe = "Путь к FFprobe:"
programmLink = "Сайт проекта"
programmVersion = "**Версия программы:** {{.Version}}"
save = "Сохранить"
selectFFPathTitle = "Укажите путь к FFmpeg и к FFprobe"
settings = "Настройки"
testFF = "Проверка FFmpeg на работоспособность..."
titleDownloadLink = "Скачать можно от сюда"
unzipRun = "Распаковывается..."

View File

@@ -0,0 +1,3 @@
[testFF]
hash = "sha1-f5b8ed88e9609963035d2235be0a79bbec619976"
other = "Checking FFmpeg for serviceability..."

View File

@@ -0,0 +1,3 @@
[testFF]
hash = "sha1-f5b8ed88e9609963035d2235be0a79bbec619976"
other = "FFmpeg функционалдығы тексерілуде..."

View File

@@ -0,0 +1,26 @@
package localizer
import (
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/src/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)
}

138
src/localizer/service.go Normal file
View File

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

76
src/localizer/view.go Normal file
View File

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

127
src/main.go Normal file
View File

@@ -0,0 +1,127 @@
package main
import (
"errors"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/src/convertor"
error2 "git.kor-elf.net/kor-elf/gui-for-ffmpeg/src/error"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/src/handler"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/src/localizer"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/src/menu"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/src/migration"
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/src/setting"
_ "github.com/mattn/go-sqlite3"
"golang.org/x/text/language"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"os"
)
const appVersion string = "0.3.1"
func main() {
a := app.New()
iconResource, err := fyne.LoadResourceFromPath("icon.png")
if err == nil {
a.SetIcon(iconResource)
}
w := a.NewWindow("GUI for FFmpeg")
w.Resize(fyne.Size{Width: 800, Height: 600})
w.CenterOnScreen()
localizerService, err := localizer.NewService("languages", language.Russian)
if err != nil {
panicErrorLang(w, err)
w.ShowAndRun()
return
}
errorView := error2.NewView(w, localizerService)
if canCreateFile("data/database") != true {
errorView.PanicErrorWriteDirectoryData()
w.ShowAndRun()
return
}
db, err := gorm.Open(sqlite.Open("data/database"), &gorm.Config{})
if err != nil {
errorView.PanicError(err)
w.ShowAndRun()
return
}
defer appCloseWithDb(db)
err = migration.Run(db)
if err != nil {
errorView.PanicError(err)
w.ShowAndRun()
return
}
settingRepository := setting.NewRepository(db)
convertorRepository := convertor.NewRepository(settingRepository)
pathFFmpeg, err := convertorRepository.GetPathFfmpeg()
if err != nil && errors.Is(err, gorm.ErrRecordNotFound) == false {
errorView.PanicError(err)
w.ShowAndRun()
return
}
pathFFprobe, err := convertorRepository.GetPathFfprobe()
if err != nil && errors.Is(err, gorm.ErrRecordNotFound) == false {
errorView.PanicError(err)
w.ShowAndRun()
return
}
ffPathUtilities := convertor.FFPathUtilities{FFmpeg: pathFFmpeg, FFprobe: pathFFprobe}
localizerView := localizer.NewView(w, localizerService)
convertorView := convertor.NewView(w, localizerService)
convertorService := convertor.NewService(ffPathUtilities)
defer appCloseWithConvert(convertorService)
convertorHandler := handler.NewConvertorHandler(convertorService, convertorView, convertorRepository, localizerService)
localizerRepository := localizer.NewRepository(settingRepository)
menuView := menu.NewView(w, a, appVersion, localizerService)
mainMenu := handler.NewMenuHandler(convertorHandler, menuView, localizerService, localizerView, localizerRepository)
mainHandler := handler.NewMainHandler(convertorHandler, mainMenu, localizerRepository, localizerService)
mainHandler.Start()
w.SetMainMenu(mainMenu.GetMainMenu())
w.ShowAndRun()
}
func appCloseWithDb(db *gorm.DB) {
sqlDB, err := db.DB()
if err == nil {
_ = sqlDB.Close()
}
}
func appCloseWithConvert(convertorService convertor.ServiceContract) {
for _, cmd := range convertorService.GetRunningProcesses() {
_ = cmd.Process.Kill()
}
}
func canCreateFile(path string) bool {
file, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0666)
if err != nil {
return false
}
_ = file.Close()
return true
}
func panicErrorLang(w fyne.Window, err error) {
w.SetContent(container.NewVBox(
widget.NewLabel("Произошла ошибка!"),
widget.NewLabel("произошла ошибка при получении языковых переводах. \n\r"+err.Error()),
))
}

641
src/menu/view.go Normal file
View File

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

View File

@@ -0,0 +1,15 @@
package migration
import (
"git.kor-elf.net/kor-elf/gui-for-ffmpeg/src/setting"
"gorm.io/gorm"
)
func Run(db *gorm.DB) error {
err := db.AutoMigrate(&setting.Setting{})
if err != nil {
return err
}
return nil
}

7
src/setting/entity.go Normal file
View File

@@ -0,0 +1,7 @@
package setting
type Setting struct {
ID uint `gorm:"primary_key"`
Code string `gorm:"type:varchar(100);uniqueIndex;not null"`
Value string `gorm:"type:text"`
}

55
src/setting/repository.go Normal file
View File

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