@bowenqt/qiniu-ai-sdk 0.19.1 → 0.20.0
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/browser/index.d.ts +6 -0
- package/dist/browser/index.d.ts.map +1 -1
- package/dist/browser/index.js +19 -2
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/index.mjs +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +6 -0
- package/dist/lib/asset-cost.d.ts +56 -0
- package/dist/lib/asset-cost.d.ts.map +1 -0
- package/dist/lib/asset-cost.js +138 -0
- package/dist/lib/asset-cost.js.map +1 -0
- package/dist/lib/asset-cost.mjs +134 -0
- package/dist/lib/asset-resolver.d.ts +70 -0
- package/dist/lib/asset-resolver.d.ts.map +1 -0
- package/dist/lib/asset-resolver.js +145 -0
- package/dist/lib/asset-resolver.js.map +1 -0
- package/dist/lib/asset-resolver.mjs +138 -0
- package/dist/lib/vframe.d.ts +94 -0
- package/dist/lib/vframe.d.ts.map +1 -0
- package/dist/lib/vframe.js +189 -0
- package/dist/lib/vframe.js.map +1 -0
- package/dist/lib/vframe.mjs +181 -0
- package/package.json +1 -1
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Asset Resolution for Qiniu Cloud resources.
|
|
3
|
+
* Provides parsing and resolution of qiniu:// URIs with security validation.
|
|
4
|
+
*/
|
|
5
|
+
import type { QiniuSigner } from './signer';
|
|
6
|
+
import { CachedSigner } from './signer';
|
|
7
|
+
/** Parsed Qiniu asset */
|
|
8
|
+
export interface QiniuAsset {
|
|
9
|
+
/** Bucket name */
|
|
10
|
+
bucket: string;
|
|
11
|
+
/** Object key (path) */
|
|
12
|
+
key: string;
|
|
13
|
+
}
|
|
14
|
+
/** Asset resolution options */
|
|
15
|
+
export interface ResolveOptions {
|
|
16
|
+
/** Processing fop command (e.g., 'vframe/jpg/offset/1') */
|
|
17
|
+
fop?: string;
|
|
18
|
+
/** Allowed buckets whitelist (if empty, all buckets allowed) */
|
|
19
|
+
allowedBuckets?: string[];
|
|
20
|
+
/** URL expiry in seconds (default: 3600) */
|
|
21
|
+
expiry?: number;
|
|
22
|
+
}
|
|
23
|
+
/** Resolved asset with signed URL */
|
|
24
|
+
export interface ResolvedAsset {
|
|
25
|
+
/** Signed URL */
|
|
26
|
+
url: string;
|
|
27
|
+
/** URL expiration timestamp (ms) */
|
|
28
|
+
expiresAt: number;
|
|
29
|
+
/** Bucket name */
|
|
30
|
+
bucket: string;
|
|
31
|
+
/** Object key */
|
|
32
|
+
key: string;
|
|
33
|
+
}
|
|
34
|
+
/** Asset resolution error */
|
|
35
|
+
export declare class AssetResolutionError extends Error {
|
|
36
|
+
readonly code: 'INVALID_URI' | 'SECURITY_VIOLATION' | 'BUCKET_NOT_ALLOWED';
|
|
37
|
+
constructor(message: string, code: 'INVALID_URI' | 'SECURITY_VIOLATION' | 'BUCKET_NOT_ALLOWED');
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Parse a qiniu:// URI into bucket and key.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```typescript
|
|
44
|
+
* const asset = parseQiniuUri('qiniu://my-bucket/path/to/video.mp4');
|
|
45
|
+
* // { bucket: 'my-bucket', key: 'path/to/video.mp4' }
|
|
46
|
+
* ```
|
|
47
|
+
*
|
|
48
|
+
* @returns Parsed asset or null if invalid URI
|
|
49
|
+
* @throws AssetResolutionError for security violations
|
|
50
|
+
*/
|
|
51
|
+
export declare function parseQiniuUri(uri: string): QiniuAsset | null;
|
|
52
|
+
/**
|
|
53
|
+
* Resolve a Qiniu asset to a signed URL.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```typescript
|
|
57
|
+
* const asset = parseQiniuUri('qiniu://my-bucket/video.mp4');
|
|
58
|
+
* const resolved = await resolveAsset(asset, signer, {
|
|
59
|
+
* allowedBuckets: ['my-bucket'],
|
|
60
|
+
* });
|
|
61
|
+
* console.log(resolved.url); // Signed URL
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
export declare function resolveAsset(asset: QiniuAsset, signer: QiniuSigner | CachedSigner, options?: ResolveOptions): Promise<ResolvedAsset>;
|
|
65
|
+
/**
|
|
66
|
+
* Resolve multiple assets in parallel.
|
|
67
|
+
* Uses CachedSigner for efficiency.
|
|
68
|
+
*/
|
|
69
|
+
export declare function resolveAssets(assets: QiniuAsset[], signer: QiniuSigner | CachedSigner, options?: ResolveOptions): Promise<ResolvedAsset[]>;
|
|
70
|
+
//# sourceMappingURL=asset-resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"asset-resolver.d.ts","sourceRoot":"","sources":["../../src/lib/asset-resolver.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,WAAW,EAA0B,MAAM,UAAU,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAMxC,yBAAyB;AACzB,MAAM,WAAW,UAAU;IACvB,kBAAkB;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,wBAAwB;IACxB,GAAG,EAAE,MAAM,CAAC;CACf;AAED,+BAA+B;AAC/B,MAAM,WAAW,cAAc;IAC3B,2DAA2D;IAC3D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,qCAAqC;AACrC,MAAM,WAAW,aAAa;IAC1B,iBAAiB;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,oCAAoC;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,iBAAiB;IACjB,GAAG,EAAE,MAAM,CAAC;CACf;AAqBD,6BAA6B;AAC7B,qBAAa,oBAAqB,SAAQ,KAAK;aAGvB,IAAI,EAAE,aAAa,GAAG,oBAAoB,GAAG,oBAAoB;gBADjF,OAAO,EAAE,MAAM,EACC,IAAI,EAAE,aAAa,GAAG,oBAAoB,GAAG,oBAAoB;CAKxF;AAMD;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAiE5D;AAMD;;;;;;;;;;;GAWG;AACH,wBAAsB,YAAY,CAC9B,KAAK,EAAE,UAAU,EACjB,MAAM,EAAE,WAAW,GAAG,YAAY,EAClC,OAAO,GAAE,cAAmB,GAC7B,OAAO,CAAC,aAAa,CAAC,CA0BxB;AAMD;;;GAGG;AACH,wBAAsB,aAAa,CAC/B,MAAM,EAAE,UAAU,EAAE,EACpB,MAAM,EAAE,WAAW,GAAG,YAAY,EAClC,OAAO,GAAE,cAAmB,GAC7B,OAAO,CAAC,aAAa,EAAE,CAAC,CAI1B"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Asset Resolution for Qiniu Cloud resources.
|
|
4
|
+
* Provides parsing and resolution of qiniu:// URIs with security validation.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.AssetResolutionError = void 0;
|
|
8
|
+
exports.parseQiniuUri = parseQiniuUri;
|
|
9
|
+
exports.resolveAsset = resolveAsset;
|
|
10
|
+
exports.resolveAssets = resolveAssets;
|
|
11
|
+
// ============================================================================
|
|
12
|
+
// Constants
|
|
13
|
+
// ============================================================================
|
|
14
|
+
const QINIU_URI_SCHEME = 'qiniu://';
|
|
15
|
+
// Characters not allowed in bucket/key names (security)
|
|
16
|
+
const DANGEROUS_PATTERNS = [
|
|
17
|
+
/\.\./, // Path traversal
|
|
18
|
+
/\\/, // Backslash
|
|
19
|
+
/[\x00-\x1f]/, // Control characters
|
|
20
|
+
/%2e%2e/i, // URL-encoded ../
|
|
21
|
+
/%5c/i, // URL-encoded \
|
|
22
|
+
];
|
|
23
|
+
// ============================================================================
|
|
24
|
+
// Errors
|
|
25
|
+
// ============================================================================
|
|
26
|
+
/** Asset resolution error */
|
|
27
|
+
class AssetResolutionError extends Error {
|
|
28
|
+
constructor(message, code) {
|
|
29
|
+
super(message);
|
|
30
|
+
this.code = code;
|
|
31
|
+
this.name = 'AssetResolutionError';
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
exports.AssetResolutionError = AssetResolutionError;
|
|
35
|
+
// ============================================================================
|
|
36
|
+
// URI Parsing
|
|
37
|
+
// ============================================================================
|
|
38
|
+
/**
|
|
39
|
+
* Parse a qiniu:// URI into bucket and key.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```typescript
|
|
43
|
+
* const asset = parseQiniuUri('qiniu://my-bucket/path/to/video.mp4');
|
|
44
|
+
* // { bucket: 'my-bucket', key: 'path/to/video.mp4' }
|
|
45
|
+
* ```
|
|
46
|
+
*
|
|
47
|
+
* @returns Parsed asset or null if invalid URI
|
|
48
|
+
* @throws AssetResolutionError for security violations
|
|
49
|
+
*/
|
|
50
|
+
function parseQiniuUri(uri) {
|
|
51
|
+
if (!uri || typeof uri !== 'string') {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
// Normalize: trim and check scheme
|
|
55
|
+
const normalized = uri.trim();
|
|
56
|
+
if (!normalized.startsWith(QINIU_URI_SCHEME)) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
// Extract path after scheme
|
|
60
|
+
const path = normalized.slice(QINIU_URI_SCHEME.length);
|
|
61
|
+
// Remove fragment (#...) and query string (?...)
|
|
62
|
+
const cleanPath = path.split('#')[0].split('?')[0];
|
|
63
|
+
// Security check before decoding
|
|
64
|
+
for (const pattern of DANGEROUS_PATTERNS) {
|
|
65
|
+
if (pattern.test(cleanPath)) {
|
|
66
|
+
throw new AssetResolutionError(`Security violation: dangerous pattern in URI`, 'SECURITY_VIOLATION');
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// URL decode
|
|
70
|
+
let decodedPath;
|
|
71
|
+
try {
|
|
72
|
+
decodedPath = decodeURIComponent(cleanPath);
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
throw new AssetResolutionError(`Invalid URI encoding`, 'INVALID_URI');
|
|
76
|
+
}
|
|
77
|
+
// Security check after decoding
|
|
78
|
+
for (const pattern of DANGEROUS_PATTERNS) {
|
|
79
|
+
if (pattern.test(decodedPath)) {
|
|
80
|
+
throw new AssetResolutionError(`Security violation: dangerous pattern in decoded URI`, 'SECURITY_VIOLATION');
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// Remove leading slash
|
|
84
|
+
const trimmedPath = decodedPath.startsWith('/') ? decodedPath.slice(1) : decodedPath;
|
|
85
|
+
// Split into bucket/key
|
|
86
|
+
const slashIndex = trimmedPath.indexOf('/');
|
|
87
|
+
if (slashIndex === -1) {
|
|
88
|
+
// Only bucket, no key
|
|
89
|
+
if (!trimmedPath)
|
|
90
|
+
return null;
|
|
91
|
+
return { bucket: trimmedPath, key: '' };
|
|
92
|
+
}
|
|
93
|
+
const bucket = trimmedPath.slice(0, slashIndex);
|
|
94
|
+
const key = trimmedPath.slice(slashIndex + 1);
|
|
95
|
+
if (!bucket)
|
|
96
|
+
return null;
|
|
97
|
+
return { bucket, key };
|
|
98
|
+
}
|
|
99
|
+
// ============================================================================
|
|
100
|
+
// Asset Resolution
|
|
101
|
+
// ============================================================================
|
|
102
|
+
/**
|
|
103
|
+
* Resolve a Qiniu asset to a signed URL.
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* ```typescript
|
|
107
|
+
* const asset = parseQiniuUri('qiniu://my-bucket/video.mp4');
|
|
108
|
+
* const resolved = await resolveAsset(asset, signer, {
|
|
109
|
+
* allowedBuckets: ['my-bucket'],
|
|
110
|
+
* });
|
|
111
|
+
* console.log(resolved.url); // Signed URL
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
114
|
+
async function resolveAsset(asset, signer, options = {}) {
|
|
115
|
+
// Bucket whitelist validation
|
|
116
|
+
if (options.allowedBuckets?.length) {
|
|
117
|
+
if (!options.allowedBuckets.includes(asset.bucket)) {
|
|
118
|
+
throw new AssetResolutionError(`Bucket '${asset.bucket}' not in allowed list`, 'BUCKET_NOT_ALLOWED');
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// Build sign options
|
|
122
|
+
const signOptions = {
|
|
123
|
+
fop: options.fop,
|
|
124
|
+
expiry: options.expiry,
|
|
125
|
+
};
|
|
126
|
+
// Sign the URL
|
|
127
|
+
const signed = await signer.sign(asset.bucket, asset.key, signOptions);
|
|
128
|
+
return {
|
|
129
|
+
url: signed.url,
|
|
130
|
+
expiresAt: signed.expiresAt,
|
|
131
|
+
bucket: asset.bucket,
|
|
132
|
+
key: asset.key,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
// ============================================================================
|
|
136
|
+
// Batch Resolution
|
|
137
|
+
// ============================================================================
|
|
138
|
+
/**
|
|
139
|
+
* Resolve multiple assets in parallel.
|
|
140
|
+
* Uses CachedSigner for efficiency.
|
|
141
|
+
*/
|
|
142
|
+
async function resolveAssets(assets, signer, options = {}) {
|
|
143
|
+
return Promise.all(assets.map(asset => resolveAsset(asset, signer, options)));
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=asset-resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"asset-resolver.js","sourceRoot":"","sources":["../../src/lib/asset-resolver.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Asset Resolution for Qiniu Cloud resources.
|
|
3
|
+
* Provides parsing and resolution of qiniu:// URIs with security validation.
|
|
4
|
+
*/
|
|
5
|
+
// ============================================================================
|
|
6
|
+
// Constants
|
|
7
|
+
// ============================================================================
|
|
8
|
+
const QINIU_URI_SCHEME = 'qiniu://';
|
|
9
|
+
// Characters not allowed in bucket/key names (security)
|
|
10
|
+
const DANGEROUS_PATTERNS = [
|
|
11
|
+
/\.\./, // Path traversal
|
|
12
|
+
/\\/, // Backslash
|
|
13
|
+
/[\x00-\x1f]/, // Control characters
|
|
14
|
+
/%2e%2e/i, // URL-encoded ../
|
|
15
|
+
/%5c/i, // URL-encoded \
|
|
16
|
+
];
|
|
17
|
+
// ============================================================================
|
|
18
|
+
// Errors
|
|
19
|
+
// ============================================================================
|
|
20
|
+
/** Asset resolution error */
|
|
21
|
+
export class AssetResolutionError extends Error {
|
|
22
|
+
constructor(message, code) {
|
|
23
|
+
super(message);
|
|
24
|
+
this.code = code;
|
|
25
|
+
this.name = 'AssetResolutionError';
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
// ============================================================================
|
|
29
|
+
// URI Parsing
|
|
30
|
+
// ============================================================================
|
|
31
|
+
/**
|
|
32
|
+
* Parse a qiniu:// URI into bucket and key.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```typescript
|
|
36
|
+
* const asset = parseQiniuUri('qiniu://my-bucket/path/to/video.mp4');
|
|
37
|
+
* // { bucket: 'my-bucket', key: 'path/to/video.mp4' }
|
|
38
|
+
* ```
|
|
39
|
+
*
|
|
40
|
+
* @returns Parsed asset or null if invalid URI
|
|
41
|
+
* @throws AssetResolutionError for security violations
|
|
42
|
+
*/
|
|
43
|
+
export function parseQiniuUri(uri) {
|
|
44
|
+
if (!uri || typeof uri !== 'string') {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
// Normalize: trim and check scheme
|
|
48
|
+
const normalized = uri.trim();
|
|
49
|
+
if (!normalized.startsWith(QINIU_URI_SCHEME)) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
// Extract path after scheme
|
|
53
|
+
const path = normalized.slice(QINIU_URI_SCHEME.length);
|
|
54
|
+
// Remove fragment (#...) and query string (?...)
|
|
55
|
+
const cleanPath = path.split('#')[0].split('?')[0];
|
|
56
|
+
// Security check before decoding
|
|
57
|
+
for (const pattern of DANGEROUS_PATTERNS) {
|
|
58
|
+
if (pattern.test(cleanPath)) {
|
|
59
|
+
throw new AssetResolutionError(`Security violation: dangerous pattern in URI`, 'SECURITY_VIOLATION');
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// URL decode
|
|
63
|
+
let decodedPath;
|
|
64
|
+
try {
|
|
65
|
+
decodedPath = decodeURIComponent(cleanPath);
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
throw new AssetResolutionError(`Invalid URI encoding`, 'INVALID_URI');
|
|
69
|
+
}
|
|
70
|
+
// Security check after decoding
|
|
71
|
+
for (const pattern of DANGEROUS_PATTERNS) {
|
|
72
|
+
if (pattern.test(decodedPath)) {
|
|
73
|
+
throw new AssetResolutionError(`Security violation: dangerous pattern in decoded URI`, 'SECURITY_VIOLATION');
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// Remove leading slash
|
|
77
|
+
const trimmedPath = decodedPath.startsWith('/') ? decodedPath.slice(1) : decodedPath;
|
|
78
|
+
// Split into bucket/key
|
|
79
|
+
const slashIndex = trimmedPath.indexOf('/');
|
|
80
|
+
if (slashIndex === -1) {
|
|
81
|
+
// Only bucket, no key
|
|
82
|
+
if (!trimmedPath)
|
|
83
|
+
return null;
|
|
84
|
+
return { bucket: trimmedPath, key: '' };
|
|
85
|
+
}
|
|
86
|
+
const bucket = trimmedPath.slice(0, slashIndex);
|
|
87
|
+
const key = trimmedPath.slice(slashIndex + 1);
|
|
88
|
+
if (!bucket)
|
|
89
|
+
return null;
|
|
90
|
+
return { bucket, key };
|
|
91
|
+
}
|
|
92
|
+
// ============================================================================
|
|
93
|
+
// Asset Resolution
|
|
94
|
+
// ============================================================================
|
|
95
|
+
/**
|
|
96
|
+
* Resolve a Qiniu asset to a signed URL.
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```typescript
|
|
100
|
+
* const asset = parseQiniuUri('qiniu://my-bucket/video.mp4');
|
|
101
|
+
* const resolved = await resolveAsset(asset, signer, {
|
|
102
|
+
* allowedBuckets: ['my-bucket'],
|
|
103
|
+
* });
|
|
104
|
+
* console.log(resolved.url); // Signed URL
|
|
105
|
+
* ```
|
|
106
|
+
*/
|
|
107
|
+
export async function resolveAsset(asset, signer, options = {}) {
|
|
108
|
+
// Bucket whitelist validation
|
|
109
|
+
if (options.allowedBuckets?.length) {
|
|
110
|
+
if (!options.allowedBuckets.includes(asset.bucket)) {
|
|
111
|
+
throw new AssetResolutionError(`Bucket '${asset.bucket}' not in allowed list`, 'BUCKET_NOT_ALLOWED');
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// Build sign options
|
|
115
|
+
const signOptions = {
|
|
116
|
+
fop: options.fop,
|
|
117
|
+
expiry: options.expiry,
|
|
118
|
+
};
|
|
119
|
+
// Sign the URL
|
|
120
|
+
const signed = await signer.sign(asset.bucket, asset.key, signOptions);
|
|
121
|
+
return {
|
|
122
|
+
url: signed.url,
|
|
123
|
+
expiresAt: signed.expiresAt,
|
|
124
|
+
bucket: asset.bucket,
|
|
125
|
+
key: asset.key,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
// ============================================================================
|
|
129
|
+
// Batch Resolution
|
|
130
|
+
// ============================================================================
|
|
131
|
+
/**
|
|
132
|
+
* Resolve multiple assets in parallel.
|
|
133
|
+
* Uses CachedSigner for efficiency.
|
|
134
|
+
*/
|
|
135
|
+
export async function resolveAssets(assets, signer, options = {}) {
|
|
136
|
+
return Promise.all(assets.map(asset => resolveAsset(asset, signer, options)));
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=asset-resolver.js.map
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Video Frame Extraction using Qiniu vframe API.
|
|
3
|
+
*
|
|
4
|
+
* @see https://developer.qiniu.com/dora/1313/video-frame-thumbnails-vframe
|
|
5
|
+
*/
|
|
6
|
+
import type { QiniuSigner } from './signer';
|
|
7
|
+
import type { QiniuAsset, ResolveOptions } from './asset-resolver';
|
|
8
|
+
/** Vframe extraction options */
|
|
9
|
+
export interface VframeOptions {
|
|
10
|
+
/** Specific second offsets to extract frames */
|
|
11
|
+
offsets?: number[];
|
|
12
|
+
/** Number of frames to extract (uniformly distributed) */
|
|
13
|
+
count?: number;
|
|
14
|
+
/** Video duration in seconds (required for count mode) */
|
|
15
|
+
duration?: number;
|
|
16
|
+
/** Frame width (default: 640, range: 20-3840) */
|
|
17
|
+
width?: number;
|
|
18
|
+
/** Frame height (default: auto, range: 20-2160) */
|
|
19
|
+
height?: number;
|
|
20
|
+
/** Output format (default: 'jpg') */
|
|
21
|
+
format?: 'jpg' | 'png';
|
|
22
|
+
/** Rotation in degrees */
|
|
23
|
+
rotate?: 90 | 180 | 270 | 'auto';
|
|
24
|
+
}
|
|
25
|
+
/** Extracted video frame */
|
|
26
|
+
export interface VideoFrame {
|
|
27
|
+
/** Signed frame URL */
|
|
28
|
+
url: string;
|
|
29
|
+
/** URL expiration timestamp (ms) */
|
|
30
|
+
expiresAt: number;
|
|
31
|
+
/** Frame offset in seconds */
|
|
32
|
+
offset: number;
|
|
33
|
+
/** Frame index (0-based) */
|
|
34
|
+
index: number;
|
|
35
|
+
}
|
|
36
|
+
/** Frame extraction result */
|
|
37
|
+
export interface VframeResult {
|
|
38
|
+
/** Extracted frames */
|
|
39
|
+
frames: VideoFrame[];
|
|
40
|
+
/** Total frame count */
|
|
41
|
+
count: number;
|
|
42
|
+
/** Video duration (if provided) */
|
|
43
|
+
duration?: number;
|
|
44
|
+
}
|
|
45
|
+
export declare class VframeError extends Error {
|
|
46
|
+
constructor(message: string);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Build a vframe fop command.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```typescript
|
|
53
|
+
* buildVframeFop(5, { width: 320, format: 'jpg' });
|
|
54
|
+
* // 'vframe/jpg/offset/5/w/320'
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export declare function buildVframeFop(offset: number, options?: Omit<VframeOptions, 'offsets' | 'count' | 'duration'>): string;
|
|
58
|
+
/**
|
|
59
|
+
* Build a complete vframe URL from a base URL.
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```typescript
|
|
63
|
+
* buildVframeUrl('https://cdn.example.com/video.mp4', 5, { width: 320 });
|
|
64
|
+
* // 'https://cdn.example.com/video.mp4?vframe/jpg/offset/5/w/320'
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
export declare function buildVframeUrl(baseUrl: string, offset: number, options?: Omit<VframeOptions, 'offsets' | 'count' | 'duration'>): string;
|
|
68
|
+
/**
|
|
69
|
+
* Extract frames from a video.
|
|
70
|
+
*
|
|
71
|
+
* @example Uniform extraction
|
|
72
|
+
* ```typescript
|
|
73
|
+
* const result = await extractFrames(asset, signer, {
|
|
74
|
+
* count: 5,
|
|
75
|
+
* duration: 120, // 2 minute video
|
|
76
|
+
* width: 640,
|
|
77
|
+
* });
|
|
78
|
+
* // Frames at: 20s, 40s, 60s, 80s, 100s
|
|
79
|
+
* ```
|
|
80
|
+
*
|
|
81
|
+
* @example Explicit offsets
|
|
82
|
+
* ```typescript
|
|
83
|
+
* const result = await extractFrames(asset, signer, {
|
|
84
|
+
* offsets: [0, 30, 60, 90],
|
|
85
|
+
* width: 640,
|
|
86
|
+
* });
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
export declare function extractFrames(asset: QiniuAsset, signer: QiniuSigner, options: VframeOptions, resolveOptions?: ResolveOptions): Promise<VframeResult>;
|
|
90
|
+
/**
|
|
91
|
+
* Extract a single frame at a specific offset.
|
|
92
|
+
*/
|
|
93
|
+
export declare function extractFrame(asset: QiniuAsset, signer: QiniuSigner, offset: number, options?: Omit<VframeOptions, 'offsets' | 'count' | 'duration'>, resolveOptions?: ResolveOptions): Promise<VideoFrame>;
|
|
94
|
+
//# sourceMappingURL=vframe.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vframe.d.ts","sourceRoot":"","sources":["../../src/lib/vframe.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAa,MAAM,UAAU,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAOnE,gCAAgC;AAChC,MAAM,WAAW,aAAa;IAE1B,gDAAgD;IAChD,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAGnB,0DAA0D;IAC1D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAGlB,iDAAiD;IACjD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mDAAmD;IACnD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qCAAqC;IACrC,MAAM,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;IACvB,0BAA0B;IAC1B,MAAM,CAAC,EAAE,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,MAAM,CAAC;CACpC;AAED,4BAA4B;AAC5B,MAAM,WAAW,UAAU;IACvB,uBAAuB;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,oCAAoC;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,8BAA8B;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,4BAA4B;IAC5B,KAAK,EAAE,MAAM,CAAC;CACjB;AAED,8BAA8B;AAC9B,MAAM,WAAW,YAAY;IACzB,uBAAuB;IACvB,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,wBAAwB;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,mCAAmC;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AAiBD,qBAAa,WAAY,SAAQ,KAAK;gBACtB,OAAO,EAAE,MAAM;CAI9B;AAMD;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,IAAI,CAAC,aAAa,EAAE,SAAS,GAAG,OAAO,GAAG,UAAU,CAAM,GAAG,MAAM,CA8B1H;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAC1B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,IAAI,CAAC,aAAa,EAAE,SAAS,GAAG,OAAO,GAAG,UAAU,CAAM,GACpE,MAAM,CAIR;AA6BD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,aAAa,CAC/B,KAAK,EAAE,UAAU,EACjB,MAAM,EAAE,WAAW,EACnB,OAAO,EAAE,aAAa,EACtB,cAAc,CAAC,EAAE,cAAc,GAChC,OAAO,CAAC,YAAY,CAAC,CAuDvB;AAED;;GAEG;AACH,wBAAsB,YAAY,CAC9B,KAAK,EAAE,UAAU,EACjB,MAAM,EAAE,WAAW,EACnB,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,IAAI,CAAC,aAAa,EAAE,SAAS,GAAG,OAAO,GAAG,UAAU,CAAM,EACnE,cAAc,CAAC,EAAE,cAAc,GAChC,OAAO,CAAC,UAAU,CAAC,CAMrB"}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Video Frame Extraction using Qiniu vframe API.
|
|
4
|
+
*
|
|
5
|
+
* @see https://developer.qiniu.com/dora/1313/video-frame-thumbnails-vframe
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.VframeError = void 0;
|
|
9
|
+
exports.buildVframeFop = buildVframeFop;
|
|
10
|
+
exports.buildVframeUrl = buildVframeUrl;
|
|
11
|
+
exports.extractFrames = extractFrames;
|
|
12
|
+
exports.extractFrame = extractFrame;
|
|
13
|
+
const asset_resolver_1 = require("./asset-resolver");
|
|
14
|
+
// ============================================================================
|
|
15
|
+
// Constants
|
|
16
|
+
// ============================================================================
|
|
17
|
+
const DEFAULT_COUNT = 5;
|
|
18
|
+
const DEFAULT_WIDTH = 640;
|
|
19
|
+
const DEFAULT_FORMAT = 'jpg';
|
|
20
|
+
const MIN_DIMENSION = 20;
|
|
21
|
+
const MAX_LONG_EDGE = 3840;
|
|
22
|
+
const MAX_SHORT_EDGE = 2160;
|
|
23
|
+
// ============================================================================
|
|
24
|
+
// Errors
|
|
25
|
+
// ============================================================================
|
|
26
|
+
class VframeError extends Error {
|
|
27
|
+
constructor(message) {
|
|
28
|
+
super(message);
|
|
29
|
+
this.name = 'VframeError';
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
exports.VframeError = VframeError;
|
|
33
|
+
// ============================================================================
|
|
34
|
+
// URL Building
|
|
35
|
+
// ============================================================================
|
|
36
|
+
/**
|
|
37
|
+
* Build a vframe fop command.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```typescript
|
|
41
|
+
* buildVframeFop(5, { width: 320, format: 'jpg' });
|
|
42
|
+
* // 'vframe/jpg/offset/5/w/320'
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
function buildVframeFop(offset, options = {}) {
|
|
46
|
+
if (offset < 0) {
|
|
47
|
+
throw new VframeError('Offset must be non-negative');
|
|
48
|
+
}
|
|
49
|
+
const format = options.format ?? DEFAULT_FORMAT;
|
|
50
|
+
const parts = [`vframe/${format}/offset/${offset}`];
|
|
51
|
+
// Width
|
|
52
|
+
if (options.width !== undefined) {
|
|
53
|
+
if (options.width < MIN_DIMENSION || options.width > MAX_LONG_EDGE) {
|
|
54
|
+
throw new VframeError(`Width must be between ${MIN_DIMENSION} and ${MAX_LONG_EDGE}`);
|
|
55
|
+
}
|
|
56
|
+
parts.push(`w/${options.width}`);
|
|
57
|
+
}
|
|
58
|
+
// Height
|
|
59
|
+
if (options.height !== undefined) {
|
|
60
|
+
if (options.height < MIN_DIMENSION || options.height > MAX_SHORT_EDGE) {
|
|
61
|
+
throw new VframeError(`Height must be between ${MIN_DIMENSION} and ${MAX_SHORT_EDGE}`);
|
|
62
|
+
}
|
|
63
|
+
parts.push(`h/${options.height}`);
|
|
64
|
+
}
|
|
65
|
+
// Rotate
|
|
66
|
+
if (options.rotate !== undefined) {
|
|
67
|
+
parts.push(`rotate/${options.rotate}`);
|
|
68
|
+
}
|
|
69
|
+
return parts.join('/');
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Build a complete vframe URL from a base URL.
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```typescript
|
|
76
|
+
* buildVframeUrl('https://cdn.example.com/video.mp4', 5, { width: 320 });
|
|
77
|
+
* // 'https://cdn.example.com/video.mp4?vframe/jpg/offset/5/w/320'
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
function buildVframeUrl(baseUrl, offset, options = {}) {
|
|
81
|
+
const fop = buildVframeFop(offset, options);
|
|
82
|
+
const separator = baseUrl.includes('?') ? '|' : '?';
|
|
83
|
+
return `${baseUrl}${separator}${fop}`;
|
|
84
|
+
}
|
|
85
|
+
// ============================================================================
|
|
86
|
+
// Frame Extraction
|
|
87
|
+
// ============================================================================
|
|
88
|
+
/**
|
|
89
|
+
* Calculate uniform offsets for frame extraction.
|
|
90
|
+
*/
|
|
91
|
+
function calculateUniformOffsets(count, duration) {
|
|
92
|
+
if (count <= 0) {
|
|
93
|
+
throw new VframeError('Count must be positive');
|
|
94
|
+
}
|
|
95
|
+
if (duration <= 0) {
|
|
96
|
+
throw new VframeError('Duration must be positive');
|
|
97
|
+
}
|
|
98
|
+
if (count === 1) {
|
|
99
|
+
return [duration / 2];
|
|
100
|
+
}
|
|
101
|
+
const offsets = [];
|
|
102
|
+
const step = duration / (count + 1);
|
|
103
|
+
for (let i = 1; i <= count; i++) {
|
|
104
|
+
offsets.push(Math.round(step * i * 100) / 100);
|
|
105
|
+
}
|
|
106
|
+
return offsets;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Extract frames from a video.
|
|
110
|
+
*
|
|
111
|
+
* @example Uniform extraction
|
|
112
|
+
* ```typescript
|
|
113
|
+
* const result = await extractFrames(asset, signer, {
|
|
114
|
+
* count: 5,
|
|
115
|
+
* duration: 120, // 2 minute video
|
|
116
|
+
* width: 640,
|
|
117
|
+
* });
|
|
118
|
+
* // Frames at: 20s, 40s, 60s, 80s, 100s
|
|
119
|
+
* ```
|
|
120
|
+
*
|
|
121
|
+
* @example Explicit offsets
|
|
122
|
+
* ```typescript
|
|
123
|
+
* const result = await extractFrames(asset, signer, {
|
|
124
|
+
* offsets: [0, 30, 60, 90],
|
|
125
|
+
* width: 640,
|
|
126
|
+
* });
|
|
127
|
+
* ```
|
|
128
|
+
*/
|
|
129
|
+
async function extractFrames(asset, signer, options, resolveOptions) {
|
|
130
|
+
// Validate options
|
|
131
|
+
if (options.offsets && options.count) {
|
|
132
|
+
throw new VframeError('Cannot specify both offsets and count');
|
|
133
|
+
}
|
|
134
|
+
let offsets;
|
|
135
|
+
if (options.offsets) {
|
|
136
|
+
// Explicit offsets mode
|
|
137
|
+
offsets = options.offsets;
|
|
138
|
+
for (const offset of offsets) {
|
|
139
|
+
if (offset < 0) {
|
|
140
|
+
throw new VframeError('All offsets must be non-negative');
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
// Uniform extraction mode
|
|
146
|
+
const count = options.count ?? DEFAULT_COUNT;
|
|
147
|
+
if (!options.duration) {
|
|
148
|
+
throw new VframeError('Duration is required for uniform extraction');
|
|
149
|
+
}
|
|
150
|
+
offsets = calculateUniformOffsets(count, options.duration);
|
|
151
|
+
}
|
|
152
|
+
// Build frame extraction options
|
|
153
|
+
const frameOpts = {
|
|
154
|
+
width: options.width ?? DEFAULT_WIDTH,
|
|
155
|
+
height: options.height,
|
|
156
|
+
format: options.format ?? DEFAULT_FORMAT,
|
|
157
|
+
rotate: options.rotate,
|
|
158
|
+
};
|
|
159
|
+
// Extract frames in parallel
|
|
160
|
+
const frames = await Promise.all(offsets.map(async (offset, index) => {
|
|
161
|
+
const fop = buildVframeFop(offset, frameOpts);
|
|
162
|
+
const resolved = await (0, asset_resolver_1.resolveAsset)(asset, signer, {
|
|
163
|
+
...resolveOptions,
|
|
164
|
+
fop,
|
|
165
|
+
});
|
|
166
|
+
return {
|
|
167
|
+
url: resolved.url,
|
|
168
|
+
expiresAt: resolved.expiresAt,
|
|
169
|
+
offset,
|
|
170
|
+
index,
|
|
171
|
+
};
|
|
172
|
+
}));
|
|
173
|
+
return {
|
|
174
|
+
frames,
|
|
175
|
+
count: frames.length,
|
|
176
|
+
duration: options.duration,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Extract a single frame at a specific offset.
|
|
181
|
+
*/
|
|
182
|
+
async function extractFrame(asset, signer, offset, options = {}, resolveOptions) {
|
|
183
|
+
const result = await extractFrames(asset, signer, {
|
|
184
|
+
offsets: [offset],
|
|
185
|
+
...options,
|
|
186
|
+
}, resolveOptions);
|
|
187
|
+
return result.frames[0];
|
|
188
|
+
}
|
|
189
|
+
//# sourceMappingURL=vframe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vframe.js","sourceRoot":"","sources":["../../src/lib/vframe.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;;AA0FH,wCA8BC;AAWD,wCAQC;AAkDD,sCA4DC;AAKD,oCAYC;AAtQD,qDAAsE;AAmDtE,+EAA+E;AAC/E,YAAY;AACZ,+EAA+E;AAE/E,MAAM,aAAa,GAAG,CAAC,CAAC;AACxB,MAAM,aAAa,GAAG,GAAG,CAAC;AAC1B,MAAM,cAAc,GAAG,KAAK,CAAC;AAC7B,MAAM,aAAa,GAAG,EAAE,CAAC;AACzB,MAAM,aAAa,GAAG,IAAI,CAAC;AAC3B,MAAM,cAAc,GAAG,IAAI,CAAC;AAE5B,+EAA+E;AAC/E,SAAS;AACT,+EAA+E;AAE/E,MAAa,WAAY,SAAQ,KAAK;IAClC,YAAY,OAAe;QACvB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;IAC9B,CAAC;CACJ;AALD,kCAKC;AAED,+EAA+E;AAC/E,eAAe;AACf,+EAA+E;AAE/E;;;;;;;;GAQG;AACH,SAAgB,cAAc,CAAC,MAAc,EAAE,UAAiE,EAAE;IAC9G,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;QACb,MAAM,IAAI,WAAW,CAAC,6BAA6B,CAAC,CAAC;IACzD,CAAC;IAED,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,cAAc,CAAC;IAChD,MAAM,KAAK,GAAG,CAAC,UAAU,MAAM,WAAW,MAAM,EAAE,CAAC,CAAC;IAEpD,QAAQ;IACR,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QAC9B,IAAI,OAAO,CAAC,KAAK,GAAG,aAAa,IAAI,OAAO,CAAC,KAAK,GAAG,aAAa,EAAE,CAAC;YACjE,MAAM,IAAI,WAAW,CAAC,yBAAyB,aAAa,QAAQ,aAAa,EAAE,CAAC,CAAC;QACzF,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,KAAK,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;IACrC,CAAC;IAED,SAAS;IACT,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAC/B,IAAI,OAAO,CAAC,MAAM,GAAG,aAAa,IAAI,OAAO,CAAC,MAAM,GAAG,cAAc,EAAE,CAAC;YACpE,MAAM,IAAI,WAAW,CAAC,0BAA0B,aAAa,QAAQ,cAAc,EAAE,CAAC,CAAC;QAC3F,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACtC,CAAC;IAED,SAAS;IACT,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAC/B,KAAK,CAAC,IAAI,CAAC,UAAU,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC3C,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC3B,CAAC;AAED;;;;;;;;GAQG;AACH,SAAgB,cAAc,CAC1B,OAAe,EACf,MAAc,EACd,UAAiE,EAAE;IAEnE,MAAM,GAAG,GAAG,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC5C,MAAM,SAAS,GAAG,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;IACpD,OAAO,GAAG,OAAO,GAAG,SAAS,GAAG,GAAG,EAAE,CAAC;AAC1C,CAAC;AAED,+EAA+E;AAC/E,mBAAmB;AACnB,+EAA+E;AAE/E;;GAEG;AACH,SAAS,uBAAuB,CAAC,KAAa,EAAE,QAAgB;IAC5D,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;QACb,MAAM,IAAI,WAAW,CAAC,wBAAwB,CAAC,CAAC;IACpD,CAAC;IACD,IAAI,QAAQ,IAAI,CAAC,EAAE,CAAC;QAChB,MAAM,IAAI,WAAW,CAAC,2BAA2B,CAAC,CAAC;IACvD,CAAC;IAED,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;QACd,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC;IAC1B,CAAC;IAED,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,IAAI,GAAG,QAAQ,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;IACpC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;QAC9B,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC;IACnD,CAAC;IACD,OAAO,OAAO,CAAC;AACnB,CAAC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACI,KAAK,UAAU,aAAa,CAC/B,KAAiB,EACjB,MAAmB,EACnB,OAAsB,EACtB,cAA+B;IAE/B,mBAAmB;IACnB,IAAI,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;QACnC,MAAM,IAAI,WAAW,CAAC,uCAAuC,CAAC,CAAC;IACnE,CAAC;IAED,IAAI,OAAiB,CAAC;IAEtB,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QAClB,wBAAwB;QACxB,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;QAC1B,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC3B,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;gBACb,MAAM,IAAI,WAAW,CAAC,kCAAkC,CAAC,CAAC;YAC9D,CAAC;QACL,CAAC;IACL,CAAC;SAAM,CAAC;QACJ,0BAA0B;QAC1B,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,aAAa,CAAC;QAC7C,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;YACpB,MAAM,IAAI,WAAW,CAAC,6CAA6C,CAAC,CAAC;QACzE,CAAC;QACD,OAAO,GAAG,uBAAuB,CAAC,KAAK,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC/D,CAAC;IAED,iCAAiC;IACjC,MAAM,SAAS,GAAG;QACd,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,aAAa;QACrC,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,cAAc;QACxC,MAAM,EAAE,OAAO,CAAC,MAAM;KACzB,CAAC;IAEF,6BAA6B;IAC7B,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,CAC5B,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE;QAChC,MAAM,GAAG,GAAG,cAAc,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QAC9C,MAAM,QAAQ,GAAG,MAAM,IAAA,6BAAY,EAAC,KAAK,EAAE,MAAM,EAAE;YAC/C,GAAG,cAAc;YACjB,GAAG;SACN,CAAC,CAAC;QACH,OAAO;YACH,GAAG,EAAE,QAAQ,CAAC,GAAG;YACjB,SAAS,EAAE,QAAQ,CAAC,SAAS;YAC7B,MAAM;YACN,KAAK;SACR,CAAC;IACN,CAAC,CAAC,CACL,CAAC;IAEF,OAAO;QACH,MAAM;QACN,KAAK,EAAE,MAAM,CAAC,MAAM;QACpB,QAAQ,EAAE,OAAO,CAAC,QAAQ;KAC7B,CAAC;AACN,CAAC;AAED;;GAEG;AACI,KAAK,UAAU,YAAY,CAC9B,KAAiB,EACjB,MAAmB,EACnB,MAAc,EACd,UAAiE,EAAE,EACnE,cAA+B;IAE/B,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE;QAC9C,OAAO,EAAE,CAAC,MAAM,CAAC;QACjB,GAAG,OAAO;KACb,EAAE,cAAc,CAAC,CAAC;IACnB,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;AAC5B,CAAC"}
|