新的域名

最近发现了MOE后缀域名,这是一个很可爱的顶级域名(TLD),按耐不住内心的欢喜,年付 14 刀将其拿下。

于是乎,这个博客的域名由sakuramiko.xyz更改为blog.ayumi.moe

至于主域名ayumi,出自于色のん老师的恋爱漫画《一途ビッチちゃん》中女主角(あゆみ)

歩酱真是太可爱啦~

一个小小的模仿

其实不只是这个域名,我还购入了另一个域masuzu.moe

其灵感来源于satania.moe这个粉丝向网站,而且这是一个开源项目。

所以我所作的工作是属于对大佬的二次创作的二次创作,有点套娃,不过我的确是只改了一些东西。

初试牛刀

使用这个项目,需要用到npm,但实际上我在部署的时候却构建出错,可能是我太菜了,但对于 npm 接触过少,也没有好的解决办法,所以我对项目进行的一定程度的魔改。

我发现 scss 文件无法被引用,而 css 文件却可以,所以我干脆用转换工具把 scss 都转为了 css,之后就可以正常构建了。

经过修改后,目录结构如下

很经典的 html,js,css 三要素。

为了方便运行在服务器上,可以编写一个 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
44
45
46
47
48
const http = require("http");
const fs = require("fs");
const path = require("path");

const server = http.createServer((req, res) => {
// 根据请求的 URL,确定要返回的文件路径
let filePath = "." + req.url;

// 如果 URL 是根路径,则返回 index.html
if (filePath === "./") {
filePath = "./index.html";
}

// 获取文件的扩展名
const extname = path.extname(filePath);

// 确定响应的 Content-Type
let contentType = "text/html";
if (extname === ".js") {
contentType = "text/javascript";
} else if (extname === ".css") {
contentType = "text/css";
}

// 读取文件
fs.readFile(filePath, (err, content) => {
if (err) {
if (err.code === "ENOENT") {
// 文件不存在,返回404错误
res.writeHead(404);
res.end("404 Not Found");
} else {
// 其他错误,返回500错误
res.writeHead(500);
res.end("500 Internal Server Error: " + err.code);
}
} else {
// 成功读取文件,返回文件内容
res.writeHead(200, { "Content-Type": contentType });
res.end(content, "utf-8");
}
});
});

const PORT = process.env.PORT || 3000;
server.listen(PORT, () => {
console.log(`Server running at http://localhost:${PORT}/`);
});

运行 web 网页

原项目是托管在 GitHub Page 上的,不需要服务器,但目前还不清楚如何用 action 部署,就先跑在服务器上了。

在服务器上的项目文件夹内输入指令Node app.js

服务即可运行在 3000 端口上,访问 localhost:3000 即可看到页面:

添加网页视频

不过单是只有一个网页,我觉得比较单一了,于是加入了一个视频播放的动画 OP,使用的 plyr 这个 js 库,很漂亮的多功能视频播放器,而且在右上角添加了一个隐藏视频的按钮,方便查看正经的背景的图片。

先引入 plyr 的库:

1
2
3
<script src="https://cdn.plyr.io/3.7.8/plyr.js"></script>

<link rel="stylesheet" href="https://cdn.plyr.io/3.7.8/plyr.css" />

初始化播放器,需要在 js 中引入一行
const player = new Plyr("#player");
视频缩放和隐藏元素的按钮

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
const player = new Plyr("#player");

player.on("fullscreenchange", (event) => {
if (player.fullscreen.active) {
// 进入全屏模式时,取消对视频大小的限制
document.querySelector(".plyr__video-wrapper video").style.maxWidth =
"none";
document.querySelector(".plyr__video-wrapper video").style.width = "100%";
document.querySelector(".plyr__video-wrapper video").style.height = "100%";
} else {
// 退出全屏模式时,恢复原来的视频大小限制
document.querySelector(".plyr__video-wrapper video").style.maxWidth =
"1080px";
document.querySelector(".plyr__video-wrapper video").style.width = "100%";
document.querySelector(".plyr__video-wrapper video").style.height = "auto";
}
});

// Get the button, widget container, and footer1 elements
const hideVideoBtn = document.getElementById("hide-video-btn");
const widgetContainer = document.getElementById("widget-container");
const playerContainer = document.querySelector(".player-container");
const footer1 = document.querySelector(".footer1");

// Add button click event
hideVideoBtn.addEventListener("click", function () {
if (playerContainer.style.display !== "none") {
// Hide the video
playerContainer.style.display = "none";
// Hide footer1
footer1.style.display = "none";
// Show the widget
widgetContainer.style.display = "block";
} else {
// Show the video
playerContainer.style.display = "block";
// Show footer1
footer1.style.display = "block";
// Hide the widget
widgetContainer.style.display = "none";
}
});

