nextav/tests/streaming/verify-hls.js

203 lines
7.3 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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);