照片
使用 listingsapi-js SDK 为地点添加徽标、封面和图库照片,将主图标为星标,并移除过时的文件。
照片通过公开 URL 关联到地点:您托管图片,平台获取并存储图片,然后发布平台从中获取图片。每个地点保留 1 张标志图片、1 张封面图片和任意数量的附加照片,其中最多可为 4 张附加照片加星标。所有照片方法都位于 ListingsAPI 客户端实例上,并以地点 ID 限定操作范围。数字形式的地点 ID 会自动编码,因此 1800289 和 'TG9jYXRpb246MTgwMDI4OQ==' 可以互换使用。
上传成功时有 2 种返回形式。小批量上传会在当前请求中直接完成存储,并返回处理完成的媒体文件;大批量上传则会加入队列,并返回供您轮询的请求 ID。请根据是否存在 request.requestId 来选择处理分支,而不是自行统计图片数量。
fetchLocationPhotos
列出关联到地点的所有媒体文件:标志图片、封面图片和附加图片。上传前调用此方法可查看缺少哪些图片,并获取加星标和移除方法所需的照片 ID。
const photos = await client.fetchLocationPhotos(1800289); for (const p of photos) {console.log(p.id, p.categoryName, p.starred, p.url);}参数
解析结果为 mediaFilesOfLocation 数组,默认为空数组。每个文件都包含 id、url、thumbnailUrl、categoryName(Logo、Cover 或 Additional)、categoryId、fileType、starred 和 viewsCount。没有媒体文件的地点会返回空数组,这是新建地点的正常响应,并非错误。
addLocationPhotos
将 1 张或多张图片关联到地点。每个条目都需要在 photo 中提供可公开访问的 JPEG 或 PNG URL,并在 type 中指定图片的用途。
const result = await client.addLocationPhotos(1800289, [{ photo: 'https://cdn.example.com/brightsmile/logo.png', type: 'LOGO' },{ photo: 'https://cdn.example.com/brightsmile/storefront.jpg', type: 'ADDITIONAL' },]); if (result.request?.requestId) {// Queued: poll fetchPhotoUploadStatus with this idconsole.log('queued', result.request.requestId);} else {for (const p of result.photos ?? []) { console.log('stored', p.id, p.type);}}照片选项
参数
解析结果为 addLocationPhotos 载荷:success、photos、request、errors 和 clientMutationId。在当前请求中直接完成的上传中,photos 包含新增的媒体文件,每个文件都包含 id、databaseId、url、thumbnailUrl、type 和 starred,而 request 为空对象。在加入队列的上传中,photos 为空,且 request.requestId 已设置。两种情况均表示成功。
图片必须为 JPEG 或 PNG 格式,且不同用途的图片须遵守发布平台相应的尺寸规则。每个地点仅保留 1 张标志图片和 1 张封面图片,因此重新上传任意一种都会替换现有图片。请准确设置 type:图库图片应使用 ADDITIONAL。
fetchPhotoUploadStatus
轮询已加入队列的上传,直到其进入最终状态。使用 addLocationPhotos 返回的 request.requestId。
let job = await client.fetchPhotoUploadStatus('e6c2d9d9-9a18-4015-88cf-9a4e19a6f49a'); while (job.status === 'PROCESSING') {await new Promise((resolve) => setTimeout(resolve, 5000));job = await client.fetchPhotoUploadStatus('e6c2d9d9-9a18-4015-88cf-9a4e19a6f49a');} for (const p of job.photos ?? []) {if (p.status === 'ERROR') { console.log('rejected:', p.errorMessage);}}参数
载荷包含 requestId、值为 PROCESSING、SUCCESS 或 ERROR 的任务 status,以及 photos 数组,数组中的每个条目都包含 mediaId、locationId、各自的 status 和一个 errorMessage。
任务的 status 表示处理已完成,并不表示所有图片都已成功存储。SUCCESS 任务中仍可能包含被拒绝的照片,因此一旦任务不再处于 PROCESSING 状态,就应遍历 photos 并读取每个条目。每次轮询之间请间隔几秒:连续循环轮询很容易触发速率限制。仅轮询近期上传返回的 ID,因为已过期的 ID 永远不会进入最终状态。
starLocationPhotos
为附加照片加星标,使其在支持精选图片的发布平台上优先展示,或传入 false 取消星标。
// Feature a photoconst result = await client.starLocationPhotos(1800289,['TWVkaWFGaWxlOjU4Nzg5MzI='],true,); console.log(result.success, result.photos[0].starred); // Reverse itawait client.starLocationPhotos(1800289, ['TWVkaWFGaWxlOjU4Nzg5MzI='], false);参数
解析结果为 starUnstarLocationPhotos 载荷:success、photos 和单个 error 对象,而不是 errors 数组。每张返回的照片都包含 id、databaseId、url、thumbnailUrl、type 及其新的 starred 值。
只有 ADDITIONAL 照片可以加星标;标志图片和封面图片已属于主要图片。每个地点最多可有 4 张带星标的照片,检查时会将已有星标的照片与本次调用中的照片合计,因此超过 4 张会导致整个请求失败。
removeLocationPhotos
根据照片 ID 解除照片与地点的关联。可用此方法移除过时的图库图片,或撤销错误的导入。
const result = await client.removeLocationPhotos(1800289, ['TWVkaWFGaWxlOjU4Nzg5MzI=',]); console.log(result.removedCount); // 1参数
解析结果为 removeLocationPhotos 载荷:removedCount、errors 和 clientMutationId。removedCount 为 0 且 errors 为 null,表示您发送的所有 ID 均未关联到该地点,您可以据此识别已被移除的 ID。
此处只能移除 ADDITIONAL 照片。要替换标志图片或封面图片,请上传新图片。移除媒体文件会解除其与地点的关联,但不一定会将文件从账户媒体库中彻底删除。
常见的图库更新
import { ListingsAPI } from 'listingsapi-js'; const client = new ListingsAPI(); // reads LISTINGSAPI_KEY from envconst LOCATION_ID = 1800289; // Brightsmile Dental // 1. See what is already attachedconst existing = await client.fetchLocationPhotos(LOCATION_ID);const hasLogo = existing.some((p) => p.categoryName === 'Logo'); // 2. Upload only what is missing, plus the new hero shotconst uploaded = await client.addLocationPhotos(LOCATION_ID, [...(hasLogo ? [] : [{ photo: 'https://cdn.example.com/brightsmile/logo.png', type: 'LOGO' as const }]),{ photo: 'https://cdn.example.com/brightsmile/hero-2026.jpg', type: 'ADDITIONAL' as const },]); // 3. Feature the new hero shotconst hero = (uploaded.photos ?? []).find((p) => p.type === 'ADDITIONAL');if (hero) {await client.starLocationPhotos(LOCATION_ID, [hero.id], true);} // 4. Retire last season's imagesconst stale = existing.filter((p) => p.categoryName === 'Additional' && !p.starred).map((p) => p.id);if (stale.length > 0) {const removed = await client.removeLocationPhotos(LOCATION_ID, stale);console.log('removed ' + removed.removedCount + ' stale photos');}