From d46d642e618c16f6b9140065846a0b9db8eed1e3 Mon Sep 17 00:00:00 2001 From: Leonid Nikitin Date: Sun, 4 Feb 2024 20:16:15 +0600 Subject: [PATCH] Added the ability to automatically download FFmpeg from the site https://github.com/BtbN/FFmpeg-Builds/releases. --- src/convertor/view.go | 1 + src/convertor/view_setting.go | 7 +- ...ew_setting_button_download_ffmpeg_anyos.go | 17 ++ ..._setting_button_download_ffmpeg_windows.go | 63 ++++++++ src/handler/convertor.go | 4 +- src/handler/convertor_anyos.go | 10 +- src/handler/convertor_windows.go | 145 +++++++++++++++++- src/languages/active.en.toml | 24 +++ src/languages/active.kk.toml | 24 +++ src/languages/active.ru.toml | 6 + src/languages/translate.en.toml | 10 +- src/languages/translate.kk.toml | 10 +- 12 files changed, 302 insertions(+), 19 deletions(-) create mode 100644 src/convertor/view_setting_button_download_ffmpeg_anyos.go create mode 100644 src/convertor/view_setting_button_download_ffmpeg_windows.go diff --git a/src/convertor/view.go b/src/convertor/view.go index 7484f7a..aa1d536 100644 --- a/src/convertor/view.go +++ b/src/convertor/view.go @@ -22,6 +22,7 @@ type ViewContract interface { ffprobePath string, save func(ffmpegPath string, ffprobePath string) error, cancel func(), + donwloadFFmpeg func(progressBar *widget.ProgressBar, progressMessage *canvas.Text) error, ) } diff --git a/src/convertor/view_setting.go b/src/convertor/view_setting.go index af68822..5e3f095 100644 --- a/src/convertor/view_setting.go +++ b/src/convertor/view_setting.go @@ -17,6 +17,7 @@ func (v View) SelectFFPath( 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 @@ -80,7 +81,11 @@ func (v View) SelectFFPath( selectFFPathTitle := v.localizerService.GetMessage(&i18n.LocalizeConfig{ MessageID: "selectFFPathTitle", }) - v.w.SetContent(widget.NewCard(selectFFPathTitle, "", container.NewVBox(form))) + + 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) { diff --git a/src/convertor/view_setting_button_download_ffmpeg_anyos.go b/src/convertor/view_setting_button_download_ffmpeg_anyos.go new file mode 100644 index 0000000..5e3dc94 --- /dev/null +++ b/src/convertor/view_setting_button_download_ffmpeg_anyos.go @@ -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() +} diff --git a/src/convertor/view_setting_button_download_ffmpeg_windows.go b/src/convertor/view_setting_button_download_ffmpeg_windows.go new file mode 100644 index 0000000..a573284 --- /dev/null +++ b/src/convertor/view_setting_button_download_ffmpeg_windows.go @@ -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, + )), + ) +} diff --git a/src/handler/convertor.go b/src/handler/convertor.go index 37c686b..1b7c3d0 100644 --- a/src/handler/convertor.go +++ b/src/handler/convertor.go @@ -47,13 +47,13 @@ func (h ConvertorHandler) MainConvertor() { h.convertorView.Main(h.runConvert) return } - h.convertorView.SelectFFPath("", "", h.saveSettingFFPath, nil) + 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.convertorView.SelectFFPath(ffmpeg, ffprobe, h.saveSettingFFPath, h.MainConvertor, h.downloadFFmpeg) } func (h ConvertorHandler) GetFfmpegVersion() (string, error) { diff --git a/src/handler/convertor_anyos.go b/src/handler/convertor_anyos.go index 6f6b25b..4db2fd4 100644 --- a/src/handler/convertor_anyos.go +++ b/src/handler/convertor_anyos.go @@ -3,8 +3,16 @@ package handler -import "git.kor-elf.net/kor-elf/gui-for-ffmpeg/convertor" +import ( + "fyne.io/fyne/v2/canvas" + "fyne.io/fyne/v2/widget" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/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 +} diff --git a/src/handler/convertor_windows.go b/src/handler/convertor_windows.go index d030a35..3453a40 100644 --- a/src/handler/convertor_windows.go +++ b/src/handler/convertor_windows.go @@ -3,8 +3,151 @@ package handler -import "git.kor-elf.net/kor-elf/gui-for-ffmpeg/convertor" +import ( + "archive/zip" + "errors" + "fyne.io/fyne/v2/canvas" + "fyne.io/fyne/v2/widget" + "git.kor-elf.net/kor-elf/gui-for-ffmpeg/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() +} diff --git a/src/languages/active.en.toml b/src/languages/active.en.toml index 5d5e531..2ffafeb 100644 --- a/src/languages/active.en.toml +++ b/src/languages/active.en.toml @@ -10,6 +10,10 @@ other = "About" hash = "sha1-8bd565814118ba8b90c40eb5b62acf8d2676e7d6" other = "A simple interface for the FFmpeg console utility. \nBut I am not the author of the FFmpeg utility itself." +[buttonDownloadFFmpeg] +hash = "sha1-c223c2e15171156192bc3146aee91e6094bb475b" +other = "Download FFmpeg automatically" + [buttonForSelectedDirTitle] hash = "sha1-52b13f1b13e82d22e8c4102332db5d4ec551247b" other = "Folder where it will be saved:" @@ -42,6 +46,18 @@ other = "Convert" 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!" @@ -138,6 +154,14 @@ other = "Specify the path to FFmpeg and FFprobe" 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..." diff --git a/src/languages/active.kk.toml b/src/languages/active.kk.toml index cb09b22..990a3ad 100644 --- a/src/languages/active.kk.toml +++ b/src/languages/active.kk.toml @@ -10,6 +10,10 @@ other = "Бағдарлама туралы" hash = "sha1-8bd565814118ba8b90c40eb5b62acf8d2676e7d6" other = "FFmpeg консоль утилитасы үшін қарапайым интерфейс. \nБірақ мен FFmpeg утилитасының авторы емеспін." +[buttonDownloadFFmpeg] +hash = "sha1-c223c2e15171156192bc3146aee91e6094bb475b" +other = "FFmpeg автоматты түрде жүктеп алыңыз" + [buttonForSelectedDirTitle] hash = "sha1-52b13f1b13e82d22e8c4102332db5d4ec551247b" other = "Файлды сақтауға арналған каталог:" @@ -42,6 +46,18 @@ other = "Файлды түрлендіру" 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 = "Қате орын алды!" @@ -138,6 +154,14 @@ other = "FFmpeg және FFprobe жолын көрсетіңіз" hash = "sha1-7f17c7c62a7fd8d1a508481f4778688927734c2f" other = "Параметрлер" +[testFF] +hash = "sha1-f5b8ed88e9609963035d2235be0a79bbec619976" +other = "FFmpeg функционалдығы тексерілуде..." + [titleDownloadLink] hash = "sha1-92df86371f6c3a06ca1e4754f113142776a32d49" other = "Сіз оны осы жерден жүктей аласыз" + +[unzipRun] +hash = "sha1-c554dad13026668a1f6adf3171837c5d51bbac36" +other = "Орамнан шығарылуда..." diff --git a/src/languages/active.ru.toml b/src/languages/active.ru.toml index 1a5a36d..7d94ea8 100644 --- a/src/languages/active.ru.toml +++ b/src/languages/active.ru.toml @@ -1,6 +1,7 @@ AlsoUsedProgram = "Также в программе используется:" about = "О программе" aboutText = "Простенький интерфейс для консольной утилиты FFmpeg. \nНо я не являюсь автором самой утилиты FFmpeg." +buttonDownloadFFmpeg = "Скачать автоматически FFmpeg" buttonForSelectedDirTitle = "Папка куда будет сохраняться:" cancel = "Отмена" changeFFPath = "FFmpeg и FFprobe" @@ -9,6 +10,9 @@ checkboxOverwriteOutputFilesTitle = "Разрешить перезаписать choose = "выбрать" converterVideoFilesSubmitTitle = "Конвертировать" converterVideoFilesTitle = "Конвертор видео файлов в mp4" +download = "Скачать" +downloadFFmpegFromSite = "Будет скачано с сайта:" +downloadRun = "Скачивается..." error = "Произошла ошибка!" errorConverter = "не смогли отконвертировать видео" errorDatabase = "не смогли создать файл 'database' в папке 'data'" @@ -33,4 +37,6 @@ programmVersion = "**Версия программы:** {{.Version}}" save = "Сохранить" selectFFPathTitle = "Укажите путь к FFmpeg и к FFprobe" settings = "Настройки" +testFF = "Проверка FFmpeg на работоспособность..." titleDownloadLink = "Скачать можно от сюда" +unzipRun = "Распаковывается..." diff --git a/src/languages/translate.en.toml b/src/languages/translate.en.toml index 6bee711..81cb622 100644 --- a/src/languages/translate.en.toml +++ b/src/languages/translate.en.toml @@ -1,7 +1,3 @@ -[AlsoUsedProgram] -hash = "sha1-a72be72e7808bb8a0144ed7a93acb29c568b1ed4" -other = "The program also uses:" - -[licenseLinkOther] -hash = "sha1-359fff328717c05104e51a2d29f05bf1875d26b7" -other = "Licenses from other products used in the program" +[testFF] +hash = "sha1-f5b8ed88e9609963035d2235be0a79bbec619976" +other = "Checking FFmpeg for serviceability..." diff --git a/src/languages/translate.kk.toml b/src/languages/translate.kk.toml index b78198a..8e64112 100644 --- a/src/languages/translate.kk.toml +++ b/src/languages/translate.kk.toml @@ -1,7 +1,3 @@ -[AlsoUsedProgram] -hash = "sha1-a72be72e7808bb8a0144ed7a93acb29c568b1ed4" -other = "Бағдарлама сонымен қатар пайдаланады:" - -[licenseLinkOther] -hash = "sha1-359fff328717c05104e51a2d29f05bf1875d26b7" -other = "Бағдарламада пайдаланылатын басқа өнімдердің лицензиялары" +[testFF] +hash = "sha1-f5b8ed88e9609963035d2235be0a79bbec619976" +other = "FFmpeg функционалдығы тексерілуде..."