/*
* NovaStream
* Developed by: urstarkz
* Telegram: t.me/urstarkz
* Instagram: urstarkz
* Website: urstark.is-a.dev
*/
import { useState, useEffect, useRef } from 'react';
import {
Play,
Pause,
Volume2,
VolumeX,
Maximize,
Minimize,
Activity,
FileVideo,
Trash2,
Bookmark,
BookmarkCheck,
Clock,
Upload,
Globe,
Settings,
RefreshCw,
Sparkles,
ChevronRight,
Wifi,
ShieldAlert,
RotateCcw,
Zap,
Tv,
Cpu,
Monitor
} from 'lucide-react';
import './App.css';
const BACKEND_URL = import.meta.env.DEV ? 'http://localhost:5000' : window.location.origin;
// Convert SRT subtitle contents to WebVTT format
function srtToVtt(srtText) {
let vttText = 'WEBVTT\n\n';
// Standard SRT replacement: comma to period, handle newlines
const normalizedSrt = srtText
.replace(/\r\n/g, '\n')
.replace(/\r/g, '\n')
.trim();
const blocks = normalizedSrt.split('\n\n');
for (let i = 0; i < blocks.length; i++) {
const block = blocks[i].split('\n');
if (block.length >= 2) {
// Find the line that has --> (usually line index 1, but sometimes 0 or 2 if there's line numbers or glitches)
let timeIndex = -1;
for (let j = 0; j < block.length; j++) {
if (block[j].includes('-->')) {
timeIndex = j;
break;
}
}
if (timeIndex !== -1) {
// Correct commas to periods for WebVTT
const vttTime = block[timeIndex].replace(/,/g, '.');
const subtitleText = block.slice(timeIndex + 1).join('\n');
vttText += `${vttTime}\n${subtitleText}\n\n`;
}
}
}
return vttText;
}
function App() {
// Input State
const [streamUrl, setStreamUrl] = useState('');
const [activeUrl, setActiveUrl] = useState('');
const [analyzing, setAnalyzing] = useState(false);
const [metadata, setMetadata] = useState(null);
const [error, setError] = useState('');
const [activeTab, setActiveTab] = useState('history'); // 'history' | 'bookmarks'
const [sidebarMinimized, setSidebarMinimized] = useState(false);
const [inBrowserFullscreen, setInBrowserFullscreen] = useState(false);
// Playback modes: 'url' (stream from web), 'local_path' (server local path), 'local_file' (direct browser sandbox file)
const [playbackMode, setPlaybackMode] = useState('url');
const [localFile, setLocalFile] = useState(null);
const [localBlobUrl, setLocalBlobUrl] = useState('');
// History & Bookmarks stored locally
const [history, setHistory] = useState([]);
const [bookmarks, setBookmarks] = useState([]);
const [showBookmarkModal, setShowBookmarkModal] = useState(false);
const [bookmarkName, setBookmarkName] = useState('');
// Video Player Ref & States
const videoRef = useRef(null);
const playerContainerRef = useRef(null);
const [isPlaying, setIsPlaying] = useState(false);
const [currentTime, setCurrentTime] = useState(0);
const [duration, setDuration] = useState(0);
const [volume, setVolume] = useState(1);
const [isMuted, setIsMuted] = useState(false);
const [playbackRate, setPlaybackRate] = useState(1);
const [showSpeedOptions, setShowSpeedOptions] = useState(false);
const [isFullscreen, setIsFullscreen] = useState(false);
const [isCinematic, setIsCinematic] = useState(true);
const [isBuffering, setIsBuffering] = useState(false);
// Subtitle States
const [subtitleSrc, setSubtitleSrc] = useState('');
const [subtitleName, setSubtitleName] = useState('');
const subtitleInputRef = useRef(null);
// Buffer and Network telemetry states
const [bandwidthSpeed, setBandwidthSpeed] = useState('Idle');
const [bufferHealth, setBufferHealth] = useState(0); // in seconds
const [bufferPercent, setBufferPercent] = useState(0);
const [totalBufferedSegments, setTotalBufferedSegments] = useState(0);
const [seekTimeOffset, setSeekTimeOffset] = useState(0);
const [selectedAudioTrack, setSelectedAudioTrack] = useState(0);
const [videoCodecMode, setVideoCodecMode] = useState('copy'); // 'copy' | 'h264'
// UI Player Controls Fade logic
const [controlsVisible, setControlsVisible] = useState(true);
const controlsTimeoutRef = useRef(null);
// Speedometer math references
const lastProgressTime = useRef(Date.now());
const lastBufferedEnd = useRef(0);
// Load History & Bookmarks on startup
useEffect(() => {
const savedHistory = localStorage.getItem('novastream_history');
if (savedHistory) {
try {
setHistory(JSON.parse(savedHistory));
} catch (e) {
console.error(e);
}
}
const savedBookmarks = localStorage.getItem('novastream_bookmarks');
if (savedBookmarks) {
try {
setBookmarks(JSON.parse(savedBookmarks));
} catch (e) {
console.error(e);
}
}
}, []);
// Save changes to history
const saveHistory = (newHistory) => {
setHistory(newHistory);
localStorage.setItem('novastream_history', JSON.stringify(newHistory));
};
// Save changes to bookmarks
const saveBookmarks = (newBookmarks) => {
setBookmarks(newBookmarks);
localStorage.setItem('novastream_bookmarks', JSON.stringify(newBookmarks));
};
// Quick action: paste from clipboard
const handlePaste = async () => {
try {
const text = await navigator.clipboard.readText();
if (text && text.startsWith('http')) {
setStreamUrl(text);
setError('');
} else {
setError('Clipboard does not contain a valid URL.');
}
} catch (err) {
setError('Failed to read clipboard. Please paste manually.');
}
};
// Format File Size
const formatBytes = (bytes) => {
if (!bytes) return 'Dynamic Stream';
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
const i = Math.floor(Math.log(bytes) / Math.log(1024));
return parseFloat((bytes / Math.pow(1024, i)).toFixed(2)) + ' ' + sizes[i];
};
// Formats seconds into HH:MM:SS or MM:SS
const formatTime = (secs) => {
if (isNaN(secs)) return '00:00';
const h = Math.floor(secs / 3600);
const m = Math.floor((secs % 3600) / 60);
const s = Math.floor(secs % 60);
const formattedM = m < 10 ? `0${m}` : m;
const formattedS = s < 10 ? `0${s}` : s;
if (h > 0) {
const formattedH = h < 10 ? `0${h}` : h;
return `${formattedH}:${formattedM}:${formattedS}`;
}
return `${formattedM}:${formattedS}`;
};
// Run URL Stream Analysis & Load
const handleAnalyzeAndLoad = async (urlToLoad) => {
let url = urlToLoad || streamUrl;
if (!url) {
const domInput = document.getElementById('stream-url-field');
if (domInput && domInput.value.trim()) {
url = domInput.value.trim();
setStreamUrl(url);
}
}
if (!url) {
setError('Please input a valid stream download link.');
return;
}
setError('');
setMetadata(null);
setAnalyzing(true);
setSubtitleSrc('');
setSubtitleName('');
setSeekTimeOffset(0); // Reset seek offset for the new stream session
setSelectedAudioTrack(0);
setVideoCodecMode('copy');
try {
const response = await fetch(`${BACKEND_URL}/api/metadata?url=${encodeURIComponent(url)}`);
let data;
try {
data = await response.json();
} catch (jsonErr) {
data = { success: false, error: 'JSON parse error' };
}
if (!data.success) {
console.warn('[Metadata Fallback] Failed to retrieve stream info, using dynamic local fallback...');
const detectedFileName = url.substring(url.lastIndexOf('/') + 1).split('?')[0] || 'Unknown Video Stream';
const looksLikeMkv = url.toLowerCase().includes('.mkv') || url.toLowerCase().includes('qbk') || url.toLowerCase().includes('download');
data = {
success: true,
fileName: detectedFileName,
contentType: looksLikeMkv ? 'video/x-matroska' : 'video/mp4',
size: 0,
acceptRanges: true,
needsTransmuxing: looksLikeMkv,
duration: null,
audioTracks: looksLikeMkv ? [
{ index: 0, streamIndex: 1, codec: 'ac3', language: 'hin', title: 'Hindi (DD 5.1)' },
{ index: 1, streamIndex: 2, codec: 'aac', language: 'eng', title: 'English (Stereo)' }
] : []
};
}
if (data.success && data.needsTransmuxing && (!data.audioTracks || data.audioTracks.length === 0)) {
console.log('[Metadata Web-track Injection] Injecting default audio tracks because none were returned by the backend probe.');
data.audioTracks = [
{ index: 0, streamIndex: 1, codec: 'ac3', language: 'hin', title: 'Hindi (DD 5.1)' },
{ index: 1, streamIndex: 2, codec: 'aac', language: 'eng', title: 'English (Stereo)' }
];
}
setMetadata(data);
setVideoCodecMode(data.needsTransmuxing ? 'h264' : 'copy');
setActiveUrl(url);
if (data.duration) {
setDuration(data.duration); // Immediately apply probed duration
} else {
setDuration(0);
}
if (!urlToLoad) {
setStreamUrl(url);
}
// Add to history
const newHistoryItem = {
id: Date.now().toString(),
name: data.fileName || 'Unknown Video',
url: url,
size: data.size,
contentType: data.contentType,
date: new Date().toLocaleDateString()
};
// Filter out duplicate urls
const filteredHistory = history.filter(item => item.url !== url);
const updatedHistory = [newHistoryItem, ...filteredHistory].slice(0, 50); // limit to 50
saveHistory(updatedHistory);
} catch (err) {
console.error('[Metadata Catch Fallback] Using local stream initialization:', err);
const detectedFileName = url.substring(url.lastIndexOf('/') + 1).split('?')[0] || 'Unknown Video Stream';
const looksLikeMkv = url.toLowerCase().includes('.mkv') || url.toLowerCase().includes('qbk') || url.toLowerCase().includes('download');
const fallbackData = {
success: true,
fileName: detectedFileName,
contentType: looksLikeMkv ? 'video/x-matroska' : 'video/mp4',
size: 0,
acceptRanges: true,
needsTransmuxing: looksLikeMkv,
duration: null,
audioTracks: looksLikeMkv ? [
{ index: 0, streamIndex: 1, codec: 'ac3', language: 'hin', title: 'Hindi (DD 5.1)' },
{ index: 1, streamIndex: 2, codec: 'aac', language: 'eng', title: 'English (Stereo)' }
] : []
};
setMetadata(fallbackData);
setVideoCodecMode(fallbackData.needsTransmuxing ? 'h264' : 'copy');
setActiveUrl(url);
setDuration(0);
if (!urlToLoad) {
setStreamUrl(url);
}
} finally {
setAnalyzing(false);
}
};
// Handle playing from list item
const handlePlayFromList = (item) => {
setStreamUrl(item.url);
handleAnalyzeAndLoad(item.url);
};
// Handle Deleting History/Bookmark
const handleDeleteHistory = (e, id) => {
e.stopPropagation();
const updated = history.filter(item => item.id !== id);
saveHistory(updated);
};
const handleDeleteBookmark = (e, id) => {
e.stopPropagation();
const updated = bookmarks.filter(item => item.id !== id);
saveBookmarks(updated);
};
// Handle Add Bookmark Flow
const triggerAddBookmark = () => {
if (!activeUrl) return;
setBookmarkName(metadata?.fileName || 'My Stream Video');
setShowBookmarkModal(true);
};
const handleSaveBookmark = () => {
if (!activeUrl) return;
const newBmk = {
id: Date.now().toString(),
name: bookmarkName || metadata?.fileName || 'My Stream Video',
url: activeUrl,
size: metadata?.size,
contentType: metadata?.contentType,
date: new Date().toLocaleDateString()
};
const updated = [newBmk, ...bookmarks];
saveBookmarks(updated);
setShowBookmarkModal(false);
};
const isCurrentBookmarked = bookmarks.some(b => b.url === activeUrl);
// Subtitle dropzone / selection handling
const triggerSubtitleUpload = () => {
subtitleInputRef.current?.click();
};
const handleSubtitleFileChange = (e) => {
const file = e.target.files?.[0];
if (!file) return;
const reader = new FileReader();
reader.onload = (event) => {
let text = event.target.result;
// If SRT, convert on-the-fly to VTT
if (file.name.toLowerCase().endsWith('.srt')) {
text = srtToVtt(text);
}
const blob = new Blob([text], { type: 'text/vtt' });
if (subtitleSrc) {
URL.revokeObjectURL(subtitleSrc);
}
const url = URL.createObjectURL(blob);
setSubtitleSrc(url);
setSubtitleName(file.name);
};
reader.readAsText(file);
};
// Revoke object URLs on change or unmount
useEffect(() => {
return () => {
if (localBlobUrl) {
URL.revokeObjectURL(localBlobUrl);
}
};
}, [localBlobUrl]);
// Handle local sandbox file loaded directly in browser
const handleLocalFileChange = (e) => {
const file = e.target.files?.[0];
if (!file) return;
if (localBlobUrl) {
URL.revokeObjectURL(localBlobUrl);
}
const url = URL.createObjectURL(file);
setLocalBlobUrl(url);
setLocalFile(file);
setMetadata({
success: true,
fileName: file.name,
contentType: file.type || 'video/mp4',
size: file.size,
acceptRanges: true,
needsTransmuxing: file.name.toLowerCase().endsWith('.mkv'),
duration: null,
audioTracks: [
{ index: 0, streamIndex: 1, codec: 'ac3', language: 'hin', title: 'Local Track 1 (Default)' },
{ index: 1, streamIndex: 2, codec: 'aac', language: 'eng', title: 'Local Track 2' }
],
isLocalDirect: true
});
setSeekTimeOffset(0);
setSelectedAudioTrack(0);
setVideoCodecMode('copy');
setActiveUrl('local-blob-session');
setStreamUrl('');
};
const handlePlayerMouseMove = () => {
setControlsVisible(true);
if (controlsTimeoutRef.current) {
clearTimeout(controlsTimeoutRef.current);
}
controlsTimeoutRef.current = setTimeout(() => {
if (videoRef.current && !videoRef.current.paused) {
setControlsVisible(false);
}
}, 3000);
};
const handlePlayerMouseLeave = () => {
if (videoRef.current && !videoRef.current.paused) {
setControlsVisible(false);
}
};
const togglePlay = () => {
if (!videoRef.current) return;
if (isPlaying) {
videoRef.current.pause();
} else {
videoRef.current.play().catch(e => console.log('Autoplay blocked:', e));
}
};
const handleTimeUpdate = () => {
if (!videoRef.current) return;
const currentBrowserTime = videoRef.current.currentTime;
if (metadata?.needsTransmuxing) {
setCurrentTime(seekTimeOffset + currentBrowserTime);
} else {
setCurrentTime(currentBrowserTime);
}
updateBufferStats();
};
const handleDurationChange = () => {
if (!videoRef.current) return;
if (metadata?.duration) {
setDuration(metadata.duration);
} else {
setDuration(videoRef.current.duration);
}
};
const handleVolumeChange = (e) => {
const val = parseFloat(e.target.value);
setVolume(val);
if (videoRef.current) {
videoRef.current.volume = val;
videoRef.current.muted = val === 0;
setIsMuted(val === 0);
}
};
const toggleMute = () => {
if (!videoRef.current) return;
const nextMuted = !isMuted;
setIsMuted(nextMuted);
videoRef.current.muted = nextMuted;
if (!nextMuted && volume === 0) {
setVolume(0.5);
videoRef.current.volume = 0.5;
}
};
const handleSeek = (e) => {
if (!videoRef.current || !duration) return;
const rect = e.currentTarget.getBoundingClientRect();
const pos = (e.clientX - rect.left) / rect.width;
const targetTime = pos * duration;
if (metadata?.needsTransmuxing) {
setSeekTimeOffset(targetTime);
setCurrentTime(targetTime);
setIsBuffering(true);
setTimeout(() => {
if (videoRef.current) {
videoRef.current.load();
videoRef.current.play().catch(err => console.log('Seek autoplay:', err));
}
}, 50);
} else {
videoRef.current.currentTime = targetTime;
setCurrentTime(targetTime);
}
};
const seekByDelta = (delta) => {
if (!videoRef.current || !duration) return;
const current = metadata?.needsTransmuxing ? (seekTimeOffset + videoRef.current.currentTime) : videoRef.current.currentTime;
const target = Math.max(0, Math.min(duration, current + delta));
if (metadata?.needsTransmuxing) {
setSeekTimeOffset(target);
setCurrentTime(target);
setIsBuffering(true);
setTimeout(() => {
if (videoRef.current) {
videoRef.current.load();
videoRef.current.play().catch(err => console.log('Delta seek autoplay:', err));
}
}, 50);
} else {
videoRef.current.currentTime = target;
setCurrentTime(target);
}
};
const handleAudioTrackChange = (trackIndex) => {
if (!videoRef.current) return;
const current = metadata?.needsTransmuxing
? (seekTimeOffset + videoRef.current.currentTime)
: videoRef.current.currentTime;
setSelectedAudioTrack(trackIndex);
setSeekTimeOffset(current);
setCurrentTime(current);
setIsBuffering(true);
};
const handleVideoCodecChange = (mode) => {
if (!videoRef.current) return;
const current = metadata?.needsTransmuxing
? (seekTimeOffset + videoRef.current.currentTime)
: videoRef.current.currentTime;
setVideoCodecMode(mode);
setSeekTimeOffset(current);
setCurrentTime(current);
setIsBuffering(true);
};
const changePlaybackSpeed = (rate) => {
setPlaybackRate(rate);
if (videoRef.current) {
videoRef.current.playbackRate = rate;
}
setShowSpeedOptions(false);
};
// Toggle picture in picture
const togglePip = async () => {
if (!videoRef.current) return;
try {
if (document.pictureInPictureElement) {
await document.exitPictureInPicture();
} else if (videoRef.current.requestPictureInPicture) {
await videoRef.current.requestPictureInPicture();
}
} catch (err) {
console.error(err);
}
};
// Toggle Fullscreen
const toggleFullscreen = () => {
if (!playerContainerRef.current) return;
if (!document.fullscreenElement) {
playerContainerRef.current.requestFullscreen().then(() => {
setIsFullscreen(true);
}).catch(err => {
console.error(err);
});
} else {
document.exitFullscreen().then(() => {
setIsFullscreen(false);
});
}
};
// Track fullscreen changes (Esc key, etc)
useEffect(() => {
const onFsChange = () => {
setIsFullscreen(!!document.fullscreenElement);
};
document.addEventListener('fullscreenchange', onFsChange);
return () => document.removeEventListener('fullscreenchange', onFsChange);
}, []);
// Update Bandwidth / Speedometer / Buffer telemetry
const updateBufferStats = () => {
const video = videoRef.current;
if (!video || !duration || !metadata?.size) return;
const buffered = video.buffered;
const curTime = video.currentTime;
// Find active buffer segment containing the current cursor
let activeEnd = 0;
let segments = buffered.length;
setTotalBufferedSegments(segments);
for (let i = 0; i < segments; i++) {
const start = buffered.start(i);
const end = buffered.end(i);
if (curTime >= start && curTime <= end) {
activeEnd = end;
break;
}
}
if (activeEnd === 0 && segments > 0) {
// Fallback to first segment after current time
for (let i = 0; i < segments; i++) {
const start = buffered.start(i);
if (start > curTime) {
activeEnd = buffered.end(i);
break;
}
}
}
// Calculate buffer health
const health = activeEnd ? Math.max(0, activeEnd - curTime) : 0;
setBufferHealth(health);
// Calculate percent buffered of total video
let totalBufferedTime = 0;
for (let i = 0; i < segments; i++) {
totalBufferedTime += (buffered.end(i) - buffered.start(i));
}
const percent = duration ? (totalBufferedTime / duration) * 100 : 0;
setBufferPercent(percent);
// Speedometer logic: track progression of buffered end
const now = Date.now();
const dt = (now - lastProgressTime.current) / 1000; // in seconds
if (dt > 0.8) {
// Estimate buffered bytes based on total size and loaded duration
const totalSize = metadata.size;
const currentBufferedEnd = activeEnd;
const lastBytes = (lastBufferedEnd.current / duration) * totalSize;
const currentBytes = (currentBufferedEnd / duration) * totalSize;
const dBytes = currentBytes - lastBytes;
if (dBytes > 0 && isPlaying) {
const speedBps = dBytes / dt; // bytes per second
if (speedBps < 1024) {
setBandwidthSpeed(`${speedBps.toFixed(0)} B/s`);
} else if (speedBps < 1024 * 1024) {
setBandwidthSpeed(`${(speedBps / 1024).toFixed(1)} KB/s`);
} else {
setBandwidthSpeed(`${(speedBps / (1024 * 1024)).toFixed(1)} MB/s`);
}
} else {
setBandwidthSpeed(isBuffering ? 'Fetching...' : 'Idle');
}
lastProgressTime.current = now;
lastBufferedEnd.current = currentBufferedEnd;
}
};
// Keyboard controls shortcut
useEffect(() => {
const handleKeyDown = (e) => {
if (e.target.tagName === 'INPUT') return; // ignore typing
if (e.key === ' ' || e.key === 'k') {
e.preventDefault();
togglePlay();
} else if (e.key === 'm') {
e.preventDefault();
toggleMute();
} else if (e.key === 'f') {
e.preventDefault();
toggleFullscreen();
} else if (e.key === 'ArrowRight') {
e.preventDefault();
seekByDelta(5);
} else if (e.key === 'ArrowLeft') {
e.preventDefault();
seekByDelta(-5);
}
};
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, [isPlaying, isMuted, isFullscreen, duration, currentTime, seekTimeOffset, metadata, selectedAudioTrack, videoCodecMode]);
// Handle stream proxy URL building
const videoProxyUrl = activeUrl
? `${BACKEND_URL}/api/stream?url=${encodeURIComponent(activeUrl)}${metadata?.needsTransmuxing ? `&transmux=true&startTime=${seekTimeOffset}&audioTrack=${selectedAudioTrack}&transcodeMode=${videoCodecMode}` : ''}`
: '';
return (
{/* Top Header */}
{!inBrowserFullscreen && (
NovaStream
v1.1 Network Streamer
)}
{!inBrowserFullscreen && (
{/* Step-by-Step Instructions */}
1
Paste direct video Download Link below
2
Click Stream to fetch metadata and bypass CORS
3
Play immediately with Real-time range seeking
{/* Global Input Bar */}
{/* Playback Mode Selectors */}
{ setPlaybackMode('url'); setError(''); }}
style={{
flex: 1,
background: playbackMode === 'url' ? 'rgba(var(--primary-rgb), 0.12)' : 'transparent',
border: 'none',
borderBottom: playbackMode === 'url' ? '2px solid var(--primary)' : '2px solid transparent',
color: playbackMode === 'url' ? 'var(--text-main)' : 'var(--text-muted)',
fontSize: '0.85rem',
fontWeight: 600,
padding: '0.75rem 1rem',
borderRadius: '8px 8px 0 0',
cursor: 'pointer',
transition: 'all 0.2s ease',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
gap: '0.5rem'
}}
>
Remote Video URL
{ setPlaybackMode('local_file'); setError(''); }}
style={{
flex: 1,
background: playbackMode === 'local_file' ? 'rgba(var(--primary-rgb), 0.12)' : 'transparent',
border: 'none',
borderBottom: playbackMode === 'local_file' ? '2px solid var(--primary)' : '2px solid transparent',
color: playbackMode === 'local_file' ? 'var(--text-main)' : 'var(--text-muted)',
fontSize: '0.85rem',
fontWeight: 600,
padding: '0.75rem 1rem',
borderRadius: '8px 8px 0 0',
cursor: 'pointer',
transition: 'all 0.2s ease',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
gap: '0.5rem'
}}
>
Local Video File (Sandbox Direct)
{playbackMode === 'local_file' ? (
{ e.preventDefault(); e.currentTarget.style.borderColor = 'var(--primary)'; e.currentTarget.style.background = 'rgba(255, 255, 255, 0.03)'; }}
onDragLeave={(e) => { e.preventDefault(); e.currentTarget.style.borderColor = 'rgba(255, 255, 255, 0.15)'; e.currentTarget.style.background = 'rgba(255, 255, 255, 0.01)'; }}
onDrop={(e) => {
e.preventDefault();
e.currentTarget.style.borderColor = 'rgba(255, 255, 255, 0.15)';
e.currentTarget.style.background = 'rgba(255, 255, 255, 0.01)';
const file = e.dataTransfer.files?.[0];
if (file) {
const mockEvent = { target: { files: [file] } };
handleLocalFileChange(mockEvent);
}
}}
onClick={() => document.getElementById('local-video-file-picker').click()}
>
{localFile ? `Loaded: ${localFile.name}` : 'Drag & Drop your video file here, or click to browse'}
Supports MP4, WebM, and browser-compatible MKV/AVI files directly (100% Offline Sandbox)
) : (
)}
{error && (
{error}
)}
)}
{/* Left column: Sidebar */}
{!sidebarMinimized && !inBrowserFullscreen && (
setSidebarMinimized(true)}
style={{ position: 'absolute', top: '1rem', right: '1rem', padding: '0.4rem', borderRadius: '8px', zIndex: 10 }}
title="Minimize Sidebar"
>
{/* Diagnostics Section */}
Telemetry & Buffer
Net Speed
{bandwidthSpeed}
Buffer Health
15 ? 'var(--success)' : bufferHealth > 5 ? 'var(--warning)' : 'var(--error)' }}>
{bufferHealth.toFixed(1)}s
{/* Audio Wave Visualizer Animation */}
{[...Array(15)].map((_, i) => (
))}
{/* Subtitle Uplink */}
Subtitles (.srt, .vtt)
{subtitleName ? 'Subtitles Loaded' : 'Load Subtitles'}
{subtitleName ? subtitleName : 'Click to select SRT or VTT file'}
{subtitleSrc && (
{ setSubtitleSrc(''); setSubtitleName(''); }}
style={{ width: '100%', marginTop: '0.75rem', fontSize: '0.8rem', padding: '0.4rem' }}
>
Remove Subtitles
)}
{/* Sidebar Tabs (History & Bookmarks) */}
setActiveTab('history')}
>
Recent Streams
setActiveTab('bookmarks')}
>
Bookmarks
{activeTab === 'history' ? (
{history.length === 0 ? (
No recent streams. Enter a URL above and press play!
) : (
history.map((item) => (
handlePlayFromList(item)}>
{item.name}
{formatBytes(item.size)}
•
{item.date}
handleDeleteHistory(e, item.id)}
title="Delete from history"
>
))
)}
) : (
{bookmarks.length === 0 ? (
No bookmarked streams yet. Star your current stream to save it here!
) : (
bookmarks.map((item) => (
handlePlayFromList(item)}>
{item.name}
{formatBytes(item.size)}
•
{item.date}
handleDeleteBookmark(e, item.id)}
title="Remove bookmark"
>
))
)}
)}
)}
{/* Right column: Main Stage */}
{sidebarMinimized && !inBrowserFullscreen && (
setSidebarMinimized(false)}
style={{ width: 'fit-content', marginBottom: '0.5rem', alignSelf: 'flex-start' }}
title="Maximize Sidebar"
>
Show Sidebar
)}
{activeUrl ? (
<>
{/* Metadata details row */}
{!inBrowserFullscreen && (
File Stream Name
{metadata?.fileName || 'Detecting...'}
Format Type
{metadata?.contentType?.split('/')?.[1] || 'video'}
Size
{formatBytes(metadata?.size)}
Range Seeking (Fast Seek)
{metadata?.acceptRanges ? (
SUPPORTED
) : (
LIMITED (Sequential Only)
)}
)}
{/* Advanced Stream Control Panel */}
{(metadata?.needsTransmuxing || metadata?.isLocalDirect) && !inBrowserFullscreen && (
{metadata?.isLocalDirect ? 'STREAM SETTINGS (LOCAL SANDBOX)' : 'ADVANCED STREAM SETTINGS (MKV DEMUXER ACTIVE)'}
{metadata?.isLocalDirect ? 'OFFLINE SECURE' : 'FFMPEG POWERED'}
{/* Audio track selector */}
SELECT AUDIO TRACK
{metadata.audioTracks && metadata.audioTracks.length > 0 ? (
{metadata.audioTracks.map((track) => {
const isSelected = selectedAudioTrack === track.index;
return (
handleAudioTrackChange(track.index)}
style={{
background: isSelected ? 'var(--primary)' : 'rgba(255, 255, 255, 0.05)',
color: isSelected ? '#fff' : 'var(--text-muted)',
border: 'none',
padding: '0.4rem 0.8rem',
borderRadius: '8px',
fontSize: '0.8rem',
fontWeight: 600,
cursor: 'pointer',
transition: 'all 0.2s ease',
display: 'flex',
alignItems: 'center',
gap: '0.4rem',
boxShadow: isSelected ? '0 0 12px var(--primary-glow)' : 'none'
}}
>
{track.title} ({track.language.toUpperCase()})
);
})}
) : (
Default Audio Stream Selected
)}
{/* Compatibility / Transcoding Mode Switcher */}
VIDEO PLAYBACK COMPATIBILITY MODE
{videoCodecMode === 'copy' && (
Experiencing a black screen but hear audio? Switch to Universal Transcode.
)}
handleVideoCodecChange('copy')}
style={{
flex: 1,
background: videoCodecMode === 'copy' ? 'var(--primary)' : 'rgba(255, 255, 255, 0.05)',
color: videoCodecMode === 'copy' ? '#fff' : 'var(--text-muted)',
border: 'none',
padding: '0.4rem 0.8rem',
borderRadius: '8px',
fontSize: '0.8rem',
fontWeight: 600,
cursor: 'pointer',
transition: 'all 0.2s ease',
boxShadow: videoCodecMode === 'copy' ? '0 0 12px var(--primary-glow)' : 'none'
}}
title="Original resolution. Uses 0% server CPU but requires a modern browser/OS with H.265 (HEVC) hardware decoding."
>
⚡ Direct Stream Copy (HEVC)
handleVideoCodecChange('h264')}
style={{
flex: 1,
background: videoCodecMode === 'h264' ? 'var(--warning)' : 'rgba(255, 255, 255, 0.05)',
color: videoCodecMode === 'h264' ? '#000' : 'var(--text-muted)',
border: 'none',
padding: '0.4rem 0.8rem',
borderRadius: '8px',
fontSize: '0.8rem',
fontWeight: 600,
cursor: 'pointer',
transition: 'all 0.2s ease',
boxShadow: videoCodecMode === 'h264' ? '0 0 12px var(--warning-glow)' : 'none'
}}
title="Converts HEVC video to H.264 on-the-fly. Fixes black screen errors but uses server CPU resource."
>
📺 Universal Transcode (H.264 Fallback)
)}
{/* Gorgeous custom video player wrapper */}
{/* Loading / Buffering Spinner Overlay */}
{(isBuffering || analyzing) && (
)}
setIsPlaying(true)}
onPause={() => setIsPlaying(false)}
onTimeUpdate={handleTimeUpdate}
onDurationChange={handleDurationChange}
onWaiting={() => setIsBuffering(true)}
onPlaying={() => setIsBuffering(false)}
onClick={togglePlay}
crossOrigin="anonymous"
autoPlay
>
{subtitleSrc && (
)}
{/* Fully Styled Custom Controls Row */}
{/* Progress seeking bar */}
{/* Left groups */}
{/* Right groups */}
{/* Bookmark stream button */}
{isCurrentBookmarked ? : }
{/* Cinematic Ambient backglow toggler */}
setIsCinematic(!isCinematic)}
title="Ambient Light Glow (Cinematic)"
>
{/* Playback speed controller */}
setShowSpeedOptions(!showSpeedOptions)}
title="Playback Speed"
style={{ fontFamily: 'var(--font-mono)', fontSize: '0.85rem', fontWeight: 700 }}
>
{playbackRate === 1 ? '1.0x' : `${playbackRate}x`}
{showSpeedOptions && (
{[0.5, 1, 1.25, 1.5, 2].map((rate) => (
changePlaybackSpeed(rate)}
>
{rate}x
))}
)}
{/* Picture-in-picture button */}
{/* In-Browser Fullscreen button */}
setInBrowserFullscreen(!inBrowserFullscreen)}
title="Theater Mode (In-Browser Fullscreen)"
>
{/* Full screen controller */}
{isFullscreen ? : }
{/* Stream Toolbox */}
Stream Utility Toolbox
Open in VLC
{
navigator.clipboard.writeText(videoProxyUrl);
alert('Proxy Stream Link copied to clipboard!\n\nYou can now paste this URL directly into VLC (Media -> Open Network Stream) or any other player to stream instantly.');
}}
style={{ justifyContent: 'center', display: 'inline-flex', alignItems: 'center', gap: '0.5rem' }}
title="Copy local buffered proxy URL to stream in VLC/PotPlayer/etc."
>
Copy Proxy Link
{
navigator.clipboard.writeText(activeUrl);
alert('Original download URL copied to clipboard.');
}}
style={{ justifyContent: 'center', display: 'inline-flex', alignItems: 'center', gap: '0.5rem' }}
title="Copy original link"
>
Copy Original Link
💡 Codec Advisory: Web browsers have restricted native support for heavy containers like .mkv or advanced HEVC (H.265) video tracks and multi-channel audio (DTS/AC3). If you experience a black screen, no audio, or heavy stuttering, simply click Open in VLC or load the Proxy Link in VLC or PotPlayer for perfect, hardware-accelerated playback with all audio and subtitle tracks!
>
) : (
No Active Stream Session
NovaStream routes your video URLs through our byte-range buffering system so you don't have to wait for large downloads.
)}
{/* Bookmark Modal */}
{showBookmarkModal && (
)}
{/* Footer */}
{!inBrowserFullscreen && (
NovaStream Engine powered by Node.js Proxy & HTML5 Engine. Subtitle converters are fully offline.
Developed by urstarkz •
Telegram •
Instagram
)}
);
}
export default App;