- 顶部展示语音运行时状态:状态标签、LiveKit 版本、下载进度,未就绪时每 5 秒轮询 - 部署失败或不支持平台时提供重新部署入口 - 语音库配置弹窗:启用开关、目录、镜像地址、公网地址、监听地址、信令与媒体端口、启动超时、外部 IP 与节点 IP - 语音日志弹窗:增量拉取、自动刷新开关、手动刷新、最多保留 2000 条 - 房间语音容量设置:参与人数与旁听人数上限 - 补充语音相关 API 与类型定义 - 中英文各补 26 个文案
This commit is contained in:
1 parent
ac38c98203
commit
667c1a0900
4 files changed
+603
-4
No files matched your search
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { SpinnerIcon } from '@modrinth/assets'
|
import { SpinnerIcon } from '@modrinth/assets'
|
||||||
import { defineMessages, injectNotificationManager, useVIntl } from '@modrinth/ui'
|
import { defineMessages, injectNotificationManager, useVIntl } from '@modrinth/ui'
|
||||||
import { computed, onMounted, ref } from 'vue'
|
import { computed, onBeforeUnmount, onMounted, reactive, ref } from 'vue'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
type SkinSiteChatPrerequisiteMode,
|
type SkinSiteChatPrerequisiteMode,
|
||||||
@@ -17,6 +17,15 @@ import {
|
|||||||
skinSiteChatSettlementOwnerAssign,
|
skinSiteChatSettlementOwnerAssign,
|
||||||
type SkinSiteChatAuditInfo,
|
type SkinSiteChatAuditInfo,
|
||||||
type SkinSiteChatUserInfo,
|
type SkinSiteChatUserInfo,
|
||||||
|
skinSiteChatVoiceCapacityUpdate,
|
||||||
|
skinSiteChatVoiceConfig,
|
||||||
|
skinSiteChatVoiceConfigUpdate,
|
||||||
|
skinSiteChatVoiceLogs,
|
||||||
|
skinSiteChatVoiceRuntime,
|
||||||
|
skinSiteChatVoiceRuntimeRetry,
|
||||||
|
type SkinSiteChatVoiceGlobalConfig,
|
||||||
|
type SkinSiteChatVoiceLogEntry,
|
||||||
|
type SkinSiteChatVoiceRuntimeInfo,
|
||||||
} from '@/helpers/skin-site-api'
|
} from '@/helpers/skin-site-api'
|
||||||
|
|
||||||
import ChatUserPicker from '../ChatUserPicker.vue'
|
import ChatUserPicker from '../ChatUserPicker.vue'
|
||||||
@@ -158,6 +167,110 @@ const messages = defineMessages({
|
|||||||
defaultMessage: 'Target: {name}',
|
defaultMessage: 'Target: {name}',
|
||||||
},
|
},
|
||||||
close: { id: 'app.skin-site.chat.admin.close', defaultMessage: 'Close' },
|
close: { id: 'app.skin-site.chat.admin.close', defaultMessage: 'Close' },
|
||||||
|
voiceRuntimeTitle: {
|
||||||
|
id: 'app.skin-site.chat.admin.voice-runtime-title',
|
||||||
|
defaultMessage: 'Realtime voice runtime',
|
||||||
|
},
|
||||||
|
voiceLogs: {
|
||||||
|
id: 'app.skin-site.chat.admin.voice-logs',
|
||||||
|
defaultMessage: 'View logs',
|
||||||
|
},
|
||||||
|
voiceConfig: {
|
||||||
|
id: 'app.skin-site.chat.admin.voice-config',
|
||||||
|
defaultMessage: 'Configure runtime',
|
||||||
|
},
|
||||||
|
voiceRetry: {
|
||||||
|
id: 'app.skin-site.chat.admin.voice-retry',
|
||||||
|
defaultMessage: 'Retry deployment',
|
||||||
|
},
|
||||||
|
voiceConfigTitle: {
|
||||||
|
id: 'app.skin-site.chat.admin.voice-config-title',
|
||||||
|
defaultMessage: 'Voice runtime configuration',
|
||||||
|
},
|
||||||
|
voiceLogsTitle: {
|
||||||
|
id: 'app.skin-site.chat.admin.voice-logs-title',
|
||||||
|
defaultMessage: 'LiveKit logs',
|
||||||
|
},
|
||||||
|
voiceCapacityTitle: {
|
||||||
|
id: 'app.skin-site.chat.admin.voice-capacity-title',
|
||||||
|
defaultMessage: 'Voice capacity',
|
||||||
|
},
|
||||||
|
voiceCapacity: {
|
||||||
|
id: 'app.skin-site.chat.admin.voice-capacity',
|
||||||
|
defaultMessage: 'Voice capacity',
|
||||||
|
},
|
||||||
|
participantLimit: {
|
||||||
|
id: 'app.skin-site.chat.admin.participant-limit',
|
||||||
|
defaultMessage: 'Participant limit',
|
||||||
|
},
|
||||||
|
listenerLimit: {
|
||||||
|
id: 'app.skin-site.chat.admin.listener-limit',
|
||||||
|
defaultMessage: 'Listener limit',
|
||||||
|
},
|
||||||
|
voiceCapacityUpdated: {
|
||||||
|
id: 'app.skin-site.chat.admin.voice-capacity-updated',
|
||||||
|
defaultMessage: 'Voice capacity updated.',
|
||||||
|
},
|
||||||
|
voiceEnabled: {
|
||||||
|
id: 'app.skin-site.chat.admin.voice-enabled',
|
||||||
|
defaultMessage: 'Enable realtime voice',
|
||||||
|
},
|
||||||
|
voiceRuntimePath: {
|
||||||
|
id: 'app.skin-site.chat.admin.voice-runtime-path',
|
||||||
|
defaultMessage: 'Runtime directory',
|
||||||
|
},
|
||||||
|
voiceMirror: {
|
||||||
|
id: 'app.skin-site.chat.admin.voice-mirror',
|
||||||
|
defaultMessage: 'Mirror base URL',
|
||||||
|
},
|
||||||
|
voicePublicUrl: {
|
||||||
|
id: 'app.skin-site.chat.admin.voice-public-url',
|
||||||
|
defaultMessage: 'Public URL',
|
||||||
|
},
|
||||||
|
voiceBind: {
|
||||||
|
id: 'app.skin-site.chat.admin.voice-bind',
|
||||||
|
defaultMessage: 'Bind address',
|
||||||
|
},
|
||||||
|
voiceSignalPort: {
|
||||||
|
id: 'app.skin-site.chat.admin.voice-signal-port',
|
||||||
|
defaultMessage: 'Signal port',
|
||||||
|
},
|
||||||
|
voiceRtcTcpPort: {
|
||||||
|
id: 'app.skin-site.chat.admin.voice-rtc-tcp-port',
|
||||||
|
defaultMessage: 'Media TCP port',
|
||||||
|
},
|
||||||
|
voiceRtcUdpPort: {
|
||||||
|
id: 'app.skin-site.chat.admin.voice-rtc-udp-port',
|
||||||
|
defaultMessage: 'Media UDP port',
|
||||||
|
},
|
||||||
|
voiceStartupTimeout: {
|
||||||
|
id: 'app.skin-site.chat.admin.voice-startup-timeout',
|
||||||
|
defaultMessage: 'Startup timeout (s)',
|
||||||
|
},
|
||||||
|
voiceUseExternalIp: {
|
||||||
|
id: 'app.skin-site.chat.admin.voice-use-external-ip',
|
||||||
|
defaultMessage: 'Use external IP',
|
||||||
|
},
|
||||||
|
voiceNodeIp: {
|
||||||
|
id: 'app.skin-site.chat.admin.voice-node-ip',
|
||||||
|
defaultMessage: 'Node IP',
|
||||||
|
},
|
||||||
|
voiceSaved: {
|
||||||
|
id: 'app.skin-site.chat.admin.voice-saved',
|
||||||
|
defaultMessage: 'Voice configuration saved.',
|
||||||
|
},
|
||||||
|
voiceLogsAutoRefresh: {
|
||||||
|
id: 'app.skin-site.chat.admin.voice-logs-auto-refresh',
|
||||||
|
defaultMessage: 'Auto refresh',
|
||||||
|
},
|
||||||
|
voiceLogsRefresh: {
|
||||||
|
id: 'app.skin-site.chat.admin.voice-logs-refresh',
|
||||||
|
defaultMessage: 'Refresh',
|
||||||
|
},
|
||||||
|
voiceLogsEmpty: {
|
||||||
|
id: 'app.skin-site.chat.admin.voice-logs-empty',
|
||||||
|
defaultMessage: 'No logs yet.',
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
type FormState = {
|
type FormState = {
|
||||||
@@ -460,7 +573,217 @@ function closeAudit() {
|
|||||||
auditItems.value = []
|
auditItems.value = []
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => void loadRooms())
|
// ---------- voice runtime ----------
|
||||||
|
const voiceRuntime = ref<SkinSiteChatVoiceRuntimeInfo | null>(null)
|
||||||
|
const runtimeRetrying = ref(false)
|
||||||
|
let runtimePollTimer: number | undefined
|
||||||
|
|
||||||
|
const VOICE_STATE_LABELS: Record<string, string> = {
|
||||||
|
DISABLED: '全局未启用',
|
||||||
|
NOT_INSTALLED: '等待部署',
|
||||||
|
DOWNLOADING: '正在下载',
|
||||||
|
VERIFYING: '正在校验',
|
||||||
|
EXTRACTING: '正在解压',
|
||||||
|
STARTING: '正在启动',
|
||||||
|
READY: '运行正常',
|
||||||
|
UNSUPPORTED_PLATFORM: '当前平台不受支持',
|
||||||
|
}
|
||||||
|
|
||||||
|
const voiceRuntimeLabel = computed(() => {
|
||||||
|
const state = voiceRuntime.value?.state
|
||||||
|
if (!state) return '状态未知'
|
||||||
|
if (state === 'FAILED') return voiceRuntime.value?.message || '部署失败'
|
||||||
|
return VOICE_STATE_LABELS[state] ?? '状态未知'
|
||||||
|
})
|
||||||
|
|
||||||
|
async function loadVoiceRuntime() {
|
||||||
|
try {
|
||||||
|
const res = await skinSiteChatVoiceRuntime()
|
||||||
|
if (res.success) voiceRuntime.value = res.payload
|
||||||
|
} catch (error) {
|
||||||
|
handleError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function retryVoiceRuntime() {
|
||||||
|
if (runtimeRetrying.value) return
|
||||||
|
runtimeRetrying.value = true
|
||||||
|
try {
|
||||||
|
const res = await skinSiteChatVoiceRuntimeRetry()
|
||||||
|
if (!res.success) throw new Error(res.errorMessage)
|
||||||
|
voiceRuntime.value = res.payload
|
||||||
|
addNotification({ type: 'success', title: formatMessage(messages.voiceRetry) })
|
||||||
|
} catch (error) {
|
||||||
|
handleError(error)
|
||||||
|
} finally {
|
||||||
|
runtimeRetrying.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- voice config ----------
|
||||||
|
const voiceConfigOpen = ref(false)
|
||||||
|
const voiceConfigLoading = ref(false)
|
||||||
|
const voiceConfigSaving = ref(false)
|
||||||
|
const voiceConfigOriginal = ref<SkinSiteChatVoiceGlobalConfig | null>(null)
|
||||||
|
const voiceConfigForm = reactive<SkinSiteChatVoiceGlobalConfig>({
|
||||||
|
enabled: true,
|
||||||
|
runtimePath: './data/voice-runtime',
|
||||||
|
mirrorBaseUrl: '',
|
||||||
|
publicUrl: '',
|
||||||
|
bindAddress: '127.0.0.1',
|
||||||
|
signalPort: 7880,
|
||||||
|
rtcTcpPort: 7881,
|
||||||
|
rtcUdpPort: 7882,
|
||||||
|
startupTimeoutSeconds: 120,
|
||||||
|
useExternalIp: true,
|
||||||
|
nodeIp: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
async function openVoiceConfig() {
|
||||||
|
voiceConfigLoading.value = true
|
||||||
|
try {
|
||||||
|
const res = await skinSiteChatVoiceConfig()
|
||||||
|
if (!res.success) throw new Error(res.errorMessage)
|
||||||
|
voiceConfigOriginal.value = { ...res.payload }
|
||||||
|
Object.assign(voiceConfigForm, res.payload)
|
||||||
|
voiceConfigOpen.value = true
|
||||||
|
} catch (error) {
|
||||||
|
handleError(error)
|
||||||
|
} finally {
|
||||||
|
voiceConfigLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeVoiceConfig() {
|
||||||
|
voiceConfigOpen.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveVoiceConfig() {
|
||||||
|
if (voiceConfigSaving.value) return
|
||||||
|
voiceConfigSaving.value = true
|
||||||
|
try {
|
||||||
|
const res = await skinSiteChatVoiceConfigUpdate({ ...voiceConfigForm })
|
||||||
|
if (!res.success) throw new Error(res.errorMessage)
|
||||||
|
voiceConfigOriginal.value = { ...res.payload.config }
|
||||||
|
Object.assign(voiceConfigForm, res.payload.config)
|
||||||
|
voiceRuntime.value = res.payload.runtime
|
||||||
|
addNotification({ type: 'success', title: formatMessage(messages.voiceSaved) })
|
||||||
|
closeVoiceConfig()
|
||||||
|
} catch (error) {
|
||||||
|
handleError(error)
|
||||||
|
} finally {
|
||||||
|
voiceConfigSaving.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- voice logs ----------
|
||||||
|
const voiceLogsOpen = ref(false)
|
||||||
|
const voiceLogs = ref<SkinSiteChatVoiceLogEntry[]>([])
|
||||||
|
const voiceLogLoading = ref(false)
|
||||||
|
const voiceLogError = ref('')
|
||||||
|
const voiceLogAutoRefresh = ref(true)
|
||||||
|
const voiceLogLatestId = ref(0)
|
||||||
|
let voiceLogPollTimer: number | undefined
|
||||||
|
|
||||||
|
async function openVoiceLogs() {
|
||||||
|
voiceLogs.value = []
|
||||||
|
voiceLogLatestId.value = 0
|
||||||
|
voiceLogError.value = ''
|
||||||
|
voiceLogsOpen.value = true
|
||||||
|
await loadVoiceLogs(true)
|
||||||
|
stopVoiceLogPolling()
|
||||||
|
voiceLogPollTimer = window.setInterval(() => {
|
||||||
|
if (voiceLogAutoRefresh.value && voiceLogsOpen.value) void loadVoiceLogs(false)
|
||||||
|
}, 2000)
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeVoiceLogs() {
|
||||||
|
voiceLogsOpen.value = false
|
||||||
|
stopVoiceLogPolling()
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopVoiceLogPolling() {
|
||||||
|
if (voiceLogPollTimer) {
|
||||||
|
window.clearInterval(voiceLogPollTimer)
|
||||||
|
voiceLogPollTimer = undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadVoiceLogs(manual: boolean) {
|
||||||
|
if (voiceLogLoading.value) return
|
||||||
|
voiceLogLoading.value = true
|
||||||
|
try {
|
||||||
|
const res = await skinSiteChatVoiceLogs(
|
||||||
|
voiceLogs.value.length ? voiceLogLatestId.value : undefined,
|
||||||
|
voiceLogs.value.length ? 500 : 300,
|
||||||
|
)
|
||||||
|
if (!res.success) throw new Error(res.errorMessage)
|
||||||
|
voiceLogError.value = ''
|
||||||
|
if (res.payload.entries.length) {
|
||||||
|
voiceLogs.value.push(...res.payload.entries)
|
||||||
|
if (voiceLogs.value.length > 2000) {
|
||||||
|
voiceLogs.value.splice(0, voiceLogs.value.length - 2000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
voiceLogLatestId.value = res.payload.latestId
|
||||||
|
} catch (error) {
|
||||||
|
voiceLogError.value = error instanceof Error ? error.message : String(error)
|
||||||
|
} finally {
|
||||||
|
voiceLogLoading.value = false
|
||||||
|
void manual
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- voice capacity ----------
|
||||||
|
const capacityRoom = ref<SkinSiteChatRoomInfo | null>(null)
|
||||||
|
const capacitySaving = ref(false)
|
||||||
|
const capacityForm = reactive({ participantLimit: 0, listenerLimit: 0 })
|
||||||
|
|
||||||
|
function openCapacity(room: SkinSiteChatRoomInfo) {
|
||||||
|
capacityRoom.value = room
|
||||||
|
capacityForm.participantLimit = room.voiceParticipantLimit
|
||||||
|
capacityForm.listenerLimit = room.voiceListenerLimit
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeCapacity() {
|
||||||
|
capacityRoom.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveCapacity() {
|
||||||
|
if (!capacityRoom.value || capacitySaving.value) return
|
||||||
|
capacitySaving.value = true
|
||||||
|
try {
|
||||||
|
const res = await skinSiteChatVoiceCapacityUpdate(capacityRoom.value.id, {
|
||||||
|
participantLimit: capacityForm.participantLimit,
|
||||||
|
listenerLimit: capacityForm.listenerLimit,
|
||||||
|
})
|
||||||
|
if (!res.success) throw new Error(res.errorMessage)
|
||||||
|
addNotification({ type: 'success', title: formatMessage(messages.voiceCapacityUpdated) })
|
||||||
|
closeCapacity()
|
||||||
|
await loadRooms()
|
||||||
|
} catch (error) {
|
||||||
|
handleError(error)
|
||||||
|
} finally {
|
||||||
|
capacitySaving.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
void loadRooms()
|
||||||
|
void loadVoiceRuntime()
|
||||||
|
runtimePollTimer = window.setInterval(() => {
|
||||||
|
const state = voiceRuntime.value?.state
|
||||||
|
if (state && !['READY', 'DISABLED', 'UNSUPPORTED_PLATFORM'].includes(state)) {
|
||||||
|
void loadVoiceRuntime()
|
||||||
|
}
|
||||||
|
}, 5000)
|
||||||
|
})
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
if (runtimePollTimer) window.clearInterval(runtimePollTimer)
|
||||||
|
stopVoiceLogPolling()
|
||||||
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -481,6 +804,55 @@ onMounted(() => void loadRooms())
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="voiceRuntime"
|
||||||
|
class="flex flex-wrap items-center gap-3 rounded-xl border border-solid border-surface-5 bg-bg-raised px-4 py-3"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="size-2.5 shrink-0 rounded-full"
|
||||||
|
:class="
|
||||||
|
voiceRuntime.state === 'READY'
|
||||||
|
? 'bg-green'
|
||||||
|
: voiceRuntime.state === 'FAILED'
|
||||||
|
? 'bg-red'
|
||||||
|
: 'bg-yellow'
|
||||||
|
"
|
||||||
|
></span>
|
||||||
|
<div class="min-w-0 flex-1">
|
||||||
|
<p class="m-0 text-sm font-semibold text-contrast">
|
||||||
|
{{ formatMessage(messages.voiceRuntimeTitle) }}
|
||||||
|
</p>
|
||||||
|
<p class="m-0 truncate text-xs text-secondary">
|
||||||
|
{{ voiceRuntimeLabel }} · LiveKit {{ voiceRuntime.version }}
|
||||||
|
<span v-if="voiceRuntime.progress != null"> · {{ voiceRuntime.progress }}%</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="rounded-lg bg-button-bg px-3 py-1.5 text-xs font-semibold text-contrast hover:brightness-110"
|
||||||
|
@click="openVoiceLogs"
|
||||||
|
>
|
||||||
|
{{ formatMessage(messages.voiceLogs) }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="rounded-lg bg-button-bg px-3 py-1.5 text-xs font-semibold text-contrast hover:brightness-110 disabled:opacity-50"
|
||||||
|
:disabled="voiceConfigLoading"
|
||||||
|
@click="openVoiceConfig"
|
||||||
|
>
|
||||||
|
{{ formatMessage(messages.voiceConfig) }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
v-if="voiceRuntime.state === 'FAILED' || voiceRuntime.state === 'UNSUPPORTED_PLATFORM'"
|
||||||
|
type="button"
|
||||||
|
class="rounded-lg bg-yellow/20 px-3 py-1.5 text-xs font-semibold text-yellow hover:brightness-110 disabled:opacity-50"
|
||||||
|
:disabled="runtimeRetrying || voiceRuntime.state === 'UNSUPPORTED_PLATFORM'"
|
||||||
|
@click="retryVoiceRuntime"
|
||||||
|
>
|
||||||
|
{{ formatMessage(messages.voiceRetry) }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-if="loading"
|
v-if="loading"
|
||||||
class="flex min-h-48 items-center justify-center gap-2 text-sm text-secondary"
|
class="flex min-h-48 items-center justify-center gap-2 text-sm text-secondary"
|
||||||
@@ -585,6 +957,14 @@ onMounted(() => void loadRooms())
|
|||||||
>
|
>
|
||||||
{{ formatMessage(messages.audit) }}
|
{{ formatMessage(messages.audit) }}
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
v-if="room.voiceEnabled"
|
||||||
|
type="button"
|
||||||
|
class="rounded-lg bg-button-bg px-3 py-1 text-xs font-semibold text-contrast hover:brightness-110"
|
||||||
|
@click="openCapacity(room)"
|
||||||
|
>
|
||||||
|
{{ formatMessage(messages.voiceCapacity) }}
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="ml-auto rounded-lg bg-button-bg px-3 py-1 text-xs font-semibold text-contrast hover:brightness-110"
|
class="ml-auto rounded-lg bg-button-bg px-3 py-1 text-xs font-semibold text-contrast hover:brightness-110"
|
||||||
|
|||||||
@@ -2252,3 +2252,118 @@ export function skinSiteChatAuditList(
|
|||||||
): Promise<SkinSiteResponse<SkinSiteChatAuditInfo[]>> {
|
): Promise<SkinSiteResponse<SkinSiteChatAuditInfo[]>> {
|
||||||
return skinSiteRequest<SkinSiteChatAuditInfo[]>('chat/rooms/' + roomId + '/audit')
|
return skinSiteRequest<SkinSiteChatAuditInfo[]>('chat/rooms/' + roomId + '/audit')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type SkinSiteChatVoiceRuntimeState =
|
||||||
|
| 'DISABLED'
|
||||||
|
| 'NOT_INSTALLED'
|
||||||
|
| 'DOWNLOADING'
|
||||||
|
| 'VERIFYING'
|
||||||
|
| 'EXTRACTING'
|
||||||
|
| 'STARTING'
|
||||||
|
| 'READY'
|
||||||
|
| 'FAILED'
|
||||||
|
| 'UNSUPPORTED_PLATFORM'
|
||||||
|
|
||||||
|
export interface SkinSiteChatVoiceRuntimeInfo {
|
||||||
|
state: SkinSiteChatVoiceRuntimeState
|
||||||
|
version: string
|
||||||
|
progress?: number | null
|
||||||
|
message?: string | null
|
||||||
|
retryCount: number
|
||||||
|
publicUrl?: string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SkinSiteChatVoiceGlobalConfig {
|
||||||
|
enabled: boolean
|
||||||
|
runtimePath: string
|
||||||
|
mirrorBaseUrl: string
|
||||||
|
publicUrl: string
|
||||||
|
bindAddress: string
|
||||||
|
signalPort: number
|
||||||
|
rtcTcpPort: number
|
||||||
|
rtcUdpPort: number
|
||||||
|
startupTimeoutSeconds: number
|
||||||
|
useExternalIp: boolean
|
||||||
|
nodeIp: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SkinSiteChatVoiceGlobalConfigUpdateResult {
|
||||||
|
config: SkinSiteChatVoiceGlobalConfig
|
||||||
|
runtime: SkinSiteChatVoiceRuntimeInfo
|
||||||
|
restarted: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SkinSiteChatVoiceLogEntry {
|
||||||
|
id: number
|
||||||
|
timestamp: number
|
||||||
|
message: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SkinSiteChatVoiceLogPage {
|
||||||
|
entries: SkinSiteChatVoiceLogEntry[]
|
||||||
|
latestId: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SkinSiteChatVoiceRoomInfo {
|
||||||
|
roomId: number
|
||||||
|
enabled: boolean
|
||||||
|
available: boolean
|
||||||
|
publisherLimit: number
|
||||||
|
participantLimit: number
|
||||||
|
listenerLimit: number
|
||||||
|
participantCount: number
|
||||||
|
publisherCount: number
|
||||||
|
listenerCount: number
|
||||||
|
joined: boolean
|
||||||
|
observer: boolean
|
||||||
|
muted: boolean
|
||||||
|
bannedUntil?: number | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export function skinSiteChatVoiceRuntime(): Promise<
|
||||||
|
SkinSiteResponse<SkinSiteChatVoiceRuntimeInfo>
|
||||||
|
> {
|
||||||
|
return skinSiteRequest<SkinSiteChatVoiceRuntimeInfo>('chat/voice/admin/runtime')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function skinSiteChatVoiceRuntimeRetry(): Promise<
|
||||||
|
SkinSiteResponse<SkinSiteChatVoiceRuntimeInfo>
|
||||||
|
> {
|
||||||
|
return skinSiteRequest<SkinSiteChatVoiceRuntimeInfo>('chat/voice/admin/runtime/retry', {
|
||||||
|
method: 'POST',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function skinSiteChatVoiceConfig(): Promise<
|
||||||
|
SkinSiteResponse<SkinSiteChatVoiceGlobalConfig>
|
||||||
|
> {
|
||||||
|
return skinSiteRequest<SkinSiteChatVoiceGlobalConfig>('chat/voice/admin/config')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function skinSiteChatVoiceConfigUpdate(
|
||||||
|
data: SkinSiteChatVoiceGlobalConfig,
|
||||||
|
): Promise<SkinSiteResponse<SkinSiteChatVoiceGlobalConfigUpdateResult>> {
|
||||||
|
return skinSiteRequest<SkinSiteChatVoiceGlobalConfigUpdateResult>(
|
||||||
|
'chat/voice/admin/config',
|
||||||
|
{ method: 'PUT', body: data },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function skinSiteChatVoiceLogs(
|
||||||
|
after?: number,
|
||||||
|
limit = 300,
|
||||||
|
): Promise<SkinSiteResponse<SkinSiteChatVoiceLogPage>> {
|
||||||
|
const query = new URLSearchParams({ limit: String(limit) })
|
||||||
|
if (after !== undefined) query.set('after', String(after))
|
||||||
|
return skinSiteRequest<SkinSiteChatVoiceLogPage>('chat/voice/admin/logs?' + query.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
export function skinSiteChatVoiceCapacityUpdate(
|
||||||
|
roomId: number,
|
||||||
|
data: { participantLimit: number; listenerLimit: number },
|
||||||
|
): Promise<SkinSiteResponse<SkinSiteChatVoiceRoomInfo>> {
|
||||||
|
return skinSiteRequest<SkinSiteChatVoiceRoomInfo>(
|
||||||
|
'chat/admin/rooms/' + roomId + '/voice/capacity',
|
||||||
|
{ method: 'PUT', body: data },
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -12317,5 +12317,57 @@
|
|||||||
"app.skin-site.chat.admin.audit-title": {"message": "Room audit"},
|
"app.skin-site.chat.admin.audit-title": {"message": "Room audit"},
|
||||||
"app.skin-site.chat.admin.audit-empty": {"message": "No audit records yet."},
|
"app.skin-site.chat.admin.audit-empty": {"message": "No audit records yet."},
|
||||||
"app.skin-site.chat.admin.audit-target": {"message": "Target: {name}"},
|
"app.skin-site.chat.admin.audit-target": {"message": "Target: {name}"},
|
||||||
"app.skin-site.chat.admin.close": {"message": "Close"}
|
"app.skin-site.chat.admin.close": {"message": "Close"},
|
||||||
|
"app.skin-site.chat.voice-runtime-title": {"message": "Realtime voice runtime"},
|
||||||
|
"app.skin-site.chat.voice-logs": {"message": "View logs"},
|
||||||
|
"app.skin-site.chat.voice-config": {"message": "Configure runtime"},
|
||||||
|
"app.skin-site.chat.voice-retry": {"message": "Retry deployment"},
|
||||||
|
"app.skin-site.chat.voice-config-title": {"message": "Voice runtime configuration"},
|
||||||
|
"app.skin-site.chat.voice-logs-title": {"message": "LiveKit logs"},
|
||||||
|
"app.skin-site.chat.voice-capacity-title": {"message": "Voice capacity"},
|
||||||
|
"app.skin-site.chat.voice-capacity": {"message": "Voice capacity"},
|
||||||
|
"app.skin-site.chat.participant-limit": {"message": "Participant limit"},
|
||||||
|
"app.skin-site.chat.listener-limit": {"message": "Listener limit"},
|
||||||
|
"app.skin-site.chat.voice-capacity-updated": {"message": "Voice capacity updated."},
|
||||||
|
"app.skin-site.chat.voice-enabled": {"message": "Enable realtime voice"},
|
||||||
|
"app.skin-site.chat.voice-runtime-path": {"message": "Runtime directory"},
|
||||||
|
"app.skin-site.chat.voice-mirror": {"message": "Mirror base URL"},
|
||||||
|
"app.skin-site.chat.voice-public-url": {"message": "Public URL"},
|
||||||
|
"app.skin-site.chat.voice-bind": {"message": "Bind address"},
|
||||||
|
"app.skin-site.chat.voice-signal-port": {"message": "Signal port"},
|
||||||
|
"app.skin-site.chat.voice-rtc-tcp-port": {"message": "Media TCP port"},
|
||||||
|
"app.skin-site.chat.voice-rtc-udp-port": {"message": "Media UDP port"},
|
||||||
|
"app.skin-site.chat.voice-startup-timeout": {"message": "Startup timeout (s)"},
|
||||||
|
"app.skin-site.chat.voice-use-external-ip": {"message": "Use external IP"},
|
||||||
|
"app.skin-site.chat.voice-node-ip": {"message": "Node IP"},
|
||||||
|
"app.skin-site.chat.voice-saved": {"message": "Voice configuration saved."},
|
||||||
|
"app.skin-site.chat.voice-logs-auto-refresh": {"message": "Auto refresh"},
|
||||||
|
"app.skin-site.chat.voice-logs-refresh": {"message": "Refresh"},
|
||||||
|
"app.skin-site.chat.voice-logs-empty": {"message": "No logs yet."},
|
||||||
|
"app.skin-site.chat.admin.voice-runtime-title": {"message": "Realtime voice runtime"},
|
||||||
|
"app.skin-site.chat.admin.voice-logs": {"message": "View logs"},
|
||||||
|
"app.skin-site.chat.admin.voice-config": {"message": "Configure runtime"},
|
||||||
|
"app.skin-site.chat.admin.voice-retry": {"message": "Retry deployment"},
|
||||||
|
"app.skin-site.chat.admin.voice-config-title": {"message": "Voice runtime configuration"},
|
||||||
|
"app.skin-site.chat.admin.voice-logs-title": {"message": "LiveKit logs"},
|
||||||
|
"app.skin-site.chat.admin.voice-capacity-title": {"message": "Voice capacity"},
|
||||||
|
"app.skin-site.chat.admin.voice-capacity": {"message": "Voice capacity"},
|
||||||
|
"app.skin-site.chat.admin.participant-limit": {"message": "Participant limit"},
|
||||||
|
"app.skin-site.chat.admin.listener-limit": {"message": "Listener limit"},
|
||||||
|
"app.skin-site.chat.admin.voice-capacity-updated": {"message": "Voice capacity updated."},
|
||||||
|
"app.skin-site.chat.admin.voice-enabled": {"message": "Enable realtime voice"},
|
||||||
|
"app.skin-site.chat.admin.voice-runtime-path": {"message": "Runtime directory"},
|
||||||
|
"app.skin-site.chat.admin.voice-mirror": {"message": "Mirror base URL"},
|
||||||
|
"app.skin-site.chat.admin.voice-public-url": {"message": "Public URL"},
|
||||||
|
"app.skin-site.chat.admin.voice-bind": {"message": "Bind address"},
|
||||||
|
"app.skin-site.chat.admin.voice-signal-port": {"message": "Signal port"},
|
||||||
|
"app.skin-site.chat.admin.voice-rtc-tcp-port": {"message": "Media TCP port"},
|
||||||
|
"app.skin-site.chat.admin.voice-rtc-udp-port": {"message": "Media UDP port"},
|
||||||
|
"app.skin-site.chat.admin.voice-startup-timeout": {"message": "Startup timeout (s)"},
|
||||||
|
"app.skin-site.chat.admin.voice-use-external-ip": {"message": "Use external IP"},
|
||||||
|
"app.skin-site.chat.admin.voice-node-ip": {"message": "Node IP"},
|
||||||
|
"app.skin-site.chat.admin.voice-saved": {"message": "Voice configuration saved."},
|
||||||
|
"app.skin-site.chat.admin.voice-logs-auto-refresh": {"message": "Auto refresh"},
|
||||||
|
"app.skin-site.chat.admin.voice-logs-refresh": {"message": "Refresh"},
|
||||||
|
"app.skin-site.chat.admin.voice-logs-empty": {"message": "No logs yet."}
|
||||||
}
|
}
|
||||||
@@ -12148,5 +12148,57 @@
|
|||||||
"app.skin-site.chat.admin.audit-title": {"message": "房间审计"},
|
"app.skin-site.chat.admin.audit-title": {"message": "房间审计"},
|
||||||
"app.skin-site.chat.admin.audit-empty": {"message": "暂无审计记录。"},
|
"app.skin-site.chat.admin.audit-empty": {"message": "暂无审计记录。"},
|
||||||
"app.skin-site.chat.admin.audit-target": {"message": "对象:{name}"},
|
"app.skin-site.chat.admin.audit-target": {"message": "对象:{name}"},
|
||||||
"app.skin-site.chat.admin.close": {"message": "关闭"}
|
"app.skin-site.chat.admin.close": {"message": "关闭"},
|
||||||
|
"app.skin-site.chat.voice-runtime-title": {"message": "实时语音库"},
|
||||||
|
"app.skin-site.chat.voice-logs": {"message": "查看日志"},
|
||||||
|
"app.skin-site.chat.voice-config": {"message": "配置语音库"},
|
||||||
|
"app.skin-site.chat.voice-retry": {"message": "重新部署"},
|
||||||
|
"app.skin-site.chat.voice-config-title": {"message": "语音库配置"},
|
||||||
|
"app.skin-site.chat.voice-logs-title": {"message": "LiveKit 日志"},
|
||||||
|
"app.skin-site.chat.voice-capacity-title": {"message": "语音容量"},
|
||||||
|
"app.skin-site.chat.voice-capacity": {"message": "语音容量"},
|
||||||
|
"app.skin-site.chat.participant-limit": {"message": "参与人数上限"},
|
||||||
|
"app.skin-site.chat.listener-limit": {"message": "旁听人数上限"},
|
||||||
|
"app.skin-site.chat.voice-capacity-updated": {"message": "语音容量已更新。"},
|
||||||
|
"app.skin-site.chat.voice-enabled": {"message": "启用实时语音"},
|
||||||
|
"app.skin-site.chat.voice-runtime-path": {"message": "语音库目录"},
|
||||||
|
"app.skin-site.chat.voice-mirror": {"message": "镜像基础地址"},
|
||||||
|
"app.skin-site.chat.voice-public-url": {"message": "公网访问地址"},
|
||||||
|
"app.skin-site.chat.voice-bind": {"message": "监听地址"},
|
||||||
|
"app.skin-site.chat.voice-signal-port": {"message": "信令端口"},
|
||||||
|
"app.skin-site.chat.voice-rtc-tcp-port": {"message": "媒体 TCP 端口"},
|
||||||
|
"app.skin-site.chat.voice-rtc-udp-port": {"message": "媒体 UDP 端口"},
|
||||||
|
"app.skin-site.chat.voice-startup-timeout": {"message": "启动等待时间(秒)"},
|
||||||
|
"app.skin-site.chat.voice-use-external-ip": {"message": "使用外部 IP"},
|
||||||
|
"app.skin-site.chat.voice-node-ip": {"message": "节点 IP"},
|
||||||
|
"app.skin-site.chat.voice-saved": {"message": "语音库配置已保存。"},
|
||||||
|
"app.skin-site.chat.voice-logs-auto-refresh": {"message": "自动刷新"},
|
||||||
|
"app.skin-site.chat.voice-logs-refresh": {"message": "刷新"},
|
||||||
|
"app.skin-site.chat.voice-logs-empty": {"message": "暂无日志。"},
|
||||||
|
"app.skin-site.chat.admin.voice-runtime-title": {"message": "实时语音库"},
|
||||||
|
"app.skin-site.chat.admin.voice-logs": {"message": "查看日志"},
|
||||||
|
"app.skin-site.chat.admin.voice-config": {"message": "配置语音库"},
|
||||||
|
"app.skin-site.chat.admin.voice-retry": {"message": "重新部署"},
|
||||||
|
"app.skin-site.chat.admin.voice-config-title": {"message": "语音库配置"},
|
||||||
|
"app.skin-site.chat.admin.voice-logs-title": {"message": "LiveKit 日志"},
|
||||||
|
"app.skin-site.chat.admin.voice-capacity-title": {"message": "语音容量"},
|
||||||
|
"app.skin-site.chat.admin.voice-capacity": {"message": "语音容量"},
|
||||||
|
"app.skin-site.chat.admin.participant-limit": {"message": "参与人数上限"},
|
||||||
|
"app.skin-site.chat.admin.listener-limit": {"message": "旁听人数上限"},
|
||||||
|
"app.skin-site.chat.admin.voice-capacity-updated": {"message": "语音容量已更新。"},
|
||||||
|
"app.skin-site.chat.admin.voice-enabled": {"message": "启用实时语音"},
|
||||||
|
"app.skin-site.chat.admin.voice-runtime-path": {"message": "语音库目录"},
|
||||||
|
"app.skin-site.chat.admin.voice-mirror": {"message": "镜像基础地址"},
|
||||||
|
"app.skin-site.chat.admin.voice-public-url": {"message": "公网访问地址"},
|
||||||
|
"app.skin-site.chat.admin.voice-bind": {"message": "监听地址"},
|
||||||
|
"app.skin-site.chat.admin.voice-signal-port": {"message": "信令端口"},
|
||||||
|
"app.skin-site.chat.admin.voice-rtc-tcp-port": {"message": "媒体 TCP 端口"},
|
||||||
|
"app.skin-site.chat.admin.voice-rtc-udp-port": {"message": "媒体 UDP 端口"},
|
||||||
|
"app.skin-site.chat.admin.voice-startup-timeout": {"message": "启动等待时间(秒)"},
|
||||||
|
"app.skin-site.chat.admin.voice-use-external-ip": {"message": "使用外部 IP"},
|
||||||
|
"app.skin-site.chat.admin.voice-node-ip": {"message": "节点 IP"},
|
||||||
|
"app.skin-site.chat.admin.voice-saved": {"message": "语音库配置已保存。"},
|
||||||
|
"app.skin-site.chat.admin.voice-logs-auto-refresh": {"message": "自动刷新"},
|
||||||
|
"app.skin-site.chat.admin.voice-logs-refresh": {"message": "刷新"},
|
||||||
|
"app.skin-site.chat.admin.voice-logs-empty": {"message": "暂无日志。"}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user