Files
u-desktop/bing.go
绝尘 9fd3acede3 新增: 星座运势+AI资讯+知识卡片+桌面设置窗口+秒显示开关
- 星座运势: 天聚数行API集成,5维进度条+幸运标签+今日概述
- AI资讯: 天聚数行API,图文布局5条展示,文件缓存2小时刷新
- 知识卡片: AI生成,关键字+提示词配置,30分钟刷新
- 桌面设置: 独立WebView2窗口,760x1350,含壁纸/布局/城市/颜色等配置
- 显示控制: 壁纸/时间/天气/星座/知识/AI资讯独立开关,秒显示开关
- 文件缓存: 星座运势+AI资讯缓存到本地,启动即显示上次数据
- initDone防抖: 防止设置窗口初始化触发卡片重载
2026-05-26 04:34:00 +08:00

335 lines
7.2 KiB
Go

package main
import (
"encoding/json"
"fmt"
"io"
"log"
"os"
"path/filepath"
"sort"
"strings"
"sync"
"time"
)
const bingAPIBase = "https://www.bing.com/HPImageArchive.aspx?format=js&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()
os.MkdirAll(bingDir(), 0755)
existing := loadBingHistory()
existingMap := make(map[string]BingRecord)
for _, r := range existing.Records {
existingMap[r.Date] = r
}
// 分页下载: idx=0,8,16... 直到命中已有记录或无新图片
for idx := 0; idx < 80; idx += 8 {
url := fmt.Sprintf("%s&idx=%d", bingAPIBase, idx)
resp, err := httpClient.Get(url)
if err != nil {
log.Printf("Bing API 请求失败 (idx=%d): %v", idx, err)
break
}
data, _ := io.ReadAll(resp.Body)
resp.Body.Close()
var br bingResponse
if json.Unmarshal(data, &br) != nil || len(br.Images) == 0 {
break
}
newCount := 0
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 {
continue
}
imgData, _ := io.ReadAll(imgResp.Body)
imgResp.Body.Close()
if len(imgData) == 0 {
continue
}
filename := date + ".jpg"
localPath := filepath.Join(bingDir(), filename)
os.WriteFile(localPath, imgData, 0644)
log.Printf("Bing 壁纸已下载: %s (%d bytes)", filename, len(imgData))
existingMap[date] = BingRecord{
Date: date,
URLBase: img.URLBase,
Copyright: img.Copyright,
Filename: filename,
}
newCount++
}
if newCount == 0 && idx > 0 {
break
}
time.Sleep(500 * time.Millisecond)
}
// 按 API 返回顺序重建 records (newest first)
// 用 existingMap 里所有记录按 date 降序排列
var records []BingRecord
for _, r := range existingMap {
records = append(records, r)
}
sort.Slice(records, func(i, j int) bool {
return records[i].Date > records[j].Date
})
history := &BingHistory{
Records: records,
CurrentIdx: 0,
}
if existing.CurrentIdx < len(records) {
history.CurrentIdx = existing.CurrentIdx
}
saveBingHistory(history)
log.Printf("Bing 壁纸: 共 %d 张", len(records))
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
}
h.CurrentIdx = (h.CurrentIdx + 1) % len(h.Records)
saveBingHistory(h)
log.Printf("Bing 壁纸: 上一个 (idx=%d, date=%s)", h.CurrentIdx, h.Records[h.CurrentIdx].Date)
bingReloadImage()
}
func bingNext() {
bingMu.Lock()
defer bingMu.Unlock()
h := loadBingHistory()
if len(h.Records) == 0 {
return
}
h.CurrentIdx = (h.CurrentIdx - 1 + len(h.Records)) % len(h.Records)
saveBingHistory(h)
log.Printf("Bing 壁纸: 下一个 (idx=%d, date=%s)", h.CurrentIdx, h.Records[h.CurrentIdx].Date)
bingReloadImage()
}
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 bingCurrentState() string {
h := loadBingHistory()
if h.CurrentIdx < 0 || h.CurrentIdx >= len(h.Records) {
return `{"fav":false,"label":"☆","copyright":"","idx":0,"total":0}`
}
r := h.Records[h.CurrentIdx]
label := "☆"
if r.Favorited {
label = "⭐"
}
data, _ := json.Marshal(map[string]interface{}{
"fav": r.Favorited,
"label": label,
"copyright": r.Copyright,
"date": r.Date,
"filename": r.Filename,
"idx": h.CurrentIdx,
"total": len(h.Records),
})
return string(data)
}
func bingFavoritesJSON() string {
h := loadBingHistory()
type favItem struct {
Date string `json:"date"`
Copyright string `json:"copyright"`
Filename string `json:"filename"`
Idx int `json:"idx"`
}
var favs []favItem
for i, r := range h.Records {
if r.Favorited {
favs = append(favs, favItem{r.Date, r.Copyright, r.Filename, i})
}
}
if favs == nil {
favs = []favItem{}
}
data, _ := json.Marshal(favs)
return string(data)
}
func bingSetByIdx(idx int) string {
bingMu.Lock()
defer bingMu.Unlock()
h := loadBingHistory()
if idx < 0 || idx >= len(h.Records) {
return bingCurrentState()
}
h.CurrentIdx = idx
saveBingHistory(h)
bingReloadImage()
return bingCurrentState()
}
func bingThumbDataURI(filename string) string {
p := filepath.Join(bingDir(), filename)
return imageToDataURI(p)
}
func bingWallpaperLoop() {
cfg := loadConfig()
if cfg.WallpaperType == WPBing {
h := loadBingHistory()
if len(h.Records) == 0 {
fetchBingHistory()
} else {
reloadWallpaper()
}
}
// 定时切换壁纸 (1 小时间隔)
ticker := time.NewTicker(1 * time.Hour)
for range ticker.C {
cfg := loadConfig()
if cfg.WallpaperType != WPBing || !cfg.BingAutoRefresh {
continue
}
h := loadBingHistory()
if len(h.Records) <= 1 {
continue
}
// 顺序切换到下一张
nextIdx := (h.CurrentIdx + 1) % len(h.Records)
h.CurrentIdx = nextIdx
saveBingHistory(h)
bingReloadImage()
log.Printf("Bing 自动切换: idx=%d/%d", nextIdx, len(h.Records))
}
// 定时拉取新壁纸 (4 小时间隔)
fetchTicker := time.NewTicker(4 * time.Hour)
for range fetchTicker.C {
cfg := loadConfig()
if cfg.WallpaperType == WPBing {
go fetchBingHistory()
}
}
}