mirror of
https://github.com/w-okada/voice-changer.git
synced 2025-02-02 16:23:58 +03:00
WIP: integrate vcs to new gui
This commit is contained in:
parent
3bed1af628
commit
b453e5fd85
11
client/demo/dist/index.html
vendored
11
client/demo/dist/index.html
vendored
@ -1 +1,10 @@
|
|||||||
<!doctype html><html style="width:100%;height:100%;overflow:hidden"><head><meta charset="utf-8"/><title>Voice Changer Client Demo</title><script defer="defer" src="index.js"></script></head><body style="width:100%;height:100%;margin:0"><div id="app" style="width:100%;height:100%"></div></body></html>
|
<!DOCTYPE html>
|
||||||
|
<html style="width: 100%; height: 100%; overflow: hidden">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Voice Changer Client Demo</title>
|
||||||
|
<script defer src="index.js"></script></head>
|
||||||
|
<body style="width: 100%; height: 100%; margin: 0px">
|
||||||
|
<div id="app" style="width: 100%; height: 100%"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
1948
client/demo/dist/index.js
vendored
1948
client/demo/dist/index.js
vendored
File diff suppressed because one or more lines are too long
31
client/demo/dist/index.js.LICENSE.txt
vendored
31
client/demo/dist/index.js.LICENSE.txt
vendored
@ -1,31 +0,0 @@
|
|||||||
/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @license React
|
|
||||||
* react-dom.production.min.js
|
|
||||||
*
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @license React
|
|
||||||
* react.production.min.js
|
|
||||||
*
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @license React
|
|
||||||
* scheduler.production.min.js
|
|
||||||
*
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree.
|
|
||||||
*/
|
|
205
client/demo/src/components/demo/904-1_MainScreen.tsx
Normal file
205
client/demo/src/components/demo/904-1_MainScreen.tsx
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
import React, { useMemo } from "react";
|
||||||
|
import { useGuiState } from "./001_GuiStateProvider";
|
||||||
|
import { useAppState } from "../../001_provider/001_AppStateProvider";
|
||||||
|
import { RVCModelSlot, fileSelector } from "@dannadori/voice-changer-client-js";
|
||||||
|
import { useMessageBuilder } from "../../hooks/useMessageBuilder";
|
||||||
|
import { ModelSlotManagerDialogScreen } from "./904_ModelSlotManagerDialog";
|
||||||
|
import { checkExtention, trimfileName } from "../../utils/utils";
|
||||||
|
|
||||||
|
export type MainScreenProps = {
|
||||||
|
screen: ModelSlotManagerDialogScreen
|
||||||
|
close: () => void
|
||||||
|
openSampleDownloader: (slotIndex: number) => void
|
||||||
|
openFileUploader: (slotIndex: number) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const MainScreen = (props: MainScreenProps) => {
|
||||||
|
const { serverSetting } = useAppState()
|
||||||
|
const guiState = useGuiState()
|
||||||
|
const messageBuilderState = useMessageBuilder()
|
||||||
|
useMemo(() => {
|
||||||
|
messageBuilderState.setMessage(__filename, "change_icon", { "ja": "アイコン変更", "en": "chage icon" })
|
||||||
|
messageBuilderState.setMessage(__filename, "rename", { "ja": "リネーム", "en": "rename" })
|
||||||
|
messageBuilderState.setMessage(__filename, "download", { "ja": "ダウンロード", "en": "download" })
|
||||||
|
messageBuilderState.setMessage(__filename, "terms_of_use", { "ja": "利用規約", "en": "terms of use" })
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
|
||||||
|
const screen = useMemo(() => {
|
||||||
|
if (props.screen != "Main") {
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
|
if (!serverSetting.serverSetting.modelSlots) {
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const iconAction = async (index: number) => {
|
||||||
|
if (!serverSetting.serverSetting.modelSlots[index].name || serverSetting.serverSetting.modelSlots[index].name.length == 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const file = await fileSelector("")
|
||||||
|
if (checkExtention(file.name, ["png", "jpg", "jpeg", "gif"]) == false) {
|
||||||
|
alert(`サムネイルの拡張子は".png", ".jpg", ".jpeg", ".gif"である必要があります。`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
await serverSetting.uploadAssets(index, "iconFile", file)
|
||||||
|
}
|
||||||
|
|
||||||
|
const nameValueAction = async (index: number) => {
|
||||||
|
if (!serverSetting.serverSetting.modelSlots[index].name || serverSetting.serverSetting.modelSlots[index].name.length == 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Open Text Input Dialog
|
||||||
|
const p = new Promise<string>((resolve) => {
|
||||||
|
guiState.setTextInputResolve({ resolve: resolve })
|
||||||
|
})
|
||||||
|
guiState.stateControls.showTextInputCheckbox.updateState(true)
|
||||||
|
const text = await p;
|
||||||
|
|
||||||
|
// Send to Server
|
||||||
|
if (text.length > 0) {
|
||||||
|
console.log("input text:", text)
|
||||||
|
await serverSetting.updateModelInfo(index, "name", text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileValueAction = (url: string) => {
|
||||||
|
if (url.length == 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const link = document.createElement("a")
|
||||||
|
link.href = "./" + url
|
||||||
|
link.download = url.replace(/^.*[\\\/]/, '')
|
||||||
|
link.click()
|
||||||
|
link.remove()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const closeButtonRow = (
|
||||||
|
<div className="body-row split-3-4-3 left-padding-1">
|
||||||
|
<div className="body-item-text">
|
||||||
|
</div>
|
||||||
|
<div className="body-button-container body-button-container-space-around">
|
||||||
|
<div className="body-button" onClick={() => { props.close() }} >close</div>
|
||||||
|
</div>
|
||||||
|
<div className="body-item-text"></div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const slotRow = serverSetting.serverSetting.modelSlots.map((x, index) => {
|
||||||
|
// モデルのアイコン
|
||||||
|
const generateIconArea = (slotIndex: number, iconUrl: string, tooltip: boolean) => {
|
||||||
|
const realIconUrl = iconUrl.length > 0 ? iconUrl : "/assets/icons/noimage.png"
|
||||||
|
const iconDivClass = tooltip ? "tooltip" : ""
|
||||||
|
const iconClass = tooltip ? "model-slot-icon-pointable" : "model-slot-icon"
|
||||||
|
return (
|
||||||
|
<div className={iconDivClass}>
|
||||||
|
<img src={realIconUrl} className={iconClass} onClick={() => { iconAction(slotIndex) }} />
|
||||||
|
<div className="tooltip-text tooltip-text-thin tooltip-text-lower">
|
||||||
|
{messageBuilderState.getMessage(__filename, "change_icon")}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// モデルの名前
|
||||||
|
const generateNameRow = (slotIndex: number, name: string, termsOfUseUrl: string) => {
|
||||||
|
const nameValueClass = name.length > 0 ? "model-slot-detail-row-value-pointable tooltip" : "model-slot-detail-row-value"
|
||||||
|
const displayName = name.length > 0 ? name : "blank"
|
||||||
|
const termOfUseUrlLink = termsOfUseUrl.length > 0 ? <a href={termsOfUseUrl} target="_blank" rel="noopener noreferrer" className="body-item-text-small">[{messageBuilderState.getMessage(__filename, "terms_of_use")}]</a> : <></>
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="model-slot-detail-row">
|
||||||
|
<div className="model-slot-detail-row-label">[{slotIndex}]</div>
|
||||||
|
<div className={nameValueClass} onClick={() => { nameValueAction(slotIndex) }}>
|
||||||
|
{displayName}
|
||||||
|
<div className="tooltip-text tooltip-text-thin">
|
||||||
|
{messageBuilderState.getMessage(__filename, "rename")}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="">{termOfUseUrlLink}</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// モデルを構成するファイル
|
||||||
|
const generateFileRow = (title: string, filePath: string) => {
|
||||||
|
const fileValueClass = filePath.length > 0 ? "model-slot-detail-row-value-download tooltip" : "model-slot-detail-row-value"
|
||||||
|
return (
|
||||||
|
<div key={`${title}`} className="model-slot-detail-row">
|
||||||
|
<div className="model-slot-detail-row-label">{title}:</div>
|
||||||
|
<div className={fileValueClass} onClick={() => { fileValueAction(filePath) }}>
|
||||||
|
{trimfileName(filePath, 20)}
|
||||||
|
<div className="tooltip-text tooltip-text-thin">
|
||||||
|
{messageBuilderState.getMessage(__filename, "download")}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// その他情報欄
|
||||||
|
const generateInfoRow = (info: string) => {
|
||||||
|
return (
|
||||||
|
<div className="model-slot-detail-row">
|
||||||
|
<div className="model-slot-detail-row-label">info: </div>
|
||||||
|
<div className="model-slot-detail-row-value">{info}</div>
|
||||||
|
<div className=""></div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let iconArea = <></>
|
||||||
|
let nameRow = <></>
|
||||||
|
const fileRows = []
|
||||||
|
let infoRow = <></>
|
||||||
|
if (x.voiceChangerType == "RVC") {
|
||||||
|
const slotInfo = x as RVCModelSlot
|
||||||
|
iconArea = generateIconArea(index, slotInfo.iconFile, true)
|
||||||
|
nameRow = generateNameRow(index, slotInfo.name, slotInfo.termsOfUseUrl)
|
||||||
|
fileRows.push(generateFileRow("model", slotInfo.modelFile))
|
||||||
|
fileRows.push(generateFileRow("index", slotInfo.indexFile))
|
||||||
|
infoRow = generateInfoRow(`${slotInfo.f0 ? "f0" : "nof0"}, ${slotInfo.samplingRate}, ${slotInfo.embChannels}, ${slotInfo.modelType}, ${slotInfo.defaultTune}, ${slotInfo.defaultIndexRatio}, ${slotInfo.defaultProtect}`)
|
||||||
|
} else {
|
||||||
|
iconArea = generateIconArea(index, "/assets/icons/blank.png", false)
|
||||||
|
nameRow = generateNameRow(index, "", "")
|
||||||
|
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div key={index} className="model-slot">
|
||||||
|
{iconArea}
|
||||||
|
<div className="model-slot-detail">
|
||||||
|
{nameRow}
|
||||||
|
{fileRows}
|
||||||
|
{infoRow}
|
||||||
|
</div>
|
||||||
|
<div className="model-slot-buttons">
|
||||||
|
<div className="model-slot-button" onClick={() => { props.openFileUploader(index) }} >upload</div>
|
||||||
|
<div className="model-slot-button" onClick={() => { props.openSampleDownloader(index) }} >DL sample</div>
|
||||||
|
</div>
|
||||||
|
</div >
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="dialog-frame">
|
||||||
|
<div className="dialog-title">Model Slot Configuration</div>
|
||||||
|
<div className="dialog-fixed-size-content">
|
||||||
|
<div className="model-slot-container">
|
||||||
|
{slotRow}
|
||||||
|
</div>
|
||||||
|
{closeButtonRow}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}, [props.screen, serverSetting.serverSetting])
|
||||||
|
|
||||||
|
return screen
|
||||||
|
}
|
123
client/demo/src/components/demo/904-2_SampleDownloader.tsx
Normal file
123
client/demo/src/components/demo/904-2_SampleDownloader.tsx
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
import React, { useMemo, useState } from "react";
|
||||||
|
import { useAppState } from "../../001_provider/001_AppStateProvider";
|
||||||
|
import { InitialFileUploadSetting } from "@dannadori/voice-changer-client-js";
|
||||||
|
import { useMessageBuilder } from "../../hooks/useMessageBuilder";
|
||||||
|
import { ModelSlotManagerDialogScreen } from "./904_ModelSlotManagerDialog";
|
||||||
|
|
||||||
|
export type SampleDownloaderScreenProps = {
|
||||||
|
screen: ModelSlotManagerDialogScreen
|
||||||
|
targetIndex: number
|
||||||
|
close: () => void
|
||||||
|
backToSlotManager: () => void
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SampleDownloaderScreen = (props: SampleDownloaderScreenProps) => {
|
||||||
|
const { serverSetting } = useAppState()
|
||||||
|
const [lang, setLang] = useState<string>("All")
|
||||||
|
const messageBuilderState = useMessageBuilder()
|
||||||
|
useMemo(() => {
|
||||||
|
messageBuilderState.setMessage(__filename, "terms_of_use", { "ja": "利用規約", "en": "terms of use" })
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////
|
||||||
|
// Sample Downloader
|
||||||
|
/////////////////////////////////////////
|
||||||
|
const screen = useMemo(() => {
|
||||||
|
if (props.screen != "SampleDownloader") {
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
|
if (!serverSetting.serverSetting.modelSlots) {
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
|
|
||||||
|
const langs = serverSetting.serverSetting.sampleModels.reduce((prev, cur) => {
|
||||||
|
if (prev.includes(cur.lang) == false) {
|
||||||
|
prev.push(cur.lang)
|
||||||
|
}
|
||||||
|
return prev
|
||||||
|
}, ["All"] as string[])
|
||||||
|
const langOptions = (
|
||||||
|
langs.map(x => {
|
||||||
|
return <option key={x} value={x}>{x}</option>
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const onDownloadSampleClicked = async (id: string) => {
|
||||||
|
serverSetting.fileUploadSettings[props.targetIndex] = {
|
||||||
|
...InitialFileUploadSetting,
|
||||||
|
rvcModel: null,
|
||||||
|
rvcIndex: null,
|
||||||
|
sampleId: id,
|
||||||
|
isSampleMode: true
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await serverSetting.loadModel(props.targetIndex)
|
||||||
|
} catch (e) {
|
||||||
|
alert(e)
|
||||||
|
}
|
||||||
|
props.backToSlotManager()
|
||||||
|
// setMode("localFile")
|
||||||
|
}
|
||||||
|
const options = (
|
||||||
|
serverSetting.serverSetting.sampleModels.filter(x => { return lang == "All" ? true : x.lang == lang }).map((x, index) => {
|
||||||
|
const termOfUseUrlLink = x.termsOfUseUrl && x.termsOfUseUrl.length > 0 ? <a href={x.termsOfUseUrl} target="_blank" rel="noopener noreferrer" className="body-item-text-small">[{messageBuilderState.getMessage(__filename, "terms_of_use")}]</a> : <></>
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div key={index} className="model-slot">
|
||||||
|
<img src={x.icon} className="model-slot-icon"></img>
|
||||||
|
<div className="model-slot-detail">
|
||||||
|
<div className="model-slot-detail-row">
|
||||||
|
<div className="model-slot-detail-row-label">name:</div>
|
||||||
|
<div className="model-slot-detail-row-value">{x.name}</div>
|
||||||
|
<div className="">{termOfUseUrlLink}</div>
|
||||||
|
</div>
|
||||||
|
<div className="model-slot-detail-row">
|
||||||
|
<div className="model-slot-detail-row-label">info: </div>
|
||||||
|
<div className="model-slot-detail-row-value">{x.modelType},{x.f0 ? "f0" : "nof0"},{x.sampleRate}</div>
|
||||||
|
<div className=""></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="model-slot-buttons">
|
||||||
|
<div className="model-slot-button" onClick={() => { onDownloadSampleClicked(x.id) }}>download</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="dialog-frame">
|
||||||
|
<div className="dialog-title">Sample Downloader</div>
|
||||||
|
<div className="dialog-fixed-size-content">
|
||||||
|
<div className="model-slot-header">Select Sample for Slot[{props.targetIndex}] <span onClick={() => {
|
||||||
|
props.backToSlotManager()
|
||||||
|
}} className="model-slot-header-button"><<back</span></div>
|
||||||
|
<div>Lang:
|
||||||
|
<select value={lang} onChange={(e) => { setLang(e.target.value) }}>
|
||||||
|
{langOptions}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="model-slot-container">
|
||||||
|
{options}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
}, [
|
||||||
|
props.screen,
|
||||||
|
props.targetIndex,
|
||||||
|
lang,
|
||||||
|
serverSetting.serverSetting
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return screen;
|
||||||
|
|
||||||
|
};
|
153
client/demo/src/components/demo/904-3_FileUploader.tsx
Normal file
153
client/demo/src/components/demo/904-3_FileUploader.tsx
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
import React, { useEffect, useMemo, useState } from "react";
|
||||||
|
import { useAppState } from "../../001_provider/001_AppStateProvider";
|
||||||
|
import { FileUploadSetting, InitialFileUploadSetting, ModelFileKind, ModelUploadSetting, VoiceChangerType, fileSelector } from "@dannadori/voice-changer-client-js";
|
||||||
|
import { useMessageBuilder } from "../../hooks/useMessageBuilder";
|
||||||
|
import { ModelSlotManagerDialogScreen } from "./904_ModelSlotManagerDialog";
|
||||||
|
import { checkExtention, trimfileName } from "../../utils/utils";
|
||||||
|
|
||||||
|
export type FileUploaderScreenProps = {
|
||||||
|
screen: ModelSlotManagerDialogScreen
|
||||||
|
targetIndex: number
|
||||||
|
close: () => void
|
||||||
|
backToSlotManager: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FileUploaderScreen = (props: FileUploaderScreenProps) => {
|
||||||
|
const { serverSetting } = useAppState()
|
||||||
|
const [voiceChangerType, setVoiceChangerType] = useState<VoiceChangerType>("RVC")
|
||||||
|
const [uploadSetting, setUploadSetting] = useState<ModelUploadSetting>()
|
||||||
|
const messageBuilderState = useMessageBuilder()
|
||||||
|
|
||||||
|
useMemo(() => {
|
||||||
|
messageBuilderState.setMessage(__filename, "select", { "ja": "ファイル選択", "en": "select file" })
|
||||||
|
messageBuilderState.setMessage(__filename, "upload", { "ja": "アップロード", "en": "upload" })
|
||||||
|
messageBuilderState.setMessage(__filename, "alert-model-ext", {
|
||||||
|
"ja": "ファイルの拡張子は次のモノである必要があります。",
|
||||||
|
"en": "extension of file should be the following."
|
||||||
|
})
|
||||||
|
messageBuilderState.setMessage(__filename, "alert-model-file", {
|
||||||
|
"ja": "ファイルが選択されていません",
|
||||||
|
"en": "file is not selected."
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setUploadSetting({
|
||||||
|
voiceChangerType: voiceChangerType,
|
||||||
|
slot: props.targetIndex,
|
||||||
|
isSampleMode: false,
|
||||||
|
sampleId: null,
|
||||||
|
files: [],
|
||||||
|
})
|
||||||
|
}, [props.targetIndex, voiceChangerType])
|
||||||
|
|
||||||
|
const screen = useMemo(() => {
|
||||||
|
if (props.screen != "FileUploader") {
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
|
|
||||||
|
const vcTypeOptions = (
|
||||||
|
Object.values(VoiceChangerType).map(x => {
|
||||||
|
return <option key={x} value={x}>{x}</option>
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const checkModelSetting = (setting: ModelUploadSetting) => {
|
||||||
|
if (setting.voiceChangerType == "RVC") {
|
||||||
|
// const enough = !!setting.files.find(x => { return x.kind == "rvcModel" }) &&
|
||||||
|
// !!setting.files.find(x => { return x.kind == "rvcIndex" })
|
||||||
|
// return enough
|
||||||
|
const enough = !!setting.files.find(x => { return x.kind == "rvcModel" })
|
||||||
|
return enough
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const generateFileRow = (setting: ModelUploadSetting, title: string, kind: ModelFileKind, ext: string[]) => {
|
||||||
|
const selectedFile = setting.files.find(x => { return x.kind == kind })
|
||||||
|
const selectedFilename = selectedFile?.file.name || ""
|
||||||
|
return (
|
||||||
|
<div key={`${title}`} className="file-uploader-file-select-row">
|
||||||
|
<div className="file-uploader-file-select-row-label">{title}:</div>
|
||||||
|
<div className="file-uploader-file-select-row-value" >
|
||||||
|
{trimfileName(selectedFilename, 30)}
|
||||||
|
</div>
|
||||||
|
<div className="file-uploader-file-select-row-button" onClick={async () => {
|
||||||
|
const file = await fileSelector("")
|
||||||
|
if (checkExtention(file.name, ext) == false) {
|
||||||
|
const alertMessage = `${messageBuilderState.getMessage(__filename, "alert-model-ext")} ${ext}`
|
||||||
|
alert(alertMessage)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (selectedFile) {
|
||||||
|
selectedFile.file = file
|
||||||
|
} else {
|
||||||
|
setting.files.push({ kind: kind, file: file })
|
||||||
|
}
|
||||||
|
setUploadSetting({ ...setting })
|
||||||
|
}}>
|
||||||
|
{messageBuilderState.getMessage(__filename, "select")}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const generateFileRowsByVCType = (vcType: VoiceChangerType) => {
|
||||||
|
const rows: JSX.Element[] = []
|
||||||
|
if (vcType == "RVC") {
|
||||||
|
rows.push(generateFileRow(uploadSetting!, "Model", "rvcModel", ["pth", "onnx"]))
|
||||||
|
rows.push(generateFileRow(uploadSetting!, "Index", "rvcIndex", ["index", "bin"]))
|
||||||
|
}
|
||||||
|
return rows
|
||||||
|
}
|
||||||
|
const fileRows = generateFileRowsByVCType(voiceChangerType)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="dialog-frame">
|
||||||
|
<div className="dialog-title">File Uploader</div>
|
||||||
|
<div className="dialog-fixed-size-content">
|
||||||
|
<div className="file-uploader-header">Upload Files for Slot[{props.targetIndex}] <span onClick={() => {
|
||||||
|
props.backToSlotManager()
|
||||||
|
}} className="file-uploader-header-button"><<back</span></div>
|
||||||
|
<div className="file-uploader-voice-changer-select" >VoiceChangerType:
|
||||||
|
<select value={voiceChangerType} onChange={(e) => {
|
||||||
|
setVoiceChangerType(e.target.value as VoiceChangerType)
|
||||||
|
}}>
|
||||||
|
{vcTypeOptions}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="file-uploader-file-select-container">
|
||||||
|
{fileRows}
|
||||||
|
</div>
|
||||||
|
<div className="file-uploader-file-select-upload-button-container">
|
||||||
|
<div className="file-uploader-file-select-upload-button" onClick={() => {
|
||||||
|
if (!uploadSetting) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (checkModelSetting(uploadSetting)) {
|
||||||
|
serverSetting.uploadModel(uploadSetting)
|
||||||
|
} else {
|
||||||
|
const errorMessage = messageBuilderState.getMessage(__filename, "alert-model-file")
|
||||||
|
alert(errorMessage)
|
||||||
|
}
|
||||||
|
}}>
|
||||||
|
{messageBuilderState.getMessage(__filename, "upload")}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
}, [
|
||||||
|
props.screen,
|
||||||
|
props.targetIndex,
|
||||||
|
voiceChangerType,
|
||||||
|
uploadSetting,
|
||||||
|
serverSetting.uploadModel
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return screen;
|
||||||
|
|
||||||
|
};
|
@ -1,9 +1,8 @@
|
|||||||
import React, { useMemo, useState } from "react";
|
import React, { useMemo, useState } from "react";
|
||||||
import { useGuiState } from "./001_GuiStateProvider";
|
import { useGuiState } from "./001_GuiStateProvider";
|
||||||
import { useAppState } from "../../001_provider/001_AppStateProvider";
|
import { MainScreen } from "./904-1_MainScreen";
|
||||||
import { InitialFileUploadSetting, fileSelector } from "@dannadori/voice-changer-client-js";
|
import { SampleDownloaderScreen } from "./904-2_SampleDownloader";
|
||||||
import { useMessageBuilder } from "../../hooks/useMessageBuilder";
|
import { FileUploaderScreen } from "./904-3_FileUploader";
|
||||||
|
|
||||||
|
|
||||||
export type uploadData = {
|
export type uploadData = {
|
||||||
slot: number
|
slot: number
|
||||||
@ -11,365 +10,60 @@ export type uploadData = {
|
|||||||
index: File | null
|
index: File | null
|
||||||
}
|
}
|
||||||
|
|
||||||
const Mode = {
|
export const ModelSlotSettingMode = {
|
||||||
"localFile": "localFile",
|
"localFile": "localFile",
|
||||||
"fromNet": "fromNet"
|
"fromNet": "fromNet"
|
||||||
} as const
|
} as const
|
||||||
type Mode = typeof Mode[keyof typeof Mode]
|
export type ModelSlotSettingMode = typeof ModelSlotSettingMode[keyof typeof ModelSlotSettingMode]
|
||||||
|
|
||||||
|
export const ModelSlotManagerDialogScreen = {
|
||||||
|
"Main": "Main",
|
||||||
|
"SampleDownloader": "SampleDownloader",
|
||||||
|
"FileUploader": "FileUploader"
|
||||||
|
} as const
|
||||||
|
export type ModelSlotManagerDialogScreen = typeof ModelSlotManagerDialogScreen[keyof typeof ModelSlotManagerDialogScreen]
|
||||||
|
|
||||||
export const ModelSlotManagerDialog = () => {
|
export const ModelSlotManagerDialog = () => {
|
||||||
const guiState = useGuiState()
|
const guiState = useGuiState()
|
||||||
const { serverSetting } = useAppState()
|
const [screen, setScreen] = useState<ModelSlotManagerDialogScreen>("Main")
|
||||||
const [uploadData, setUploadData] = useState<uploadData | null>(null)
|
const [targetIndex, setTargetIndex] = useState<number>(0)
|
||||||
const [mode, setMode] = useState<Mode>("localFile")
|
|
||||||
const [fromNetTargetIndex, setFromNetTargetIndex] = useState<number>(0)
|
|
||||||
const [lang, setLang] = useState<string>("All")
|
|
||||||
const messageBuilderState = useMessageBuilder()
|
|
||||||
|
|
||||||
useMemo(() => {
|
|
||||||
messageBuilderState.setMessage(__filename, "change_icon", { "ja": "アイコン変更", "en": "chage icon" })
|
|
||||||
messageBuilderState.setMessage(__filename, "rename", { "ja": "リネーム", "en": "rename" })
|
|
||||||
messageBuilderState.setMessage(__filename, "download", { "ja": "ダウンロード", "en": "download" })
|
|
||||||
messageBuilderState.setMessage(__filename, "terms_of_use", { "ja": "利用規約", "en": "terms of use" })
|
|
||||||
}, [])
|
|
||||||
/////////////////////////////////////////
|
|
||||||
// Slot Manager
|
|
||||||
/////////////////////////////////////////
|
|
||||||
const localFileContent = useMemo(() => {
|
|
||||||
if (mode != "localFile") {
|
|
||||||
return <></>
|
|
||||||
}
|
|
||||||
if (!serverSetting.serverSetting.modelSlots) {
|
|
||||||
return <></>
|
|
||||||
}
|
|
||||||
|
|
||||||
const checkExtention = (filename: string, acceptExtentions: string[]) => {
|
|
||||||
const ext = filename.split('.').pop();
|
|
||||||
if (!ext) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return acceptExtentions.includes(ext)
|
|
||||||
}
|
|
||||||
|
|
||||||
const onRVCModelLoadClicked = async (slot: number) => {
|
|
||||||
const file = await fileSelector("")
|
|
||||||
if (checkExtention(file.name, ["pth", "onnx"]) == false) {
|
|
||||||
alert(`モデルファイルの拡張子は".pth", ".onnx"である必要があります。`)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (uploadData?.slot == slot) {
|
|
||||||
setUploadData({ ...uploadData, model: file })
|
|
||||||
} else {
|
|
||||||
const newUploadData = {
|
|
||||||
slot: slot,
|
|
||||||
model: file,
|
|
||||||
index: null
|
|
||||||
}
|
|
||||||
setUploadData(newUploadData)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const onRVCIndexLoadClicked = async (slot: number) => {
|
|
||||||
const file = await fileSelector("")
|
|
||||||
if (checkExtention(file.name, ["index", "bin"]) == false) {
|
|
||||||
alert(`モデルファイルの拡張子は".pth"である必要があります。`)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (uploadData?.slot == slot) {
|
|
||||||
setUploadData({ ...uploadData, index: file })
|
|
||||||
} else {
|
|
||||||
const newUploadData = {
|
|
||||||
slot: slot,
|
|
||||||
model: null,
|
|
||||||
index: file
|
|
||||||
}
|
|
||||||
setUploadData(newUploadData)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const onUploadClicked = async () => {
|
|
||||||
if (!uploadData) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (!uploadData.model) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
serverSetting.fileUploadSettings[uploadData.slot] = {
|
|
||||||
...InitialFileUploadSetting,
|
|
||||||
rvcModel: { file: uploadData.model },
|
|
||||||
rvcIndex: uploadData.index ? { file: uploadData.index } : null,
|
|
||||||
sampleId: null,
|
|
||||||
isSampleMode: false
|
|
||||||
}
|
|
||||||
serverSetting.setFileUploadSetting(uploadData.slot, {
|
|
||||||
...serverSetting.fileUploadSettings[uploadData.slot]
|
|
||||||
})
|
|
||||||
await serverSetting.loadModel(uploadData.slot)
|
|
||||||
setUploadData(null)
|
|
||||||
}
|
|
||||||
const onClearClicked = () => {
|
|
||||||
setUploadData(null)
|
|
||||||
}
|
|
||||||
const onOpenSampleDownloadDialog = (index: number) => {
|
|
||||||
setMode("fromNet")
|
|
||||||
setFromNetTargetIndex(index)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const slots = serverSetting.serverSetting.modelSlots.map((x, index) => {
|
|
||||||
let modelFileName = ""
|
|
||||||
if (uploadData?.slot == index) {
|
|
||||||
modelFileName = (uploadData.model?.name || "").replace(/^.*[\\\/]/, '')
|
|
||||||
} else if (x.modelFile && x.modelFile.length > 0) {
|
|
||||||
modelFileName = x.modelFile.replace(/^.*[\\\/]/, '')
|
|
||||||
}
|
|
||||||
if (modelFileName.length > 20) {
|
|
||||||
modelFileName = modelFileName.substring(0, 20) + "..."
|
|
||||||
}
|
|
||||||
|
|
||||||
let indexFileName = ""
|
|
||||||
if (uploadData?.slot == index) {
|
|
||||||
indexFileName = (uploadData.index?.name || "").replace(/^.*[\\\/]/, '')
|
|
||||||
} else if (x.indexFile && x.indexFile.length > 0) {
|
|
||||||
indexFileName = x.indexFile.replace(/^.*[\\\/]/, '')
|
|
||||||
}
|
|
||||||
if (indexFileName.length > 20) {
|
|
||||||
indexFileName = indexFileName.substring(0, 20) + "..."
|
|
||||||
}
|
|
||||||
|
|
||||||
const isRegisterd = modelFileName.length > 0 ? true : false
|
|
||||||
const name = x.name && x.name.length > 0 ? x.name : isRegisterd ? modelFileName : "blank"
|
|
||||||
const termOfUseUrlLink = x.termsOfUseUrl && x.termsOfUseUrl.length > 0 ? <a href={x.termsOfUseUrl} target="_blank" rel="noopener noreferrer" className="body-item-text-small">[{messageBuilderState.getMessage(__filename, "terms_of_use")}]</a> : <></>
|
|
||||||
|
|
||||||
const nameValueClass = isRegisterd ? "model-slot-detail-row-value-pointable" : "model-slot-detail-row-value"
|
|
||||||
const nameValueAction = isRegisterd ? async (index: number) => {
|
|
||||||
const p = new Promise<string>((resolve) => {
|
|
||||||
guiState.setTextInputResolve({ resolve: resolve })
|
|
||||||
})
|
|
||||||
|
|
||||||
guiState.stateControls.showTextInputCheckbox.updateState(true)
|
|
||||||
const text = await p;
|
|
||||||
if (text.length > 0) {
|
|
||||||
console.log("input text:", text)
|
|
||||||
await serverSetting.updateModelInfo(index, "name", text)
|
|
||||||
}
|
|
||||||
} : async (_index: number) => { }
|
|
||||||
const iconClass = isRegisterd ? "model-slot-icon-pointable" : "model-slot-icon"
|
|
||||||
const iconAction = isRegisterd ? async (index: number) => {
|
|
||||||
const file = await fileSelector("")
|
|
||||||
if (checkExtention(file.name, ["png", "jpg", "jpeg", "gif"]) == false) {
|
|
||||||
alert(`モデルファイルの拡張子は".png", ".jpg", ".jpeg", ".gif"である必要があります。`)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
await serverSetting.uploadAssets(index, "iconFile", file)
|
|
||||||
} : async (_index: number) => { }
|
|
||||||
|
|
||||||
const fileValueClass = (uploadData?.slot == index) ? "model-slot-detail-row-value-edit" : isRegisterd ? "model-slot-detail-row-value-download" : "model-slot-detail-row-value"
|
|
||||||
const fileValueAction = (uploadData?.slot == index) ? (_url: string) => {
|
|
||||||
} : isRegisterd ? (url: string) => {
|
|
||||||
console.log("Download url:::", url)
|
|
||||||
const link = document.createElement("a")
|
|
||||||
link.href = "/" + url
|
|
||||||
link.download = url.replace(/^.*[\\\/]/, '')
|
|
||||||
link.click()
|
|
||||||
} : (_url: string) => { }
|
|
||||||
|
|
||||||
const iconUrl = x.modelFile && x.modelFile.length > 0 ? (x.iconFile && x.iconFile.length > 0 ? x.iconFile : "/assets/icons/noimage.png") : "/assets/icons/blank.png"
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div key={index} className="model-slot">
|
|
||||||
<div className="tooltip">
|
|
||||||
<img src={iconUrl} className={iconClass} onClick={() => { iconAction(index) }} />
|
|
||||||
<div className="tooltip-text tooltip-text-thin tooltip-text-lower">
|
|
||||||
{messageBuilderState.getMessage(__filename, "change_icon")}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="model-slot-detail">
|
|
||||||
<div className="model-slot-detail-row">
|
|
||||||
<div className="model-slot-detail-row-label">[{index}]</div>
|
|
||||||
<div className={nameValueClass + " tooltip"} onClick={() => { nameValueAction(index) }}>
|
|
||||||
{name}
|
|
||||||
<div className="tooltip-text tooltip-text-thin">
|
|
||||||
{messageBuilderState.getMessage(__filename, "rename")}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="">{termOfUseUrlLink}</div>
|
|
||||||
</div>
|
|
||||||
<div className="model-slot-detail-row">
|
|
||||||
<div className="model-slot-detail-row-label">model:</div>
|
|
||||||
<div className={fileValueClass + " tooltip"} onClick={() => { fileValueAction(x.modelFile) }}>
|
|
||||||
{modelFileName}
|
|
||||||
<div className="tooltip-text tooltip-text-thin">
|
|
||||||
{messageBuilderState.getMessage(__filename, "download")}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="model-slot-button model-slot-detail-row-button" onClick={() => { onRVCModelLoadClicked(index) }}>select</div>
|
|
||||||
</div>
|
|
||||||
<div className="model-slot-detail-row">
|
|
||||||
<div className="model-slot-detail-row-label">index:</div>
|
|
||||||
<div className={fileValueClass + " tooltip"} onClick={() => { fileValueAction(x.indexFile) }}>
|
|
||||||
{indexFileName}
|
|
||||||
<div className="tooltip-text tooltip-text-thin">
|
|
||||||
{messageBuilderState.getMessage(__filename, "download")}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="model-slot-button model-slot-detail-row-button" onClick={() => { onRVCIndexLoadClicked(index) }}>select</div>
|
|
||||||
</div>
|
|
||||||
<div className="model-slot-detail-row">
|
|
||||||
<div className="model-slot-detail-row-label">info: </div>
|
|
||||||
<div className="model-slot-detail-row-value">{x.f0 ? "f0" : "nof0"}, {x.samplingRate}, {x.embChannels}, {x.modelType}, {x.defaultTune}, {x.defaultIndexRatio}, {x.defaultProtect}</div>
|
|
||||||
<div className=""></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="model-slot-buttons">
|
|
||||||
{(uploadData?.slot == index) && (uploadData.model != null) ?
|
|
||||||
<div></div> :
|
|
||||||
<div className="model-slot-button" onClick={() => { onOpenSampleDownloadDialog(index) }}>DL sample>> </div>
|
|
||||||
}
|
|
||||||
|
|
||||||
{(uploadData?.slot == index) && (uploadData.model != null) ?
|
|
||||||
<div className="model-slot-button" onClick={onUploadClicked}>upload</div> : <div></div>
|
|
||||||
}
|
|
||||||
{(uploadData?.slot == index) && (uploadData.model != null || uploadData.index != null) ?
|
|
||||||
<div className="model-slot-button" onClick={onClearClicked}>clear</div> : <div></div>
|
|
||||||
}
|
|
||||||
{(uploadData?.slot == index) && (uploadData.model != null) ?
|
|
||||||
<div>{serverSetting.uploadProgress.toFixed(1)}%</div> : <div></div>
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div >
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="model-slot-container">
|
|
||||||
{slots}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
}, [
|
|
||||||
mode,
|
|
||||||
serverSetting.serverSetting.modelSlots,
|
|
||||||
serverSetting.fileUploadSettings,
|
|
||||||
serverSetting.uploadProgress,
|
|
||||||
serverSetting.setFileUploadSetting,
|
|
||||||
serverSetting.loadModel,
|
|
||||||
uploadData
|
|
||||||
])
|
|
||||||
|
|
||||||
|
|
||||||
/////////////////////////////////////////
|
|
||||||
// Sample Downloader
|
|
||||||
/////////////////////////////////////////
|
|
||||||
const fromNetContent = useMemo(() => {
|
|
||||||
if (mode != "fromNet") {
|
|
||||||
return <></>
|
|
||||||
}
|
|
||||||
|
|
||||||
const langs = serverSetting.serverSetting.sampleModels.reduce((prev, cur) => {
|
|
||||||
if (prev.includes(cur.lang) == false) {
|
|
||||||
prev.push(cur.lang)
|
|
||||||
}
|
|
||||||
return prev
|
|
||||||
}, ["All"] as string[])
|
|
||||||
const langOptions = (
|
|
||||||
langs.map(x => {
|
|
||||||
return <option key={x} value={x}>{x}</option>
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
const onDownloadSampleClicked = async (id: string) => {
|
|
||||||
serverSetting.fileUploadSettings[fromNetTargetIndex] = {
|
|
||||||
...InitialFileUploadSetting,
|
|
||||||
rvcModel: null,
|
|
||||||
rvcIndex: null,
|
|
||||||
sampleId: id,
|
|
||||||
isSampleMode: true
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
await serverSetting.loadModel(fromNetTargetIndex)
|
|
||||||
} catch (e) {
|
|
||||||
alert(e)
|
|
||||||
}
|
|
||||||
setMode("localFile")
|
|
||||||
}
|
|
||||||
const options = (
|
|
||||||
serverSetting.serverSetting.sampleModels.filter(x => { return lang == "All" ? true : x.lang == lang }).map((x, index) => {
|
|
||||||
const termOfUseUrlLink = x.termsOfUseUrl && x.termsOfUseUrl.length > 0 ? <a href={x.termsOfUseUrl} target="_blank" rel="noopener noreferrer" className="body-item-text-small">[{messageBuilderState.getMessage(__filename, "terms_of_use")}]</a> : <></>
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div key={index} className="model-slot">
|
|
||||||
<img src={x.icon} className="model-slot-icon"></img>
|
|
||||||
<div className="model-slot-detail">
|
|
||||||
<div className="model-slot-detail-row">
|
|
||||||
<div className="model-slot-detail-row-label">name:</div>
|
|
||||||
<div className="model-slot-detail-row-value">{x.name}</div>
|
|
||||||
<div className="">{termOfUseUrlLink}</div>
|
|
||||||
</div>
|
|
||||||
<div className="model-slot-detail-row">
|
|
||||||
<div className="model-slot-detail-row-label">info: </div>
|
|
||||||
<div className="model-slot-detail-row-value">{x.modelType},{x.f0 ? "f0" : "nof0"},{x.sampleRate}</div>
|
|
||||||
<div className=""></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="model-slot-buttons">
|
|
||||||
<div className="model-slot-button" onClick={() => { onDownloadSampleClicked(x.id) }}>download</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="">
|
|
||||||
<div className="model-slot-header">Select Sample for Slot[{fromNetTargetIndex}] <span onClick={() => { setMode("localFile") }} className="model-slot-header-button"><<back</span></div>
|
|
||||||
<div>Lang:
|
|
||||||
<select value={lang} onChange={(e) => { setLang(e.target.value) }}>
|
|
||||||
{langOptions}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="model-slot-container">
|
|
||||||
{options}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
|
|
||||||
}, [
|
|
||||||
mode,
|
|
||||||
fromNetTargetIndex,
|
|
||||||
lang
|
|
||||||
])
|
|
||||||
|
|
||||||
|
|
||||||
const dialog = useMemo(() => {
|
const dialog = useMemo(() => {
|
||||||
const closeButtonRow = (
|
const close = () => { guiState.stateControls.showModelSlotManagerCheckbox.updateState(false) }
|
||||||
<div className="body-row split-3-4-3 left-padding-1">
|
const openSampleDownloader = (index: number) => { setTargetIndex(index); setScreen("SampleDownloader") }
|
||||||
<div className="body-item-text">
|
const openFileUploader = (index: number) => { setTargetIndex(index); setScreen("FileUploader") }
|
||||||
</div>
|
const backToSlotManager = () => { setScreen("Main") }
|
||||||
<div className="body-button-container body-button-container-space-around">
|
const mainScreen = (
|
||||||
<div className="body-button" onClick={() => { guiState.stateControls.showModelSlotManagerCheckbox.updateState(false) }} >close</div>
|
<MainScreen
|
||||||
</div>
|
screen={screen}
|
||||||
<div className="body-item-text"></div>
|
close={close}
|
||||||
</div>
|
openSampleDownloader={openSampleDownloader}
|
||||||
|
openFileUploader={openFileUploader} />
|
||||||
|
)
|
||||||
|
const sampleDownloaderScreen = (
|
||||||
|
<SampleDownloaderScreen
|
||||||
|
screen={screen}
|
||||||
|
targetIndex={targetIndex}
|
||||||
|
close={close}
|
||||||
|
backToSlotManager={backToSlotManager} />
|
||||||
|
)
|
||||||
|
const fileUploaderScreen = (
|
||||||
|
<FileUploaderScreen
|
||||||
|
screen={screen}
|
||||||
|
targetIndex={targetIndex}
|
||||||
|
close={close}
|
||||||
|
backToSlotManager={backToSlotManager} />
|
||||||
)
|
)
|
||||||
return (
|
return (
|
||||||
<div className="dialog-frame">
|
<div className="dialog-frame">
|
||||||
<div className="dialog-title">{mode == "localFile" ? "Model Slot Configuration" : "Sample Downloader"}</div>
|
{mainScreen}
|
||||||
<div className="dialog-fixed-size-content">
|
{sampleDownloaderScreen}
|
||||||
{localFileContent}
|
{fileUploaderScreen}
|
||||||
{fromNetContent}
|
|
||||||
{closeButtonRow}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
|
}, [screen, targetIndex])
|
||||||
|
|
||||||
}, [
|
|
||||||
localFileContent,
|
|
||||||
fromNetContent,
|
|
||||||
fromNetTargetIndex,
|
|
||||||
]);
|
|
||||||
|
|
||||||
return dialog;
|
return dialog;
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React, { useEffect, useMemo, useState } from "react";
|
import React, { useEffect, useMemo, useState } from "react";
|
||||||
import { useGuiState } from "./001_GuiStateProvider";
|
import { useGuiState } from "./001_GuiStateProvider";
|
||||||
import { useAppState } from "../../001_provider/001_AppStateProvider";
|
import { useAppState } from "../../001_provider/001_AppStateProvider";
|
||||||
import { MergeElement, ModelType } from "@dannadori/voice-changer-client-js";
|
import { MergeElement, RVCModelType } from "@dannadori/voice-changer-client-js";
|
||||||
|
|
||||||
|
|
||||||
export const MergeLabDialog = () => {
|
export const MergeLabDialog = () => {
|
||||||
@ -36,7 +36,7 @@ export const MergeLabDialog = () => {
|
|||||||
prev[key] = val
|
prev[key] = val
|
||||||
}
|
}
|
||||||
return prev
|
return prev
|
||||||
}, {} as { [key: string]: { type: ModelType, samplingRate: number, embChannels: number } })
|
}, {} as { [key: string]: { type: RVCModelType, samplingRate: number, embChannels: number } })
|
||||||
|
|
||||||
}, [newSlotChangeKey])
|
}, [newSlotChangeKey])
|
||||||
|
|
||||||
|
@ -732,9 +732,11 @@ body {
|
|||||||
.dialog-fixed-size-content {
|
.dialog-fixed-size-content {
|
||||||
width: 90%;
|
width: 90%;
|
||||||
max-height: 70vh;
|
max-height: 70vh;
|
||||||
.model-slot-header {
|
.model-slot-header,
|
||||||
|
.file-uploader-header {
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
.model-slot-header-button {
|
.model-slot-header-button,
|
||||||
|
.file-uploader-header-button {
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
@ -849,6 +851,59 @@ body {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.file-uploader-voice-changer-select {
|
||||||
|
font-size: 1rem;
|
||||||
|
padding-left: 0.5rem;
|
||||||
|
}
|
||||||
|
.file-uploader-file-select-container {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
padding-left: 1rem;
|
||||||
|
.file-uploader-file-select-row {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
margin: 0.2rem;
|
||||||
|
.file-uploader-file-select-row-label {
|
||||||
|
width: 5rem;
|
||||||
|
}
|
||||||
|
.file-uploader-file-select-row-value {
|
||||||
|
width: 20rem;
|
||||||
|
color: #f00;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.file-uploader-file-select-row-button {
|
||||||
|
user-select: none;
|
||||||
|
border: solid 1px #999;
|
||||||
|
border-radius: 2px;
|
||||||
|
cursor: pointer;
|
||||||
|
vertical-align: middle;
|
||||||
|
text-align: center;
|
||||||
|
padding: 1px;
|
||||||
|
&:hover {
|
||||||
|
border: solid 1px #000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.file-uploader-file-select-upload-button-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
margin-top: 1rem;
|
||||||
|
justify-content: center;
|
||||||
|
.file-uploader-file-select-upload-button {
|
||||||
|
width: 8rem;
|
||||||
|
user-select: none;
|
||||||
|
border: solid 1px #999;
|
||||||
|
border-radius: 2px;
|
||||||
|
cursor: pointer;
|
||||||
|
vertical-align: middle;
|
||||||
|
text-align: center;
|
||||||
|
padding: 1px;
|
||||||
|
&:hover {
|
||||||
|
border: solid 1px #000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
16
client/demo/src/utils/utils.ts
Normal file
16
client/demo/src/utils/utils.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
export const trimfileName = (name: string, length: number) => {
|
||||||
|
const trimmedName = name.replace(/^.*[\\\/]/, '')
|
||||||
|
if (trimmedName.length > length) {
|
||||||
|
return trimmedName.substring(0, length) + "..."
|
||||||
|
} else {
|
||||||
|
return trimmedName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const checkExtention = (filename: string, acceptExtentions: string[]) => {
|
||||||
|
const ext = filename.split('.').pop();
|
||||||
|
if (!ext) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return acceptExtentions.includes(ext)
|
||||||
|
}
|
@ -16,6 +16,19 @@ export const ClientType = {
|
|||||||
} as const
|
} as const
|
||||||
export type ClientType = typeof ClientType[keyof typeof ClientType]
|
export type ClientType = typeof ClientType[keyof typeof ClientType]
|
||||||
|
|
||||||
|
|
||||||
|
export const VoiceChangerType = {
|
||||||
|
"MMVCv15": "MMVCv15",
|
||||||
|
"MMVCv13": "MMVCv13",
|
||||||
|
"so-vits-svc-40": "so-vits-svc-40",
|
||||||
|
"so-vits-svc-40_c": "so-vits-svc-40_c",
|
||||||
|
"so-vits-svc-40v2": "so-vits-svc-40v2",
|
||||||
|
"DDSP-SVC": "DDSP-SVC",
|
||||||
|
"RVC": "RVC"
|
||||||
|
|
||||||
|
} as const
|
||||||
|
export type VoiceChangerType = typeof VoiceChangerType[keyof typeof VoiceChangerType]
|
||||||
|
|
||||||
///////////////////////
|
///////////////////////
|
||||||
// サーバセッティング
|
// サーバセッティング
|
||||||
///////////////////////
|
///////////////////////
|
||||||
@ -70,7 +83,7 @@ export const DiffMethod = {
|
|||||||
} as const
|
} as const
|
||||||
export type DiffMethod = typeof DiffMethod[keyof typeof DiffMethod]
|
export type DiffMethod = typeof DiffMethod[keyof typeof DiffMethod]
|
||||||
|
|
||||||
export const ModelType = {
|
export const RVCModelType = {
|
||||||
"pyTorchRVC": "pyTorchRVC",
|
"pyTorchRVC": "pyTorchRVC",
|
||||||
"pyTorchRVCNono": "pyTorchRVCNono",
|
"pyTorchRVCNono": "pyTorchRVCNono",
|
||||||
"pyTorchRVCv2": "pyTorchRVCv2",
|
"pyTorchRVCv2": "pyTorchRVCv2",
|
||||||
@ -80,7 +93,7 @@ export const ModelType = {
|
|||||||
"onnxRVC": "onnxRVC",
|
"onnxRVC": "onnxRVC",
|
||||||
"onnxRVCNono": "onnxRVCNono",
|
"onnxRVCNono": "onnxRVCNono",
|
||||||
} as const
|
} as const
|
||||||
export type ModelType = typeof ModelType[keyof typeof ModelType]
|
export type RVCModelType = typeof RVCModelType[keyof typeof RVCModelType]
|
||||||
|
|
||||||
export const ServerSettingKey = {
|
export const ServerSettingKey = {
|
||||||
"srcId": "srcId",
|
"srcId": "srcId",
|
||||||
@ -198,21 +211,7 @@ export type VoiceChangerServerSetting = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ModelSlot = {
|
type ModelSlot = {
|
||||||
modelFile: string
|
voiceChangerType: VoiceChangerType
|
||||||
featureFile: string,
|
|
||||||
indexFile: string,
|
|
||||||
|
|
||||||
defaultTune: number,
|
|
||||||
defaultIndexRatio: number,
|
|
||||||
defaultProtect: number,
|
|
||||||
|
|
||||||
modelType: ModelType,
|
|
||||||
embChannels: number,
|
|
||||||
f0: boolean,
|
|
||||||
samplingRate: number
|
|
||||||
deprecated: boolean
|
|
||||||
|
|
||||||
|
|
||||||
name: string,
|
name: string,
|
||||||
description: string,
|
description: string,
|
||||||
credit: string,
|
credit: string,
|
||||||
@ -220,6 +219,22 @@ type ModelSlot = {
|
|||||||
iconFile: string
|
iconFile: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type RVCModelSlot = ModelSlot & {
|
||||||
|
modelFile: string
|
||||||
|
indexFile: string,
|
||||||
|
defaultIndexRatio: number,
|
||||||
|
defaultProtect: number,
|
||||||
|
defaultTune: number,
|
||||||
|
modelType: RVCModelType,
|
||||||
|
|
||||||
|
embChannels: number,
|
||||||
|
f0: boolean,
|
||||||
|
samplingRate: number
|
||||||
|
deprecated: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ModelSlotUnion = RVCModelSlot
|
||||||
|
|
||||||
type ServerAudioDevice = {
|
type ServerAudioDevice = {
|
||||||
kind: "audioinput" | "audiooutput",
|
kind: "audioinput" | "audiooutput",
|
||||||
index: number,
|
index: number,
|
||||||
@ -233,7 +248,7 @@ export type ServerInfo = VoiceChangerServerSetting & {
|
|||||||
pyTorchModelFile: string,
|
pyTorchModelFile: string,
|
||||||
onnxModelFile: string,
|
onnxModelFile: string,
|
||||||
onnxExecutionProviders: OnnxExecutionProvider[]
|
onnxExecutionProviders: OnnxExecutionProvider[]
|
||||||
modelSlots: ModelSlot[]
|
modelSlots: ModelSlotUnion[]
|
||||||
serverAudioInputDevices: ServerAudioDevice[]
|
serverAudioInputDevices: ServerAudioDevice[]
|
||||||
serverAudioOutputDevices: ServerAudioDevice[]
|
serverAudioOutputDevices: ServerAudioDevice[]
|
||||||
sampleModels: RVCSampleModel[]
|
sampleModels: RVCSampleModel[]
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useState, useMemo, useEffect } from "react"
|
import { useState, useMemo, useEffect } from "react"
|
||||||
import { VoiceChangerServerSetting, ServerInfo, ServerSettingKey, INDEXEDDB_KEY_SERVER, INDEXEDDB_KEY_MODEL_DATA, ClientType, DefaultServerSetting_MMVCv13, DefaultServerSetting_MMVCv15, DefaultServerSetting_so_vits_svc_40v2, DefaultServerSetting_so_vits_svc_40, DefaultServerSetting_so_vits_svc_40_c, DefaultServerSetting_RVC, OnnxExporterInfo, DefaultServerSetting_DDSP_SVC, MAX_MODEL_SLOT_NUM, Framework, MergeModelRequest } from "../const"
|
import { VoiceChangerServerSetting, ServerInfo, ServerSettingKey, INDEXEDDB_KEY_SERVER, INDEXEDDB_KEY_MODEL_DATA, ClientType, DefaultServerSetting_MMVCv13, DefaultServerSetting_MMVCv15, DefaultServerSetting_so_vits_svc_40v2, DefaultServerSetting_so_vits_svc_40, DefaultServerSetting_so_vits_svc_40_c, DefaultServerSetting_RVC, OnnxExporterInfo, DefaultServerSetting_DDSP_SVC, MAX_MODEL_SLOT_NUM, Framework, MergeModelRequest, VoiceChangerType } from "../const"
|
||||||
import { VoiceChangerClient } from "../VoiceChangerClient"
|
import { VoiceChangerClient } from "../VoiceChangerClient"
|
||||||
import { useIndexedDB } from "./useIndexedDB"
|
import { useIndexedDB } from "./useIndexedDB"
|
||||||
import { ModelLoadException } from "../exceptions"
|
import { ModelLoadException } from "../exceptions"
|
||||||
@ -17,6 +17,48 @@ export const ModelAssetName = {
|
|||||||
export type ModelAssetName = typeof ModelAssetName[keyof typeof ModelAssetName]
|
export type ModelAssetName = typeof ModelAssetName[keyof typeof ModelAssetName]
|
||||||
|
|
||||||
|
|
||||||
|
export const ModelFileKind = {
|
||||||
|
"mmvcv13Config": "mmvcv13Config",
|
||||||
|
"mmvcv13Model": "mmvcv13Model",
|
||||||
|
"mmvcv15Config": "mmvcv15Config",
|
||||||
|
"mmvcv15Model": "mmvcv15Model",
|
||||||
|
|
||||||
|
"soVitsSvc40Config": "soVitsSvc40Config",
|
||||||
|
"soVitsSvc40Model": "soVitsSvc40Model",
|
||||||
|
"soVitsSvc40Cluster": "soVitsSvc40Cluster",
|
||||||
|
|
||||||
|
"rvcModel": "rvcModel",
|
||||||
|
"rvcIndex": "rvcIndex",
|
||||||
|
|
||||||
|
"ddspSvcModel": "ddspSvcModel",
|
||||||
|
"ddspSvcModelConfig": "ddspSvcModelConfig",
|
||||||
|
"ddspSvcDiffusion": "ddspSvcDiffusion",
|
||||||
|
"ddspSvcDiffusionConfig": "ddspSvcDiffusionConfig",
|
||||||
|
|
||||||
|
} as const
|
||||||
|
export type ModelFileKind = typeof ModelFileKind[keyof typeof ModelFileKind]
|
||||||
|
|
||||||
|
export type ModelFile = {
|
||||||
|
file: File,
|
||||||
|
kind: ModelFileKind
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ModelUploadSetting = {
|
||||||
|
voiceChangerType: VoiceChangerType,
|
||||||
|
slot: number
|
||||||
|
isSampleMode: boolean
|
||||||
|
sampleId: string | null
|
||||||
|
|
||||||
|
files: ModelFile[]
|
||||||
|
}
|
||||||
|
export type ModelFileForServer = Omit<ModelFile, "file"> & {
|
||||||
|
name: string,
|
||||||
|
kind: ModelFileKind
|
||||||
|
}
|
||||||
|
export type ModelUploadSettingForServer = Omit<ModelUploadSetting, "files"> & {
|
||||||
|
files: ModelFileForServer[]
|
||||||
|
}
|
||||||
|
|
||||||
export type FileUploadSetting = {
|
export type FileUploadSetting = {
|
||||||
isHalf: boolean
|
isHalf: boolean
|
||||||
uploaded: boolean
|
uploaded: boolean
|
||||||
@ -105,6 +147,7 @@ export type ServerSettingState = {
|
|||||||
fileUploadSettings: FileUploadSetting[]
|
fileUploadSettings: FileUploadSetting[]
|
||||||
setFileUploadSetting: (slot: number, val: FileUploadSetting) => void
|
setFileUploadSetting: (slot: number, val: FileUploadSetting) => void
|
||||||
loadModel: (slot: number) => Promise<void>
|
loadModel: (slot: number) => Promise<void>
|
||||||
|
uploadModel: (setting: ModelUploadSetting) => Promise<void>
|
||||||
uploadProgress: number
|
uploadProgress: number
|
||||||
isUploading: boolean
|
isUploading: boolean
|
||||||
|
|
||||||
@ -259,7 +302,46 @@ export const useServerSetting = (props: UseServerSettingProps): ServerSettingSta
|
|||||||
}
|
}
|
||||||
}, [props.voiceChangerClient])
|
}, [props.voiceChangerClient])
|
||||||
|
|
||||||
|
// 新しいアップローダ
|
||||||
|
const uploadModel = useMemo(() => {
|
||||||
|
return async (setting: ModelUploadSetting) => {
|
||||||
|
if (!props.voiceChangerClient) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
setUploadProgress(0)
|
||||||
|
setIsUploading(true)
|
||||||
|
|
||||||
|
|
||||||
|
if (setting.isSampleMode == false) {
|
||||||
|
const progRate = 1 / setting.files.length
|
||||||
|
for (let i = 0; i < setting.files.length; i++) {
|
||||||
|
const progOffset = 100 * i * progRate
|
||||||
|
await _uploadFile2(setting.files[i].file, (progress: number, _end: boolean) => {
|
||||||
|
setUploadProgress(progress * progRate + progOffset)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const params: ModelUploadSettingForServer = {
|
||||||
|
...setting, files: setting.files.map((f) => { return { name: f.file.name, kind: f.kind } })
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadPromise = props.voiceChangerClient.loadModel(
|
||||||
|
0,
|
||||||
|
false,
|
||||||
|
JSON.stringify(params),
|
||||||
|
)
|
||||||
|
await loadPromise
|
||||||
|
|
||||||
|
setUploadProgress(0)
|
||||||
|
setIsUploading(false)
|
||||||
|
reloadServerInfo()
|
||||||
|
|
||||||
|
}
|
||||||
|
}, [props.voiceChangerClient])
|
||||||
|
|
||||||
|
|
||||||
|
// 古いアップローダ(新GUIへ以降まで、当分残しておく。)
|
||||||
const loadModel = useMemo(() => {
|
const loadModel = useMemo(() => {
|
||||||
return async (slot: number) => {
|
return async (slot: number) => {
|
||||||
const fileUploadSetting = fileUploadSettings[slot]
|
const fileUploadSetting = fileUploadSettings[slot]
|
||||||
@ -571,6 +653,7 @@ export const useServerSetting = (props: UseServerSettingProps): ServerSettingSta
|
|||||||
fileUploadSettings,
|
fileUploadSettings,
|
||||||
setFileUploadSetting,
|
setFileUploadSetting,
|
||||||
loadModel,
|
loadModel,
|
||||||
|
uploadModel,
|
||||||
uploadProgress,
|
uploadProgress,
|
||||||
isUploading,
|
isUploading,
|
||||||
getOnnx,
|
getOnnx,
|
||||||
|
@ -88,20 +88,24 @@ class MMVC_Rest_Fileuploader:
|
|||||||
try:
|
try:
|
||||||
paramDict = json.loads(params)
|
paramDict = json.loads(params)
|
||||||
print("paramDict", paramDict)
|
print("paramDict", paramDict)
|
||||||
|
if paramDict["voiceChangerType"]:
|
||||||
# Change Filepath
|
# 新しいアップローダ用
|
||||||
newFilesDict = {}
|
print("NEW UPLOADER")
|
||||||
for key, val in paramDict["files"].items():
|
props: LoadModelParams = LoadModelParams(slot=slot, isHalf=isHalf, params=paramDict)
|
||||||
if val != "-" and val != "":
|
else:
|
||||||
uploadPath = os.path.join(UPLOAD_DIR, val)
|
# 古いアップローダ用
|
||||||
storePath = os.path.join(UPLOAD_DIR, f"{slot}", val)
|
# Change Filepath
|
||||||
storeDir = os.path.dirname(storePath)
|
newFilesDict = {}
|
||||||
os.makedirs(storeDir, exist_ok=True)
|
for key, val in paramDict["files"].items():
|
||||||
shutil.move(uploadPath, storePath)
|
if val != "-" and val != "":
|
||||||
newFilesDict[key] = storePath
|
uploadPath = os.path.join(UPLOAD_DIR, val)
|
||||||
paramDict["files"] = newFilesDict
|
storePath = os.path.join(UPLOAD_DIR, f"{slot}", val)
|
||||||
|
storeDir = os.path.dirname(storePath)
|
||||||
props: LoadModelParams = LoadModelParams(slot=slot, isHalf=isHalf, params=paramDict)
|
os.makedirs(storeDir, exist_ok=True)
|
||||||
|
shutil.move(uploadPath, storePath)
|
||||||
|
newFilesDict[key] = storePath
|
||||||
|
paramDict["files"] = newFilesDict
|
||||||
|
props = LoadModelParams(slot=slot, isHalf=isHalf, params=paramDict)
|
||||||
|
|
||||||
info = self.voiceChangerManager.loadModel(props)
|
info = self.voiceChangerManager.loadModel(props)
|
||||||
json_compatible_item_data = jsonable_encoder(info)
|
json_compatible_item_data = jsonable_encoder(info)
|
||||||
|
@ -30,7 +30,7 @@ from voice_changer.RVC.ModelSlotGenerator import (
|
|||||||
)
|
)
|
||||||
from voice_changer.RVC.RVCSettings import RVCSettings
|
from voice_changer.RVC.RVCSettings import RVCSettings
|
||||||
from voice_changer.RVC.embedder.EmbedderManager import EmbedderManager
|
from voice_changer.RVC.embedder.EmbedderManager import EmbedderManager
|
||||||
from voice_changer.utils.LoadModelParams import LoadModelParams
|
from voice_changer.utils.LoadModelParams import LoadModelParams, LoadModelParams2
|
||||||
from voice_changer.utils.VoiceChangerModel import AudioInOut
|
from voice_changer.utils.VoiceChangerModel import AudioInOut
|
||||||
from voice_changer.utils.VoiceChangerParams import VoiceChangerParams
|
from voice_changer.utils.VoiceChangerParams import VoiceChangerParams
|
||||||
from voice_changer.RVC.onnxExporter.export2onnx import export2onnx
|
from voice_changer.RVC.onnxExporter.export2onnx import export2onnx
|
||||||
@ -90,6 +90,27 @@ class RVC:
|
|||||||
shutil.move(file, dst)
|
shutil.move(file, dst)
|
||||||
return dst
|
return dst
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def loadModel2(cls, props: LoadModelParams2):
|
||||||
|
slotInfo: RVCModelSlot = RVCModelSlot()
|
||||||
|
for file in props.files:
|
||||||
|
if file.kind == "rvcModel":
|
||||||
|
slotInfo.modelFile = file.name
|
||||||
|
elif file.kind == "rvcIndex":
|
||||||
|
slotInfo.indexFile = file.name
|
||||||
|
slotInfo.defaultTune = 0
|
||||||
|
slotInfo.defaultIndexRatio = 0
|
||||||
|
slotInfo.defaultProtect = 0.5
|
||||||
|
slotInfo.isONNX = slotInfo.modelFile.endswith(".onnx")
|
||||||
|
slotInfo.name = os.path.splitext(os.path.basename(slotInfo.modelFile))[0]
|
||||||
|
# slotInfo.iconFile = "/assets/icons/noimage.png"
|
||||||
|
|
||||||
|
if slotInfo.isONNX:
|
||||||
|
_setInfoByONNX(slotInfo)
|
||||||
|
else:
|
||||||
|
_setInfoByPytorch(slotInfo)
|
||||||
|
return slotInfo
|
||||||
|
|
||||||
def loadModel(self, props: LoadModelParams):
|
def loadModel(self, props: LoadModelParams):
|
||||||
target_slot_idx = props.slot
|
target_slot_idx = props.slot
|
||||||
params = props.params
|
params = props.params
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
|
import os
|
||||||
|
import shutil
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from downloader.SampleDownloader import downloadSample, getSampleInfos
|
from downloader.SampleDownloader import downloadSample, getSampleInfos
|
||||||
from voice_changer.Local.ServerDevice import ServerDevice, ServerDeviceCallbacks
|
from voice_changer.Local.ServerDevice import ServerDevice, ServerDeviceCallbacks
|
||||||
from voice_changer.ModelSlotManager import ModelSlotManager
|
from voice_changer.ModelSlotManager import ModelSlotManager
|
||||||
from voice_changer.VoiceChanger import VoiceChanger
|
from voice_changer.VoiceChanger import VoiceChanger
|
||||||
from const import ModelType
|
from const import UPLOAD_DIR, ModelType
|
||||||
from voice_changer.utils.LoadModelParams import LoadModelParams
|
from voice_changer.utils.LoadModelParams import LoadModelParamFile, LoadModelParams, LoadModelParams2
|
||||||
from voice_changer.utils.VoiceChangerModel import AudioInOut
|
from voice_changer.utils.VoiceChangerModel import AudioInOut
|
||||||
from voice_changer.utils.VoiceChangerParams import VoiceChangerParams
|
from voice_changer.utils.VoiceChangerParams import VoiceChangerParams
|
||||||
from dataclasses import dataclass, asdict
|
from dataclasses import dataclass, asdict
|
||||||
@ -82,12 +84,37 @@ class VoiceChangerManager(ServerDeviceCallbacks):
|
|||||||
|
|
||||||
def loadModel(self, props: LoadModelParams):
|
def loadModel(self, props: LoadModelParams):
|
||||||
paramDict = props.params
|
paramDict = props.params
|
||||||
if "sampleId" in paramDict and len(paramDict["sampleId"]) > 0:
|
if paramDict["sampleId"] is not None:
|
||||||
|
# サンプルダウンロード
|
||||||
downloadSample(self.params.sample_mode, paramDict["sampleId"], self.params.model_dir, props.slot, {"useIndex": paramDict["rvcIndexDownload"]})
|
downloadSample(self.params.sample_mode, paramDict["sampleId"], self.params.model_dir, props.slot, {"useIndex": paramDict["rvcIndexDownload"]})
|
||||||
self.modelSlotManager.getAllSlotInfo(reload=True)
|
self.modelSlotManager.getAllSlotInfo(reload=True)
|
||||||
info = {"status": "OK"}
|
info = {"status": "OK"}
|
||||||
return info
|
return info
|
||||||
|
elif paramDict["voiceChangerType"]:
|
||||||
|
# 新しいアップローダ
|
||||||
|
# Dataを展開
|
||||||
|
params = LoadModelParams2(**paramDict)
|
||||||
|
params.files = [LoadModelParamFile(**x) for x in paramDict["files"]]
|
||||||
|
# ファイルをslotにコピー
|
||||||
|
for file in params.files:
|
||||||
|
print("FILE", file)
|
||||||
|
srcPath = os.path.join(UPLOAD_DIR, file.name)
|
||||||
|
dstDir = os.path.join(self.params.model_dir, str(params.slot))
|
||||||
|
dstPath = os.path.join(dstDir, file.name)
|
||||||
|
os.makedirs(dstDir, exist_ok=True)
|
||||||
|
print(f"move to {srcPath} -> {dstPath}")
|
||||||
|
shutil.move(srcPath, dstPath)
|
||||||
|
file.name = dstPath
|
||||||
|
# メタデータ作成(各VCで定義)
|
||||||
|
if params.voiceChangerType == "RVC":
|
||||||
|
from voice_changer.RVC.RVC import RVC # 起動時にインポートするとパラメータが取れない。
|
||||||
|
|
||||||
|
slotInfo = RVC.loadModel2(params)
|
||||||
|
self.modelSlotManager.save_model_slot(params.slot, slotInfo)
|
||||||
|
print("params", params)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
# 古いアップローダ
|
||||||
print("[Voice Canger]: upload models........")
|
print("[Voice Canger]: upload models........")
|
||||||
info = self.voiceChanger.loadModel(props)
|
info = self.voiceChanger.loadModel(props)
|
||||||
if hasattr(info, "status") and info["status"] == "NG":
|
if hasattr(info, "status") and info["status"] == "NG":
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
from const import VoiceChangerType
|
||||||
|
from typing import Literal, TypeAlias
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class FilePaths:
|
class FilePaths:
|
||||||
@ -17,3 +20,35 @@ class LoadModelParams:
|
|||||||
slot: int
|
slot: int
|
||||||
isHalf: bool
|
isHalf: bool
|
||||||
params: Any
|
params: Any
|
||||||
|
|
||||||
|
|
||||||
|
LoadModelParamFileKind: TypeAlias = Literal[
|
||||||
|
"mmvcv13Config",
|
||||||
|
"mmvcv13Model",
|
||||||
|
"mmvcv15Config",
|
||||||
|
"mmvcv15Model",
|
||||||
|
"soVitsSvc40Config",
|
||||||
|
"soVitsSvc40Model",
|
||||||
|
"soVitsSvc40Cluster",
|
||||||
|
"rvcModel",
|
||||||
|
"rvcIndex",
|
||||||
|
"ddspSvcModel",
|
||||||
|
"ddspSvcModelConfig",
|
||||||
|
"ddspSvcDiffusion",
|
||||||
|
"ddspSvcDiffusionConfig",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class LoadModelParamFile:
|
||||||
|
name: str
|
||||||
|
kind: LoadModelParamFileKind
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class LoadModelParams2:
|
||||||
|
voiceChangerType: VoiceChangerType
|
||||||
|
slot: int
|
||||||
|
isSampleMode: bool
|
||||||
|
sampleId: str
|
||||||
|
files: list[LoadModelParamFile]
|
||||||
|
Loading…
Reference in New Issue
Block a user