kn-http-server 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apis/api.js +49 -0
- package/favicon.ico +0 -0
- package/http/client/httpc.css +0 -0
- package/http/client/httpc.html +0 -0
- package/http/client/httpc.js +0 -0
- package/http/fe.js +96 -0
- package/http/https.js +83 -0
- package/index.html +20 -0
- package/package.json +12 -0
- package/readme.md +6 -0
- package/script.js +14 -0
- package/server.js +100 -0
- package/style.css +16 -0
- package/test.js +14 -0
- package/utils/dirpath.js +23 -0
- package/utils/ffmpeg.js +129 -0
- package/utils/mime.js +113 -0
package/apis/api.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { open } from "node:fs/promises";
|
|
2
|
+
import http from "node:http";
|
|
3
|
+
import url from "node:url";
|
|
4
|
+
|
|
5
|
+
const clients = [];
|
|
6
|
+
|
|
7
|
+
const port = "3000";
|
|
8
|
+
const ip = "0.0.0.0";
|
|
9
|
+
|
|
10
|
+
const fp = String.raw`C:\Users\karan_pnrp70e\Desktop\Captures\screenrecording\Recording 2025-11-04 091942.mp4`;
|
|
11
|
+
// const fp = String.raw`C:\Users\karan_pnrp70e\Videos\Frankenstein (2025) [1080p] [WEBRip] [5.1] [YTS.MX]\Frankenstein.2025.1080p.WEBRip.x264.AAC5.1-[YTS.MX].mp4`;
|
|
12
|
+
// const fp = String.raw`C:\Users\karan_pnrp70e\Videos\Better Call Saul Season 2 (1080p x265 10bit Joy)\Better Call Saul S02E01 Switch (1080p x265 10bit Joy).mkv`;
|
|
13
|
+
|
|
14
|
+
const server = http.createServer(async (req, res) => {
|
|
15
|
+
res.statusCode = 200;
|
|
16
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
17
|
+
// res.setHeader("Content-Type", "video/x-matroska");
|
|
18
|
+
|
|
19
|
+
if (req.url === "/video") {
|
|
20
|
+
const fh = await open(fp);
|
|
21
|
+
const rs = fh.createReadStream();
|
|
22
|
+
const { size } = await fh.stat();
|
|
23
|
+
res.setHeader("Content-Type", "video/mp4");
|
|
24
|
+
res.setHeader("Content-Length", size);
|
|
25
|
+
rs.pipe(res);
|
|
26
|
+
|
|
27
|
+
// res.end("sending video");
|
|
28
|
+
return
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
res.write("Hello from HTTP server!\n");
|
|
32
|
+
res.end();
|
|
33
|
+
|
|
34
|
+
req.on("data", (chunk) => {
|
|
35
|
+
console.log(chunk.toString());
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// server.on("connection", (socket) => {
|
|
40
|
+
// clients.push(socket);
|
|
41
|
+
// // socket.on("data", (chunk) => {
|
|
42
|
+
// // console.log(chunk.toString());
|
|
43
|
+
// // });
|
|
44
|
+
// console.log(clients);
|
|
45
|
+
// });
|
|
46
|
+
|
|
47
|
+
server.listen(port, ip, () =>
|
|
48
|
+
console.log(`API listening on http://${ip}:${port}`)
|
|
49
|
+
);
|
package/favicon.ico
ADDED
|
Binary file
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/http/fe.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import { execSync } from "node:child_process";
|
|
5
|
+
|
|
6
|
+
// const dirName = process.argv[2] || "OneDrive";
|
|
7
|
+
// const absolutePath = path.join(os.homedir(), dirName);
|
|
8
|
+
|
|
9
|
+
// const files = fs.readdirSync(fullPath);
|
|
10
|
+
// console.log(files);
|
|
11
|
+
|
|
12
|
+
const ignoreFolderList = new Set([".git", ".env", "node_modules"]);
|
|
13
|
+
|
|
14
|
+
const allowedFilesList = new Set([
|
|
15
|
+
".png",
|
|
16
|
+
".jpg",
|
|
17
|
+
".jpeg",
|
|
18
|
+
".mp4",
|
|
19
|
+
".txt",
|
|
20
|
+
".svg",
|
|
21
|
+
".pdf",
|
|
22
|
+
".docx",
|
|
23
|
+
".xlsx",
|
|
24
|
+
".pptx",
|
|
25
|
+
".mp3",
|
|
26
|
+
".mkv",
|
|
27
|
+
".webp",
|
|
28
|
+
".heic",
|
|
29
|
+
".dng",
|
|
30
|
+
".cr2",
|
|
31
|
+
".cr3",
|
|
32
|
+
".nef",
|
|
33
|
+
".arw",
|
|
34
|
+
".raf",
|
|
35
|
+
".rw2",
|
|
36
|
+
".orf",
|
|
37
|
+
".mov",
|
|
38
|
+
".avi",
|
|
39
|
+
".webm",
|
|
40
|
+
".flv",
|
|
41
|
+
".wmv",
|
|
42
|
+
".m4v",
|
|
43
|
+
".3gp",
|
|
44
|
+
".ts",
|
|
45
|
+
".mts",
|
|
46
|
+
".vob",
|
|
47
|
+
".wav",
|
|
48
|
+
".aac",
|
|
49
|
+
".flac",
|
|
50
|
+
".ogg",
|
|
51
|
+
".m4a",
|
|
52
|
+
".wma",
|
|
53
|
+
".opus",
|
|
54
|
+
".alac",
|
|
55
|
+
".aiff",
|
|
56
|
+
".ape",
|
|
57
|
+
".amr",
|
|
58
|
+
]);
|
|
59
|
+
|
|
60
|
+
export function getAllFiles(dirPath) {
|
|
61
|
+
try {
|
|
62
|
+
let files = [];
|
|
63
|
+
|
|
64
|
+
const stats = fs.statSync(dirPath);
|
|
65
|
+
|
|
66
|
+
if (!stats.isDirectory()) {
|
|
67
|
+
if (allowedFilesList.has(path.extname(dirPath))) {
|
|
68
|
+
return [dirPath];
|
|
69
|
+
} else {
|
|
70
|
+
return [];
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const items = fs.readdirSync(dirPath);
|
|
75
|
+
for (const item of items) {
|
|
76
|
+
if (ignoreFolderList.has(item)) continue;
|
|
77
|
+
const fullPath = path.join(dirPath, item);
|
|
78
|
+
|
|
79
|
+
const stats = fs.statSync(fullPath);
|
|
80
|
+
|
|
81
|
+
if (stats.isDirectory()) {
|
|
82
|
+
files = [...files, ...getAllFiles(fullPath)];
|
|
83
|
+
} else {
|
|
84
|
+
if (!allowedFilesList.has(path.extname(fullPath))) continue;
|
|
85
|
+
files.push(fullPath);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return files;
|
|
90
|
+
} catch (error) {
|
|
91
|
+
throw error;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// console.log("Working directory:", process.cwd());
|
|
96
|
+
// console.log(getAllFiles(fullPath));
|
package/http/https.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// import { open } from "node:fs/promises";
|
|
2
|
+
import { createReadStream, statSync, existsSync } from "node:fs";
|
|
3
|
+
// import { stat } from "node:fs/promises";
|
|
4
|
+
import http from "node:http";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { dirs } from "./dir.js";
|
|
7
|
+
|
|
8
|
+
const clients = [];
|
|
9
|
+
|
|
10
|
+
const port = 3000;
|
|
11
|
+
const ip = "0.0.0.0";
|
|
12
|
+
|
|
13
|
+
// const fp = String.raw`C:\Users\karan_pnrp70e\Desktop\Captures\screenrecording\Recording 2025-11-04 091942.mp4`;
|
|
14
|
+
// const fp = String.raw`C:\Users\karan_pnrp70e\Videos\Frankenstein (2025) [1080p] [WEBRip] [5.1] [YTS.MX]\Frankenstein.2025.1080p.WEBRip.x264.AAC5.1-[YTS.MX].mp4`;
|
|
15
|
+
// const fp = String.raw`C:\Users\karan_pnrp70e\Videos\Better Call Saul Season 2 (1080p x265 10bit Joy)\Better Call Saul S02E01 Switch (1080p x265 10bit Joy).mkv`;
|
|
16
|
+
|
|
17
|
+
const server = http.createServer(async (req, res) => {
|
|
18
|
+
// res.statusCode = 200;
|
|
19
|
+
// res.setHeader("Access-Control-Allow-Origin", "*");
|
|
20
|
+
// res.setHeader("Content-Type", "video/x-matroska");
|
|
21
|
+
console.log(req.url);
|
|
22
|
+
|
|
23
|
+
if (req.url === "/") {
|
|
24
|
+
// const fh = await open("./public/index.html");
|
|
25
|
+
const rs = createReadStream("./public/index.html");
|
|
26
|
+
rs.pipe(res);
|
|
27
|
+
return;
|
|
28
|
+
} else {
|
|
29
|
+
// const fh = await open(`./public${req.url}`);
|
|
30
|
+
const fp = req.url.substring(1);
|
|
31
|
+
|
|
32
|
+
// const info = statSync(fp);
|
|
33
|
+
// console.log(info);
|
|
34
|
+
|
|
35
|
+
console.log(fp);
|
|
36
|
+
|
|
37
|
+
const ext = path.extname(req.url);
|
|
38
|
+
|
|
39
|
+
console.log("file ext", ext);
|
|
40
|
+
|
|
41
|
+
if (ext) {
|
|
42
|
+
const rs = createReadStream(`.${req.url}`);
|
|
43
|
+
rs.pipe(res);
|
|
44
|
+
|
|
45
|
+
rs.on("error", (err) => {
|
|
46
|
+
res.statusCode = 404;
|
|
47
|
+
res.end(err.message);
|
|
48
|
+
});
|
|
49
|
+
} else {
|
|
50
|
+
const dirsList = dirs(fp);
|
|
51
|
+
dirsList.forEach((item, index) => {
|
|
52
|
+
// dirsList[index] = `<li>${item}</li>`;
|
|
53
|
+
// res.write(`<link>${item}</link>`);
|
|
54
|
+
res.write(`<a href="${fp}/${item}">Go to ${item}</a><br>`);
|
|
55
|
+
});
|
|
56
|
+
// res.end(dirsList.join("\n"));
|
|
57
|
+
res.end();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// if (req.url === "/video") {
|
|
62
|
+
// const fh = await open(fp);
|
|
63
|
+
// const rs = fh.createReadStream();
|
|
64
|
+
// const { size } = await fh.stat();
|
|
65
|
+
// res.setHeader("Content-Type", "video/mp4");
|
|
66
|
+
// res.setHeader("Content-Length", size);
|
|
67
|
+
// rs.pipe(res);
|
|
68
|
+
|
|
69
|
+
// // res.end("sending video");
|
|
70
|
+
// return;
|
|
71
|
+
// }
|
|
72
|
+
|
|
73
|
+
// res.write("Hello from HTTP server!\n");
|
|
74
|
+
// res.end();
|
|
75
|
+
|
|
76
|
+
// req.on("data", (chunk) => {
|
|
77
|
+
// console.log(chunk.toString());
|
|
78
|
+
// });
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
server.listen(port, ip, () =>
|
|
82
|
+
console.log(`API listening on http://${ip}:${port}`)
|
|
83
|
+
);
|
package/index.html
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<link rel="stylesheet" href="style.css" />
|
|
7
|
+
<title>Index</title>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<h1>Files in Directory: - ${DynamicFolder}</h1>
|
|
11
|
+
|
|
12
|
+
<div id="dirs" data-="">
|
|
13
|
+
<ol>
|
|
14
|
+
${DynamicHTML}
|
|
15
|
+
</ol>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
<script src="script.js" , type="module"></script>
|
|
19
|
+
</body>
|
|
20
|
+
</html>
|
package/package.json
ADDED
package/readme.md
ADDED
package/script.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// console.log("hii html")
|
|
2
|
+
|
|
3
|
+
// document.documentElement.style.background = "blue";
|
|
4
|
+
// or
|
|
5
|
+
// document.body.style.background = "pink";
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
// Dynamic Element from js
|
|
9
|
+
const el = document.createElement("div");
|
|
10
|
+
el.textContent = "test";
|
|
11
|
+
// el.id = "ce"
|
|
12
|
+
el.style.background = "blue"
|
|
13
|
+
|
|
14
|
+
document.body.appendChild(el);
|
package/server.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { open, readdir, readFile } from "node:fs/promises";
|
|
3
|
+
import http from "node:http";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { mime } from "./utils/mime.js";
|
|
6
|
+
const server = http.createServer();
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
//create absolute path
|
|
10
|
+
export function createApath(filePath) {
|
|
11
|
+
const ap = path.join(import.meta.dirname, filePath);
|
|
12
|
+
return ap;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let port = 3000;
|
|
16
|
+
let ip = "0.0.0.0";
|
|
17
|
+
|
|
18
|
+
const args = process.argv.filter(Boolean);
|
|
19
|
+
|
|
20
|
+
if (args.includes("-p")) {
|
|
21
|
+
const portNum = args[args.indexOf("-p") + 1];
|
|
22
|
+
if (!portNum || isNaN(Number(portNum))) {
|
|
23
|
+
throw Error("Invalid port number after -p");
|
|
24
|
+
}
|
|
25
|
+
port = Number(portNum);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (args.includes("-ip")) {
|
|
29
|
+
const ipAddr = args[args.indexOf("-ip") + 1];
|
|
30
|
+
if (!ipAddr) {
|
|
31
|
+
throw Error("Invalid IP address after -ip");
|
|
32
|
+
}
|
|
33
|
+
ip = ipAddr;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
server.on("request", async (req, res) => {
|
|
37
|
+
let [url, query] = req.url.split("?");
|
|
38
|
+
url = decodeURIComponent(url);
|
|
39
|
+
console.log(url, "and", query);
|
|
40
|
+
|
|
41
|
+
if (url == "/favicon.ico") {
|
|
42
|
+
const favicon = await readFile(createApath("favicon.ico"));
|
|
43
|
+
res.setHeader("Content-Type", "image/x-icon");
|
|
44
|
+
return res.end(favicon);
|
|
45
|
+
// console.clear();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const fh = await open(`.${url}`);
|
|
50
|
+
const stat = await fh.stat();
|
|
51
|
+
const contentType = mime(url);
|
|
52
|
+
|
|
53
|
+
if (stat.isDirectory()) {
|
|
54
|
+
const htmlContent = await readFile(createApath("index.html"));
|
|
55
|
+
const dirsList = await readdir(`.${url}`);
|
|
56
|
+
let DynamicHTML = "";
|
|
57
|
+
let DynamicFolder = url;
|
|
58
|
+
dirsList.forEach((item, index) => {
|
|
59
|
+
// dirsList[index] = `<li>${item}</li>`;
|
|
60
|
+
|
|
61
|
+
DynamicHTML += `<li><a href="${
|
|
62
|
+
url === "/" ? "" : url
|
|
63
|
+
}/${item}"> ${item}</a> <a href="${
|
|
64
|
+
url === "/" ? "" : url
|
|
65
|
+
}/${item}?preview"> 👁️</a> <a href="${
|
|
66
|
+
url === "/" ? "" : url
|
|
67
|
+
}/${item}?download"> ⬇️</a></li>`;
|
|
68
|
+
});
|
|
69
|
+
if (query === "download") {
|
|
70
|
+
res.setHeader("content-disposition", "attachment");
|
|
71
|
+
// res.setHeader("Content-Type", `${contentType}`);
|
|
72
|
+
}
|
|
73
|
+
res.end(
|
|
74
|
+
htmlContent
|
|
75
|
+
.toString()
|
|
76
|
+
.replace("${DynamicHTML}", DynamicHTML)
|
|
77
|
+
.replace("${DynamicFolder}", DynamicFolder)
|
|
78
|
+
);
|
|
79
|
+
} else {
|
|
80
|
+
res.setHeader("Content-Type", `${contentType}`);
|
|
81
|
+
if (query === "download") {
|
|
82
|
+
res.setHeader("content-disposition", "attachment");
|
|
83
|
+
// res.setHeader("filename", "a.txt")
|
|
84
|
+
}
|
|
85
|
+
res.setHeader("Content-length", `${stat.size}`);
|
|
86
|
+
const rs = fh.createReadStream();
|
|
87
|
+
rs.pipe(res);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
res.on("close", () => {
|
|
91
|
+
fh.close();
|
|
92
|
+
});
|
|
93
|
+
} catch (err) {
|
|
94
|
+
res.end(err.message);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
server.listen(port, ip, () => {
|
|
99
|
+
console.log(`API listening on http://${ip}:${port}`);
|
|
100
|
+
});
|
package/style.css
ADDED
package/test.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
|
|
6
|
+
const dirn = path.join(process.cwd(), "public");
|
|
7
|
+
console.log(dirn);
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
const fp = fileURLToPath("file:///C:/Users/karan_pnrp70e/Desktop/server/mime.js")
|
|
11
|
+
const dir = path.dirname(fp)
|
|
12
|
+
const tfn = path.basename(dir);
|
|
13
|
+
console.log(tfn)
|
|
14
|
+
// console.log(import.meta.dirname)
|
package/utils/dirpath.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
|
|
4
|
+
//create absolute path
|
|
5
|
+
export function createApath(filePath) {
|
|
6
|
+
const dir = path.join(import.meta.dirname, filePath);
|
|
7
|
+
return dir;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
//join relative path
|
|
11
|
+
export function joinRpath(filePath) {
|
|
12
|
+
const dirn = path.join(process.cwd(), filePath);
|
|
13
|
+
return dirn;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// console.log(joinPath(".././public"))
|
|
17
|
+
|
|
18
|
+
function test() {
|
|
19
|
+
const fp = fileURLToPath(import.meta.url);
|
|
20
|
+
const dir = path.dirname(fp);
|
|
21
|
+
const tfn = path.basename(dir);
|
|
22
|
+
console.log(tfn);
|
|
23
|
+
}
|
package/utils/ffmpeg.js
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { open, readdir, readFile } from "node:fs/promises";
|
|
2
|
+
import { createReadStream } from "node:fs";
|
|
3
|
+
import { spawn } from "node:child_process";
|
|
4
|
+
import http from "node:http";
|
|
5
|
+
import { MIMEParams, MIMEType } from "node:util";
|
|
6
|
+
import { mime } from "../mime.js";
|
|
7
|
+
const server = http.createServer();
|
|
8
|
+
|
|
9
|
+
const port = 3000;
|
|
10
|
+
const ip = "0.0.0.0";
|
|
11
|
+
|
|
12
|
+
server.on("request", async (req, res) => {
|
|
13
|
+
let [url, query] = req.url.split("?");
|
|
14
|
+
url = decodeURIComponent(url);
|
|
15
|
+
console.log(url, "and", query);
|
|
16
|
+
|
|
17
|
+
if (url == "/favicon.ico") return res.end("no favicon");
|
|
18
|
+
// console.clear();
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
const fh = await open(`.${url}`);
|
|
22
|
+
const stat = await fh.stat();
|
|
23
|
+
// const contentType = mime.lookup(url);
|
|
24
|
+
const contentType = mime(url);
|
|
25
|
+
|
|
26
|
+
if (stat.isDirectory()) {
|
|
27
|
+
const htmlContent = await readFile("./public/index.html");
|
|
28
|
+
const dirsList = await readdir(`.${url}`);
|
|
29
|
+
let DynamicHTML = "";
|
|
30
|
+
let DynamicFolder = url;
|
|
31
|
+
dirsList.forEach((item, index) => {
|
|
32
|
+
// dirsList[index] = `<li>${item}</li>`;
|
|
33
|
+
|
|
34
|
+
DynamicHTML += `<li><a href="${
|
|
35
|
+
url === "/" ? "" : url
|
|
36
|
+
}/${item}"> ${item}</a> <a href="${
|
|
37
|
+
url === "/" ? "" : url
|
|
38
|
+
}/${item}?preview"> 👁️</a> <a href="${
|
|
39
|
+
url === "/" ? "" : url
|
|
40
|
+
}/${item}?download"> ⬇️</a></li>`;
|
|
41
|
+
});
|
|
42
|
+
if (query === "download") {
|
|
43
|
+
res.setHeader("content-disposition", "attachment");
|
|
44
|
+
// res.setHeader("Content-Type", `${contentType}`);
|
|
45
|
+
}
|
|
46
|
+
res.end(
|
|
47
|
+
htmlContent
|
|
48
|
+
.toString()
|
|
49
|
+
.replace("${DynamicHTML}", DynamicHTML)
|
|
50
|
+
.replace("${DynamicFolder}", DynamicFolder)
|
|
51
|
+
);
|
|
52
|
+
} else {
|
|
53
|
+
const ext = url.split(".").pop()?.toLowerCase();
|
|
54
|
+
const filePath = `.${url}`;
|
|
55
|
+
|
|
56
|
+
if (query === "preview" && ext === "mkv") {
|
|
57
|
+
res.statusCode = 200;
|
|
58
|
+
res.setHeader("Content-Type", "video/mp4");
|
|
59
|
+
res.setHeader("Accept-Ranges", "bytes");
|
|
60
|
+
|
|
61
|
+
const ffmpeg = spawn("ffmpeg", [
|
|
62
|
+
"-i",
|
|
63
|
+
filePath,
|
|
64
|
+
"-f",
|
|
65
|
+
"mp4",
|
|
66
|
+
"-movflags",
|
|
67
|
+
"frag_keyframe+empty_moov",
|
|
68
|
+
"-vcodec",
|
|
69
|
+
"libx264",
|
|
70
|
+
"-acodec",
|
|
71
|
+
"aac",
|
|
72
|
+
"-preset",
|
|
73
|
+
"veryfast",
|
|
74
|
+
"-crf",
|
|
75
|
+
"23",
|
|
76
|
+
"-pix_fmt",
|
|
77
|
+
"yuv420p",
|
|
78
|
+
"-g",
|
|
79
|
+
"48",
|
|
80
|
+
"-r",
|
|
81
|
+
"30",
|
|
82
|
+
"pipe:1",
|
|
83
|
+
]);
|
|
84
|
+
|
|
85
|
+
ffmpeg.stdout.pipe(res);
|
|
86
|
+
ffmpeg.stderr.on("data", () => {});
|
|
87
|
+
|
|
88
|
+
const killFfmpeg = () => ffmpeg.kill("SIGKILL");
|
|
89
|
+
res.on("close", killFfmpeg);
|
|
90
|
+
res.on("finish", killFfmpeg);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
res.setHeader("Content-Type", `${contentType}`);
|
|
95
|
+
if (query === "download") {
|
|
96
|
+
res.setHeader("content-disposition", "attachment");
|
|
97
|
+
// res.setHeader("filename", "a.txt")
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const range = req.headers.range;
|
|
101
|
+
if (range) {
|
|
102
|
+
const [startStr, endStr] = range.replace(/bytes=/, "").split("-");
|
|
103
|
+
const start = Number.parseInt(startStr, 10);
|
|
104
|
+
const end = endStr ? Number.parseInt(endStr, 10) : stat.size - 1;
|
|
105
|
+
const chunkSize = end - start + 1;
|
|
106
|
+
|
|
107
|
+
res.statusCode = 206;
|
|
108
|
+
res.setHeader("Content-Range", `bytes ${start}-${end}/${stat.size}`);
|
|
109
|
+
res.setHeader("Accept-Ranges", "bytes");
|
|
110
|
+
res.setHeader("Content-Length", `${chunkSize}`);
|
|
111
|
+
createReadStream(filePath, { start, end }).pipe(res);
|
|
112
|
+
} else {
|
|
113
|
+
res.setHeader("Content-length", `${stat.size}`);
|
|
114
|
+
const rs = fh.createReadStream();
|
|
115
|
+
rs.pipe(res);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
res.on("close", () => {
|
|
120
|
+
fh.close();
|
|
121
|
+
});
|
|
122
|
+
} catch (err) {
|
|
123
|
+
res.end(err.message);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
server.listen(port, ip, () => {
|
|
128
|
+
console.log(`API listening on http://${ip}:${port}`);
|
|
129
|
+
});
|
package/utils/mime.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
const extensionToMime = new Map([
|
|
2
|
+
// Text
|
|
3
|
+
["txt", "text/plain; charset=utf-8"],
|
|
4
|
+
["text", "text/plain; charset=utf-8"],
|
|
5
|
+
["csv", "text/csv; charset=utf-8"],
|
|
6
|
+
["tsv", "text/tab-separated-values; charset=utf-8"],
|
|
7
|
+
["md", "text/markdown; charset=utf-8"],
|
|
8
|
+
["html", "text/html; charset=utf-8"],
|
|
9
|
+
["htm", "text/html; charset=utf-8"],
|
|
10
|
+
["css", "text/css; charset=utf-8"],
|
|
11
|
+
["js", "text/javascript; charset=utf-8"],
|
|
12
|
+
["mjs", "text/javascript; charset=utf-8"],
|
|
13
|
+
["cjs", "text/javascript; charset=utf-8"],
|
|
14
|
+
["json", "application/json; charset=utf-8"],
|
|
15
|
+
["map", "application/json; charset=utf-8"],
|
|
16
|
+
["xml", "application/xml; charset=utf-8"],
|
|
17
|
+
["yaml", "text/yaml; charset=utf-8"],
|
|
18
|
+
["yml", "text/yaml; charset=utf-8"],
|
|
19
|
+
["svg", "image/svg+xml"],
|
|
20
|
+
["srt", "application/x-subrip; charset=utf-8"],
|
|
21
|
+
|
|
22
|
+
// Images
|
|
23
|
+
["png", "image/png"],
|
|
24
|
+
["jpg", "image/jpeg"],
|
|
25
|
+
["jpeg", "image/jpeg"],
|
|
26
|
+
["gif", "image/gif"],
|
|
27
|
+
["webp", "image/webp"],
|
|
28
|
+
["avif", "image/avif"],
|
|
29
|
+
["ico", "image/x-icon"],
|
|
30
|
+
["bmp", "image/bmp"],
|
|
31
|
+
["tif", "image/tiff"],
|
|
32
|
+
["tiff", "image/tiff"],
|
|
33
|
+
["heic", "image/heic"],
|
|
34
|
+
["heif", "image/heif"],
|
|
35
|
+
["dng", "image/x-adobe-dng"],
|
|
36
|
+
["cr2", "image/x-canon-cr2"],
|
|
37
|
+
["cr3", "image/x-canon-cr3"],
|
|
38
|
+
["nef", "image/x-nikon-nef"],
|
|
39
|
+
["arw", "image/x-sony-arw"],
|
|
40
|
+
["raf", "image/x-fuji-raf"],
|
|
41
|
+
["rw2", "image/x-panasonic-rw2"],
|
|
42
|
+
["orf", "image/x-olympus-orf"],
|
|
43
|
+
|
|
44
|
+
// Audio
|
|
45
|
+
["mp3", "audio/mpeg"],
|
|
46
|
+
["wav", "audio/wav"],
|
|
47
|
+
["aac", "audio/aac"],
|
|
48
|
+
["flac", "audio/flac"],
|
|
49
|
+
["ogg", "audio/ogg"],
|
|
50
|
+
["opus", "audio/opus"],
|
|
51
|
+
["m4a", "audio/mp4"],
|
|
52
|
+
["wma", "audio/x-ms-wma"],
|
|
53
|
+
["alac", "audio/alac"],
|
|
54
|
+
["aiff", "audio/aiff"],
|
|
55
|
+
["ape", "audio/ape"],
|
|
56
|
+
["amr", "audio/amr"],
|
|
57
|
+
|
|
58
|
+
// Video
|
|
59
|
+
["mp4", "video/mp4"],
|
|
60
|
+
["m4v", "video/x-m4v"],
|
|
61
|
+
["mkv", "video/x-matroska"],
|
|
62
|
+
["mov", "video/quicktime"],
|
|
63
|
+
["avi", "video/x-msvideo"],
|
|
64
|
+
["webm", "video/webm"],
|
|
65
|
+
["flv", "video/x-flv"],
|
|
66
|
+
["wmv", "video/x-ms-wmv"],
|
|
67
|
+
["3gp", "video/3gpp"],
|
|
68
|
+
["ts", "video/mp2t"],
|
|
69
|
+
["mts", "video/mp2t"],
|
|
70
|
+
["vob", "video/dvd"],
|
|
71
|
+
|
|
72
|
+
// Documents
|
|
73
|
+
["pdf", "application/pdf"],
|
|
74
|
+
["doc", "application/msword"],
|
|
75
|
+
[
|
|
76
|
+
"docx",
|
|
77
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
78
|
+
],
|
|
79
|
+
["xls", "application/vnd.ms-excel"],
|
|
80
|
+
["xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"],
|
|
81
|
+
["ppt", "application/vnd.ms-powerpoint"],
|
|
82
|
+
[
|
|
83
|
+
"pptx",
|
|
84
|
+
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
85
|
+
],
|
|
86
|
+
["rtf", "application/rtf"],
|
|
87
|
+
["odt", "application/vnd.oasis.opendocument.text"],
|
|
88
|
+
["ods", "application/vnd.oasis.opendocument.spreadsheet"],
|
|
89
|
+
["odp", "application/vnd.oasis.opendocument.presentation"],
|
|
90
|
+
|
|
91
|
+
// Archives
|
|
92
|
+
["zip", "application/zip"],
|
|
93
|
+
["tar", "application/x-tar"],
|
|
94
|
+
["gz", "application/gzip"],
|
|
95
|
+
["tgz", "application/gzip"],
|
|
96
|
+
["rar", "application/vnd.rar"],
|
|
97
|
+
["7z", "application/x-7z-compressed"],
|
|
98
|
+
["bz2", "application/x-bzip2"],
|
|
99
|
+
["xz", "application/x-xz"],
|
|
100
|
+
|
|
101
|
+
// Fonts
|
|
102
|
+
["woff", "font/woff"],
|
|
103
|
+
["woff2", "font/woff2"],
|
|
104
|
+
["ttf", "font/ttf"],
|
|
105
|
+
["otf", "font/otf"],
|
|
106
|
+
["eot", "application/vnd.ms-fontobject"],
|
|
107
|
+
]);
|
|
108
|
+
|
|
109
|
+
export function mime(filePath) {
|
|
110
|
+
const ext = filePath.split(".").pop()?.toLowerCase();
|
|
111
|
+
if (!ext || ext === filePath.toLowerCase()) return "application/octet-stream";
|
|
112
|
+
return extensionToMime.get(ext) || "application/octet-stream";
|
|
113
|
+
}
|