WIP: slot manager

This commit is contained in:
wataru 2023-06-08 03:08:59 +09:00
parent 16598b3034
commit bc5fd76cc2
11 changed files with 246 additions and 19 deletions

File diff suppressed because one or more lines are too long

View File

@ -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&gt;&gt; </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">&lt;&lt;back</span></div>
<div>Lang:
<select value={lang} onChange={(e) => { setLang(e.target.value) }}>
{langOptions}

View File

@ -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;

View File

@ -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
}
}

View File

@ -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) => {

View File

@ -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 = {

View File

@ -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
}
}

View File

@ -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)

View File

@ -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()

View File

@ -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

View File

@ -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)