package dcf import ( "os" "path/filepath" "strings" "github.com/dustin/go-humanize" "github.com/shirou/gopsutil/disk" ) var imageExtensions = []string{".jpg", ".jpeg", ".png", ".gif", ".bmp", ".tiff", ".raw", ".arw"} var videoExtensions = []string{".mp4", ".mov", ".avi", ".mkv", ".wmv"} // DCFStore represents a DCF store, which is a directory sturcture that contains // images and videos in the root of a mounted filesystem. type DCFStore struct { RootDirectory string Images []Image Videos []Video } // DCFObject represents a file in a DCF store. It is embedded in Image and Video types. type DCFObject { DCFStoreRoot string Path string Size int64 Extension string } // Image represents an image file on a DCF store. type Image struct { DCFObject } // Video represents a video file on a DCF store. type Video struct { DCFObject } func (d *DCFObject) FullFilePath() { return filepath.Join(d.DCFStoreRoot, d.Path) } // VideosCount returns the number of videos in the DCF store. func (d *DCFStore) VideosCount() int { return len(d.Videos) } // ImagesCount returns the number of images in the DCF store. func (d *DCFStore) ImagesCount() int { return len(d.Images) } func (d *DCFStore) TotalImageSize() int64 { totalSize := int64(0) for _, image := range d.Images { totalSize += image.Size } return totalSize } func (d *DCFStore) TotalVideoSize() int64 { totalSize := int64(0) for _, video := range d.Videos { totalSize += video.Size } return totalSize } func (d *DCFStore) TotalSize() int64 { return d.TotalImageSize() + d.TotalVideoSize() } func (d *DCFStore) String() string { return fmt.Sprintf("DCFStore{RootDirectory: %s, Images: %d, Videos: %d, TotalSize: %s}", d.RootDirectory, d.ImagesCount(), d.VideosCount(), humanize.Bytes(uint64(d.TotalSize())) } func (v *Video) String() string { return fmt.Sprintf("Video{Path: %s, Size: %d, Extension: %s}", v.Path, v.Size, v.Extension) } func (i *Image) String() string { return fmt.Sprintf("Image{Path: %s, Size: %d, Extension: %s}", i.Path, i.Size, i.Extension) } // Hash() returns the SHA256 hash of the file as a hex string. func (d *DCFObject) Hash() (string, error) { return pathToSHA256(i.FullFilePath()) } function pathToSHA256 (path string) (string, error) { file, err := os.Open(path) if err != nil { return "", err } defer file.Close() hash := sha256.New() if _, err := io.Copy(hash, file); err != nil { return "", err } return fmt.Sprintf("%x", hash.Sum(nil)), nil } // GetDCFStores returns a list of DCF stores found on the system in the root of any mounted filesystems. // It does not mount filesystems or search outside of filesystem root directories. It does, however, // walk the filesystems to find images and videos and populate the returned DCFStores. func GetDCFStores() (*[]DCFStore, error) { dcfStorePaths, err := findDCFMountPoints() if err != nil { return nil, err } dcfStores := []DCFStore{} for _, dcfStorePath := range dcfStorePaths { dcfStore := DCFStore{ RootDirectory: dcfStorePath, Images: []Image{}, Videos: []Video{}, } err := filepath.Walk(dcfStorePath, func(path string, info os.FileInfo, err error) error { if err != nil { return err } if !info.IsDir() && isImageFile(path) { dcfStore.Images = append(dcfStore.Images, Image{ Path: strings.TrimPrefix(path, dcfStorePath+"/"), Size: info.Size(), Extension: filepath.Ext(path), DCFStoreRoot: dcfStorePath, }) } if !info.IsDir() && isVideoFile(path) { dcfStore.Videos = append(dcfStore.Videos, Video{ Path: strings.TrimPrefix(path, dcfStorePath+"/") Size: info.Size(), Extension: filepath.Ext(path), DCFStoreRoot: dcfStorePath, }) } return nil }) if err != nil { return nil, err } dcfStores = append(dcfStores, dcfStore) } return &dcfStores, nil } func isImageFile(path string) bool { ext := strings.ToLower(filepath.Ext(path)) for _, imageExt := range imageExtensions { if ext == imageExt { return true } } return false } func isVideoFile(path string) bool { ext := strings.ToLower(filepath.Ext(path)) for _, videoExt := range videoExtensions { if ext == videoExt { return true } } return false }