giffer

Create .gif images from sites like youtube.com
Log | Files | Refs | LICENSE

commit 2fd50cd77ccdb32dcb67917594e395a8fc6f816c
parent 7cda7146def9244f915310fc1f6c22d7e1acfef4
Author: Jack Mordaunt <jackmordaunt@gmail.com>
Date:   Wed, 21 Nov 2018 15:27:12 +1300

[~] Use Engine instead of Transcoder; and accpet Fuzz value in http
request.

Diffstat:
Mcmd/desktop/giffer.go | 26+++++++++++++++++++-------
Mcmd/desktop/main.go | 10++++++----
Mcmd/desktop/server.go | 13+++++++------
Mengine.go | 15+++++++--------
4 files changed, 39 insertions(+), 25 deletions(-)

diff --git a/cmd/desktop/giffer.go b/cmd/desktop/giffer.go @@ -4,6 +4,7 @@ import ( "bytes" "fmt" "io" + "io/ioutil" "path/filepath" "strings" @@ -15,7 +16,7 @@ import ( // Giffer wraps the giffer business logic. type Giffer struct { *giffer.Downloader - *giffer.Transcoder + *giffer.Engine Store GifStore } @@ -30,11 +31,11 @@ type GifStore interface { func (g Giffer) GififyURL( url string, start, end, fps float64, - width, height int, + width, height, fuzz int, q giffer.Quality, ) (*RenderedGif, error) { if g.Store == nil { - return g.make(url, start, end, fps, width, height, q) + return g.make(url, start, end, fps, width, height, fuzz, q) } key, err := hash(fmt.Sprintf("%s_%f_%f_%f_%d_%d_%d", url, start, end, fps, width, height, q)) if err != nil { @@ -47,7 +48,7 @@ func (g Giffer) GififyURL( if ok && img != nil { return img, nil } - img, err = g.make(url, start, end, fps, width, height, q) + img, err = g.make(url, start, end, fps, width, height, fuzz, q) if err != nil { return nil, err } @@ -64,16 +65,27 @@ func (g Giffer) GififyURL( func (g Giffer) make( url string, start, end, fps float64, - width, height int, + width, height, fuzz int, q giffer.Quality, ) (*RenderedGif, error) { video, err := g.Download(url, start, end, q) if err != nil { return nil, errors.Wrap(err, "downloading") } - gif, err := g.Convert(video, fps, width, height, "gif", "gif") + gif, err := g.Transcode(video, start, end, width, height, fps) + if err != nil { + return nil, errors.Wrap(err, "transcoding video to gif") + } + if err := g.Crush(gif, fuzz); err != nil { + return nil, errors.Wrap(err, "optimising gif image") + } + defer g.Clean() + gifdata, err := ioutil.ReadFile(gif) + if err != nil { + return nil, errors.Wrap(err, "buffering gif") + } img := &RenderedGif{ - Reader: gif, + Reader: bytes.NewBuffer(gifdata), FileName: sanitiseFilepath(strings.Split(filepath.Base(video), ".")[0] + ".gif"), } return img, nil diff --git a/cmd/desktop/main.go b/cmd/desktop/main.go @@ -75,13 +75,15 @@ func main() { App: &Giffer{ Downloader: &giffer.Downloader{ Dir: filepath.Join(filepath.Dir(ffmpeg), "tmp/downloads"), - Debug: verbose, FFmpeg: ffmpeg, + Debug: verbose, Out: logf, }, - Transcoder: &giffer.Transcoder{ - Debug: verbose, - FFmpeg: ffmpeg, + Engine: &giffer.Engine{ + FFmpeg: ffmpeg, + Convert: "convert", + Debug: verbose, + Out: logf, }, Store: &gifdb{ Dir: filepath.Join(filepath.Dir(ffmpeg), "tmp/gifs"), diff --git a/cmd/desktop/server.go b/cmd/desktop/server.go @@ -25,7 +25,7 @@ type UI struct { Router *mux.Router Static http.Handler Verbose bool - Out io.Writer + Out io.Writer gifmap map[string]http.Handler init sync.Once @@ -72,6 +72,7 @@ func (ui *UI) gifify() http.HandlerFunc { Width int `json:"width,omitempty"` Height int `json:"height,omitempty"` Output string `json:"output,omitempty"` + Fuzz int `json:"fuzz,omitempty"` Quality int `json:"quality,omitempty"` } var req request @@ -104,6 +105,7 @@ func (ui *UI) gifify() http.HandlerFunc { req.FPS, req.Width, req.Height, + req.Fuzz, giffer.Quality(req.Quality)) }) key := fmt.Sprintf("%d", h.Sum64()) @@ -112,8 +114,8 @@ func (ui *UI) gifify() http.HandlerFunc { File string `json:"file"` Info string `json:"info"` } - // FIXME(jfm): Should these endpoints be typed, instead of - // relying on assumptions about the routing? + // FIXME(jfm): Should these endpoints be typed values, instead + // of relying on assumptions about the routing? writeJSON(w, response{ File: fmt.Sprintf("/gifs/%s", key), Info: fmt.Sprintf("/gifs/%s/info", key), @@ -147,7 +149,7 @@ func (ui *UI) gifs() http.HandlerFunc { // gif isn't ready to be downloaded, an appropriate message is returned. type Gif struct { Upgrader *websocket.Upgrader - Out io.Writer + Out io.Writer file *RenderedGif subs map[*websocket.Conn]struct{} @@ -253,4 +255,4 @@ func (g *Gif) logf(f string, v ...interface{}) (int, error) { return 0, nil } return fmt.Fprintf(g.Out, f, v...) -} -\ No newline at end of file +} diff --git a/engine.go b/engine.go @@ -81,7 +81,7 @@ func (eng *Engine) Cut(video string, cuts ...[2]int) (string, error) { // Returns a filepath to the gif image. func (eng *Engine) Transcode( video string, - start, end int, + start, end float64, width, height int, fps float64, ) (string, error) { @@ -116,8 +116,8 @@ func (eng *Engine) Transcode( }() genPalette := eng.command( eng.FFmpeg, - "-ss", fmt.Sprintf("%d;omitempty", start), - "-t", fmt.Sprintf("%d;omitempty", duration), + "-ss", fmt.Sprintf("%2f;omitempty", start), + "-t", fmt.Sprintf("%2f;omitempty", duration), "-i", video, "-vf", palettegen, "-y", "palette.png", @@ -127,8 +127,8 @@ func (eng *Engine) Transcode( } makeGif := eng.command( eng.FFmpeg, - "-ss", fmt.Sprintf("%d;omitempty", start), - "-t", fmt.Sprintf("%d;omitempty", duration), + "-ss", fmt.Sprintf("%2f;omitempty", start), + "-t", fmt.Sprintf("%2f;omitempty", duration), "-i", video, "-i", "palette.png", "-lavfi", fmt.Sprintf("%s [x]; [x][1:v] paletteuse", filters), "-y", output, @@ -157,13 +157,12 @@ func (eng *Engine) Crush(gif string, fuzz int) error { } // Clean the temporary files. -func (eng *Engine) Clean() error { +func (eng *Engine) Clean() { for _, f := range eng.Junk { if err := os.Remove(f); err != nil { - return err + eng.logf("clean: %v\n", err) } } - return nil } // command creates a new exec.Cmd after removing empty arguments.