ugcinc 4.27.0 → 4.28.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 +114 -30
- package/dist/content-skills.js +3 -3
- package/dist/media-analysis.d.ts +1984 -19
- package/dist/media-analysis.js +41 -5
- package/dist/tools/media.js +1 -1
- package/package.json +1 -1
- package/skills/media/SKILL.md +16 -4
- package/skills/media/references/tools.md +26 -1
package/dist/media-analysis.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.mediaAnalysisResultSchema = exports.matchTextStyleResultSchema = exports.matchTextStyleSchema = exports.measureLayoutResultSchema = exports.sampleColorsResultSchema = exports.measureLayoutSchema = exports.sampleColorsSchema = exports.binaryMaskSchema = exports.audioTranscriptResultSchema = exports.audioLevelsResultSchema = exports.measureAudioLevelsSchema = exports.transcribeAudioSchema = exports.videoTextResultSchema = exports.imageTextResultSchema = exports.textLineSchema = exports.textRegionSchema = exports.readVideoTextSchema = exports.readImageTextSchema = exports.scenesResultSchema = exports.framesResultSchema = exports.analysisSourceSchema = exports.mediaAnalysisStatusSchema = exports.extractFramesSchema = exports.detectScenesSchema = void 0;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
|
+
const native_text_1 = require("./render/types/native-text");
|
|
5
6
|
const videoUrl = zod_1.z.string().url().max(8192).refine(value => {
|
|
6
7
|
const url = new URL(value);
|
|
7
8
|
return url.protocol === 'https:' && !url.username && !url.password && !url.hash;
|
|
@@ -108,12 +109,47 @@ exports.measureLayoutResultSchema = zod_1.z.object({ version: zod_1.z.literal(1)
|
|
|
108
109
|
warnings: zod_1.z.array(zod_1.z.string()), previewUrl: zod_1.z.string().url(), manifestUrl: zod_1.z.string().url() });
|
|
109
110
|
exports.matchTextStyleSchema = zod_1.z.object({ url: videoUrl,
|
|
110
111
|
box: zod_1.z.object({ x: zod_1.z.number().int().min(0).max(4096), y: zod_1.z.number().int().min(0).max(4096), width: zod_1.z.number().int().min(16).max(1536), height: zod_1.z.number().int().min(16).max(512) }).strict().refine(b => b.x + b.width <= 4096 && b.y + b.height <= 4096, 'Region exceeds image limit'),
|
|
111
|
-
|
|
112
|
-
|
|
112
|
+
renderer: zod_1.z.enum(['auto', 'image-editor', 'tiktok-native']).optional(),
|
|
113
|
+
text: zod_1.z.string().min(1).max(160).refine(t => t.trim().length > 0 && t.split('\n').length <= 4
|
|
114
|
+
&& !/[\p{Surrogate}\u0000-\u0009\u000b-\u001f\u007f-\u009f\u2028\u2029\u202a-\u202e\u2066-\u2069]/u.test(t), 'Use valid printable Unicode, at most 160 UTF-16 code units and four lines').optional(),
|
|
115
|
+
}).strict().superRefine((value, ctx) => {
|
|
116
|
+
if ((value.renderer === undefined || value.renderer === 'image-editor') && value.text !== undefined && !/^[\x20-\x7e\n]+$/.test(value.text))
|
|
117
|
+
ctx.addIssue({ code: 'custom', path: ['text'], message: 'The image-editor renderer supports printable Latin ASCII text only.' });
|
|
118
|
+
if (value.renderer === 'tiktok-native' && value.text !== undefined && value.text.split('\n').some(line => line.replace(/ /g, '').length === 0))
|
|
119
|
+
ctx.addIssue({ code: 'custom', path: ['text'], message: 'Native text does not support empty or space-only paragraphs.' });
|
|
120
|
+
});
|
|
113
121
|
const matchedTextElement = zod_1.z.object({ id: zod_1.z.string(), type: zod_1.z.literal('text'), x: finiteNumber, y: finiteNumber, width: finiteNumber, height: finiteNumber, zIndex: zod_1.z.number(), textIsVariable: zod_1.z.literal(false), text: zod_1.z.string(), font: zod_1.z.enum(['arial', 'tiktok', 'apple']), fontSize: finiteNumber,
|
|
114
122
|
fontWeight: zod_1.z.union([zod_1.z.literal(400), zod_1.z.literal(500), zod_1.z.literal(600), zod_1.z.literal(700), zod_1.z.literal(800), zod_1.z.literal(900)]), color: zod_1.z.string(), strokeColor: zod_1.z.string(), outlineWidth: finiteNumber, lineHeight: finiteNumber, letterSpacing: finiteNumber, textAlign: zod_1.z.enum(['left', 'center', 'right']), verticalAlign: zod_1.z.literal('top'), autoWidth: zod_1.z.literal(false), paddingTop: finiteNumber, paddingRight: finiteNumber, paddingBottom: finiteNumber, paddingLeft: finiteNumber });
|
|
115
|
-
|
|
116
|
-
|
|
123
|
+
const nativeTextMatchSchema = zod_1.z.object({
|
|
124
|
+
profile: zod_1.z.literal('tiktok-classic-ios-47'), settings: native_text_1.nativeTextStickerSchema,
|
|
125
|
+
stickerUrl: videoUrl,
|
|
126
|
+
composition: zod_1.z.object({ x: finiteNumber, y: finiteNumber, width: finiteNumber.positive(), height: finiteNumber.positive(), scale: finiteNumber.positive() }),
|
|
127
|
+
maskIoU: zod_1.z.number().min(0).max(1), foregroundMae: zod_1.z.number().min(0).max(1),
|
|
128
|
+
fontIdentity: zod_1.z.literal('unverified'), quality: zod_1.z.enum(['close', 'approximate']),
|
|
129
|
+
diagnostics: zod_1.z.object({ method: zod_1.z.literal('local-white-fill-dark-outline'), textPixelCount: zod_1.z.number().int().nonnegative(), outlinePixelCount: zod_1.z.number().int().nonnegative(),
|
|
130
|
+
backgroundExcluded: zod_1.z.literal(true), observedLineCount: zod_1.z.number().int().nonnegative(), expectedLineCount: zod_1.z.number().int().positive(),
|
|
131
|
+
renderCount: zod_1.z.number().int().nonnegative(), clipped: zod_1.z.boolean(), comparison: zod_1.z.literal('segmented-text-and-outline'), resampling: zod_1.z.enum(['none', 'lanczos3']),
|
|
132
|
+
maskMetric: zod_1.z.literal('white-fill-iou'), outlineIoU: zod_1.z.number().min(0).max(1), emojiPixelCount: zod_1.z.number().int().nonnegative(),
|
|
133
|
+
residualMetric: zod_1.z.literal('segmented-text-outline-and-warm-emoji'), foregroundMetric: zod_1.z.literal('estimated-background-composite-MAE') }),
|
|
134
|
+
});
|
|
135
|
+
exports.matchTextStyleResultSchema = zod_1.z.object({ version: zod_1.z.union([zod_1.z.literal(1), zod_1.z.literal(2)]), operation: zod_1.z.literal('match_text_style'), source: visualSource, box: boxSchema, surface: zod_1.z.enum(['image-editor-element', 'native-text-sticker']), status: zod_1.z.enum(['fitted', 'unavailable']), reason: zod_1.z.string().nullable(), text: zod_1.z.string().nullable(), textSource: zod_1.z.enum(['supplied', 'ocr']),
|
|
136
|
+
renderer: zod_1.z.union([
|
|
137
|
+
zod_1.z.object({ name: zod_1.z.literal('UGC-ImageEditorComposition'), ugcVersion: zod_1.z.string(), fonts: zod_1.z.array(zod_1.z.string()), fontHashes: zod_1.z.record(zod_1.z.string()), maxRenders: zod_1.z.number() }),
|
|
138
|
+
zod_1.z.object({ name: zod_1.z.literal('UGC-NativeTikTok'), profile: zod_1.z.literal('tiktok-classic-ios-47'), fontHashes: zod_1.z.record(zod_1.z.string()), maxRenders: zod_1.z.number() }),
|
|
139
|
+
]),
|
|
117
140
|
candidates: zod_1.z.array(zod_1.z.object({ element: matchedTextElement, previewConfig: zod_1.z.object({ width: zod_1.z.number(), height: zod_1.z.number(), aspectRatio: zod_1.z.string(), dimensionPreset: zod_1.z.literal('custom'), elements: zod_1.z.array(matchedTextElement), backgroundType: zod_1.z.literal('color'), backgroundColor: zod_1.z.string() }), maskIoU: zod_1.z.number().min(0).max(1), foregroundMae: zod_1.z.number().min(0).max(1), fontIdentity: zod_1.z.literal('unverified'), quality: zod_1.z.enum(['close', 'approximate']) })).max(3),
|
|
141
|
+
nativeResult: nativeTextMatchSchema.optional(), reasonCode: zod_1.z.string().max(80).nullable().optional(), nextActions: zod_1.z.array(zod_1.z.string()).max(8).optional(), fitAttempted: zod_1.z.boolean().optional(),
|
|
118
142
|
renders: zod_1.z.number().int().nonnegative(), backgroundColor: zod_1.z.string(), warnings: zod_1.z.array(zod_1.z.string()), previewUrl: zod_1.z.string().url(), manifestUrl: zod_1.z.string().url() });
|
|
119
|
-
exports.mediaAnalysisResultSchema = zod_1.z.discriminatedUnion('operation', [exports.framesResultSchema, exports.scenesResultSchema, exports.imageTextResultSchema, exports.videoTextResultSchema, exports.audioTranscriptResultSchema, exports.audioLevelsResultSchema, exports.sampleColorsResultSchema, exports.measureLayoutResultSchema, exports.matchTextStyleResultSchema])
|
|
143
|
+
exports.mediaAnalysisResultSchema = zod_1.z.discriminatedUnion('operation', [exports.framesResultSchema, exports.scenesResultSchema, exports.imageTextResultSchema, exports.videoTextResultSchema, exports.audioTranscriptResultSchema, exports.audioLevelsResultSchema, exports.sampleColorsResultSchema, exports.measureLayoutResultSchema, exports.matchTextStyleResultSchema]).superRefine((value, ctx) => {
|
|
144
|
+
if (value.operation !== 'match_text_style')
|
|
145
|
+
return;
|
|
146
|
+
if (value.surface === 'native-text-sticker') {
|
|
147
|
+
if (value.version !== 2 || value.renderer.name !== 'UGC-NativeTikTok' || value.candidates.length !== 0)
|
|
148
|
+
ctx.addIssue({ code: 'custom', message: 'Native matching requires its versioned native renderer and no image-editor candidates.' });
|
|
149
|
+
if ((value.status === 'fitted') !== (value.nativeResult !== undefined))
|
|
150
|
+
ctx.addIssue({ code: 'custom', path: ['nativeResult'], message: 'A fitted native match requires its reproducible settings and sticker.' });
|
|
151
|
+
}
|
|
152
|
+
else if (value.version !== 1 || value.renderer.name !== 'UGC-ImageEditorComposition' || value.nativeResult !== undefined) {
|
|
153
|
+
ctx.addIssue({ code: 'custom', message: 'Image-editor matching must retain the version1 image-editor result shape.' });
|
|
154
|
+
}
|
|
155
|
+
});
|
package/dist/tools/media.js
CHANGED
|
@@ -4,7 +4,7 @@ exports.mediaTools = void 0;
|
|
|
4
4
|
const media_analysis_1 = require("../media-analysis");
|
|
5
5
|
const zod_1 = require("zod");
|
|
6
6
|
exports.mediaTools = [
|
|
7
|
-
{ name: 'match_text_style', description: 'Fit a reference
|
|
7
|
+
{ name: 'match_text_style', description: 'Fit a reference caption with local render-and-compare. Provide HTTPS image URL, integer display-pixel box (16–1536 by 16–512), and preferably exact text/line breaks. Set renderer:auto to allow native TikTok fallback for white text with dark outlines on photographic backgrounds, or tiktok-native to request that profile; these accept Unicode/emoji up to 160 UTF-16 units/four lines. Omitted renderer retains legacy image-editor matching with ASCII/plain background. Native results include nativeResult settings, clean transparent stickerUrl, composition coordinates and segmented-text residuals; generic results retain candidates/previewConfig. Check result.status, reasonCode, fitAttempted and nextActions: unavailable with zero renders is a preflight rejection, not a font search. Inspect comparison before claiming accuracy; font identity stays unverified. Do not repeat similar rejected crops or download replacement fonts. Use get_media_analysis if pending.', schema: media_analysis_1.matchTextStyleSchema, execute: async (client, params) => client.media.matchTextStyle(params) },
|
|
8
8
|
{ name: 'sample_colors', description: 'Sample observed sRGB colors from an image URL: optional integer point, half-open source-pixel box, or full-size row-major binary RLE mask. Counts alternate background/foreground starting with background (not COCO order). Default whole image, maxColors 8 (1–32). Returns RGBA point and alpha-weighted palette, method, omitted proportion, source hash and preview. Does not recover original authoring colors. Use get_media_analysis if pending.', schema: media_analysis_1.sampleColorsSchema, execute: async (client, params) => client.media.sampleColors(params) },
|
|
9
9
|
{ name: 'measure_layout', description: 'Measure supplied element boxes or binary masks against an image URL. Returns margins, centers, spacing, alignment deltas and overlap. Coordinates are display-oriented source pixels. Masks match source dimensions, row-major counts alternate background/foreground starting with background (not COCO). Up to 32 uniquely identified elements. Visible area/intersection requires masks; boxes do not imply visible ink. Does not detect elements. Use get_media_analysis if pending.', schema: media_analysis_1.measureLayoutSchema, execute: async (client, params) => client.media.measureLayout(params) },
|
|
10
10
|
{ name: 'transcribe_audio', description: 'Transcribe audio/video using an owned local speech model. Returns text, source-relative milliseconds, estimated segment/word timings, model details and an audio preview. No forced alignment or speaker separation. Optional range <=120 seconds and channel 0/1; default averages channels. If pending, use get_media_analysis.', schema: media_analysis_1.transcribeAudioSchema, execute: async (client, params) => client.media.transcribeAudio(params) },
|
package/package.json
CHANGED
package/skills/media/SKILL.md
CHANGED
|
@@ -56,10 +56,18 @@ coverage. A zero process exit code alone does not establish API success.
|
|
|
56
56
|
A transcript does not establish music, emotion, delivery or sound effects.
|
|
57
57
|
- Color sampling measures decoded sRGB pixels, not guaranteed design hex values.
|
|
58
58
|
Layout measurement does not detect objects or recover original layer files.
|
|
59
|
-
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
59
|
+
- For text over photographic backgrounds, call `match_text_style` with
|
|
60
|
+
`renderer: "auto"` or `"tiktok-native"` and exact text/line breaks when known.
|
|
61
|
+
Native results include Unicode/emoji and a clean transparent sticker using
|
|
62
|
+
the supported native TikTok profile. Omitted renderer keeps the older
|
|
63
|
+
ASCII/plain-background image-editor path. Inspect `surface`/version and use
|
|
64
|
+
`nativeResult` or generic `candidates` accordingly.
|
|
65
|
+
- A completed matching job can return inner `status: "unavailable"`. Check
|
|
66
|
+
`reasonCode`, `fitAttempted`, `renders` and `nextActions`. Zero renders means
|
|
67
|
+
no font fit was attempted: do not spend repeated calls on similar rejected
|
|
68
|
+
crops or download replacement fonts. Font identity stays unverified; compare
|
|
69
|
+
text/outline residuals and the preview before calling any result accurate.
|
|
70
|
+
Equal line widths or matching positions alone do not prove identical text.
|
|
63
71
|
- SAM/object segmentation, automatic overlay detection, element/mask tracking,
|
|
64
72
|
motion measurement, extraction, media comparison/alignment and semantic search
|
|
65
73
|
are not available through this tool set. Do not invent those calls or install
|
|
@@ -81,3 +89,7 @@ uploading, use the supported owned storage path; do not claim a file is hosted
|
|
|
81
89
|
because a media record was created for an arbitrary URL.
|
|
82
90
|
|
|
83
91
|
For building a reusable content format, use the `content-automation` skill.
|
|
92
|
+
|
|
93
|
+
For native iMessage render jobs use the `imessage` skill and its separate
|
|
94
|
+
submit/status/cancel tools. They require `content:render`, not `content:analyze`,
|
|
95
|
+
and are not media-analysis jobs.
|
|
@@ -45,7 +45,7 @@ These return `{ok, data:{jobId,status,operation,result?,error?}}`. Use the compl
|
|
|
45
45
|
| `measure_audio_levels` / `media.measureAudioLevels` | `{url,range?:{startMs,endMs},windowMs?:100,silenceThresholdDbfs?:-50}` | Native-channel RMS/sample peaks, loudness timeline/LUFS, silence, possible clipping, `previewUrl`. Windows20–5000ms, threshold−100to−10dBFS, range at most120seconds. |
|
|
46
46
|
| `sample_colors` / `media.sampleColors` | `{url,maxColors?:8,point?:{x,y},box?:{x,y,width,height},mask?}`; choose at most one selection | Observed RGBA point or palette/proportions, omitted mass, declared color policy, `previewUrl`. Palette1–32colors. |
|
|
47
47
|
| `measure_layout` / `media.measureLayout` | `{url,elements:[{id,box:{x,y,width,height}}]}`; each element may instead have `mask` | Margins, centers, spacing/alignment deltas, intersections/IoU and `previewUrl`.1–32unique IDs. Boxes are supplied, not automatically detected. |
|
|
48
|
-
| `match_text_style` / `media.matchTextStyle` | `{url,box:{x,y,width,height},text?}`; integer
|
|
48
|
+
| `match_text_style` / `media.matchTextStyle` | `{url,box:{x,y,width,height},text?,renderer?}`; integer crop 16–1536 wide, 16–512 high. `renderer: auto` allows native fallback; `tiktok-native` requests the native profile. Omitted renderer keeps legacy image-editor behavior. Supply exact text and line breaks when available. | `status`, `reasonCode`, `fitAttempted`, `nextActions`, comparison and warnings. Generic v1 returns `candidates` with image-editor `element`/`previewConfig`. Native v2 returns `nativeResult` with actual native `settings`, clean transparent `stickerUrl`, source-pixel `composition`, masked residuals and diagnostics; `candidates` is empty. Native/auto accept Unicode/emoji, up to 160 UTF-16 code units/four lines; generic remains ASCII/plain-background only. Original font identity is unverified. |
|
|
49
49
|
| `get_media_analysis` / `media.getMediaAnalysis` | `{jobId}` | Current status/result in the current organization; use when automatic waiting returned pending/processing. |
|
|
50
50
|
|
|
51
51
|
Boxes are half-open source-pixel rectangles. For masks used by colors/layout:
|
|
@@ -53,6 +53,31 @@ Boxes are half-open source-pixel rectangles. For masks used by colors/layout:
|
|
|
53
53
|
foreground. Counts must sum to width×height. This is not COCO's column-major
|
|
54
54
|
convention. Supply only masks you actually have; automatic segmentation is off.
|
|
55
55
|
|
|
56
|
+
## Text matching decisions
|
|
57
|
+
|
|
58
|
+
For photographed TikTok captions, use `renderer: "auto"` or `"tiktok-native"`
|
|
59
|
+
and supply the exact caption, including emoji and intended line breaks. The
|
|
60
|
+
native candidate uses the supported `tiktok-classic-ios-47` profile: white text,
|
|
61
|
+
dark outline, original native shaping and emoji. This does not imply every
|
|
62
|
+
TikTok caption uses that profile. Unsupported styles remain unavailable or
|
|
63
|
+
explicit approximations; no new font download or hosted model is required.
|
|
64
|
+
|
|
65
|
+
Check all three levels: outer response `ok`, analysis job `status`, and inner
|
|
66
|
+
matching `result.status`. A completed analysis can legitimately return
|
|
67
|
+
`unavailable`. If `fitAttempted` is false or `renders` is zero, no font candidate
|
|
68
|
+
was tested. Read `reasonCode` and `nextActions`; do not keep resubmitting nearly
|
|
69
|
+
the same crop after a background/segmentation rejection. Keep the source frame
|
|
70
|
+
and failure for a tool repair rather than claiming a missing font.
|
|
71
|
+
|
|
72
|
+
Use `surface` and result version to choose the output. Generic image-editor
|
|
73
|
+
results retain `candidates[].element` and `previewConfig`. Native results use
|
|
74
|
+
`nativeResult.settings` with the native sticker renderer, or directly reuse
|
|
75
|
+
`nativeResult.stickerUrl`. Its `composition` positions the clean PNG in source
|
|
76
|
+
pixels; native settings themselves use points and produce 3× pixels. Do not
|
|
77
|
+
paste extracted source-caption pixels onto a changed background. Compare the
|
|
78
|
+
returned fit/difference preview and residuals; matching a line width or locating
|
|
79
|
+
its top-left corner does not establish correct glyphs, spacing or emoji.
|
|
80
|
+
|
|
56
81
|
## Existing media library
|
|
57
82
|
|
|
58
83
|
- `list_media({ids?,tag?})`, `list_social_audio({ids?,tag?})`.
|