mirror of
https://github.com/KuekHaoYang/KVideo.git
synced 2026-08-22 12:13:43 +08:00
feat: Implement HLS download and enhance copy link functionality to support original and proxied URLs.
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
import { Segment } from '@/lib/utils/hlsManifestParser';
|
||||
import { downloadSegmentQueue } from '@/lib/utils/segmentDownloader';
|
||||
|
||||
interface PreloadParams {
|
||||
currentTime: number;
|
||||
segments: Segment[];
|
||||
videoRef: React.RefObject<HTMLVideoElement | null>;
|
||||
lastStartIndexRef: React.MutableRefObject<number>;
|
||||
isInitializedRef: React.MutableRefObject<boolean>;
|
||||
abortControllerRef: React.MutableRefObject<AbortController | null>;
|
||||
videoUrl: string;
|
||||
}
|
||||
|
||||
export function preloadSegments({
|
||||
currentTime,
|
||||
segments,
|
||||
videoRef,
|
||||
lastStartIndexRef,
|
||||
isInitializedRef,
|
||||
abortControllerRef,
|
||||
videoUrl
|
||||
}: PreloadParams) {
|
||||
// Find segment index for currentTime
|
||||
let startIndex = 0;
|
||||
for (let i = 0; i < segments.length; i++) {
|
||||
if (currentTime < segments[i].startTime + segments[i].duration) {
|
||||
startIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Check browser buffer health
|
||||
if (videoRef.current) {
|
||||
const buffered = videoRef.current.buffered;
|
||||
let bufferEnd = 0;
|
||||
for (let i = 0; i < buffered.length; i++) {
|
||||
if (buffered.start(i) <= currentTime && buffered.end(i) >= currentTime) {
|
||||
bufferEnd = buffered.end(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If browser buffer is less than 30s ahead, let browser handle it
|
||||
if (bufferEnd - currentTime < 30) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Offset start index by 3 segments to avoid competing with browser playback
|
||||
startIndex = Math.min(startIndex + 3, segments.length - 1);
|
||||
|
||||
if (startIndex >= segments.length) return;
|
||||
|
||||
// Check if this is sequential playback or a seek
|
||||
const diff = startIndex - lastStartIndexRef.current;
|
||||
const isSequential = diff >= 0 && diff < 3;
|
||||
|
||||
// Skip if already downloading sequentially
|
||||
if (isSequential && isInitializedRef.current && abortControllerRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only log on significant seeks or initial start
|
||||
if (!isInitializedRef.current) {
|
||||
console.log(`[Preloader] Initial start at segment ${startIndex} (${currentTime.toFixed(2)}s)`);
|
||||
isInitializedRef.current = true;
|
||||
} else if (!isSequential) {
|
||||
console.log(`[Preloader] Seek detected. Current Time: ${currentTime.toFixed(2)}s. Starting from segment ${startIndex}.`);
|
||||
}
|
||||
|
||||
lastStartIndexRef.current = startIndex;
|
||||
|
||||
// Abort previous queue and start new one
|
||||
if (abortControllerRef.current) {
|
||||
abortControllerRef.current.abort();
|
||||
}
|
||||
abortControllerRef.current = new AbortController();
|
||||
|
||||
downloadSegmentQueue({
|
||||
segments: segments,
|
||||
startIndex,
|
||||
signal: abortControllerRef.current.signal,
|
||||
videoUrl: videoUrl
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { NextRequest } from 'next/server';
|
||||
|
||||
export async function processM3u8Content(
|
||||
content: string,
|
||||
baseUrl: string,
|
||||
origin: string
|
||||
): Promise<string> {
|
||||
const lines = content.split('\n');
|
||||
const base = new URL(baseUrl);
|
||||
|
||||
const processedLines = lines.map(line => {
|
||||
// Skip comments and empty lines
|
||||
if (line.trim().startsWith('#') || !line.trim()) {
|
||||
return line;
|
||||
}
|
||||
|
||||
// Resolve relative URLs
|
||||
try {
|
||||
const absoluteUrl = new URL(line.trim(), base).toString();
|
||||
// Wrap in proxy
|
||||
return `${origin}/api/proxy?url=${encodeURIComponent(absoluteUrl)}`;
|
||||
} catch (e) {
|
||||
return line;
|
||||
}
|
||||
});
|
||||
|
||||
return processedLines.join('\n');
|
||||
}
|
||||
Reference in New Issue
Block a user