mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-22 06:28:11 +02:00
Repris d'AJEAN : recherche d'images, aperçu du navigateur, vignettes, liens d'images
- web_images (amont v0.17.7) : recherche d'images DuckDuckGo, pendant de web_search ; les images du web s'affichent dans le fil avec la loupe, sans Referer, et disparaissent si le site les refuse. Schéma minimal pour tenir le budget du préambule. - Aperçu en direct du navigateur piloté (cu_autoshot.go) : une capture après chaque action browser_* qui change la page, gardée en RAM, jamais envoyée au modèle, retirée du journal en fin de tour ; une carte dans le fil, en fondu. - Vignettes calculées par le serveur (?thumb=, amont v0.17.9) : ~560 px au lieu de l'original (7 Ko au lieu de 550 Ko sur une capture), chargées deux par deux avec un nouvel essai ; la loupe ouvre l'original. - Un lien vers une image ([graphique](graph.png)) l'affiche au lieu d'un bouton de téléchargement. - Chrome du computer use tué avec Loki sous Linux (Pdeathsig), même en cas d'arrêt brutal : plus de navigateur orphelin dans le conteneur. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
502f23e833
commit
5016396998
20 files changed
+759
-30
No files matched your search
@@ -0,0 +1,96 @@
|
||||
package loki
|
||||
|
||||
// Vignettes des images du fil (voir handleChatImage, paramètre thumb ; repris d'AJEAN). Une
|
||||
// vignette est calculée une fois par fichier et par taille, puis gardée en
|
||||
// mémoire tant que le fichier ne change pas (date et taille).
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
"image/draw"
|
||||
"image/jpeg"
|
||||
"os"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
_ "image/gif" // décodeurs enregistrés pour image.Decode
|
||||
_ "image/png"
|
||||
)
|
||||
|
||||
const (
|
||||
thumbMaxSourceBytes = 40 << 20 // au-delà, pas de vignette : le client prendra l'original
|
||||
thumbCacheMax = 300
|
||||
)
|
||||
|
||||
type thumbEntry struct {
|
||||
mod time.Time
|
||||
size int64
|
||||
data []byte
|
||||
}
|
||||
|
||||
var (
|
||||
thumbMu sync.Mutex
|
||||
thumbCache = map[string]thumbEntry{}
|
||||
)
|
||||
|
||||
// chatThumb renvoie une vignette JPEG dont le grand côté vaut px (borné
|
||||
// 64..1600). Erreur si le fichier n'est pas une image décodable ici (webp…) :
|
||||
// le client se rabat alors sur l'original.
|
||||
func chatThumb(path string, st os.FileInfo, px int) ([]byte, string, error) {
|
||||
if px < 64 {
|
||||
px = 64
|
||||
}
|
||||
if px > 1600 {
|
||||
px = 1600
|
||||
}
|
||||
if st.Size() > thumbMaxSourceBytes {
|
||||
return nil, "", fmt.Errorf("image trop lourde pour une vignette")
|
||||
}
|
||||
key := path + "\x00" + strconv.Itoa(px)
|
||||
thumbMu.Lock()
|
||||
if e, ok := thumbCache[key]; ok && e.mod.Equal(st.ModTime()) && e.size == st.Size() {
|
||||
thumbMu.Unlock()
|
||||
return e.data, "image/jpeg", nil
|
||||
}
|
||||
thumbMu.Unlock()
|
||||
|
||||
raw, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
src, _, err := image.Decode(bytes.NewReader(raw))
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("format non pris en charge pour une vignette")
|
||||
}
|
||||
if o := exifOrientation(raw); o >= 2 && o <= 8 {
|
||||
src = applyOrientation(src, o)
|
||||
}
|
||||
if b := src.Bounds(); b.Dx() > px || b.Dy() > px {
|
||||
src = scaleDownToMax(src, px)
|
||||
}
|
||||
// Fond blanc sous la transparence : en JPEG elle deviendrait noire.
|
||||
flat := image.NewRGBA(src.Bounds())
|
||||
draw.Draw(flat, flat.Bounds(), &image.Uniform{C: color.White}, image.Point{}, draw.Src)
|
||||
draw.Draw(flat, flat.Bounds(), src, src.Bounds().Min, draw.Over)
|
||||
var buf bytes.Buffer
|
||||
if err := jpeg.Encode(&buf, flat, &jpeg.Options{Quality: 78}); err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
data := buf.Bytes()
|
||||
|
||||
thumbMu.Lock()
|
||||
if len(thumbCache) >= thumbCacheMax {
|
||||
for k := range thumbCache { // éviction grossière : la place compte plus que l'ordre
|
||||
delete(thumbCache, k)
|
||||
if len(thumbCache) < thumbCacheMax/2 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
thumbCache[key] = thumbEntry{mod: st.ModTime(), size: st.Size(), data: data}
|
||||
thumbMu.Unlock()
|
||||
return data, "image/jpeg", nil
|
||||
}
|
||||
Reference in new issue
Block a user