diff --git a/README.md b/README.md index 4d11239..4b0707d 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ pic - работа с картинками ## История - 29-07-2023 Первый коммит. Сделал новый релиз для проверки - v0.0.2 +- 30-07-2023 Начинаю использовать ffmpeg. ## Комментарии diff --git a/go.mod b/go.mod index 5689f94..0a57200 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,7 @@ require ( require ( github.com/davecgh/go-spew v1.1.1 // indirect + github.com/estebangarcia21/subprocess v0.0.0-20230526204252-a1a6de4773be // indirect github.com/pmezard/go-difflib v1.0.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index fc93e0a..501f0ba 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,8 @@ 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/estebangarcia21/subprocess v0.0.0-20230526204252-a1a6de4773be h1:Mf6Xc0DgENbsDgKn10KNCNmW9FYSTcMr2bFsJ9uzvX0= +github.com/estebangarcia21/subprocess v0.0.0-20230526204252-a1a6de4773be/go.mod h1:PlHe6+WP6t7m4ghYrX6GBzB0KZLdOKWz2Ih3h0nusAY= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= @@ -15,11 +17,7 @@ github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A 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/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/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 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= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= diff --git a/picture.go b/picture.go index 52addc8..0798e61 100644 --- a/picture.go +++ b/picture.go @@ -2,11 +2,15 @@ package pic import ( "fmt" + "io/ioutil" + "regexp" "strings" + + "github.com/estebangarcia21/subprocess" //"github.com/jmoiron/sqlx" ) -//Picture - работа с картинками +// Picture - работа с картинками type Picture struct { ID int `db:"id" json:"id"` Album string `db:"album" json:"album"` @@ -18,18 +22,19 @@ type Picture struct { ThumbHeight int `db:"thumb_height" json:"imgtHeight"` } -//Hello - приветствие и комментарии к модулю +// Hello - приветствие и комментарии к модулю func Hello() { fmt.Printf("pic - пакет для работы с картинками\n") fmt.Printf("v0.0.1 - начальная версия\n") } func NewPicture(album string) Picture { - pic := Picture {Album: album} + pic := Picture{Album: album} return pic } // возвращает имя файла без расширения +// т.е. из test/filename.jpg получаем filename func baseName(filename string) string { // на всякий случай заменим обратный слэш filename = strings.ReplaceAll(filename, "\\", "/") @@ -39,8 +44,89 @@ func baseName(filename string) string { if len(items) > 0 { // есть каталоги, уберем filename = items[len(items)-1] - } + } fn := strings.Split(filename, ".") return fn[0] } +// cmdFfmpeg формирует команду ffmpeg для последующего исполнения +// ffmpeg - увеличивает gif в 2 раза и сразу преобразует в .webp +// filename - сам файл gif +// Возвращает команду ffmpeg и выходной файл webp +func cmdFfmpeg(src string) (string, string, error) { + if !strings.HasSuffix(src, ".gif") { + return "", "", fmt.Errorf("файл не .gif") + } + webp := strings.ReplaceAll(src, ".gif", "-2x.webp") + cmd := fmt.Sprintf("ffmpeg -i \"%s\" -vf scale=iw*2:ih*2 -loop 0 \"%s\"", src, webp) + return cmd, webp, nil +} + +type WebpFile struct { + filename string + width string + height string +} + +// конвертация из .gif в .webp с изменением размеров файла +// filename - исходный файл gif +func convert(filename string) (WebpFile, error) { + webpfile := WebpFile{} + + cmd, webp, err := cmdFfmpeg(filename) + if err != nil { + return webpfile, err + } + + s := subprocess.New(cmd, subprocess.HideStderr) + + if err := s.Exec(); err != nil { + return webpfile, fmt.Errorf("(%s) ffmpeg: %v", filename, err) + } + + if s.ExitCode() != 0 { + return webpfile, fmt.Errorf("(%s): ffmpeg exit code: %d", filename, s.ExitCode()) + } + + // из вывода команды ffmpeg получим новый размер файла webp + w, h := getSize(s.StderrText()) + webpfile = WebpFile{ + filename: webp, + width: w, + height: h, + } + // fid := upload(newname) + // println(fid) + return webpfile, nil +} + +// получает размер файла из вывода команды ffmpeg +// если пустая строка - бросаем панику +func getSize(out string) (string, string) { + if out == "" { + panic("пустая строка вывода ffmpeg") + } + lines := strings.Split(out, "\n") + for _, s := range lines { + if strings.Contains(s, "Stream #0:0: Video: webp") { + // fmt.Printf("== %s\n", s) + re := regexp.MustCompile(`(\d*)x(\d*)`) + matches := re.FindAllStringSubmatch(s, -1) + // fmt.Printf("matches: %v\n", matches) + return matches[0][1], matches[0][2] + } + } + return "0", "0" +} + +func copyFile(src, dst string) error { + input, err := ioutil.ReadFile(src) + if err != nil { + return err + } + err = ioutil.WriteFile(dst, input, 0644) + if err != nil { + return err + } + return nil +} diff --git a/picture_test.go b/picture_test.go index 1e06585..74247b1 100644 --- a/picture_test.go +++ b/picture_test.go @@ -2,15 +2,18 @@ package pic import ( //"log" + "log" + "path/filepath" "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestNewPicture(t *testing.T) { // для тестирования базу данных не учитываем - p := NewPicture("проверка") - assert.Equal(t, p.Album, "проверка", "должны быть одинаковыми") + p := NewPicture("альбом") + assert.Equal(t, p.Album, "альбом", "должны быть одинаковыми") } func TestBaseName(t *testing.T) { @@ -29,3 +32,48 @@ func TestBaseName(t *testing.T) { base = baseName("test.jpg") assert.NotEqual(t, base, "неверное имя", "должны быть разными") } + +func TestCmdFfmpeg(t *testing.T) { + // правильный файл + cmd, out, err := cmdFfmpeg("testdir\\test.gif") + log.Printf("cmd: %s\n", cmd) + + assert.Nil(t, err) + assert.Equal(t, out, "testdir\\test-2x.webp", "должны быть одинаковыми") + assert.Contains(t, cmd, "ffmpeg", "команда должна сформироваться") + + // не верный файл + cmd, out, err = cmdFfmpeg("test.jpg") + assert.Error(t, err, "должна быть ошибка") + assert.Equal(t, out, "", "должен быть пустой строкой") + assert.Equal(t, cmd, "", "должен быть пустой строкой") +} + +func TestGetSize(t *testing.T) { + out := ` + Stream #0:0: Video: webp <какой-то текст> 1200x1980 <еще какой-то текст> + ` + w, h := getSize(out) + assert.Equal(t, w, "1200", "должны быть 1200") + assert.Equal(t, h, "1980", "должны быть 1980") +} + +func TestConvert(t *testing.T) { + // предварительно скопируем тестовый файл во временный каталог + tmpgif := "test.gif" + require.FileExists(t, tmpgif) + tmpdir := t.TempDir() + filename := filepath.Join(tmpdir, tmpgif) + + err := copyFile(tmpgif, filename) + require.NoError(t, err) + require.FileExists(t, filename) + + webp, err := convert(filename) + assert.NoError(t, err) + require.FileExists(t, webp.filename) + + // скопируем обратно, что бы посмотреть, что получилось + err = copyFile(webp.filename, "d:/projects/pic/test.webp") + assert.NoError(t, err) +} diff --git a/test.gif b/test.gif new file mode 100644 index 0000000..39eb41b Binary files /dev/null and b/test.gif differ diff --git a/test.webp b/test.webp new file mode 100644 index 0000000..b43914f Binary files /dev/null and b/test.webp differ