add sample realaudio, fix ragen
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
zefie_wtvp_minisrv/includes/ServiceVault/pnm/classicrom/WTVa.ra
Normal file
BIN
zefie_wtvp_minisrv/includes/ServiceVault/pnm/classicrom/WTVa.ra
Normal file
Binary file not shown.
Binary file not shown.
BIN
zefie_wtvp_minisrv/includes/ServiceVault/pnm/classicrom/karTV.ra
Normal file
BIN
zefie_wtvp_minisrv/includes/ServiceVault/pnm/classicrom/karTV.ra
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
zefie_wtvp_minisrv/includes/ServiceVault/pnm/classicrom/seqJ.ra
Normal file
BIN
zefie_wtvp_minisrv/includes/ServiceVault/pnm/classicrom/seqJ.ra
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -16,13 +16,58 @@ if (minisrv_config.config.ServiceVaults) {
|
|||||||
throw ("ERROR: No Service Vaults defined!");
|
throw ("ERROR: No Service Vaults defined!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Detect subdirectory structure of this catchall.js file and strip it from requests
|
||||||
|
// e.g., if at /ServiceVault/wtv-music/ragen/catchall.js, extract "ragen"
|
||||||
|
// if at /ServiceVault/wtv-music/ra/gen/catchall.js, extract "ra/gen"
|
||||||
|
let subDirPath = '';
|
||||||
|
const currentDir = path.dirname(__filename);
|
||||||
|
const serviceVaultIdx = currentDir.indexOf('ServiceVault');
|
||||||
|
console.log("DEBUG: currentDir =", currentDir, "serviceVaultIdx =", serviceVaultIdx);
|
||||||
|
if (serviceVaultIdx !== -1) {
|
||||||
|
const afterVault = currentDir.substring(serviceVaultIdx + 12); // 12 = length of 'ServiceVault'
|
||||||
|
console.log("DEBUG: afterVault =", afterVault);
|
||||||
|
const parts = afterVault.split(path.sep).filter(p => p);
|
||||||
|
console.log("DEBUG: parts =", parts);
|
||||||
|
if (parts.length > 1) {
|
||||||
|
// parts[0] is the service name (e.g., 'wtv-music'), parts[1+] are the subdirs
|
||||||
|
const subdirs = parts.slice(1);
|
||||||
|
subDirPath = '/' + subdirs.join('/');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log("DEBUG: Detected subDirPath =", subDirPath);
|
||||||
|
|
||||||
const url_path = request_headers.request_url.split('?')[0];
|
const url_path = request_headers.request_url.split('?')[0];
|
||||||
const pathParts = url_path.split('/').filter(p => p);
|
const pathParts = url_path.split('/').filter(p => p);
|
||||||
const serviceName = pathParts.length > 0 ? pathParts[0] : '';
|
const serviceName = pathParts.length > 0 ? pathParts[0] : '';
|
||||||
const remainingPath = '/' + pathParts.slice(1).join('/');
|
let remainingPath = '/' + pathParts.slice(1).join('/');
|
||||||
const filename = remainingPath.split('/').pop().replace('.ram', '');
|
const hadTrailingSlash = request_headers.request_url.endsWith('/');
|
||||||
const directory = remainingPath.endsWith('/') || !filename ? remainingPath.replace(/\/$/, '') : remainingPath.substring(0, remainingPath.lastIndexOf('/'));
|
|
||||||
|
|
||||||
|
console.log("DEBUG: Before stripping - subDirPath =", subDirPath, "remainingPath =", remainingPath);
|
||||||
|
|
||||||
|
let strippedSubDir = ''; // Store what was stripped for link rebuilding
|
||||||
|
// Strip the subdirectory structure from the request path
|
||||||
|
if (subDirPath) {
|
||||||
|
if (remainingPath.startsWith(subDirPath + '/')) {
|
||||||
|
// Has something after the subdirectory, e.g., /ragen/classicrom
|
||||||
|
strippedSubDir = subDirPath;
|
||||||
|
remainingPath = remainingPath.substring(subDirPath.length);
|
||||||
|
} else if (remainingPath === subDirPath || remainingPath === subDirPath + '/') {
|
||||||
|
// Just the subdirectory itself, e.g., /ragen or /ragen/
|
||||||
|
strippedSubDir = subDirPath;
|
||||||
|
remainingPath = '/';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("DEBUG: After stripping - remainingPath =", remainingPath, "strippedSubDir =", strippedSubDir);
|
||||||
|
|
||||||
|
// Restore trailing slash if original URL had one
|
||||||
|
if (hadTrailingSlash && !remainingPath.endsWith('/')) {
|
||||||
|
remainingPath += '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
const filename = remainingPath.endsWith('/') ? '' : remainingPath.split('/').pop().replace('.ram', '');
|
||||||
|
const directory = remainingPath.endsWith('/') ? remainingPath.replace(/\/$/, '') : remainingPath.substring(0, remainingPath.lastIndexOf('/'));
|
||||||
|
console.log("DEBUG: Request for service", serviceName, "with filename", filename, "and directory", directory, "remainingPath", remainingPath);
|
||||||
|
|
||||||
let fileFound = false;
|
let fileFound = false;
|
||||||
const extensions = ['.ra', '.rm'];
|
const extensions = ['.ra', '.rm'];
|
||||||
@@ -43,6 +88,8 @@ if (!filename || (request_headers.request_url.endsWith('/') && minisrv_config.se
|
|||||||
if (fs.statSync(fullPath).isFile() && (file.endsWith('.ra') || file.endsWith('.rm'))) {
|
if (fs.statSync(fullPath).isFile() && (file.endsWith('.ra') || file.endsWith('.rm'))) {
|
||||||
const baseFileName = file.substring(0, file.lastIndexOf('.'));
|
const baseFileName = file.substring(0, file.lastIndexOf('.'));
|
||||||
allFiles.push(baseFileName + '.ram');
|
allFiles.push(baseFileName + '.ram');
|
||||||
|
} else if (fs.statSync(fullPath).isDirectory()) {
|
||||||
|
allFiles.push(file + '/');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -51,7 +98,16 @@ if (!filename || (request_headers.request_url.endsWith('/') && minisrv_config.se
|
|||||||
if (allFiles.length > 0) {
|
if (allFiles.length > 0) {
|
||||||
headers = `200 OK
|
headers = `200 OK
|
||||||
Content-type: text/html`;
|
Content-type: text/html`;
|
||||||
data = `<html><body><h1>RealAudio Files on this minisrv</h1><ul>${allFiles.map(f => `<li><a href="${f}">${f}</a></li>`).join('')}</ul></body></html>`;
|
data = `<html>
|
||||||
|
<body bgcolor="#110e1f" text="#44a1cc" link="36d5ff" vlink="36d5ff" vspace=0>
|
||||||
|
<display nosave nosend>
|
||||||
|
<title>RealAudio Files on this Service</title>
|
||||||
|
<sidebar width=20%>
|
||||||
|
<img src="wtv-tricks:/images/Realaudio_bg.gif">
|
||||||
|
</sidebar>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<h1>RealAudio Files on this Service</h1><ul>${(directory === "") ? "" : `<li><a href="../">../</a></li>\n`}${allFiles.map(f => `<li><a href="${(directory === "") ? f : `${strippedSubDir}${directory}/${f}`}">${f}</a></li>`).join("\n")}</ul></body></html>`;
|
||||||
} else {
|
} else {
|
||||||
headers = `404 Not Found
|
headers = `404 Not Found
|
||||||
Content-type: text/html`;
|
Content-type: text/html`;
|
||||||
@@ -61,7 +117,7 @@ Content-type: text/html`;
|
|||||||
// Original file search logic
|
// Original file search logic
|
||||||
for (const pnmVault of pnmVaults) {
|
for (const pnmVault of pnmVaults) {
|
||||||
for (const ext of extensions) {
|
for (const ext of extensions) {
|
||||||
const filePath = path.join(pnmVault, filename + ext);
|
const filePath = path.join(pnmVault, directory, filename + ext);
|
||||||
console.log("DEBUG: Checking for file", filePath);
|
console.log("DEBUG: Checking for file", filePath);
|
||||||
if (fs.existsSync(filePath)) {
|
if (fs.existsSync(filePath)) {
|
||||||
fileFound = true;
|
fileFound = true;
|
||||||
@@ -76,8 +132,11 @@ Content-type: text/html`;
|
|||||||
headers = `404 Not Found
|
headers = `404 Not Found
|
||||||
Content-type: text/html`;
|
Content-type: text/html`;
|
||||||
} else {
|
} else {
|
||||||
|
const filePath = path.join(directory || '/', filename + path.extname(resolvedPath));
|
||||||
|
const pnmURL = `pnm://${minisrv_config.config.service_ip}:${minisrv_config.services['pnm'].port}${filePath.replace(/\\/g, '/')}`;
|
||||||
|
console.log("DEBUG: File found at", resolvedPath, "serving as", pnmURL);
|
||||||
headers = `200 OK
|
headers = `200 OK
|
||||||
Content-type: audio/x-pn-realaudio`
|
Content-type: audio/x-pn-realaudio`
|
||||||
data = `pnm://${minisrv_config.config.service_ip}:${minisrv_config.services['pnm'].port}/${filename + path.extname(resolvedPath)}`;
|
data = pnmURL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
@@ -352,14 +352,35 @@ class WTVPNM {
|
|||||||
socket.write(headers + body, () => socket.end());
|
socket.write(headers + body, () => socket.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
normalizeRequestedMediaPath(value) {
|
||||||
|
if (value === null || value === undefined) return null;
|
||||||
|
|
||||||
|
let raw = String(value).replace(/\x00+$/g, '').trim();
|
||||||
|
if (!raw) return null;
|
||||||
|
|
||||||
|
// Trim query/fragment and normalize separators to URL-style slashes.
|
||||||
|
raw = raw.split(/[?#]/)[0].replace(/\\+/g, '/');
|
||||||
|
|
||||||
|
// Drop common URI scheme prefixes if present.
|
||||||
|
raw = raw.replace(/^[A-Za-z][A-Za-z0-9+.-]*:\/\/[^/]*\/?/, '');
|
||||||
|
raw = raw.replace(/^[A-Za-z][A-Za-z0-9+.-]*:\/*/, '');
|
||||||
|
|
||||||
|
// Keep only a safe relative path under the service vault.
|
||||||
|
const parts = raw.split('/').filter((part) => part && part !== '.' && part !== '..');
|
||||||
|
if (parts.length === 0) return null;
|
||||||
|
|
||||||
|
return parts.join('/');
|
||||||
|
}
|
||||||
|
|
||||||
getRequestedMediaName(fields, rawData) {
|
getRequestedMediaName(fields, rawData) {
|
||||||
if (!Array.isArray(fields) || fields.length === 0) return this.scanRawForMediaName(rawData);
|
if (!Array.isArray(fields) || fields.length === 0) return this.scanRawForMediaName(rawData);
|
||||||
|
|
||||||
// Field 0x52 (82) carries the requested file name in observed captures.
|
// Field 0x52 (82) carries the requested file name in observed captures.
|
||||||
const fileField = fields.find((f) => f && f.id === 82 && f.len > 0);
|
const fileField = fields.find((f) => f && f.id === 82 && f.len > 0);
|
||||||
if (fileField) {
|
if (fileField) {
|
||||||
const raw = fileField.value.toString('latin1').replace(/\x00+$/g, '').trim();
|
const raw = fileField.value.toString('latin1');
|
||||||
if (raw) return path.basename(raw);
|
const normalized = this.normalizeRequestedMediaPath(raw);
|
||||||
|
if (normalized) return normalized;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some clients may carry filename in another TLV field; scan all text values.
|
// Some clients may carry filename in another TLV field; scan all text values.
|
||||||
@@ -368,7 +389,8 @@ class WTVPNM {
|
|||||||
const raw = field.value.toString('latin1').replace(/\x00+/g, ' ').trim();
|
const raw = field.value.toString('latin1').replace(/\x00+/g, ' ').trim();
|
||||||
const match = raw.match(/([A-Za-z0-9_\-\.\/]+\.(?:ra|ray|rm|ram))/i);
|
const match = raw.match(/([A-Za-z0-9_\-\.\/]+\.(?:ra|ray|rm|ram))/i);
|
||||||
if (match) {
|
if (match) {
|
||||||
return path.basename(match[1]);
|
const normalized = this.normalizeRequestedMediaPath(match[1]);
|
||||||
|
if (normalized) return normalized;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -379,8 +401,8 @@ class WTVPNM {
|
|||||||
scanRawForMediaName(rawData) {
|
scanRawForMediaName(rawData) {
|
||||||
if (!Buffer.isBuffer(rawData)) return null;
|
if (!Buffer.isBuffer(rawData)) return null;
|
||||||
const str = rawData.toString('latin1');
|
const str = rawData.toString('latin1');
|
||||||
const match = str.match(/([A-Za-z0-9_\-\.]+\.(?:ra|ray|rm|ram))(?:[^A-Za-z0-9]|$)/i);
|
const match = str.match(/([A-Za-z0-9_\-\.\/]+\.(?:ra|ray|rm|ram))(?:[^A-Za-z0-9]|$)/i);
|
||||||
return match ? path.basename(match[1]) : null;
|
return match ? this.normalizeRequestedMediaPath(match[1]) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
getClientChallenge(fields) {
|
getClientChallenge(fields) {
|
||||||
@@ -423,6 +445,7 @@ class WTVPNM {
|
|||||||
const base = this.wtvshared.getAbsolutePath(serviceVaultDir, vault);
|
const base = this.wtvshared.getAbsolutePath(serviceVaultDir, vault);
|
||||||
for (const variant of extensionVariants) {
|
for (const variant of extensionVariants) {
|
||||||
const candidate = this.wtvshared.makeSafePath(base, variant);
|
const candidate = this.wtvshared.makeSafePath(base, variant);
|
||||||
|
this.debugLog('testing media candidate', candidate);
|
||||||
if (candidate && fs.existsSync(candidate) && fs.lstatSync(candidate).isFile()) {
|
if (candidate && fs.existsSync(candidate) && fs.lstatSync(candidate).isFile()) {
|
||||||
if (this.service_config.debug) {
|
if (this.service_config.debug) {
|
||||||
this.debugLog('media file found', variant, '->', candidate);
|
this.debugLog('media file found', variant, '->', candidate);
|
||||||
@@ -438,12 +461,12 @@ class WTVPNM {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getMediaNameVariants(requestedMedia) {
|
getMediaNameVariants(requestedMedia) {
|
||||||
const base = path.basename(requestedMedia || '').trim();
|
const requestedPath = this.normalizeRequestedMediaPath(requestedMedia);
|
||||||
if (!base) return [];
|
if (!requestedPath) return [];
|
||||||
|
|
||||||
const ext = path.extname(base).toLowerCase();
|
const ext = path.posix.extname(requestedPath).toLowerCase();
|
||||||
const stem = ext.length > 0 ? base.slice(0, -ext.length) : base;
|
const stem = ext.length > 0 ? requestedPath.slice(0, -ext.length) : requestedPath;
|
||||||
const variants = [base];
|
const variants = [requestedPath];
|
||||||
|
|
||||||
if (ext === '.ray') variants.push(`${stem}.ra`);
|
if (ext === '.ray') variants.push(`${stem}.ra`);
|
||||||
if (ext === '.ram') variants.push(`${stem}.ra`);
|
if (ext === '.ram') variants.push(`${stem}.ra`);
|
||||||
@@ -1147,7 +1170,12 @@ class WTVPNM {
|
|||||||
const serverChallenge = session?.serverChallenge || 0;
|
const serverChallenge = session?.serverChallenge || 0;
|
||||||
const challengeBuf = Buffer.from(challenge, 'latin1');
|
const challengeBuf = Buffer.from(challenge, 'latin1');
|
||||||
const requestedMedia = session?.requestedMedia || '';
|
const requestedMedia = session?.requestedMedia || '';
|
||||||
const resolvedMedia = session?.mediaPath ? path.basename(session.mediaPath) : '';
|
const requestedMediaPath = this.normalizeRequestedMediaPath(requestedMedia);
|
||||||
|
const resolvedBase = session?.mediaPath ? path.basename(session.mediaPath) : '';
|
||||||
|
const requestedDir = requestedMediaPath ? path.posix.dirname(requestedMediaPath) : '';
|
||||||
|
const resolvedMedia = resolvedBase
|
||||||
|
? (requestedDir && requestedDir !== '.' ? `${requestedDir}/${resolvedBase}` : resolvedBase)
|
||||||
|
: requestedMediaPath;
|
||||||
const responseSource = resolvedMedia || requestedMedia || challenge;
|
const responseSource = resolvedMedia || requestedMedia || challenge;
|
||||||
const respSrcBuf = Buffer.from(responseSource, 'latin1');
|
const respSrcBuf = Buffer.from(responseSource, 'latin1');
|
||||||
const timestamp = this.getClientTimestamp(session?.pnaFields) ?? Math.floor(Date.now() / 1000);
|
const timestamp = this.getClientTimestamp(session?.pnaFields) ?? Math.floor(Date.now() / 1000);
|
||||||
@@ -1159,7 +1187,8 @@ class WTVPNM {
|
|||||||
|
|
||||||
this.debugLog('session token seed', session?.id || '?',
|
this.debugLog('session token seed', session?.id || '?',
|
||||||
`clientChallenge=${challenge}`,
|
`clientChallenge=${challenge}`,
|
||||||
`requestedMedia=${requestedMedia || '(fallback:clientChallenge)'}`,
|
`requestedMedia=${requestedMedia}`,
|
||||||
|
`responseSource=${responseSource}`,
|
||||||
`serverChallenge=${serverChallenge.toString(16)}`,
|
`serverChallenge=${serverChallenge.toString(16)}`,
|
||||||
`v12=${v12}`,
|
`v12=${v12}`,
|
||||||
`resp1=${resp1}`, `initMD5=${initMD5}`);
|
`resp1=${resp1}`, `initMD5=${initMD5}`);
|
||||||
|
|||||||
@@ -401,7 +401,7 @@
|
|||||||
"protocol_handler": "pnm",
|
"protocol_handler": "pnm",
|
||||||
"descriptor_after_hello_ms": 85,
|
"descriptor_after_hello_ms": 85,
|
||||||
"burst_prestart_ms": 5000,
|
"burst_prestart_ms": 5000,
|
||||||
"debug": false,
|
"debug": true,
|
||||||
"allow_indexing": true
|
"allow_indexing": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user