├── LICENSE ├── README.md ├── rvc_jp_translate.user.js └── rvc_tweaks.user.js /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # rvc_tweaks 3 | 4 | RVCにちょっと便利な機能を追加するTampermonkeyスクリプトです。[ddPn08/rvc-webui](https://github.com/ddPn08/rvc-webui) でも動作するようにしています。将来的にどちらを重視するかは未定です。 5 | 6 | Tampermonkey機能拡張を使用している状態で [rvc_tweaks.user.js](https://github.com/hetima/rvc_jp_translate/raw/main/rvc_tweaks.user.js) を開くとインストールできます。 7 | 8 | - ""で囲まれたファイルパスをペーストすると自動的に"を除去します 9 | - 最近使った音源を再選択できるポップアップを追加します。最大16個まで記憶します(UI上のテキストで対象を識別しているのでローカライズの種類によっては動作しません) 10 | - 特徴量検索用ファイルパス(added_XXXX.index)と特徴量ファイルパス(total_fea.npy)を自動設定 11 | 12 | モデルを変更すると、対応する特徴量ファイルパスを自動で設定します。 13 | 推論実行タブの最下段にフォルダパスを設定する項目が追加されます。そこで設定したフォルダの中に「モデル名.index」「モデル名.npy」とリネームして配置してください。 14 | 例えばフォルダパスを `D:\RVC\subdata` と指定したら `added_XXXX.index` と `total_fea.npy` を `モデル名.index` と `モデル名.npy`にリネームして `D:\RVC\subdata` に入れてください。 15 | ただし、モデル名が_+数字で終わる場合は数字を除いた名前にしてください(xxx_100→xxx つまり「xxx」と「xxx_数字」ではファイルを共用します)。 16 | ファイルが存在するかどうかはチェックせず強制的に設定変更するのでご注意ください。 17 | この機能をオフにしたい場合はパス設定を空白にしてください。 18 | 19 | 手動選択機能も追加しました。別モデルの特徴量ファイルで推論する手順が簡略になります。indexを設定するテキストエリアの上にポップアップが表示されます。特徴量ファイルが存在するかどうかはチェックせずモデル名基準でリストされるのでご注意ください。 20 | 21 | localStorageを利用しているため、ポート番号が変わると設定が飛ぶので再設定が必要です。 22 | 23 | # ~~rvc_jp_translate~~ 24 | 25 | RVC(Retrieval-based-Voice-Conversion)のWebUIを日本語ローカライズするTampermonkeyスクリプトです。RVC本体ファイルには手を加えずにローカライズします。 26 | 27 | **20230416updated 以降を適用している場合、標準でローカライズされるのでこのスクリプトは不要です。** 28 | 29 | Tampermonkey機能拡張を使用している状態で [rvc_jp_translate.user.js](https://github.com/hetima/rvc_jp_translate/raw/main/rvc_jp_translate.user.js) を開くとインストールできます。 30 | 31 | -------------------------------------------------------------------------------- /rvc_jp_translate.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name rvc_jp_translate 3 | // @namespace http://tampermonkey.net/ 4 | // @version 0.1.3 5 | // @description インターフェイスを日本語化 6 | // @author hetima 7 | // @match http://127.0.0.1:7865/ 8 | // @match https://*.gradio.live/ 9 | // @run-at document-end 10 | // @grant none 11 | // ==/UserScript== 12 | 13 | // 0.1.3 起動処理改善 14 | 15 | (function () { 16 | 'use strict'; 17 | function setup() { 18 | } 19 | 20 | function dumpText() { 21 | var texts = jpDict; 22 | var elementList = document.querySelector('gradio-app').shadowRoot.querySelectorAll("p,span,button"); 23 | elementList.forEach(function (itm) { 24 | var txt = itm.innerHTML; 25 | if (!(txt in jpDict)) { 26 | texts[txt] = ""; 27 | } 28 | }); 29 | let write_json = JSON.stringify(texts); 30 | console.log(write_json); 31 | } 32 | 33 | function replaceText() { 34 | console.log("replaceText"); 35 | var elementList = document.querySelector('gradio-app').shadowRoot.querySelectorAll("p,span,button"); 36 | elementList.forEach(function (itm) { 37 | var txt = itm.innerHTML; 38 | if (txt in jpDict) { 39 | var repTxt = jpDict[txt]; 40 | if (repTxt != ""){ 41 | itm.innerHTML = repTxt; 42 | } 43 | } 44 | }); 45 | } 46 | 47 | const observer = new MutationObserver(records => { 48 | //dumpText(); 49 | replaceText(); 50 | }); 51 | const options = { 52 | childList: true, 53 | subtree: true 54 | }; 55 | const shadowRoot = document.querySelector('gradio-app').shadowRoot 56 | if (shadowRoot == undefined) { 57 | console.log("shadowRoot is undefined"); 58 | setTimeout(function () { 59 | replaceText(); 60 | observer.observe(document.querySelector('gradio-app').shadowRoot, options); 61 | }, 2000); 62 | } else { 63 | observer.observe(document.querySelector('gradio-app').shadowRoot, options); 64 | setTimeout(function () { 65 | //時間がたったら監視停止 66 | //observer.disconnect(); 67 | //しないでずっと動かす。タブの切替でタブタイトルが変更されてしまうので 68 | }, 8000); 69 | } 70 | 71 | 72 | 73 | // observer.observe(document.querySelector('gradio-app').shadowRoot, options); 74 | // setup(); 75 | // setTimeout(function () { 76 | // //時間がたったら監視停止 77 | // }, 6000); 78 | 79 | 80 | // "是" "否" は動作に影響するので翻訳しない 81 | const jpDict = { 82 | "模型推理 ": "推論実行", 83 | "伴奏人声分离 ": "音声分離", 84 | "训练 ": "トレーニング", 85 | "ckpt处理 ": "ckpt処理", 86 | "招募音高曲线前端编辑器 ": "", 87 | "招募实时变声插件开发 ": "", 88 | "点击查看交流、问题反馈群号 ": "", 89 | "刷新音色列表": "モデル一覧を更新", 90 | "卸载音色省显存": "モデルをアンロード", 91 | "转换": "変換", 92 | "处理数据": "処理開始", 93 | "特征提取": "抽出開始", 94 | "训练模型": "モデルトレーニング", 95 | "训练特征索引": "特徴トレーニング", 96 | "一键训练": "一括処理", 97 | "融合": "マージ実行", 98 | "修改": "修正", 99 | "查看": "検査", 100 | "提取": "抽出", 101 | "输出信息": "出力情報", 102 | "本软件以MIT协议开源,作者不对软件具备任何控制力,使用软件者、传播软件导出的声音者自负全责。
\n如不认可该条款,则不能使用或引用软件包内任何代码和文件。详见根目录\"使用需遵守的协议-LICENSE.txt\"。": "本ソフトウェアはMITライセンスに基づくオープンソースであり、作者は本ソフトウェアの使用および本ソフトウェアから派生する音の配布について一切の責任を負わないものとし、本ソフトウェアの使用および本ソフトウェアから派生する音の配布について一切の責任を負わないものとします。
\n本契約の条項に同意されない場合、パッケージ内のコードやファイルを使用したり参照したりすることはできません。 詳しくはルートディレクトリの LICENSE.txt をご覧ください。", 103 | "推理音色": "モデル", 104 | "请选择说话人id": "話者ID", 105 | "男转女推荐+12key,女转男推荐-12key,如果音域爆炸导致音色失真也可以自己调整到合适音域。": "男性から女性への変換の場合は、+12キーを推奨。女性から男性への変換の場合は、-12キーを推奨", 106 | "变调(整数,半音数量,升八度12降八度-12)": "変調(整数、半音階量、1オクターブ=12、-1オクターブ=-12)", 107 | "输入待处理音频文件路径(默认是正确格式示例)": "変換元のファイルパス", 108 | "选择音高提取算法,输入歌声可用pm提速,harvest低音好但巨慢无比": "音高抽出アルゴリズムを選択。pmは速い。harvestは精度が高いが遅い。", 109 | "特征检索库文件路径": "特徴量検索用ファイルパス(added_XXXX.index)", 110 | "特征文件路径": "特徴量ファイルパス(total_fea.npy)", 111 | "检索特征占比": "特徴量の割合", 112 | "批量转换,输入待转换音频文件夹,或上传多个音频文件,在指定文件夹(默认opt)下输出转换的音频。": "バッチ変換用", 113 | "指定输出文件夹": "出力フォルダ", 114 | "输入待处理音频文件夹路径(去文件管理器地址栏拷就行了)": "処理するファイルが含まれるフォルダのパス", 115 | "人声伴奏分离批量处理,使用UVR5模型。
\n不带和声用HP2,带和声且提取的人声不需要和声用HP5
\n合格的文件夹路径格式举例:E:\\codes\\py39\\vits_vc_gpu\\白鹭霜华测试样例(去文件管理器地址栏拷就行了)": "UVR5モデルによる音声分離バッチ処理。
\nハーモニーのない場合は、HP2を使用。ハーモニーのある場合は、HP5を使用。", 116 | "输入待处理音频文件夹路径": "処理するファイルが含まれるフォルダのパス", 117 | "模型": "モデル", 118 | "指定输出人声文件夹": "Vocal出力フォルダ", 119 | "指定输出乐器文件夹": "Inst出力フォルダ", 120 | "step1:填写实验配置。实验数据放在logs下,每个实验一个文件夹,需手工输入实验名路径,内含实验配置,日志,训练得到的模型文件。": "step1: 構成。logsフォルダにモデル名のフォルダが作成されます。", 121 | "输入实验名": "モデル名", 122 | "目标采样率": "サンプリングレート", 123 | "32k": "", 124 | "40k": "", 125 | "48k": "", 126 | "模型是否带音高指导(唱歌一定要,语音可以不要)": "ピッチガイドの有無(歌は必須、話し声は省くことができる)", 127 | "step2a:自动遍历训练文件夹下所有可解码成音频的文件并进行切片归一化,在实验目录下生成2个wav文件夹;暂时只支持单人训练。": "step2a:トレーニングフォルダ内のすべての音声ファイルを自動的に処理し、スライスと正規化を行います。処理後のファイルは、logsフォルダ内に生成されたフォルダに保存されます。ただし、現在は、単一話者のみサポートされています。", 128 | "输入训练文件夹路径": "トレーニングフォルダーパスを入力", 129 | "请指定说话人id": "話者ID", 130 | "step2b:使用CPU提取音高(如果模型带音高),使用GPU提取特征(选择卡号)": "step2b:CPUでピッチ抽出(ピッチ付きモデルの場合)、GPUで特徴抽出(カード番号を選択)", 131 | "以-分隔输入使用的卡号,例如 0-1-2 使用卡0和卡1和卡2": "使用するカード番号を-で区切って入力。例:0、1、2を使用する場合 0-1-2", 132 | "显卡信息": "グラフィックカード情報", 133 | "提取音高使用的CPU进程数": "ピッチ抽出に使用するCPUのプロセス数", 134 | "选择音高提取算法:输入歌声可用pm提速,高质量语音但CPU差可用dio提速,harvest质量更好但慢": "ピッチ抽出方法(品質はharvest が良い)", 135 | "step3:填写训练设置,开始训练模型和索引": "step3:トレーニング実行", 136 | "保存频率save_every_epoch": "save_every_epoch", 137 | "总训练轮数total_epoch": "total_epoch", 138 | "每张显卡的batch_size": "batch_size", 139 | "是否仅保存最新的ckpt文件以节省硬盘空间": "最新のpthのみ保存して容量節約するかどうか", 140 | "是否缓存所有训练集至显存。10min以下小数据可缓存以加速训练,大数据缓存会炸显存也加不了多少速": "すべてのデータをVRAMにキャッシュするかどうか", 141 | "加载预训练底模G路径": "プリトレインGのパス", 142 | "加载预训练底模D路径": "プリトレインDのパス", 143 | "模型融合,可用于测试音色融合": "モデルのマージ", 144 | "A模型路径": "Aモデルパス", 145 | "B模型路径": "Bモデルパス", 146 | "A模型权重": "Aモデルの重み", 147 | "模型是否带音高指导": "ピッチガイドの有無", 148 | "要置入的模型信息": "保存ファイルパス", 149 | "保存的模型名不带后缀": "あるいはweightsフォルダに書き出すファイル名(.pthは付けない)", 150 | "修改模型信息(仅支持weights文件夹下提取的小模型文件)": "モデル情報の修正(weightsフォルダの小さなモデルファイルのみ対応)", 151 | "模型路径": "モデルのパス", 152 | "要改的模型信息": "変更する内容", 153 | "保存的文件名,默认空为和源文件同名": "保存するモデル名(xxx.pth)(空白だと上書き)", 154 | "查看模型信息(仅支持weights文件夹下提取的小模型文件)": "モデル情報の閲覧(weightsフォルダの小さなモデルファイルのみ対応)", 155 | "模型提取(输入logs文件夹下大文件模型路径),适用于训一半不想训了模型没有自动提取保存小文件模型,或者想测试中间模型的情况": "モデル抽出(logsフォルダにあるG~.pthのパスを入力してweightsフォルダに抽出)、トレーニングセッションの途中で、小さなモデルファイルが自動的に抽出・保存されない場合や、中間モデルをテストしたい場合などに使用します(ここの「保存ファイルパス」設定は今のところ使えない模様)", 156 | "保存名": "weightsフォルダに書き出すファイル名(.pthは付けない)", 157 | "模型是否带音高指导,1是0否": "ピッチガイド 1:有 0:無", 158 | "加开发群联系我647947694": "" 159 | } 160 | 161 | })(); 162 | 163 | -------------------------------------------------------------------------------- /rvc_tweaks.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name rvc_tweaks 3 | // @namespace http://tampermonkey.net/ 4 | // @version 0.4.4 5 | // @description rvc_tweaks 6 | // @author hetima 7 | // @match http://127.0.0.1/ 8 | // @match http://localhost/ 9 | // @match https://*.gradio.live/ 10 | // @run-at document-end 11 | // ==/UserScript== 12 | 13 | // 0.4.4 最新のddPn08対応 14 | // 0.4.3 最新のddPn08対応 15 | // 0.4.2 RVC20230428対応 16 | // 0.4.1 最近使った音源履歴 8個→16個 17 | // 0.4.0 特徴量ファイルの手動選択 18 | // 0.3.3 RVC20230416対応 19 | // 0.3.2 最新のddPn08/rvc-webui対応 20 | // 0.3.1 "取る処理の修正 21 | // 0.3.0 最近使った音源 22 | // 0.2.4 ddPn08/rvc-webui でのバグ修正 23 | // 0.2.3 ddPn08/rvc-webui にちょっと対応 24 | // 0.2.1 特徴量ファイルパスの共用名を「モデル名_数字」に変更 25 | // 0.2.0 特徴量ファイルパスの自動設定を追加 26 | // 0.1.3 save_every_epoch、total_epoch、batch_sizeの保存対応停止 27 | // 0.1.2 save_every_epoch、total_epoch、batch_sizeの保存値をスライダーにも反映 28 | 29 | (function () { 30 | 'use strict'; 31 | var inited = false; 32 | 33 | function getNumInputForSpan(elm, name){ 34 | if (elm.innerHTML.indexOf(name) != -1) { 35 | var nm = elm.parentNode.nextElementSibling; 36 | if (nm != null && nm.type == "number"){ 37 | return nm; 38 | } 39 | } 40 | return null; 41 | } 42 | function getRangeInputForNumInput(elm) { 43 | var nm = elm.parentNode.parentNode.nextElementSibling; 44 | if (nm != null && nm.type == "range") { 45 | return nm; 46 | } 47 | 48 | return null; 49 | } 50 | function setup() { 51 | if (inited){ 52 | return; 53 | } 54 | if (gAppRoot == null) { 55 | gAppRoot = document.querySelector('gradio-app').shadowRoot; 56 | if (gAppRoot == null) { 57 | gAppRoot = document.querySelector('gradio-app'); 58 | } 59 | } 60 | 61 | var elementList = gAppRoot.querySelectorAll("textarea"); 62 | if (elementList.length > 0) { 63 | console.log("rvc_tweaks inited"); 64 | inited = true; 65 | 66 | // テキストが""で囲まれてたら"を削除する 67 | elementList.forEach(function (itm) { 68 | //itm.onkeyup = handleChangeText; 69 | itm.addEventListener('keyup', function (evt) { 70 | var tgt = evt.target; 71 | var txt = tgt.value; 72 | if (txt.length > 8 ) { 73 | var char = txt.slice(0, 1); 74 | if (char == '"' && char == txt.slice(-1)) { 75 | tgt.value = txt.slice(1, -1); 76 | //Gradioに変更したことを認識させる 77 | tgt.dispatchEvent(new Event("input")); 78 | } 79 | } 80 | }); 81 | }); 82 | 83 | 84 | setupIndexPath(); 85 | //動くようになったけどローカライズの種類に依存するので 86 | // setupTrainCount(); 87 | setupRecentsSelect(); 88 | } 89 | 90 | } 91 | 92 | function setupTrainCount() { 93 | 94 | //トレーニング設定保存 95 | const elementList = gAppRoot.querySelectorAll("span"); 96 | elementList.forEach(function (itm) { 97 | var numInput = getNumInputForSpan(itm, 'save_every_epoch'); 98 | var rangeInput; 99 | if (numInput != null){ 100 | var save_every_epoch = localStorage.getItem("rvc_tweaks_save_every_epoch"); 101 | if (!Number.isNaN(parseInt(save_every_epoch))){ 102 | numInput.value = save_every_epoch; 103 | numInput.dispatchEvent(new Event("input")) 104 | } 105 | numInput.addEventListener('change', function (evt) { 106 | localStorage.setItem("rvc_tweaks_save_every_epoch", evt.target.value); 107 | }); 108 | rangeInput = getRangeInputForNumInput(numInput); 109 | // if (rangeInput != null) { 110 | // rangeInput.value = save_every_epoch; 111 | // } 112 | } 113 | 114 | numInput = getNumInputForSpan(itm, 'total_epoch'); 115 | if (numInput != null) { 116 | var total_epoch = localStorage.getItem("rvc_tweaks_total_epoch"); 117 | if (!Number.isNaN(parseInt(total_epoch))) { 118 | numInput.value = total_epoch; 119 | numInput.dispatchEvent(new Event("input")) 120 | } 121 | numInput.addEventListener('change', function (evt) { 122 | localStorage.setItem("rvc_tweaks_total_epoch", evt.target.value); 123 | }); 124 | // rangeInput = getRangeInputForNumInput(numInput); 125 | // if (rangeInput != null) { 126 | // rangeInput.value = total_epoch; 127 | // } 128 | } 129 | 130 | numInput = getNumInputForSpan(itm, 'batch_size'); 131 | if (numInput != null) { 132 | var batch_size = localStorage.getItem("rvc_tweaks_batch_size"); 133 | if (!Number.isNaN(parseInt(batch_size))) { 134 | numInput.value = batch_size; 135 | numInput.dispatchEvent(new Event("input")) 136 | } 137 | numInput.addEventListener('change', function (evt) { 138 | localStorage.setItem("rvc_tweaks_batch_size", evt.target.value); 139 | }); 140 | // rangeInput = getRangeInputForNumInput(numInput); 141 | // if (rangeInput != null) { 142 | // rangeInput.value = batch_size; 143 | // } 144 | } 145 | 146 | }); 147 | } 148 | 149 | //特徴量ファイルパス 150 | const gIndexSelect = document.createElement('select'); 151 | gIndexSelect.style = "width:20px;border-width:0px;padding:2px 22px;"; 152 | const addedIndexTextareas = new Array(); 153 | const totalFeaTextareas = new Array(); 154 | function setupIndexPath(){ 155 | 156 | //textareaを探す 157 | //RVC 158 | var elementList = gAppRoot.querySelectorAll("textarea"); 159 | var textareaCount = 0; 160 | if (elementList.length > 0) { 161 | elementList.forEach(function (itm) { 162 | if (itm.value.indexOf(".index") != -1) { 163 | addedIndexTextareas.push(itm); 164 | textareaCount++; 165 | } else if (itm.value.indexOf("total_fea.npy") != -1) { 166 | totalFeaTextareas.push(itm); 167 | } 168 | }); 169 | } 170 | //rvc-webui 171 | if (textareaCount < 2){ 172 | elementList = gAppRoot.querySelectorAll("span"); 173 | textareaCount = 0; 174 | if (elementList.length > 0) { 175 | elementList.forEach(function (itm) { 176 | var t; 177 | if (itm.innerHTML == "Feature Retrieval Library" || itm.innerHTML == "Faiss Index File Path") { 178 | t = itm.nextElementSibling; 179 | if(t.type=="textarea"){ 180 | addedIndexTextareas.push(t); 181 | textareaCount++; 182 | } 183 | } else if (itm.innerHTML == "Feature File Path" || itm.innerHTML == "Big NPY File Path") { 184 | t = itm.nextElementSibling; 185 | if (t.type == "textarea") { 186 | totalFeaTextareas.push(t); 187 | textareaCount++; 188 | } 189 | } 190 | }); 191 | } 192 | } 193 | 194 | if (textareaCount < 1) { 195 | return; 196 | } 197 | // console.log("textareaCount=" + textareaCount); 198 | // 設定UIを追加 199 | const view = addViewToTab(0, "rvc_tweaks 設定"); 200 | const iPathTextarea = addTextareaToView(view, "特徴量検索用ファイルパス(added_XXXX.index)と特徴量ファイルパス(total_fea.npy)を自動設定するためのフォルダパス(例:D:\\RVC-beta\\weights\\subdata)
このフォルダの中に「モデル名.index」「モデル名.npy」とリネームして配置してください。ただし、モデル名が_+数字で終わる場合は_+数字を除いた名前にしてください(xxx_100→xxx)
ファイルが存在するかどうかはチェックせず強制的に設定変更するのでご注意ください。"); 201 | iPathTextarea.value = localStorage.getItem("rvc_tweaks_t_path"); 202 | iPathTextarea.addEventListener('change', function (evt) { 203 | localStorage.setItem("rvc_tweaks_t_path", evt.target.value); 204 | }); 205 | iPathTextarea.addEventListener('keyup', function (evt) { 206 | var tgt = evt.target; 207 | var txt = tgt.value; 208 | var char = txt.slice(0, 1); 209 | if (char == '"' && char == txt.slice(-1)) { 210 | tgt.value = txt.slice(1, -1); 211 | localStorage.setItem("rvc_tweaks_t_path", evt.target.value); 212 | } 213 | }); 214 | 215 | //モデル選択したらパスを更新 216 | let slct = gAppRoot.querySelector(".tabitem>div>div>div>div>label>select"); 217 | //ddpn gradio==3.25.0 218 | if(slct==null){ 219 | slct = gAppRoot.querySelector(".single-select"); //span 220 | } 221 | //ddpn gradio==3.28.3 222 | if (slct==null){ 223 | const elementList = gAppRoot.querySelectorAll("span"); 224 | elementList.forEach(function (itm) { 225 | if(itm.innerHTML == "Model"){ 226 | if (slct == null) { 227 | slct = itm.nextElementSibling.querySelector("input"); 228 | } 229 | } 230 | }); 231 | } 232 | 233 | if (slct && slct.type == "select-one"){ 234 | slct.addEventListener('change', function (evt) { 235 | updateIndexPath(evt.target.value); 236 | }); 237 | } else if (slct && slct.tagName == "SPAN") { //ddpn gradio==3.25.0 238 | if (slct.textContent != ""){ 239 | updateIndexPath(slct.textContent); 240 | } 241 | const observer2 = new MutationObserver(records => { 242 | const newName = records[0].target.textContent; 243 | console.log(newName + " was selected"); 244 | updateIndexPath(newName); 245 | }); 246 | const options = { 247 | childList: true, 248 | subtree: true, 249 | characterData: true 250 | }; 251 | observer2.observe(slct, options); 252 | } else if (slct && slct.tagName == "INPUT") { //ddpn gradio==3.28.3 253 | if (slct.value != "") { 254 | updateIndexPath(slct.value); 255 | } 256 | const observer2 = new MutationObserver(records => { 257 | const newName = records[0].target.value; 258 | if(newName != ""){ 259 | console.log(newName + " was selected"); 260 | updateIndexPath(newName); 261 | } 262 | }); 263 | const options = { 264 | attributes: true 265 | }; 266 | observer2.observe(slct, options); 267 | } 268 | 269 | 270 | //manual index select 271 | updateIndexSelect(); 272 | gIndexSelect.addEventListener('change', function (evt) { 273 | let val = evt.target.value; 274 | if (val == "") { 275 | updateIndexSelect(); 276 | } else if (val && val.length > 0) { 277 | updateIndexPath(val); 278 | } 279 | gIndexSelect.selectedIndex = -1; 280 | }); 281 | addedIndexTextareas[0].parentNode.insertBefore(gIndexSelect, addedIndexTextareas[0]); 282 | } 283 | function updateIndexSelect(){ 284 | gIndexSelect.replaceChildren(); 285 | if (unsafeWindow.gradio_config == undefined){ 286 | return; 287 | } 288 | const components = unsafeWindow.gradio_config.components; 289 | let indexNames = new Array(); 290 | components.forEach(element => { 291 | if (element.type == "dropdown" && (element.props.label == "Model" || element.props.label == "音源推論")){ 292 | const choices = element.props.choices; 293 | if (choices != undefined){ 294 | choices.forEach(choice => { 295 | choice = indexNameFromModelName(choice); 296 | if (choice.length > 0 && !indexNames.includes(choice)){ 297 | indexNames.push(choice); 298 | } 299 | }); 300 | } 301 | } 302 | }); 303 | indexNames.forEach(indexName => { 304 | var opt = document.createElement("option"); 305 | opt.text = indexName; 306 | opt.value = indexName; 307 | gIndexSelect.appendChild(opt); 308 | }); 309 | 310 | var option = document.createElement("option"); 311 | option.text = "[Reload]"; 312 | option.value = ""; 313 | gIndexSelect.appendChild(option); 314 | gIndexSelect.selectedIndex = -1; 315 | } 316 | function updateIndexPath(modelName) { 317 | // console.log("model selection changed"); 318 | let iPath = localStorage.getItem("rvc_tweaks_t_path"); 319 | if (!iPath || iPath.length <= 0) { 320 | return; 321 | } 322 | if (iPath.slice(-1) == "\\") { 323 | iPath = iPath.slice(0, -1); 324 | } 325 | modelName = indexNameFromModelName(modelName); 326 | if (modelName.length > 0) { 327 | let addIndexPath = iPath + "\\" + modelName + ".index"; 328 | let totalFeaPath = iPath + "\\" + modelName + ".npy"; 329 | addedIndexTextareas.forEach((element) => { 330 | element.value = addIndexPath; 331 | //Gradioに変更したことを認識させる 332 | element.dispatchEvent(new Event("input")); 333 | }); 334 | totalFeaTextareas.forEach((element) => { 335 | element.value = totalFeaPath; 336 | //Gradioに変更したことを認識させる 337 | element.dispatchEvent(new Event("input")); 338 | }); 339 | } 340 | } 341 | 342 | //最近使った音源 343 | const gRecentSelect = document.createElement('select'); 344 | let gWavTextarea; 345 | let gRecentSelectsArray = new Array(); 346 | gRecentSelect.style = "width:20px;border-width:0px;padding:2px 22px;"; 347 | function setupRecentsSelect(){ 348 | const tab = gAppRoot.querySelector(".tabitem"); //最初のタブだけ 349 | if (tab == null) { 350 | return; 351 | } 352 | var elementList = tab.querySelectorAll("span"); 353 | gWavTextarea = null; 354 | if (elementList.length > 0) { 355 | elementList.forEach(function (itm) { 356 | if (itm.innerText == "Source Audio" || itm.innerText == "変換元のファイルパス" || itm.innerText == "输入待处理音频文件路径(默认是正确格式示例)" || itm.innerText == "処理対象音声ファイルのパスを入力してください(デフォルトは正しいフォーマットの例です)" || itm.innerText == "Enter the file path of the audio to be processed (default is the correct format example)") { 357 | if (itm.nextElementSibling.type =="textarea"){ 358 | gWavTextarea = itm.nextElementSibling; 359 | } 360 | return; 361 | } 362 | }); 363 | } 364 | if (gWavTextarea == null) { 365 | return; 366 | } 367 | elementList = tab.querySelectorAll("button"); 368 | var InferBtn = null; 369 | if (elementList.length > 0) { 370 | elementList.forEach(function (itm) { 371 | if (itm.innerText == "Infer") { 372 | InferBtn = itm; 373 | return; 374 | } 375 | }); 376 | } 377 | if (InferBtn == null) { 378 | InferBtn = gWavTextarea.parentNode.parentNode.parentNode.parentNode.parentNode.querySelector("button"); 379 | if (InferBtn == null) { 380 | return; 381 | } 382 | } 383 | 384 | InferBtn.addEventListener('click', function (evt) { 385 | inferBtnClicked(); 386 | }); 387 | 388 | let recents = localStorage.getItem("rvc_tweaks_recents"); 389 | if (recents && recents.length > 0){ 390 | gRecentSelectsArray = JSON.parse(recents); 391 | } 392 | 393 | const view = document.createElement('span'); 394 | view.style = "width:20px;"; 395 | // gRecentSelect.innerHTML =''; 396 | updateRecentSelect(); 397 | gWavTextarea.parentNode.insertBefore(gRecentSelect, gWavTextarea); 398 | 399 | gRecentSelect.addEventListener('change', function (evt) { 400 | let val = evt.target.value; 401 | if(val == "x"){ 402 | gRecentSelectsArray = new Array(); 403 | let json = JSON.stringify(gRecentSelectsArray, undefined, 1); 404 | localStorage.setItem("rvc_tweaks_recents", json); 405 | updateRecentSelect(); 406 | }else if(val && val.length>0){ 407 | gWavTextarea.value = val; 408 | //Gradioに変更したことを認識させる 409 | gWavTextarea.dispatchEvent(new Event("input")); 410 | } 411 | gRecentSelect.selectedIndex = -1; 412 | }); 413 | } 414 | function inferBtnClicked(){ 415 | let wavPath = gWavTextarea.value; 416 | addRecentSelect(wavPath); 417 | } 418 | function addRecentSelect(newPath){ 419 | if (!newPath || newPath.length <= 0){ 420 | return; 421 | } 422 | if (gRecentSelectsArray.includes(newPath)) { 423 | gRecentSelectsArray.splice(gRecentSelectsArray.indexOf(newPath), 1); 424 | } 425 | gRecentSelectsArray.unshift(newPath); 426 | if (gRecentSelectsArray.length > 16){ 427 | gRecentSelectsArray.pop(); 428 | } 429 | let json = JSON.stringify(gRecentSelectsArray, undefined, 1); 430 | localStorage.setItem("rvc_tweaks_recents", json); 431 | updateRecentSelect(); 432 | 433 | } 434 | function updateRecentSelect() { 435 | gRecentSelect.replaceChildren(); 436 | gRecentSelectsArray.forEach(function (itm) { 437 | var option = document.createElement("option"); 438 | option.text = itm; 439 | option.value = itm; 440 | gRecentSelect.appendChild(option); 441 | }); 442 | var option = document.createElement("option"); 443 | option.text = gRecentSelectsArray.length > 0 ? "Clear History" : "No History"; 444 | option.value = "x"; 445 | gRecentSelect.appendChild(option); 446 | gRecentSelect.selectedIndex = -1; 447 | } 448 | 449 | 450 | 451 | 452 | const observer = new MutationObserver(records => { 453 | setup(); 454 | }); 455 | const options = { 456 | childList: true, 457 | subtree: true 458 | }; 459 | 460 | let gAppRoot = document.querySelector('gradio-app').shadowRoot; 461 | if (gAppRoot == undefined){ 462 | console.log("shadowRoot is undefined"); 463 | setTimeout(function () { 464 | setup(); 465 | }, 1200); 466 | }else{ 467 | observer.observe(gAppRoot, options); 468 | setTimeout(function () { 469 | //時間がたったら監視停止 470 | observer.disconnect(); 471 | }, 8000); 472 | } 473 | 474 | function addViewToTab(index, title){ 475 | const elementList = gAppRoot.querySelectorAll(".tabitem"); 476 | if (elementList.length > index) { 477 | const tab = elementList[index].childNodes[0]; 478 | const view = document.createElement('div'); 479 | view.classList.add("svelte-10ogue4"); 480 | const label = document.createElement('div'); 481 | label.innerHTML = "

