mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-01-31 21:39:54 +00:00
record audio node support (#4289)
Co-authored-by: github-actions <github-actions@github.com> Co-authored-by: Christian Byrne <cbyrne@comfy.org>
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
import type { LGraphNode } from '@comfyorg/litegraph'
|
||||
import type { IStringWidget } from '@comfyorg/litegraph/dist/types/widgets'
|
||||
import type {
|
||||
IBaseWidget,
|
||||
IStringWidget
|
||||
} from '@comfyorg/litegraph/dist/types/widgets'
|
||||
import { MediaRecorder as ExtendableMediaRecorder } from 'extendable-media-recorder'
|
||||
|
||||
import { useChainCallback } from '@/composables/functional/useChainCallback'
|
||||
import { useNodeDragAndDrop } from '@/composables/node/useNodeDragAndDrop'
|
||||
@@ -9,6 +13,7 @@ import { t } from '@/i18n'
|
||||
import type { ResultItemType } from '@/schemas/apiSchema'
|
||||
import type { ComfyNodeDef } from '@/schemas/nodeDefSchema'
|
||||
import type { DOMWidget } from '@/scripts/domWidget'
|
||||
import { useAudioService } from '@/services/audioService'
|
||||
import { useToastStore } from '@/stores/toastStore'
|
||||
import { NodeLocatorId } from '@/types'
|
||||
import { getNodeByLocatorId } from '@/utils/graphTraversalUtil'
|
||||
@@ -257,3 +262,167 @@ app.registerExtension({
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
app.registerExtension({
|
||||
name: 'Comfy.RecordAudio',
|
||||
|
||||
getCustomWidgets() {
|
||||
return {
|
||||
AUDIO_RECORD(node, inputName: string) {
|
||||
const audio = document.createElement('audio')
|
||||
audio.controls = true
|
||||
audio.classList.add('comfy-audio')
|
||||
audio.setAttribute('name', 'media')
|
||||
|
||||
const audioUIWidget: DOMWidget<HTMLAudioElement, string> =
|
||||
node.addDOMWidget(inputName, /* name=*/ 'audioUI', audio)
|
||||
|
||||
let mediaRecorder: MediaRecorder | null = null
|
||||
let isRecording = false
|
||||
let audioChunks: Blob[] = []
|
||||
let currentStream: MediaStream | null = null
|
||||
let recordWidget: IBaseWidget | null = null
|
||||
|
||||
let stopPromise: Promise<void> | null = null
|
||||
let stopResolve: (() => void) | null = null
|
||||
|
||||
audioUIWidget.serializeValue = async () => {
|
||||
if (isRecording && mediaRecorder) {
|
||||
stopPromise = new Promise((resolve) => {
|
||||
stopResolve = resolve
|
||||
})
|
||||
|
||||
mediaRecorder.stop()
|
||||
|
||||
await stopPromise
|
||||
}
|
||||
|
||||
const audioSrc = audioUIWidget.element.src
|
||||
|
||||
if (!audioSrc) {
|
||||
useToastStore().addAlert(t('g.noAudioRecorded'))
|
||||
return ''
|
||||
}
|
||||
|
||||
const blob = await fetch(audioSrc).then((r) => r.blob())
|
||||
|
||||
return await useAudioService().convertBlobToFileAndSubmit(blob)
|
||||
}
|
||||
|
||||
recordWidget = node.addWidget(
|
||||
'button',
|
||||
inputName,
|
||||
'',
|
||||
async () => {
|
||||
if (!isRecording) {
|
||||
try {
|
||||
currentStream = await navigator.mediaDevices.getUserMedia({
|
||||
audio: true
|
||||
})
|
||||
|
||||
mediaRecorder = new ExtendableMediaRecorder(currentStream, {
|
||||
mimeType: 'audio/wav'
|
||||
}) as unknown as MediaRecorder
|
||||
|
||||
audioChunks = []
|
||||
|
||||
mediaRecorder.ondataavailable = (event) => {
|
||||
audioChunks.push(event.data)
|
||||
}
|
||||
|
||||
mediaRecorder.onstop = async () => {
|
||||
const audioBlob = new Blob(audioChunks, { type: 'audio/wav' })
|
||||
|
||||
useAudioService().stopAllTracks(currentStream)
|
||||
|
||||
if (
|
||||
audioUIWidget.element.src &&
|
||||
audioUIWidget.element.src.startsWith('blob:')
|
||||
) {
|
||||
URL.revokeObjectURL(audioUIWidget.element.src)
|
||||
}
|
||||
|
||||
audioUIWidget.element.src = URL.createObjectURL(audioBlob)
|
||||
|
||||
isRecording = false
|
||||
|
||||
if (recordWidget) {
|
||||
recordWidget.label = t('g.startRecording')
|
||||
}
|
||||
|
||||
if (stopResolve) {
|
||||
stopResolve()
|
||||
stopResolve = null
|
||||
stopPromise = null
|
||||
}
|
||||
}
|
||||
|
||||
mediaRecorder.onerror = (event) => {
|
||||
console.error('MediaRecorder error:', event)
|
||||
useAudioService().stopAllTracks(currentStream)
|
||||
isRecording = false
|
||||
|
||||
if (recordWidget) {
|
||||
recordWidget.label = t('g.startRecording')
|
||||
}
|
||||
|
||||
if (stopResolve) {
|
||||
stopResolve()
|
||||
stopResolve = null
|
||||
stopPromise = null
|
||||
}
|
||||
}
|
||||
|
||||
mediaRecorder.start()
|
||||
isRecording = true
|
||||
if (recordWidget) {
|
||||
recordWidget.label = t('g.stopRecording')
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error accessing microphone:', err)
|
||||
useToastStore().addAlert(t('g.micPermissionDenied'))
|
||||
|
||||
if (mediaRecorder) {
|
||||
try {
|
||||
mediaRecorder.stop()
|
||||
} catch {}
|
||||
}
|
||||
useAudioService().stopAllTracks(currentStream)
|
||||
currentStream = null
|
||||
isRecording = false
|
||||
if (recordWidget) {
|
||||
recordWidget.label = t('g.startRecording')
|
||||
}
|
||||
}
|
||||
} else if (mediaRecorder && isRecording) {
|
||||
mediaRecorder.stop()
|
||||
}
|
||||
},
|
||||
{ serialize: false }
|
||||
)
|
||||
|
||||
recordWidget.label = t('g.startRecording')
|
||||
|
||||
const originalOnRemoved = node.onRemoved
|
||||
node.onRemoved = function () {
|
||||
if (isRecording && mediaRecorder) {
|
||||
mediaRecorder.stop()
|
||||
}
|
||||
useAudioService().stopAllTracks(currentStream)
|
||||
if (audioUIWidget.element.src?.startsWith('blob:')) {
|
||||
URL.revokeObjectURL(audioUIWidget.element.src)
|
||||
}
|
||||
originalOnRemoved?.call(this)
|
||||
}
|
||||
|
||||
return { widget: recordWidget }
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
async nodeCreated(node) {
|
||||
if (node.constructor.comfyClass !== 'RecordAudio') return
|
||||
|
||||
await useAudioService().registerWavEncoder()
|
||||
}
|
||||
})
|
||||
|
||||
@@ -158,6 +158,12 @@
|
||||
"Comfy_Manager_ToggleManagerProgressDialog": {
|
||||
"label": "Toggle the Custom Nodes Manager Progress Bar"
|
||||
},
|
||||
"Comfy_MaskEditor_BrushSize_Decrease": {
|
||||
"label": "Decrease Brush Size in MaskEditor"
|
||||
},
|
||||
"Comfy_MaskEditor_BrushSize_Increase": {
|
||||
"label": "Increase Brush Size in MaskEditor"
|
||||
},
|
||||
"Comfy_MaskEditor_OpenMaskEditor": {
|
||||
"label": "Open Mask Editor for Selected Node"
|
||||
},
|
||||
|
||||
@@ -134,6 +134,10 @@
|
||||
"releaseTitle": "{package} {version} Release",
|
||||
"progressCountOf": "of",
|
||||
"keybindingAlreadyExists": "Keybinding already exists on",
|
||||
"startRecording": "Start Recording",
|
||||
"stopRecording": "Stop Recording",
|
||||
"micPermissionDenied": "Microphone permission denied",
|
||||
"noAudioRecorded": "No audio recorded",
|
||||
"nodesRunning": "nodes running"
|
||||
},
|
||||
"manager": {
|
||||
@@ -973,6 +977,8 @@
|
||||
"Load Default Workflow": "Load Default Workflow",
|
||||
"Toggle the Custom Nodes Manager": "Toggle the Custom Nodes Manager",
|
||||
"Toggle the Custom Nodes Manager Progress Bar": "Toggle the Custom Nodes Manager Progress Bar",
|
||||
"Decrease Brush Size in MaskEditor": "Decrease Brush Size in MaskEditor",
|
||||
"Increase Brush Size in MaskEditor": "Increase Brush Size in MaskEditor",
|
||||
"Open Mask Editor for Selected Node": "Open Mask Editor for Selected Node",
|
||||
"New": "New",
|
||||
"Clipspace": "Clipspace",
|
||||
|
||||
@@ -158,6 +158,12 @@
|
||||
"Comfy_Manager_ToggleManagerProgressDialog": {
|
||||
"label": "Alternar diálogo de progreso del administrador"
|
||||
},
|
||||
"Comfy_MaskEditor_BrushSize_Decrease": {
|
||||
"label": "Disminuir tamaño del pincel en MaskEditor"
|
||||
},
|
||||
"Comfy_MaskEditor_BrushSize_Increase": {
|
||||
"label": "Aumentar tamaño del pincel en MaskEditor"
|
||||
},
|
||||
"Comfy_MaskEditor_OpenMaskEditor": {
|
||||
"label": "Abrir editor de máscara para el nodo seleccionado"
|
||||
},
|
||||
|
||||
@@ -331,12 +331,14 @@
|
||||
"loadingPanel": "Cargando panel {panel}...",
|
||||
"login": "Iniciar sesión",
|
||||
"logs": "Registros",
|
||||
"micPermissionDenied": "Permiso de micrófono denegado",
|
||||
"migrate": "Migrar",
|
||||
"missing": "Faltante",
|
||||
"name": "Nombre",
|
||||
"newFolder": "Nueva carpeta",
|
||||
"next": "Siguiente",
|
||||
"no": "No",
|
||||
"noAudioRecorded": "No se grabó audio",
|
||||
"noResultsFound": "No se encontraron resultados",
|
||||
"noTasksFound": "No se encontraron tareas",
|
||||
"noTasksFoundMessage": "No hay tareas en la cola.",
|
||||
@@ -376,7 +378,9 @@
|
||||
"showReport": "Mostrar informe",
|
||||
"sort": "Ordenar",
|
||||
"source": "Fuente",
|
||||
"startRecording": "Iniciar grabación",
|
||||
"status": "Estado",
|
||||
"stopRecording": "Detener grabación",
|
||||
"success": "Éxito",
|
||||
"systemInfo": "Información del sistema",
|
||||
"terminal": "Terminal",
|
||||
@@ -747,6 +751,7 @@
|
||||
"Contact Support": "Contactar soporte",
|
||||
"Convert Selection to Subgraph": "Convertir selección en subgrafo",
|
||||
"Convert selected nodes to group node": "Convertir nodos seleccionados en nodo de grupo",
|
||||
"Decrease Brush Size in MaskEditor": "Disminuir tamaño del pincel en MaskEditor",
|
||||
"Delete Selected Items": "Eliminar elementos seleccionados",
|
||||
"Desktop User Guide": "Guía de usuario de escritorio",
|
||||
"Duplicate Current Workflow": "Duplicar flujo de trabajo actual",
|
||||
@@ -758,6 +763,7 @@
|
||||
"Give Feedback": "Dar retroalimentación",
|
||||
"Group Selected Nodes": "Agrupar nodos seleccionados",
|
||||
"Help": "Ayuda",
|
||||
"Increase Brush Size in MaskEditor": "Aumentar tamaño del pincel en MaskEditor",
|
||||
"Interrupt": "Interrumpir",
|
||||
"Load Default Workflow": "Cargar flujo de trabajo predeterminado",
|
||||
"Manage group nodes": "Gestionar nodos de grupo",
|
||||
|
||||
@@ -158,6 +158,12 @@
|
||||
"Comfy_Manager_ToggleManagerProgressDialog": {
|
||||
"label": "Basculer la boîte de dialogue de progression"
|
||||
},
|
||||
"Comfy_MaskEditor_BrushSize_Decrease": {
|
||||
"label": "Réduire la taille du pinceau dans MaskEditor"
|
||||
},
|
||||
"Comfy_MaskEditor_BrushSize_Increase": {
|
||||
"label": "Augmenter la taille du pinceau dans MaskEditor"
|
||||
},
|
||||
"Comfy_MaskEditor_OpenMaskEditor": {
|
||||
"label": "Ouvrir l'éditeur de masque pour le nœud sélectionné"
|
||||
},
|
||||
|
||||
@@ -331,12 +331,14 @@
|
||||
"loadingPanel": "Chargement du panneau {panel}...",
|
||||
"login": "Connexion",
|
||||
"logs": "Journaux",
|
||||
"micPermissionDenied": "Permission du microphone refusée",
|
||||
"migrate": "Migrer",
|
||||
"missing": "Manquant",
|
||||
"name": "Nom",
|
||||
"newFolder": "Nouveau dossier",
|
||||
"next": "Suivant",
|
||||
"no": "Non",
|
||||
"noAudioRecorded": "Aucun audio enregistré",
|
||||
"noResultsFound": "Aucun résultat trouvé",
|
||||
"noTasksFound": "Aucune tâche trouvée",
|
||||
"noTasksFoundMessage": "Il n'y a pas de tâches dans la file d'attente.",
|
||||
@@ -376,7 +378,9 @@
|
||||
"showReport": "Afficher le rapport",
|
||||
"sort": "Trier",
|
||||
"source": "Source",
|
||||
"startRecording": "Commencer l’enregistrement",
|
||||
"status": "Statut",
|
||||
"stopRecording": "Arrêter l’enregistrement",
|
||||
"success": "Succès",
|
||||
"systemInfo": "Informations système",
|
||||
"terminal": "Terminal",
|
||||
@@ -747,6 +751,7 @@
|
||||
"Contact Support": "Contacter le support",
|
||||
"Convert Selection to Subgraph": "Convertir la sélection en sous-graphe",
|
||||
"Convert selected nodes to group node": "Convertir les nœuds sélectionnés en nœud de groupe",
|
||||
"Decrease Brush Size in MaskEditor": "Réduire la taille du pinceau dans MaskEditor",
|
||||
"Delete Selected Items": "Supprimer les éléments sélectionnés",
|
||||
"Desktop User Guide": "Guide de l'utilisateur de bureau",
|
||||
"Duplicate Current Workflow": "Dupliquer le flux de travail actuel",
|
||||
@@ -758,6 +763,7 @@
|
||||
"Give Feedback": "Donnez votre avis",
|
||||
"Group Selected Nodes": "Grouper les nœuds sélectionnés",
|
||||
"Help": "Aide",
|
||||
"Increase Brush Size in MaskEditor": "Augmenter la taille du pinceau dans MaskEditor",
|
||||
"Interrupt": "Interrompre",
|
||||
"Load Default Workflow": "Charger le flux de travail par défaut",
|
||||
"Manage group nodes": "Gérer les nœuds de groupe",
|
||||
|
||||
@@ -158,6 +158,12 @@
|
||||
"Comfy_Manager_ToggleManagerProgressDialog": {
|
||||
"label": "プログレスダイアログの切り替え"
|
||||
},
|
||||
"Comfy_MaskEditor_BrushSize_Decrease": {
|
||||
"label": "マスクエディタでブラシサイズを縮小"
|
||||
},
|
||||
"Comfy_MaskEditor_BrushSize_Increase": {
|
||||
"label": "マスクエディタでブラシサイズを大きくする"
|
||||
},
|
||||
"Comfy_MaskEditor_OpenMaskEditor": {
|
||||
"label": "選択したノードのマスクエディタを開く"
|
||||
},
|
||||
|
||||
@@ -331,12 +331,14 @@
|
||||
"loadingPanel": "{panel} パネルを読み込み中...",
|
||||
"login": "ログイン",
|
||||
"logs": "ログ",
|
||||
"micPermissionDenied": "マイクの許可が拒否されました",
|
||||
"migrate": "移行する",
|
||||
"missing": "不足している",
|
||||
"name": "名前",
|
||||
"newFolder": "新しいフォルダー",
|
||||
"next": "次へ",
|
||||
"no": "いいえ",
|
||||
"noAudioRecorded": "音声が録音されていません",
|
||||
"noResultsFound": "結果が見つかりません",
|
||||
"noTasksFound": "タスクが見つかりません",
|
||||
"noTasksFoundMessage": "キューにタスクがありません。",
|
||||
@@ -376,7 +378,9 @@
|
||||
"showReport": "レポートを表示",
|
||||
"sort": "並び替え",
|
||||
"source": "ソース",
|
||||
"startRecording": "録音開始",
|
||||
"status": "ステータス",
|
||||
"stopRecording": "録音停止",
|
||||
"success": "成功",
|
||||
"systemInfo": "システム情報",
|
||||
"terminal": "ターミナル",
|
||||
@@ -747,6 +751,7 @@
|
||||
"Contact Support": "サポートに連絡",
|
||||
"Convert Selection to Subgraph": "選択範囲をサブグラフに変換",
|
||||
"Convert selected nodes to group node": "選択したノードをグループノードに変換",
|
||||
"Decrease Brush Size in MaskEditor": "マスクエディタでブラシサイズを小さくする",
|
||||
"Delete Selected Items": "選択したアイテムを削除",
|
||||
"Desktop User Guide": "デスクトップユーザーガイド",
|
||||
"Duplicate Current Workflow": "現在のワークフローを複製",
|
||||
@@ -758,6 +763,7 @@
|
||||
"Give Feedback": "フィードバックを送る",
|
||||
"Group Selected Nodes": "選択したノードをグループ化",
|
||||
"Help": "ヘルプ",
|
||||
"Increase Brush Size in MaskEditor": "マスクエディタでブラシサイズを大きくする",
|
||||
"Interrupt": "中断",
|
||||
"Load Default Workflow": "デフォルトワークフローを読み込む",
|
||||
"Manage group nodes": "グループノードを管理",
|
||||
|
||||
@@ -158,6 +158,12 @@
|
||||
"Comfy_Manager_ToggleManagerProgressDialog": {
|
||||
"label": "진행 상황 대화 상자 전환"
|
||||
},
|
||||
"Comfy_MaskEditor_BrushSize_Decrease": {
|
||||
"label": "MaskEditor에서 브러시 크기 줄이기"
|
||||
},
|
||||
"Comfy_MaskEditor_BrushSize_Increase": {
|
||||
"label": "MaskEditor에서 브러시 크기 늘리기"
|
||||
},
|
||||
"Comfy_MaskEditor_OpenMaskEditor": {
|
||||
"label": "선택한 노드 마스크 편집기 열기"
|
||||
},
|
||||
|
||||
@@ -331,12 +331,14 @@
|
||||
"loadingPanel": "{panel} 패널 불러오는 중...",
|
||||
"login": "로그인",
|
||||
"logs": "로그",
|
||||
"micPermissionDenied": "마이크 권한이 거부되었습니다",
|
||||
"migrate": "이전(migrate)",
|
||||
"missing": "누락됨",
|
||||
"name": "이름",
|
||||
"newFolder": "새 폴더",
|
||||
"next": "다음",
|
||||
"no": "아니오",
|
||||
"noAudioRecorded": "녹음된 오디오가 없습니다",
|
||||
"noResultsFound": "결과를 찾을 수 없습니다.",
|
||||
"noTasksFound": "작업을 찾을 수 없습니다.",
|
||||
"noTasksFoundMessage": "대기열에 작업이 없습니다.",
|
||||
@@ -376,7 +378,9 @@
|
||||
"showReport": "보고서 보기",
|
||||
"sort": "정렬",
|
||||
"source": "소스",
|
||||
"startRecording": "녹음 시작",
|
||||
"status": "상태",
|
||||
"stopRecording": "녹음 중지",
|
||||
"success": "성공",
|
||||
"systemInfo": "시스템 정보",
|
||||
"terminal": "터미널",
|
||||
@@ -747,6 +751,7 @@
|
||||
"Contact Support": "고객 지원 문의",
|
||||
"Convert Selection to Subgraph": "선택 영역을 서브그래프로 변환",
|
||||
"Convert selected nodes to group node": "선택한 노드를 그룹 노드로 변환",
|
||||
"Decrease Brush Size in MaskEditor": "MaskEditor에서 브러시 크기 줄이기",
|
||||
"Delete Selected Items": "선택한 항목 삭제",
|
||||
"Desktop User Guide": "데스크톱 사용자 가이드",
|
||||
"Duplicate Current Workflow": "현재 워크플로 복제",
|
||||
@@ -758,6 +763,7 @@
|
||||
"Give Feedback": "피드백 제공",
|
||||
"Group Selected Nodes": "선택한 노드 그룹화",
|
||||
"Help": "도움말",
|
||||
"Increase Brush Size in MaskEditor": "MaskEditor에서 브러시 크기 늘리기",
|
||||
"Interrupt": "중단",
|
||||
"Load Default Workflow": "기본 워크플로 불러오기",
|
||||
"Manage group nodes": "그룹 노드 관리",
|
||||
|
||||
@@ -158,6 +158,12 @@
|
||||
"Comfy_Manager_ToggleManagerProgressDialog": {
|
||||
"label": "Переключить диалоговое окно прогресса"
|
||||
},
|
||||
"Comfy_MaskEditor_BrushSize_Decrease": {
|
||||
"label": "Уменьшить размер кисти в MaskEditor"
|
||||
},
|
||||
"Comfy_MaskEditor_BrushSize_Increase": {
|
||||
"label": "Увеличить размер кисти в MaskEditor"
|
||||
},
|
||||
"Comfy_MaskEditor_OpenMaskEditor": {
|
||||
"label": "Открыть редактор масок для выбранной ноды"
|
||||
},
|
||||
|
||||
@@ -331,12 +331,14 @@
|
||||
"loadingPanel": "Загрузка панели {panel}...",
|
||||
"login": "Вход",
|
||||
"logs": "Логи",
|
||||
"micPermissionDenied": "Доступ к микрофону запрещён",
|
||||
"migrate": "Мигрировать",
|
||||
"missing": "Отсутствует",
|
||||
"name": "Имя",
|
||||
"newFolder": "Новая папка",
|
||||
"next": "Далее",
|
||||
"no": "Нет",
|
||||
"noAudioRecorded": "Аудио не записано",
|
||||
"noResultsFound": "Результатов не найдено",
|
||||
"noTasksFound": "Задачи не найдены",
|
||||
"noTasksFoundMessage": "В очереди нет задач.",
|
||||
@@ -376,7 +378,9 @@
|
||||
"showReport": "Показать отчёт",
|
||||
"sort": "Сортировать",
|
||||
"source": "Источник",
|
||||
"startRecording": "Начать запись",
|
||||
"status": "Статус",
|
||||
"stopRecording": "Остановить запись",
|
||||
"success": "Успех",
|
||||
"systemInfo": "Информация о системе",
|
||||
"terminal": "Терминал",
|
||||
@@ -747,6 +751,7 @@
|
||||
"Contact Support": "Связаться с поддержкой",
|
||||
"Convert Selection to Subgraph": "Преобразовать выделенное в подграф",
|
||||
"Convert selected nodes to group node": "Преобразовать выбранные ноды в групповую ноду",
|
||||
"Decrease Brush Size in MaskEditor": "Уменьшить размер кисти в MaskEditor",
|
||||
"Delete Selected Items": "Удалить выбранные элементы",
|
||||
"Desktop User Guide": "Руководство пользователя для настольных ПК",
|
||||
"Duplicate Current Workflow": "Дублировать текущий рабочий процесс",
|
||||
@@ -758,6 +763,7 @@
|
||||
"Give Feedback": "Оставить отзыв",
|
||||
"Group Selected Nodes": "Сгруппировать выбранные ноды",
|
||||
"Help": "Помощь",
|
||||
"Increase Brush Size in MaskEditor": "Увеличить размер кисти в MaskEditor",
|
||||
"Interrupt": "Прервать",
|
||||
"Load Default Workflow": "Загрузить стандартный рабочий процесс",
|
||||
"Manage group nodes": "Управление групповыми нодами",
|
||||
|
||||
@@ -158,6 +158,12 @@
|
||||
"Comfy_Manager_ToggleManagerProgressDialog": {
|
||||
"label": "切換自訂節點管理器進度條"
|
||||
},
|
||||
"Comfy_MaskEditor_BrushSize_Decrease": {
|
||||
"label": "減少 MaskEditor 畫筆大小"
|
||||
},
|
||||
"Comfy_MaskEditor_BrushSize_Increase": {
|
||||
"label": "增加 MaskEditor 畫筆大小"
|
||||
},
|
||||
"Comfy_MaskEditor_OpenMaskEditor": {
|
||||
"label": "為選取的節點開啟 Mask 編輯器"
|
||||
},
|
||||
|
||||
@@ -331,12 +331,14 @@
|
||||
"loadingPanel": "正在載入{panel}面板...",
|
||||
"login": "登入",
|
||||
"logs": "日誌",
|
||||
"micPermissionDenied": "麥克風權限被拒絕",
|
||||
"migrate": "遷移",
|
||||
"missing": "缺少",
|
||||
"name": "名稱",
|
||||
"newFolder": "新資料夾",
|
||||
"next": "下一步",
|
||||
"no": "否",
|
||||
"noAudioRecorded": "沒有錄製到音訊",
|
||||
"noResultsFound": "找不到結果",
|
||||
"noTasksFound": "找不到任務",
|
||||
"noTasksFoundMessage": "佇列中沒有任務。",
|
||||
@@ -376,7 +378,9 @@
|
||||
"showReport": "顯示報告",
|
||||
"sort": "排序",
|
||||
"source": "來源",
|
||||
"startRecording": "開始錄音",
|
||||
"status": "狀態",
|
||||
"stopRecording": "停止錄音",
|
||||
"success": "成功",
|
||||
"systemInfo": "系統資訊",
|
||||
"terminal": "終端機",
|
||||
@@ -747,6 +751,7 @@
|
||||
"Contact Support": "聯絡支援",
|
||||
"Convert Selection to Subgraph": "將選取內容轉為子圖",
|
||||
"Convert selected nodes to group node": "將選取節點轉為群組節點",
|
||||
"Decrease Brush Size in MaskEditor": "在 MaskEditor 中減小筆刷大小",
|
||||
"Delete Selected Items": "刪除選取項目",
|
||||
"Desktop User Guide": "桌面應用程式使用指南",
|
||||
"Duplicate Current Workflow": "複製目前工作流程",
|
||||
@@ -758,6 +763,7 @@
|
||||
"Give Feedback": "提供意見回饋",
|
||||
"Group Selected Nodes": "群組選取節點",
|
||||
"Help": "說明",
|
||||
"Increase Brush Size in MaskEditor": "在 MaskEditor 中增大筆刷大小",
|
||||
"Interrupt": "中斷",
|
||||
"Load Default Workflow": "載入預設工作流程",
|
||||
"Manage group nodes": "管理群組節點",
|
||||
|
||||
@@ -158,6 +158,12 @@
|
||||
"Comfy_Manager_ToggleManagerProgressDialog": {
|
||||
"label": "切换进度对话框"
|
||||
},
|
||||
"Comfy_MaskEditor_BrushSize_Decrease": {
|
||||
"label": "減小 MaskEditor 中的筆刷大小"
|
||||
},
|
||||
"Comfy_MaskEditor_BrushSize_Increase": {
|
||||
"label": "增加 MaskEditor 畫筆大小"
|
||||
},
|
||||
"Comfy_MaskEditor_OpenMaskEditor": {
|
||||
"label": "打开选中节点的遮罩编辑器"
|
||||
},
|
||||
|
||||
@@ -331,12 +331,14 @@
|
||||
"loadingPanel": "正在加载{panel}面板...",
|
||||
"login": "登录",
|
||||
"logs": "日志",
|
||||
"micPermissionDenied": "麦克风权限被拒绝",
|
||||
"migrate": "迁移",
|
||||
"missing": "缺失",
|
||||
"name": "名称",
|
||||
"newFolder": "新文件夹",
|
||||
"next": "下一个",
|
||||
"no": "否",
|
||||
"noAudioRecorded": "未录制音频",
|
||||
"noResultsFound": "未找到结果",
|
||||
"noTasksFound": "未找到任务",
|
||||
"noTasksFoundMessage": "队列中没有任务。",
|
||||
@@ -376,7 +378,9 @@
|
||||
"showReport": "显示报告",
|
||||
"sort": "排序",
|
||||
"source": "来源",
|
||||
"startRecording": "开始录音",
|
||||
"status": "状态",
|
||||
"stopRecording": "停止录音",
|
||||
"success": "成功",
|
||||
"systemInfo": "系统信息",
|
||||
"terminal": "终端",
|
||||
@@ -747,6 +751,7 @@
|
||||
"Contact Support": "联系支持",
|
||||
"Convert Selection to Subgraph": "将选中内容转换为子图",
|
||||
"Convert selected nodes to group node": "将选中节点转换为组节点",
|
||||
"Decrease Brush Size in MaskEditor": "在 MaskEditor 中減小筆刷大小",
|
||||
"Delete Selected Items": "删除选定的项目",
|
||||
"Desktop User Guide": "桌面端用户指南",
|
||||
"Duplicate Current Workflow": "复制当前工作流",
|
||||
@@ -758,6 +763,7 @@
|
||||
"Give Feedback": "提供反馈",
|
||||
"Group Selected Nodes": "将选中节点转换为组节点",
|
||||
"Help": "帮助",
|
||||
"Increase Brush Size in MaskEditor": "在 MaskEditor 中增大筆刷大小",
|
||||
"Interrupt": "中断",
|
||||
"Load Default Workflow": "加载默认工作流",
|
||||
"Manage group nodes": "管理组节点",
|
||||
|
||||
84
src/services/audioService.ts
Normal file
84
src/services/audioService.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import { register } from 'extendable-media-recorder'
|
||||
import { connect } from 'extendable-media-recorder-wav-encoder'
|
||||
|
||||
import { api } from '@/scripts/api'
|
||||
import { useToastStore } from '@/stores/toastStore'
|
||||
|
||||
export interface AudioRecordingError {
|
||||
type: 'permission' | 'not_supported' | 'encoder' | 'recording' | 'unknown'
|
||||
message: string
|
||||
originalError?: unknown
|
||||
}
|
||||
|
||||
let isEncoderRegistered: boolean = false
|
||||
|
||||
export const useAudioService = () => {
|
||||
const handleError = (
|
||||
type: AudioRecordingError['type'],
|
||||
message: string,
|
||||
originalError?: unknown
|
||||
) => {
|
||||
console.error(`Audio Service Error (${type}):`, message, originalError)
|
||||
}
|
||||
|
||||
const stopAllTracks = (currentStream: MediaStream | null) => {
|
||||
if (currentStream) {
|
||||
currentStream.getTracks().forEach((track) => {
|
||||
track.stop()
|
||||
})
|
||||
currentStream = null
|
||||
}
|
||||
}
|
||||
|
||||
const registerWavEncoder = async (): Promise<void> => {
|
||||
if (isEncoderRegistered) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await register(await connect())
|
||||
isEncoderRegistered = true
|
||||
} catch (err) {
|
||||
if (
|
||||
err instanceof Error &&
|
||||
err.message.includes('already an encoder stored')
|
||||
) {
|
||||
isEncoderRegistered = true
|
||||
} else {
|
||||
handleError('encoder', 'Failed to register WAV encoder', err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const convertBlobToFileAndSubmit = async (blob: Blob): Promise<string> => {
|
||||
const name = `recording-${Date.now()}.wav`
|
||||
const file = new File([blob], name, { type: blob.type || 'audio/wav' })
|
||||
|
||||
const body = new FormData()
|
||||
body.append('image', file)
|
||||
body.append('subfolder', 'audio')
|
||||
body.append('type', 'temp')
|
||||
|
||||
const resp = await api.fetchApi('/upload/image', {
|
||||
method: 'POST',
|
||||
body
|
||||
})
|
||||
|
||||
if (resp.status !== 200) {
|
||||
const err = `Error uploading temp file: ${resp.status} - ${resp.statusText}`
|
||||
useToastStore().addAlert(err)
|
||||
throw new Error(err)
|
||||
}
|
||||
|
||||
const tempAudio = await resp.json()
|
||||
|
||||
return `audio/${tempAudio.name} [temp]`
|
||||
}
|
||||
|
||||
return {
|
||||
// Methods
|
||||
convertBlobToFileAndSubmit,
|
||||
registerWavEncoder,
|
||||
stopAllTracks
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user