Fix pinned icons, add bulk download, add media metadata
Some checks failed
Automated Container Build / build-and-push (push) Has been cancelled

This commit is contained in:
Elijah 2026-05-23 08:56:58 -07:00
parent 2b49e90b81
commit ed5cc79c25
6 changed files with 182 additions and 18 deletions

View file

@ -32,9 +32,10 @@ type FileInfo struct {
Size int64 `json:"size"`
MimeType string `json:"mime_type"`
IsPinned bool `json:"is_pinned"`
IsTrashed bool `json:"is_trashed"`
ModTime time.Time `json:"mod_time"`
Checksum string `json:"checksum,omitempty"`
IsTrashed bool `json:"is_trashed"`
ModTime time.Time `json:"mod_time"`
Checksum string `json:"checksum,omitempty"`
Metadata map[string]interface{} `json:"metadata,omitempty"`
}
type FolderNode struct {
@ -280,10 +281,28 @@ func (h *FSHandler) GetExtendedFileInfo(c *fiber.Ctx) error {
return nil
})
fi.Size = totalSize
return c.JSON(fiber.Map{
"info": fi,
"file_count": fileCount,
"created_at": createdAt,
})
}
// For files, attempt to get media metadata
cat := categorizeFile(filepath.Ext(info.Name()))
if cat == "images" || cat == "videos" || cat == "audio" {
cmd := exec.Command("exiftool", "-json", resolvedPath)
output, err := cmd.Output()
if err == nil {
var meta []map[string]interface{}
if err := json.Unmarshal(output, &meta); err == nil && len(meta) > 0 {
fi.Metadata = meta[0]
}
}
}
return c.JSON(fiber.Map{
"info": fi,
"info": fi,
"file_count": fileCount,
"created_at": createdAt,
})
@ -679,6 +698,90 @@ func (h *FSHandler) DownloadFolder(c *fiber.Ctx) error {
return nil
}
// DownloadBulk creates and streams a ZIP archive of multiple selected items.
// POST /api/files/download-bulk
func (h *FSHandler) DownloadBulk(c *fiber.Ctx) error {
var body struct {
Paths []string `json:"paths" form:"paths"`
}
if err := c.BodyParser(&body); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid payload"})
}
if len(body.Paths) == 0 {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "no paths provided"})
}
c.Set("Content-Type", "application/zip")
c.Set("Content-Disposition", "attachment; filename=\"download.zip\"")
c.Context().SetBodyStreamWriter(func(w *bufio.Writer) {
zipWriter := zip.NewWriter(w)
defer zipWriter.Close()
for _, p := range body.Paths {
fullPath, err := h.resolveSafe(p)
if err != nil {
continue
}
info, err := os.Stat(fullPath)
if err != nil {
continue
}
if info.IsDir() {
filepath.Walk(fullPath, func(path string, walkInfo os.FileInfo, err error) error {
if err != nil {
return err
}
// Make it relative to the parent of the chosen directory
relPath, err := filepath.Rel(filepath.Dir(fullPath), path)
if err != nil {
return err
}
if walkInfo.IsDir() {
relPath += "/"
_, _ = zipWriter.Create(relPath)
return nil
}
zipFile, err := zipWriter.Create(relPath)
if err != nil {
return err
}
fsFile, err := os.Open(path)
if err != nil {
return err
}
defer fsFile.Close()
_, _ = io.Copy(zipFile, fsFile)
return nil
})
} else {
zipFile, err := zipWriter.Create(info.Name())
if err != nil {
continue
}
fsFile, err := os.Open(fullPath)
if err != nil {
continue
}
_, _ = io.Copy(zipFile, fsFile)
fsFile.Close()
}
}
})
return nil
}
// TogglePin pins or unpins a file/folder.
// POST /api/files/pin
func (h *FSHandler) TogglePin(c *fiber.Ctx) error {