From 19a1141af3f1aa52c706e402af734797d5d929ff Mon Sep 17 00:00:00 2001 From: Heinrich 'Henrik' Langos Date: Mon, 16 Jan 2023 11:33:12 +0000 Subject: preserve-video-filename (#49) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This should fix issue #40 I have not made the `allowed_characters` configurable yet nor the `replacement_character`. Mostly because I couldn't decide if I should define those "globally" on a server basis, or on the library nodes. Feel free to modify, extend, rip apart. 😁 Reviewed-on: https://git.mills.io/prologic/tube/pulls/49 Co-authored-by: Heinrich 'Henrik' Langos Co-committed-by: Heinrich 'Henrik' Langos --- app/app.go | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++------ app/config.go | 32 ++++++++++++++++++-------------- 2 files changed, 70 insertions(+), 20 deletions(-) (limited to 'app') diff --git a/app/app.go b/app/app.go index f2d729a..9d0828b 100644 --- a/app/app.go +++ b/app/app.go @@ -21,6 +21,7 @@ import ( "git.mills.io/prologic/tube/templates" "git.mills.io/prologic/tube/utils" + "github.com/cyphar/filepath-securejoin" "github.com/dustin/go-humanize" "github.com/fsnotify/fsnotify" "github.com/gorilla/handlers" @@ -148,8 +149,9 @@ func (a *App) Run() error { for _, pc := range a.Config.Library { pc.Path = filepath.Clean(pc.Path) p := &media.Path{ - Path: pc.Path, - Prefix: pc.Prefix, + Path: pc.Path, + Prefix: pc.Prefix, + PreserveUploadFilename: pc.PreserveUploadFilename, } err := a.Library.AddPath(p) if err != nil { @@ -215,6 +217,11 @@ func (a *App) indexHandler(w http.ResponseWriter, r *http.Request) { } } +func filenameWithoutExtension(path string) (stem string) { + var basename string = filepath.Base(path) + return basename[0:len(basename)-len(filepath.Ext(basename))] +} + // HTTP handler for /upload func (a *App) uploadHandler(w http.ResponseWriter, r *http.Request) { if r.Method == "GET" { @@ -278,10 +285,49 @@ func (a *App) uploadHandler(w http.ResponseWriter, r *http.Request) { return } - vf := filepath.Join( - a.Library.Paths[targetLibraryPath].Path, - fmt.Sprintf("%s.mp4", shortuuid.New()), - ) + // Here we set the final filename for the video file after transcoding. + var vf string + if a.Config.Server.PreserveUploadFilename || + a.Library.Paths[targetLibraryPath].PreserveUploadFilename { + vf, err = securejoin.SecureJoin( + a.Library.Paths[targetLibraryPath].Path, + fmt.Sprintf("%s.mp4", filenameWithoutExtension(handler.Filename)), + ) + } else { + vf, err = securejoin.SecureJoin( + a.Library.Paths[targetLibraryPath].Path, + fmt.Sprintf("%s.mp4", shortuuid.New()), + ) + } + if err != nil { + err := fmt.Errorf("error creating file name in target library: %w", err) + log.Error(err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + // If the (sanitized) original filename collides with an existing file, + // we try to add a shortuuid() to it until we find one that doesn't exist. + for _, err := os.Stat(vf) ; ! os.IsNotExist(err) ; _, err = os.Stat(vf) { + if err != nil { + log.Error(err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + log.Warn("File '"+ vf + "' already exists."); + vf, err = securejoin.SecureJoin( + a.Library.Paths[targetLibraryPath].Path, + fmt.Sprintf("%s_%s.mp4", filenameWithoutExtension(vf), shortuuid.New()), + ) + if err != nil { + err := fmt.Errorf("error creating file name in target library: %w", err) + log.Error(err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + log.Warn("Using filename '" + vf + "' instead."); + } + + thumbFn1 := fmt.Sprintf("%s.jpg", strings.TrimSuffix(tf.Name(), filepath.Ext(tf.Name()))) thumbFn2 := fmt.Sprintf("%s.jpg", strings.TrimSuffix(vf, filepath.Ext(vf))) diff --git a/app/config.go b/app/config.go index 2b11688..7d886c0 100644 --- a/app/config.go +++ b/app/config.go @@ -17,17 +17,19 @@ type Config struct { // PathConfig settings for media library path. type PathConfig struct { - Path string `json:"path"` - Prefix string `json:"prefix"` + Path string `json:"path"` + Prefix string `json:"prefix"` + PreserveUploadFilename bool `json:"preserve_upload_filename,omitempty"` } // ServerConfig settings for App Server. type ServerConfig struct { - Host string `json:"host"` - Port int `json:"port"` - StorePath string `json:"store_path"` - UploadPath string `json:"upload_path"` - MaxUploadSize int64 `json:"max_upload_size"` + Host string `json:"host"` + Port int `json:"port"` + StorePath string `json:"store_path"` + UploadPath string `json:"upload_path"` + PreserveUploadFilename bool `json:"preserve_upload_filename,omitempty"` + MaxUploadSize int64 `json:"max_upload_size"` } // ThumbnailerConfig settings for Transcoder @@ -68,16 +70,18 @@ func DefaultConfig() *Config { return &Config{ Library: []*PathConfig{ &PathConfig{ - Path: "videos", - Prefix: "", + Path: "videos", + Prefix: "", + PreserveUploadFilename: false, }, }, Server: &ServerConfig{ - Host: "0.0.0.0", - Port: 8000, - StorePath: "tube.db", - UploadPath: "uploads", - MaxUploadSize: 104857600, + Host: "0.0.0.0", + Port: 8000, + StorePath: "tube.db", + UploadPath: "uploads", + PreserveUploadFilename: false, + MaxUploadSize: 104857600, }, Thumbnailer: &ThumbnailerConfig{ Timeout: 60, -- cgit v1.2.3