Files
field-sync-gui/internal/detect/detect.go

76 lines
1.9 KiB
Go

package detect
import (
"path/filepath"
"strings"
"github.com/shirou/gopsutil/v3/disk"
)
// SDCard represents a detected removable storage device
type SDCard struct {
Mountpoint string `json:"mountpoint"`
Label string `json:"label"`
Device string `json:"device"`
Fstype string `json:"fstype"`
TotalBytes uint64 `json:"totalBytes"`
FreeBytes uint64 `json:"freeBytes"`
MediaFiles []string `json:"mediaFiles"`
}
// mediaExtensions is the set of file extensions we care about
var mediaExtensions = map[string]bool{
".mp4": true, ".mov": true, ".jpg": true, ".jpeg": true,
".png": true, ".raw": true, ".gpr": true, ".360": true,
".lrv": true, ".thm": true,
}
// Detect returns all removable partitions that look like SD cards / media
func Detect() []SDCard {
partitions, err := disk.Partitions(true)
if err != nil {
return nil
}
var cards []SDCard
for _, p := range partitions {
if !isRemovable(p) {
continue
}
usage, err := disk.Usage(p.Mountpoint)
if err != nil {
continue
}
card := SDCard{
Mountpoint: p.Mountpoint,
Label: filepath.Base(p.Mountpoint),
Device: p.Device,
Fstype: p.Fstype,
TotalBytes: usage.Total,
FreeBytes: usage.Free,
}
cards = append(cards, card)
}
return cards
}
// isRemovable heuristic: opts contain "removable" or path looks like /media/
func isRemovable(p disk.PartitionStat) bool {
opts := strings.Join(p.Opts, ",")
if strings.Contains(opts, "removable") {
return true
}
mp := p.Mountpoint
if strings.HasPrefix(mp, "/media/") || strings.HasPrefix(mp, "/run/media/") ||
strings.HasPrefix(mp, "/Volumes/") || strings.HasPrefix(mp, "/mnt/sd") {
return true
}
return false
}
// HasMediaExtension returns true if path has a known media extension
func HasMediaExtension(path string) bool {
ext := strings.ToLower(filepath.Ext(path))
return mediaExtensions[ext]
}