开发者工作台
开发者服务统一工作台,切换开发配置、接口文档、小程序、离线下载、CDN 与插件等区块。
1. 页面信息
- 网页地址:https://9kvr.cn/user/develop/workbench
- 所属模块:用户中心 · 开发者服务
- 页面用途:开发者服务的统一工作台。左侧(移动端为顶部标签)可切换六个区块:开发初始配置、接口文档服务、小程序工作台、全景离线下载、自定义 CDN 素材、全景插件服务。开发者配置未完成时切换区块会被拦截;自定义 CDN 素材需要开发版及以上会员;接口文档服务中的 API 读写能力需要企业版及以上会员。
- 适用用户:已完成或正在完成开发者配置的用户
- 访问条件:需登录后访问(API 读写能力需企业版及以上会员)
2. 功能说明
- 开发初始配置:配置开发者应用信息与可用平台
- 接口文档服务:查看接口文档
- 小程序工作台:在微信 / 快手 / 百度 / 字节 / QQ 等平台间切换
- 全景离线下载:离线部署相关能力
- 自定义 CDN 素材(需开发版及以上会员)
- 全景插件服务
- 支持通过参数指定初始区块、预置作品与下载来源
3. 业务说明
权限要求
- 开发初始配置:需登录
- 自定义 CDN 素材:需开发版及以上会员
配置前置
在完成开发者基本信息配置前,切换其他区块会被提示「请先配置完成基本信息后再试」。
平台限制
小程序工作台可用平台取决于账号允许的平台范围;开发版及以上会员不受平台校验限制。
接口与 API 的最低会员档位
「接口文档服务」中的 API 能力按会员版本开放:通过 API 获取作品信息(读)、管理全景作品(写)、提交素材制作全景、接收制作结果通知等接口,均在企业版及以上版本开放(开发版不含)。各版本具体权益以 VIP 会员服务页的权益矩阵实时口径为准。
- 读接口(获取作品信息):企业版及以上开放
- 写接口(管理全景作品、提交制作、接收结果通知):企业版及以上开放
- 开发版不含上述 API 读写能力,需升级到企业版及以上
界面可选值
| 可选值 | 取值 | 说明 |
|---|---|---|
| 开发初始配置 | — | — |
| 接口文档服务 | — | — |
| 小程序工作台 | — | — |
| 全景离线下载 | — | — |
| 自定义CDN素材 | — | — |
| 全景插件服务 | — | — |
| 微信小程序 | weixin | — |
| 快手小程序 | kuaishou | — |
| 百度小程序 | baidu | — |
| 字节小程序 | toutiao | — |
| QQ小程序 | — |
<!-- public-api:start -->
开发者公开 API 参考(10 项)
以下为面向开发者公开的正式接口能力,接口地址、请求参数、返回字段与多语言调用示例均来自官方公开文档。运行示例前请把 EXAMPLE_API_KEY / YOUR_WORK_ID / YOUR_MEDIA_ID 等占位符替换为你自己的合法值;POST 接口会产生写入副作用,请勿使用他人密钥或他人作品 ID 反复调用。
1. 使用Api获取作品列表(getVrList())
接口地址
GET https://develop.9kvr.cn/api/develop/listd.html?key=apiKey
请求参数
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| key | string | 是 | 开发者API密钥 |
响应示例
{
"info": {
"total": 310,
"per_page": 12,
"current_page": 1,
"last_page": 26,
"data": [
{
"work_name": "示例作品",
"work_profile": "",
"work_thumb": "https://cdn.example.com/example-pano.jpg",
"work_createtime": "2025-07-15 22:53:05",
"work_status": 2,
"work_makes": 2,
"work_type": 1,
"work_viewid": "YOUR_WORK_ID",
"work_looks": 6,
"mark_douyin_title": null,
"mark_kuaishou_title": null,
"mark_recommend": null,
"mark_boutique": null,
"work_xls_id": 1,
"location_name": null,
"location_address": null,
"location_latitude": null,
"location_longitude": null,
"work_play": "https://develop.9kvr.cn/tour/index?id=YOUR_WORK_ID&key=EXAMPLE_API_KEY",
"work_thumb_last": "https://cdn.example.com/example-pano.jpg",
"mount_title": "示例作品"
}
]
},
"msg": "获取成功",
"code": 1,
"sparrow": 0,
"time": 104,
"ip": "127.0.0.1",
"version": 2
}
响应参数说明
分页信息 (info)
| 参数名 | 类型 | 说明 |
|---|---|---|
| total | number | 总记录数 |
| per_page | number | 每页显示数量 |
| current_page | number | 当前页码 |
| last_page | number | 最后一页页码 |
| data | array | 作品数据列表 |
作品信息 (data)
| 参数名 | 类型 | 说明 |
|---|---|---|
| work_name | string | 作品名称 |
| work_profile | string | 作品描述 |
| work_thumb | string | 作品缩略图 |
| work_createtime | string | 作品创建时间 |
| work_status | number | 作品状态 |
| work_makes | number | 制作状态 |
| work_type | number | 作品类型 |
| work_viewid | string | 作品唯一ID |
| work_looks | number | 浏览次数 |
| mark_douyin_title | string | 抖音标题标记 |
| mark_kuaishou_title | string | 快手标题标记 |
| mark_recommend | string | 推荐标记 |
| mark_boutique | string | 精品标记 |
| work_xls_id | number | 作品关联ID |
| location_name | string | 位置名称 |
| location_address | string | 位置地址 |
| location_latitude | string | 纬度坐标 |
| location_longitude | string | 经度坐标 |
| work_play | string | 作品播放链接 |
| work_thumb_last | string | 最后缩略图 |
| mount_title | string | 挂载标题 |
响应状态
| 参数名 | 类型 | 说明 |
|---|---|---|
| msg | string | 响应消息 |
| code | number | 响应状态码 (1: 成功, 0: 失败) |
| sparrow | number | 系统标识 |
| time | number | 响应时间(毫秒) |
| ip | string | 请求IP地址 |
| version | number | API版本号 |
状态码说明
| 状态码 | 说明 |
|---|---|
| 1 | 成功 |
| 0 | 失败 |
使用说明
- 请确保已获取有效的API密钥
- 该接口返回用户的所有全景作品列表
- 支持分页查询,默认每页12条记录
- 返回数据包含完整的分页信息和作品详情
- 作品播放链接可直接用于webview打开
2. 使用Api获取作品详情(getVrDetail())
接口地址
GET https://develop.9kvr.cn/api/develop/info.html?key=apiKey&id=全景ID
请求参数
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| key | string | 是 | 开发者API密钥 |
| id | string | 是 | 全景作品ID |
响应示例
{
"info": {
"work_name": "示例作品",
"work_profile": "",
"work_thumb": "https://cdn.example.com/example-pano.jpg",
"work_createtime": "2025-07-15 22:53:05",
"work_status": 2,
"work_makes": 2,
"work_type": 1,
"work_viewid": "YOUR_WORK_ID",
"work_looks": 6,
"mark_douyin_title": null,
"mark_kuaishou_title": null,
"mark_recommend": null,
"mark_boutique": null,
"work_xls_id": 1,
"location_name": null,
"location_address": null,
"location_latitude": null,
"location_longitude": null,
"work_play": "https://develop.9kvr.cn/tour/index?id=YOUR_WORK_ID&key=EXAMPLE_API_KEY",
"work_thumb_last": "https://cdn.example.com/example-pano.jpg",
"mount_title": "示例作品"
},
"msg": "获取成功",
"code": 1,
"sparrow": 0,
"time": 28,
"ip": "127.0.0.1",
"version": 2
}
响应参数说明
作品信息 (info)
| 参数名 | 类型 | 说明 |
|---|---|---|
| work_name | string | 作品名称 |
| work_profile | string | 作品描述 |
| work_thumb | string | 作品缩略图 |
| work_createtime | string | 作品创建时间 |
| work_status | number | 作品状态 |
| work_makes | number | 制作状态 |
| work_type | number | 作品类型 |
| work_viewid | string | 作品唯一ID |
| work_looks | number | 浏览次数 |
| mark_douyin_title | string | 抖音标题标记 |
| mark_kuaishou_title | string | 快手标题标记 |
| mark_recommend | string | 推荐标记 |
| mark_boutique | string | 精品标记 |
| work_xls_id | number | 作品关联ID |
| location_name | string | 位置名称 |
| location_address | string | 位置地址 |
| location_latitude | string | 纬度坐标 |
| location_longitude | string | 经度坐标 |
| work_play | string | 作品播放链接 |
| work_thumb_last | string | 最后缩略图 |
| mount_title | string | 挂载标题 |
响应状态
| 参数名 | 类型 | 说明 |
|---|---|---|
| msg | string | 响应消息 |
| code | number | 响应状态码 (1: 成功, 0: 失败) |
| sparrow | number | 系统标识 |
| time | number | 响应时间(毫秒) |
| ip | string | 请求IP地址 |
| version | number | API版本号 |
状态码说明
| 状态码 | 说明 |
|---|---|
| 1 | 成功 |
| 0 | 失败 |
使用说明
- 通过作品ID获取单个全景作品的详细信息
- 包含作品的完整信息,包括基本信息、状态、位置、播放链接等
- 可用于小程序或H5页面展示全景内容
- 作品播放链接可直接用于webview打开
- 支持获取作品的地理位置信息(如果已设置)
3. Webview打开单个全景链接(openWebview())
链接格式
https://develop.9kvr.cn/tour/index?id=作品ID&key=apiKey
参数说明
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| id | string | 是 | 全景作品ID |
| key | string | 是 | 开发者API密钥 |
使用场景
- 小程序Webview组件:在小程序中使用webview组件打开全景页面
- H5页面跳转:在H5页面中直接跳转到全景播放页面
- 分享链接:生成可分享的全景查看链接
注意事项
- 请勿直接在小程序中复制H5链接使用
- 必须使用上述格式的链接作为webview的src
- 确保API密钥有效且具有相应权限
- 建议在小程序webview中设置合适的宽高比例
示例代码
// 小程序webview使用示例
<webview src="https://develop.9kvr.cn/tour/index?id=123456&key=your_api_key"></webview>
4. 使用API制作VR全景(createVrPanorama())
接口地址
POST https://develop.9kvr.cn/api/develop/makeTour.html?key=apiKey
请求参数
基础参数
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| key | string | 是 | 开发者KEY(在开发者配置页面获取) |
| notifier | number | 否 | 是否接受微信模板消息通知, 1 接收, 0 不接收 |
作品信息 (work) - json 格式
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| work_name | string | 是 | 作品名称 |
| work_profile | string | 否 | 作品介绍 |
| work_private | number | 否 | 是否私有, 1 公开, 0 私有 |
| work_password | string | 否 | 访问密码, 必须设置为私有才可设置访问密码 |
素材信息 (media)- json 格式
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| imgs_location | string | 是 | VR素材CDN地址 |
| imgs_filename | string | 是 | VR素材名称(最大支持15个字符) |
| imgs_description | string | 否 | 素材介绍(最大支持60个字符) |
注意: media数组最大支持20组素材
配置信息 (config) - json 格式
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| set_more_menu | number | 否 | 显示更多菜单按钮, 1显示(默认), 0 不显示 |
| set_hide_right | number | 否 | 隐藏右侧整体导航条, 1显示(默认), 0 不显示 |
| set_title_mask | number | 否 | 芊云全景版权小尾巴, 1显示(默认), 0 不显示 |
| work_music | string | 否 | 作品全局背景音乐(仅限mp3文件) |
入参示例
{
"work": {
"work_name": "示例作品",
"work_profile": "作品介绍",
"work_private": "是否私有,1 公开 0私有",
"work_password": "访问密码, 必须设置为私有才可设置访问密码"
},
"key": "开发者KEY(在开发者配置页面获取)",
"media": [
{
"imgs_location": "VR素材CDN地址",
"imgs_filename": "VR素材名称",
"imgs_description": "素材介绍"
},
{
"imgs_location": "... media数组最大支持20组素材",
"imgs_filename": "... VR素材名称(名称最大支持15个字符)",
"imgs_description": "... VR素材介绍(名称最大支持60个字符)"
}
],
"notifier": "是否接受微信模板消息通知, 1 接收, 0 不接收",
"config": {
"set_more_menu": "显示更多菜单按钮; 1显示(默认), 0 不显示",
"set_hide_right": "隐藏右侧整体导航条; 1显示(默认), 0 不显示",
"set_title_mask": "芊云全景版权小尾巴: 1显示(默认), 0 不显示",
"work_music": "作品全局背景音乐(仅限mp3文件)"
}
}
调用示例代码
Node.js 示例
const axios = require('axios');
const FormData = require('form-data');
async function makeTour() {
const apiKey = 'your_api_key_here';
const url = `https://develop.9kvr.cn/api/develop/makeTour.html?key=${apiKey}`;
const formData = new FormData();
// 作品信息
const work = {
work_name: '我的VR作品',
work_profile: '这是一个测试VR作品',
work_private: '1'
};
formData.append('work', JSON.stringify(work));
// 素材信息
const media = [
{
imgs_location: 'https://cdn.example.com/example-pano.jpg',
imgs_filename: '全景图1',
imgs_description: '第一个全景场景'
},
{
imgs_location: 'https://cdn.example.com/pano2.jpg',
imgs_filename: '全景图2',
imgs_description: '第二个全景场景'
}
];
formData.append('media', JSON.stringify(media));
// 通知设置
formData.append('notifier', '1');
// 配置信息
const config = {
set_more_menu: '1',
set_hide_right: '1',
set_title_mask: '1'
};
formData.append('config', JSON.stringify(config));
try {
const response = await axios.post(url, formData, {
headers: {
...formData.getHeaders(),
'Origin': 'https://your-domain.com'
}
});
console.log('成功:', response.data);
return response.data;
} catch (error) {
console.error('失败:', error.response ? error.response.data : error.message);
throw error;
}
}
makeTour();
PHP 示例
<?php
function makeTour() {
$apiKey = 'your_api_key_here';
$url = 'https://develop.9kvr.cn/api/develop/makeTour.html?key=' . $apiKey;
// 准备表单数据
$postData = [
// 作品信息
'work' => json_encode([
'work_name' => '我的VR作品',
'work_profile' => '这是一个测试VR作品',
'work_private' => '1'
]),
// 素材信息
'media' => json_encode([
[
'imgs_location' => 'https://cdn.example.com/example-pano.jpg',
'imgs_filename' => '全景图1',
'imgs_description' => '第一个全景场景'
],
[
'imgs_location' => 'https://cdn.example.com/pano2.jpg',
'imgs_filename' => '全景图2',
'imgs_description' => '第二个全景场景'
]
]),
// 通知设置
'notifier' => '1',
// 配置信息
'config' => json_encode([
'set_more_menu' => '1',
'set_hide_right' => '1',
'set_title_mask' => '1'
])
];
// 初始化 cURL
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query($postData),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Origin: https://your-domain.com',
'Content-Type: application/x-www-form-urlencoded'
]
]);
// 执行请求
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (curl_errno($ch)) {
echo '请求错误: ' . curl_error($ch);
curl_close($ch);
return false;
}
curl_close($ch);
// 解析响应
$result = json_decode($response, true);
if ($httpCode === 200) {
echo "成功: \n";
print_r($result);
return $result;
} else {
echo "失败: HTTP {$httpCode}\n";
print_r($result);
return false;
}
}
makeTour();
?>
Python 示例
import requests
def make_tour():
import json
api_key = 'your_api_key_here'
url = f'https://develop.9kvr.cn/api/develop/makeTour.html?key={api_key}'
# 准备表单数据
data = {
# 作品信息
'work': json.dumps({
'work_name': '我的VR作品',
'work_profile': '这是一个测试VR作品',
'work_private': '1'
}),
# 素材信息
'media': json.dumps([
{
'imgs_location': 'https://cdn.example.com/example-pano.jpg',
'imgs_filename': '全景图1',
'imgs_description': '第一个全景场景'
},
{
'imgs_location': 'https://cdn.example.com/pano2.jpg',
'imgs_filename': '全景图2',
'imgs_description': '第二个全景场景'
}
]),
# 通知设置
'notifier': '1',
# 配置信息
'config': json.dumps({
'set_more_menu': '1',
'set_hide_right': '1',
'set_title_mask': '1'
})
}
# 设置请求头
headers = {
'Origin': 'https://your-domain.com'
}
try:
# 发送 POST 请求
response = requests.post(url, data=data, headers=headers)
response.raise_for_status()
# 解析响应
result = response.json()
print('成功:', result)
return result
except requests.exceptions.RequestException as e:
print(f'请求失败: {e}')
if hasattr(e, 'response') and e.response is not None:
print('响应内容:', e.response.text)
return None
if __name__ == '__main__':
make_tour()
Java 示例
import com.google.gson.Gson;
import okhttp3.*;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MakeTourExample {
private static final String API_KEY = "your_api_key_here";
private static final String API_URL = "https://develop.9kvr.cn/api/develop/makeTour.html?key=" + API_KEY;
public static void main(String[] args) {
try {
makeTour();
} catch (IOException e) {
System.err.println("请求失败: " + e.getMessage());
e.printStackTrace();
}
}
public static void makeTour() throws IOException {
Gson gson = new Gson();
// 作品信息
Map<String, String> work = new HashMap<>();
work.put("work_name", "我的VR作品");
work.put("work_profile", "这是一个测试VR作品");
work.put("work_private", "1");
String workJson = gson.toJson(work);
// 素材信息
Map<String, String> media1 = new HashMap<>();
media1.put("imgs_location", "https://cdn.example.com/example-pano.jpg");
media1.put("imgs_filename", "全景图1");
media1.put("imgs_description", "第一个全景场景");
Map<String, String> media2 = new HashMap<>();
media2.put("imgs_location", "https://cdn.example.com/pano2.jpg");
media2.put("imgs_filename", "全景图2");
media2.put("imgs_description", "第二个全景场景");
List<Map<String, String>> mediaList = Arrays.asList(media1, media2);
String mediaJson = gson.toJson(mediaList);
// 配置信息
Map<String, String> config = new HashMap<>();
config.put("set_more_menu", "1");
config.put("set_hide_right", "1");
config.put("set_title_mask", "1");
String configJson = gson.toJson(config);
// 构建表单数据
RequestBody formBody = new FormBody.Builder()
.add("work", workJson)
.add("media", mediaJson)
.add("notifier", "1")
.add("config", configJson)
.build();
// 创建请求
Request request = new Request.Builder()
.url(API_URL)
.post(formBody)
.addHeader("Origin", "https://your-domain.com")
.build();
// 发送请求
OkHttpClient client = new OkHttpClient();
try (Response response = client.newCall(request).execute()) {
if (response.isSuccessful() && response.body() != null) {
String responseBody = response.body().string();
System.out.println("成功: " + responseBody);
// 解析响应
@SuppressWarnings("unchecked")
Map<String, Object> result = gson.fromJson(responseBody, Map.class);
System.out.println("返回码: " + result.get("code"));
System.out.println("消息: " + result.get("msg"));
} else {
System.err.println("请求失败: HTTP " + response.code());
if (response.body() != null) {
System.err.println("响应内容: " + response.body().string());
}
}
}
}
}
依赖配置 (Maven):
<dependencies>
<!-- OkHttp -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.12.0</version>
</dependency>
<!-- Gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>
</dependencies>
依赖配置 (Gradle):
dependencies {
implementation 'com.squareup.okhttp3:okhttp:4.12.0'
implementation 'com.google.code.gson:gson:2.10.1'
}
返回示例
成功响应
{
"info": {
"work_id": "作品Id"
},
"msg": "投递成功",
"code": 1
}
失败响应
{
"info": {},
"msg": "投递失败的原因",
"code": 0
}
回调通知
成功回调
{
"info": {
"work_id": "作品Id",
"media": {
"imgs_filename": "素材名称",
"imgs_thumb": "素材预览图",
"imgs_id": "素材ID",
"imgs_web_link": "素材预览链接",
"imgs_mini_link": "小程序预览链接"
},
"link": {
"web": "网页链接",
"mini": "小程序链接"
},
"work": {
"work_thumb": "全景作品封面",
"work_uid": "开发者 UID",
"work_viewid": "YOUR_WORK_ID"
}
},
"msg": "全景制作完成",
"code": 1
}
失败回调
{
"info": {},
"msg": "全景制作失败的原因",
"code": 0
}
注意事项
⚠️ 重要提醒:
- 必须设置回调地址,否则无法接收制作完成通知
- 请使用
formData格式发送POST请求 - 在请求头设置来源域名
- 全景图片支持 JPG、JPEG、WebP 格式
- 素材名称最大支持15个字符
- 素材介绍最大支持60个字符
- media数组最大支持20组素材
- 每日最大发布作品数量与主站保持一致(60个),如需扩容请咨询客服
状态码说明
| 状态码 | 说明 |
|---|---|
| 1 | 成功 |
| 0 | 失败 |
5. 发布全景素材(makeMedia())
接口地址
POST https://develop.9kvr.cn/api/develop/makeMedia.html?key=apiKey
请求参数
基础参数
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| key | string | 是 | 开发者KEY(在开发者配置页面获取) |
素材信息 (media) - json 格式
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| imgs_location | string | 是 | VR素材CDN地址 |
| imgs_filename | string | 否 | VR素材名称(最大支持15个字符, 默认"来自API素材") |
| imgs_description | string | 否 | 素材介绍(最大支持60个字符, 默认"来自API素材") |
注意: media 可传单个对象或数组, 数组最大支持20组素材 注意: 仅生成素材, 不创建全景作品, 如需制作作品请使用 使用API制作VR全景
入参示例
单个素材
{
"media": {
"imgs_location": "https://cdn.example.com/pano.jpg",
"imgs_filename": "全景素材1",
"imgs_description": "我的第一个全景素材"
}
}
多个素材
{
"media": [
{
"imgs_location": "https://cdn.example.com/pano1.jpg",
"imgs_filename": "全景素材1",
"imgs_description": "第一个全景素材"
},
{
"imgs_location": "https://cdn.example.com/pano2.jpg",
"imgs_filename": "全景素材2",
"imgs_description": "第二个全景素材"
}
]
}
调用示例代码
Node.js 示例
const axios = require('axios');
async function makeMedia() {
const apiKey = 'your_api_key_here';
const url = `https://develop.9kvr.cn/api/develop/makeMedia.html?key=${apiKey}`;
const formData = new FormData();
// 素材信息(单个对象或数组)
const media = [
{
imgs_location: 'https://cdn.example.com/pano1.jpg',
imgs_filename: '全景素材1',
imgs_description: '第一个全景素材'
},
{
imgs_location: 'https://cdn.example.com/pano2.jpg',
imgs_filename: '全景素材2',
imgs_description: '第二个全景素材'
}
];
formData.append('media', JSON.stringify(media));
try {
const response = await axios.post(url, formData, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
});
console.log('成功:', response.data);
return response.data;
} catch (error) {
console.error('失败:', error.response ? error.response.data : error.message);
throw error;
}
}
makeMedia();
Python 示例
import requests
import json
def make_media():
api_key = 'your_api_key_here'
url = f'https://develop.9kvr.cn/api/develop/makeMedia.html?key={api_key}'
# 素材信息(单个对象或数组)
media = [
{
'imgs_location': 'https://cdn.example.com/pano1.jpg',
'imgs_filename': '全景素材1',
'imgs_description': '第一个全景素材'
}
]
data = {
'media': json.dumps(media, ensure_ascii=False)
}
try:
response = requests.post(url, data=data)
response.raise_for_status()
result = response.json()
print('成功:', result)
return result
except requests.exceptions.RequestException as e:
print(f'请求失败: {e}')
return None
if __name__ == '__main__':
make_media()
返回示例
成功响应
{
"code": 1,
"msg": "发布素材成功",
"info": {
"media_id": [10001]
}
}
失败响应
{
"code": 0,
"msg": "发布素材失败的原因",
"info": {}
}
注意事项
⚠️ 重要提醒:
- 素材CDN地址必填, 图片支持 JPG、JPEG、WebP 格式
- 素材名称最大支持15个字符
- 素材介绍最大支持60个字符
- media数组最大支持20组素材
- 本接口仅生成素材ID, 不创建全景作品, 不触发制作
- 每日最大发布数量与主站保持一致(60个), 如需扩容请咨询客服
状态码说明
| 状态码 | 说明 |
|---|---|
| 1 | 成功 |
| 0 | 失败 |
6. 通过素材库制作VR全景(makeTourByMedia())
接口地址
POST https://develop.9kvr.cn/api/develop/makeTourByMedia.html?key=apiKey
请求参数
基础参数
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| key | string | 是 | 开发者KEY(在开发者配置页面获取) |
作品信息 (work) - json 格式
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| work_name | string | 是 | 作品名称 |
| work_profile | string | 否 | 作品介绍 |
| work_music | string | 否 | 作品全局背景音乐(仅限mp3文件) |
素材信息 (media) - json 格式
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| imgs_workid | string | 是 | 素材ID(来自 发布全景素材 或 查询素材列表) |
注意:
- media 可传单个对象或数组, 数组最大支持20组素材
- 素材必须是当前开发者账号下已发布的素材, 且素材制作已完成
- 该接口直接使用素材库已有的成品素材制作作品, 无需再传CDN地址
入参示例
{
"work": {
"work_name": "示例作品",
"work_profile": "作品介绍"
},
"key": "开发者KEY(在开发者配置页面获取)",
"media": [
{
"imgs_workid": "YOUR_MEDIA_ID"
},
{
"imgs_workid": "YOUR_MEDIA_ID"
}
]
}
调用示例代码
Node.js 示例
const axios = require('axios');
async function makeTourByMedia() {
const apiKey = 'your_api_key_here';
const url = `https://develop.9kvr.cn/api/develop/makeTourByMedia.html?key=${apiKey}`;
const formData = new FormData();
formData.append('work', JSON.stringify({
work_name: '我的作品',
work_profile: '作品介绍'
}));
// 素材ID列表(来自素材列表接口, 值为 imgs_workid)
formData.append('media', JSON.stringify([
{ imgs_workid: 'YOUR_MEDIA_ID' },
{ imgs_workid: 'YOUR_MEDIA_ID' }
]));
try {
const response = await axios.post(url, formData, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
});
console.log('成功:', response.data);
return response.data;
} catch (error) {
console.error('失败:', error.response ? error.response.data : error.message);
throw error;
}
}
makeTourByMedia();
返回示例
成功响应
{
"code": 1,
"msg": "制作成功",
"info": "作品viewid"
}
失败响应
{
"code": 0,
"msg": "发布失败的原因",
"info": []
}
注意事项
⚠️ 重要提醒:
7. 查询素材列表(getMediaList())
接口地址
POST https://develop.9kvr.cn/api/develop/getMediaList.html?key=apiKey
请求参数
基础参数
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| key | string | 是 | 开发者KEY(在开发者配置页面获取) |
| page | number | 否 | 页码, 默认1 |
| limit | number | 否 | 每页数量, 默认12 |
| status | number | 否 | 素材状态, 0全部(默认), 1制作中, 2制作完成 |
| keyword | string | 否 | 素材名称关键词搜索 |
入参示例
POST ?key=apiKey&page=1&limit=12&status=0
调用示例代码
Node.js 示例
const axios = require('axios');
async function getMediaList() {
const apiKey = 'your_api_key_here';
const url = `https://develop.9kvr.cn/api/develop/getMediaList.html?key=${apiKey}`;
const formData = new FormData();
formData.append('page', '1');
formData.append('limit', '12');
formData.append('status', '0');
try {
const response = await axios.post(url, formData, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
});
console.log('成功:', response.data);
return response.data;
} catch (error) {
console.error('失败:', error.response ? error.response.data : error.message);
throw error;
}
}
getMediaList();
返回示例
成功响应
{
"code": 1,
"msg": "获取成功",
"info": {
"list": [
{
"id": "YOUR_MEDIA_ID",
"imgs_workid": "YOUR_MEDIA_ID",
"imgs_filename": "客厅全景",
"imgs_description": "客厅裸眼全景图",
"imgs_location": "https://cdn.example.com/panorama.jpg",
"imgs_thumb": "https://cdn.example.com/example-pano.jpg",
"imgs_size": 2.5,
"imgs_width": 8400,
"imgs_height": 4100,
"imgs_status": 2,
"imgs_type": 1,
"imgs_version": 1,
"imgs_slice_type": 1,
"imgs_createtime": "2026-08-08 10:00:00"
}
],
"page": 1,
"limit": 12,
"total": 30,
"total_page": 3
}
}
失败响应
{
"code": 0,
"msg": "权限验证失败,请检查key是否正确",
"info": []
}
返回字段说明
| 字段 | 类型 | 说明 |
|---|---|---|
| id | string | 素材ID(与 imgs_workid 相同, 制作作品时使用) |
| imgs_workid | string | 素材对外ID(无规律字符串) |
| imgs_filename | string | 素材名称 |
| imgs_description | string | 素材介绍 |
| imgs_thumb | string | 素材缩略图(可直接访问的完整URL) |
| imgs_size | number | 素材大小(M) |
| imgs_width | number | 素材宽度 |
| imgs_height | number | 素材高度 |
| imgs_status | number | 素材状态, 1制作中, 2制作完成 |
| imgs_type | number | 素材类型, 1全景图片 |
| imgs_version | number | 素材版本 |
| imgs_slice_type | number | 素材切片类型 |
| imgs_createtime | string | 素材创建时间 |
注意事项
⚠️ 重要提醒:
- 素材列表查询的素材必须是当前开发者发布的素材
status=2表示素材已完成, 用于 通过素材库制作VR全景
8. 使用小程序跳转打开全景(navigateToMiniProgram())
平台介绍
芊云全景作为一个开放性较强的全景开发平台,我们也赋予了平台一直开发者支持。下面简单介绍一下其他小程序打开芊云全景小程序的方案。
兼容性说明
基础库支持
- 基础库版本: 1.3.0 开始支持,低版本需做兼容处理
- Promise风格: 支持
- 页面权限: 小程序不能在插件页面中调用该接口,插件也不能在小程序页面中调用该接口
- 小程序插件: 支持,需要小程序基础库版本不低于 2.18.1
- 微信 Windows 版: 支持
- 微信 Mac 版: 支持
小程序信息
| 信息类型 | 值 | 说明 |
|---|---|---|
| AppID | wx9f77d65eb4eff65b | 芊云全景小程序AppID |
| 环境版本 | release | 正式环境 |
页面路径
| 页面类型 | 路径 | 说明 |
|---|---|---|
| 小程序首页 | pages/index/index | 小程序主页面 |
| 全景加载页面 | pages/tour/wait?id=全景ID | 全景加载等待页面 |
| 全景播放器详情页 | pages/tour/index?id=全景ID | 全景播放器页面 |
| 个人主页 | pages/index/home?id=自己UID | 用户个人主页 |
跳转方式
基础跳转
wx.navigateToMiniProgram({
appId: 'wx9f77d65eb4eff65b',
path: 'pages/index/index',
extraData: {},
envVersion: 'release',
success(res) {
console.log('打开成功');
},
fail(err) {
console.error('打开失败', err);
}
});
带参数跳转(当前暂未启用)
⚠️ 注意: 当前暂未启用,也许在后续某个版本启用
直接打开播放器详情页面,需要携带芊云全景提供的 secretKey 参数,否则即使打开,也会强制跳转到加载页面。
wx.navigateToMiniProgram({
appId: 'wx9f77d65eb4eff65b',
path: 'pages/tour/wait?id=全景ID',
extraData: {
secretKey: '开发者服务页面获取',
appName: '您自己小程序名称'
},
envVersion: 'release',
success(res) {
console.log('打开成功');
},
fail(err) {
console.error('打开失败', err);
}
});
参数说明
跳转参数 (extraData)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| secretKey | string | 否 | 开发者服务页面获取的密钥(当前暂未启用) |
| appName | string | 否 | 您自己小程序名称,携带后若返回小程序则显示来源小程序名称,否则只显示"返回来源小程序" |
返回参数 (extraData)
| 参数名 | 类型 | 说明 |
|---|---|---|
| comeBack | string | 固定值 'vr29' |
| id | string | 打开全景的ID |
返回来源小程序
芊云全景逻辑处理
若是来自其他小程序,等待页面的"返回"或者"查看更多全景"将变为"返回来源小程序",返回来源小程序后携带参数如下:
wx.navigateBackMiniProgram({
extraData: {
comeBack: 'vr29',
id: '打开全景的ID'
},
success(res) {
console.log('返回成功');
},
fail(err) {
console.error('返回失败', err);
}
});
开发者集成
获取来源参数
开发者可以自行在自己小程序的生命周期内获取来源参数!
// 在小程序页面中获取来源参数
onLoad(options) {
if (options.comeBack === 'vr29') {
console.log('来自芊云全景小程序');
console.log('全景ID:', options.id);
}
}
注意事项
- 需要在小程序后台配置跳转关系
- 确保secretKey有效且具有跳转权限(当前暂未启用)
- 建议在跳转前检查用户登录状态
- 支持传递自定义参数到全景页面
- 注意基础库版本兼容性
- 开发者可以自行处理返回参数
相关文档
9. 芊云全景打开半屏小程序VR(openEmbeddedMiniProgram())
功能说明
在芊云全景H5页面中,可以打开半屏小程序来展示VR内容,提供更好的用户体验。
重要提醒
⚠️ 注意: 自己小程序打开芊云全景为半屏小程序目前可以免费使用。 但是若直接打开全景播放器(pages/tour/index)有广告,跳转其他页面无广告。 开通 VIP 可去除所有广告,其他流程均不受影响。
配置步骤
1. 添加小程序权限
在小程序后台申请半屏能力,添加芊云全景小程序。
2. 搜索并添加小程序
- 搜索方式: 搜索"芊云全景"
- AppID: 如果搜索不到,请填写
wx9f77d65eb4eff65b进行搜索 - 审核: 添加后等待管理员审核,也可以联系客服进行审核
小程序页面路径
| 页面类型 | 路径 | 说明 |
|---|---|---|
| 小程序首页 | pages/index/index | 小程序主页面 |
| 全景加载页面 | pages/tour/wait?id=全景ID | 全景加载等待页面 |
| 全景播放器详情页 | pages/tour/index?id=全景ID | 全景播放器页面 |
| 个人主页 | pages/index/home?id=自己UID | 用户个人主页 |
接入代码示例
打开半屏小程序
wx.openEmbeddedMiniProgram({
appId: 'wx9f77d65eb4eff65b',
path: 'pages/tour/index?id=全景ID',
extraData: {
secretKey: '开发者服务页面获取',
appName: '您自己小程序名称'
},
envVersion: 'release',
success(res) {
console.log('打开半屏小程序成功');
},
fail(err) {
console.error('打开失败', err);
}
});
返回来源小程序
wx.navigateBackMiniProgram({
extraData: {
comeBack: 'vr29',
id: '打开全景的ID'
},
success(res) {
console.log('返回成功');
},
fail(err) {
console.error('返回失败', err);
}
});
参数说明
打开参数 (extraData)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| secretKey | string | 是 | 开发者服务页面获取的密钥 |
| appName | string | 否 | 您自己小程序名称,携带后若返回小程序则显示来源小程序名称,否则只显示"返回来源小程序" |
返回参数 (extraData)
| 参数名 | 类型 | 说明 |
|---|---|---|
| comeBack | string | 固定值 'vr29' |
| id | string | 打开全景的ID |
获取参数方法
如何获取全景ID
打开任意一个作品获取 id=xxx,其中的 xxx 就是全景ID
如何获取 secretKey
在芊云开发者服务平台获取开发者密钥
逻辑处理说明
芊云全景逻辑处理
若是来自其他小程序,等待页面的"返回"或者"查看更多全景"将变为"返回来源小程序",返回来源小程序后携带参数如上所示。
注意事项
- 半屏小程序需要特殊申请和审核
- 建议在小程序中提供返回按钮
- 注意半屏小程序的尺寸限制
- 确保H5页面和小程序的数据同步
- 直接打开播放器详情页面,需要携带芊云全景提供的 secretKey 参数,否则即使打开,也会强制跳转到加载页面
相关文档
10. 使用iframe打开全景(embedIframe())
功能说明
使用iframe标签在网页中嵌入芊云全景播放器,实现无缝的全景展示体验。
基础用法
简单iframe嵌入
<iframe
src="https://develop.9kvr.cn/tour/index?id=全景ID&key=apiKey"
width="100%"
height="600px"
frameborder="0">
</iframe>
全屏无边框iframe
<iframe
src="https://develop.9kvr.cn/tour/index?id=全景ID&key=apiKey"
style="width: 100vw; height: 100vh; border: none; position: fixed; top: 0; left: 0; z-index: 9999;">
</iframe>
参数说明
iframe属性
| 属性名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| src | string | 是 | 全景播放器URL |
| width | string | 否 | 宽度,建议使用100%或100vw |
| height | string | 否 | 高度,建议使用100vh实现全屏 |
| frameborder | string | 否 | 边框,建议设置为"0"或"no" |
| scrolling | string | 否 | 滚动条,建议设置为"no" |
URL参数
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| id | string | 是 | 全景作品ID |
| key | string | 是 | 开发者API密钥 |
样式配置
基础样式
.vr-iframe {
width: 100%;
height: 600px;
border: none;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
全屏样式
.vr-iframe-fullscreen {
width: 100vw;
height: 100vh;
border: none;
position: fixed;
top: 0;
left: 0;
z-index: 9999;
background: #000;
}
响应式样式
.vr-iframe-responsive {
width: 100%;
height: 0;
padding-bottom: 56.25%; /* 16:9 宽高比 */
position: relative;
border: none;
}
.vr-iframe-responsive iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
完整示例
HTML结构
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>全景展示</title>
<style>
body {
margin: 0;
padding: 0;
font-family: Arial, sans-serif;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.vr-iframe {
width: 100%;
height: 600px;
border: none;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.fullscreen-btn {
position: fixed;
top: 20px;
right: 20px;
z-index: 10000;
padding: 10px 20px;
background: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.fullscreen-iframe {
width: 100vw;
height: 100vh;
border: none;
position: fixed;
top: 0;
left: 0;
z-index: 9999;
background: #000;
}
</style>
</head>
<body>
<div class="container">
<h1>全景展示</h1>
<iframe
id="vrIframe"
class="vr-iframe"
src="https://develop.9kvr.cn/tour/index?id=全景ID&key=apiKey">
</iframe>
</div>
<button class="fullscreen-btn" onclick="toggleFullscreen()">全屏显示</button>
<script>
function toggleFullscreen() {
const iframe = document.getElementById('vrIframe');
if (iframe.classList.contains('fullscreen-iframe')) {
iframe.classList.remove('fullscreen-iframe');
iframe.classList.add('vr-iframe');
} else {
iframe.classList.remove('vr-iframe');
iframe.classList.add('fullscreen-iframe');
}
}
// 监听ESC键退出全屏
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {
const iframe = document.getElementById('vrIframe');
if (iframe.classList.contains('fullscreen-iframe')) {
toggleFullscreen();
}
}
});
</script>
</body>
</html>
移动端适配
移动端样式
@media screen and (max-width: 768px) {
.vr-iframe {
height: 400px;
}
.fullscreen-iframe {
width: 100vw;
height: 100vh;
}
}
注意事项
⚠️ 重要提醒:
- 确保全景ID和API密钥正确
- 建议使用HTTPS协议访问iframe内容
- 注意跨域问题,确保域名已授权
- 全屏模式下注意z-index层级管理
- 移动端建议使用响应式设计
- 注意iframe加载性能优化
常见问题
Q: iframe无法加载怎么办?
A: 检查URL参数是否正确,确保网络连接正常,查看浏览器控制台错误信息。
Q: 如何实现自适应高度?
A: 可以使用JavaScript监听iframe内容高度变化,动态调整iframe高度。
Q: 如何去除iframe边框?
A: 设置frameborder="0"和CSS中的border: none。
Q: 全屏模式下如何退出?
A: 可以监听ESC键或添加退出按钮,切换CSS类名即可。
相关文档
<!-- public-api:end -->
4. 使用说明
- 登录后进入开发者工作台
- 在「开发初始配置」中完成基本信息配置
- 切换到接口文档、小程序工作台、离线下载等区块
- 如需自定义 CDN 素材,先开通开发版及以上会员
5. 规则与限制
- 自定义 CDN 素材需开发版及以上会员
- API 读写能力(获取作品信息、管理全景作品、提交制作、接收结果通知)需企业版及以上会员,开发版不含
- 未完成开发者基本信息配置时,切换区块会提示先完成配置
- 未登录时不进行额外初始化
6. 状态与异常
- 加载中:检查登录态与会员等级
- 正常:可切换各区块
- 受限:自定义 CDN 区块未达要求时展示开通提示
- 未完成基本信息配置时切换区块提示先配置
- 未开通开发版会员时自定义 CDN 区块不可用
7. 相关文档
目录导航
- 所属目录索引:/user/develop
- 上一篇:开发者平台首页
同目录文档(5)
相关主题
8. 文档信息
- 事实来源:芊云全景官网页面核验
- 文档构建时间:2026-10-21 00:05:20
- 数据获取/快照时间:不适用(静态产品知识)
- 可信状态:已核验