Files

280 lines
8.5 KiB
JavaScript

const BackgroundManager = (function () {
const CACHE_KEY = 'background_cache';
const LAST_UPDATE_KEY = 'background_last_update';
const CACHE_EXPIRE_DAYS = 30;
const CONFIG_ENDPOINT = '/api/calendar/sys-config';
let configCache = null;
const UNSPLASH_ACCESS_KEY = 'osWW4Fx0IbpfJs2LbCl_Jpv5ej-6ZaphyIX9sgdPdxI';
const PICSUM_BASE_URL = 'https://picsum.photos';
const CHINESE_THEME_KEYWORDS = [
'chinese landscape', 'chinese painting', 'bamboo', 'lotus', 'plum blossom',
'orchid', 'chrysanthemum', 'mountain water', 'ink wash', 'traditional chinese',
'landscape painting', 'watercolor', 'minimalist', 'serene', 'nature'
];
async function loadConfig() {
if (configCache) return configCache;
try {
const res = await fetch(CONFIG_ENDPOINT, { cache: 'no-store' });
if (!res.ok) {
configCache = {};
return configCache;
}
const data = await res.json().catch(() => ({}));
const cfg = data && data.config && typeof data.config === 'object' ? data.config : {};
configCache = cfg;
return cfg;
} catch (_) {
configCache = {};
return configCache;
}
}
async function getLocalPoolBackground(cfg) {
try {
const theme = String((cfg && (cfg.background_theme || cfg.backgroundTheme)) || '默认背景');
const params = new URLSearchParams();
params.set('theme', theme);
params.set('prefer_local', '1');
const res = await fetch('/api/home/bg?' + params.toString(), { cache: 'no-store' });
if (!res.ok) return '';
const data = await res.json().catch(() => ({}));
const url = data && typeof data.url === 'string' ? data.url : '';
return url || '';
} catch (_) {
return '';
}
}
async function getAllPoolBackground() {
const themes = [
'夜空星轨',
'蓝天白云',
'治愈森林',
'运动风',
'科技霓虹',
'商务简约',
'暖色日落',
'冷色极夜'
];
const theme = themes[Math.floor(Math.random() * themes.length)];
try {
const params = new URLSearchParams();
params.set('theme', theme);
params.set('prefer_local', '1');
const res = await fetch('/api/home/bg?' + params.toString(), { cache: 'no-store' });
if (!res.ok) return '';
const data = await res.json().catch(() => ({}));
const url = data && typeof data.url === 'string' ? data.url : '';
return url || '';
} catch (_) {
return '';
}
}
async function getDailyBackground() {
const cfg = await loadConfig();
const today = new Date().toDateString();
const cached = getCachedBackground();
if (cached && cached.date === today && cached.url) {
return cached.url;
}
const enableDynamic = cfg && Object.prototype.hasOwnProperty.call(cfg, 'ENABLE_DYNAMIC_BACKGROUND')
? cfg.ENABLE_DYNAMIC_BACKGROUND !== false
: true;
const modeRaw = cfg && cfg.background_mode ? String(cfg.background_mode) : '';
const mode = modeRaw.toUpperCase();
const useAllPool = mode === 'ALL_POOL';
if (!enableDynamic) {
const url = getDefaultBackground();
cacheBackground(url, today);
return url;
}
if (useAllPool) {
const allUrl = await getAllPoolBackground();
if (allUrl) {
cacheBackground(allUrl, today);
return allUrl;
}
const fallback = getDefaultBackground();
cacheBackground(fallback, today);
return fallback;
}
if (cfg && (cfg.background_theme || cfg.backgroundTheme)) {
const localUrl = await getLocalPoolBackground(cfg);
if (localUrl) {
cacheBackground(localUrl, today);
return localUrl;
}
const fallback = getDefaultBackground();
cacheBackground(fallback, today);
return fallback;
}
try {
const unsplashUrl = await tryUnsplash();
if (unsplashUrl) {
cacheBackground(unsplashUrl, today);
return unsplashUrl;
}
} catch (error) {
console.warn('Unsplash API failed:', error);
}
if (cached && cached.url) return cached.url;
try {
const picsumUrl = await tryPicsum();
if (picsumUrl) {
cacheBackground(picsumUrl, today);
return picsumUrl;
}
} catch (error) {
console.warn('Picsum API failed:', error);
}
const fallback = getDefaultBackground();
cacheBackground(fallback, today);
return fallback;
}
async function tryUnsplash() {
if (!UNSPLASH_ACCESS_KEY || UNSPLASH_ACCESS_KEY === 'your_unsplash_access_key_here') {
return null;
}
const randomKeyword = CHINESE_THEME_KEYWORDS[Math.floor(Math.random() * CHINESE_THEME_KEYWORDS.length)];
const url = `https://api.unsplash.com/photos/random?query=${encodeURIComponent(randomKeyword)}&orientation=landscape&client_id=${UNSPLASH_ACCESS_KEY}`;
try {
const response = await fetch(url, {
method: 'GET',
headers: {
'Accept': 'application/json',
'Accept-Version': 'v1'
}
});
if (!response.ok) {
console.warn('Unsplash API error:', response.status, response.statusText);
return null;
}
const data = await response.json();
return data.urls.regular;
} catch (error) {
console.warn('Unsplash fetch failed:', error);
return null;
}
}
async function tryPicsum() {
const width = window.innerWidth;
const height = window.innerHeight;
const imageId = Math.floor(Math.random() * 1000);
return `${PICSUM_BASE_URL}/${width}/${height}?blur=2&image=${imageId}`;
}
function getDefaultBackground() {
return 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)';
}
function getCachedBackground() {
try {
const cached = localStorage.getItem(CACHE_KEY);
const lastUpdate = localStorage.getItem(LAST_UPDATE_KEY);
if (!cached || !lastUpdate) return null;
// 检查缓存是否过期(30天)
const updateDate = new Date(lastUpdate);
const expireDate = new Date(updateDate);
expireDate.setDate(expireDate.getDate() + CACHE_EXPIRE_DAYS);
if (new Date() > expireDate) {
localStorage.removeItem(CACHE_KEY);
localStorage.removeItem(LAST_UPDATE_KEY);
return null;
}
return JSON.parse(cached);
} catch (error) {
console.warn('Failed to read cache:', error);
return null;
}
}
function cacheBackground(url, date) {
try {
const cacheData = { url, date };
localStorage.setItem(CACHE_KEY, JSON.stringify(cacheData));
localStorage.setItem(LAST_UPDATE_KEY, new Date().toISOString());
} catch (error) {
console.warn('Failed to cache background:', error);
}
}
function applyBackground(url) {
const body = document.body;
let bgContainer = document.getElementById('background-container');
if (!bgContainer) {
bgContainer = document.createElement('div');
bgContainer.id = 'background-container';
const isImage = typeof url === 'string' && (url.startsWith('http://') || url.startsWith('https://') || url.startsWith('/'));
bgContainer.style.cssText = `
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
background: ${isImage ? `url(${url}) center/cover no-repeat` : url};
opacity: 0.15;
filter: blur(8px);
`;
document.body.appendChild(bgContainer);
} else {
const isImage = typeof url === 'string' && (url.startsWith('http://') || url.startsWith('https://') || url.startsWith('/'));
bgContainer.style.background = isImage ? `url(${url}) center/cover no-repeat` : url;
}
body.style.background = 'transparent';
const importantElements = document.querySelectorAll('.card, .calendar-grid, .navbar');
importantElements.forEach(el => {
el.style.backgroundColor = 'rgba(255, 255, 255, 0.95)';
el.style.backdropFilter = 'blur(10px)';
});
}
async function init() {
try {
const backgroundUrl = await getDailyBackground();
applyBackground(backgroundUrl);
} catch (error) {
console.error('Failed to set background:', error);
applyBackground(getDefaultBackground());
}
}
return {
init,
refresh: init
};
})();
// 页面加载时初始化
window.addEventListener('load', () => {
BackgroundManager.init();
});
// 导出供其他模块使用
if (typeof module !== 'undefined' && module.exports) {
module.exports = BackgroundManager;
}