Skip to content

Commit a1ac9a9

Browse files
committed
improvement(buffer): explicit mediaType override; error instead of guessing on ambiguous media
1 parent e94e514 commit a1ac9a9

7 files changed

Lines changed: 65 additions & 7 deletions

File tree

apps/docs/content/docs/en/integrations/buffer.mdx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ Create a post in Buffer for a channel — add it to the queue, share it immediat
3434
| `dueAt` | string | No | Publish time as an ISO 8601 timestamp \(required when mode is customScheduled\) |
3535
| `saveToDraft` | boolean | No | Save the post as a draft instead of scheduling it |
3636
| `media` | file | No | Image or video to attach — an uploaded file, a file reference from a previous block, or a publicly accessible URL |
37+
| `mediaType` | string | No | Force the attachment type when it cannot be detected from the file or URL: image or video \(default auto\) |
3738
| `mediaAltText` | string | No | Alt text for an attached image |
3839

3940
#### Output
@@ -83,6 +84,7 @@ Edit an existing Buffer post — update its text, schedule, or media. Attaching
8384
| `dueAt` | string | No | Publish time as an ISO 8601 timestamp \(required when mode is customScheduled\) |
8485
| `saveToDraft` | boolean | No | Save the post as a draft instead of scheduling it |
8586
| `media` | file | No | Image or video to attach — an uploaded file, a file reference from a previous block, or a publicly accessible URL. Replaces existing attachments |
87+
| `mediaType` | string | No | Force the attachment type when it cannot be detected from the file or URL: image or video \(default auto\) |
8688
| `mediaAltText` | string | No | Alt text for an attached image |
8789

8890
#### Output

