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");
|
const db = new sqlite3.Database("database.sqlite3");
|
||||||
|
|
||||||
function dbGet(query, ...parameters) {
|
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) {
|
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) {
|
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) {
|
function randomString(length) {
|
||||||
@ -54,7 +58,7 @@ app.post("/api/register", async (req, res) => {
|
|||||||
return res.status(400).json({ ok: false, error: "bad request" });
|
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) {
|
if (existingUser !== undefined) {
|
||||||
return res.status(400).json({ ok: false, error: "username taken" });
|
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);
|
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 });
|
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 [start, end] = [20 * page, 20];
|
||||||
const videos = await dbAll(`
|
const videos = await dbAll(`
|
||||||
SELECT videos.*, users.username AS author
|
SELECT videos.*, users.username AS author
|
||||||
FROM videos
|
FROM videos
|
||||||
JOIN users ON users.id = videos.user_id
|
JOIN users ON users.id = videos.user_id
|
||||||
WHERE title LIKE CONCAT('%', ?, '%')
|
WHERE title LIKE CONCAT('%', ?, '%')
|
||||||
LIMIT ?
|
LIMIT ?
|
||||||
OFFSET ?
|
OFFSET ?
|
||||||
`, search, end, start);
|
`, 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 });
|
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 });
|
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;
|
const { title } = req.body;
|
||||||
|
|
||||||
if (!req.files || !req.files.video) {
|
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 userId = req.user.id;
|
||||||
const id = randomString(4);
|
const id = randomString(4);
|
||||||
const tempPath = req.files.video.tempFilePath;
|
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`);
|
const thumbnailPath = path.join(dirname(), "videos", `${id}.png`);
|
||||||
|
|
||||||
console.log(newPath);
|
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);
|
await dbRun("INSERT INTO videos (id, user_id, title) VALUES (?, ?, ?)", id, userId, title);
|
||||||
|
|
||||||
return res.status(200).json({ ok: true });
|
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) => {
|
app.use((err, req, res, next) => {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
|
@ -35,7 +35,7 @@ function displayHeader() {
|
|||||||
${links}
|
${links}
|
||||||
-
|
-
|
||||||
<form id="search-form" method="GET" target="_self" action="/search">
|
<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">
|
<input type="submit" value="Search">
|
||||||
</form>
|
</form>
|
||||||
</nav>
|
</nav>
|
||||||
|
@ -72,7 +72,16 @@ input::file-selector-button {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#video-player {
|
#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 {
|
.video-item {
|
||||||
@ -130,6 +139,3 @@ a {
|
|||||||
color: var(--red);
|
color: var(--red);
|
||||||
}
|
}
|
||||||
|
|
||||||
a:visited {
|
|
||||||
color: #FF5722;
|
|
||||||
}
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>MaoTube</h1>
|
<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>
|
<label for="username"><p>Title</p></label>
|
||||||
<input type="text" name="title" autofocus>
|
<input type="text" name="title" autofocus>
|
||||||
<label for="password"><p>Video</p></label>
|
<label for="password"><p>Video</p></label>
|
||||||
|
@ -7,19 +7,21 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>MaoTube</h1>
|
<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>
|
<br>
|
||||||
<div id="result">
|
<div id="result">
|
||||||
|
<div id="video-result">
|
||||||
|
<video id="video-player"></video>
|
||||||
|
<h1 id="video-title"></h1>
|
||||||
|
<span id="video-author"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<noscript>
|
<noscript>
|
||||||
<div class="mao-error">
|
<div class="mao-error">
|
||||||
<p>javascript not enabled</p>
|
<p>javascript not enabled</p>
|
||||||
<p>bottom text</p>
|
<p>bottom text</p>
|
||||||
</div>
|
</div>
|
||||||
</noscript>
|
</noscript>
|
||||||
|
|
||||||
<div id="mao-error" class="mao-error hidden">
|
<div id="mao-error" class="mao-error hidden">
|
||||||
<p id="mao-error-message"></p>
|
<p id="mao-error-message"></p>
|
||||||
<p>bottom text</p>
|
<p>bottom text</p>
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
|
|
||||||
function error(message) {
|
function error(message) {
|
||||||
const errorContainer = document.getElementById("mao-error");
|
const errorContainer = document.getElementById("mao-error");
|
||||||
const errorElement = document.getElementById("mao-error-message");
|
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");
|
errorContainer.classList.remove("hidden");
|
||||||
}
|
}
|
||||||
|
|
||||||
function main() {
|
async function main() {
|
||||||
const params = new URLSearchParams(window.location.search);
|
const params = new URLSearchParams(window.location.search);
|
||||||
const id = params.get("id");
|
const id = params.get("id");
|
||||||
if (!id) {
|
if (!id) {
|
||||||
@ -15,17 +16,35 @@ function main() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = document.getElementById("result");
|
const info = await fetch(`/api/video-info?id=${id}`);
|
||||||
|
if (!info.ok) {
|
||||||
const video = document.createElement("video");
|
error("error fetching video info");
|
||||||
video.controls = true;
|
return;
|
||||||
video.id = "video-player";
|
|
||||||
video.src = `/videos/${id}.mp4`;
|
|
||||||
result.appendChild(video);
|
|
||||||
video.onerror = () => {
|
|
||||||
video.remove();
|
|
||||||
error("invalid id parameter");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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();
|
main();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user