动漫管理系统
动漫管理系统
动漫管理系统 API
下面是一个完整的 Node.js 后端程序,可以管理动漫文件夹结构、播放记录,并提供相关操作接口。
项目结构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
anime-manager/
├── data/
│ └── animeData.json (自动生成)
├── controllers/
│ ├── animeController.js
│ └── historyController.js
├── routes/
│ ├── animeRoutes.js
│ └── historyRoutes.js
├── utils/
│ ├── fileScanner.js
│ └── dataManager.js
├── app.js
└── package.json
安装依赖
1
npm install express fs-extra path body-parser cors
代码实现
1. app.js (主入口文件)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const animeRoutes = require('./routes/animeRoutes');
const historyRoutes = require('./routes/historyRoutes');
const fileScanner = require('./utils/fileScanner');
const dataManager = require('./utils/dataManager');
const app = express();
const ANIME_DIR = './anime'; // 动漫文件夹路径
const DATA_FILE = './data/animeData.json'; // 数据存储文件
// 中间件
app.use(cors());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
// 初始化扫描动漫文件夹
async function initialize() {
try {
const animeData = await fileScanner.scanAnimeFolder(ANIME_DIR);
await dataManager.saveData(DATA_FILE, animeData);
console.log('动漫数据初始化完成');
} catch (err) {
console.error('初始化失败:', err);
}
}
// 路由
app.use('/api/anime', animeRoutes);
app.use('/api/history', historyRoutes);
// 错误处理
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ error: '服务器错误' });
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, async () => {
await initialize();
console.log(`服务器运行在端口 ${PORT}`);
});
2. utils/fileScanner.js (文件扫描工具)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
const fs = require('fs-extra');
const path = require('path');
module.exports = {
// 扫描动漫文件夹
async scanAnimeFolder(baseDir) {
try {
const animeData = {};
const animeFolders = await fs.readdir(baseDir);
for (const animeName of animeFolders) {
const animePath = path.join(baseDir, animeName);
const stats = await fs.stat(animePath);
if (stats.isDirectory()) {
animeData[animeName] = {
episodes: []
};
const episodeFiles = await fs.readdir(animePath);
for (const episodeName of episodeFiles) {
const episodePath = path.join(animePath, episodeName);
const epStats = await fs.stat(episodePath);
if (!epStats.isDirectory()) {
animeData[animeName].episodes.push({
name: episodeName,
path: episodePath,
playTime: 0 // 默认播放时长为0
});
}
}
}
}
return animeData;
} catch (err) {
throw err;
}
}
};
3. utils/dataManager.js (数据管理工具)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const fs = require('fs-extra');
const path = require('path');
module.exports = {
// 保存数据到文件
async saveData(filePath, data) {
try {
await fs.ensureDir(path.dirname(filePath));
await fs.writeJson(filePath, data, { spaces: 2 });
} catch (err) {
throw err;
}
},
// 从文件读取数据
async loadData(filePath) {
try {
return await fs.readJson(filePath);
} catch (err) {
if (err.code === 'ENOENT') {
return {};
}
throw err;
}
}
};
4. controllers/animeController.js (动漫控制器)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
const fs = require('fs-extra');
const path = require('path');
const dataManager = require('../utils/dataManager');
const fileScanner = require('../utils/fileScanner');
const DATA_FILE = './data/animeData.json';
const ANIME_DIR = './anime';
module.exports = {
// 获取所有动漫
async getAllAnime(req, res) {
try {
const data = await dataManager.loadData(DATA_FILE);
const animeList = Object.keys(data).map(animeName => ({
name: animeName,
episodeCount: data[animeName].episodes.length
}));
res.json(animeList);
} catch (err) {
res.status(500).json({ error: err.message });
}
},
// 获取动漫详情
async getAnimeDetails(req, res) {
try {
const { animeName } = req.params;
const data = await dataManager.loadData(DATA_FILE);
if (!data[animeName]) {
return res.status(404).json({ error: '动漫未找到' });
}
res.json(data[animeName]);
} catch (err) {
res.status(500).json({ error: err.message });
}
},
// 重命名动漫
async renameAnime(req, res) {
try {
const { oldName, newName } = req.body;
const data = await dataManager.loadData(DATA_FILE);
if (!data[oldName]) {
return res.status(404).json({ error: '动漫未找到' });
}
// 重命名文件夹
await fs.rename(
path.join(ANIME_DIR, oldName),
path.join(ANIME_DIR, newName)
);
// 更新数据
data[newName] = data[oldName];
delete data[oldName];
await dataManager.saveData(DATA_FILE, data);
res.json({ success: true });
} catch (err) {
res.status(500).json({ error: err.message });
}
},
// 重命名剧集
async renameEpisode(req, res) {
try {
const { animeName, oldName, newName } = req.body;
const data = await dataManager.loadData(DATA_FILE);
if (!data[animeName]) {
return res.status(404).json({ error: '动漫未找到' });
}
const episodeIndex = data[animeName].episodes.findIndex(ep => ep.name === oldName);
if (episodeIndex === -1) {
return res.status(404).json({ error: '剧集未找到' });
}
// 重命名文件
await fs.rename(
path.join(ANIME_DIR, animeName, oldName),
path.join(ANIME_DIR, animeName, newName)
);
// 更新数据
data[animeName].episodes[episodeIndex].name = newName;
data[animeName].episodes[episodeIndex].path = path.join(ANIME_DIR, animeName, newName);
await dataManager.saveData(DATA_FILE, data);
res.json({ success: true });
} catch (err) {
res.status(500).json({ error: err.message });
}
}
};
5. controllers/historyController.js (播放记录控制器)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
const dataManager = require('../utils/dataManager');
const DATA_FILE = './data/animeData.json';
module.exports = {
// 更新播放记录
async updatePlayTime(req, res) {
try {
const { animeName, episodeName, playTime } = req.body;
const data = await dataManager.loadData(DATA_FILE);
if (!data[animeName]) {
return res.status(404).json({ error: '动漫未找到' });
}
const episode = data[animeName].episodes.find(ep => ep.name === episodeName);
if (!episode) {
return res.status(404).json({ error: '剧集未找到' });
}
episode.playTime = playTime;
await dataManager.saveData(DATA_FILE, data);
res.json({ success: true });
} catch (err) {
res.status(500).json({ error: err.message });
}
},
// 获取播放记录
async getPlayTime(req, res) {
try {
const { animeName, episodeName } = req.params;
const data = await dataManager.loadData(DATA_FILE);
if (!data[animeName]) {
return res.status(404).json({ error: '动漫未找到' });
}
const episode = data[animeName].episodes.find(ep => ep.name === episodeName);
if (!episode) {
return res.status(404).json({ error: '剧集未找到' });
}
res.json({ playTime: episode.playTime });
} catch (err) {
res.status(500).json({ error: err.message });
}
}
};
6. routes/animeRoutes.js (动漫路由)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const express = require('express');
const animeController = require('../controllers/animeController');
const router = express.Router();
// 获取所有动漫
router.post('/', animeController.getAllAnime);
// 获取动漫详情
router.post('/:animeName', animeController.getAnimeDetails);
// 重命名动漫
router.post('/rename', animeController.renameAnime);
// 重命名剧集
router.post('/episode/rename', animeController.renameEpisode);
module.exports = router;
7. routes/historyRoutes.js (播放记录路由)
1
2
3
4
5
6
7
8
9
10
11
12
const express = require('express');
const historyController = require('../controllers/historyController');
const router = express.Router();
// 更新播放记录
router.post('/update', historyController.updatePlayTime);
// 获取播放记录
router.get('/:animeName/:episodeName', historyController.getPlayTime);
module.exports = router;
API 文档
动漫相关接口
- 获取所有动漫
POST /api/anime
- 返回所有动漫列表及每部动漫的剧集数量
- 获取动漫详情
POST /api/anime/:animeName
- 返回指定动漫的所有剧集信息及播放记录
- 重命名动漫
POST /api/anime/rename
- 请求体:
{ oldName: "旧动漫名", newName: "新动漫名" }
- 重命名剧集
POST /api/anime/episode/rename
- 请求体:
{ animeName: "动漫名", oldName: "旧剧集名", newName: "新剧集名" }
播放记录相关接口
- 更新播放记录
POST /api/history/update
- 请求体:
{ animeName: "动漫名", episodeName: "剧集名", playTime: 123 }
(播放时间,单位秒)
- 获取播放记录
GET /api/history/:animeName/:episodeName
- 返回指定剧集的播放时间
使用说明
- 将动漫文件夹放在项目根目录下的
anime
文件夹中 - 启动服务器:
node app.js
- 服务器会自动扫描动漫文件夹并生成数据文件
- 通过 API 接口管理动漫和播放记录
这个系统会在启动时自动扫描动漫文件夹结构,并将信息保存在 data/animeData.json
文件中。所有播放记录也会保存在这个文件中。
本文由作者按照 CC BY 4.0 进行授权