在 index.html 中的尾页插入视频。

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
<div i18n-group="end" class="end">
<div class="main">
<div class="footer1">
<a href="https://github.com/Pizzacus/satania.moe">
<picture>
<source srcset="webp/github_link.webp" type="image/webp" />
<img src="img/github_link.png" alt="" />
</picture>
</a>
</div>
<div class="player-container">
<h2>Oreshura Animation OP</h2>
<video id="player" controls poster="/webp/p12.webp">
<source
src="https://localhost:3000/stream/oreshura/oped.mp4"
type="video/mp4"
/>
Your browser does not support the video tag.
<track
kind="subtitles"
src="./video/p1.vtt"
srclang="ja"
label="Japanese"
default
/>
</video>
</div>
<!-- 添加一个按钮来隐藏视频和显示小组件 -->
<button id="hide-video-btn">Hide Video</button>
<!-- 小组件容器 -->
<div id="widget-container"></div>
</div>
</div>

css 的样式设置:

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
.player-container {
position: relative;
text-align: center;
color: rgb(255, 255, 255);
}

.player-container h2 {
text-shadow: 2px 2px 5px blue;
position: absolute;
top: -60px;
left: 50%;
transform: translateX(-50%);
white-space: nowrap;
overflow-wrap: break-word;
display: inline-block;
max-width: 100%;
}

.player-container video {
max-width: 1080px;
width: 100%;
height: auto;
}

/* 自定义 Plyr 播放器样式 */
.plyr {
border-radius: 10px;
/* 设置圆角大小,这里设置为 10px */
}

.plyr__video-wrapper video {
object-fit: cover;
width: 100%;
height: 100%;
}

/* 按钮样式 */
#hide-video-btn {
position: absolute;
top: 10px;
right: 10px;
/* 添加其他样式以修改按钮的外观 */
background-color: #40ccd8;
/* 背景颜色 */
color: white;
/* 文字颜色 */
padding: 5px 10px;
/* 内边距 */
border: none;
/* 去除边框 */
border-radius: 5px;
/* 圆角 */
cursor: pointer;
/* 鼠标样式为手型 */
}

/* 按钮悬停样式 */
#hide-video-btn:hover {
background-color: #fd8080;
/* 悬停时的背景颜色 */
}

.footer1 img {
filter: invert(80%);
max-height: 50px;
/* 限制图片的最大高度,根据需要调整 */
}

.end {
background-image: url("img/s2.png");
background-repeat: no-repeat;
background-size: cover;
}

关于视频,我是使用 Express 框架实现了一个简单的视频流服务。
这是一个简单的stream.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
const express = require("express");
const app = express();
const fs = require("fs");
const path = require("path");

const streamFolderPath = "/root/docker/data/masuzu.moe/src/stream"; // 设置视频文件夹的绝对路径

app.use("/stream", express.static(streamFolderPath));

app.get("/stream/:folder/:video", function (req, res) {
const { folder, video } = req.params;
const videoPath = path.join(streamFolderPath, folder, video);

fs.access(videoPath, fs.constants.F_OK, (err) => {
if (err) {
res.status(404).send("Video not found");
return;
}

const videoSize = fs.statSync(videoPath).size;
const range = req.headers.range;
if (!range) {
res.status(400).send("Requires Range header");
return;
}

const CHUNK_SIZE = 10 ** 6;
const start = Number(range.replace(/\D/g, ""));
const end = Math.min(start + CHUNK_SIZE, videoSize - 1);

const contentLength = end - start + 1;
const headers = {
"Content-Range": `bytes ${start}-${end}/${videoSize}`,
"Accept-Ranges": "bytes",
"Content-Length": contentLength,
"Content-Type": "video/mp4", // 设置正确的 Content-Type
};

// HTTP Status 206 for Partial Content
res.writeHead(206, headers);

// 创建视频读取流,从指定位置开始读取
const videoStream = fs.createReadStream(videoPath, { start, end });

// 将视频流导向响应流
videoStream.pipe(res);
});
});

const PORT = process.env.PORT || 8000;
app.listen(PORT, function () {
console.log(`Server is running on port ${PORT}`);
});

确保视频文件路径存在,运行 node stream.js
服务运行在 8000 端口上,访问 localhost:8000 即可查看视频。
可以将其解析到域名上,比如:https://stream.masuzu.moe/stream/oreshura/oped.mp4
在 plyr 上还可以引入字幕,只需要准备 vtt 格式的字幕文件即可。

1
2
3
4
5
6
7
8
<div class="player-container">
<h2>Oreshura Animation OP</h2>
<video id="player" controls poster="/webp/p12.webp">
<source src="https://stream.masuzu.moe/stream/oreshura/oped.mp4" type="video/mp4">
Your browser does not support the video tag.
<track kind="subtitles" src="./video/p1.vtt" srclang="ja" label="Japanese" default>
</video>
</div>

小结

做网站只是一个兴趣,重点不是会有多少人看到,而是自己看着满意,就足够了。

搭建一个网站并不是很困难的事情,这只是一次尝试,打算以后再做一些更有趣的东西。

最后我想说,歩ちゃん真可爱~