mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-04-20 16:22:04 +00:00
Update youtubeExtractor.ts
This commit is contained in:
parent
5fa02d7c53
commit
a2a801d68a
1 changed files with 74 additions and 66 deletions
|
|
@ -416,35 +416,69 @@ async function collectAllFormats(
|
||||||
const adaptiveAudio: StreamCandidate[] = [];
|
const adaptiveAudio: StreamCandidate[] = [];
|
||||||
const hlsManifests: Array<{ clientKey: string; priority: number; url: string }> = [];
|
const hlsManifests: Array<{ clientKey: string; priority: number; url: string }> = [];
|
||||||
|
|
||||||
for (const client of CLIENTS) {
|
// Fire all client requests in parallel — same approach as Kotlin coroutines
|
||||||
try {
|
const results = await Promise.allSettled(
|
||||||
const resp = await fetchPlayerResponse(videoId, client, apiKey, visitorData);
|
CLIENTS.map(client => fetchPlayerResponse(videoId, client, apiKey, visitorData)
|
||||||
if (!resp) continue;
|
.then(resp => ({ client, resp }))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
const status = resp.playabilityStatus?.status;
|
for (const result of results) {
|
||||||
if (status && status !== 'OK' && status !== 'CONTENT_CHECK_REQUIRED') {
|
if (result.status === 'rejected') {
|
||||||
logger.warn('YouTubeExtractor', `[${client.key}] status=${status} reason=${resp.playabilityStatus?.reason ?? ''}`);
|
logger.warn('YouTubeExtractor', `Client request rejected:`, result.reason);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const sd = resp.streamingData;
|
const { client, resp } = result.value;
|
||||||
if (!sd) continue;
|
if (!resp) continue;
|
||||||
|
|
||||||
if (sd.hlsManifestUrl) {
|
const status = resp.playabilityStatus?.status;
|
||||||
hlsManifests.push({ clientKey: client.key, priority: client.priority, url: sd.hlsManifestUrl });
|
if (status && status !== 'OK' && status !== 'CONTENT_CHECK_REQUIRED') {
|
||||||
}
|
logger.warn('YouTubeExtractor', `[${client.key}] status=${status} reason=${resp.playabilityStatus?.reason ?? ''}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
let nProg = 0, nVid = 0, nAud = 0;
|
const sd = resp.streamingData;
|
||||||
|
if (!sd) continue;
|
||||||
|
|
||||||
// Progressive (muxed) formats — matching Kotlin: skip non-video mimeTypes
|
if (sd.hlsManifestUrl) {
|
||||||
for (const f of (sd.formats ?? [])) {
|
hlsManifests.push({ clientKey: client.key, priority: client.priority, url: sd.hlsManifestUrl });
|
||||||
if (!f.url) continue;
|
}
|
||||||
const mimeBase = getMimeBase(f.mimeType);
|
|
||||||
if (f.mimeType && !mimeBase.startsWith('video/')) continue;
|
let nProg = 0, nVid = 0, nAud = 0;
|
||||||
|
|
||||||
|
// Progressive (muxed) formats — matching Kotlin: skip non-video mimeTypes
|
||||||
|
for (const f of (sd.formats ?? [])) {
|
||||||
|
if (!f.url) continue;
|
||||||
|
const mimeBase = getMimeBase(f.mimeType);
|
||||||
|
if (f.mimeType && !mimeBase.startsWith('video/')) continue;
|
||||||
|
const height = f.height ?? parseQualityLabel(f.qualityLabel);
|
||||||
|
const fps = f.fps ?? 0;
|
||||||
|
const bitrate = f.bitrate ?? f.averageBitrate ?? 0;
|
||||||
|
progressive.push({
|
||||||
|
client: client.key,
|
||||||
|
priority: client.priority,
|
||||||
|
url: f.url,
|
||||||
|
score: videoScore(height, fps, bitrate),
|
||||||
|
height,
|
||||||
|
fps,
|
||||||
|
ext: getExt(f.mimeType),
|
||||||
|
bitrate,
|
||||||
|
mimeType: f.mimeType ?? '',
|
||||||
|
});
|
||||||
|
nProg++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adaptive formats
|
||||||
|
for (const f of (sd.adaptiveFormats ?? [])) {
|
||||||
|
if (!f.url) continue;
|
||||||
|
const mimeBase = getMimeBase(f.mimeType);
|
||||||
|
|
||||||
|
if (mimeBase.startsWith('video/')) {
|
||||||
const height = f.height ?? parseQualityLabel(f.qualityLabel);
|
const height = f.height ?? parseQualityLabel(f.qualityLabel);
|
||||||
const fps = f.fps ?? 0;
|
const fps = f.fps ?? 0;
|
||||||
const bitrate = f.bitrate ?? f.averageBitrate ?? 0;
|
const bitrate = f.bitrate ?? f.averageBitrate ?? 0;
|
||||||
progressive.push({
|
adaptiveVideo.push({
|
||||||
client: client.key,
|
client: client.key,
|
||||||
priority: client.priority,
|
priority: client.priority,
|
||||||
url: f.url,
|
url: f.url,
|
||||||
|
|
@ -455,53 +489,27 @@ async function collectAllFormats(
|
||||||
bitrate,
|
bitrate,
|
||||||
mimeType: f.mimeType ?? '',
|
mimeType: f.mimeType ?? '',
|
||||||
});
|
});
|
||||||
nProg++;
|
nVid++;
|
||||||
|
} else if (mimeBase.startsWith('audio/')) {
|
||||||
|
const bitrate = f.bitrate ?? f.averageBitrate ?? 0;
|
||||||
|
const sampleRate = parseFloat(f.audioSampleRate ?? '0') || 0;
|
||||||
|
adaptiveAudio.push({
|
||||||
|
client: client.key,
|
||||||
|
priority: client.priority,
|
||||||
|
url: f.url,
|
||||||
|
score: audioScore(bitrate, sampleRate),
|
||||||
|
height: 0,
|
||||||
|
fps: 0,
|
||||||
|
ext: getExt(f.mimeType),
|
||||||
|
bitrate,
|
||||||
|
audioSampleRate: f.audioSampleRate,
|
||||||
|
mimeType: f.mimeType ?? '',
|
||||||
|
});
|
||||||
|
nAud++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adaptive formats
|
|
||||||
for (const f of (sd.adaptiveFormats ?? [])) {
|
|
||||||
if (!f.url) continue;
|
|
||||||
const mimeBase = getMimeBase(f.mimeType);
|
|
||||||
|
|
||||||
if (mimeBase.startsWith('video/')) {
|
|
||||||
const height = f.height ?? parseQualityLabel(f.qualityLabel);
|
|
||||||
const fps = f.fps ?? 0;
|
|
||||||
const bitrate = f.bitrate ?? f.averageBitrate ?? 0;
|
|
||||||
adaptiveVideo.push({
|
|
||||||
client: client.key,
|
|
||||||
priority: client.priority,
|
|
||||||
url: f.url,
|
|
||||||
score: videoScore(height, fps, bitrate),
|
|
||||||
height,
|
|
||||||
fps,
|
|
||||||
ext: getExt(f.mimeType),
|
|
||||||
bitrate,
|
|
||||||
mimeType: f.mimeType ?? '',
|
|
||||||
});
|
|
||||||
nVid++;
|
|
||||||
} else if (mimeBase.startsWith('audio/')) {
|
|
||||||
const bitrate = f.bitrate ?? f.averageBitrate ?? 0;
|
|
||||||
const sampleRate = parseFloat(f.audioSampleRate ?? '0') || 0;
|
|
||||||
adaptiveAudio.push({
|
|
||||||
client: client.key,
|
|
||||||
priority: client.priority,
|
|
||||||
url: f.url,
|
|
||||||
score: audioScore(bitrate, sampleRate),
|
|
||||||
height: 0,
|
|
||||||
fps: 0,
|
|
||||||
ext: getExt(f.mimeType),
|
|
||||||
bitrate,
|
|
||||||
audioSampleRate: f.audioSampleRate,
|
|
||||||
mimeType: f.mimeType ?? '',
|
|
||||||
});
|
|
||||||
nAud++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info('YouTubeExtractor', `[${client.key}] progressive=${nProg} video=${nVid} audio=${nAud} hls=${sd.hlsManifestUrl ? 1 : 0}`);
|
|
||||||
} catch (err) {
|
|
||||||
logger.warn('YouTubeExtractor', `[${client.key}] Failed:`, err);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.info('YouTubeExtractor', `[${client.key}] progressive=${nProg} video=${nVid} audio=${nAud} hls=${sd.hlsManifestUrl ? 1 : 0}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return { progressive, adaptiveVideo, adaptiveAudio, hlsManifests };
|
return { progressive, adaptiveVideo, adaptiveAudio, hlsManifests };
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue