package main import ( "encoding/base64" "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{} ) 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 photoToDataURI(dir, name string) string { path := filepath.Join(dir, name) data, err := os.ReadFile(path) if err != nil { return "" } ext := strings.ToLower(filepath.Ext(name)) mime := "image/jpeg" switch ext { case ".png": mime = "image/png" case ".gif": mime = "image/gif" case ".webp": mime = "image/webp" case ".bmp": mime = "image/bmp" } return fmt.Sprintf("data:%s;base64,%s", mime, base64.StdEncoding.EncodeToString(data)) } 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 := photoToDataURI(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 == "" { 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() log.Printf("相册: 共 %d 张, 间隔 %ds", len(files), interval) 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 photoMu.Unlock() if stop != nil { close(stop) } if done != nil { <-done } } func restartPhotoLoop() { stopPhotoLoop() evalJS(`if(window.updatePhotoFromGo) updatePhotoFromGo(null)`) startPhotoLoop() }