feat: 小程序剧本、个人信息页、AI 运行时及 TTS 服务优化
This commit is contained in:
@@ -176,7 +176,7 @@ const pagePath = '/pages/life-event/form'
|
|||||||
const { capsuleTopReservePx } = useMenuButtonSafeArea({ extraTopPx: 10 })
|
const { capsuleTopReservePx } = useMenuButtonSafeArea({ extraTopPx: 10 })
|
||||||
const saving = ref(false)
|
const saving = ref(false)
|
||||||
const assisting = ref(false)
|
const assisting = ref(false)
|
||||||
const assistWriter = useTypewriterStream({ interval: 24, step: 1 })
|
const assistWriter = useTypewriterStream({ interval: 32, step: 1 })
|
||||||
const currentYear = new Date().getFullYear()
|
const currentYear = new Date().getFullYear()
|
||||||
|
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
|
|||||||
@@ -296,7 +296,22 @@ const isFavorite = (script) => {
|
|||||||
return Boolean(script.isFavorite || script.favorite || localFavorites.value[String(script.id)])
|
return Boolean(script.isFavorite || script.favorite || localFavorites.value[String(script.id)])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const openScriptChat = (script) => {
|
||||||
|
if (!script?.id || String(script.id).startsWith('demo-')) return
|
||||||
|
uni.setStorageSync('pending_open_script_chat', {
|
||||||
|
id: script.id
|
||||||
|
})
|
||||||
|
uni.$emit('switchTab', 'script')
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.$emit('openScriptChat', { id: script.id, script })
|
||||||
|
}, 80)
|
||||||
|
}
|
||||||
|
|
||||||
const viewScript = (script) => {
|
const viewScript = (script) => {
|
||||||
|
openScriptChat(script)
|
||||||
|
}
|
||||||
|
|
||||||
|
const openScriptDetail = (script) => {
|
||||||
if (!script?.id || String(script.id).startsWith('demo-')) return
|
if (!script?.id || String(script.id).startsWith('demo-')) return
|
||||||
uni.navigateTo({ url: `/pages/main/ScriptDetailView?id=${script.id}` })
|
uni.navigateTo({ url: `/pages/main/ScriptDetailView?id=${script.id}` })
|
||||||
}
|
}
|
||||||
@@ -346,7 +361,7 @@ const toggleViewMode = () => {
|
|||||||
const openScriptMenu = (script) => {
|
const openScriptMenu = (script) => {
|
||||||
const favorite = isFavorite(script)
|
const favorite = isFavorite(script)
|
||||||
uni.showActionSheet({
|
uni.showActionSheet({
|
||||||
itemList: [favorite ? '取消收藏' : '收藏剧本', '查看详情', '映射路径'],
|
itemList: [favorite ? '取消收藏' : '收藏剧本', '继续生成', '查看详情'],
|
||||||
success: ({ tapIndex }) => {
|
success: ({ tapIndex }) => {
|
||||||
if (tapIndex === 0) {
|
if (tapIndex === 0) {
|
||||||
const next = { ...localFavorites.value }
|
const next = { ...localFavorites.value }
|
||||||
@@ -357,23 +372,11 @@ const openScriptMenu = (script) => {
|
|||||||
uni.showToast({ title: favorite ? '已取消收藏' : '已收藏', icon: 'success' })
|
uni.showToast({ title: favorite ? '已取消收藏' : '已收藏', icon: 'success' })
|
||||||
}
|
}
|
||||||
if (tapIndex === 1) viewScript(script)
|
if (tapIndex === 1) viewScript(script)
|
||||||
if (tapIndex === 2) mapScript(script)
|
if (tapIndex === 2) openScriptDetail(script)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapScript = async (script) => {
|
|
||||||
if (!script?.id || String(script.id).startsWith('demo-')) {
|
|
||||||
uni.showToast({ title: '示例剧本暂不可映射', icon: 'none' })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const res = await store.selectScript(script.id)
|
|
||||||
if (!res.success) {
|
|
||||||
uni.showToast({ title: res.error || '映射失败', icon: 'none' })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
uni.navigateTo({ url: '/pages/main/PathView' })
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ const { capsuleTopReservePx } = useMenuButtonSafeArea({ extraTopPx: 10 })
|
|||||||
|
|
||||||
const pathData = ref(null)
|
const pathData = ref(null)
|
||||||
const pathGenerating = ref(false)
|
const pathGenerating = ref(false)
|
||||||
const pathWriter = useTypewriterStream({ interval: 24, step: 1 })
|
const pathWriter = useTypewriterStream({ interval: 32, step: 1 })
|
||||||
|
|
||||||
const selectedScript = computed(() => {
|
const selectedScript = computed(() => {
|
||||||
return store.scripts.find(s => s.isSelected)
|
return store.scripts.find(s => s.isSelected)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<view class="topbar" :style="topbarStyle">
|
<view class="topbar" :style="topbarStyle">
|
||||||
<button class="back-btn" @click="goBack">‹</button>
|
<button class="back-btn" @click="goBack">‹</button>
|
||||||
<text class="top-title">人生剧本 ✦</text>
|
<text class="top-title">人生剧本 ✦</text>
|
||||||
<button class="save-btn kos-pill" @click="selectCurrent">映射</button>
|
<button class="save-btn kos-pill" @click="continueCurrent">继续</button>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<scroll-view class="scroll" scroll-y :show-scrollbar="false">
|
<scroll-view class="scroll" scroll-y :show-scrollbar="false">
|
||||||
@@ -28,10 +28,6 @@
|
|||||||
<text class="stat-label">字数</text>
|
<text class="stat-label">字数</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="audio-inline" @click="trackTtsClick">
|
|
||||||
<text class="audio-inline-icon">▶</text>
|
|
||||||
<text class="audio-inline-text">{{ detailTtsButtonText }}</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="tabs kos-card">
|
<view class="tabs kos-card">
|
||||||
@@ -56,7 +52,11 @@
|
|||||||
|
|
||||||
<view class="bottom-actions">
|
<view class="bottom-actions">
|
||||||
<button class="secondary-btn kos-pill" @click="goBack">返回列表</button>
|
<button class="secondary-btn kos-pill" @click="goBack">返回列表</button>
|
||||||
<button class="primary-btn kos-primary" @click="selectCurrent">映射实现路径</button>
|
<button class="voice-btn kos-pill" @click="trackTtsClick">
|
||||||
|
<text class="voice-icon">{{ detailTtsIcon }}</text>
|
||||||
|
<text>{{ detailTtsActionText }}</text>
|
||||||
|
</button>
|
||||||
|
<button class="primary-btn kos-primary" @click="continueCurrent">继续生成</button>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
@@ -83,9 +83,14 @@ const lengthText = computed(() => {
|
|||||||
return map[script.value?.length] || script.value?.length || '中篇'
|
return map[script.value?.length] || script.value?.length || '中篇'
|
||||||
})
|
})
|
||||||
|
|
||||||
const detailTtsButtonText = computed(() => {
|
const detailTtsActionText = computed(() => {
|
||||||
if (!script.value?.id) return '生成保存后可语音播放'
|
if (!script.value?.id) return '播放'
|
||||||
return ttsPlayer.buttonText.value
|
if (ttsPlayer.loading.value) return '生成中'
|
||||||
|
return ttsPlayer.playing.value ? '暂停' : '播放'
|
||||||
|
})
|
||||||
|
|
||||||
|
const detailTtsIcon = computed(() => {
|
||||||
|
return ttsPlayer.playing.value ? 'Ⅱ' : '▶'
|
||||||
})
|
})
|
||||||
|
|
||||||
const outline = computed(() => {
|
const outline = computed(() => {
|
||||||
@@ -120,19 +125,17 @@ const loadScript = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectCurrent = async () => {
|
const continueCurrent = () => {
|
||||||
if (!script.value?.id) return
|
if (!script.value?.id) return
|
||||||
analytics.track('path_select', {
|
uni.setStorageSync('pending_open_script_chat', {
|
||||||
|
id: script.value.id
|
||||||
|
})
|
||||||
|
analytics.track('script_detail_continue_click', {
|
||||||
script_id: script.value.id,
|
script_id: script.value.id,
|
||||||
style: script.value.style || '',
|
style: script.value.style || '',
|
||||||
length: script.value.length || ''
|
length: script.value.length || ''
|
||||||
}, { eventType: 'script', pagePath })
|
}, { eventType: 'script', pagePath })
|
||||||
const res = await store.selectScript(script.value.id)
|
uni.reLaunch({ url: '/pages/main/index?tab=script' })
|
||||||
if (!res.success) {
|
|
||||||
uni.showToast({ title: res.error || '映射失败', icon: 'none' })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
uni.navigateTo({ url: '/pages/main/PathView' })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const trackTtsClick = () => {
|
const trackTtsClick = () => {
|
||||||
@@ -272,41 +275,6 @@ onUnmounted(() => {
|
|||||||
margin-top: 28rpx;
|
margin-top: 28rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.audio-inline {
|
|
||||||
height: 76rpx;
|
|
||||||
margin-top: 26rpx;
|
|
||||||
border-radius: 999rpx;
|
|
||||||
padding: 0 26rpx;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
gap: 14rpx;
|
|
||||||
color: #fff;
|
|
||||||
font-size: 25rpx;
|
|
||||||
font-weight: 800;
|
|
||||||
line-height: 1;
|
|
||||||
background: linear-gradient(135deg, #24c6dc, #7f5af0);
|
|
||||||
box-shadow: 0 12rpx 30rpx rgba(36, 198, 220, 0.22);
|
|
||||||
}
|
|
||||||
|
|
||||||
.audio-inline-icon {
|
|
||||||
width: 34rpx;
|
|
||||||
height: 34rpx;
|
|
||||||
border-radius: 50%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
color: rgba(5, 6, 21, 0.86);
|
|
||||||
font-size: 18rpx;
|
|
||||||
font-weight: 900;
|
|
||||||
background: rgba(255, 255, 255, 0.86);
|
|
||||||
}
|
|
||||||
|
|
||||||
.audio-inline-text {
|
|
||||||
line-height: 1.2;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat {
|
.stat {
|
||||||
padding: 18rpx 10rpx;
|
padding: 18rpx 10rpx;
|
||||||
border-radius: 20rpx;
|
border-radius: 20rpx;
|
||||||
@@ -416,13 +384,14 @@ onUnmounted(() => {
|
|||||||
padding: 16rpx 30rpx 26rpx;
|
padding: 16rpx 30rpx 26rpx;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1.45fr;
|
grid-template-columns: 1fr 1fr 1.24fr;
|
||||||
gap: 18rpx;
|
gap: 18rpx;
|
||||||
background: rgba(5, 6, 21, 0.72);
|
background: rgba(5, 6, 21, 0.72);
|
||||||
backdrop-filter: blur(24rpx);
|
backdrop-filter: blur(24rpx);
|
||||||
}
|
}
|
||||||
|
|
||||||
.secondary-btn,
|
.secondary-btn,
|
||||||
|
.voice-btn,
|
||||||
.primary-btn {
|
.primary-btn {
|
||||||
height: 82rpx;
|
height: 82rpx;
|
||||||
border-radius: 999rpx;
|
border-radius: 999rpx;
|
||||||
@@ -439,4 +408,15 @@ onUnmounted(() => {
|
|||||||
.secondary-btn {
|
.secondary-btn {
|
||||||
color: #caa0ff;
|
color: #caa0ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.voice-btn {
|
||||||
|
gap: 8rpx;
|
||||||
|
color: #e8ccff;
|
||||||
|
border: 1rpx solid rgba(192, 132, 252, 0.32);
|
||||||
|
}
|
||||||
|
|
||||||
|
.voice-icon {
|
||||||
|
font-size: 22rpx;
|
||||||
|
font-weight: 900;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -18,6 +18,13 @@
|
|||||||
|
|
||||||
<view class="bottom-nav">
|
<view class="bottom-nav">
|
||||||
<view class="nav-inner">
|
<view class="nav-inner">
|
||||||
|
<view class="nav-item" :class="{ active: activeTab === 'record' }" @click="switchTab('record')">
|
||||||
|
<view class="tab-icon planet-ring-icon">
|
||||||
|
<view class="planet-core"></view>
|
||||||
|
<view class="planet-ring"></view>
|
||||||
|
</view>
|
||||||
|
<text>人生轨迹</text>
|
||||||
|
</view>
|
||||||
<view class="nav-item" :class="{ active: activeTab === 'script' }" @click="switchTab('script')">
|
<view class="nav-item" :class="{ active: activeTab === 'script' }" @click="switchTab('script')">
|
||||||
<view class="tab-icon book-star-icon">
|
<view class="tab-icon book-star-icon">
|
||||||
<view class="book-page left"></view>
|
<view class="book-page left"></view>
|
||||||
@@ -26,13 +33,6 @@
|
|||||||
</view>
|
</view>
|
||||||
<text>爽文生成</text>
|
<text>爽文生成</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="nav-item" :class="{ active: activeTab === 'record' }" @click="switchTab('record')">
|
|
||||||
<view class="tab-icon planet-ring-icon">
|
|
||||||
<view class="planet-core"></view>
|
|
||||||
<view class="planet-ring"></view>
|
|
||||||
</view>
|
|
||||||
<text>人生轨迹</text>
|
|
||||||
</view>
|
|
||||||
<view class="nav-item" :class="{ active: activeTab === 'mine' }" @click="switchTab('mine')">
|
<view class="nav-item" :class="{ active: activeTab === 'mine' }" @click="switchTab('mine')">
|
||||||
<view class="tab-icon smile-face-icon">
|
<view class="tab-icon smile-face-icon">
|
||||||
<view class="smile-eye left"></view>
|
<view class="smile-eye left"></view>
|
||||||
|
|||||||
@@ -44,6 +44,33 @@ const parseSseFrame = (frame) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const findOverlapLength = (current, next) => {
|
||||||
|
const max = Math.min(current.length, next.length)
|
||||||
|
for (let size = max; size > 0; size--) {
|
||||||
|
if (current.slice(-size) === next.slice(0, size)) return size
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
const mergeStreamOutput = (current, chunk) => {
|
||||||
|
const next = String(chunk || '')
|
||||||
|
if (!next) return { output: current, delta: '' }
|
||||||
|
if (!current) return { output: next, delta: next }
|
||||||
|
if (next === current) return { output: current, delta: '' }
|
||||||
|
if (next.length >= 16 && current.endsWith(next)) return { output: current, delta: '' }
|
||||||
|
if (next.startsWith(current)) {
|
||||||
|
return { output: next, delta: next.slice(current.length) }
|
||||||
|
}
|
||||||
|
const currentIndex = next.length > current.length ? next.indexOf(current) : -1
|
||||||
|
if (currentIndex >= 0) {
|
||||||
|
return { output: next, delta: next.slice(currentIndex + current.length) }
|
||||||
|
}
|
||||||
|
const overlap = findOverlapLength(current, next)
|
||||||
|
if (overlap < 8) return { output: current + next, delta: next }
|
||||||
|
const delta = next.slice(overlap)
|
||||||
|
return { output: current + delta, delta }
|
||||||
|
}
|
||||||
|
|
||||||
const queryRuntimeResult = (requestId) => {
|
const queryRuntimeResult = (requestId) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
uni.request({
|
uni.request({
|
||||||
@@ -113,6 +140,7 @@ export const streamAiScene = ({ sceneCode, inputs = {}, onStart, onDelta, onDone
|
|||||||
let requestTask
|
let requestTask
|
||||||
let recoveryTimer
|
let recoveryTimer
|
||||||
let recoveryPromise
|
let recoveryPromise
|
||||||
|
let streamStarted = false
|
||||||
|
|
||||||
const clearRecoveryTimer = () => {
|
const clearRecoveryTimer = () => {
|
||||||
if (recoveryTimer) {
|
if (recoveryTimer) {
|
||||||
@@ -130,6 +158,7 @@ export const streamAiScene = ({ sceneCode, inputs = {}, onStart, onDelta, onDone
|
|||||||
|
|
||||||
const completeFromRecoveredOutput = async () => {
|
const completeFromRecoveredOutput = async () => {
|
||||||
if (closed) return
|
if (closed) return
|
||||||
|
if (streamStarted || output.trim()) return
|
||||||
try {
|
try {
|
||||||
const recoveredOutput = await recoverOnce()
|
const recoveredOutput = await recoverOnce()
|
||||||
if (closed) return
|
if (closed) return
|
||||||
@@ -149,7 +178,7 @@ export const streamAiScene = ({ sceneCode, inputs = {}, onStart, onDelta, onDone
|
|||||||
clearRecoveryTimer()
|
clearRecoveryTimer()
|
||||||
recoveryTimer = setTimeout(() => {
|
recoveryTimer = setTimeout(() => {
|
||||||
completeFromRecoveredOutput()
|
completeFromRecoveredOutput()
|
||||||
}, 8000)
|
}, 25000)
|
||||||
}
|
}
|
||||||
|
|
||||||
const finishRecovered = (message, event) => {
|
const finishRecovered = (message, event) => {
|
||||||
@@ -172,7 +201,6 @@ export const streamAiScene = ({ sceneCode, inputs = {}, onStart, onDelta, onDone
|
|||||||
|
|
||||||
const recoverOrFail = async (message, event) => {
|
const recoverOrFail = async (message, event) => {
|
||||||
if (closed) return
|
if (closed) return
|
||||||
if (finishRecovered(message, event)) return
|
|
||||||
try {
|
try {
|
||||||
const recoveredOutput = await recoverOnce()
|
const recoveredOutput = await recoverOnce()
|
||||||
if (closed) return
|
if (closed) return
|
||||||
@@ -182,6 +210,7 @@ export const streamAiScene = ({ sceneCode, inputs = {}, onStart, onDelta, onDone
|
|||||||
resolve({ output })
|
resolve({ output })
|
||||||
} catch (recoverError) {
|
} catch (recoverError) {
|
||||||
if (closed) return
|
if (closed) return
|
||||||
|
if (finishRecovered(message || recoverError.message, event)) return
|
||||||
const finalMessage = message || recoverError.message || 'AI 生成结果暂时没有返回'
|
const finalMessage = message || recoverError.message || 'AI 生成结果暂时没有返回'
|
||||||
closed = true
|
closed = true
|
||||||
clearRecoveryTimer()
|
clearRecoveryTimer()
|
||||||
@@ -196,10 +225,7 @@ export const streamAiScene = ({ sceneCode, inputs = {}, onStart, onDelta, onDone
|
|||||||
|
|
||||||
const failWithoutRecovery = (message, event) => {
|
const failWithoutRecovery = (message, event) => {
|
||||||
if (closed) return
|
if (closed) return
|
||||||
closed = true
|
recoverOrFail(message, event)
|
||||||
clearRecoveryTimer()
|
|
||||||
onError?.(message, event)
|
|
||||||
reject(new Error(message))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const finishWithOutputOrRecover = async () => {
|
const finishWithOutputOrRecover = async () => {
|
||||||
@@ -247,6 +273,8 @@ export const streamAiScene = ({ sceneCode, inputs = {}, onStart, onDelta, onDone
|
|||||||
|
|
||||||
requestTask?.onChunkReceived?.((res) => {
|
requestTask?.onChunkReceived?.((res) => {
|
||||||
try {
|
try {
|
||||||
|
streamStarted = true
|
||||||
|
clearRecoveryTimer()
|
||||||
consumeText(decodeChunk(res.data), failStream)
|
consumeText(decodeChunk(res.data), failStream)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
failStream(error.message || 'AI流式请求失败')
|
failStream(error.message || 'AI流式请求失败')
|
||||||
@@ -262,11 +290,20 @@ export const streamAiScene = ({ sceneCode, inputs = {}, onStart, onDelta, onDone
|
|||||||
const event = parseSseFrame(frame)
|
const event = parseSseFrame(frame)
|
||||||
if (!event) return
|
if (!event) return
|
||||||
if (event.type === 'start') {
|
if (event.type === 'start') {
|
||||||
|
streamStarted = true
|
||||||
|
clearRecoveryTimer()
|
||||||
onStart?.(event)
|
onStart?.(event)
|
||||||
} else if (event.type === 'delta') {
|
} else if (event.type === 'delta') {
|
||||||
output += event.content || ''
|
streamStarted = true
|
||||||
onDelta?.(event.content || '', output, event)
|
clearRecoveryTimer()
|
||||||
|
const merged = mergeStreamOutput(output, event.content)
|
||||||
|
output = merged.output
|
||||||
|
if (merged.delta) {
|
||||||
|
onDelta?.(merged.delta, output, event)
|
||||||
|
}
|
||||||
} else if (event.type === 'done') {
|
} else if (event.type === 'done') {
|
||||||
|
streamStarted = true
|
||||||
|
clearRecoveryTimer()
|
||||||
onDone?.(event, output)
|
onDone?.(event, output)
|
||||||
} else if (event.type === 'error') {
|
} else if (event.type === 'error') {
|
||||||
const message = event.message || event.code || 'AI流式请求失败'
|
const message = event.message || event.code || 'AI流式请求失败'
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ import { getEnvValue } from '../config/env.js'
|
|||||||
|
|
||||||
const DEFAULT_SOURCE_TYPE = 'epic_script'
|
const DEFAULT_SOURCE_TYPE = 'epic_script'
|
||||||
const DEFAULT_VOICE = 'default_zh_female'
|
const DEFAULT_VOICE = 'default_zh_female'
|
||||||
|
const DEFAULT_SPEECH_RATE = 0.92
|
||||||
|
const DEFAULT_EMOTION = 'story'
|
||||||
|
|
||||||
const normalizeAudioUrl = (task) => {
|
const normalizeAudioUrl = (task) => {
|
||||||
if (!task?.audioUrl || /^https?:\/\//.test(task.audioUrl)) {
|
if (!task?.audioUrl || /^https?:\/\//.test(task.audioUrl)) {
|
||||||
@@ -25,9 +27,12 @@ const normalizeResponse = (response) => {
|
|||||||
export const createTtsTask = ({
|
export const createTtsTask = ({
|
||||||
sourceType = DEFAULT_SOURCE_TYPE,
|
sourceType = DEFAULT_SOURCE_TYPE,
|
||||||
sourceId,
|
sourceId,
|
||||||
voice = DEFAULT_VOICE
|
voice = DEFAULT_VOICE,
|
||||||
|
speechRate = DEFAULT_SPEECH_RATE,
|
||||||
|
pitch = 0,
|
||||||
|
emotion = DEFAULT_EMOTION
|
||||||
}) => {
|
}) => {
|
||||||
return post('/tts/tasks', { sourceType, sourceId, voice }).then(normalizeResponse)
|
return post('/tts/tasks', { sourceType, sourceId, voice, speechRate, pitch, emotion }).then(normalizeResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getTtsTask = (id) => {
|
export const getTtsTask = (id) => {
|
||||||
@@ -37,9 +42,12 @@ export const getTtsTask = (id) => {
|
|||||||
export const getTtsTaskBySource = ({
|
export const getTtsTaskBySource = ({
|
||||||
sourceType = DEFAULT_SOURCE_TYPE,
|
sourceType = DEFAULT_SOURCE_TYPE,
|
||||||
sourceId,
|
sourceId,
|
||||||
voice = DEFAULT_VOICE
|
voice = DEFAULT_VOICE,
|
||||||
|
speechRate = DEFAULT_SPEECH_RATE,
|
||||||
|
pitch = 0,
|
||||||
|
emotion = DEFAULT_EMOTION
|
||||||
}) => {
|
}) => {
|
||||||
return get('/tts/tasks/by-source', { sourceType, sourceId, voice }).then(normalizeResponse)
|
return get('/tts/tasks/by-source', { sourceType, sourceId, voice, speechRate, pitch, emotion }).then(normalizeResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|||||||
Reference in New Issue
Block a user