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.
@@ -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
- text: zod_1.z.string().min(1).max(160).refine(t => t.trim().length > 0 && t.split('\n').length <= 4 && /^[\x20-\x7e\n]+$/.test(t), 'Use printable Latin ASCII text, at most four lines').optional(),
112
- }).strict();
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
- exports.matchTextStyleResultSchema = zod_1.z.object({ version: zod_1.z.literal(1), operation: zod_1.z.literal('match_text_style'), source: visualSource, box: boxSchema, surface: zod_1.z.literal('image-editor-element'), 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']),
116
- renderer: 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() }),
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
+ });
@@ -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 text crop to actual UGC image-editor text settings using local render-and-compare. Provide HTTPS image URL and integer display-pixel box with a small plain-background margin; optionally exact text with line breaks (otherwise local English OCR). Up to 160 printable ASCII characters/four lines; crop 16–1536 by 16–512. Returns three supported candidates, renderable preview configs, foreground residuals and a comparison image. Fonts are approximations, not identified originals. No shadow/gradient/perspective/mixed-style support; unsuitable references explicitly unavailable. Use get_media_analysis if pending.', schema: media_analysis_1.matchTextStyleSchema, execute: async (client, params) => client.media.matchTextStyle(params) },
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ugcinc",
3
- "version": "4.27.0",
3
+ "version": "4.28.0",
4
4
  "description": "TypeScript/JavaScript client for the UGC Inc API",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -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
- - Text-style matching returns supported UGC settings and visual residuals. Font
60
- identity is unverified; inspect the preview before calling a fit accurate.
61
- It has a restricted ASCII/font/style surface; the native TikTok renderer is a
62
- separate bounded rendering capability, not proof that matching is exact.
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 crop16–1536wide,16–512high; optional supplied text | `status`, candidates with real image-editor `element`/`previewConfig`, `maskIoU`, `foregroundMae`, `quality`, previews/comparison and warnings. Up to160printable ASCII characters/four lines; supported fonts Arial/TikTok/Apple. Missing/unsupported styles may be unavailable or approximations. |
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?})`.