apps/sim/app/api/tools/buffer/server-utils.ts

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ const EDIT_POST_MUTATION = `
5353

5454
interface ResolveMediaAssetOptions {
5555
media: RawFileInput | string
56+
mediaType?: 'auto' | 'image' | 'video' | null
5657
mediaAltText?: string | null
5758
userId: string
5859
requestId: string
@@ -79,15 +80,16 @@ function mediaKindFromExtension(pathOrName: string): 'image' | 'video' | null {
7980
* Determines whether media should be attached as a video or image asset.
8081
* Prefers the file's MIME type, then the path/URL extension, and for
8182
* extensionless URLs falls back to a DNS-pinned HEAD probe of the resolved
82-
* URL's Content-Type. Defaults to image when nothing is conclusive.
83+
* URL's Content-Type. Returns null when nothing is conclusive so the caller
84+
* can ask for an explicit media type instead of guessing.
8385
*/
8486
async function resolveMediaKind(
8587
mimeType: string | undefined,
8688
pathOrName: string,
8789
fileUrl: string,
8890
requestId: string,
8991
logger: Logger
90-
): Promise<'image' | 'video'> {
92+
): Promise<'image' | 'video' | null> {
9193
if (mimeType?.startsWith('video/')) return 'video'
9294
if (mimeType?.startsWith('image/')) return 'image'
9395

@@ -106,11 +108,11 @@ async function resolveMediaKind(
106108
if (contentType.startsWith('image/')) return 'image'
107109
}
108110
} catch (error) {
109-
logger.warn(`[${requestId}] Media content-type probe failed, defaulting to image`, {
111+
logger.warn(`[${requestId}] Media content-type probe was inconclusive`, {
110112
error: getErrorMessage(error, 'probe failed'),
111113
})
112114
}
113-
return 'image'
115+
return null
114116
}
115117

116118
/**
@@ -122,7 +124,7 @@ async function resolveMediaKind(
122124
export async function resolveMediaAsset(
123125
options: ResolveMediaAssetOptions
124126
): Promise<ResolvedMediaAsset> {
125-
const { media, mediaAltText, userId, requestId, logger } = options
127+
const { media, mediaType, mediaAltText, userId, requestId, logger } = options
126128

127129
const isFileInput = typeof media === 'object'
128130
const resolution = await resolveFileInputToUrl({
@@ -143,7 +145,22 @@ export async function resolveMediaAsset(
143145

144146
const mimeType = isFileInput ? media.type : undefined
145147
const pathOrName = isFileInput ? media.name || '' : media
146-
const kind = await resolveMediaKind(mimeType, pathOrName, resolution.fileUrl, requestId, logger)
148+
const kind =
149+
mediaType === 'image' || mediaType === 'video'
150+
? mediaType
151+
: await resolveMediaKind(mimeType, pathOrName, resolution.fileUrl, requestId, logger)
152+
if (!kind) {
153+
return {
154+
errorResponse: NextResponse.json(
155+
{
156+
success: false,
157+
error:
158+
'Could not determine whether the media is an image or a video. Set mediaType to "image" or "video".',
159+
},
160+
{ status: 400 }
161+
),
162+
}
163+
}
147164
if (kind === 'video') {
148165
return { asset: { video: { url: resolution.fileUrl } } }
149166
}
@@ -210,6 +227,7 @@ interface ForwardPostMutationOptions {
210227
dueAt?: string | null
211228
saveToDraft?: boolean | null
212229
media?: RawFileInput | string | null
230+
mediaType?: 'auto' | 'image' | 'video' | null
213231
mediaAltText?: string | null
214232
userId: string
215233
requestId: string
@@ -224,7 +242,8 @@ interface ForwardPostMutationOptions {
224242
export async function forwardPostMutation(
225243
options: ForwardPostMutationOptions
226244
): Promise<NextResponse> {
227-
const { apiKey, postId, channelId, media, mediaAltText, userId, requestId, logger } = options
245+
const { apiKey, postId, channelId, media, mediaType, mediaAltText, userId, requestId, logger } =
246+
options
228247

229248
const input: Record<string, unknown> = {
230249
mode: options.mode,
@@ -243,6 +262,7 @@ export async function forwardPostMutation(
243262
if (media) {
244263
const { asset, errorResponse } = await resolveMediaAsset({
245264
media,
265+
mediaType,
246266
mediaAltText,
247267
userId,
248268
requestId,

apps/sim/blocks/blocks/buffer.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,19 @@ export const BufferBlock: BlockConfig<BufferPostResponse> = {
140140
mode: 'advanced',
141141
condition: { field: 'operation', value: POST_EDIT_OPS },
142142
},
143+
{
144+
id: 'mediaType',
145+
title: 'Media Type',
146+
type: 'dropdown',
147+
options: [
148+
{ label: 'Auto-detect', id: 'auto' },
149+
{ label: 'Image', id: 'image' },
150+
{ label: 'Video', id: 'video' },
151+
],
152+
value: () => 'auto',
153+
mode: 'advanced',
154+
condition: { field: 'operation', value: POST_EDIT_OPS },
155+
},
143156
{
144157
id: 'mediaAltText',
145158
title: 'Media Alt Text',
@@ -321,6 +334,10 @@ export const BufferBlock: BlockConfig<BufferPostResponse> = {
321334
dueAt: { type: 'string', description: 'Publish time (ISO 8601)' },
322335
saveToDraft: { type: 'boolean', description: 'Save the post as a draft' },
323336
media: { type: 'string', description: 'Image or video attachment (file or public URL)' },
337+
mediaType: {
338+
type: 'string',
339+
description: 'Attachment type override: auto, image, or video',
340+
},
324341
mediaAltText: { type: 'string', description: 'Alt text for an attached image' },
325342
channelIds: { type: 'string', description: 'Comma-separated channel IDs filter' },
326343
status: { type: 'string', description: 'Comma-separated post status filter' },

apps/sim/lib/api/contracts/tools/buffer.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ const postSharedFields = {
6262
.nullable(),
6363
saveToDraft: z.boolean().optional().nullable(),
6464
media: FileInputSchema.optional().nullable(),
65+
mediaType: z.enum(['auto', 'image', 'video']).default('auto'),
6566
mediaAltText: z.string().max(1000, 'mediaAltText is too long').optional().nullable(),
6667
}
6768

apps/sim/tools/buffer/create_post.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,13 @@ export const bufferCreatePostTool: ToolConfig<BufferCreatePostParams, BufferPost
6464
description:
6565
'Image or video to attach — an uploaded file, a file reference from a previous block, or a publicly accessible URL',
6666
},
67+
mediaType: {
68+
type: 'string',
69+
required: false,
70+
visibility: 'user-or-llm',
71+
description:
72+
'Force the attachment type when it cannot be detected from the file or URL: image or video (default auto)',
73+
},
6774
mediaAltText: {
6875
type: 'string',
6976
required: false,
@@ -85,6 +92,7 @@ export const bufferCreatePostTool: ToolConfig<BufferCreatePostParams, BufferPost
8592
dueAt: params.dueAt,
8693
saveToDraft: params.saveToDraft,
8794
media: params.media,
95+
mediaType: params.mediaType,
8896
mediaAltText: params.mediaAltText,
8997
}),
9098
},

apps/sim/tools/buffer/edit_post.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,13 @@ export const bufferEditPostTool: ToolConfig<BufferEditPostParams, BufferPostResp
6464
description:
6565
'Image or video to attach — an uploaded file, a file reference from a previous block, or a publicly accessible URL. Replaces existing attachments',
6666
},
67+
mediaType: {
68+
type: 'string',
69+
required: false,
70+
visibility: 'user-or-llm',
71+
description:
72+
'Force the attachment type when it cannot be detected from the file or URL: image or video (default auto)',
73+
},
6774
mediaAltText: {
6875
type: 'string',
6976
required: false,
@@ -85,6 +92,7 @@ export const bufferEditPostTool: ToolConfig<BufferEditPostParams, BufferPostResp
8592
dueAt: params.dueAt,
8693
saveToDraft: params.saveToDraft,
8794
media: params.media,
95+
mediaType: params.mediaType,
8896
mediaAltText: params.mediaAltText,
8997
}),
9098
},

apps/sim/tools/buffer/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,7 @@ export interface BufferCreatePostParams extends BufferBaseParams {
389389
dueAt?: string
390390
saveToDraft?: boolean
391391
media?: unknown
392+
mediaType?: 'auto' | 'image' | 'video'
392393
mediaAltText?: string
393394
}
394395

@@ -400,6 +401,7 @@ export interface BufferEditPostParams extends BufferBaseParams {
400401
dueAt?: string
401402
saveToDraft?: boolean
402403
media?: unknown
404+
mediaType?: 'auto' | 'image' | 'video'
403405
mediaAltText?: string
404406
}
405407

0 commit comments

Comments
 (0)