" + title + "

"; 482 | view.insertBefore(label, null); 483 | tab.insertBefore(view, null); 484 | return view; 485 | } 486 | } 487 | function addTextareaToView(view, title){ 488 | const pane = document.createElement('div'); 489 | //pane.classList.add("flex","row", "w-full", "flex-wrap", "gap-4"); 490 | //pane.classList.add("gr-form", "overflow-hidden", "flex", "border-solid", "border", "bg-gray-200", "dark:bg-gray-700", "gap-px", "rounded-lg", "flex-wrap"); 491 | const textarea = document.createElement('textarea'); 492 | textarea.classList.add("scroll-hide", "block", "gr-box", "gr-input", "w-full", "gr-text-input", "svelte-4xt1ch") 493 | textarea.style = "overflow-y: scroll; height: 42px; width:100%"; 494 | 495 | const label = document.createElement('div'); 496 | label.innerHTML = "" + title + ""; 497 | label.classList.add("block", "w-full"); 498 | 499 | label.insertBefore(textarea, null); 500 | pane.insertBefore(label, null); 501 | view.insertBefore(pane, null); 502 | return textarea; 503 | } 504 | 505 | function indexNameFromModelName(modelName) { 506 | //拡張子と末尾の数字を除去 507 | if (modelName.lastIndexOf(".pth") > 0) { 508 | modelName = modelName.substring(0, modelName.lastIndexOf(".pth")); 509 | } 510 | modelName = modelName.replace(/_\d+$/, ""); 511 | return modelName; 512 | } 513 | 514 | })(); 515 | 516 | --------------------------------------------------------------------------------