recker 1.0.19 → 1.0.20-next.de24f7d
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/client.d.ts +2 -0
- package/dist/core/client.d.ts.map +1 -1
- package/dist/core/client.js +6 -7
- package/dist/plugins/hls.d.ts +90 -17
- package/dist/plugins/hls.d.ts.map +1 -1
- package/dist/plugins/hls.js +343 -173
- package/dist/transport/undici.d.ts +1 -1
- package/dist/transport/undici.d.ts.map +1 -1
- package/dist/transport/undici.js +10 -4
- package/package.json +1 -1
package/dist/core/client.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { DedupOptions } from '../plugins/dedup.js';
|
|
|
7
7
|
import { ReckerWebSocket, type WebSocketOptions } from '../websocket/client.js';
|
|
8
8
|
import { type WhoisOptions, type WhoisResult } from '../utils/whois.js';
|
|
9
9
|
import { type ScrapePromise } from '../plugins/scrape.js';
|
|
10
|
+
import { HlsPromise, type HlsOptions } from '../plugins/hls.js';
|
|
10
11
|
interface ClientCacheConfig extends Omit<CacheOptions, 'storage'> {
|
|
11
12
|
storage?: CacheStorage;
|
|
12
13
|
driver?: 'memory' | 'file';
|
|
@@ -111,6 +112,7 @@ export declare class Client {
|
|
|
111
112
|
ws(path: string, options?: WebSocketOptions): ReckerWebSocket;
|
|
112
113
|
whois(query: string, options?: WhoisOptions): Promise<WhoisResult>;
|
|
113
114
|
isDomainAvailable(domain: string, options?: WhoisOptions): Promise<boolean>;
|
|
115
|
+
hls(manifestUrl: string, options?: HlsOptions): HlsPromise;
|
|
114
116
|
}
|
|
115
117
|
export declare function createClient(options?: ExtendedClientOptions): Client;
|
|
116
118
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/core/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAgB,aAAa,EAAE,cAAc,EAAE,cAAc,EAAa,YAAY,EAAyC,UAAU,EAAmD,MAAM,mBAAmB,CAAC;AAIxP,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAStD,OAAO,EAAY,iBAAiB,EAAe,MAAM,0BAA0B,CAAC;AACpF,OAAO,EAAS,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAS,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAS,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAO1D,OAAO,EAAE,eAAe,EAAE,KAAK,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAChF,OAAO,EAA4C,KAAK,YAAY,EAAE,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAElH,OAAO,EAA0B,KAAK,aAAa,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/core/client.ts"],"names":[],"mappings":"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"}
|
package/dist/core/client.js
CHANGED
|
@@ -22,6 +22,7 @@ import { ReckerWebSocket } from '../websocket/client.js';
|
|
|
22
22
|
import { whois as performWhois, isDomainAvailable } from '../utils/whois.js';
|
|
23
23
|
import { MemoryCookieJar } from '../cookies/memory-cookie-jar.js';
|
|
24
24
|
import { scrape as scrapeHelper } from '../plugins/scrape.js';
|
|
25
|
+
import { HlsPromise } from '../plugins/hls.js';
|
|
25
26
|
export class Client {
|
|
26
27
|
baseUrl;
|
|
27
28
|
middlewares;
|
|
@@ -72,7 +73,7 @@ export class Client {
|
|
|
72
73
|
if (options.transport) {
|
|
73
74
|
this.transport = options.transport;
|
|
74
75
|
}
|
|
75
|
-
else
|
|
76
|
+
else {
|
|
76
77
|
let http2Options;
|
|
77
78
|
if (options.http2) {
|
|
78
79
|
if (typeof options.http2 === 'boolean') {
|
|
@@ -83,7 +84,7 @@ export class Client {
|
|
|
83
84
|
}
|
|
84
85
|
}
|
|
85
86
|
this.agentManager = new AgentManager(this.concurrencyConfig.agent);
|
|
86
|
-
this.transport = new UndiciTransport(this.baseUrl, {
|
|
87
|
+
this.transport = new UndiciTransport(this.baseUrl || undefined, {
|
|
87
88
|
proxy: options.proxy,
|
|
88
89
|
http2: http2Options,
|
|
89
90
|
dns: options.dns,
|
|
@@ -93,11 +94,6 @@ export class Client {
|
|
|
93
94
|
observability: options.observability
|
|
94
95
|
});
|
|
95
96
|
}
|
|
96
|
-
else {
|
|
97
|
-
throw new ConfigurationError('baseUrl is required for default UndiciTransport, or provide a custom transport.', {
|
|
98
|
-
configKey: 'baseUrl',
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
97
|
if (options.retry) {
|
|
102
98
|
retry(options.retry)(this);
|
|
103
99
|
}
|
|
@@ -662,6 +658,9 @@ export class Client {
|
|
|
662
658
|
async isDomainAvailable(domain, options) {
|
|
663
659
|
return isDomainAvailable(domain, options);
|
|
664
660
|
}
|
|
661
|
+
hls(manifestUrl, options = {}) {
|
|
662
|
+
return new HlsPromise(this, manifestUrl, options);
|
|
663
|
+
}
|
|
665
664
|
}
|
|
666
665
|
export function createClient(options = {}) {
|
|
667
666
|
return new Client(options);
|
package/dist/plugins/hls.d.ts
CHANGED
|
@@ -1,33 +1,106 @@
|
|
|
1
|
-
import { Client } from '../core/client.js';
|
|
2
|
-
|
|
1
|
+
import type { Client } from '../core/client.js';
|
|
2
|
+
import { type WriteStream } from 'node:fs';
|
|
3
|
+
import { Writable } from 'node:stream';
|
|
4
|
+
export interface HlsVariant {
|
|
3
5
|
url: string;
|
|
4
6
|
bandwidth?: number;
|
|
5
7
|
resolution?: string;
|
|
8
|
+
codecs?: string;
|
|
9
|
+
name?: string;
|
|
6
10
|
}
|
|
7
|
-
export interface
|
|
11
|
+
export interface HlsSegment {
|
|
8
12
|
url: string;
|
|
9
13
|
duration: number;
|
|
10
14
|
sequence: number;
|
|
11
|
-
key?:
|
|
15
|
+
key?: HlsKeyInfo;
|
|
16
|
+
discontinuity?: boolean;
|
|
17
|
+
programDateTime?: Date;
|
|
12
18
|
}
|
|
13
|
-
export interface
|
|
14
|
-
method:
|
|
19
|
+
export interface HlsKeyInfo {
|
|
20
|
+
method: 'NONE' | 'AES-128' | 'SAMPLE-AES';
|
|
15
21
|
uri?: string;
|
|
16
22
|
iv?: string;
|
|
17
23
|
}
|
|
18
|
-
export interface
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
24
|
+
export interface HlsPlaylist {
|
|
25
|
+
segments: HlsSegment[];
|
|
26
|
+
targetDuration: number;
|
|
27
|
+
mediaSequence: number;
|
|
28
|
+
endList: boolean;
|
|
29
|
+
playlistType?: 'VOD' | 'EVENT';
|
|
30
|
+
discontinuitySequence: number;
|
|
23
31
|
}
|
|
24
|
-
export interface
|
|
32
|
+
export interface HlsMasterPlaylist {
|
|
33
|
+
variants: HlsVariant[];
|
|
34
|
+
isMaster: true;
|
|
35
|
+
}
|
|
36
|
+
export interface SegmentData {
|
|
37
|
+
sequence: number;
|
|
38
|
+
duration: number;
|
|
39
|
+
data: Uint8Array;
|
|
40
|
+
url: string;
|
|
41
|
+
downloadedAt: Date;
|
|
42
|
+
}
|
|
43
|
+
export interface HlsProgress {
|
|
44
|
+
downloadedSegments: number;
|
|
45
|
+
totalSegments?: number;
|
|
46
|
+
downloadedBytes: number;
|
|
47
|
+
currentSegment: number;
|
|
48
|
+
isLive: boolean;
|
|
49
|
+
elapsed: number;
|
|
50
|
+
}
|
|
51
|
+
export interface HlsOptions {
|
|
52
|
+
mode?: 'merge' | 'chunks';
|
|
53
|
+
format?: 'ts' | 'mp4' | 'mkv';
|
|
54
|
+
live?: boolean | {
|
|
55
|
+
duration: number;
|
|
56
|
+
};
|
|
57
|
+
quality?: 'highest' | 'lowest' | {
|
|
58
|
+
bandwidth?: number;
|
|
59
|
+
resolution?: string;
|
|
60
|
+
};
|
|
25
61
|
concurrency?: number;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
duration?: number;
|
|
29
|
-
onInfo?: (message: string) => void;
|
|
62
|
+
onSegment?: (segment: SegmentData) => void | Promise<void>;
|
|
63
|
+
onProgress?: (progress: HlsProgress) => void;
|
|
30
64
|
onError?: (error: Error) => void;
|
|
65
|
+
headers?: Record<string, string>;
|
|
66
|
+
}
|
|
67
|
+
type DownloadDest = string | ((segment: HlsSegment) => string);
|
|
68
|
+
export declare class HlsPromise implements Promise<void> {
|
|
69
|
+
private client;
|
|
70
|
+
private manifestUrl;
|
|
71
|
+
private options;
|
|
72
|
+
private seenSequences;
|
|
73
|
+
private downloadedBytes;
|
|
74
|
+
private downloadedSegments;
|
|
75
|
+
private startTime;
|
|
76
|
+
private aborted;
|
|
77
|
+
private abortController;
|
|
78
|
+
constructor(client: Client, manifestUrl: string, options?: HlsOptions);
|
|
79
|
+
get [Symbol.toStringTag](): string;
|
|
80
|
+
then<TResult1 = void, TResult2 = never>(onfulfilled?: ((value: void) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
|
|
81
|
+
catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null): Promise<void | TResult>;
|
|
82
|
+
finally(onfinally?: (() => void) | null): Promise<void>;
|
|
83
|
+
cancel(): void;
|
|
84
|
+
download(dest: DownloadDest): Promise<void>;
|
|
85
|
+
stream(): AsyncGenerator<SegmentData>;
|
|
86
|
+
pipe(writable: Writable | WriteStream): Promise<void>;
|
|
87
|
+
info(): Promise<{
|
|
88
|
+
master?: HlsMasterPlaylist;
|
|
89
|
+
playlist?: HlsPlaylist;
|
|
90
|
+
selectedVariant?: HlsVariant;
|
|
91
|
+
isLive: boolean;
|
|
92
|
+
totalDuration?: number;
|
|
93
|
+
}>;
|
|
94
|
+
private resolveMediaPlaylist;
|
|
95
|
+
private fetchMediaPlaylist;
|
|
96
|
+
private downloadSegment;
|
|
97
|
+
private downloadMerged;
|
|
98
|
+
private downloadChunks;
|
|
99
|
+
private isLiveMode;
|
|
100
|
+
private getMaxDuration;
|
|
101
|
+
private emitProgress;
|
|
102
|
+
private sleep;
|
|
31
103
|
}
|
|
32
|
-
export declare function
|
|
104
|
+
export declare function hls(client: Client, manifestUrl: string, options?: HlsOptions): HlsPromise;
|
|
105
|
+
export type { HlsVariant as Variant, HlsSegment as Segment, HlsKeyInfo as KeyInfo, };
|
|
33
106
|
//# sourceMappingURL=hls.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hls.d.ts","sourceRoot":"","sources":["../../src/plugins/hls.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"hls.d.ts","sourceRoot":"","sources":["../../src/plugins/hls.ts"],"names":[],"mappings":"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"}
|
package/dist/plugins/hls.js
CHANGED
|
@@ -1,225 +1,395 @@
|
|
|
1
1
|
import { createWriteStream } from 'node:fs';
|
|
2
|
+
import { mkdir } from 'node:fs/promises';
|
|
2
3
|
import { dirname, join } from 'node:path';
|
|
3
|
-
|
|
4
|
-
|
|
4
|
+
function parseAttributes(line, prefix) {
|
|
5
|
+
const attrs = {};
|
|
6
|
+
const content = line.substring(prefix.length);
|
|
7
|
+
const regex = /([A-Z0-9-]+)=(?:"([^"]*)"|([^,]*))/g;
|
|
8
|
+
let match;
|
|
9
|
+
while ((match = regex.exec(content)) !== null) {
|
|
10
|
+
attrs[match[1]] = match[2] ?? match[3];
|
|
11
|
+
}
|
|
12
|
+
return attrs;
|
|
13
|
+
}
|
|
14
|
+
function resolveUrl(url, baseUrl) {
|
|
15
|
+
if (url.startsWith('http://') || url.startsWith('https://')) {
|
|
16
|
+
return url;
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
const base = new URL(baseUrl);
|
|
20
|
+
const basePath = base.pathname.substring(0, base.pathname.lastIndexOf('/') + 1);
|
|
21
|
+
return new URL(url, base.origin + basePath).toString();
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return url;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function parseMasterPlaylist(content, baseUrl) {
|
|
28
|
+
const lines = content.split('\n');
|
|
29
|
+
const variants = [];
|
|
30
|
+
let pendingVariant = {};
|
|
31
|
+
for (const rawLine of lines) {
|
|
32
|
+
const line = rawLine.trim();
|
|
33
|
+
if (!line)
|
|
34
|
+
continue;
|
|
35
|
+
if (line.startsWith('#EXT-X-STREAM-INF:')) {
|
|
36
|
+
const attrs = parseAttributes(line, '#EXT-X-STREAM-INF:');
|
|
37
|
+
pendingVariant = {
|
|
38
|
+
bandwidth: attrs.BANDWIDTH ? parseInt(attrs.BANDWIDTH, 10) : undefined,
|
|
39
|
+
resolution: attrs.RESOLUTION,
|
|
40
|
+
codecs: attrs.CODECS,
|
|
41
|
+
name: attrs.NAME,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
else if (!line.startsWith('#') && pendingVariant.bandwidth !== undefined) {
|
|
45
|
+
variants.push({
|
|
46
|
+
...pendingVariant,
|
|
47
|
+
url: resolveUrl(line, baseUrl),
|
|
48
|
+
});
|
|
49
|
+
pendingVariant = {};
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return { variants, isMaster: true };
|
|
53
|
+
}
|
|
54
|
+
function parseMediaPlaylist(content, baseUrl) {
|
|
5
55
|
const lines = content.split('\n');
|
|
6
56
|
const segments = [];
|
|
7
|
-
let currentDuration = 0;
|
|
8
|
-
let mediaSequence = 0;
|
|
9
57
|
let targetDuration = 5;
|
|
58
|
+
let mediaSequence = 0;
|
|
59
|
+
let discontinuitySequence = 0;
|
|
10
60
|
let endList = false;
|
|
61
|
+
let playlistType;
|
|
62
|
+
let currentDuration = 0;
|
|
11
63
|
let currentKey;
|
|
12
|
-
|
|
13
|
-
|
|
64
|
+
let currentDiscontinuity = false;
|
|
65
|
+
let currentProgramDateTime;
|
|
66
|
+
let segmentIndex = 0;
|
|
67
|
+
for (const rawLine of lines) {
|
|
68
|
+
const line = rawLine.trim();
|
|
14
69
|
if (!line)
|
|
15
70
|
continue;
|
|
16
71
|
if (line.startsWith('#EXT-X-TARGETDURATION:')) {
|
|
17
|
-
targetDuration =
|
|
72
|
+
targetDuration = parseInt(line.split(':')[1], 10);
|
|
18
73
|
}
|
|
19
74
|
else if (line.startsWith('#EXT-X-MEDIA-SEQUENCE:')) {
|
|
20
75
|
mediaSequence = parseInt(line.split(':')[1], 10);
|
|
21
76
|
}
|
|
22
|
-
else if (line.startsWith('#EXT-X-
|
|
77
|
+
else if (line.startsWith('#EXT-X-DISCONTINUITY-SEQUENCE:')) {
|
|
78
|
+
discontinuitySequence = parseInt(line.split(':')[1], 10);
|
|
79
|
+
}
|
|
80
|
+
else if (line.startsWith('#EXT-X-PLAYLIST-TYPE:')) {
|
|
81
|
+
playlistType = line.split(':')[1];
|
|
82
|
+
}
|
|
83
|
+
else if (line === '#EXT-X-ENDLIST') {
|
|
23
84
|
endList = true;
|
|
24
85
|
}
|
|
86
|
+
else if (line === '#EXT-X-DISCONTINUITY') {
|
|
87
|
+
currentDiscontinuity = true;
|
|
88
|
+
}
|
|
89
|
+
else if (line.startsWith('#EXT-X-PROGRAM-DATE-TIME:')) {
|
|
90
|
+
currentProgramDateTime = new Date(line.split(':').slice(1).join(':'));
|
|
91
|
+
}
|
|
25
92
|
else if (line.startsWith('#EXT-X-KEY:')) {
|
|
26
|
-
const attrs = line
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
93
|
+
const attrs = parseAttributes(line, '#EXT-X-KEY:');
|
|
94
|
+
if (attrs.METHOD === 'NONE') {
|
|
95
|
+
currentKey = undefined;
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
31
98
|
currentKey = {
|
|
32
|
-
method:
|
|
33
|
-
uri:
|
|
34
|
-
iv:
|
|
99
|
+
method: attrs.METHOD,
|
|
100
|
+
uri: attrs.URI ? resolveUrl(attrs.URI, baseUrl) : undefined,
|
|
101
|
+
iv: attrs.IV,
|
|
35
102
|
};
|
|
36
103
|
}
|
|
37
104
|
}
|
|
38
105
|
else if (line.startsWith('#EXTINF:')) {
|
|
39
|
-
const durationStr = line.substring(8).split(','
|
|
106
|
+
const durationStr = line.substring(8).split(',')[0];
|
|
40
107
|
currentDuration = parseFloat(durationStr);
|
|
41
108
|
}
|
|
42
109
|
else if (!line.startsWith('#')) {
|
|
43
|
-
let url = line;
|
|
44
|
-
if (!url.startsWith('http')) {
|
|
45
|
-
try {
|
|
46
|
-
const base = new URL(baseUrl);
|
|
47
|
-
const basePath = base.pathname.substring(0, base.pathname.lastIndexOf('/') + 1);
|
|
48
|
-
url = new URL(url, base.origin + basePath).toString();
|
|
49
|
-
}
|
|
50
|
-
catch {
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
110
|
segments.push({
|
|
54
|
-
url,
|
|
111
|
+
url: resolveUrl(line, baseUrl),
|
|
55
112
|
duration: currentDuration,
|
|
56
|
-
sequence:
|
|
57
|
-
key: currentKey
|
|
113
|
+
sequence: mediaSequence + segmentIndex,
|
|
114
|
+
key: currentKey,
|
|
115
|
+
discontinuity: currentDiscontinuity,
|
|
116
|
+
programDateTime: currentProgramDateTime,
|
|
58
117
|
});
|
|
118
|
+
segmentIndex++;
|
|
119
|
+
currentDiscontinuity = false;
|
|
120
|
+
currentProgramDateTime = undefined;
|
|
59
121
|
}
|
|
60
122
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
123
|
+
return {
|
|
124
|
+
segments,
|
|
125
|
+
targetDuration,
|
|
126
|
+
mediaSequence,
|
|
127
|
+
endList,
|
|
128
|
+
playlistType,
|
|
129
|
+
discontinuitySequence,
|
|
130
|
+
};
|
|
65
131
|
}
|
|
66
|
-
function
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const bwMatch = line.match(/BANDWIDTH=(\d+)/);
|
|
77
|
-
const resMatch = line.match(/RESOLUTION=(\d+x\d+)/);
|
|
78
|
-
if (bwMatch)
|
|
79
|
-
currentBandwidth = parseInt(bwMatch[1], 10);
|
|
80
|
-
if (resMatch)
|
|
81
|
-
currentResolution = resMatch[1];
|
|
82
|
-
}
|
|
83
|
-
else if (!line.startsWith('#')) {
|
|
84
|
-
let url = line;
|
|
85
|
-
if (!url.startsWith('http')) {
|
|
86
|
-
try {
|
|
87
|
-
const base = new URL(baseUrl);
|
|
88
|
-
const basePath = base.pathname.substring(0, base.pathname.lastIndexOf('/') + 1);
|
|
89
|
-
url = new URL(url, base.origin + basePath).toString();
|
|
90
|
-
}
|
|
91
|
-
catch { }
|
|
92
|
-
}
|
|
93
|
-
variants.push({
|
|
94
|
-
url,
|
|
95
|
-
bandwidth: currentBandwidth,
|
|
96
|
-
resolution: currentResolution
|
|
97
|
-
});
|
|
98
|
-
}
|
|
132
|
+
function isMasterPlaylist(content) {
|
|
133
|
+
return content.includes('#EXT-X-STREAM-INF');
|
|
134
|
+
}
|
|
135
|
+
function selectVariant(variants, quality) {
|
|
136
|
+
if (!variants.length) {
|
|
137
|
+
throw new Error('No variants found in master playlist');
|
|
138
|
+
}
|
|
139
|
+
const sorted = [...variants].sort((a, b) => (a.bandwidth ?? 0) - (b.bandwidth ?? 0));
|
|
140
|
+
if (quality === 'lowest') {
|
|
141
|
+
return sorted[0];
|
|
99
142
|
}
|
|
100
|
-
|
|
143
|
+
if (quality === 'highest' || quality === undefined) {
|
|
144
|
+
return sorted[sorted.length - 1];
|
|
145
|
+
}
|
|
146
|
+
if (quality.resolution) {
|
|
147
|
+
const match = variants.find((v) => v.resolution === quality.resolution);
|
|
148
|
+
if (match)
|
|
149
|
+
return match;
|
|
150
|
+
}
|
|
151
|
+
if (quality.bandwidth) {
|
|
152
|
+
const target = quality.bandwidth;
|
|
153
|
+
return sorted.reduce((prev, curr) => {
|
|
154
|
+
const prevDiff = Math.abs((prev.bandwidth ?? 0) - target);
|
|
155
|
+
const currDiff = Math.abs((curr.bandwidth ?? 0) - target);
|
|
156
|
+
return currDiff < prevDiff ? curr : prev;
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
return sorted[sorted.length - 1];
|
|
101
160
|
}
|
|
102
|
-
export
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
}
|
|
124
|
-
if (initialManifest.includes('#EXT-X-STREAM-INF')) {
|
|
125
|
-
const variants = parseVariants(initialManifest, currentManifestUrl);
|
|
126
|
-
if (variants.length > 0) {
|
|
127
|
-
let selected = variants[variants.length - 1];
|
|
128
|
-
if (options.onVariantSelected) {
|
|
129
|
-
const userSelected = options.onVariantSelected(variants, selected);
|
|
130
|
-
if (userSelected)
|
|
131
|
-
selected = userSelected;
|
|
132
|
-
}
|
|
133
|
-
currentManifestUrl = selected.url;
|
|
134
|
-
info(`Master playlist detected. Switching to variant: ${currentManifestUrl}`);
|
|
135
|
-
}
|
|
161
|
+
export class HlsPromise {
|
|
162
|
+
client;
|
|
163
|
+
manifestUrl;
|
|
164
|
+
options;
|
|
165
|
+
seenSequences = new Set();
|
|
166
|
+
downloadedBytes = 0;
|
|
167
|
+
downloadedSegments = 0;
|
|
168
|
+
startTime = 0;
|
|
169
|
+
aborted = false;
|
|
170
|
+
abortController = new AbortController();
|
|
171
|
+
constructor(client, manifestUrl, options = {}) {
|
|
172
|
+
this.client = client;
|
|
173
|
+
this.manifestUrl = manifestUrl;
|
|
174
|
+
this.options = {
|
|
175
|
+
mode: 'merge',
|
|
176
|
+
format: 'ts',
|
|
177
|
+
concurrency: 5,
|
|
178
|
+
...options,
|
|
179
|
+
};
|
|
180
|
+
if (this.options.format !== 'ts') {
|
|
181
|
+
throw new Error(`Format '${this.options.format}' requires ffmpeg. Use format: 'ts' or install ffmpeg.`);
|
|
136
182
|
}
|
|
137
183
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
return;
|
|
184
|
+
get [Symbol.toStringTag]() {
|
|
185
|
+
return 'HlsPromise';
|
|
141
186
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
187
|
+
then(onfulfilled, onrejected) {
|
|
188
|
+
return Promise.reject(new Error('HlsPromise requires .download(), .stream(), or .pipe() to execute')).then(onfulfilled, onrejected);
|
|
189
|
+
}
|
|
190
|
+
catch(onrejected) {
|
|
191
|
+
return this.then(null, onrejected);
|
|
192
|
+
}
|
|
193
|
+
finally(onfinally) {
|
|
194
|
+
return this.then(() => {
|
|
195
|
+
onfinally?.();
|
|
196
|
+
}, () => {
|
|
197
|
+
onfinally?.();
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
cancel() {
|
|
201
|
+
this.aborted = true;
|
|
202
|
+
this.abortController.abort();
|
|
203
|
+
}
|
|
204
|
+
async download(dest) {
|
|
205
|
+
this.startTime = Date.now();
|
|
206
|
+
const mediaPlaylistUrl = await this.resolveMediaPlaylist();
|
|
207
|
+
if (this.options.mode === 'chunks') {
|
|
208
|
+
await this.downloadChunks(mediaPlaylistUrl, dest);
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
if (typeof dest !== 'string') {
|
|
212
|
+
throw new Error('Merge mode requires a string path, not a function');
|
|
153
213
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
filtered.push(seg);
|
|
214
|
+
await this.downloadMerged(mediaPlaylistUrl, dest);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
async *stream() {
|
|
218
|
+
this.startTime = Date.now();
|
|
219
|
+
const mediaPlaylistUrl = await this.resolveMediaPlaylist();
|
|
220
|
+
const isLive = this.isLiveMode();
|
|
221
|
+
const maxDuration = this.getMaxDuration();
|
|
222
|
+
while (!this.aborted) {
|
|
223
|
+
const playlist = await this.fetchMediaPlaylist(mediaPlaylistUrl);
|
|
224
|
+
const newSegments = playlist.segments.filter((s) => !this.seenSequences.has(s.sequence));
|
|
225
|
+
for (const segment of newSegments) {
|
|
226
|
+
if (this.aborted)
|
|
227
|
+
break;
|
|
228
|
+
if (maxDuration && Date.now() - this.startTime > maxDuration) {
|
|
229
|
+
return;
|
|
171
230
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
231
|
+
this.seenSequences.add(segment.sequence);
|
|
232
|
+
const data = await this.downloadSegment(segment);
|
|
233
|
+
const segmentData = {
|
|
234
|
+
sequence: segment.sequence,
|
|
235
|
+
duration: segment.duration,
|
|
236
|
+
data,
|
|
237
|
+
url: segment.url,
|
|
238
|
+
downloadedAt: new Date(),
|
|
239
|
+
};
|
|
240
|
+
this.downloadedSegments++;
|
|
241
|
+
this.downloadedBytes += data.byteLength;
|
|
242
|
+
this.emitProgress(playlist, isLive);
|
|
243
|
+
if (this.options.onSegment) {
|
|
244
|
+
await this.options.onSegment(segmentData);
|
|
245
|
+
}
|
|
246
|
+
yield segmentData;
|
|
183
247
|
}
|
|
184
248
|
if (!isLive || playlist.endList) {
|
|
185
|
-
recording = false;
|
|
186
249
|
break;
|
|
187
250
|
}
|
|
188
|
-
if (maxDuration
|
|
189
|
-
info('Max duration reached. Stopping.');
|
|
190
|
-
recording = false;
|
|
251
|
+
if (maxDuration && Date.now() - this.startTime > maxDuration) {
|
|
191
252
|
break;
|
|
192
253
|
}
|
|
193
|
-
const
|
|
194
|
-
await
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
254
|
+
const pollInterval = Math.max(1000, (playlist.targetDuration * 1000) / 2);
|
|
255
|
+
await this.sleep(pollInterval);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
async pipe(writable) {
|
|
259
|
+
try {
|
|
260
|
+
for await (const segment of this.stream()) {
|
|
261
|
+
const canContinue = writable.write(segment.data);
|
|
262
|
+
if (!canContinue) {
|
|
263
|
+
await new Promise((resolve) => writable.once('drain', resolve));
|
|
264
|
+
}
|
|
201
265
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
266
|
+
}
|
|
267
|
+
finally {
|
|
268
|
+
if ('end' in writable && typeof writable.end === 'function') {
|
|
269
|
+
writable.end();
|
|
205
270
|
}
|
|
206
271
|
}
|
|
207
272
|
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
const
|
|
216
|
-
|
|
273
|
+
async info() {
|
|
274
|
+
const content = await this.client.get(this.manifestUrl).text();
|
|
275
|
+
if (isMasterPlaylist(content)) {
|
|
276
|
+
const master = parseMasterPlaylist(content, this.manifestUrl);
|
|
277
|
+
const selectedVariant = selectVariant(master.variants, this.options.quality);
|
|
278
|
+
const playlistContent = await this.client.get(selectedVariant.url).text();
|
|
279
|
+
const playlist = parseMediaPlaylist(playlistContent, selectedVariant.url);
|
|
280
|
+
const totalDuration = playlist.endList
|
|
281
|
+
? playlist.segments.reduce((sum, s) => sum + s.duration, 0)
|
|
282
|
+
: undefined;
|
|
283
|
+
return {
|
|
284
|
+
master,
|
|
285
|
+
playlist,
|
|
286
|
+
selectedVariant,
|
|
287
|
+
isLive: !playlist.endList,
|
|
288
|
+
totalDuration,
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
const playlist = parseMediaPlaylist(content, this.manifestUrl);
|
|
292
|
+
const totalDuration = playlist.endList
|
|
293
|
+
? playlist.segments.reduce((sum, s) => sum + s.duration, 0)
|
|
294
|
+
: undefined;
|
|
295
|
+
return {
|
|
296
|
+
playlist,
|
|
297
|
+
isLive: !playlist.endList,
|
|
298
|
+
totalDuration,
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
async resolveMediaPlaylist() {
|
|
302
|
+
const content = await this.client.get(this.manifestUrl).text();
|
|
303
|
+
if (!isMasterPlaylist(content)) {
|
|
304
|
+
return this.manifestUrl;
|
|
305
|
+
}
|
|
306
|
+
const master = parseMasterPlaylist(content, this.manifestUrl);
|
|
307
|
+
const variant = selectVariant(master.variants, this.options.quality);
|
|
308
|
+
return variant.url;
|
|
309
|
+
}
|
|
310
|
+
async fetchMediaPlaylist(url) {
|
|
311
|
+
const content = await this.client.get(url).text();
|
|
312
|
+
return parseMediaPlaylist(content, url);
|
|
313
|
+
}
|
|
314
|
+
async downloadSegment(segment) {
|
|
315
|
+
if (segment.key && segment.key.method !== 'NONE') {
|
|
316
|
+
throw new Error(`Encrypted HLS (${segment.key.method}) requires ffmpeg. Use unencrypted streams or install ffmpeg.`);
|
|
317
|
+
}
|
|
318
|
+
const response = await this.client.get(segment.url, {
|
|
319
|
+
headers: this.options.headers,
|
|
320
|
+
signal: this.abortController.signal,
|
|
321
|
+
});
|
|
322
|
+
const blob = await response.blob();
|
|
323
|
+
return new Uint8Array(await blob.arrayBuffer());
|
|
324
|
+
}
|
|
325
|
+
async downloadMerged(playlistUrl, outputPath) {
|
|
326
|
+
await mkdir(dirname(outputPath), { recursive: true });
|
|
327
|
+
const output = createWriteStream(outputPath);
|
|
328
|
+
try {
|
|
329
|
+
await this.pipe(output);
|
|
330
|
+
}
|
|
331
|
+
catch (error) {
|
|
332
|
+
output.destroy();
|
|
333
|
+
throw error;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
async downloadChunks(playlistUrl, dest) {
|
|
337
|
+
const getPath = typeof dest === 'string'
|
|
338
|
+
? (seg) => join(dest, `segment-${seg.sequence}.ts`)
|
|
339
|
+
: dest;
|
|
340
|
+
const baseDir = typeof dest === 'string' ? dest : dirname(getPath({ sequence: 0, duration: 0, url: '' }));
|
|
341
|
+
await mkdir(baseDir, { recursive: true });
|
|
342
|
+
for await (const segment of this.stream()) {
|
|
343
|
+
const filePath = getPath({
|
|
344
|
+
sequence: segment.sequence,
|
|
345
|
+
duration: segment.duration,
|
|
346
|
+
url: segment.url,
|
|
347
|
+
});
|
|
348
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
349
|
+
const output = createWriteStream(filePath);
|
|
350
|
+
await new Promise((resolve, reject) => {
|
|
351
|
+
output.write(segment.data, (err) => {
|
|
352
|
+
if (err)
|
|
353
|
+
reject(err);
|
|
354
|
+
else {
|
|
355
|
+
output.end();
|
|
356
|
+
resolve();
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
});
|
|
217
360
|
}
|
|
218
|
-
dest.end();
|
|
219
|
-
await rm(tempDir, { recursive: true, force: true });
|
|
220
|
-
info(`Saved to ${outputPath}`);
|
|
221
361
|
}
|
|
222
|
-
|
|
223
|
-
|
|
362
|
+
isLiveMode() {
|
|
363
|
+
return this.options.live === true || typeof this.options.live === 'object';
|
|
224
364
|
}
|
|
365
|
+
getMaxDuration() {
|
|
366
|
+
if (typeof this.options.live === 'object' && this.options.live.duration) {
|
|
367
|
+
return this.options.live.duration;
|
|
368
|
+
}
|
|
369
|
+
return undefined;
|
|
370
|
+
}
|
|
371
|
+
emitProgress(playlist, isLive) {
|
|
372
|
+
if (!this.options.onProgress)
|
|
373
|
+
return;
|
|
374
|
+
this.options.onProgress({
|
|
375
|
+
downloadedSegments: this.downloadedSegments,
|
|
376
|
+
totalSegments: isLive ? undefined : playlist.segments.length,
|
|
377
|
+
downloadedBytes: this.downloadedBytes,
|
|
378
|
+
currentSegment: Math.max(...this.seenSequences),
|
|
379
|
+
isLive,
|
|
380
|
+
elapsed: Date.now() - this.startTime,
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
sleep(ms) {
|
|
384
|
+
return new Promise((resolve) => {
|
|
385
|
+
const timeout = setTimeout(resolve, ms);
|
|
386
|
+
this.abortController.signal.addEventListener('abort', () => {
|
|
387
|
+
clearTimeout(timeout);
|
|
388
|
+
resolve();
|
|
389
|
+
}, { once: true });
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
export function hls(client, manifestUrl, options = {}) {
|
|
394
|
+
return new HlsPromise(client, manifestUrl, options);
|
|
225
395
|
}
|
|
@@ -31,7 +31,7 @@ export declare class UndiciTransport implements Transport {
|
|
|
31
31
|
private tlsOptions?;
|
|
32
32
|
private socketClient?;
|
|
33
33
|
private observability;
|
|
34
|
-
constructor(baseUrl
|
|
34
|
+
constructor(baseUrl?: string, options?: UndiciTransportOptions);
|
|
35
35
|
dispatch(req: ReckerRequest): Promise<ReckerResponse>;
|
|
36
36
|
private dispatchFast;
|
|
37
37
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"undici.d.ts","sourceRoot":"","sources":["../../src/transport/undici.ts"],"names":[],"mappings":"AACA,OAAO,EAAkB,aAAa,EAAE,cAAc,EAAW,SAAS,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAgB,UAAU,EAAkD,MAAM,mBAAmB,CAAC;AAOxN,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAwNzD,UAAU,sBAAsB;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,YAAY,GAAG,MAAM,CAAC;IAC9B,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,GAAG,CAAC,EAAE,UAAU,CAAC;IACjB,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,GAAG,CAAC,EAAE,UAAU,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;IAMtB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAqDD,qBAAa,eAAgB,YAAW,SAAS;IAC/C,OAAO,CAAC,MAAM,CAAC,cAAc,CAAK;IAElC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,OAAO,CAAyB;IACxC,OAAO,CAAC,UAAU,CAAC,CAAa;IAChC,OAAO,CAAC,QAAQ,CAAC,CAAQ;IACzB,OAAO,CAAC,YAAY,CAAC,CAAe;IACpC,OAAO,CAAC,eAAe,CAAC,CAAW;IACnC,OAAO,CAAC,UAAU,CAAC,CAAa;IAChC,OAAO,CAAC,YAAY,CAAC,CAAS;IAC9B,OAAO,CAAC,aAAa,CAAU;gBAEnB,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,sBAA2B;
|
|
1
|
+
{"version":3,"file":"undici.d.ts","sourceRoot":"","sources":["../../src/transport/undici.ts"],"names":[],"mappings":"AACA,OAAO,EAAkB,aAAa,EAAE,cAAc,EAAW,SAAS,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAgB,UAAU,EAAkD,MAAM,mBAAmB,CAAC;AAOxN,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAwNzD,UAAU,sBAAsB;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,YAAY,GAAG,MAAM,CAAC;IAC9B,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,GAAG,CAAC,EAAE,UAAU,CAAC;IACjB,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,GAAG,CAAC,EAAE,UAAU,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;IAMtB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAqDD,qBAAa,eAAgB,YAAW,SAAS;IAC/C,OAAO,CAAC,MAAM,CAAC,cAAc,CAAK;IAElC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,OAAO,CAAyB;IACxC,OAAO,CAAC,UAAU,CAAC,CAAa;IAChC,OAAO,CAAC,QAAQ,CAAC,CAAQ;IACzB,OAAO,CAAC,YAAY,CAAC,CAAe;IACpC,OAAO,CAAC,eAAe,CAAC,CAAW;IACnC,OAAO,CAAC,UAAU,CAAC,CAAa;IAChC,OAAO,CAAC,YAAY,CAAC,CAAS;IAC9B,OAAO,CAAC,aAAa,CAAU;gBAEnB,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,GAAE,sBAA2B;IAmG5D,QAAQ,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC;YAsV7C,YAAY;CAoQ3B"}
|
package/dist/transport/undici.js
CHANGED
|
@@ -143,7 +143,7 @@ export class UndiciTransport {
|
|
|
143
143
|
socketClient;
|
|
144
144
|
observability;
|
|
145
145
|
constructor(baseUrl, options = {}) {
|
|
146
|
-
this.baseUrl = baseUrl;
|
|
146
|
+
this.baseUrl = baseUrl || '';
|
|
147
147
|
this.options = options;
|
|
148
148
|
this.tlsOptions = options.tls;
|
|
149
149
|
this.observability = options.observability !== false;
|
|
@@ -203,7 +203,7 @@ export class UndiciTransport {
|
|
|
203
203
|
localAddress: options.localAddress,
|
|
204
204
|
});
|
|
205
205
|
}
|
|
206
|
-
if (options.socketPath) {
|
|
206
|
+
if (options.socketPath && baseUrl) {
|
|
207
207
|
this.socketClient = new Client(baseUrl, {
|
|
208
208
|
socketPath: options.socketPath
|
|
209
209
|
});
|
|
@@ -213,8 +213,14 @@ export class UndiciTransport {
|
|
|
213
213
|
const headers = Object.fromEntries(req.headers);
|
|
214
214
|
const contentLengthHeader = headers['content-length'];
|
|
215
215
|
const uploadTotal = contentLengthHeader ? parseInt(contentLengthHeader, 10) : undefined;
|
|
216
|
-
|
|
217
|
-
|
|
216
|
+
let currentUrl;
|
|
217
|
+
if (this.baseUrl) {
|
|
218
|
+
const path = req.url.startsWith(this.baseUrl) ? req.url.substring(this.baseUrl.length) : req.url;
|
|
219
|
+
currentUrl = new URL(path, this.baseUrl).toString();
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
currentUrl = req.url;
|
|
223
|
+
}
|
|
218
224
|
const handleRedirectsManually = Boolean(req.beforeRedirect);
|
|
219
225
|
const maxRedirects = req.maxRedirects ?? 20;
|
|
220
226
|
const followRedirects = req.followRedirects !== false;
|