888 lines
35 KiB
PHP
888 lines
35 KiB
PHP
<?php
|
|
date_default_timezone_set('Asia/Shanghai');
|
|
ob_start();
|
|
include './cfg.php';
|
|
?>
|
|
|
|
<!DOCTYPE html>
|
|
<html lang="zh-CN" data-bs-theme="<?php echo substr($neko_theme, 0, -4) ?>">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<link rel="icon" href="./assets/img/nekobox.png">
|
|
<link href="./assets/css/bootstrap.min.css" rel="stylesheet">
|
|
<link href="./assets/theme/<?php echo $neko_theme ?>" rel="stylesheet">
|
|
<link href="./assets/css/custom.css" rel="stylesheet">
|
|
<script type="text/javascript" src="./assets/js/bootstrap.min.js"></script>
|
|
<script type="text/javascript" src="./assets/js/feather.min.js"></script>
|
|
<script type="text/javascript" src="./assets/bootstrap/bootstrap.bundle.min.js"></script>
|
|
<script type="text/javascript" src="./assets/js/jquery-2.1.3.min.js"></script>
|
|
<script type="text/javascript" src="./assets/js/neko.js"></script>
|
|
</head>
|
|
<body>
|
|
<style>
|
|
.controls {
|
|
display: flex;
|
|
align-items: center;
|
|
margin-bottom: 10px;
|
|
}
|
|
.controls label {
|
|
margin-right: 10px;
|
|
font-weight: bold;
|
|
color: #FF5733;
|
|
}
|
|
.controls input {
|
|
margin-right: 20px;
|
|
}
|
|
.controls p {
|
|
margin: 0;
|
|
color: #00F;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<div class="container my-3 p-3 border border-3 rounded-4" style="background-color: transparent;">
|
|
<div class="controls">
|
|
<label for="main-toggle">系统开关</label>
|
|
<input type="checkbox" id="main-toggle">
|
|
|
|
<label for="weather-toggle">天气播报</label>
|
|
<input type="checkbox" id="weather-toggle">
|
|
|
|
<label for="website-toggle">网站检查</label>
|
|
<input type="checkbox" id="website-toggle">
|
|
<p>
|
|
当前城市:
|
|
<span id="current-city" style="font-weight: bold; color: #33FF57;">未设置</span>
|
|
</p>
|
|
</div>
|
|
|
|
<div class="controls mt-3">
|
|
<label>城市设置</label>
|
|
<input type="text" id="city-input" class="form-control" placeholder="如 Beijing" style="padding: 5px;">
|
|
<button onclick="saveCity()" class="btn btn-success mt-2" style="padding: 3px 10px;">保存城市</button>
|
|
</div>
|
|
|
|
<div id="player" onclick="toggleAnimation()" class="rounded-circle" style="background-color: transparent;">
|
|
<p id="hidePlayer">NeKoBox</p>
|
|
<p id="timeDisplay">00:00</p>
|
|
<audio id="audioPlayer" controls>
|
|
<source src="" type="audio/mpeg">
|
|
您的浏览器不支持音频播放。
|
|
</audio>
|
|
<div id="controls">
|
|
<button id="prev" class="rounded-button">⏮️</button>
|
|
<button id="orderLoop" class="rounded-button">🔁</button>
|
|
<button id="play" class="rounded-button">⏸️</button>
|
|
<button id="next" class="rounded-button">⏭️</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="mobile-controls" style="display: flex; justify-content: center; gap: 10px;">
|
|
<button id="togglePlay" class="rounded-button">播放/暂停</button>
|
|
<button id="prevMobile" class="rounded-button">上一首</button>
|
|
<button id="nextMobile" class="rounded-button">下一首</button>
|
|
<button id="toggleEnable" class="rounded-button">启用/禁用</button>
|
|
</div>
|
|
|
|
<div id="tooltip"></div>
|
|
</div>
|
|
<script>
|
|
let city = 'Beijing';
|
|
const apiKey = 'fc8bd2637768c286c6f1ed5f1915eb22';
|
|
let systemEnabled = true;
|
|
let weatherEnabled = true;
|
|
let websiteCheckEnabled = true;
|
|
let lastHour = -1;
|
|
|
|
function speakMessage(message) {
|
|
const utterance = new SpeechSynthesisUtterance(message);
|
|
utterance.lang = 'zh-CN';
|
|
speechSynthesis.speak(utterance);
|
|
}
|
|
|
|
function getGreeting() {
|
|
const hours = new Date().getHours();
|
|
if (hours >= 5 && hours < 12) return '早上好!';
|
|
if (hours >= 12 && hours < 18) return '下午好!';
|
|
if (hours >= 18 && hours < 22) return '晚上好!';
|
|
return '夜深了,注意休息!';
|
|
}
|
|
|
|
function speakCurrentTime() {
|
|
const now = new Date();
|
|
const hours = now.getHours();
|
|
const minutes = now.getMinutes().toString().padStart(2, '0');
|
|
const seconds = now.getSeconds().toString().padStart(2, '0');
|
|
const currentTime = `${hours}点${minutes}分${seconds}秒`;
|
|
|
|
const timeOfDay = (hours >= 5 && hours < 8) ? '清晨'
|
|
: (hours >= 8 && hours < 11) ? '早上'
|
|
: (hours >= 11 && hours < 13) ? '中午'
|
|
: (hours >= 13 && hours < 18) ? '下午'
|
|
: (hours >= 18 && hours < 20) ? '傍晚'
|
|
: (hours >= 20 && hours < 24) ? '晚上'
|
|
: '凌晨';
|
|
|
|
speakMessage(`${getGreeting()} 现在是北京时间: ${timeOfDay}${currentTime}`);
|
|
}
|
|
|
|
function updateHourlyTime() {
|
|
const now = new Date();
|
|
const hours = now.getHours();
|
|
const minutes = now.getMinutes();
|
|
const seconds = now.getSeconds();
|
|
|
|
if (minutes === 0 && seconds === 0 && hours !== lastHour) {
|
|
lastHour = hours;
|
|
const timeOfDay = (hours >= 5 && hours < 8) ? '清晨'
|
|
: (hours >= 8 && hours < 11) ? '早上'
|
|
: (hours >= 11 && hours < 13) ? '中午'
|
|
: (hours >= 13 && hours < 18) ? '下午'
|
|
: (hours >= 18 && hours < 20) ? '傍晚'
|
|
: (hours >= 20 && hours < 24) ? '晚上'
|
|
: '凌晨';
|
|
speakMessage(`整点播报,现在是北京时间 ${timeOfDay} ${hours}点`);
|
|
}
|
|
}
|
|
|
|
const websites = [
|
|
'https://www.youtube.com/',
|
|
'https://www.google.com/',
|
|
'https://www.facebook.com/',
|
|
'https://www.twitter.com/',
|
|
'https://www.github.com/'
|
|
];
|
|
|
|
function getWebsiteStatusMessage(url, status) {
|
|
const statusMessages = {
|
|
'https://www.youtube.com/': status ? 'YouTube 网站访问正常。' : '无法访问 YouTube 网站,请检查网络连接。',
|
|
'https://www.google.com/': status ? 'Google 网站访问正常。' : '无法访问 Google 网站,请检查网络连接。',
|
|
'https://www.facebook.com/': status ? 'Facebook 网站访问正常。' : '无法访问 Facebook 网站,请检查网络连接。',
|
|
'https://www.twitter.com/': status ? 'Twitter 网站访问正常。' : '无法访问 Twitter 网站,请检查网络连接。',
|
|
'https://www.github.com/': status ? 'GitHub 网站访问正常。' : '无法访问 GitHub 网站,请检查网络连接。',
|
|
};
|
|
|
|
return statusMessages[url] || (status ? `${url} 网站访问正常。` : `无法访问 ${url} 网站,请检查网络连接。`);
|
|
}
|
|
|
|
function checkWebsiteAccess(urls) {
|
|
const statusMessages = [];
|
|
let requestsCompleted = 0;
|
|
|
|
urls.forEach(url => {
|
|
fetch(url, { mode: 'no-cors' })
|
|
.then(response => {
|
|
const isAccessible = response.type === 'opaque';
|
|
statusMessages.push(getWebsiteStatusMessage(url, isAccessible));
|
|
|
|
if (!isAccessible && url === 'https://www.youtube.com/') {
|
|
speakMessage('无法访问 YouTube 网站,请检查网络连接。');
|
|
}
|
|
})
|
|
.catch(() => {
|
|
statusMessages.push(getWebsiteStatusMessage(url, false));
|
|
|
|
if (url === 'https://www.youtube.com/') {
|
|
speakMessage('无法访问 YouTube 网站,请检查网络连接。');
|
|
}
|
|
})
|
|
.finally(() => {
|
|
requestsCompleted++;
|
|
if (requestsCompleted === urls.length) {
|
|
speakMessage(statusMessages.join(' '));
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
function getRandomPoem() {
|
|
const poems = [
|
|
'红豆生南国,春来发几枝。', '独在异乡为异客,每逢佳节倍思亲。',
|
|
'海上生明月,天涯共此时。', '但愿人长久,千里共婵娟。',
|
|
'江南好,风景旧曾谙。', '君不见黄河之水天上来,奔流到海不复回。',
|
|
'露从今夜白,月是故乡明。', '自古逢秋悲寂寥,我言秋日胜春朝。',
|
|
'两岸猿声啼不住,轻舟已过万重山。', '一去二三里,烟村四五家。',
|
|
'问君何为别,心逐青云行。', '风急天高猿啸哀,渚清沙白鸟飞回。',
|
|
'锦城虽云乐,不如早还家。', '白下驿穷冬望,红楼隔雨弄晴寒。',
|
|
'夜泊牛渚怀古,牛渚西江夜。', '空山新雨后,天气晚来秋。',
|
|
'山中相送罢,日暮掩柴扉。', '寒蝉凄切,对长亭晚,骤雨初歇。',
|
|
'湖上初晴后雨,水面晕开清晖。', '孤舟蓑笠翁,独钓寒江雪。',
|
|
'黄河远上白云间,一片孤城万仞山。', '松下问童子,言师采药去。',
|
|
'白云深处有人家,黄鹤楼中吹玉笛。', '枯藤老树昏鸦,小桥流水人家。',
|
|
'寒山转苍翠,秋水共长天一色。', '年年岁岁花相似,岁岁年年人不同。',
|
|
'锦江春色来天地,玉垒浮云变古今。', '天街小雨润如酥,草色遥看近却无。',
|
|
'长江绕郭知鱼美,苏堤春晓胜地宜。'
|
|
];
|
|
return poems[Math.floor(Math.random() * poems.length)];
|
|
}
|
|
|
|
function speakRandomPoem() {
|
|
const poem = getRandomPoem();
|
|
speakMessage(`${poem}`);
|
|
}
|
|
|
|
function speakWeather(weather) {
|
|
if (!weatherEnabled) return;
|
|
|
|
const descriptions = {
|
|
"clear sky": "晴天", "few clouds": "少量云", "scattered clouds": "多云",
|
|
"broken clouds": "多云", "shower rain": "阵雨", "rain": "雨",
|
|
"light rain": "小雨", "moderate rain": "中雨", "heavy rain": "大雨",
|
|
"very heavy rain": "暴雨", "extreme rain": "极端降雨", "snow": "雪",
|
|
"light snow": "小雪", "moderate snow": "中雪", "heavy snow": "大雪",
|
|
"very heavy snow": "特大暴雪", "extreme snow": "极端降雪",
|
|
"sleet": "雨夹雪", "freezing rain": "冻雨", "mist": "薄雾",
|
|
"fog": "雾", "haze": "霾", "sand": "沙尘", "dust": "扬尘", "squall": "阵风",
|
|
"tornado": "龙卷风", "ash": "火山灰", "drizzle": "毛毛雨",
|
|
"overcast": "阴天", "partly cloudy": "局部多云", "cloudy": "多云",
|
|
"tropical storm": "热带风暴", "hurricane": "飓风", "cold": "寒冷",
|
|
"hot": "炎热", "windy": "大风", "breezy": "微风", "blizzard": "暴风雪"
|
|
};
|
|
|
|
const weatherDescription = descriptions[weather.weather[0].description.toLowerCase()] || weather.weather[0].description;
|
|
const temperature = weather.main.temp;
|
|
const tempMax = weather.main.temp_max;
|
|
const tempMin = weather.main.temp_min;
|
|
const humidity = weather.main.humidity;
|
|
const windSpeed = weather.wind.speed;
|
|
const visibility = weather.visibility / 1000;
|
|
|
|
let message = `以下是今天${city}的天气预报:当前气温为${temperature}摄氏度,${weatherDescription}。` +
|
|
`预计今天的最高气温为${tempMax}摄氏度,今晚的最低气温为${tempMin}摄氏度。`;
|
|
|
|
if (weather.rain && weather.rain['1h']) {
|
|
var rainProbability = weather.rain['1h'];
|
|
message += ` 接下来一小时有${rainProbability * 100}%的降雨概率。`;
|
|
} else if (weather.rain && weather.rain['3h']) {
|
|
var rainProbability = weather.rain['3h'];
|
|
message += ` 接下来三小时有${rainProbability * 100}%的降雨概率。`;
|
|
} else {
|
|
message += ' 今天降雨概率较低。';
|
|
}
|
|
|
|
message += ` 西南风速为每小时${windSpeed}米。` +
|
|
` 湿度为${humidity}%。`;
|
|
|
|
if (temperature >= 25) {
|
|
message += ` 紫外线指数较高,如果外出,请记得涂防晒霜。`;
|
|
} else if (temperature >= 16 && temperature < 25) {
|
|
message += ` 紫外线指数适中,如果外出,建议涂防晒霜。`;
|
|
} else if (temperature >= 5 && temperature < 16) {
|
|
message += ` 当前天气较冷,外出时请注意保暖。`;
|
|
} else {
|
|
message += ` 当前天气非常寒冷,外出时请注意防寒保暖。`;
|
|
}
|
|
|
|
if (weatherDescription.includes('雨') || weatherDescription.includes('阵雨') || weatherDescription.includes('雷暴')) {
|
|
message += ` 建议您外出时携带雨伞。`;
|
|
}
|
|
|
|
message += ` 能见度为${visibility}公里。` +
|
|
`请注意安全,保持好心情,祝您有美好的一天!`;
|
|
|
|
speakMessage(message);
|
|
}
|
|
|
|
function fetchWeather() {
|
|
if (!weatherEnabled || !systemEnabled) return;
|
|
|
|
const apiUrl = `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}&units=metric&lang=zh_cn`;
|
|
fetch(apiUrl)
|
|
.then(response => response.ok ? response.json() : Promise.reject('网络响应不正常'))
|
|
.then(data => {
|
|
if (data.weather && data.main) {
|
|
speakWeather(data);
|
|
} else {
|
|
console.error('无法获取天气数据');
|
|
}
|
|
})
|
|
.catch(error => console.error('获取天气数据时出错:', error));
|
|
}
|
|
|
|
function saveCity() {
|
|
const cityInput = document.getElementById('city-input').value.trim();
|
|
const chineseCharPattern = /[\u4e00-\u9fff]/;
|
|
const startsWithUppercasePattern = /^[A-Z]/;
|
|
if (chineseCharPattern.test(cityInput)) {
|
|
speakMessage('请输入非中文的城市名称。');
|
|
} else if (!startsWithUppercasePattern.test(cityInput)) {
|
|
speakMessage('城市名称必须以大写英文字母开头。');
|
|
} else if (cityInput) {
|
|
city = cityInput;
|
|
localStorage.setItem('city', city);
|
|
document.getElementById('current-city').textContent = city;
|
|
speakMessage(`城市已保存为${city},正在获取最新天气信息...`);
|
|
fetchWeather();
|
|
} else {
|
|
speakMessage('请输入有效的城市名称。');
|
|
}
|
|
}
|
|
|
|
document.getElementById('main-toggle').addEventListener('change', (event) => {
|
|
systemEnabled = event.target.checked;
|
|
localStorage.setItem('systemEnabled', systemEnabled);
|
|
if (systemEnabled) {
|
|
speakMessage('系统已启用。');
|
|
speakCurrentTime();
|
|
speakRandomPoem();
|
|
if (weatherEnabled) fetchWeather();
|
|
if (websiteCheckEnabled) checkWebsiteAccess(websites);
|
|
} else {
|
|
speakMessage('系统已关闭。');
|
|
}
|
|
});
|
|
|
|
document.getElementById('weather-toggle').addEventListener('change', (event) => {
|
|
weatherEnabled = event.target.checked;
|
|
localStorage.setItem('weatherEnabled', weatherEnabled);
|
|
if (systemEnabled && weatherEnabled) {
|
|
speakMessage('天气播报已启用。');
|
|
fetchWeather();
|
|
} else {
|
|
speakMessage('天气播报已关闭。');
|
|
}
|
|
});
|
|
|
|
document.getElementById('website-toggle').addEventListener('change', (event) => {
|
|
websiteCheckEnabled = event.target.checked;
|
|
localStorage.setItem('websiteCheckEnabled', websiteCheckEnabled);
|
|
if (systemEnabled && websiteCheckEnabled) {
|
|
speakMessage('网站检测已启用。');
|
|
checkWebsiteAccess(websites);
|
|
} else {
|
|
speakMessage('网站检测已关闭。');
|
|
}
|
|
});
|
|
|
|
window.onload = function() {
|
|
const savedCity = localStorage.getItem('city');
|
|
if (savedCity) {
|
|
city = savedCity;
|
|
document.getElementById('current-city').textContent = city;
|
|
}
|
|
|
|
const savedSystemEnabled = localStorage.getItem('systemEnabled');
|
|
if (savedSystemEnabled !== null) {
|
|
systemEnabled = savedSystemEnabled === 'true';
|
|
document.getElementById('main-toggle').checked = systemEnabled;
|
|
} else {
|
|
systemEnabled = true;
|
|
localStorage.setItem('systemEnabled', systemEnabled);
|
|
document.getElementById('main-toggle').checked = systemEnabled;
|
|
}
|
|
|
|
const savedWeatherEnabled = localStorage.getItem('weatherEnabled');
|
|
if (savedWeatherEnabled !== null) {
|
|
weatherEnabled = savedWeatherEnabled === 'true';
|
|
document.getElementById('weather-toggle').checked = weatherEnabled;
|
|
} else {
|
|
weatherEnabled = true;
|
|
localStorage.setItem('weatherEnabled', weatherEnabled);
|
|
document.getElementById('weather-toggle').checked = weatherEnabled;
|
|
}
|
|
|
|
const savedWebsiteCheckEnabled = localStorage.getItem('websiteCheckEnabled');
|
|
if (savedWebsiteCheckEnabled !== null) {
|
|
websiteCheckEnabled = savedWebsiteCheckEnabled === 'true';
|
|
document.getElementById('website-toggle').checked = websiteCheckEnabled;
|
|
} else {
|
|
websiteCheckEnabled = true;
|
|
localStorage.setItem('websiteCheckEnabled', websiteCheckEnabled);
|
|
document.getElementById('website-toggle').checked = websiteCheckEnabled;
|
|
}
|
|
|
|
if (systemEnabled) {
|
|
speakMessage('欢迎使用语音播报系统!');
|
|
}
|
|
if (systemEnabled && websiteCheckEnabled) {
|
|
checkWebsiteAccess(websites);
|
|
}
|
|
|
|
if (systemEnabled) {
|
|
speakCurrentTime();
|
|
if (weatherEnabled) fetchWeather();
|
|
speakRandomPoem();
|
|
}
|
|
|
|
setInterval(updateHourlyTime, 1000);
|
|
};
|
|
</script>
|
|
</body>
|
|
</html>
|
|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<style>
|
|
body {
|
|
font-family: Arial, sans-serif;
|
|
overflow: hidden;
|
|
transition: background-color 0.3s ease;
|
|
}
|
|
#container {
|
|
text-align: center;
|
|
margin-top: 50px;
|
|
}
|
|
#player {
|
|
width: 320px;
|
|
height: 320px;
|
|
margin: 50px auto;
|
|
padding: 20px;
|
|
background: url('/nekobox/assets/img/3.svg') no-repeat center center;
|
|
background-size: cover;
|
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
border-radius: 50%;
|
|
transform-style: preserve-3d;
|
|
transition: transform 0.5s;
|
|
position: relative;
|
|
animation: rainbow 5s infinite, rotatePlayer 10s linear infinite;
|
|
}
|
|
#player:hover {
|
|
transform: rotateY(360deg) rotateX(360deg);
|
|
}
|
|
#player h2 {
|
|
margin-top: 0;
|
|
}
|
|
#audio-container {
|
|
position: absolute;
|
|
top: 80%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
background-color: rgba(0, 0, 0, 1);
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
#audioPlayer {
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
}
|
|
|
|
#audioPlayer::-webkit-media-controls-panel {
|
|
background-color: black;
|
|
}
|
|
#audioPlayer::-webkit-media-controls-current-time-display,
|
|
#audioPlayer::-webkit-media-controls-time-remaining-display {
|
|
color: #fff;
|
|
}
|
|
#audioPlayer::-webkit-media-controls-play-button,
|
|
#audioPlayer::-webkit-media-controls-volume-slider-container,
|
|
#audioPlayer::-webkit-media-controls-mute-button,
|
|
#audioPlayer::-webkit-media-controls-timeline {
|
|
filter: invert(1);
|
|
}
|
|
#controls {
|
|
position: absolute;
|
|
bottom: 80px;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
display: flex;
|
|
justify-content: center;
|
|
gap: 10px;
|
|
}
|
|
button {
|
|
background-color: #4CAF50;
|
|
border: none;
|
|
color: white;
|
|
padding: 10px 20px;
|
|
text-align: center;
|
|
text-decoration: none;
|
|
display: inline-block;
|
|
font-size: 16px;
|
|
margin: 4px 2px;
|
|
cursor: pointer;
|
|
box-shadow: 0 4px #666;
|
|
transition: transform 0.2s, box-shadow 0.2s;
|
|
}
|
|
button:active {
|
|
transform: translateY(4px);
|
|
box-shadow: 0 2px #444;
|
|
}
|
|
@keyframes rotatePlayer {
|
|
0% { transform: rotate(0deg); }
|
|
100% { transform: rotate(360deg); }
|
|
}
|
|
#hidePlayer, #timeDisplay {
|
|
font-size: 24px;
|
|
font-weight: bold;
|
|
margin: 10px 0;
|
|
background: linear-gradient(90deg, #FF0000, #FF7F00, #FFFF00, #00FF00, #0000FF, #4B0082, #9400D3);
|
|
-webkit-background-clip: text;
|
|
color: transparent;
|
|
transition: background 1s ease;
|
|
}
|
|
.rounded-button {
|
|
border-radius: 30px 15px;
|
|
}
|
|
#tooltip {
|
|
position: absolute;
|
|
background-color: green;
|
|
color: #fff;
|
|
padding: 5px;
|
|
border-radius: 5px;
|
|
display: none;
|
|
}
|
|
#mobile-controls {
|
|
margin-top: 20px;
|
|
position: relative;
|
|
top: -35px;
|
|
transition: opacity 1s ease-in-out;
|
|
opacity: 1;
|
|
}
|
|
#mobile-controls.hidden {
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
}
|
|
body {
|
|
margin: 0;
|
|
padding: 0;
|
|
font-family: Arial, sans-serif;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
}
|
|
#top-center-container {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
position: absolute;
|
|
top: 10px;
|
|
width: 100%;
|
|
}
|
|
#weather-toggle {
|
|
margin-left: 10px;
|
|
}
|
|
@media (min-width: 768px) {
|
|
#mobile-controls {
|
|
display: none;
|
|
}
|
|
}
|
|
@media (max-width: 767px) {
|
|
#mobile-controls {
|
|
display: block;
|
|
}
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<script>
|
|
let colors = ['#FF0000', '#FF7F00', '#FFFF00', '#00FF00', '#0000FF', '#4B0082', '#9400D3'];
|
|
let isPlayingAllowed = JSON.parse(localStorage.getItem('isPlayingAllowed')) || false;
|
|
let isLooping = false;
|
|
let isOrdered = false;
|
|
let currentSongIndex = 0;
|
|
let songs = [];
|
|
const audioPlayer = document.getElementById('audioPlayer');
|
|
|
|
function speakMessage(message) {
|
|
const utterance = new SpeechSynthesisUtterance(message);
|
|
utterance.lang = 'zh-CN';
|
|
speechSynthesis.speak(utterance);
|
|
}
|
|
|
|
function toggleAnimation() {
|
|
const player = document.getElementById('player');
|
|
if (player.style.animationPlayState === 'paused') {
|
|
player.style.animationPlayState = 'running';
|
|
} else {
|
|
player.style.animationPlayState = 'paused';
|
|
}
|
|
}
|
|
|
|
var hidePlayerButton = document.getElementById('hidePlayer');
|
|
hidePlayerButton.addEventListener('click', function() {
|
|
var player = document.getElementById('player');
|
|
if (player.style.display === 'none') {
|
|
localStorage.setItem('playerVisible', 'true');
|
|
} else {
|
|
player.style.display = 'none';
|
|
localStorage.setItem('playerVisible', 'false');
|
|
}
|
|
});
|
|
|
|
function applyGradient(text, elementId) {
|
|
const element = document.getElementById(elementId);
|
|
element.innerHTML = '';
|
|
for (let i = 0; i < text.length; i++) {
|
|
const span = document.createElement('span');
|
|
span.textContent = text[i];
|
|
span.style.color = colors[i % colors.length];
|
|
element.appendChild(span);
|
|
}
|
|
const firstColor = colors.shift();
|
|
colors.push(firstColor);
|
|
}
|
|
|
|
function updateTime() {
|
|
const now = new Date();
|
|
const hours = now.getHours();
|
|
const timeString = now.toLocaleTimeString('zh-CN', { hour12: false });
|
|
let ancientTime;
|
|
|
|
if (hours >= 23 || hours < 1) {
|
|
ancientTime = '子時';
|
|
} else if (hours >= 1 && hours < 3) {
|
|
ancientTime = '丑時';
|
|
} else if (hours >= 3 && hours < 5) {
|
|
ancientTime = '寅時';
|
|
} else if (hours >= 5 && hours < 7) {
|
|
ancientTime = '卯時';
|
|
} else if (hours >= 7 && hours < 9) {
|
|
ancientTime = '辰時';
|
|
} else if (hours >= 9 && hours < 11) {
|
|
ancientTime = '巳時';
|
|
} else if (hours >= 11 && hours < 13) {
|
|
ancientTime = '午時';
|
|
} else if (hours >= 13 && hours < 15) {
|
|
ancientTime = '未時';
|
|
} else if (hours >= 15 && hours < 17) {
|
|
ancientTime = '申時';
|
|
} else if (hours >= 17 && hours < 19) {
|
|
ancientTime = '酉時';
|
|
} else if (hours >= 19 && hours < 21) {
|
|
ancientTime = '戌時';
|
|
} else {
|
|
ancientTime = '亥時';
|
|
}
|
|
|
|
const displayString = `${timeString} (${ancientTime})`;
|
|
applyGradient(displayString, 'timeDisplay');
|
|
}
|
|
|
|
applyGradient('NeKoBox', 'hidePlayer');
|
|
updateTime();
|
|
setInterval(updateTime, 1000);
|
|
|
|
function showTooltip(text) {
|
|
const tooltip = document.getElementById('tooltip');
|
|
tooltip.textContent = text;
|
|
tooltip.style.display = 'block';
|
|
tooltip.style.left = (window.innerWidth - tooltip.offsetWidth - 20) + 'px';
|
|
tooltip.style.top = '10px';
|
|
setTimeout(hideTooltip, 5000);
|
|
}
|
|
|
|
function hideTooltip() {
|
|
const tooltip = document.getElementById('tooltip');
|
|
tooltip.style.display = 'none';
|
|
}
|
|
|
|
function handlePlayPause() {
|
|
const playButton = document.getElementById('play');
|
|
if (isPlayingAllowed) {
|
|
if (audioPlayer.paused) {
|
|
showTooltip('播放');
|
|
audioPlayer.play();
|
|
playButton.textContent = '暂停';
|
|
speakMessage('播放');
|
|
} else {
|
|
showTooltip('暂停播放');
|
|
audioPlayer.pause();
|
|
playButton.textContent = '播放';
|
|
speakMessage('暂停播放');
|
|
}
|
|
} else {
|
|
showTooltip('播放被禁止');
|
|
audioPlayer.pause();
|
|
playButton.textContent = '播放';
|
|
speakMessage('播放被禁用。');
|
|
}
|
|
}
|
|
|
|
function handleOrderLoop() {
|
|
if (isPlayingAllowed) {
|
|
const orderLoopButton = document.getElementById('orderLoop');
|
|
if (isOrdered) {
|
|
isOrdered = false;
|
|
isLooping = !isLooping;
|
|
orderLoopButton.textContent = isLooping ? '循' : '';
|
|
showTooltip(isLooping ? '循环播放' : '暂停循环');
|
|
speakMessage(isLooping ? '循环播放' : '暂停循环');
|
|
} else {
|
|
isOrdered = true;
|
|
isLooping = false;
|
|
orderLoopButton.textContent = '顺';
|
|
showTooltip('顺序播放');
|
|
speakMessage('顺序播放');
|
|
}
|
|
} else {
|
|
speakMessage('播放被禁用。');
|
|
}
|
|
}
|
|
|
|
document.addEventListener('keydown', function(event) {
|
|
switch (event.key) {
|
|
case 'ArrowLeft':
|
|
if (isPlayingAllowed) {
|
|
document.getElementById('prev').click();
|
|
} else {
|
|
showTooltip('播放被禁止');
|
|
speakMessage('播放被禁用。');
|
|
}
|
|
break;
|
|
case 'ArrowRight':
|
|
if (isPlayingAllowed) {
|
|
document.getElementById('next').click();
|
|
} else {
|
|
showTooltip('播放被禁止');
|
|
speakMessage('播放被禁用。');
|
|
}
|
|
break;
|
|
case ' ':
|
|
handlePlayPause();
|
|
break;
|
|
case 'ArrowUp':
|
|
handleOrderLoop();
|
|
break;
|
|
case 'Escape':
|
|
isPlayingAllowed = !isPlayingAllowed;
|
|
localStorage.setItem('isPlayingAllowed', isPlayingAllowed);
|
|
if (!isPlayingAllowed) {
|
|
audioPlayer.pause();
|
|
audioPlayer.src = '';
|
|
showTooltip('播放已禁用');
|
|
speakMessage('播放已禁用。');
|
|
} else {
|
|
showTooltip('播放已启用');
|
|
speakMessage('播放已启用。');
|
|
if (songs.length > 0) {
|
|
loadSong(currentSongIndex);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
});
|
|
|
|
document.getElementById('play').addEventListener('click', handlePlayPause);
|
|
document.getElementById('next').addEventListener('click', function() {
|
|
if (isPlayingAllowed) {
|
|
currentSongIndex = (currentSongIndex + 1) % songs.length;
|
|
loadSong(currentSongIndex);
|
|
showTooltip('下一首');
|
|
speakMessage('下一首');
|
|
} else {
|
|
showTooltip('播放被禁止');
|
|
speakMessage('播放被禁用。');
|
|
}
|
|
});
|
|
document.getElementById('prev').addEventListener('click', function() {
|
|
if (isPlayingAllowed) {
|
|
currentSongIndex = (currentSongIndex - 1 + songs.length) % songs.length;
|
|
loadSong(currentSongIndex);
|
|
showTooltip('上一首');
|
|
speakMessage('上一首');
|
|
} else {
|
|
showTooltip('播放被禁止');
|
|
speakMessage('播放被禁用。');
|
|
}
|
|
});
|
|
document.getElementById('orderLoop').addEventListener('click', handleOrderLoop);
|
|
|
|
document.getElementById('togglePlay').addEventListener('click', handlePlayPause);
|
|
document.getElementById('prevMobile').addEventListener('click', function() {
|
|
if (isPlayingAllowed) {
|
|
currentSongIndex = (currentSongIndex - 1 + songs.length) % songs.length;
|
|
loadSong(currentSongIndex);
|
|
showTooltip('上一首');
|
|
speakMessage('上一首');
|
|
} else {
|
|
showTooltip('播放被禁止');
|
|
speakMessage('播放被禁用。');
|
|
}
|
|
});
|
|
document.getElementById('nextMobile').addEventListener('click', function() {
|
|
if (isPlayingAllowed) {
|
|
currentSongIndex = (currentSongIndex + 1) % songs.length;
|
|
loadSong(currentSongIndex);
|
|
showTooltip('下一首');
|
|
speakMessage('下一首');
|
|
} else {
|
|
showTooltip('播放被禁止');
|
|
speakMessage('播放被禁用。');
|
|
}
|
|
});
|
|
document.getElementById('toggleEnable').addEventListener('click', function() {
|
|
isPlayingAllowed = !isPlayingAllowed;
|
|
localStorage.setItem('isPlayingAllowed', isPlayingAllowed);
|
|
if (!isPlayingAllowed) {
|
|
audioPlayer.pause();
|
|
audioPlayer.src = '';
|
|
showTooltip('播放已禁用');
|
|
speakMessage('播放已禁用。');
|
|
} else {
|
|
showTooltip('播放已启用');
|
|
speakMessage('播放已启用。');
|
|
if (songs.length > 0) {
|
|
loadSong(currentSongIndex);
|
|
}
|
|
}
|
|
});
|
|
|
|
function loadSong(index) {
|
|
if (isPlayingAllowed && index >= 0 && index < songs.length) {
|
|
audioPlayer.src = songs[index];
|
|
audioPlayer.play();
|
|
} else {
|
|
audioPlayer.pause();
|
|
}
|
|
}
|
|
|
|
audioPlayer.addEventListener('ended', function() {
|
|
if (isPlayingAllowed) {
|
|
if (isLooping) {
|
|
audioPlayer.currentTime = 0;
|
|
audioPlayer.play();
|
|
} else {
|
|
currentSongIndex = (currentSongIndex + 1) % songs.length;
|
|
loadSong(currentSongIndex);
|
|
}
|
|
}
|
|
});
|
|
|
|
function initializePlayer() {
|
|
if (songs.length > 0) {
|
|
loadSong(currentSongIndex);
|
|
}
|
|
}
|
|
|
|
function loadDefaultPlaylist() {
|
|
fetch('https://raw.githubusercontent.com/Thaolga/Rules/main/Clash/songs.txt')
|
|
.then(response => {
|
|
if (!response.ok) {
|
|
throw new Error('默认歌单加载失败,网络响应不正常');
|
|
}
|
|
return response.text();
|
|
})
|
|
.then(data => {
|
|
songs = data.split('\n').filter(url => url.trim() !== '');
|
|
if (songs.length === 0) {
|
|
throw new Error('默认歌单中没有有效的歌曲');
|
|
}
|
|
initializePlayer();
|
|
console.log('默认歌单已加载:', songs);
|
|
})
|
|
.catch(error => {
|
|
console.error('加载默认歌单时出错:', error.message);
|
|
});
|
|
}
|
|
|
|
loadDefaultPlaylist();
|
|
document.addEventListener('dblclick', function() {
|
|
var player = document.getElementById('player');
|
|
if (player.style.display === 'none') {
|
|
player.style.display = 'flex';
|
|
} else {
|
|
player.style.display = 'none';
|
|
}
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|