diff --git a/files/file.go b/files/file.go index 569b0be4..e0ef153d 100644 --- a/files/file.go +++ b/files/file.go @@ -26,20 +26,22 @@ import ( // FileInfo describes a file. type FileInfo struct { *Listing - Fs afero.Fs `json:"-"` - Path string `json:"path"` - Name string `json:"name"` - Size int64 `json:"size"` - Extension string `json:"extension"` - ModTime time.Time `json:"modified"` - Mode os.FileMode `json:"mode"` - IsDir bool `json:"isDir"` - IsSymlink bool `json:"isSymlink"` - Type string `json:"type"` - Subtitles []string `json:"subtitles,omitempty"` - Content string `json:"content,omitempty"` - Checksums map[string]string `json:"checksums,omitempty"` - Token string `json:"token,omitempty"` + Fs afero.Fs `json:"-"` + Dir string `json:"dir"` + Path string `json:"path"` + Name string `json:"name"` + Size int64 `json:"size"` + Extension string `json:"extension"` + ModTime time.Time `json:"modified"` + Mode os.FileMode `json:"mode"` + IsDir bool `json:"isDir"` + IsSymlink bool `json:"isSymlink"` + IsThumbsEnabled bool `json:"isThumbsEnabled"` + Type string `json:"type"` + Subtitles []string `json:"subtitles,omitempty"` + Content string `json:"content,omitempty"` + Checksums map[string]string `json:"checksums,omitempty"` + Token string `json:"token,omitempty"` } // FileOptions are the options when getting a file info. @@ -54,6 +56,11 @@ type FileOptions struct { Content bool } +type FileThumbnail struct { + Dir string + Path string +} + // NewFileInfo creates a File object from a path and a given user. This File // object will be automatically filled depending on if it is a directory // or a file. If it's a video file, it will also detect any subtitles. @@ -84,6 +91,34 @@ func NewFileInfo(opts FileOptions) (*FileInfo, error) { return file, err } +func NewThumbnailInfo(opts FileOptions) (*FileInfo, error) { + return NewFileInfo(FileOptions{ + Fs: opts.Fs, + Path: NewFileThumbnail(opts).Path, + Modify: opts.Modify, + Expand: opts.Expand, + ReadHeader: opts.ReadHeader, + Checker: opts.Checker, + }) +} + +func NewFileThumbnail(opts FileOptions) FileThumbnail { + dir, name := filepath.Split(opts.Path) + + hash := md5.Sum([]byte(name)) + + thumbnailName := hex.EncodeToString(hash[:]) + ".jpg" + + thumbnailPath := path.Join(dir, ".filebrowser", thumbnailName) + + dir, _ = filepath.Split(thumbnailPath) + + return FileThumbnail{ + Dir: dir, + Path: thumbnailPath, + } +} + func stat(opts FileOptions) (*FileInfo, error) { var file *FileInfo @@ -92,8 +127,10 @@ func stat(opts FileOptions) (*FileInfo, error) { if err != nil { return nil, err } + dir, _ := filepath.Split(opts.Path) file = &FileInfo{ Fs: opts.Fs, + Dir: dir, Path: opts.Path, Name: info.Name(), ModTime: info.ModTime(), @@ -128,8 +165,10 @@ func stat(opts FileOptions) (*FileInfo, error) { return file, nil } + dir, _ := filepath.Split(opts.Path) file = &FileInfo{ Fs: opts.Fs, + Dir: dir, Path: opts.Path, Name: info.Name(), ModTime: info.ModTime(), @@ -224,12 +263,14 @@ func (i *FileInfo) detectType(modify, saveContent, readHeader bool) error { switch { case strings.HasPrefix(mimetype, "video"): i.Type = "video" + i.detectThumbnail() i.detectSubtitles() return nil case strings.HasPrefix(mimetype, "audio"): i.Type = "audio" return nil case strings.HasPrefix(mimetype, "image"): + i.IsThumbsEnabled = true i.Type = "image" return nil case strings.HasSuffix(mimetype, "pdf"): @@ -301,6 +342,21 @@ func (i *FileInfo) detectSubtitles() { } } +func (i *FileInfo) detectThumbnail() { + dir, name := filepath.Split(i.RealPath()) + + hash := md5.Sum([]byte(name)) + thumbnailName := hex.EncodeToString(hash[:]) + + path := path.Join(dir, ".filebrowser", thumbnailName+".jpg") + + _, err := os.Stat(path) + + if err == nil { + i.IsThumbsEnabled = true + } +} + func (i *FileInfo) readListing(checker rules.Checker, readHeader bool) error { afs := &afero.Afero{Fs: i.Fs} dir, err := afs.ReadDir(i.Path) diff --git a/fileutils/dir.go b/fileutils/dir.go index 07a3528e..d32426dc 100644 --- a/fileutils/dir.go +++ b/fileutils/dir.go @@ -6,10 +6,7 @@ import ( "github.com/spf13/afero" ) -// CopyDir copies a directory from source to dest and all -// of its sub-directories. It doesn't stop if it finds an error -// during the copy. Returns an error if any. -func CopyDir(fs afero.Fs, source, dest string) error { +func CreateDir(fs afero.Fs, source, dest string) error { // Get properties of source. srcinfo, err := fs.Stat(source) if err != nil { @@ -22,6 +19,18 @@ func CopyDir(fs afero.Fs, source, dest string) error { return err } + return nil +} + +// CopyDir copies a directory from source to dest and all +// of its sub-directories. It doesn't stop if it finds an error +// during the copy. Returns an error if any. +func CopyDir(fs afero.Fs, source, dest string) error { + err := CreateDir(fs, source, dest) + if err != nil { + return err + } + dir, _ := fs.Open(source) obs, err := dir.Readdir(-1) if err != nil { diff --git a/frontend/src/components/files/ListingItem.vue b/frontend/src/components/files/ListingItem.vue index 351fba1f..c3c58e0d 100644 --- a/frontend/src/components/files/ListingItem.vue +++ b/frontend/src/components/files/ListingItem.vue @@ -8,6 +8,7 @@ @dragover="dragOver" @drop="drop" @click="itemClick" + :data-thumbs-enabled="isThumbsEnabled" :data-dir="isDir" :data-type="type" :aria-label="name" @@ -15,7 +16,7 @@ >