Add video title and author to watch page
This commit is contained in:
parent
52e07b54dc
commit
8852a6f0c3
58
index.js
58
index.js
@ -14,15 +14,19 @@ let sessions = [];
|
||||
const db = new sqlite3.Database("database.sqlite3");
|
||||
|
||||
function dbGet(query, ...parameters) {
|
||||
return new Promise((resolve, reject) => db.get(query, parameters, (err, data) => err ? reject(err) : resolve(data)));
|
||||
return new Promise((resolve, reject) => db.get(query, parameters, (err, data) => err ? reject(err) : resolve(data)));
|
||||
}
|
||||
|
||||
function dbAll(query, ...parameters) {
|
||||
return new Promise((resolve, reject) => db.all(query, parameters, (err, data) => err ? reject(err) : resolve(data)));
|
||||
return new Promise((resolve, reject) => db.all(query, parameters, (err, data) => err ? reject(err) : resolve(data)));
|
||||
}
|
||||
|
||||
function dbRun(query, ...parameters) {
|
||||
return new Promise((resolve, reject) => db.run(query, parameters, (err, data) => err ? reject(err) : resolve(data)));
|
||||
return new Promise((resolve, reject) => db.run(query, parameters, (err, data) => err ? reject(err) : resolve(data)));
|
||||
}
|
||||
|
||||
function videoPath(id) {
|
||||
return `videos/${id}.webm`;
|
||||
}
|
||||
|
||||
function randomString(length) {
|
||||
@ -54,7 +58,7 @@ app.post("/api/register", async (req, res) => {
|
||||
return res.status(400).json({ ok: false, error: "bad request" });
|
||||
}
|
||||
|
||||
const existingUser = await dbGet("SELECT * FROM users WHERE username = ?", username);
|
||||
const existingUser = await dbGet("SELECT * FROM users WHERE username = ?", username);
|
||||
|
||||
if (existingUser !== undefined) {
|
||||
return res.status(400).json({ ok: false, error: "username taken" });
|
||||
@ -62,7 +66,7 @@ app.post("/api/register", async (req, res) => {
|
||||
|
||||
const passwordHash = await bcrypt.hash(password, 10);
|
||||
|
||||
await dbGet("INSERT INTO users (username, password) VALUES (?, ?)", username, passwordHash);
|
||||
await dbGet("INSERT INTO users (username, password) VALUES (?, ?)", username, passwordHash);
|
||||
|
||||
return res.status(200).json({ ok: true });
|
||||
});
|
||||
@ -107,16 +111,16 @@ app.get("/api/search", async (req, res) => {
|
||||
}
|
||||
|
||||
const [start, end] = [20 * page, 20];
|
||||
const videos = await dbAll(`
|
||||
SELECT videos.*, users.username AS author
|
||||
FROM videos
|
||||
JOIN users ON users.id = videos.user_id
|
||||
WHERE title LIKE CONCAT('%', ?, '%')
|
||||
LIMIT ?
|
||||
OFFSET ?
|
||||
`, search, end, start);
|
||||
const videos = await dbAll(`
|
||||
SELECT videos.*, users.username AS author
|
||||
FROM videos
|
||||
JOIN users ON users.id = videos.user_id
|
||||
WHERE title LIKE CONCAT('%', ?, '%')
|
||||
LIMIT ?
|
||||
OFFSET ?
|
||||
`, search, end, start);
|
||||
|
||||
const { total } = await dbGet("SELECT COUNT(*) AS total FROM videos");
|
||||
const { total } = await dbGet("SELECT COUNT(*) AS total FROM videos");
|
||||
|
||||
return res.status(200).json({ ok: true, videos, total });
|
||||
});
|
||||
@ -155,7 +159,7 @@ app.get("/api/logout", authorized(), (req, res) => {
|
||||
return res.status(200).json({ ok: true });
|
||||
});
|
||||
|
||||
app.post("/api/upload_video", authorized(), fileUpload({ limits: { fileSize: 2 ** 26 }, useTempFiles: true }), async (req, res) => {
|
||||
app.post("/api/upload-video", authorized(), fileUpload({ limits: { fileSize: 2 ** 26 }, useTempFiles: true }), async (req, res) => {
|
||||
const { title } = req.body;
|
||||
|
||||
if (!req.files || !req.files.video) {
|
||||
@ -169,7 +173,7 @@ app.post("/api/upload_video", authorized(), fileUpload({ limits: { fileSize: 2 *
|
||||
const userId = req.user.id;
|
||||
const id = randomString(4);
|
||||
const tempPath = req.files.video.tempFilePath;
|
||||
const newPath = path.join(dirname(), "videos", `${id}.mp4`);
|
||||
const newPath = path.join(dirname(), videoPath(id));
|
||||
const thumbnailPath = path.join(dirname(), "videos", `${id}.png`);
|
||||
|
||||
console.log(newPath);
|
||||
@ -197,7 +201,27 @@ app.post("/api/upload_video", authorized(), fileUpload({ limits: { fileSize: 2 *
|
||||
await dbRun("INSERT INTO videos (id, user_id, title) VALUES (?, ?, ?)", id, userId, title);
|
||||
|
||||
return res.status(200).json({ ok: true });
|
||||
})
|
||||
});
|
||||
|
||||
app.get("/api/video-info", async (req, res) => {
|
||||
const id = req.query["id"];
|
||||
|
||||
const video = await dbGet(`
|
||||
SELECT videos.id, videos.title, users.username AS author
|
||||
FROM videos
|
||||
JOIN users ON users.id = videos.user_id
|
||||
WHERE videos.id = ?
|
||||
LIMIT 1
|
||||
`, id);
|
||||
|
||||
if (!video) {
|
||||
return res.status(404).json({ ok: false, error: "video not found" });
|
||||
}
|
||||
|
||||
video.path = path.join("/", videoPath(id));
|
||||
|
||||
return res.status(200).json({ ok: true, video });
|
||||
});
|
||||
|
||||
app.use((err, req, res, next) => {
|
||||
console.error(err);
|
||||
|
@ -35,7 +35,7 @@ function displayHeader() {
|
||||
${links}
|
||||
-
|
||||
<form id="search-form" method="GET" target="_self" action="/search">
|
||||
<input type="text" id="search" name="query" placeholder="Search">
|
||||
<input type="text" id="search" name="query" placeholder="Search" accesskey="s">
|
||||
<input type="submit" value="Search">
|
||||
</form>
|
||||
</nav>
|
||||
|
@ -72,7 +72,16 @@ input::file-selector-button {
|
||||
}
|
||||
|
||||
#video-player {
|
||||
max-height: 80vh;
|
||||
height: 60vh;
|
||||
width: 100%;
|
||||
background-color: black;
|
||||
}
|
||||
|
||||
#video-result {
|
||||
width: 106vh;
|
||||
display: none;
|
||||
text-align: left;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.video-item {
|
||||
@ -130,6 +139,3 @@ a {
|
||||
color: var(--red);
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: #FF5722;
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
</head>
|
||||
<body>
|
||||
<h1>MaoTube</h1>
|
||||
<form action="/api/upload_video" method="POST" enctype="multipart/form-data">
|
||||
<form action="/api/upload-video" method="POST" enctype="multipart/form-data">
|
||||
<label for="username"><p>Title</p></label>
|
||||
<input type="text" name="title" autofocus>
|
||||
<label for="password"><p>Video</p></label>
|
||||
|
@ -7,19 +7,21 @@
|
||||
</head>
|
||||
<body>
|
||||
<h1>MaoTube</h1>
|
||||
<form method="GET" target="_self" action="/search">
|
||||
<label for="query"><p>Search</p></label>
|
||||
<input type="text" id="query" name="query" placeholder="...">
|
||||
<input type="submit" value="Search">
|
||||
</form>
|
||||
<br>
|
||||
<div id="result">
|
||||
<div id="video-result">
|
||||
<video id="video-player"></video>
|
||||
<h1 id="video-title"></h1>
|
||||
<span id="video-author"></span>
|
||||
</div>
|
||||
|
||||
<noscript>
|
||||
<div class="mao-error">
|
||||
<p>javascript not enabled</p>
|
||||
<p>bottom text</p>
|
||||
</div>
|
||||
</noscript>
|
||||
|
||||
<div id="mao-error" class="mao-error hidden">
|
||||
<p id="mao-error-message"></p>
|
||||
<p>bottom text</p>
|
||||
|
@ -1,13 +1,14 @@
|
||||
|
||||
function error(message) {
|
||||
const errorContainer = document.getElementById("mao-error");
|
||||
const errorElement = document.getElementById("mao-error-message");
|
||||
|
||||
errorElement.innerText = message;
|
||||
document.getElementById("video-result").style.display = "none";
|
||||
|
||||
errorElement.innerText = message || "unknown error";
|
||||
errorContainer.classList.remove("hidden");
|
||||
}
|
||||
|
||||
function main() {
|
||||
async function main() {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const id = params.get("id");
|
||||
if (!id) {
|
||||
@ -15,17 +16,35 @@ function main() {
|
||||
return;
|
||||
}
|
||||
|
||||
const result = document.getElementById("result");
|
||||
|
||||
const video = document.createElement("video");
|
||||
video.controls = true;
|
||||
video.id = "video-player";
|
||||
video.src = `/videos/${id}.mp4`;
|
||||
result.appendChild(video);
|
||||
video.onerror = () => {
|
||||
video.remove();
|
||||
error("invalid id parameter");
|
||||
const info = await fetch(`/api/video-info?id=${id}`);
|
||||
if (!info.ok) {
|
||||
error("error fetching video info");
|
||||
return;
|
||||
}
|
||||
|
||||
const json = await info.json();
|
||||
if (!json.ok) {
|
||||
error(json.error);
|
||||
return;
|
||||
}
|
||||
|
||||
const video = json.video;
|
||||
|
||||
const player = document.getElementById("video-player");
|
||||
player.controls = true;
|
||||
player.src = video.path;
|
||||
player.onerror = () => {
|
||||
error("unable to play video");
|
||||
}
|
||||
player.onload = () => {
|
||||
player.style.height = player.clientWidth / 16 * 9 + "px";
|
||||
}
|
||||
|
||||
document.getElementById("video-title").innerText = video.title;
|
||||
document.getElementById("video-author").innerText = "by " + video.author;
|
||||
|
||||
document.getElementById("video-result").style.display = "block";
|
||||
}
|
||||
|
||||
main();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user