update README.md

This commit is contained in:
2025-11-09 13:52:38 +01:00
parent b7021b13a3
commit 9d622a829f
2 changed files with 108 additions and 139 deletions

View File

@@ -10,7 +10,6 @@ const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const app = express();
app.set('trust proxy', true);
app.use(morgan("tiny"));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
@@ -19,9 +18,7 @@ app.use(express.static(path.join(__dirname, "..", "public")));
const YT_DLP = process.env.YT_DLP_PATH || "yt-dlp";
const limit = pLimit(2); // simple per-process concurrency limit
// -------------------------
// Version detection (local + latest from GitHub)
// -------------------------
// ---- Version detection & refresh (local + latest from GitHub) ----
let localYtDlpVersion = "unknown";
let latestYtDlpVersion = null;
@@ -31,7 +28,7 @@ function fetchLatestYtDlpVersion() {
{
hostname: "api.github.com",
path: "/repos/yt-dlp/yt-dlp/releases/latest",
headers: { "User-Agent": "vrc-ytdlp-resolver" }
headers: { "User-Agent": "vrc-ytdlp-webtool" }
},
(res) => {
let data = "";
@@ -61,7 +58,7 @@ async function detectLocalYtDlpVersion() {
}
(async () => {
localYtDlpVersion = await detectLocalYtDlpVersion().catch(() => "unknown");
localYtDlpVersion = await detectLocalYtDlpVersion();
try {
latestYtDlpVersion = await fetchLatestYtDlpVersion();
} catch {
@@ -77,19 +74,47 @@ async function detectLocalYtDlpVersion() {
}, 6 * 60 * 60 * 1000);
})();
// -------------------------
// Helpers
// -------------------------
function stripIpParam(directUrl) {
try {
const u = new URL(directUrl);
// remove IP-scoping params without touching signature/expiry/etc.
u.searchParams.delete("ip");
u.searchParams.delete("ipbits");
return u.toString();
} catch {
return directUrl; // if parsing fails, return original
// ---- Core: resolve direct media URL via yt-dlp ----
async function resolveDirectUrl(inputUrl) {
const formatSelector =
// 1) Progressive MP4 with audio (H.264 + AAC), https
"best[acodec!=none][vcodec*=avc][ext=mp4][protocol*=https]/" +
// 2) Any progressive with audio
"best[acodec!=none][protocol*=https]/" +
// 3) Anything with audio (may be HLS/DASH)
"best[acodec!=none]/" +
// 4) Fallback best
"best";
const json = await execJson([
"-J",
"-f",
formatSelector,
"--no-warnings",
"--no-playlist",
inputUrl
]);
// Case A: yt-dlp returns a single playable URL (progressive)
if (json.url) {
return { url: json.url, note: "Direct stream (single URL)." };
}
// Case B: adaptive (separate video/audio)
if (Array.isArray(json.requested_formats) && json.requested_formats.length) {
const vid = json.requested_formats.find((f) => f.vcodec && f.acodec === "none");
const aud = json.requested_formats.find((f) => f.acodec && f.vcodec === "none");
if (vid?.url && aud?.url) {
return {
url: vid.url,
audioUrl: aud.url,
note:
"Adaptive streams (separate video/audio). Many in-world players expect a single URL."
};
}
}
throw new Error("Could not extract a playable URL.");
}
function execJson(args) {
@@ -115,54 +140,7 @@ function execJson(args) {
});
}
// -------------------------
// Core: resolve direct media URL via yt-dlp
// -------------------------
async function resolveDirectUrl(inputUrl) {
const formatSelector =
// 1) Progressive MP4 with audio (H.264 + AAC), https
"best[acodec!=none][vcodec*=avc][ext=mp4][protocol*=https]/" +
// 2) Any progressive with audio
"best[acodec!=none][protocol*=https]/" +
// 3) Anything with audio (may be HLS/DASH)
"best[acodec!=none]/" +
// 4) Fallback best
"best";
const json = await execJson([
"-J",
"-f",
formatSelector,
"--no-warnings",
"--no-playlist",
inputUrl
]);
// Case A: yt-dlp returns a single playable URL (progressive)
if (json.url) {
return { url: stripIpParam(json.url), note: "Direct stream (single URL)." };
}
// Case B: adaptive (separate video/audio)
if (Array.isArray(json.requested_formats) && json.requested_formats.length) {
const vid = json.requested_formats.find((f) => f.vcodec && f.acodec === "none");
const aud = json.requested_formats.find((f) => f.acodec && f.vcodec === "none");
if (vid?.url && aud?.url) {
return {
url: stripIpParam(vid.url),
audioUrl: stripIpParam(aud.url),
note:
"Adaptive streams (separate video/audio). Many in-world players expect a single URL."
};
}
}
throw new Error("Could not extract a playable URL.");
}
// -------------------------
// Routes
// -------------------------
// ---- API routes ----
app.post("/api/resolve", async (req, res) => {
const { url } = req.body || {};
if (
@@ -192,9 +170,6 @@ app.get("/api/version", (req, res) => {
});
});
// -------------------------
// Server
// -------------------------
const port = process.env.PORT || 3000;
app.listen(port, () => {
console.log(`VRC yt-dlp WebTool running at http://localhost:${port}`);