@x12i/youtube-video-uploader-cli 1.2.0 → 1.4.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/README.md +351 -27
- package/bin/cli.js +620 -2
- package/package.json +7 -1
- package/src/actions/index.js +276 -0
- package/src/actions/journey-overlay.js +623 -0
- package/src/actions/replace-background.js +192 -0
- package/src/actions/shorten.js +310 -0
- package/src/batch/clone-metadata.js +51 -0
- package/src/batch/plan.js +195 -0
- package/src/cli/apply-actions.js +152 -0
- package/src/cli/preview-journey.js +111 -0
- package/src/index.d.ts +532 -3
- package/src/index.js +82 -1
- package/src/media/cache.js +134 -0
- package/src/media/compose.js +300 -0
- package/src/media/download.js +151 -0
- package/src/media/resolve.js +476 -0
- package/src/media/select.js +176 -0
- package/src/providers/index.js +235 -0
- package/src/providers/pexels.js +171 -0
- package/src/providers/pixabay.js +184 -0
- package/src/state.js +11 -0
- package/src/update.js +392 -0
- package/src/uploader.js +20 -0
package/src/index.d.ts
CHANGED
|
@@ -8,11 +8,29 @@ export interface VideoMetadata {
|
|
|
8
8
|
tags: string[];
|
|
9
9
|
categoryId?: string;
|
|
10
10
|
privacyStatus?: 'private' | 'unlisted' | 'public';
|
|
11
|
+
defaultLanguage?: string;
|
|
12
|
+
defaultAudioLanguage?: string;
|
|
13
|
+
license?: string;
|
|
14
|
+
embeddable?: boolean;
|
|
15
|
+
publicStatsViewable?: boolean;
|
|
16
|
+
selfDeclaredMadeForKids?: boolean;
|
|
17
|
+
notifySubscribers?: boolean;
|
|
18
|
+
trackNumber?: number;
|
|
19
|
+
playlistPosition?: number;
|
|
11
20
|
filePath: string;
|
|
12
21
|
}
|
|
13
22
|
|
|
23
|
+
export interface PlaylistConfig {
|
|
24
|
+
key?: string;
|
|
25
|
+
snippet?: Record<string, any>;
|
|
26
|
+
status?: Record<string, any>;
|
|
27
|
+
publishStatusAfterQc?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
14
30
|
export interface MetadataConfig {
|
|
15
31
|
playlistId: string | null;
|
|
32
|
+
playlist?: PlaylistConfig | null;
|
|
33
|
+
templateVariables: Record<string, string | null>;
|
|
16
34
|
privacyStatus: 'private' | 'unlisted' | 'public';
|
|
17
35
|
categoryId: string;
|
|
18
36
|
videos: VideoMetadata[];
|
|
@@ -22,6 +40,9 @@ export interface BatchUploadOptions {
|
|
|
22
40
|
metadataFile?: string;
|
|
23
41
|
force?: boolean;
|
|
24
42
|
dryRun?: boolean;
|
|
43
|
+
limit?: number;
|
|
44
|
+
safeQuota?: boolean;
|
|
45
|
+
maxQuota?: number;
|
|
25
46
|
tokenFile?: string;
|
|
26
47
|
clientSecretsFile?: string;
|
|
27
48
|
clientId?: string;
|
|
@@ -34,19 +55,26 @@ export interface BatchUploadResult {
|
|
|
34
55
|
total: number;
|
|
35
56
|
uploaded: Array<{ filename: string; videoId: string; url: string; addedToPlaylist: boolean }>;
|
|
36
57
|
skipped: Array<{ filename: string; videoId?: string; reason: string }>;
|
|
58
|
+
deferred: Array<{ filename: string; reason: string }>;
|
|
37
59
|
failed: Array<{ filename: string; error: Error }>;
|
|
38
60
|
estimatedQuota: number;
|
|
61
|
+
playlistId?: string | null;
|
|
39
62
|
}
|
|
40
63
|
|
|
41
64
|
export declare class UploadState {
|
|
42
65
|
constructor(targetFolder?: string, stateFilename?: string);
|
|
66
|
+
listUploads(): Array<{ filename: string; videoId: string; uploadedAt: string; playlistId?: string }>;
|
|
43
67
|
getUpload(filename: string): { videoId: string; uploadedAt: string; playlistId?: string } | null;
|
|
44
68
|
recordUpload(filename: string, videoId: string, playlistId?: string | null): void;
|
|
45
69
|
recordPlaylist(filename: string, playlistId: string): void;
|
|
70
|
+
getPlaylist(key?: string): { playlistId: string; url: string; createdAt: string | null } | null;
|
|
71
|
+
recordPlaylistInfo(key: string, playlistId: string, url?: string): void;
|
|
46
72
|
save(): void;
|
|
47
73
|
}
|
|
48
74
|
|
|
49
75
|
export function loadMetadata(targetFolder?: string, customMetadataPath?: string): MetadataConfig;
|
|
76
|
+
export function interpolateTemplates(text: string, variables?: Record<string, string | null | undefined>): string;
|
|
77
|
+
|
|
50
78
|
export function authenticate(options?: {
|
|
51
79
|
clientId?: string;
|
|
52
80
|
clientSecret?: string;
|
|
@@ -67,9 +95,16 @@ export function uploadVideo(
|
|
|
67
95
|
export function addToPlaylist(
|
|
68
96
|
youtube: youtube_v3.Youtube,
|
|
69
97
|
playlistId: string,
|
|
70
|
-
videoId: string
|
|
98
|
+
videoId: string,
|
|
99
|
+
position?: number
|
|
71
100
|
): Promise<any>;
|
|
72
101
|
|
|
102
|
+
export function createPlaylist(
|
|
103
|
+
youtube: youtube_v3.Youtube,
|
|
104
|
+
snippet: Record<string, any>,
|
|
105
|
+
status?: Record<string, any>
|
|
106
|
+
): Promise<{ playlistId: string; url: string; data: any }>;
|
|
107
|
+
|
|
73
108
|
export function batchUpload(
|
|
74
109
|
targetFolder?: string,
|
|
75
110
|
options?: BatchUploadOptions
|
|
@@ -77,10 +112,13 @@ export function batchUpload(
|
|
|
77
112
|
|
|
78
113
|
export const QUOTA_PER_VIDEO_UPLOAD: number;
|
|
79
114
|
export const QUOTA_PER_PLAYLIST_ITEM: number;
|
|
115
|
+
export const QUOTA_PER_PLAYLIST_CREATE: number;
|
|
80
116
|
export const DAILY_DEFAULT_QUOTA: number;
|
|
81
117
|
export const DEFAULT_CATEGORY_ID: string;
|
|
82
118
|
export const DEFAULT_PRIVACY_STATUS: string;
|
|
83
119
|
export const VALID_PRIVACY_STATUSES: Set<string>;
|
|
120
|
+
export const DEFAULT_STATE_FILENAME: string;
|
|
121
|
+
export const YOUTUBE_SCOPES: string[];
|
|
84
122
|
|
|
85
123
|
// ─── Playlist video swap ───────────────────────────────────────────────────
|
|
86
124
|
|
|
@@ -118,7 +156,7 @@ export class SwapStore {
|
|
|
118
156
|
export function uploadNewVideo(
|
|
119
157
|
youtube: youtube_v3.Youtube,
|
|
120
158
|
newVideoPath: string,
|
|
121
|
-
opts?: { oldVideoId?: string; meta?: VideoMetadata
|
|
159
|
+
opts?: { oldVideoId?: string; meta?: Partial<VideoMetadata>; retries?: number }
|
|
122
160
|
): Promise<{ videoId: string; url: string }>;
|
|
123
161
|
|
|
124
162
|
export function findPlaylistItem(
|
|
@@ -144,7 +182,7 @@ export function swapSong(params: {
|
|
|
144
182
|
newVideoPath: string;
|
|
145
183
|
playlistId: string;
|
|
146
184
|
oldVideoId: string;
|
|
147
|
-
meta?: VideoMetadata
|
|
185
|
+
meta?: Partial<VideoMetadata>;
|
|
148
186
|
stateDir?: string;
|
|
149
187
|
stateFile?: string;
|
|
150
188
|
fresh?: boolean;
|
|
@@ -154,3 +192,494 @@ export function swapSong(params: {
|
|
|
154
192
|
export const QUOTA_PER_SWAP: number;
|
|
155
193
|
export const DEFAULT_SWAP_STATE_FILENAME: string;
|
|
156
194
|
export const SWAP_STATUSES: string[];
|
|
195
|
+
|
|
196
|
+
// ─── Metadata & Title Updates ──────────────────────────────────────────────
|
|
197
|
+
|
|
198
|
+
export interface BatchUpdateMetadataOptions {
|
|
199
|
+
titlePostfix?: string;
|
|
200
|
+
titlePrefix?: string;
|
|
201
|
+
playlistPostfix?: string;
|
|
202
|
+
playlistTitle?: string;
|
|
203
|
+
metadataFile?: string;
|
|
204
|
+
updateLocalMetadata?: boolean;
|
|
205
|
+
syncMetadata?: boolean;
|
|
206
|
+
singleVideoId?: string;
|
|
207
|
+
singleVideoTitle?: string;
|
|
208
|
+
singlePlaylistId?: string;
|
|
209
|
+
dryRun?: boolean;
|
|
210
|
+
tokenFile?: string;
|
|
211
|
+
clientSecretsFile?: string;
|
|
212
|
+
clientId?: string;
|
|
213
|
+
clientSecret?: string;
|
|
214
|
+
onEvent?: (event: { type: string; [key: string]: any }) => void;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export interface VideoPlanItem {
|
|
218
|
+
filename: string;
|
|
219
|
+
videoId: string;
|
|
220
|
+
oldTitle: string;
|
|
221
|
+
newTitle: string;
|
|
222
|
+
changed: boolean;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export interface PlaylistPlanItem {
|
|
226
|
+
playlistId: string;
|
|
227
|
+
oldTitle: string;
|
|
228
|
+
newTitle: string;
|
|
229
|
+
changed: boolean;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export interface BatchUpdateMetadataResult {
|
|
233
|
+
playlist?: PlaylistPlanItem | null;
|
|
234
|
+
videos: VideoPlanItem[];
|
|
235
|
+
estimatedQuota: number;
|
|
236
|
+
dryRun: boolean;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export function updateVideoSnippet(
|
|
240
|
+
youtube: youtube_v3.Youtube,
|
|
241
|
+
videoId: string,
|
|
242
|
+
snippetUpdates: Record<string, any>
|
|
243
|
+
): Promise<{ videoId: string; title: string; data: any }>;
|
|
244
|
+
|
|
245
|
+
export function updateVideoTitle(
|
|
246
|
+
youtube: youtube_v3.Youtube,
|
|
247
|
+
videoId: string,
|
|
248
|
+
newTitle: string
|
|
249
|
+
): Promise<{ videoId: string; title: string; data: any }>;
|
|
250
|
+
|
|
251
|
+
export function updatePlaylistSnippet(
|
|
252
|
+
youtube: youtube_v3.Youtube,
|
|
253
|
+
playlistId: string,
|
|
254
|
+
snippetUpdates: Record<string, any>
|
|
255
|
+
): Promise<{ playlistId: string; title: string; data: any }>;
|
|
256
|
+
|
|
257
|
+
export function updatePlaylistTitle(
|
|
258
|
+
youtube: youtube_v3.Youtube,
|
|
259
|
+
playlistId: string,
|
|
260
|
+
newTitle: string
|
|
261
|
+
): Promise<{ playlistId: string; title: string; data: any }>;
|
|
262
|
+
|
|
263
|
+
export function batchUpdateMetadata(
|
|
264
|
+
targetFolder?: string,
|
|
265
|
+
options?: BatchUpdateMetadataOptions
|
|
266
|
+
): Promise<BatchUpdateMetadataResult>;
|
|
267
|
+
|
|
268
|
+
export const QUOTA_PER_VIDEO_UPDATE: number;
|
|
269
|
+
export const QUOTA_PER_PLAYLIST_UPDATE: number;
|
|
270
|
+
export const QUOTA_PER_LIST: number;
|
|
271
|
+
|
|
272
|
+
// ─── Automated Stock Media Sourcing (Pexels + Pixabay) ──────────────────────
|
|
273
|
+
|
|
274
|
+
export interface UnifiedResult {
|
|
275
|
+
provider: 'pexels' | 'pixabay';
|
|
276
|
+
id: string;
|
|
277
|
+
type: 'video' | 'photo';
|
|
278
|
+
rank: number;
|
|
279
|
+
pageUrl: string;
|
|
280
|
+
previewUrl: string;
|
|
281
|
+
downloadUrl: string;
|
|
282
|
+
width: number;
|
|
283
|
+
height: number;
|
|
284
|
+
duration?: number;
|
|
285
|
+
orientation: 'landscape' | 'portrait' | 'square';
|
|
286
|
+
tags: string[];
|
|
287
|
+
creator: { name: string; profileUrl?: string };
|
|
288
|
+
license: 'Pexels License' | 'Pixabay Content License';
|
|
289
|
+
raw: unknown;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export interface MediaSearchOptions {
|
|
293
|
+
query: string;
|
|
294
|
+
type?: 'video' | 'photo';
|
|
295
|
+
provider?: 'auto' | 'pexels' | 'pixabay';
|
|
296
|
+
mergeStrategy?: 'fallback' | 'merge';
|
|
297
|
+
providerOrder?: Array<'pexels' | 'pixabay'>;
|
|
298
|
+
orientation?: 'landscape' | 'portrait' | 'square';
|
|
299
|
+
page?: number;
|
|
300
|
+
perPage?: number;
|
|
301
|
+
safeSearch?: boolean;
|
|
302
|
+
pexelsKey?: string;
|
|
303
|
+
pixabayKey?: string;
|
|
304
|
+
useCache?: boolean;
|
|
305
|
+
cacheDir?: string;
|
|
306
|
+
fetchFn?: typeof fetch;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
export interface CandidateCriteria {
|
|
310
|
+
preferredWidth?: number;
|
|
311
|
+
preferredHeight?: number;
|
|
312
|
+
minWidth?: number;
|
|
313
|
+
minHeight?: number;
|
|
314
|
+
orientation?: 'landscape' | 'portrait' | 'square';
|
|
315
|
+
targetDuration?: number;
|
|
316
|
+
minDuration?: number;
|
|
317
|
+
maxDuration?: number;
|
|
318
|
+
avoidReuse?: boolean;
|
|
319
|
+
cacheDir?: string;
|
|
320
|
+
usedAssetSet?: Set<string>;
|
|
321
|
+
providerOrder?: Array<'pexels' | 'pixabay'>;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
export interface CandidateScore {
|
|
325
|
+
score: number;
|
|
326
|
+
breakdown: {
|
|
327
|
+
rank: number;
|
|
328
|
+
resolution: number;
|
|
329
|
+
orientation: number;
|
|
330
|
+
duration: number;
|
|
331
|
+
novelty: number;
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
export interface RankedCandidate {
|
|
336
|
+
candidate: UnifiedResult;
|
|
337
|
+
score: number;
|
|
338
|
+
breakdown: CandidateScore['breakdown'];
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
export interface ComposeVideoOptions {
|
|
342
|
+
backgroundPath: string;
|
|
343
|
+
audioPath: string;
|
|
344
|
+
outputPath: string;
|
|
345
|
+
mode?: 'video-loop' | 'video-trim' | 'ken-burns';
|
|
346
|
+
targetWidth?: number;
|
|
347
|
+
targetHeight?: number;
|
|
348
|
+
fps?: number;
|
|
349
|
+
dryRun?: boolean;
|
|
350
|
+
onProgress?: (progress: { percent?: number; timemark?: string }) => void;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
export interface ResolveMediaOptions {
|
|
354
|
+
compose?: boolean;
|
|
355
|
+
force?: boolean;
|
|
356
|
+
dryRun?: boolean;
|
|
357
|
+
allowRepeatMedia?: boolean;
|
|
358
|
+
provider?: 'auto' | 'pexels' | 'pixabay';
|
|
359
|
+
type?: 'video' | 'photo';
|
|
360
|
+
orientation?: 'landscape' | 'portrait' | 'square';
|
|
361
|
+
cacheDir?: string;
|
|
362
|
+
pexelsKey?: string;
|
|
363
|
+
pixabayKey?: string;
|
|
364
|
+
autoMedia?: boolean;
|
|
365
|
+
onEvent?: (event: { type: string; [key: string]: any }) => void;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
export interface ResolvedMediaItem {
|
|
369
|
+
songKey: string;
|
|
370
|
+
resolvedMedia: {
|
|
371
|
+
provider: 'pexels' | 'pixabay';
|
|
372
|
+
assetId: string;
|
|
373
|
+
type: 'video' | 'photo';
|
|
374
|
+
pageUrl: string;
|
|
375
|
+
localPath: string;
|
|
376
|
+
width: number;
|
|
377
|
+
height: number;
|
|
378
|
+
duration?: number;
|
|
379
|
+
license: string;
|
|
380
|
+
credit: string;
|
|
381
|
+
resolvedAt: string;
|
|
382
|
+
};
|
|
383
|
+
generatedVideoPath?: string;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
export interface ResolveMediaResult {
|
|
387
|
+
resolved: ResolvedMediaItem[];
|
|
388
|
+
skipped: Array<{ songKey: string; reason: string }>;
|
|
389
|
+
errors: Array<{ songKey: string; error: string }>;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
export function searchPexels(options: {
|
|
393
|
+
query: string;
|
|
394
|
+
type?: 'video' | 'photo';
|
|
395
|
+
orientation?: 'landscape' | 'portrait' | 'square';
|
|
396
|
+
page?: number;
|
|
397
|
+
perPage?: number;
|
|
398
|
+
apiKey: string;
|
|
399
|
+
fetchFn?: typeof fetch;
|
|
400
|
+
}): Promise<UnifiedResult[]>;
|
|
401
|
+
|
|
402
|
+
export function searchPixabay(options: {
|
|
403
|
+
query: string;
|
|
404
|
+
type?: 'video' | 'photo';
|
|
405
|
+
orientation?: 'landscape' | 'portrait' | 'square';
|
|
406
|
+
page?: number;
|
|
407
|
+
perPage?: number;
|
|
408
|
+
safeSearch?: boolean;
|
|
409
|
+
apiKey: string;
|
|
410
|
+
fetchFn?: typeof fetch;
|
|
411
|
+
}): Promise<UnifiedResult[]>;
|
|
412
|
+
|
|
413
|
+
export function searchMedia(options: MediaSearchOptions): Promise<UnifiedResult[]>;
|
|
414
|
+
|
|
415
|
+
export function resolveProviderCredentials(options?: {
|
|
416
|
+
pexelsKey?: string;
|
|
417
|
+
pixabayKey?: string;
|
|
418
|
+
providerOrder?: Array<'pexels' | 'pixabay'>;
|
|
419
|
+
}): {
|
|
420
|
+
pexelsKey?: string;
|
|
421
|
+
pixabayKey?: string;
|
|
422
|
+
providerOrder: Array<'pexels' | 'pixabay'>;
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
export const DEFAULT_MEDIA_CACHE_DIR: string;
|
|
426
|
+
export const CACHE_TTL_MS: number;
|
|
427
|
+
|
|
428
|
+
export function createSearchCacheKey(params: {
|
|
429
|
+
provider: string;
|
|
430
|
+
type: string;
|
|
431
|
+
query: string;
|
|
432
|
+
orientation?: string;
|
|
433
|
+
}): string;
|
|
434
|
+
|
|
435
|
+
export function getCachedSearch(cacheDir: string, cacheKey: string, ttlMs?: number): any[] | null;
|
|
436
|
+
export function setCachedSearch(cacheDir: string, cacheKey: string, results: any[]): void;
|
|
437
|
+
export function readUsedAssetsLedger(cacheDir?: string): Record<string, { provider: string; assetId: string; usedAt: string; songKey?: string }>;
|
|
438
|
+
export function isAssetUsed(cacheDir: string, provider: string, assetId: string): boolean;
|
|
439
|
+
export function recordUsedAsset(cacheDir: string, assetInfo: { provider: string; assetId: string; songKey?: string }): void;
|
|
440
|
+
|
|
441
|
+
export function scoreCandidate(candidate: UnifiedResult, criteria?: CandidateCriteria): CandidateScore;
|
|
442
|
+
export function rankCandidates(results?: UnifiedResult[], criteria?: CandidateCriteria): RankedCandidate[];
|
|
443
|
+
export function selectBest(results?: UnifiedResult[], criteria?: CandidateCriteria): UnifiedResult | null;
|
|
444
|
+
|
|
445
|
+
export function inferExtension(contentType?: string, url?: string, type?: 'video' | 'photo'): string;
|
|
446
|
+
export function downloadAsset(
|
|
447
|
+
asset: UnifiedResult,
|
|
448
|
+
options?: {
|
|
449
|
+
cacheDir?: string;
|
|
450
|
+
targetPath?: string;
|
|
451
|
+
force?: boolean;
|
|
452
|
+
maxRetries?: number;
|
|
453
|
+
fetchFn?: typeof fetch;
|
|
454
|
+
}
|
|
455
|
+
): Promise<{ localPath: string; size: number; ext: string; fromCache: boolean }>;
|
|
456
|
+
|
|
457
|
+
export function getMediaDuration(filePath: string): Promise<number>;
|
|
458
|
+
export function composeVideo(options: ComposeVideoOptions): Promise<{ outputPath: string; duration: number; dryRun?: boolean; command?: string }>;
|
|
459
|
+
|
|
460
|
+
export function deriveSearchQuery(item?: {
|
|
461
|
+
title?: string;
|
|
462
|
+
tags?: string[];
|
|
463
|
+
artist?: string;
|
|
464
|
+
album?: { title?: string };
|
|
465
|
+
filename?: string;
|
|
466
|
+
}): string;
|
|
467
|
+
|
|
468
|
+
export function formatMediaCredit(asset: UnifiedResult): string;
|
|
469
|
+
export function writeAttributionsFile(targetDir: string, resolvedItems: Array<{ songKey: string; resolvedMedia: object }>): void;
|
|
470
|
+
export function resolveMediaForMetadata(metadataFilePathOrDir: string, options?: ResolveMediaOptions): Promise<ResolveMediaResult>;
|
|
471
|
+
|
|
472
|
+
// ─── Batch Video Actions Pipeline ──────────────────────────────────────────
|
|
473
|
+
|
|
474
|
+
export interface ShortenAction {
|
|
475
|
+
type: 'shorten';
|
|
476
|
+
cutPoint: string | number;
|
|
477
|
+
fadeDuration?: number;
|
|
478
|
+
paddingDuration?: number;
|
|
479
|
+
paddingFill?: 'black' | 'freeze-frame';
|
|
480
|
+
fadeTargets?: Array<'audio' | 'video'>;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
export interface ReplaceBackgroundAction {
|
|
484
|
+
type: 'replace-background';
|
|
485
|
+
query?: string;
|
|
486
|
+
provider?: 'pexels' | 'pixabay' | 'auto';
|
|
487
|
+
orientation?: 'landscape' | 'portrait' | 'square';
|
|
488
|
+
mode?: 'video-loop' | 'video-trim' | 'ken-burns';
|
|
489
|
+
mediaType?: 'video' | 'photo';
|
|
490
|
+
avoidReuse?: boolean;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
export interface JourneyOverlayAction {
|
|
494
|
+
type: 'journey-overlay';
|
|
495
|
+
centerText?: string;
|
|
496
|
+
centerConcepts?: string[];
|
|
497
|
+
conceptsDisplayMode?: 'intro' | 'full';
|
|
498
|
+
conceptDisplaySeconds?: number;
|
|
499
|
+
conceptFadeSeconds?: number;
|
|
500
|
+
showSidebar?: boolean;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
export type VideoAction = ShortenAction | ReplaceBackgroundAction | JourneyOverlayAction | { type: string; [key: string]: any };
|
|
504
|
+
|
|
505
|
+
export interface JourneyConfig {
|
|
506
|
+
enabled?: boolean;
|
|
507
|
+
mode?: 'before-current' | 'all';
|
|
508
|
+
position?: 'left' | 'right';
|
|
509
|
+
style?: 'badge' | 'panel';
|
|
510
|
+
badgeText?: string;
|
|
511
|
+
displayDuration?: number | 'full';
|
|
512
|
+
sidebarDisplayDuration?: number | 'full';
|
|
513
|
+
sidebarLabel?: 'title' | 'stage' | 'both';
|
|
514
|
+
theme?: 'dark' | 'light';
|
|
515
|
+
maxCenterChars?: number;
|
|
516
|
+
maxVisibleRows?: number;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
export interface PlannedEntry {
|
|
520
|
+
index: number;
|
|
521
|
+
songKey: string;
|
|
522
|
+
filename: string;
|
|
523
|
+
title: string;
|
|
524
|
+
sourcePath: string;
|
|
525
|
+
targetPath: string;
|
|
526
|
+
actions: VideoAction[];
|
|
527
|
+
rawEntry?: any;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
export interface BatchActionsPlan {
|
|
531
|
+
sourceDir: string;
|
|
532
|
+
outputDir: string;
|
|
533
|
+
hasShorten: boolean;
|
|
534
|
+
rawMetadata: any;
|
|
535
|
+
journeyConfig: JourneyConfig;
|
|
536
|
+
entries: PlannedEntry[];
|
|
537
|
+
dryRun: boolean;
|
|
538
|
+
force: boolean;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
export interface BatchApplyActionsOptions {
|
|
542
|
+
metadataFile?: string;
|
|
543
|
+
actionsFile?: string;
|
|
544
|
+
outputDir?: string;
|
|
545
|
+
outputSuffix?: string;
|
|
546
|
+
only?: string;
|
|
547
|
+
force?: boolean;
|
|
548
|
+
dryRun?: boolean;
|
|
549
|
+
pexelsKey?: string;
|
|
550
|
+
pixabayKey?: string;
|
|
551
|
+
cacheDir?: string;
|
|
552
|
+
fetchFn?: typeof fetch;
|
|
553
|
+
onEvent?: (event: { type: string; [key: string]: any }) => void;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
export interface BatchApplyActionsResult {
|
|
557
|
+
sourceDir: string;
|
|
558
|
+
outputDir: string;
|
|
559
|
+
total: number;
|
|
560
|
+
processed: Array<{
|
|
561
|
+
songKey: string;
|
|
562
|
+
filename: string;
|
|
563
|
+
outputPath: string;
|
|
564
|
+
actionsApplied: string[];
|
|
565
|
+
steps?: string[];
|
|
566
|
+
}>;
|
|
567
|
+
skipped: Array<{
|
|
568
|
+
songKey: string;
|
|
569
|
+
filename: string;
|
|
570
|
+
reason?: string;
|
|
571
|
+
}>;
|
|
572
|
+
failed: Array<{
|
|
573
|
+
songKey: string;
|
|
574
|
+
filename: string;
|
|
575
|
+
error: Error;
|
|
576
|
+
}>;
|
|
577
|
+
dryRun: boolean;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
export function parseTimeSeconds(time: string | number): number;
|
|
581
|
+
export function getVideoMetadata(filePath: string): Promise<{ duration: number; width: number; height: number; fps: number; sampleRate: number }>;
|
|
582
|
+
export function shortenVideo(params: {
|
|
583
|
+
inputPath: string;
|
|
584
|
+
outputPath: string;
|
|
585
|
+
cutPoint: string | number;
|
|
586
|
+
fadeDuration?: number;
|
|
587
|
+
paddingDuration?: number;
|
|
588
|
+
paddingFill?: 'black' | 'freeze-frame';
|
|
589
|
+
fadeTargets?: Array<'audio' | 'video'>;
|
|
590
|
+
dryRun?: boolean;
|
|
591
|
+
onWarn?: (msg: string) => void;
|
|
592
|
+
}): Promise<{ outputPath: string; duration: number; dryRun?: boolean; command?: string }>;
|
|
593
|
+
|
|
594
|
+
export function extractAudio(inputVideoPath: string, outputAudioPath: string): Promise<string>;
|
|
595
|
+
export function replaceBackground(params: {
|
|
596
|
+
inputPath: string;
|
|
597
|
+
outputPath: string;
|
|
598
|
+
action?: Partial<ReplaceBackgroundAction>;
|
|
599
|
+
entry?: any;
|
|
600
|
+
context?: any;
|
|
601
|
+
dryRun?: boolean;
|
|
602
|
+
}): Promise<{ outputPath: string; duration?: number; dryRun?: boolean; command?: string; asset?: any }>;
|
|
603
|
+
|
|
604
|
+
export function resolveJourneyTracks(params: {
|
|
605
|
+
currentSongKey: string;
|
|
606
|
+
rawMetadata?: any;
|
|
607
|
+
mode?: 'before-current' | 'all';
|
|
608
|
+
maxVisibleRows?: number;
|
|
609
|
+
sidebarLabel?: 'title' | 'stage' | 'both';
|
|
610
|
+
}): Array<{ index: number; title: string; isCurrent: boolean; isUpcoming: boolean; isOverflow?: boolean }>;
|
|
611
|
+
|
|
612
|
+
export function generateJourneySvg(params: {
|
|
613
|
+
width?: number;
|
|
614
|
+
height?: number;
|
|
615
|
+
tracks?: Array<{ index: number; title: string; isCurrent: boolean; isUpcoming: boolean; isOverflow?: boolean }>;
|
|
616
|
+
centerText?: string;
|
|
617
|
+
showSidebar?: boolean;
|
|
618
|
+
theme?: 'dark' | 'light';
|
|
619
|
+
position?: 'left' | 'right';
|
|
620
|
+
style?: 'badge' | 'panel';
|
|
621
|
+
stage?: string;
|
|
622
|
+
step?: number;
|
|
623
|
+
totalSteps?: number;
|
|
624
|
+
badgeText?: string;
|
|
625
|
+
}): string;
|
|
626
|
+
|
|
627
|
+
export function renderSvgToPng(svgContent: string, width?: number, height?: number): Buffer;
|
|
628
|
+
|
|
629
|
+
export function applyJourneyOverlay(params: {
|
|
630
|
+
inputPath: string;
|
|
631
|
+
outputPath: string;
|
|
632
|
+
action?: Partial<JourneyOverlayAction>;
|
|
633
|
+
entry?: any;
|
|
634
|
+
rawMetadata?: any;
|
|
635
|
+
journeyConfig?: JourneyConfig;
|
|
636
|
+
dryRun?: boolean;
|
|
637
|
+
onWarn?: (msg: string) => void;
|
|
638
|
+
}): Promise<{ outputPath: string; duration?: number; dryRun?: boolean; command?: string }>;
|
|
639
|
+
|
|
640
|
+
export function runActionsPipeline(
|
|
641
|
+
entry: PlannedEntry | { songKey?: string; filename: string; sourcePath: string; targetPath?: string },
|
|
642
|
+
actions?: VideoAction[],
|
|
643
|
+
context?: {
|
|
644
|
+
outputDir?: string;
|
|
645
|
+
rawMetadata?: any;
|
|
646
|
+
journeyConfig?: JourneyConfig;
|
|
647
|
+
dryRun?: boolean;
|
|
648
|
+
force?: boolean;
|
|
649
|
+
pexelsKey?: string;
|
|
650
|
+
pixabayKey?: string;
|
|
651
|
+
cacheDir?: string;
|
|
652
|
+
fetchFn?: typeof fetch;
|
|
653
|
+
onEvent?: (event: any) => void;
|
|
654
|
+
onWarn?: (msg: string) => void;
|
|
655
|
+
}
|
|
656
|
+
): Promise<{
|
|
657
|
+
songKey: string;
|
|
658
|
+
filename: string;
|
|
659
|
+
outputPath: string;
|
|
660
|
+
actionsApplied: string[];
|
|
661
|
+
skipped?: boolean;
|
|
662
|
+
reason?: string;
|
|
663
|
+
dryRun?: boolean;
|
|
664
|
+
steps?: string[];
|
|
665
|
+
}>;
|
|
666
|
+
|
|
667
|
+
export function resolveOutputDirectory(sourceDir: string, options?: { outputDir?: string; outputSuffix?: string; hasShorten?: boolean }): string;
|
|
668
|
+
export function resolveEntryActions(params: { songKey: string; filename: string; rawEntry?: any; rawMetadata?: any; actionsFileConfig?: any }): VideoAction[];
|
|
669
|
+
export function planBatchActions(sourceDir: string, options?: BatchApplyActionsOptions): BatchActionsPlan;
|
|
670
|
+
export function cloneMetadataForOutput(rawMetadata: any, outputDir: string, results?: Array<any>): any;
|
|
671
|
+
export function batchApplyActions(targetDir?: string, options?: BatchApplyActionsOptions): Promise<BatchApplyActionsResult>;
|
|
672
|
+
|
|
673
|
+
export function previewJourney(
|
|
674
|
+
songKey: string,
|
|
675
|
+
options?: {
|
|
676
|
+
dir?: string;
|
|
677
|
+
metadata?: string;
|
|
678
|
+
centerText?: string;
|
|
679
|
+
output?: string;
|
|
680
|
+
width?: number;
|
|
681
|
+
height?: number;
|
|
682
|
+
}
|
|
683
|
+
): Promise<{ outputPath: string; width: number; height: number; tracksCount: number }>;
|
|
684
|
+
|
|
685
|
+
|
package/src/index.js
CHANGED
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
export { authenticate, resolveOAuthCredentials, YOUTUBE_SCOPES } from './auth.js';
|
|
2
|
-
export {
|
|
2
|
+
export {
|
|
3
|
+
loadMetadata,
|
|
4
|
+
interpolateTemplates,
|
|
5
|
+
DEFAULT_CATEGORY_ID,
|
|
6
|
+
DEFAULT_PRIVACY_STATUS,
|
|
7
|
+
VALID_PRIVACY_STATUSES,
|
|
8
|
+
} from './metadata.js';
|
|
3
9
|
export { UploadState, DEFAULT_STATE_FILENAME } from './state.js';
|
|
4
10
|
export {
|
|
5
11
|
uploadVideo,
|
|
6
12
|
addToPlaylist,
|
|
13
|
+
createPlaylist,
|
|
7
14
|
batchUpload,
|
|
8
15
|
QUOTA_PER_VIDEO_UPLOAD,
|
|
9
16
|
QUOTA_PER_PLAYLIST_ITEM,
|
|
17
|
+
QUOTA_PER_PLAYLIST_CREATE,
|
|
10
18
|
DAILY_DEFAULT_QUOTA,
|
|
11
19
|
} from './uploader.js';
|
|
12
20
|
export { SwapStore, DEFAULT_SWAP_STATE_FILENAME, SWAP_STATUSES } from './swap/store.js';
|
|
@@ -18,3 +26,76 @@ export {
|
|
|
18
26
|
finalizeSwap,
|
|
19
27
|
} from './swap/steps.js';
|
|
20
28
|
export { swapSong, QUOTA_PER_SWAP } from './swap/swapSong.js';
|
|
29
|
+
export {
|
|
30
|
+
updateVideoSnippet,
|
|
31
|
+
updateVideoTitle,
|
|
32
|
+
updatePlaylistSnippet,
|
|
33
|
+
updatePlaylistTitle,
|
|
34
|
+
batchUpdateMetadata,
|
|
35
|
+
QUOTA_PER_VIDEO_UPDATE,
|
|
36
|
+
QUOTA_PER_PLAYLIST_UPDATE,
|
|
37
|
+
QUOTA_PER_LIST,
|
|
38
|
+
} from './update.js';
|
|
39
|
+
export {
|
|
40
|
+
searchPexels,
|
|
41
|
+
searchPixabay,
|
|
42
|
+
searchMedia,
|
|
43
|
+
resolveProviderCredentials,
|
|
44
|
+
} from './providers/index.js';
|
|
45
|
+
export {
|
|
46
|
+
DEFAULT_MEDIA_CACHE_DIR,
|
|
47
|
+
CACHE_TTL_MS,
|
|
48
|
+
createSearchCacheKey,
|
|
49
|
+
getCachedSearch,
|
|
50
|
+
setCachedSearch,
|
|
51
|
+
readUsedAssetsLedger,
|
|
52
|
+
isAssetUsed,
|
|
53
|
+
recordUsedAsset,
|
|
54
|
+
} from './media/cache.js';
|
|
55
|
+
export {
|
|
56
|
+
scoreCandidate,
|
|
57
|
+
rankCandidates,
|
|
58
|
+
selectBest,
|
|
59
|
+
} from './media/select.js';
|
|
60
|
+
export {
|
|
61
|
+
downloadAsset,
|
|
62
|
+
inferExtension,
|
|
63
|
+
} from './media/download.js';
|
|
64
|
+
export {
|
|
65
|
+
composeVideo,
|
|
66
|
+
getMediaDuration,
|
|
67
|
+
} from './media/compose.js';
|
|
68
|
+
export {
|
|
69
|
+
deriveSearchQuery,
|
|
70
|
+
formatMediaCredit,
|
|
71
|
+
writeAttributionsFile,
|
|
72
|
+
resolveMediaForMetadata,
|
|
73
|
+
} from './media/resolve.js';
|
|
74
|
+
export {
|
|
75
|
+
shortenVideo,
|
|
76
|
+
parseTimeSeconds,
|
|
77
|
+
getVideoMetadata,
|
|
78
|
+
replaceBackground,
|
|
79
|
+
extractAudio,
|
|
80
|
+
applyJourneyOverlay,
|
|
81
|
+
generateJourneySvg,
|
|
82
|
+
renderSvgToPng,
|
|
83
|
+
resolveJourneyTracks,
|
|
84
|
+
runActionsPipeline,
|
|
85
|
+
} from './actions/index.js';
|
|
86
|
+
export {
|
|
87
|
+
resolveOutputDirectory,
|
|
88
|
+
resolveEntryActions,
|
|
89
|
+
planBatchActions,
|
|
90
|
+
} from './batch/plan.js';
|
|
91
|
+
export {
|
|
92
|
+
cloneMetadataForOutput,
|
|
93
|
+
} from './batch/clone-metadata.js';
|
|
94
|
+
export {
|
|
95
|
+
batchApplyActions,
|
|
96
|
+
} from './cli/apply-actions.js';
|
|
97
|
+
export {
|
|
98
|
+
previewJourney,
|
|
99
|
+
} from './cli/preview-journey.js';
|
|
100
|
+
|
|
101
|
+
|