package main import ( "encoding/json" "fmt" "io" "log" "os" "path/filepath" "strings" "sync" "time" ) const bingAPI = "https://www.bing.com/HPImageArchive.aspx?format=js&idx=0&n=8&mkt=zh-CN" type bingResponse struct { Images []struct { StartDate string `json:"startdate"` URL string `json:"url"` URLBase string `json:"urlbase"` Copyright string `json:"copyright"` } `json:"images"` } type BingRecord struct { Date string `json:"date"` URLBase string `json:"urlbase"` Copyright string `json:"copyright"` Filename string `json:"filename"` Favorited bool `json:"favorited"` } type BingHistory struct { Records []BingRecord `json:"records"` CurrentIdx int `json:"currentIdx"` } var bingMu sync.Mutex func bingDir() string { return filepath.Join(configDir(), "bing") } func bingHistoryPath() string { return filepath.Join(configDir(), "bing_history.json") } func loadBingHistory() *BingHistory { data, err := os.ReadFile(bingHistoryPath()) if err != nil { return &BingHistory{} } var h BingHistory if json.Unmarshal(data, &h) != nil { return &BingHistory{} } return &h } func saveBingHistory(h *BingHistory) error { data, _ := json.MarshalIndent(h, "", " ") return os.WriteFile(bingHistoryPath(), data, 0644) } func fetchBingHistory() { bingMu.Lock() defer bingMu.Unlock() resp, err := httpClient.Get(bingAPI) if err != nil { log.Println("Bing API 请求失败:", err) return } defer resp.Body.Close() data, err := io.ReadAll(resp.Body) if err != nil { return } var br bingResponse if json.Unmarshal(data, &br) != nil || len(br.Images) == 0 { log.Println("Bing API 解析失败") return } os.MkdirAll(bingDir(), 0755) existing := loadBingHistory() existingMap := make(map[string]BingRecord) for _, r := range existing.Records { existingMap[r.Date] = r } for _, img := range br.Images { date := img.StartDate if date == "" { continue } if _, exists := existingMap[date]; exists { continue } imgURL := img.URL if !strings.HasPrefix(imgURL, "http") { imgURL = "https://www.bing.com" + imgURL } imgResp, err := httpClient.Get(imgURL) if err != nil { log.Printf("Bing 图片下载失败 (%s): %v", date, err) continue } imgData, _ := io.ReadAll(imgResp.Body) imgResp.Body.Close() if len(imgData) == 0 { continue } filename := date + ".jpg" localPath := filepath.Join(bingDir(), filename) if err := os.WriteFile(localPath, imgData, 0644); err != nil { log.Printf("Bing 图片保存失败 (%s): %v", date, err) continue } log.Printf("Bing 壁纸已下载: %s (%d bytes)", filename, len(imgData)) existingMap[date] = BingRecord{ Date: date, URLBase: img.URLBase, Copyright: img.Copyright, Filename: filename, } } var records []BingRecord for _, img := range br.Images { if r, ok := existingMap[img.StartDate]; ok { records = append(records, r) } } if len(records) == 0 { return } history := &BingHistory{ Records: records, CurrentIdx: 0, } if existing.CurrentIdx < len(records) { history.CurrentIdx = existing.CurrentIdx } saveBingHistory(history) reloadWallpaper() } func getCurrentBingPath() string { h := loadBingHistory() if h.CurrentIdx < 0 || h.CurrentIdx >= len(h.Records) { return "" } return filepath.Join(bingDir(), h.Records[h.CurrentIdx].Filename) } func getCurrentBingRecord() *BingRecord { h := loadBingHistory() if h.CurrentIdx < 0 || h.CurrentIdx >= len(h.Records) { return nil } return &h.Records[h.CurrentIdx] } func bingPrev() { bingMu.Lock() defer bingMu.Unlock() h := loadBingHistory() if len(h.Records) == 0 { return } if h.CurrentIdx < len(h.Records)-1 { h.CurrentIdx++ saveBingHistory(h) log.Printf("Bing 壁纸: 上一个 (idx=%d, date=%s)", h.CurrentIdx, h.Records[h.CurrentIdx].Date) reloadWallpaper() } } func bingNext() { bingMu.Lock() defer bingMu.Unlock() h := loadBingHistory() if h.CurrentIdx > 0 { h.CurrentIdx-- saveBingHistory(h) log.Printf("Bing 壁纸: 下一个 (idx=%d, date=%s)", h.CurrentIdx, h.Records[h.CurrentIdx].Date) reloadWallpaper() } } func bingToggleFavorite() string { bingMu.Lock() defer bingMu.Unlock() h := loadBingHistory() if h.CurrentIdx < 0 || h.CurrentIdx >= len(h.Records) { return "" } r := &h.Records[h.CurrentIdx] r.Favorited = !r.Favorited saveBingHistory(h) state := "收藏" if r.Favorited { state = "取消收藏" } log.Printf("Bing 壁纸: %s (date=%s)", state, r.Date) if r.Favorited { return "☆ 取消收藏" } return "★ 收藏当前壁纸" } func bingCopyrightInfo() string { r := getCurrentBingRecord() if r != nil { return fmt.Sprintf("%s (%s)", r.Copyright, r.Date) } return "" } func bingWallpaperLoop() { cfg := loadConfig() if cfg.WallpaperType == WPBing { h := loadBingHistory() if len(h.Records) == 0 { fetchBingHistory() } else { reloadWallpaper() } } ticker := time.NewTicker(4 * time.Hour) for range ticker.C { cfg := loadConfig() if cfg.WallpaperType == WPBing { fetchBingHistory() } } }