initial work on mms:// server
This commit is contained in:
@@ -156,8 +156,8 @@ function configureService(service_name, service_obj, initial = false) {
|
|||||||
else ports.push(service_obj.port);
|
else ports.push(service_obj.port);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exclude PNM services
|
// Exclude PNM and MMS services (they manage their own TCP sockets)
|
||||||
if (service_obj.protocol_handler === 'pnm') {
|
if (service_obj.protocol_handler === 'pnm' || service_obj.protocol_handler === 'mms') {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -457,7 +457,7 @@ async function handleCGI(executable, cgi_file, socket, request_headers, vault, s
|
|||||||
env.SERVER_PORT = request_data.port;
|
env.SERVER_PORT = request_data.port;
|
||||||
env.SERVER_ADDR = request_data.host;
|
env.SERVER_ADDR = request_data.host;
|
||||||
env.SERVER_NAME = request_data.host;
|
env.SERVER_NAME = request_data.host;
|
||||||
if (minisrv_config.services[socket.service_name] && minisrv_config.services[socket.service_name].hide_minisrv_version) {
|
if ((minisrv_config.services[socket.service_name] && minisrv_config.services[socket.service_name].hide_minisrv_version) || minisrv_config.config.hide_server_version) {
|
||||||
env.SERVER_SOFTWARE = "NodeJS; minisrv";
|
env.SERVER_SOFTWARE = "NodeJS; minisrv";
|
||||||
} else {
|
} else {
|
||||||
// Full version
|
// Full version
|
||||||
@@ -2172,8 +2172,9 @@ function reloadConfig() {
|
|||||||
|
|
||||||
// SERVER START
|
// SERVER START
|
||||||
const git_commit = getGitRevision()
|
const git_commit = getGitRevision()
|
||||||
let z_title = "zefie's wtv minisrv v" + require('./package.json').version;
|
const pkgjson = require('./package.json');
|
||||||
const z_cgiver = "minisrv/" + require('./package.json').version;
|
let z_title = "zefie's wtv minisrv v" + pkgjson.version;
|
||||||
|
const z_cgiver = "minisrv/" + pkgjson.version;
|
||||||
if (git_commit) z_title += " (git " + git_commit + ")";
|
if (git_commit) z_title += " (git " + git_commit + ")";
|
||||||
console.log("**** Welcome to " + z_title + " ****");
|
console.log("**** Welcome to " + z_title + " ****");
|
||||||
console.log("**** Detected nodejs v" + process.versions.node + " ****")
|
console.log("**** Detected nodejs v" + process.versions.node + " ****")
|
||||||
@@ -2333,6 +2334,14 @@ Object.keys(minisrv_config.services).forEach((service_name) => {
|
|||||||
throw ("Could not bind PNM protocol handler to port " + service.port + " on " + minisrv_config.config.bind_ip + ": " + e.toString());
|
throw ("Could not bind PNM protocol handler to port " + service.port + " on " + minisrv_config.config.bind_ip + ": " + e.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (service.protocol_handler === 'mms') {
|
||||||
|
try {
|
||||||
|
handlerModules['wtvmms'].listen(service.port, minisrv_config.config.bind_ip);
|
||||||
|
protocolServers.push(handlerModules['wtvmms']);
|
||||||
|
} catch (e) {
|
||||||
|
throw ("Could not bind MMS protocol handler to port " + service.port + " on " + minisrv_config.config.bind_ip + ": " + e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const protocolHandledPorts = new Set();
|
const protocolHandledPorts = new Set();
|
||||||
@@ -2343,6 +2352,9 @@ Object.keys(minisrv_config.services).forEach((service_name) => {
|
|||||||
if (service.protocol_handler === 'pnm') {
|
if (service.protocol_handler === 'pnm') {
|
||||||
protocolHandledPorts.add([service_name, service.protocol_handler, parseInt(service.port)]);
|
protocolHandledPorts.add([service_name, service.protocol_handler, parseInt(service.port)]);
|
||||||
}
|
}
|
||||||
|
if (service.protocol_handler === 'mms') {
|
||||||
|
protocolHandledPorts.add([service_name, service.protocol_handler, parseInt(service.port)]);
|
||||||
|
}
|
||||||
// Any other future special protocols would go here, and should be added to the `protocolHandledPorts` set to avoid conflicts with the main socket listener
|
// Any other future special protocols would go here, and should be added to the `protocolHandledPorts` set to avoid conflicts with the main socket listener
|
||||||
// We ignore unknown protocols and treat it like the flag doesn't exist.
|
// We ignore unknown protocols and treat it like the flag doesn't exist.
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,141 @@
|
|||||||
|
const minisrv_service_file = true;
|
||||||
|
|
||||||
|
if (!minisrv_config.services['mms']) {
|
||||||
|
throw ("ERROR: mms service not defined in config!");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const mmsVaults = [];
|
||||||
|
// Check mms service vault for .wma, .wmv, and .asf files
|
||||||
|
if (minisrv_config.config.ServiceVaults) {
|
||||||
|
Object.keys(minisrv_config.config.ServiceVaults).forEach(function (k) {
|
||||||
|
const service_vault = wtvshared.getAbsolutePath(minisrv_config.config.ServiceVaults[k]);
|
||||||
|
mmsVaults.push(service_vault + "/mms");
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
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/asxgen/catchall.js, extract "ragen"
|
||||||
|
// if at /ServiceVault/wtv-music/asx/gen/catchall.js, extract "asx/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('/');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const url_path = request_headers.request_url.split('?')[0];
|
||||||
|
const pathParts = url_path.split('/').filter(p => p);
|
||||||
|
const serviceName = pathParts.length > 0 ? pathParts[0] : '';
|
||||||
|
let remainingPath = '/' + pathParts.slice(1).join('/');
|
||||||
|
const hadTrailingSlash = request_headers.request_url.endsWith('/');
|
||||||
|
|
||||||
|
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 = '/';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Restore trailing slash if original URL had one
|
||||||
|
if (hadTrailingSlash && !remainingPath.endsWith('/')) {
|
||||||
|
remainingPath += '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
const filename = remainingPath.endsWith('/') ? '' : remainingPath.split('/').pop().replace('.asx', '');
|
||||||
|
const directory = remainingPath.endsWith('/') ? remainingPath.replace(/\/$/, '') : remainingPath.substring(0, remainingPath.lastIndexOf('/'));
|
||||||
|
|
||||||
|
let fileFound = false;
|
||||||
|
const extensions = ['.asf', '.wma', '.wmv'];
|
||||||
|
let resolvedPath = null;
|
||||||
|
|
||||||
|
// Check if request is for a directory listing (no filename or ends with /)
|
||||||
|
if (!filename || (request_headers.request_url.endsWith('/') && minisrv_config.services['mms'].allow_indexing !== false)) {
|
||||||
|
const listingDir = filename ? directory : directory || '/';
|
||||||
|
const allFiles = [];
|
||||||
|
|
||||||
|
for (const mmsVault of mmsVaults) {
|
||||||
|
const targetDir = path.join(mmsVault, listingDir);
|
||||||
|
if (fs.existsSync(targetDir) && fs.statSync(targetDir).isDirectory()) {
|
||||||
|
const files = fs.readdirSync(targetDir);
|
||||||
|
files.forEach(file => {
|
||||||
|
const fullPath = path.join(targetDir, file);
|
||||||
|
if (fs.statSync(fullPath).isFile() && (file.endsWith('.wma') || file.endsWith('.wmv') || file.endsWith('.asf'))) {
|
||||||
|
const baseFileName = file.substring(0, file.lastIndexOf('.'));
|
||||||
|
allFiles.push(baseFileName + '.asx');
|
||||||
|
} else if (fs.statSync(fullPath).isDirectory()) {
|
||||||
|
allFiles.push(file + '/');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allFiles.length > 0) {
|
||||||
|
headers = `200 OK
|
||||||
|
Content-type: text/html`;
|
||||||
|
data = `<html>
|
||||||
|
<body bgcolor="#110e1f" text="#44a1cc" link="36d5ff" vlink="36d5ff" vspace=0>
|
||||||
|
<display nosave nosend>
|
||||||
|
<title>Windows Media on this Service</title>
|
||||||
|
<sidebar width=20%>
|
||||||
|
<img src="wtv-tricks:/images/Realaudio_bg.gif">
|
||||||
|
</sidebar>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<h1>Windows Media 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 {
|
||||||
|
headers = `404 Not Found
|
||||||
|
Content-type: text/html`;
|
||||||
|
data = `<html><body><h1>No files found</h1></body></html>`;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Original file search logic
|
||||||
|
for (const mmsVault of mmsVaults) {
|
||||||
|
for (const ext of extensions) {
|
||||||
|
const filePath = path.join(mmsVault, directory, filename + ext);
|
||||||
|
if (fs.existsSync(filePath)) {
|
||||||
|
fileFound = true;
|
||||||
|
resolvedPath = filePath;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (fileFound) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fileFound) {
|
||||||
|
headers = `404 Not Found
|
||||||
|
Content-type: text/html`;
|
||||||
|
} else {
|
||||||
|
const filePath = path.join(directory || '/', filename + path.extname(resolvedPath));
|
||||||
|
const mmsURL = `mms://${minisrv_config.config.service_ip}:${minisrv_config.services['mms'].port}${filePath.replace(/\\/g, '/')}`;
|
||||||
|
const title = (request_headers.query['wtv-title']) ? request_headers.query['wtv-title'] : minisrv_config.config.service_name+" media";
|
||||||
|
headers = `200 OK
|
||||||
|
Content-type: video/x-ms-asf`
|
||||||
|
data = `<ASX version="3.0">
|
||||||
|
<TITLE>${title} media</TITLE>
|
||||||
|
<ENTRY>
|
||||||
|
<TITLE>${title}</TITLE>
|
||||||
|
<REF HREF="${mmsURL}" />
|
||||||
|
</ENTRY>
|
||||||
|
</ASX>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
1921
zefie_wtvp_minisrv/includes/classes/WTVMMS.js
Normal file
1921
zefie_wtvp_minisrv/includes/classes/WTVMMS.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -422,17 +422,28 @@
|
|||||||
"key": "%ServiceDeps%/https/selfsigned_key.pem"
|
"key": "%ServiceDeps%/https/selfsigned_key.pem"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"mms": {
|
||||||
|
"port": 1755,
|
||||||
|
"udp_port": 1755,
|
||||||
|
"enable_udp": true,
|
||||||
|
"protocol_handler": "mms",
|
||||||
|
"pacing_multiplier": 0.9,
|
||||||
|
"enable_burst": true,
|
||||||
|
"burst_multiplier": 4.0,
|
||||||
|
"burst_prestart_ms": 15000,
|
||||||
|
"allow_indexing": true,
|
||||||
|
"debug": false,
|
||||||
|
"handler_module": "WTVMMS"
|
||||||
|
},
|
||||||
"pnm": {
|
"pnm": {
|
||||||
"port": 7070,
|
"port": 7070,
|
||||||
"flags": "0x00000001",
|
|
||||||
"allow_double_slash": true,
|
|
||||||
"protocol_handler": "pnm",
|
"protocol_handler": "pnm",
|
||||||
"udp_bind_port_min": 57361,
|
"udp_bind_port_min": 57361,
|
||||||
"udp_bind_port_max": 57391,
|
"udp_bind_port_max": 57391,
|
||||||
"descriptor_after_hello_ms": 85,
|
"descriptor_after_hello_ms": 85,
|
||||||
"burst_prestart_ms": 5000,
|
"burst_prestart_ms": 5000,
|
||||||
"debug": false,
|
|
||||||
"allow_indexing": true,
|
"allow_indexing": true,
|
||||||
|
"debug": false,
|
||||||
"handler_module": "WTVPNM"
|
"handler_module": "WTVPNM"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user