203 lines
7.3 KiB
JavaScript
203 lines
7.3 KiB
JavaScript
#!/usr/bin/env node
|
||
|
||
/**
|
||
* HLS Endpoint Verification Script
|
||
* Tests the HLS streaming endpoints to ensure they work correctly
|
||
*/
|
||
|
||
const http = require('http');
|
||
const fs = require('fs');
|
||
const path = require('path');
|
||
|
||
const TEST_VIDEO_ID = 109; // The .ts file from the logs
|
||
const HOST = 'localhost';
|
||
const PORT = 3000;
|
||
|
||
function makeRequest(path, method = 'GET') {
|
||
return new Promise((resolve, reject) => {
|
||
const options = {
|
||
hostname: HOST,
|
||
port: PORT,
|
||
path: path,
|
||
method: method,
|
||
headers: {
|
||
'Accept': '*/*',
|
||
'User-Agent': 'HLS-Test/1.0'
|
||
}
|
||
};
|
||
|
||
const req = http.request(options, (res) => {
|
||
let data = '';
|
||
|
||
res.on('data', (chunk) => {
|
||
data += chunk;
|
||
});
|
||
|
||
res.on('end', () => {
|
||
resolve({
|
||
status: res.statusCode,
|
||
headers: res.headers,
|
||
data: data
|
||
});
|
||
});
|
||
});
|
||
|
||
req.on('error', (err) => {
|
||
reject(err);
|
||
});
|
||
|
||
req.setTimeout(5000, () => {
|
||
req.destroy();
|
||
reject(new Error('Request timeout'));
|
||
});
|
||
|
||
req.end();
|
||
});
|
||
}
|
||
|
||
async function testHLSEndpoints() {
|
||
console.log(`🧪 Testing HLS endpoints for video ID: ${TEST_VIDEO_ID}`);
|
||
console.log('=====================================');
|
||
|
||
try {
|
||
// Test 1: Player Config Endpoint
|
||
console.log('\n1️⃣ Testing Player Config Endpoint...');
|
||
try {
|
||
const playerConfig = await makeRequest(`/api/video/${TEST_VIDEO_ID}/player-config`);
|
||
console.log(` Status: ${playerConfig.status} ${playerConfig.status === 200 ? '✅' : '❌'}`);
|
||
|
||
if (playerConfig.status === 200) {
|
||
const config = JSON.parse(playerConfig.data);
|
||
console.log(` Player Type: ${config.player?.type || 'unknown'}`);
|
||
console.log(` Format Type: ${config.format?.type || 'unknown'}`);
|
||
console.log(` HLS URL: ${config.streaming?.hls_url || 'none'}`);
|
||
console.log(` Direct URL: ${config.streaming?.direct_url || 'none'}`);
|
||
|
||
if (config.format?.type === 'hls') {
|
||
console.log(' ✅ Format correctly identified as HLS');
|
||
} else {
|
||
console.log(' ⚠️ Format not identified as HLS - checking file extension...');
|
||
}
|
||
} else {
|
||
console.log(` Response: ${playerConfig.data}`);
|
||
}
|
||
} catch (err) {
|
||
console.log(` ❌ Error: ${err.message}`);
|
||
}
|
||
|
||
// Test 2: HLS Playlist Endpoint (.m3u8)
|
||
console.log('\n2️⃣ Testing HLS Playlist (.m3u8)...');
|
||
try {
|
||
const playlist = await makeRequest(`/api/stream/hls/${TEST_VIDEO_ID}/playlist.m3u8`);
|
||
console.log(` Status: ${playlist.status} ${playlist.status === 200 ? '✅' : '❌'}`);
|
||
console.log(` Content-Type: ${playlist.headers['content-type'] || 'none'}`);
|
||
|
||
if (playlist.status === 200) {
|
||
console.log(' ✅ Playlist loaded successfully');
|
||
console.log(` Content-Length: ${playlist.headers['content-length'] || 'unknown'} bytes`);
|
||
|
||
// Validate M3U8 format
|
||
const lines = playlist.data.split('\n');
|
||
const hasM3UHeader = lines[0].includes('#EXTM3U');
|
||
const hasTargetDuration = lines.some(line => line.includes('#EXT-X-TARGETDURATION'));
|
||
const hasSegments = lines.some(line => line.endsWith('.ts'));
|
||
|
||
console.log(` M3U8 Format: ${hasM3UHeader ? '✅ Valid' : '❌ Invalid'}`);
|
||
console.log(` Target Duration: ${hasTargetDuration ? '✅ Present' : '❌ Missing'}`);
|
||
console.log(` Segments: ${hasSegments ? '✅ Found' : '❌ None'}`);
|
||
|
||
if (hasM3UHeader) {
|
||
// Count segments
|
||
const segments = lines.filter(line => line.endsWith('.ts'));
|
||
console.log(` Number of segments: ${segments.length}`);
|
||
|
||
// Show first few lines
|
||
console.log(' First 5 lines:');
|
||
lines.slice(0, 5).forEach(line => console.log(` ${line}`));
|
||
}
|
||
} else {
|
||
console.log(` Response: ${playlist.data}`);
|
||
}
|
||
} catch (err) {
|
||
console.log(` ❌ Error: ${err.message}`);
|
||
}
|
||
|
||
// Test 3: HLS Playlist Endpoint (without .m3u8)
|
||
console.log('\n3️⃣ Testing HLS Playlist (without .m3u8)...');
|
||
try {
|
||
const playlist = await makeRequest(`/api/stream/hls/${TEST_VIDEO_ID}/playlist`);
|
||
console.log(` Status: ${playlist.status} ${playlist.status === 200 ? '✅' : '❌'}`);
|
||
|
||
if (playlist.status === 200) {
|
||
console.log(' ✅ Alternative playlist route works');
|
||
}
|
||
} catch (err) {
|
||
console.log(` ❌ Error: ${err.message}`);
|
||
}
|
||
|
||
// Test 4: HLS Segment Endpoint
|
||
console.log('\n4️⃣ Testing HLS Segment (segment 0)...');
|
||
try {
|
||
const segment = await makeRequest(`/api/stream/hls/${TEST_VIDEO_ID}/segment/0.ts`);
|
||
console.log(` Status: ${segment.status} ${segment.status === 200 ? '✅' : '❌'}`);
|
||
console.log(` Content-Type: ${segment.headers['content-type'] || 'none'}`);
|
||
|
||
if (segment.status === 200) {
|
||
console.log(' ✅ Segment loaded successfully');
|
||
console.log(` Content-Length: ${segment.headers['content-length'] || 'unknown'} bytes`);
|
||
|
||
// Check if it's actually a TS file
|
||
const isBinary = segment.data.includes('\0') || /[^\x20-\x7E\n\r\t]/.test(segment.data);
|
||
console.log(` Content Type: ${isBinary ? '✅ Binary (likely TS)' : '⚠️ Text (unexpected)'}`);
|
||
} else {
|
||
console.log(` Response: ${segment.data}`);
|
||
|
||
// Check if it's a fallback message
|
||
if (segment.data.includes('not yet implemented')) {
|
||
console.log(' ⚠️ Segment serving not implemented for this format');
|
||
}
|
||
}
|
||
} catch (err) {
|
||
console.log(` ❌ Error: ${err.message}`);
|
||
}
|
||
|
||
// Test 5: Direct Stream Endpoint (for comparison)
|
||
console.log('\n5️⃣ Testing Direct Stream (for comparison)...');
|
||
try {
|
||
const direct = await makeRequest(`/api/stream/direct/${TEST_VIDEO_ID}`);
|
||
console.log(` Status: ${direct.status} ${direct.status === 200 ? '✅' : '❌'}`);
|
||
console.log(` Content-Type: ${direct.headers['content-type'] || 'none'}`);
|
||
|
||
if (direct.status === 200) {
|
||
console.log(' ✅ Direct streaming works');
|
||
console.log(` Content-Length: ${direct.headers['content-length'] || 'unknown'} bytes`);
|
||
|
||
// Check for range support
|
||
const acceptsRanges = direct.headers['accept-ranges'];
|
||
console.log(` Range Support: ${acceptsRanges ? `✅ ${acceptsRanges}` : '❌ None'}`);
|
||
} else {
|
||
console.log(` Response: ${direct.data}`);
|
||
}
|
||
} catch (err) {
|
||
console.log(` ❌ Error: ${err.message}`);
|
||
}
|
||
|
||
console.log('\n✅ HLS endpoint testing completed!');
|
||
console.log('\n🎯 Summary:');
|
||
console.log('• Player config should identify .ts files as HLS-compatible');
|
||
console.log('• HLS playlist should return valid M3U8 format');
|
||
console.log('• Segments should load as binary data');
|
||
console.log('• Direct streaming should work as fallback');
|
||
|
||
} catch (error) {
|
||
console.error('❌ Test suite failed:', error.message);
|
||
}
|
||
}
|
||
|
||
// Run the tests
|
||
console.log('🚀 Starting HLS endpoint verification...');
|
||
console.log(`Testing against: http://${HOST}:${PORT}`);
|
||
console.log('Make sure your development server is running!');
|
||
console.log('');
|
||
|
||
testHLSEndpoints().catch(console.error); |