mirror of
https://github.com/w-okada/voice-changer.git
synced 2025-01-23 13:35:12 +03:00
WIP: slot manager
This commit is contained in:
parent
16598b3034
commit
bc5fd76cc2
6
client/demo/dist/index.js
vendored
6
client/demo/dist/index.js
vendored
File diff suppressed because one or more lines are too long
@ -28,6 +28,10 @@ export const ModelSlotManagerDialog = () => {
|
||||
const [lang, setLang] = useState<string>("All")
|
||||
const [sampleId, setSampleId] = useState<string>("")
|
||||
|
||||
|
||||
/////////////////////////////////////////
|
||||
// Slot Manager
|
||||
/////////////////////////////////////////
|
||||
const localFileContent = useMemo(() => {
|
||||
if (mode != "localFile") {
|
||||
return <></>
|
||||
@ -125,39 +129,71 @@ export const ModelSlotManagerDialog = () => {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const isRegisterd = modelFileName.length > 0 ? true : false
|
||||
const name = x.name && x.name.length > 0 ? x.name : isRegisterd ? modelFileName : "blank"
|
||||
const termOfUseUrlLink = x.termsOfUseUrl.length > 0 ? <a href={x.termsOfUseUrl} target="_blank" rel="noopener noreferrer" className="body-item-text-small">[terms of use]</a> : <></>
|
||||
|
||||
const fileValueClass = (uploadData?.slot == index) ? "model-slot-detail-row-value-edit" : "model-slot-detail-row-value"
|
||||
const nameValueClass = isRegisterd ? "model-slot-detail-row-value-pointable" : "model-slot-detail-row-value"
|
||||
const nameValueAction = isRegisterd ? async (index: number) => {
|
||||
const name = window.prompt("input new name", "");
|
||||
if (!name) {
|
||||
return
|
||||
}
|
||||
await serverSetting.updateModelInfo(index, "name", name)
|
||||
console.log(name)
|
||||
} : 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) => {
|
||||
} : (url: string) => {
|
||||
const link = document.createElement("a")
|
||||
link.href = url
|
||||
link.download = url.replace(/^.*[\\\/]/, '')
|
||||
link.click()
|
||||
}
|
||||
|
||||
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">
|
||||
<img src={iconUrl} className="model-slot-icon"></img>
|
||||
<img src={iconUrl} className={iconClass} onClick={() => { iconAction(index) }}></img>
|
||||
<div className="model-slot-detail">
|
||||
<div className="model-slot-detail-row">
|
||||
<div className="model-slot-detail-row-label">[{index}]</div>
|
||||
<div className="model-slot-detail-row-value">{x.name}</div>
|
||||
<div className={nameValueClass} onClick={() => { nameValueAction(index) }}>{name}</div>
|
||||
<div className="">{termOfUseUrlLink}</div>
|
||||
</div>
|
||||
<div className="model-slot-detail-row">
|
||||
<div className="model-slot-detail-row-label">model:</div>
|
||||
<div className={fileValueClass}>{modelFileName}</div>
|
||||
<div className={fileValueClass} onClick={() => { fileValueAction(x.modelFile) }}>{modelFileName}</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}>{indexFileName}</div>
|
||||
<div className={fileValueClass} onClick={() => { fileValueAction(x.indexFile) }}>{indexFileName}</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">f0, 40k, 768, onnx, tune, i-rate, p-rate</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">
|
||||
<div className="model-slot-button" onClick={() => { onOpenSampleDownloadDialog(index) }}>from net</div>
|
||||
{(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>
|
||||
@ -166,7 +202,7 @@ export const ModelSlotManagerDialog = () => {
|
||||
<div className="model-slot-button" onClick={onClearClicked}>clear</div> : <div></div>
|
||||
}
|
||||
{(uploadData?.slot == index) && (uploadData.model != null) ?
|
||||
<div>%</div> : <div></div>
|
||||
<div>{serverSetting.uploadProgress.toFixed(1)}%</div> : <div></div>
|
||||
}
|
||||
|
||||
|
||||
@ -185,14 +221,16 @@ export const ModelSlotManagerDialog = () => {
|
||||
mode,
|
||||
serverSetting.serverSetting.modelSlots,
|
||||
serverSetting.fileUploadSettings,
|
||||
serverSetting.uploadProgress,
|
||||
serverSetting.setFileUploadSetting,
|
||||
serverSetting.loadModel,
|
||||
uploadData
|
||||
])
|
||||
|
||||
|
||||
|
||||
|
||||
/////////////////////////////////////////
|
||||
// Sample Downloader
|
||||
/////////////////////////////////////////
|
||||
const fromNetContent = useMemo(() => {
|
||||
if (mode != "fromNet") {
|
||||
return <></>
|
||||
@ -236,7 +274,7 @@ export const ModelSlotManagerDialog = () => {
|
||||
</div>
|
||||
<div className="model-slot-detail-row">
|
||||
<div className="model-slot-detail-row-label">info: </div>
|
||||
<div className="model-slot-detail-row-value">f0, 40k, 768, onnx, tune, i-rate, p-rate</div>
|
||||
<div className="model-slot-detail-row-value">{x.modelType},{x.f0 ? "f0" : "nof0"},{x.sampleRate}</div>
|
||||
<div className=""></div>
|
||||
</div>
|
||||
</div>
|
||||
@ -249,8 +287,8 @@ export const ModelSlotManagerDialog = () => {
|
||||
)
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>Select Sample for Slot[{fromNetTargetIndex}] <span onClick={() => { setMode("localFile") }}>back</span></div>
|
||||
<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}
|
||||
|
@ -726,6 +726,23 @@ body {
|
||||
.dialog-fixed-size-content {
|
||||
width: 90%;
|
||||
max-height: 70vh;
|
||||
.model-slot-header {
|
||||
font-weight: 700;
|
||||
.model-slot-header-button {
|
||||
font-weight: 400;
|
||||
font-size: 0.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
.model-slot-container {
|
||||
max-height: 60vh;
|
||||
width: 100%;
|
||||
@ -738,6 +755,11 @@ body {
|
||||
width: 5rem;
|
||||
height: 5rem;
|
||||
}
|
||||
.model-slot-icon-pointable {
|
||||
width: 5rem;
|
||||
height: 5rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
.model-slot-detail {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@ -754,6 +776,16 @@ body {
|
||||
width: 60%;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.model-slot-detail-row-value-download {
|
||||
width: 60%;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
}
|
||||
.model-slot-detail-row-value-pointable {
|
||||
width: 60%;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
}
|
||||
.model-slot-detail-row-value-edit {
|
||||
width: 60%;
|
||||
font-weight: 700;
|
||||
|
@ -188,6 +188,22 @@ export class ServerConfigurator {
|
||||
return await info
|
||||
}
|
||||
|
||||
uploadAssets = async (params: string) => {
|
||||
const url = this.serverUrl + "/upload_model_assets"
|
||||
const info = new Promise<ServerInfo>(async (resolve) => {
|
||||
const formData = new FormData();
|
||||
formData.append("params", params);
|
||||
|
||||
const request = new Request(url, {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
});
|
||||
const res = await (await fetch(request)).json() as ServerInfo
|
||||
resolve(res)
|
||||
})
|
||||
return await info
|
||||
}
|
||||
|
||||
switchModelType = async (clinetType: ClientType) => {
|
||||
const url = this.serverUrl + "/model_type"
|
||||
const info = new Promise<ServerInfo>(async (resolve) => {
|
||||
@ -258,5 +274,23 @@ export class ServerConfigurator {
|
||||
return await info
|
||||
}
|
||||
|
||||
updateModelInfo = async (slot: number, key: string, val: string) => {
|
||||
const url = this.serverUrl + "/update_model_info"
|
||||
const newData = { slot, key, val }
|
||||
|
||||
const info = new Promise<ServerInfo>(async (resolve) => {
|
||||
const formData = new FormData();
|
||||
formData.append("newData", JSON.stringify(newData));
|
||||
|
||||
const request = new Request(url, {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
});
|
||||
const res = await (await fetch(request)).json() as ServerInfo
|
||||
console.log("RESPONSE", res)
|
||||
resolve(res)
|
||||
})
|
||||
return await info
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -279,6 +279,9 @@ export class VoiceChangerClient {
|
||||
updateModelDefault = async () => {
|
||||
return this.configurator.updateModelDefault()
|
||||
}
|
||||
updateModelInfo = async (slot: number, key: string, val: string) => {
|
||||
return this.configurator.updateModelInfo(slot, key, val)
|
||||
}
|
||||
|
||||
updateServerSettings = (key: ServerSettingKey, val: string) => {
|
||||
return this.configurator.updateSettings(key, val)
|
||||
@ -299,6 +302,9 @@ export class VoiceChangerClient {
|
||||
) => {
|
||||
return this.configurator.loadModel(slot, isHalf, params)
|
||||
}
|
||||
uploadAssets = (params: string) => {
|
||||
return this.configurator.uploadAssets(params)
|
||||
}
|
||||
|
||||
//## Worklet ##//
|
||||
configureWorklet = (setting: WorkletSetting) => {
|
||||
|
@ -254,8 +254,11 @@ export type RVCSampleModel = {
|
||||
credit: string
|
||||
description: string
|
||||
lang: string
|
||||
tag: string
|
||||
tag: string[]
|
||||
icon: string
|
||||
f0: boolean
|
||||
sampleRate: number
|
||||
modelType: string
|
||||
}
|
||||
|
||||
export const DefaultServerSetting: ServerInfo = {
|
||||
|
@ -10,6 +10,12 @@ type ModelData = {
|
||||
filename?: string
|
||||
}
|
||||
|
||||
export const ModelAssetName = {
|
||||
iconFile: "iconFile"
|
||||
} as const
|
||||
export type ModelAssetName = typeof ModelAssetName[keyof typeof ModelAssetName]
|
||||
|
||||
|
||||
export type FileUploadSetting = {
|
||||
isHalf: boolean
|
||||
uploaded: boolean
|
||||
@ -76,8 +82,12 @@ export const InitialFileUploadSetting: FileUploadSetting = {
|
||||
ddspSvcModelConfig: null,
|
||||
ddspSvcDiffusion: null,
|
||||
ddspSvcDiffusionConfig: null,
|
||||
}
|
||||
|
||||
|
||||
type AssetUploadSetting = {
|
||||
slot: number
|
||||
name: ModelAssetName
|
||||
file: string
|
||||
}
|
||||
|
||||
export type UseServerSettingProps = {
|
||||
@ -100,7 +110,8 @@ export type ServerSettingState = {
|
||||
getOnnx: () => Promise<OnnxExporterInfo>
|
||||
mergeModel: (request: MergeModelRequest) => Promise<ServerInfo>
|
||||
updateModelDefault: () => Promise<ServerInfo>
|
||||
|
||||
updateModelInfo: (slot: number, key: string, val: string) => Promise<ServerInfo>
|
||||
uploadAssets: (slot: number, name: ModelAssetName, file: File) => Promise<void>
|
||||
}
|
||||
|
||||
export const useServerSetting = (props: UseServerSettingProps): ServerSettingState => {
|
||||
@ -501,6 +512,25 @@ export const useServerSetting = (props: UseServerSettingProps): ServerSettingSta
|
||||
|
||||
|
||||
|
||||
const uploadAssets = useMemo(() => {
|
||||
return async (slot: number, name: ModelAssetName, file: File) => {
|
||||
if (!props.voiceChangerClient) return
|
||||
|
||||
await _uploadFile2(file, (progress: number, _end: boolean) => {
|
||||
console.log(progress, _end)
|
||||
})
|
||||
const assetUploadSetting: AssetUploadSetting = {
|
||||
slot,
|
||||
name,
|
||||
file: file.name
|
||||
}
|
||||
await props.voiceChangerClient.uploadAssets(JSON.stringify(assetUploadSetting))
|
||||
reloadServerInfo()
|
||||
}
|
||||
}, [fileUploadSettings, props.voiceChangerClient, props.clientType])
|
||||
|
||||
|
||||
|
||||
const reloadServerInfo = useMemo(() => {
|
||||
return async () => {
|
||||
|
||||
@ -538,6 +568,11 @@ export const useServerSetting = (props: UseServerSettingProps): ServerSettingSta
|
||||
setServerSetting(serverInfo)
|
||||
return serverInfo
|
||||
}
|
||||
const updateModelInfo = async (slot: number, key: string, val: string) => {
|
||||
const serverInfo = await props.voiceChangerClient!.updateModelInfo(slot, key, val)
|
||||
setServerSetting(serverInfo)
|
||||
return serverInfo
|
||||
}
|
||||
|
||||
return {
|
||||
serverSetting,
|
||||
@ -553,5 +588,7 @@ export const useServerSetting = (props: UseServerSettingProps): ServerSettingSta
|
||||
getOnnx,
|
||||
mergeModel,
|
||||
updateModelDefault,
|
||||
updateModelInfo,
|
||||
uploadAssets
|
||||
}
|
||||
}
|
@ -43,6 +43,12 @@ class MMVC_Rest_Fileuploader:
|
||||
self.router.add_api_route(
|
||||
"/update_model_default", self.post_update_model_default, methods=["POST"]
|
||||
)
|
||||
self.router.add_api_route(
|
||||
"/update_model_info", self.post_update_model_info, methods=["POST"]
|
||||
)
|
||||
self.router.add_api_route(
|
||||
"/upload_model_assets", self.post_upload_model_assets, methods=["POST"]
|
||||
)
|
||||
|
||||
def post_upload_file(self, file: UploadFile = File(...), filename: str = Form(...)):
|
||||
res = upload_file(UPLOAD_DIR, file, filename)
|
||||
@ -128,3 +134,13 @@ class MMVC_Rest_Fileuploader:
|
||||
info = self.voiceChangerManager.update_model_default()
|
||||
json_compatible_item_data = jsonable_encoder(info)
|
||||
return JSONResponse(content=json_compatible_item_data)
|
||||
|
||||
def post_update_model_info(self, newData: str = Form(...)):
|
||||
info = self.voiceChangerManager.update_model_info(newData)
|
||||
json_compatible_item_data = jsonable_encoder(info)
|
||||
return JSONResponse(content=json_compatible_item_data)
|
||||
|
||||
def post_upload_model_assets(self, params: str = Form(...)):
|
||||
info = self.voiceChangerManager.upload_model_assets(params)
|
||||
json_compatible_item_data = jsonable_encoder(info)
|
||||
return JSONResponse(content=json_compatible_item_data)
|
||||
|
@ -478,3 +478,44 @@ class RVC:
|
||||
|
||||
json.dump(params, open(os.path.join(slotDir, "params.json"), "w"))
|
||||
self.loadSlots()
|
||||
|
||||
def update_model_info(self, newData: str):
|
||||
print("[Voice Changer] UPDATE MODEL INFO", newData)
|
||||
newDataDict = json.loads(newData)
|
||||
try:
|
||||
slotDir = os.path.join(
|
||||
self.params.model_dir, RVC_MODEL_DIRNAME, str(newDataDict["slot"])
|
||||
)
|
||||
except Exception as e:
|
||||
print("Exception::::", e)
|
||||
params = json.load(
|
||||
open(os.path.join(slotDir, "params.json"), "r", encoding="utf-8")
|
||||
)
|
||||
params[newDataDict["key"]] = newDataDict["val"]
|
||||
json.dump(params, open(os.path.join(slotDir, "params.json"), "w"))
|
||||
self.loadSlots()
|
||||
|
||||
def upload_model_assets(self, params: str):
|
||||
print("[Voice Changer] UPLOAD ASSETS", params)
|
||||
paramsDict = json.loads(params)
|
||||
uploadPath = os.path.join(UPLOAD_DIR, paramsDict["file"])
|
||||
storeDir = os.path.join(
|
||||
self.params.model_dir, RVC_MODEL_DIRNAME, str(paramsDict["slot"])
|
||||
)
|
||||
storePath = os.path.join(
|
||||
storeDir,
|
||||
paramsDict["file"],
|
||||
)
|
||||
storeJson = os.path.join(
|
||||
storeDir,
|
||||
"params.json",
|
||||
)
|
||||
try:
|
||||
shutil.move(uploadPath, storePath)
|
||||
params = json.load(open(storeJson, "r", encoding="utf-8"))
|
||||
params[paramsDict["name"]] = storePath
|
||||
json.dump(params, open(storeJson, "w"))
|
||||
except Exception as e:
|
||||
print("Exception::::", e)
|
||||
|
||||
self.loadSlots()
|
||||
|
@ -608,6 +608,20 @@ class VoiceChanger:
|
||||
self.voiceChanger.update_model_default()
|
||||
return self.get_info()
|
||||
|
||||
def update_model_info(self, newData: str):
|
||||
if self.voiceChanger is None:
|
||||
print("[Voice Changer] Voice Changer is not selected.")
|
||||
return
|
||||
self.voiceChanger.update_model_info(newData)
|
||||
return self.get_info()
|
||||
|
||||
def upload_model_assets(self, params: str):
|
||||
if self.voiceChanger is None:
|
||||
print("[Voice Changer] Voice Changer is not selected.")
|
||||
return
|
||||
self.voiceChanger.upload_model_assets(params)
|
||||
return self.get_info()
|
||||
|
||||
|
||||
PRINT_CONVERT_PROCESSING: bool = False
|
||||
# PRINT_CONVERT_PROCESSING = True
|
||||
|
@ -69,3 +69,9 @@ class VoiceChangerManager(object):
|
||||
|
||||
def update_model_default(self):
|
||||
return self.voiceChanger.update_model_default()
|
||||
|
||||
def update_model_info(self, newData: str):
|
||||
return self.voiceChanger.update_model_info(newData)
|
||||
|
||||
def upload_model_assets(self, params: str):
|
||||
return self.voiceChanger.upload_model_assets(params)
|
||||
|
Loading…
Reference in New Issue
Block a user