Files
u-desktop/photo.go

189 lines
3.5 KiB
Go

package main
import (
"encoding/json"
"fmt"
"log"
"os"
"path/filepath"
"sort"
"strings"
"sync"
"time"
)
var (
photoMu sync.Mutex
photoFiles []string
photoIdx int
photoDir string
photoStop chan struct{}
photoDone chan struct{}
photoCacheMu sync.Mutex
photoCacheMap map[string]string
photoCacheDir string
)
func scanPhotoDir(dir string) []string {
entries, err := os.ReadDir(dir)
if err != nil {
return nil
}
var files []string
for _, e := range entries {
if e.IsDir() {
continue
}
ext := strings.ToLower(filepath.Ext(e.Name()))
switch ext {
case ".jpg", ".jpeg", ".png", ".bmp", ".webp", ".gif":
files = append(files, e.Name())
}
}
sort.Strings(files)
return files
}
func preCachePhotos(dir string, files []string) {
cache := make(map[string]string, len(files))
for _, name := range files {
uri := imageToDataURI(filepath.Join(dir, name))
if uri != "" {
cache[name] = uri
}
}
photoCacheMu.Lock()
photoCacheMap = cache
photoCacheDir = dir
photoCacheMu.Unlock()
log.Printf("相册: 预缓存 %d/%d 张", len(cache), len(files))
}
func getCachedPhotoURI(dir, name string) string {
photoCacheMu.Lock()
if photoCacheMap != nil && photoCacheDir == dir {
if uri, ok := photoCacheMap[name]; ok {
photoCacheMu.Unlock()
return uri
}
}
photoCacheMu.Unlock()
return imageToDataURI(filepath.Join(dir, name))
}
func pushCurrentPhoto(interval int) {
photoMu.Lock()
files := photoFiles
idx := photoIdx
dir := photoDir
photoMu.Unlock()
if len(files) == 0 || dir == "" {
evalJS(`if(window.updatePhotoFromGo) updatePhotoFromGo(null)`)
return
}
if idx >= len(files) {
idx = 0
}
src := getCachedPhotoURI(dir, files[idx])
if src == "" {
return
}
data, _ := json.Marshal(map[string]interface{}{
"src": src,
"counter": fmt.Sprintf("%d / %d", idx+1, len(files)),
"interval": interval,
})
evalJS(fmt.Sprintf(`if(window.updatePhotoFromGo) updatePhotoFromGo(%s)`, string(data)))
}
func startPhotoLoop() {
cfg := loadConfig()
if cfg.PhotoDir == "" || cfg.HidePhoto {
return
}
interval := cfg.PhotoInterval
if interval <= 0 {
interval = 15
}
files := scanPhotoDir(cfg.PhotoDir)
if len(files) == 0 {
log.Println("相册: 目录为空或无图片")
return
}
photoMu.Lock()
photoDir = cfg.PhotoDir
photoFiles = files
photoIdx = 0
stop := make(chan struct{})
done := make(chan struct{})
photoStop = stop
photoDone = done
photoMu.Unlock()
photoCacheMu.Lock()
photoCacheMap = nil
photoCacheDir = cfg.PhotoDir
photoCacheMu.Unlock()
log.Printf("相册: 共 %d 张, 间隔 %ds", len(files), interval)
go preCachePhotos(cfg.PhotoDir, files)
pushCurrentPhoto(interval)
go func() {
ticker := time.NewTicker(time.Duration(interval) * time.Second)
defer func() {
ticker.Stop()
close(done)
}()
for {
select {
case <-ticker.C:
photoMu.Lock()
if len(photoFiles) > 0 {
photoIdx = (photoIdx + 1) % len(photoFiles)
}
photoMu.Unlock()
pushCurrentPhoto(interval)
case <-stop:
return
}
}
}()
}
func stopPhotoLoop() {
photoMu.Lock()
stop := photoStop
done := photoDone
photoStop = nil
photoDone = nil
photoFiles = nil
photoIdx = 0
photoDir = ""
photoMu.Unlock()
photoCacheMu.Lock()
photoCacheMap = nil
photoCacheDir = ""
photoCacheMu.Unlock()
if stop != nil {
close(stop)
}
if done != nil {
<-done
}
}
func restartPhotoLoop() {
stopPhotoLoop()
evalJS(`if(window.updatePhotoFromGo) updatePhotoFromGo(null)`)
startPhotoLoop()
}