- 顶部展示语音运行时状态:状态标签、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">
|
||||
import { SpinnerIcon } from '@modrinth/assets'
|
||||
import { defineMessages, injectNotificationManager, useVIntl } from '@modrinth/ui'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { computed, onBeforeUnmount, onMounted, reactive, ref } from 'vue'
|
||||
|
||||
import {
|
||||
type SkinSiteChatPrerequisiteMode,
|
||||
@@ -17,6 +17,15 @@ import {
|
||||
skinSiteChatSettlementOwnerAssign,
|
||||
type SkinSiteChatAuditInfo,
|
||||
type SkinSiteChatUserInfo,
|
||||
skinSiteChatVoiceCapacityUpdate,
|
||||
skinSiteChatVoiceConfig,
|
||||
skinSiteChatVoiceConfigUpdate,
|
||||
skinSiteChatVoiceLogs,
|
||||
skinSiteChatVoiceRuntime,
|
||||
skinSiteChatVoiceRuntimeRetry,
|
||||
type SkinSiteChatVoiceGlobalConfig,
|
||||
type SkinSiteChatVoiceLogEntry,
|
||||
type SkinSiteChatVoiceRuntimeInfo,
|
||||
} from '@/helpers/skin-site-api'
|
||||
|
||||
import ChatUserPicker from '../ChatUserPicker.vue'
|
||||
@@ -158,6 +167,110 @@ const messages = defineMessages({
|
||||
defaultMessage: 'Target: {name}',
|
||||
},
|
||||
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 = {
|
||||
@@ -460,7 +573,217 @@ function closeAudit() {
|
||||
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>
|
||||
|
||||
<template>
|
||||
@@ -481,6 +804,55 @@ onMounted(() => void loadRooms())
|
||||
</button>
|
||||
</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
|
||||
v-if="loading"
|
||||
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) }}
|
||||
</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
|
||||
type="button"
|
||||
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[]>> {
|
||||
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-empty": {"message": "No audit records yet."},
|
||||
"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-empty": {"message": "暂无审计记录。"},
|
||||
"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