25時作業風景
25時作業風景(25ji Sagyo Fukei)是一个沉浸式学习工具,提供番茄钟计时、背景视频和 Project SEKAI 音乐播放。
项目信息
- 类型: 学习工具
- 技术栈: 原生 JavaScript, HLS.js, Web Audio API
- 生产环境: 25ji.nightcord.de5.net
- GitHub: 25-ji-code-de/25ji
- 代码量: ~15,000 行
功能特性
番茄钟计时
- 25 分钟工作 + 5 分钟休息 - 经典番茄钟
- 自定义时长 - 支持自定义工作/休息时间
- 自动循环 - 完成后自动开始下一轮
- 通知提醒 - 浏览器通知 + 音效
背景视频
- 时间同步 - 自动根据现实时间选择背景
- HLS 流式播放 - 高质量视频,低带宽消耗
- 循环播放 - 无缝循环
- 音量控制 - 独立音量调节
音乐播放
- Project SEKAI 音乐库 - 200+ 首歌曲
- 搜索和筛选 - 按标题、作曲家、角色搜索
- 播放列表 - 创建自定义播放列表
- 歌词显示 - 同步显示歌词(规划中)
在线状态
- 实时在线人数 - 显示当前在线用户
- 活动统计 - 今日学习时长、完成番茄钟数
- 排行榜 - 学习时长排行(规划中)
技术架构
前端架构
index.html
├── css/
│ ├── style.css
│ └── themes/ # 主题样式
├── js/
│ ├── main.js # 入口文件
│ ├── timer.js # 番茄钟逻辑
│ ├── video.js # 视频播放
│ ├── music.js # 音乐播放
│ ├── ui.js # UI 更新
│ └── storage.js # 本地存储
└── assets/
├── videos/ # 背景视频
├── music/ # 音乐文件
└── sounds/ # 音效视频播放
使用 HLS.js 播放 HLS 流:
javascript
import Hls from 'hls.js';
const video = document.getElementById('background-video');
const hls = new Hls();
hls.loadSource('https://assets.nightcord.de5.net/videos/nightcord.m3u8');
hls.attachMedia(video);
hls.on(Hls.Events.MANIFEST_PARSED, () => {
video.play();
});音乐播放
使用 Web Audio API:
javascript
const audioContext = new AudioContext();
const source = audioContext.createBufferSource();
const gainNode = audioContext.createGain();
source.connect(gainNode);
gainNode.connect(audioContext.destination);
// 加载音频
const response = await fetch('https://assets.nightcord.de5.net/music/song.mp3');
const arrayBuffer = await response.arrayBuffer();
const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
source.buffer = audioBuffer;
source.start();数据存储
使用 IndexedDB 存储音乐数据:
javascript
const db = await openDB('25ji', 1, {
upgrade(db) {
db.createObjectStore('music', { keyPath: 'id' });
db.createObjectStore('playlists', { keyPath: 'id' });
}
});
// 存储音乐
await db.put('music', {
id: 1,
title: 'Tell Your World',
artist: 'kz (livetune)',
duration: 126
});
// 查询音乐
const music = await db.getAll('music');本地开发
克隆项目
bash
git clone https://github.com/25-ji-code-de/25ji.git
cd 25ji运行
bash
# 使用 HTTP 服务器
python3 -m http.server 8000
# 访问 http://localhost:8000配置
编辑 js/config.js:
javascript
const CONFIG = {
API_URL: 'https://api.nightcord.de5.net',
ASSETS_URL: 'https://assets.nightcord.de5.net',
WS_URL: 'wss://25ji.nightcord.de5.net/ws'
};部署
Cloudflare Pages
bash
# 推送到 GitHub
git push origin main
# Cloudflare Pages 自动部署构建配置:
- 构建命令: 无(静态站点)
- 输出目录:
/ - 环境变量: 无
自定义域名
在 Cloudflare Pages 设置中添加自定义域名 25ji.nightcord.de5.net。
使用指南
开始学习
- 访问 25ji.nightcord.de5.net
- 点击"开始"按钮启动番茄钟
- 专注学习 25 分钟
- 休息 5 分钟
- 重复
自定义设置
- 点击设置图标
- 调整工作时长(默认 25 分钟)
- 调整休息时长(默认 5 分钟)
- 选择背景视频
- 选择音乐播放列表
播放音乐
- 点击音乐图标
- 搜索或浏览歌曲
- 点击播放
- 调整音量
API 集成
获取音乐数据
javascript
async function loadMusicData() {
const response = await fetch('https://api.nightcord.de5.net/sekai/music_data.json');
const data = await response.json();
// 存储到 IndexedDB
const db = await openDB('25ji', 1);
for (const song of data.songs) {
await db.put('music', song);
}
return data.songs;
}上报学习时长
javascript
async function reportStudyTime(duration) {
await fetch('https://25ji.nightcord.de5.net/api/stats', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
userId: currentUser.id,
duration,
timestamp: Date.now()
})
});
}性能优化
视频预加载
javascript
// 预加载下一个时段的视频
function preloadNextVideo() {
const nextHour = (currentHour + 1) % 24;
const nextVideo = getVideoForHour(nextHour);
const link = document.createElement('link');
link.rel = 'prefetch';
link.href = nextVideo.url;
document.head.appendChild(link);
}音乐缓存
javascript
// 使用 Service Worker 缓存音乐
self.addEventListener('fetch', (event) => {
if (event.request.url.includes('/music/')) {
event.respondWith(
caches.match(event.request).then((response) => {
return response || fetch(event.request).then((response) => {
return caches.open('music-cache').then((cache) => {
cache.put(event.request, response.clone());
return response;
});
});
})
);
}
});懒加载
javascript
// 懒加载音乐列表
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
loadMoreMusic();
}
});
});
observer.observe(document.getElementById('music-list-end'));时间同步
背景视频根据现实时间自动切换:
| 时间 | 视频 |
|---|---|
| 00:00 - 05:59 | 深夜 |
| 06:00 - 11:59 | 早晨 |
| 12:00 - 17:59 | 下午 |
| 18:00 - 23:59 | 傍晚 |
javascript
function getVideoForCurrentTime() {
const hour = new Date().getHours();
if (hour >= 0 && hour < 6) return 'night';
if (hour >= 6 && hour < 12) return 'morning';
if (hour >= 12 && hour < 18) return 'afternoon';
return 'evening';
}贡献
参考 CONTRIBUTING.md。
许可证
MIT License - 详见 LICENSE。