@x12i/youtube-video-uploader-cli 1.3.0 → 1.6.1
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 +76 -5
- package/bin/cli.js +344 -6
- package/package.json +5 -3
- package/src/access/privacy.js +113 -0
- package/src/access/set-access.js +247 -0
- package/src/actions/index.js +33 -0
- package/src/actions/journey-overlay.js +117 -61
- package/src/actions/remove-watermark.js +109 -0
- package/src/actions/replace-background.js +42 -4
- package/src/actions/shorten.js +49 -10
- package/src/batch/plan.js +4 -3
- package/src/cli/preview-journey.js +9 -4
- package/src/cli/remove-watermark.js +252 -0
- package/src/index.d.ts +156 -3
- package/src/index.js +42 -0
- package/src/media/compose.js +85 -12
- package/src/playlist/detect-outdated.js +50 -0
- package/src/playlist/order.js +57 -0
- package/src/playlist/reorder.js +90 -0
- package/src/playlist/sync.js +192 -0
- package/src/providers/watermark/index.js +113 -0
- package/src/providers/watermark/pilio.js +201 -0
- package/src/swap/steps.js +17 -12
- package/src/swap/swapSong.js +40 -2
- package/src/update.js +165 -25
|
@@ -38,11 +38,12 @@ export async function previewJourney(songKey, options = {}) {
|
|
|
38
38
|
const journeyConfig = {
|
|
39
39
|
enabled: true,
|
|
40
40
|
mode: 'before-current',
|
|
41
|
-
position: '
|
|
42
|
-
displayDuration:
|
|
41
|
+
position: 'right',
|
|
42
|
+
displayDuration: 4,
|
|
43
43
|
sidebarDisplayDuration: undefined,
|
|
44
|
-
sidebarLabel: '
|
|
44
|
+
sidebarLabel: 'stage',
|
|
45
45
|
theme: 'dark',
|
|
46
|
+
style: 'badge',
|
|
46
47
|
maxCenterChars: 60,
|
|
47
48
|
maxVisibleRows: 8,
|
|
48
49
|
...(rawMetadata.journey || {}),
|
|
@@ -71,7 +72,7 @@ export async function previewJourney(songKey, options = {}) {
|
|
|
71
72
|
currentSongKey: songKey,
|
|
72
73
|
rawMetadata,
|
|
73
74
|
mode: journeyConfig.mode,
|
|
74
|
-
maxVisibleRows: journeyConfig.maxVisibleRows,
|
|
75
|
+
maxVisibleRows: journeyConfig.style === 'panel' ? journeyConfig.maxVisibleRows : null,
|
|
75
76
|
sidebarLabel: journeyConfig.sidebarLabel,
|
|
76
77
|
});
|
|
77
78
|
|
|
@@ -86,6 +87,10 @@ export async function previewJourney(songKey, options = {}) {
|
|
|
86
87
|
showSidebar: journeyConfig.enabled !== false,
|
|
87
88
|
theme: journeyConfig.theme,
|
|
88
89
|
position: journeyConfig.position,
|
|
90
|
+
style: journeyConfig.style,
|
|
91
|
+
stage: songData.journeyStage || songData.albumRole,
|
|
92
|
+
step: typeof songData.trackNumber === 'number' ? songData.trackNumber : (songData.playlistPosition !== undefined ? songData.playlistPosition + 1 : undefined),
|
|
93
|
+
totalSteps: rawMetadata.album?.trackCount || rawMetadata.album?.arc?.length || (tracks[0]?.totalTracks) || 11,
|
|
89
94
|
});
|
|
90
95
|
|
|
91
96
|
const pngBuffer = renderSvgToPng(svgContent, width, height);
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import {
|
|
4
|
+
removeWatermark,
|
|
5
|
+
isWatermarkSupportedMedia,
|
|
6
|
+
defaultCleanOutputPath,
|
|
7
|
+
defaultCleanOutDir,
|
|
8
|
+
} from '../actions/remove-watermark.js';
|
|
9
|
+
import {
|
|
10
|
+
DEFAULT_WATERMARK_PROVIDER,
|
|
11
|
+
listWatermarkProviders,
|
|
12
|
+
} from '../providers/watermark/index.js';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Batch / single-file watermark removal without metadata.json.
|
|
16
|
+
*
|
|
17
|
+
* @param {string} input - File or directory path
|
|
18
|
+
* @param {object} [options]
|
|
19
|
+
* @param {string} [options.output] - Explicit output file (file input only)
|
|
20
|
+
* @param {string} [options.outDir] - Output directory (directory input, or optional for file)
|
|
21
|
+
* @param {string} [options.provider]
|
|
22
|
+
* @param {boolean} [options.overwrite=false]
|
|
23
|
+
* @param {boolean} [options.json=false]
|
|
24
|
+
* @param {number} [options.videoBitrateMbps]
|
|
25
|
+
* @param {boolean} [options.dryRun=false]
|
|
26
|
+
* @param {(event: object) => void} [options.onEvent]
|
|
27
|
+
* @returns {Promise<{
|
|
28
|
+
* input: string,
|
|
29
|
+
* mode: 'file' | 'directory',
|
|
30
|
+
* provider: string,
|
|
31
|
+
* dryRun: boolean,
|
|
32
|
+
* processed: Array<object>,
|
|
33
|
+
* skipped: Array<object>,
|
|
34
|
+
* failed: Array<object>
|
|
35
|
+
* }>}
|
|
36
|
+
*/
|
|
37
|
+
export async function batchRemoveWatermark(input, options = {}) {
|
|
38
|
+
const {
|
|
39
|
+
output,
|
|
40
|
+
outDir,
|
|
41
|
+
provider = DEFAULT_WATERMARK_PROVIDER,
|
|
42
|
+
overwrite = false,
|
|
43
|
+
json = false,
|
|
44
|
+
videoBitrateMbps,
|
|
45
|
+
dryRun = false,
|
|
46
|
+
onEvent,
|
|
47
|
+
} = options;
|
|
48
|
+
|
|
49
|
+
const resolvedInput = path.resolve(input || '.');
|
|
50
|
+
if (!fs.existsSync(resolvedInput)) {
|
|
51
|
+
throw new Error(`Input path not found: ${resolvedInput}`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const stat = fs.statSync(resolvedInput);
|
|
55
|
+
const actionOpts = { provider, overwrite, json, videoBitrateMbps };
|
|
56
|
+
|
|
57
|
+
if (stat.isFile()) {
|
|
58
|
+
return removeSingleFile(resolvedInput, { output, outDir, dryRun, actionOpts, onEvent });
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (stat.isDirectory()) {
|
|
62
|
+
return removeDirectory(resolvedInput, { outDir, dryRun, actionOpts, onEvent });
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
throw new Error(`Unsupported input path type: ${resolvedInput}`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* @param {string} inputPath
|
|
70
|
+
* @param {object} ctx
|
|
71
|
+
*/
|
|
72
|
+
async function removeSingleFile(inputPath, ctx) {
|
|
73
|
+
const { output, outDir, dryRun, actionOpts, onEvent } = ctx;
|
|
74
|
+
let outputPath = output ? path.resolve(output) : null;
|
|
75
|
+
if (!outputPath && outDir) {
|
|
76
|
+
outputPath = path.join(path.resolve(outDir), path.basename(inputPath));
|
|
77
|
+
}
|
|
78
|
+
if (!outputPath) {
|
|
79
|
+
outputPath = defaultCleanOutputPath(inputPath);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const processed = [];
|
|
83
|
+
const skipped = [];
|
|
84
|
+
const failed = [];
|
|
85
|
+
|
|
86
|
+
if (onEvent) {
|
|
87
|
+
onEvent({
|
|
88
|
+
type: 'watermark_plan',
|
|
89
|
+
mode: 'file',
|
|
90
|
+
input: inputPath,
|
|
91
|
+
total: 1,
|
|
92
|
+
provider: actionOpts.provider,
|
|
93
|
+
dryRun,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
if (onEvent) {
|
|
99
|
+
onEvent({ type: 'entry_start', index: 0, total: 1, filename: path.basename(inputPath), inputPath });
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (!isWatermarkSupportedMedia(inputPath)) {
|
|
103
|
+
const skip = { filename: path.basename(inputPath), inputPath, reason: 'Unsupported media extension' };
|
|
104
|
+
skipped.push(skip);
|
|
105
|
+
if (onEvent) onEvent({ type: 'entry_skipped', ...skip });
|
|
106
|
+
} else if (fs.existsSync(outputPath) && !actionOpts.overwrite && !dryRun) {
|
|
107
|
+
const skip = {
|
|
108
|
+
filename: path.basename(inputPath),
|
|
109
|
+
inputPath,
|
|
110
|
+
outputPath,
|
|
111
|
+
reason: 'Output file already exists (use --overwrite / --force)',
|
|
112
|
+
};
|
|
113
|
+
skipped.push(skip);
|
|
114
|
+
if (onEvent) onEvent({ type: 'entry_skipped', ...skip });
|
|
115
|
+
} else {
|
|
116
|
+
const res = await removeWatermark({
|
|
117
|
+
inputPath,
|
|
118
|
+
outputPath,
|
|
119
|
+
action: actionOpts,
|
|
120
|
+
dryRun,
|
|
121
|
+
onStderr: (chunk) => {
|
|
122
|
+
if (onEvent) onEvent({ type: 'provider_stderr', chunk });
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
const item = {
|
|
126
|
+
filename: path.basename(inputPath),
|
|
127
|
+
inputPath,
|
|
128
|
+
outputPath,
|
|
129
|
+
command: res.command,
|
|
130
|
+
provider: res.provider,
|
|
131
|
+
dryRun: res.dryRun,
|
|
132
|
+
};
|
|
133
|
+
processed.push(item);
|
|
134
|
+
if (onEvent) onEvent({ type: 'entry_complete', ...item });
|
|
135
|
+
}
|
|
136
|
+
} catch (err) {
|
|
137
|
+
const fail = { filename: path.basename(inputPath), inputPath, error: err };
|
|
138
|
+
failed.push(fail);
|
|
139
|
+
if (onEvent) onEvent({ type: 'entry_failed', filename: fail.filename, inputPath, error: err });
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return {
|
|
143
|
+
input: inputPath,
|
|
144
|
+
mode: 'file',
|
|
145
|
+
provider: actionOpts.provider,
|
|
146
|
+
dryRun,
|
|
147
|
+
processed,
|
|
148
|
+
skipped,
|
|
149
|
+
failed,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* @param {string} inputDir
|
|
155
|
+
* @param {object} ctx
|
|
156
|
+
*/
|
|
157
|
+
async function removeDirectory(inputDir, ctx) {
|
|
158
|
+
const { outDir, dryRun, actionOpts, onEvent } = ctx;
|
|
159
|
+
const resolvedOutDir = outDir ? path.resolve(outDir) : defaultCleanOutDir(inputDir);
|
|
160
|
+
|
|
161
|
+
const entries = fs.readdirSync(inputDir)
|
|
162
|
+
.map((name) => path.join(inputDir, name))
|
|
163
|
+
.filter((p) => fs.statSync(p).isFile() && isWatermarkSupportedMedia(p))
|
|
164
|
+
.sort((a, b) => a.localeCompare(b));
|
|
165
|
+
|
|
166
|
+
if (onEvent) {
|
|
167
|
+
onEvent({
|
|
168
|
+
type: 'watermark_plan',
|
|
169
|
+
mode: 'directory',
|
|
170
|
+
input: inputDir,
|
|
171
|
+
outDir: resolvedOutDir,
|
|
172
|
+
total: entries.length,
|
|
173
|
+
provider: actionOpts.provider,
|
|
174
|
+
dryRun,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Directory batch: prefer provider --out-dir when not dry-run for a single spawn when overwrite/json only.
|
|
179
|
+
// We still process file-by-file so skip/exists and progress events stay consistent with apply-actions.
|
|
180
|
+
const processed = [];
|
|
181
|
+
const skipped = [];
|
|
182
|
+
const failed = [];
|
|
183
|
+
|
|
184
|
+
for (let i = 0; i < entries.length; i++) {
|
|
185
|
+
const inputPath = entries[i];
|
|
186
|
+
const outputPath = path.join(resolvedOutDir, path.basename(inputPath));
|
|
187
|
+
|
|
188
|
+
if (onEvent) {
|
|
189
|
+
onEvent({
|
|
190
|
+
type: 'entry_start',
|
|
191
|
+
index: i,
|
|
192
|
+
total: entries.length,
|
|
193
|
+
filename: path.basename(inputPath),
|
|
194
|
+
inputPath,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
try {
|
|
199
|
+
if (fs.existsSync(outputPath) && !actionOpts.overwrite && !dryRun) {
|
|
200
|
+
const skip = {
|
|
201
|
+
filename: path.basename(inputPath),
|
|
202
|
+
inputPath,
|
|
203
|
+
outputPath,
|
|
204
|
+
reason: 'Output file already exists (use --overwrite / --force)',
|
|
205
|
+
};
|
|
206
|
+
skipped.push(skip);
|
|
207
|
+
if (onEvent) onEvent({ type: 'entry_skipped', ...skip });
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const res = await removeWatermark({
|
|
212
|
+
inputPath,
|
|
213
|
+
outputPath,
|
|
214
|
+
action: actionOpts,
|
|
215
|
+
dryRun,
|
|
216
|
+
onStderr: (chunk) => {
|
|
217
|
+
if (onEvent) onEvent({ type: 'provider_stderr', chunk });
|
|
218
|
+
},
|
|
219
|
+
});
|
|
220
|
+
const item = {
|
|
221
|
+
filename: path.basename(inputPath),
|
|
222
|
+
inputPath,
|
|
223
|
+
outputPath,
|
|
224
|
+
command: res.command,
|
|
225
|
+
provider: res.provider,
|
|
226
|
+
dryRun: res.dryRun,
|
|
227
|
+
};
|
|
228
|
+
processed.push(item);
|
|
229
|
+
if (onEvent) onEvent({ type: 'entry_complete', ...item });
|
|
230
|
+
} catch (err) {
|
|
231
|
+
const fail = { filename: path.basename(inputPath), inputPath, error: err };
|
|
232
|
+
failed.push(fail);
|
|
233
|
+
if (onEvent) onEvent({ type: 'entry_failed', filename: fail.filename, inputPath, error: err });
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return {
|
|
238
|
+
input: inputDir,
|
|
239
|
+
mode: 'directory',
|
|
240
|
+
provider: actionOpts.provider,
|
|
241
|
+
outDir: resolvedOutDir,
|
|
242
|
+
dryRun,
|
|
243
|
+
processed,
|
|
244
|
+
skipped,
|
|
245
|
+
failed,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export {
|
|
250
|
+
listWatermarkProviders,
|
|
251
|
+
DEFAULT_WATERMARK_PROVIDER,
|
|
252
|
+
};
|
package/src/index.d.ts
CHANGED
|
@@ -193,6 +193,78 @@ export const QUOTA_PER_SWAP: number;
|
|
|
193
193
|
export const DEFAULT_SWAP_STATE_FILENAME: string;
|
|
194
194
|
export const SWAP_STATUSES: string[];
|
|
195
195
|
|
|
196
|
+
// ─── Access / Privacy ────────────────────────────────────────────────────────
|
|
197
|
+
|
|
198
|
+
export type PrivacyStatus = 'private' | 'unlisted' | 'public';
|
|
199
|
+
|
|
200
|
+
export const QUOTA_PER_PRIVACY_UPDATE: number;
|
|
201
|
+
|
|
202
|
+
export function normalizePrivacyStatus(value: unknown): PrivacyStatus;
|
|
203
|
+
export function getVideosPrivacy(youtube: youtube_v3.Youtube, videoIds: string[]): Promise<Map<string, PrivacyStatus>>;
|
|
204
|
+
export function getPlaylistPrivacy(youtube: youtube_v3.Youtube, playlistId: string): Promise<PrivacyStatus>;
|
|
205
|
+
export function setVideoPrivacy(youtube: youtube_v3.Youtube, videoId: string, privacyStatus: PrivacyStatus): Promise<void>;
|
|
206
|
+
export function setPlaylistPrivacy(youtube: youtube_v3.Youtube, playlistId: string, privacyStatus: PrivacyStatus): Promise<void>;
|
|
207
|
+
|
|
208
|
+
export interface AccessVideoPlan {
|
|
209
|
+
videoId: string;
|
|
210
|
+
title?: string;
|
|
211
|
+
position?: number;
|
|
212
|
+
currentPrivacy: PrivacyStatus | null;
|
|
213
|
+
targetPrivacy: PrivacyStatus;
|
|
214
|
+
changed: boolean;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export interface SetPlaylistAccessOptions {
|
|
218
|
+
youtube?: youtube_v3.Youtube;
|
|
219
|
+
playlistId: string;
|
|
220
|
+
privacyStatus: PrivacyStatus;
|
|
221
|
+
videoIds?: string[];
|
|
222
|
+
videoSource?: 'playlist' | 'history';
|
|
223
|
+
albumDir?: string;
|
|
224
|
+
overviewHistoryPath?: string;
|
|
225
|
+
overviewKeys?: string[];
|
|
226
|
+
updateMetadata?: boolean;
|
|
227
|
+
metadataFile?: string;
|
|
228
|
+
dryRun?: boolean;
|
|
229
|
+
onEvent?: (event: { type: string; [key: string]: any }) => void;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export interface SetPlaylistAccessResult {
|
|
233
|
+
dryRun: boolean;
|
|
234
|
+
playlistId: string;
|
|
235
|
+
privacyStatus: PrivacyStatus;
|
|
236
|
+
videoSource: 'playlist' | 'history';
|
|
237
|
+
playlist: {
|
|
238
|
+
playlistId: string;
|
|
239
|
+
currentPrivacy: PrivacyStatus | null;
|
|
240
|
+
targetPrivacy: PrivacyStatus;
|
|
241
|
+
changed: boolean;
|
|
242
|
+
};
|
|
243
|
+
videos: AccessVideoPlan[];
|
|
244
|
+
changedCount: number;
|
|
245
|
+
estimatedQuota: number;
|
|
246
|
+
updated: {
|
|
247
|
+
playlist: boolean;
|
|
248
|
+
videos: Array<{ videoId: string; privacyStatus: PrivacyStatus }>;
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export function setPlaylistAccess(options: SetPlaylistAccessOptions): Promise<SetPlaylistAccessResult>;
|
|
253
|
+
|
|
254
|
+
export interface SetVideosAccessOptions {
|
|
255
|
+
youtube?: youtube_v3.Youtube;
|
|
256
|
+
videoIds: string[];
|
|
257
|
+
privacyStatus: PrivacyStatus;
|
|
258
|
+
dryRun?: boolean;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export function setVideosAccess(options: SetVideosAccessOptions): Promise<{
|
|
262
|
+
dryRun: boolean;
|
|
263
|
+
videos: AccessVideoPlan[];
|
|
264
|
+
changedCount?: number;
|
|
265
|
+
updated?: string[];
|
|
266
|
+
}>;
|
|
267
|
+
|
|
196
268
|
// ─── Metadata & Title Updates ──────────────────────────────────────────────
|
|
197
269
|
|
|
198
270
|
export interface BatchUpdateMetadataOptions {
|
|
@@ -200,6 +272,11 @@ export interface BatchUpdateMetadataOptions {
|
|
|
200
272
|
titlePrefix?: string;
|
|
201
273
|
playlistPostfix?: string;
|
|
202
274
|
playlistTitle?: string;
|
|
275
|
+
replacePattern?: string;
|
|
276
|
+
replaceWith?: string;
|
|
277
|
+
replaceLiteral?: boolean;
|
|
278
|
+
replaceInSnippet?: boolean;
|
|
279
|
+
includePlaylistId?: string;
|
|
203
280
|
metadataFile?: string;
|
|
204
281
|
updateLocalMetadata?: boolean;
|
|
205
282
|
syncMetadata?: boolean;
|
|
@@ -260,6 +337,9 @@ export function updatePlaylistTitle(
|
|
|
260
337
|
newTitle: string
|
|
261
338
|
): Promise<{ playlistId: string; title: string; data: any }>;
|
|
262
339
|
|
|
340
|
+
export function applyTextReplace(text: string, pattern: string, replacement?: string, literal?: boolean): string;
|
|
341
|
+
export function deepReplaceInObject(value: unknown, pattern: string, replacement?: string, literal?: boolean): unknown;
|
|
342
|
+
|
|
263
343
|
export function batchUpdateMetadata(
|
|
264
344
|
targetFolder?: string,
|
|
265
345
|
options?: BatchUpdateMetadataOptions
|
|
@@ -500,12 +580,22 @@ export interface JourneyOverlayAction {
|
|
|
500
580
|
showSidebar?: boolean;
|
|
501
581
|
}
|
|
502
582
|
|
|
503
|
-
export
|
|
583
|
+
export interface RemoveWatermarkAction {
|
|
584
|
+
type: 'remove-watermark';
|
|
585
|
+
provider?: string;
|
|
586
|
+
overwrite?: boolean;
|
|
587
|
+
json?: boolean;
|
|
588
|
+
videoBitrateMbps?: number;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
export type VideoAction = ShortenAction | ReplaceBackgroundAction | JourneyOverlayAction | RemoveWatermarkAction | { type: string; [key: string]: any };
|
|
504
592
|
|
|
505
593
|
export interface JourneyConfig {
|
|
506
594
|
enabled?: boolean;
|
|
507
595
|
mode?: 'before-current' | 'all';
|
|
508
|
-
position?: 'left';
|
|
596
|
+
position?: 'left' | 'right';
|
|
597
|
+
style?: 'badge' | 'panel';
|
|
598
|
+
badgeText?: string;
|
|
509
599
|
displayDuration?: number | 'full';
|
|
510
600
|
sidebarDisplayDuration?: number | 'full';
|
|
511
601
|
sidebarLabel?: 'title' | 'stage' | 'both';
|
|
@@ -614,7 +704,12 @@ export function generateJourneySvg(params: {
|
|
|
614
704
|
centerText?: string;
|
|
615
705
|
showSidebar?: boolean;
|
|
616
706
|
theme?: 'dark' | 'light';
|
|
617
|
-
position?: 'left';
|
|
707
|
+
position?: 'left' | 'right';
|
|
708
|
+
style?: 'badge' | 'panel';
|
|
709
|
+
stage?: string;
|
|
710
|
+
step?: number;
|
|
711
|
+
totalSteps?: number;
|
|
712
|
+
badgeText?: string;
|
|
618
713
|
}): string;
|
|
619
714
|
|
|
620
715
|
export function renderSvgToPng(svgContent: string, width?: number, height?: number): Buffer;
|
|
@@ -630,6 +725,64 @@ export function applyJourneyOverlay(params: {
|
|
|
630
725
|
onWarn?: (msg: string) => void;
|
|
631
726
|
}): Promise<{ outputPath: string; duration?: number; dryRun?: boolean; command?: string }>;
|
|
632
727
|
|
|
728
|
+
export function isWatermarkSupportedMedia(filePath: string): boolean;
|
|
729
|
+
export function defaultCleanOutputPath(inputPath: string): string;
|
|
730
|
+
export function defaultCleanOutDir(inputDir: string): string;
|
|
731
|
+
|
|
732
|
+
export function removeWatermark(params: {
|
|
733
|
+
inputPath: string;
|
|
734
|
+
outputPath: string;
|
|
735
|
+
action?: Partial<RemoveWatermarkAction>;
|
|
736
|
+
dryRun?: boolean;
|
|
737
|
+
onWarn?: (msg: string) => void;
|
|
738
|
+
onStderr?: (chunk: string) => void;
|
|
739
|
+
}): Promise<{ outputPath: string; dryRun?: boolean; command: string; provider: string }>;
|
|
740
|
+
|
|
741
|
+
export function listWatermarkProviders(): Array<{ id: string; license: string; packageName: string }>;
|
|
742
|
+
export function getWatermarkProvider(providerId?: string): {
|
|
743
|
+
id: string;
|
|
744
|
+
license: string;
|
|
745
|
+
packageName: string;
|
|
746
|
+
remove: (options: object) => Promise<any>;
|
|
747
|
+
};
|
|
748
|
+
export function removeWatermarkWithProvider(options: {
|
|
749
|
+
inputPath: string;
|
|
750
|
+
outputPath?: string;
|
|
751
|
+
outDir?: string;
|
|
752
|
+
provider?: string;
|
|
753
|
+
overwrite?: boolean;
|
|
754
|
+
json?: boolean;
|
|
755
|
+
videoBitrateMbps?: number;
|
|
756
|
+
dryRun?: boolean;
|
|
757
|
+
onStderr?: (chunk: string) => void;
|
|
758
|
+
}): Promise<{ outputPath?: string; outDir?: string; dryRun?: boolean; command: string; exitCode?: number; stdout?: string }>;
|
|
759
|
+
|
|
760
|
+
export const DEFAULT_WATERMARK_PROVIDER: string;
|
|
761
|
+
export const PERMISSIVE_LICENSES: readonly string[];
|
|
762
|
+
|
|
763
|
+
export function batchRemoveWatermark(
|
|
764
|
+
input?: string,
|
|
765
|
+
options?: {
|
|
766
|
+
output?: string;
|
|
767
|
+
outDir?: string;
|
|
768
|
+
provider?: string;
|
|
769
|
+
overwrite?: boolean;
|
|
770
|
+
json?: boolean;
|
|
771
|
+
videoBitrateMbps?: number;
|
|
772
|
+
dryRun?: boolean;
|
|
773
|
+
onEvent?: (event: { type: string; [key: string]: any }) => void;
|
|
774
|
+
}
|
|
775
|
+
): Promise<{
|
|
776
|
+
input: string;
|
|
777
|
+
mode: 'file' | 'directory';
|
|
778
|
+
provider: string;
|
|
779
|
+
dryRun: boolean;
|
|
780
|
+
outDir?: string;
|
|
781
|
+
processed: Array<object>;
|
|
782
|
+
skipped: Array<object>;
|
|
783
|
+
failed: Array<object>;
|
|
784
|
+
}>;
|
|
785
|
+
|
|
633
786
|
export function runActionsPipeline(
|
|
634
787
|
entry: PlannedEntry | { songKey?: string; filename: string; sourcePath: string; targetPath?: string },
|
|
635
788
|
actions?: VideoAction[],
|
package/src/index.js
CHANGED
|
@@ -32,6 +32,8 @@ export {
|
|
|
32
32
|
updatePlaylistSnippet,
|
|
33
33
|
updatePlaylistTitle,
|
|
34
34
|
batchUpdateMetadata,
|
|
35
|
+
applyTextReplace,
|
|
36
|
+
deepReplaceInObject,
|
|
35
37
|
QUOTA_PER_VIDEO_UPDATE,
|
|
36
38
|
QUOTA_PER_PLAYLIST_UPDATE,
|
|
37
39
|
QUOTA_PER_LIST,
|
|
@@ -81,6 +83,10 @@ export {
|
|
|
81
83
|
generateJourneySvg,
|
|
82
84
|
renderSvgToPng,
|
|
83
85
|
resolveJourneyTracks,
|
|
86
|
+
removeWatermark,
|
|
87
|
+
isWatermarkSupportedMedia,
|
|
88
|
+
defaultCleanOutputPath,
|
|
89
|
+
defaultCleanOutDir,
|
|
84
90
|
runActionsPipeline,
|
|
85
91
|
} from './actions/index.js';
|
|
86
92
|
export {
|
|
@@ -97,5 +103,41 @@ export {
|
|
|
97
103
|
export {
|
|
98
104
|
previewJourney,
|
|
99
105
|
} from './cli/preview-journey.js';
|
|
106
|
+
export {
|
|
107
|
+
batchRemoveWatermark,
|
|
108
|
+
} from './cli/remove-watermark.js';
|
|
109
|
+
export {
|
|
110
|
+
getWatermarkProvider,
|
|
111
|
+
listWatermarkProviders,
|
|
112
|
+
removeWatermarkWithProvider,
|
|
113
|
+
DEFAULT_WATERMARK_PROVIDER,
|
|
114
|
+
PERMISSIVE_LICENSES,
|
|
115
|
+
} from './providers/watermark/index.js';
|
|
116
|
+
export {
|
|
117
|
+
detectOutdatedUploads,
|
|
118
|
+
} from './playlist/detect-outdated.js';
|
|
119
|
+
export {
|
|
120
|
+
listPlaylistItems,
|
|
121
|
+
reorderPlaylist,
|
|
122
|
+
playlistOrderMatches,
|
|
123
|
+
} from './playlist/reorder.js';
|
|
124
|
+
export {
|
|
125
|
+
resolveDesiredPlaylistOrder,
|
|
126
|
+
} from './playlist/order.js';
|
|
127
|
+
export {
|
|
128
|
+
syncPlaylist,
|
|
129
|
+
} from './playlist/sync.js';
|
|
130
|
+
export {
|
|
131
|
+
normalizePrivacyStatus,
|
|
132
|
+
getVideosPrivacy,
|
|
133
|
+
getPlaylistPrivacy,
|
|
134
|
+
setVideoPrivacy,
|
|
135
|
+
setPlaylistPrivacy,
|
|
136
|
+
QUOTA_PER_PRIVACY_UPDATE,
|
|
137
|
+
} from './access/privacy.js';
|
|
138
|
+
export {
|
|
139
|
+
setPlaylistAccess,
|
|
140
|
+
setVideosAccess,
|
|
141
|
+
} from './access/set-access.js';
|
|
100
142
|
|
|
101
143
|
|