add audio downloader
This commit is contained in:
1 parent
ea5991cc4e
commit
5c236885ce
5 files changed
+533
-10
No files matched your search
@@ -1,3 +1,8 @@
|
||||
import fs from "node:fs";
|
||||
import { IncomingMessage, ServerResponse } from "node:http";
|
||||
import https from "node:https";
|
||||
import path from "node:path";
|
||||
import { Connect, Plugin, ViteDevServer } from "vite";
|
||||
import { defineConfig, MarkdownOptions } from "vitepress";
|
||||
|
||||
const host = process.env.TAURI_DEV_HOST;
|
||||
@@ -9,7 +14,7 @@ export default defineConfig({
|
||||
lang: "ko-KR",
|
||||
themeConfig: {
|
||||
socialLinks: [
|
||||
{ icon: "github", link: "https://github.com/vuejs/vitepress" },
|
||||
{ icon: "github", link: "https://git.polv.cc/polv/ko-grammar" },
|
||||
],
|
||||
outline: {
|
||||
level: "deep",
|
||||
@@ -65,6 +70,7 @@ export default defineConfig({
|
||||
},
|
||||
},
|
||||
vite: {
|
||||
plugins: [audioProxyPlugin() as any],
|
||||
envDir: process.cwd(),
|
||||
ssr: {
|
||||
noExternal: [],
|
||||
@@ -107,3 +113,85 @@ export default defineConfig({
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
function audioProxyPlugin(): Plugin {
|
||||
return {
|
||||
name: "audio-proxy",
|
||||
configureServer(server: ViteDevServer) {
|
||||
// Vite dev server middlewares are connect-style: (req, res, next)
|
||||
server.middlewares.use((req, res, next) => {
|
||||
const reqUrl = req.url;
|
||||
if (!reqUrl || !reqUrl.startsWith("/uploads/")) return next();
|
||||
if (!reqUrl.endsWith(".mp3")) return next();
|
||||
|
||||
proxyAudio(req, res, next);
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function proxyAudio(
|
||||
req: Connect.IncomingMessage,
|
||||
res: ServerResponse<IncomingMessage>,
|
||||
next: Connect.NextFunction,
|
||||
) {
|
||||
const url = new URL(
|
||||
`https://www.howtostudykorean.com/wp-content/uploads${req.url}`,
|
||||
);
|
||||
|
||||
// Forward common headers (important for Range streaming)
|
||||
const headers = {
|
||||
...req.headers,
|
||||
host: url.host,
|
||||
range: req.headers["range"],
|
||||
};
|
||||
|
||||
const segs = req.url!.slice(1).split("/");
|
||||
const filename = segs.pop()!;
|
||||
const saveroot = "docs/public";
|
||||
const savetemp = path.join(saveroot, ...segs, filename + ".temp");
|
||||
const saveto = path.join(saveroot, ...segs, filename);
|
||||
|
||||
if (fs.existsSync(saveto)) {
|
||||
next();
|
||||
return;
|
||||
}
|
||||
|
||||
fs.mkdirSync(path.join(saveroot, ...segs), { recursive: true });
|
||||
const fileStream = fs.createWriteStream(savetemp);
|
||||
|
||||
const upstreamReq = https.request(
|
||||
{
|
||||
protocol: url.protocol,
|
||||
hostname: url.hostname,
|
||||
port: url.port || undefined,
|
||||
path: url.pathname + url.search,
|
||||
method: req.method,
|
||||
headers,
|
||||
},
|
||||
(upstreamRes) => {
|
||||
// Pass status (200 or 206 for Range)
|
||||
res.writeHead(upstreamRes.statusCode || 200, upstreamRes.headers);
|
||||
|
||||
// Stream through
|
||||
upstreamRes.pipe(res);
|
||||
upstreamRes.pipe(fileStream);
|
||||
},
|
||||
);
|
||||
|
||||
fileStream.on("finish", () => {
|
||||
fs.renameSync(savetemp, saveto);
|
||||
console.log("saved to", saveto);
|
||||
});
|
||||
|
||||
fileStream.on("error", (err) => {
|
||||
console.error("write failed:", err);
|
||||
});
|
||||
|
||||
upstreamReq.on("error", (err) => {
|
||||
res.statusCode = 502;
|
||||
res.end(`Bad Gateway: ${err.message}`);
|
||||
});
|
||||
|
||||
req.pipe(upstreamReq);
|
||||
}
|
||||
Reference in new issue
Block a user