321683f863
CI / docker (push) Successful in 3m38s
Add model input defaults (including prompt), workflow model editing, ingress API docs, and fix img2img image upload filenames for signed URLs. Co-authored-by: Cursor <cursoragent@cursor.com>
134 lines
2.9 KiB
Go
134 lines
2.9 KiB
Go
package prediction
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"net/url"
|
|
"path"
|
|
"strings"
|
|
)
|
|
|
|
func imageFilenameFromURL(raw string, data []byte) string {
|
|
name := ""
|
|
if u, err := url.Parse(raw); err == nil {
|
|
if base := path.Base(u.Path); base != "" && base != "." && base != "/" {
|
|
name = base
|
|
}
|
|
}
|
|
if name == "" {
|
|
name = "input"
|
|
}
|
|
name = sanitizeUploadFilename(name)
|
|
if path.Ext(name) == "" {
|
|
name += sniffImageExt(data)
|
|
}
|
|
return name
|
|
}
|
|
|
|
func imageFilenameFromStored(filename, mime string, data []byte) string {
|
|
name := filename
|
|
if name == "" {
|
|
name = "input"
|
|
}
|
|
name = sanitizeUploadFilename(name)
|
|
if path.Ext(name) == "" {
|
|
if ext := extFromMime(mime); ext != "" {
|
|
name += ext
|
|
} else {
|
|
name += sniffImageExt(data)
|
|
}
|
|
}
|
|
return name
|
|
}
|
|
|
|
func sanitizeUploadFilename(name string) string {
|
|
name = path.Base(name)
|
|
replacer := strings.NewReplacer(
|
|
"?", "_",
|
|
"&", "_",
|
|
"=", "_",
|
|
"#", "_",
|
|
"%", "_",
|
|
"\\", "_",
|
|
":", "_",
|
|
"*", "_",
|
|
"\"", "_",
|
|
"<", "_",
|
|
">", "_",
|
|
"|", "_",
|
|
)
|
|
name = replacer.Replace(name)
|
|
name = strings.TrimSpace(name)
|
|
if name == "" || name == "." {
|
|
return "input"
|
|
}
|
|
return name
|
|
}
|
|
|
|
func sniffImageExt(data []byte) string {
|
|
switch {
|
|
case len(data) >= 3 && data[0] == 0xFF && data[1] == 0xD8 && data[2] == 0xFF:
|
|
return ".jpg"
|
|
case len(data) >= 8 && bytes.Equal(data[:8], []byte{0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A}):
|
|
return ".png"
|
|
case len(data) >= 12 && bytes.Equal(data[:4], []byte("RIFF")) && bytes.Equal(data[8:12], []byte("WEBP")):
|
|
return ".webp"
|
|
case len(data) >= 6 && (bytes.Equal(data[:6], []byte("GIF87a")) || bytes.Equal(data[:6], []byte("GIF89a"))):
|
|
return ".gif"
|
|
default:
|
|
return ".png"
|
|
}
|
|
}
|
|
|
|
func extFromMime(mime string) string {
|
|
switch strings.ToLower(strings.TrimSpace(mime)) {
|
|
case "image/jpeg", "image/jpg":
|
|
return ".jpg"
|
|
case "image/png":
|
|
return ".png"
|
|
case "image/webp":
|
|
return ".webp"
|
|
case "image/gif":
|
|
return ".gif"
|
|
default:
|
|
return ""
|
|
}
|
|
}
|
|
|
|
func parseGatewayFileID(raw string) (string, bool) {
|
|
u, err := url.Parse(raw)
|
|
if err != nil {
|
|
return "", false
|
|
}
|
|
segments := strings.Split(strings.Trim(u.Path, "/"), "/")
|
|
if len(segments) == 2 && segments[0] == "files" && segments[1] != "" {
|
|
return segments[1], true
|
|
}
|
|
return "", false
|
|
}
|
|
|
|
func validateImageBytes(data []byte) error {
|
|
if len(data) == 0 {
|
|
return fmt.Errorf("empty image payload")
|
|
}
|
|
if !looksLikeImage(data) {
|
|
return fmt.Errorf("unrecognized image format")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func looksLikeImage(data []byte) bool {
|
|
switch {
|
|
case len(data) >= 3 && data[0] == 0xFF && data[1] == 0xD8 && data[2] == 0xFF:
|
|
return true
|
|
case len(data) >= 8 && bytes.Equal(data[:8], []byte{0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A}):
|
|
return true
|
|
case len(data) >= 12 && bytes.Equal(data[:4], []byte("RIFF")) && bytes.Equal(data[8:12], []byte("WEBP")):
|
|
return true
|
|
case len(data) >= 6 && (bytes.Equal(data[:6], []byte("GIF87a")) || bytes.Equal(data[:6], []byte("GIF89a"))):
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|