{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "0pKllbPyK_BC"
   },
   "source": [
    "## **Applio NoUI**\n",
    "A simple, high-quality voice conversion tool focused on ease of use and performance. \n",
    "\n",
    "[Support](https://discord.gg/urxFjYmYYh) — [GitHub](https://github.com/IAHispano/Applio)\n",
    "\n",
    "<br>\n",
    "\n",
    "### **Credits**\n",
    "- Encryption method: [Hina](https://github.com/hinabl)\n",
    "- Extra section: [Poopmaster](https://github.com/poiqazwsx)\n",
    "- Main development: [Applio Team](https://github.com/IAHispano)\n",
    "- Colab inspired on [RVC v2 Disconnected](https://colab.research.google.com/drive/1XIPCP9ken63S7M6b5ui1b36Cs17sP-NS)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "Y-iR3WeLMlac"
   },
   "source": [
    "### If you restart the runtime, run it again."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "xwZkZGd-H0zT"
   },
   "outputs": [],
   "source": [
    "%cd /content/Applio"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "ymMCTSD6m8qV"
   },
   "source": [
    "# Installation\n",
    "## If the runtime restarts, run the cell above and re-run the installation steps."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "cellView": "form",
    "id": "yFhAeKGOp9aa"
   },
   "outputs": [],
   "source": [
    "# @title Mount Google Drive\n",
    "from google.colab import drive\n",
    "\n",
    "drive.mount(\"/content/drive\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "cellView": "form",
    "id": "7GysECSxBya4"
   },
   "outputs": [],
   "source": [
    "# @title Clone\n",
    "!git clone https://github.com/IAHispano/Applio --branch 3.2.8-bugfix --single-branch\n",
    "%cd /content/Applio"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "cellView": "form",
    "id": "CAXW55BQm0PP"
   },
   "outputs": [],
   "source": [
    "# @title Install\n",
    "rot_47 = lambda encoded_text: \"\".join(\n",
    "    [\n",
    "        (\n",
    "            chr(\n",
    "                (ord(c) - (ord(\"a\") if c.islower() else ord(\"A\")) - 47) % 26\n",
    "                + (ord(\"a\") if c.islower() else ord(\"A\"))\n",
    "            )\n",
    "            if c.isalpha()\n",
    "            else c\n",
    "        )\n",
    "        for c in encoded_text\n",
    "    ]\n",
    ")\n",
    "import codecs\n",
    "import os\n",
    "import tarfile\n",
    "import subprocess\n",
    "from pathlib import Path\n",
    "from IPython.display import clear_output\n",
    "\n",
    "def vidal_setup(C):\n",
    "    def F():\n",
    "        print(\"Installing pip packages...\")\n",
    "        subprocess.check_call([\"pip\", \"install\", \"-r\", \"requirements.txt\", \"--quiet\"])\n",
    "\n",
    "    A = \"/content/\" + rot_47(\"Kikpm.ovm.bu\")\n",
    "    D = \"/\"\n",
    "    if not os.path.exists(A):\n",
    "        M = os.path.dirname(A)\n",
    "        os.makedirs(M, exist_ok=True)\n",
    "        print(\"No cached install found..\")\n",
    "        try:\n",
    "            N = codecs.decode(\n",
    "                    \"uggcf://uhttvatsnpr.pb/VNUvfcnab/Nccyvb/erfbyir/znva/Raivebzrag/Pbyno/Cache.gne.tm\",\n",
    "                    \"rot_13\",\n",
    "                )\n",
    "            subprocess.run([\"wget\", \"-O\", A, N])\n",
    "            print(\"Download completed successfully!\")\n",
    "        except Exception as H:\n",
    "            print(str(H))\n",
    "            if os.path.exists(A):\n",
    "                os.remove(A)\n",
    "    if Path(A).exists():\n",
    "        with tarfile.open(A, \"r:gz\") as I:\n",
    "            I.extractall(D)\n",
    "            print(f\"Extraction of {A} to {D} completed.\")\n",
    "        if os.path.exists(A):\n",
    "            os.remove(A)\n",
    "        if C:\n",
    "            F()\n",
    "            C = False\n",
    "    else:\n",
    "        F()\n",
    "\n",
    "\n",
    "vidal_setup(False)\n",
    "!pip uninstall torch torchvision torchaudio -y\n",
    "!pip install torch==2.3.1 torchvision==0.18.1 torchaudio==2.3.1 --upgrade --index-url https://download.pytorch.org/whl/cu121\n",
    "clear_output()\n",
    "print(\"Finished installing requirements!\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "cellView": "form",
    "id": "QlTibPnjmj6-"
   },
   "outputs": [],
   "source": [
    "# @title Download models\n",
    "!python core.py \"prerequisites\" --models \"True\" --exe \"True\" --pretraineds_v1_f0 \"False\" --pretraineds_v2_f0 \"True\" --pretraineds_v1_nof0 \"False\" --pretraineds_v2_nof0 \"False\" "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "YzaeMYsUE97Y"
   },
   "source": [
    "# Infer\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "cellView": "form",
    "id": "v0EgikgjFCjE"
   },
   "outputs": [],
   "source": [
    "# @title Download model\n",
    "# @markdown Hugging Face or Google Drive\n",
    "model_link = \"https://huggingface.co/Darwin/Darwin/resolve/main/Darwin.zip\"  # @param {type:\"string\"}\n",
    "\n",
    "!python core.py download --model_link \"{model_link}\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "cellView": "form",
    "id": "lrCKEOzvDPRu"
   },
   "outputs": [],
   "source": [
    "# @title Run Inference\n",
    "# @markdown Please upload the audio file to your Google Drive path `/content/drive/MyDrive` and specify its name here. For the model name, use the zip file name without the extension. Alternatively, you can check the path `/content/Applio/logs` for the model name (name of the folder).\n",
    "\n",
    "import os\n",
    "\n",
    "current_dir = os.getcwd()\n",
    "\n",
    "model_name = \"Darwin\"  # @param {type:\"string\"}\n",
    "model_folder = os.path.join(current_dir, f\"logs/{model_name}\")\n",
    "\n",
    "if not os.path.exists(model_folder):\n",
    "    raise FileNotFoundError(f\"Model directory not found: {model_folder}\")\n",
    "\n",
    "files_in_folder = os.listdir(model_folder)\n",
    "pth_path = next((f for f in files_in_folder if f.endswith(\".pth\")), None)\n",
    "index_file = next((f for f in files_in_folder if f.endswith(\".index\")), None)\n",
    "\n",
    "if pth_path is None or index_file is None:\n",
    "    raise FileNotFoundError(\"No model found.\")\n",
    "\n",
    "pth_file = os.path.join(model_folder, pth_path)\n",
    "index_file = os.path.join(model_folder, index_file)\n",
    "\n",
    "input_path = \"/content/example.wav\"  # @param {type:\"string\"}\n",
    "output_path = \"/content/output.wav\"\n",
    "export_format = \"WAV\"  # @param ['WAV', 'MP3', 'FLAC', 'OGG', 'M4A'] {allow-input: false}\n",
    "f0_method = \"rmvpe\"  # @param [\"crepe\", \"crepe-tiny\", \"rmvpe\", \"fcpe\", \"hybrid[rmvpe+fcpe]\"] {allow-input: false}\n",
    "f0_up_key = 0  # @param {type:\"slider\", min:-24, max:24, step:0}\n",
    "filter_radius = 3  # @param {type:\"slider\", min:0, max:10, step:0}\n",
    "rms_mix_rate = 0.8  # @param {type:\"slider\", min:0.0, max:1.0, step:0.1}\n",
    "protect = 0.5  # @param {type:\"slider\", min:0.0, max:0.5, step:0.1}\n",
    "index_rate = 0.7  # @param {type:\"slider\", min:0.0, max:1.0, step:0.1}\n",
    "hop_length = 128  # @param {type:\"slider\", min:1, max:512, step:0}\n",
    "clean_strength = 0.7  # @param {type:\"slider\", min:0.0, max:1.0, step:0.1}\n",
    "split_audio = False  # @param{type:\"boolean\"}\n",
    "clean_audio = False  # @param{type:\"boolean\"}\n",
    "f0_autotune = False  # @param{type:\"boolean\"}\n",
    "formant_shift = False # @param{type:\"boolean\"}\n",
    "formant_qfrency = 1.0 # @param {type:\"slider\", min:1.0, max:16.0, step:0.1}\n",
    "formant_timbre = 1.0 # @param {type:\"slider\", min:1.0, max:16.0, step:0.1}\n",
    "embedder_model = \"contentvec\" # @param [\"contentvec\", \"chinese-hubert-base\", \"japanese-hubert-base\", \"korean-hubert-base\", \"custom\"] {allow-input: false}\n",
    "embedder_model_custom = \"\" # @param {type:\"string\"}\n",
    "\n",
    "\n",
    "# Post-processing effects\n",
    "if \"post_process\" not in globals():\n",
    "  post_process = False \n",
    "if \"reverb\" not in globals():\n",
    "  reverb = False \n",
    "if \"pitch_shift\" not in globals():\n",
    "  pitch_shift = False \n",
    "if \"limiter\" not in globals():\n",
    "  limiter = False \n",
    "if \"gain\" not in globals():\n",
    "  gain = False \n",
    "if \"distortion\" not in globals():\n",
    "  distortion = False \n",
    "if \"chorus\" not in globals():\n",
    "  chorus = False \n",
    "if \"bitcrush\" not in globals():\n",
    "  bitcrush = False\n",
    "if \"clipping\" not in globals():\n",
    "  clipping = False \n",
    "if \"compressor\" not in globals():\n",
    "  compressor = False \n",
    "if \"delay\" not in globals():\n",
    "  delay = False\n",
    "\n",
    "if \"reverb_room_size\" not in globals():\n",
    "  reverb_room_size = 0.5 \n",
    "if \"reverb_damping\" not in globals():\n",
    "  reverb_damping = 0.5 \n",
    "if \"reverb_wet_gain\" not in globals():\n",
    "  reverb_wet_gain = 0.0 \n",
    "if \"reverb_dry_gain\" not in globals():\n",
    "  reverb_dry_gain = 0.0 \n",
    "if \"reverb_width\" not in globals():\n",
    "  reverb_width = 1.0 \n",
    "if \"reverb_freeze_mode\" not in globals():\n",
    "  reverb_freeze_mode = 0.0 \n",
    "\n",
    "if \"pitch_shift_semitones\" not in globals():\n",
    "  pitch_shift_semitones = 0.0 \n",
    "\n",
    "if \"limiter_threshold\" not in globals():\n",
    "  limiter_threshold = -1.0 \n",
    "if \"limiter_release_time\" not in globals():\n",
    "  limiter_release_time = 0.05 \n",
    "\n",
    "if \"gain_db\" not in globals():\n",
    "  gain_db = 0.0 \n",
    "\n",
    "if \"distortion_gain\" not in globals():\n",
    "  distortion_gain = 0.0 \n",
    "\n",
    "if \"chorus_rate\" not in globals():\n",
    "  chorus_rate = 1.5 \n",
    "if \"chorus_depth\" not in globals():\n",
    "  chorus_depth = 0.1 \n",
    "if \"chorus_center_delay\" not in globals():\n",
    "  chorus_center_delay = 15.0 \n",
    "if \"chorus_feedback\" not in globals():\n",
    "  chorus_feedback = 0.25 \n",
    "if \"chorus_mix\" not in globals():\n",
    "  chorus_mix = 0.5 \n",
    "\n",
    "if \"bitcrush_bit_depth\" not in globals():\n",
    "  bitcrush_bit_depth = 4 \n",
    "\n",
    "if \"clipping_threshold\" not in globals():\n",
    "  clipping_threshold = 0.5 \n",
    "\n",
    "if \"compressor_threshold\" not in globals():\n",
    "  compressor_threshold = -20.0\n",
    "if \"compressor_ratio\" not in globals():\n",
    "  compressor_ratio = 4.0 \n",
    "if \"compressor_attack\" not in globals():\n",
    "  compressor_attack = 0.001 \n",
    "if \"compressor_release\" not in globals():\n",
    "  compressor_release = 0.1 \n",
    "\n",
    "if \"delay_seconds\" not in globals():\n",
    "  delay_seconds = 0.1\n",
    "if \"delay_feedback\" not in globals():\n",
    "  delay_feedback = 0.5 \n",
    "if \"delay_mix\" not in globals():\n",
    "  delay_mix = 0.5 \n",
    "  \n",
    "!python core.py infer --pitch \"{f0_up_key}\" --filter_radius \"{filter_radius}\" --volume_envelope \"{rms_mix_rate}\" --index_rate \"{index_rate}\" --hop_length \"{hop_length}\" --protect \"{protect}\" --f0_autotune \"{f0_autotune}\" --f0_method \"{f0_method}\" --input_path \"{input_path}\" --output_path \"{output_path}\" --pth_path \"{pth_file}\" --index_path \"{index_file}\" --split_audio \"{split_audio}\" --clean_audio \"{clean_audio}\" --clean_strength \"{clean_strength}\" --export_format \"{export_format}\" --embedder_model \"{embedder_model}\" --embedder_model_custom \"{embedder_model_custom}\" --formant_shifting \"{formant_shift}\" --formant_qfrency \"{formant_qfrency}\" --formant_timbre \"{formant_timbre}\" --post_process \"{post_process}\" --reverb \"{reverb}\" --pitch_shift \"{pitch_shift}\" --limiter \"{limiter}\" --gain \"{gain}\" --distortion \"{distortion}\" --chorus \"{chorus}\" --bitcrush \"{bitcrush}\" --clipping \"{clipping}\" --compressor \"{compressor}\" --delay \"{delay}\" --reverb_room_size \"{reverb_room_size}\" --reverb_damping \"{reverb_damping}\" --reverb_wet_gain \"{reverb_wet_gain}\" --reverb_dry_gain \"{reverb_dry_gain}\" --reverb_width \"{reverb_width}\" --reverb_freeze_mode \"{reverb_freeze_mode}\" --pitch_shift_semitones \"{pitch_shift_semitones}\" --limiter_threshold \"{limiter_threshold}\" --limiter_release_time \"{limiter_release_time}\" --gain_db \"{gain_db}\" --distortion_gain \"{distortion_gain}\" --chorus_rate \"{chorus_rate}\" --chorus_depth \"{chorus_depth}\" --chorus_center_delay \"{chorus_center_delay}\" --chorus_feedback \"{chorus_feedback}\" --chorus_mix \"{chorus_mix}\" --bitcrush_bit_depth \"{bitcrush_bit_depth}\" --clipping_threshold \"{clipping_threshold}\" --compressor_threshold \"{compressor_threshold}\" --compressor_ratio \"{compressor_ratio}\" --compressor_attack \"{compressor_attack}\" --compressor_release \"{compressor_release}\" --delay_seconds \"{delay_seconds}\" --delay_feedback \"{delay_feedback}\" --delay_mix \"{delay_mix}\"\n",
    "\n",
    "from IPython.display import Audio, display, clear_output\n",
    "\n",
    "output_path = output_path.replace(\".wav\", f\".{export_format.lower()}\")\n",
    "# clear_output()\n",
    "display(Audio(output_path, autoplay=True))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "yrWw2h9d2TRn"
   },
   "source": [
    "## **Advanced Settings**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "cellView": "form",
    "id": "J43qejJ-2Tpp"
   },
   "outputs": [],
   "source": [
    "# @title # Post-processing effects\n",
    "post_process = False # @param{type:\"boolean\"}\n",
    "reverb = False # @param{type:\"boolean\"}\n",
    "pitch_shift = False # @param{type:\"boolean\"}\n",
    "limiter = False # @param{type:\"boolean\"}\n",
    "gain = False # @param{type:\"boolean\"}\n",
    "distortion = False # @param{type:\"boolean\"}\n",
    "chorus = False # @param{type:\"boolean\"}\n",
    "bitcrush = False # @param{type:\"boolean\"}\n",
    "clipping = False # @param{type:\"boolean\"}\n",
    "compressor = False # @param{type:\"boolean\"}\n",
    "delay = False # @param{type:\"boolean\"}\n",
    "\n",
    "reverb_room_size = 0.5 # @param {type:\"slider\", min:0.0, max:1.0, step:0.1}\n",
    "reverb_damping = 0.5 # @param {type:\"slider\", min:0.0, max:1.0, step:0.1}\n",
    "reverb_wet_gain = 0.0 # @param {type:\"slider\", min:-20.0, max:20.0, step:0.1}\n",
    "reverb_dry_gain = 0.0 # @param {type:\"slider\", min:-20.0, max:20.0, step:0.1}\n",
    "reverb_width = 1.0 # @param {type:\"slider\", min:0.0, max:1.0, step:0.1}\n",
    "reverb_freeze_mode = 0.0 # @param {type:\"slider\", min:0.0, max:1.0, step:0.1}\n",
    "\n",
    "pitch_shift_semitones = 0.0 # @param {type:\"slider\", min:-12.0, max:12.0, step:0.1}\n",
    "\n",
    "limiter_threshold = -1.0 # @param {type:\"slider\", min:-20.0, max:0.0, step:0.1}\n",
    "limiter_release_time = 0.05 # @param {type:\"slider\", min:0.0, max:1.0, step:0.01}\n",
    "\n",
    "gain_db = 0.0 # @param {type:\"slider\", min:-20.0, max:20.0, step:0.1}\n",
    "\n",
    "distortion_gain = 0.0 # @param {type:\"slider\", min:0.0, max:1.0, step:0.1}\n",
    "\n",
    "chorus_rate = 1.5 # @param {type:\"slider\", min:0.1, max:10.0, step:0.1}\n",
    "chorus_depth = 0.1 # @param {type:\"slider\", min:0.0, max:1.0, step:0.1}\n",
    "chorus_center_delay = 15.0 # @param {type:\"slider\", min:0.0, max:50.0, step:0.1}\n",
    "chorus_feedback = 0.25 # @param {type:\"slider\", min:0.0, max:1.0, step:0.1}\n",
    "chorus_mix = 0.5 # @param {type:\"slider\", min:0.0, max:1.0, step:0.1}\n",
    "\n",
    "bitcrush_bit_depth = 4 # @param {type:\"slider\", min:1, max:16, step:1}\n",
    "\n",
    "clipping_threshold = 0.5 # @param {type:\"slider\", min:0.0, max:1.0, step:0.1}\n",
    "\n",
    "compressor_threshold = -20.0 # @param {type:\"slider\", min:-60.0, max:0.0, step:0.1}\n",
    "compressor_ratio = 4.0 # @param {type:\"slider\", min:1.0, max:20.0, step:0.1}\n",
    "compressor_attack = 0.001 # @param {type:\"slider\", min:0.0, max:0.1, step:0.001}\n",
    "compressor_release = 0.1 # @param {type:\"slider\", min:0.0, max:1.0, step:0.01}\n",
    "\n",
    "delay_seconds = 0.1 # @param {type:\"slider\", min:0.0, max:1.0, step:0.01}\n",
    "delay_feedback = 0.5 # @param {type:\"slider\", min:0.0, max:1.0, step:0.1}\n",
    "delay_mix = 0.5 # @param {type:\"slider\", min:0.0, max:1.0, step:0.1}\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "1QkabnLlF2KB"
   },
   "source": [
    "# Train"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "cellView": "form",
    "id": "oBzqm4JkGGa0"
   },
   "outputs": [],
   "source": [
    "# @title Preprocess Dataset\n",
    "import os\n",
    "os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'\n",
    "model_name = \"Darwin\"  # @param {type:\"string\"}\n",
    "dataset_path = \"/content/drive/MyDrive/Darwin_Dataset\"  # @param {type:\"string\"}\n",
    "\n",
    "sample_rate = \"40k\"  # @param [\"32k\", \"40k\", \"48k\"] {allow-input: false}\n",
    "sr = int(sample_rate.rstrip(\"k\")) * 1000\n",
    "cpu_cores = 2 # @param {type:\"slider\", min:1, max:2, step:1}\n",
    "cut_preprocess = True # @param{type:\"boolean\"}\n",
    "process_effects = False # @param{type:\"boolean\"}\n",
    "noise_reduction = False # @param{type:\"boolean\"}\n",
    "noise_reduction_strength = 0.7 # @param {type:\"slider\", min:0.0, max:1.0, step:0.1}\n",
    "\n",
    "!python core.py preprocess --model_name \"{model_name}\" --dataset_path \"{dataset_path}\" --sample_rate \"{sr}\" --cpu_cores \"{cpu_cores}\" --cut_preprocess \"{cut_preprocess}\" --process_effects \"{process_effects}\" --noise_reduction \"{noise_reduction}\" --noise_reduction_strength \"{noise_reduction_strength}\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "cellView": "form",
    "id": "zWMiMYfRJTJv"
   },
   "outputs": [],
   "source": [
    "# @title Extract Features\n",
    "f0_method = \"rmvpe\"  # @param [\"crepe\", \"crepe-tiny\", \"rmvpe\"] {allow-input: false}\n",
    "hop_length = 128  # @param {type:\"slider\", min:1, max:512, step:0}\n",
    "\n",
    "sr = int(sample_rate.rstrip(\"k\")) * 1000\n",
    "cpu_cores = 2 # @param {type:\"slider\", min:1, max:2, step:1}\n",
    "embedder_model = \"contentvec\" # @param [\"contentvec\", \"chinese-hubert-base\", \"japanese-hubert-base\", \"korean-hubert-base\", \"custom\"] {allow-input: false}\n",
    "embedder_model_custom = \"\" # @param {type:\"string\"}\n",
    "\n",
    "!python core.py extract --model_name \"{model_name}\" --f0_method \"{f0_method}\" --hop_length \"{hop_length}\" --sample_rate \"{sr}\" --cpu_cores \"{cpu_cores}\" --gpu \"0\" --embedder_model \"{embedder_model}\" --embedder_model_custom \"{embedder_model_custom}\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "cellView": "form",
    "id": "TI6LLdIzKAIa"
   },
   "outputs": [],
   "source": [
    "# @title Train\n",
    "import threading\n",
    "import time\n",
    "import os\n",
    "import shutil\n",
    "import hashlib\n",
    "import time\n",
    "\n",
    "LOGS_FOLDER = \"/content/Applio/logs/\"\n",
    "GOOGLE_DRIVE_PATH = \"/content/drive/MyDrive/RVC_Backup\"\n",
    "\n",
    "\n",
    "def import_google_drive_backup():\n",
    "    print(\"Importing Google Drive backup...\")\n",
    "    for root, dirs, files in os.walk(GOOGLE_DRIVE_PATH):\n",
    "        for filename in files:\n",
    "            filepath = os.path.join(root, filename)\n",
    "            if os.path.isfile(filepath):\n",
    "                backup_filepath = os.path.join(\n",
    "                    LOGS_FOLDER, os.path.relpath(filepath, GOOGLE_DRIVE_PATH)\n",
    "                )\n",
    "                backup_folderpath = os.path.dirname(backup_filepath)\n",
    "                if not os.path.exists(backup_folderpath):\n",
    "                    os.makedirs(backup_folderpath)\n",
    "                    print(f\"Created backup folder: {backup_folderpath}\", flush=True)\n",
    "                shutil.copy2(filepath, backup_filepath)\n",
    "                print(f\"Imported file from Google Drive backup: {filename}\")\n",
    "    print(\"Google Drive backup import completed.\")\n",
    "\n",
    "\n",
    "def get_md5_hash(file_path):\n",
    "    hash_md5 = hashlib.md5()\n",
    "    with open(file_path, \"rb\") as f:\n",
    "        for chunk in iter(lambda: f.read(4096), b\"\"):\n",
    "            hash_md5.update(chunk)\n",
    "    return hash_md5.hexdigest()\n",
    "\n",
    "\n",
    "if \"autobackups\" not in globals():\n",
    "    autobackups = False\n",
    "# @markdown ### 💾 AutoBackup\n",
    "cooldown = 15  # @param {type:\"slider\", min:0, max:100, step:0}\n",
    "auto_backups = True  # @param{type:\"boolean\"}\n",
    "def backup_files():\n",
    "    print(\"\\nStarting backup loop...\")\n",
    "    last_backup_timestamps_path = os.path.join(\n",
    "        LOGS_FOLDER, \"last_backup_timestamps.txt\"\n",
    "    )\n",
    "    fully_updated = False\n",
    "\n",
    "    while True:\n",
    "        try:\n",
    "            updated_files = 0\n",
    "            deleted_files = 0\n",
    "            new_files = 0\n",
    "            last_backup_timestamps = {}\n",
    "\n",
    "            try:\n",
    "                with open(last_backup_timestamps_path, \"r\") as f:\n",
    "                    last_backup_timestamps = dict(line.strip().split(\":\") for line in f)\n",
    "            except FileNotFoundError:\n",
    "                pass\n",
    "\n",
    "            for root, dirs, files in os.walk(LOGS_FOLDER):\n",
    "                # Excluding \"zips\" and \"mute\" directories\n",
    "                if \"zips\" in dirs:\n",
    "                    dirs.remove(\"zips\")\n",
    "                if \"mute\" in dirs:\n",
    "                    dirs.remove(\"mute\")\n",
    "\n",
    "                for filename in files:\n",
    "                    if filename != \"last_backup_timestamps.txt\":\n",
    "                        filepath = os.path.join(root, filename)\n",
    "                        if os.path.isfile(filepath):\n",
    "                            backup_filepath = os.path.join(\n",
    "                                GOOGLE_DRIVE_PATH,\n",
    "                                os.path.relpath(filepath, LOGS_FOLDER),\n",
    "                            )\n",
    "                            backup_folderpath = os.path.dirname(backup_filepath)\n",
    "                            if not os.path.exists(backup_folderpath):\n",
    "                                os.makedirs(backup_folderpath)\n",
    "                            last_backup_timestamp = last_backup_timestamps.get(filepath)\n",
    "                            current_timestamp = os.path.getmtime(filepath)\n",
    "                            if (\n",
    "                                last_backup_timestamp is None\n",
    "                                or float(last_backup_timestamp) < current_timestamp\n",
    "                            ):\n",
    "                                shutil.copy2(filepath, backup_filepath)\n",
    "                                last_backup_timestamps[filepath] = str(current_timestamp)\n",
    "                                if last_backup_timestamp is None:\n",
    "                                    new_files += 1\n",
    "                                else:\n",
    "                                    updated_files += 1\n",
    "\n",
    "\n",
    "            for filepath in list(last_backup_timestamps.keys()):\n",
    "                if not os.path.exists(filepath):\n",
    "                    backup_filepath = os.path.join(\n",
    "                        GOOGLE_DRIVE_PATH, os.path.relpath(filepath, LOGS_FOLDER)\n",
    "                    )\n",
    "                    if os.path.exists(backup_filepath):\n",
    "                        os.remove(backup_filepath)\n",
    "                        deleted_files += 1\n",
    "                    del last_backup_timestamps[filepath]\n",
    "\n",
    "\n",
    "            if updated_files > 0 or deleted_files > 0 or new_files > 0:\n",
    "                print(f\"Backup Complete: {new_files} new, {updated_files} updated, {deleted_files} deleted.\")\n",
    "                fully_updated = False\n",
    "            elif not fully_updated:\n",
    "                print(\"Files are up to date.\")\n",
    "                fully_updated = True\n",
    "\n",
    "            with open(last_backup_timestamps_path, \"w\") as f:\n",
    "                for filepath, timestamp in last_backup_timestamps.items():\n",
    "                    f.write(f\"{filepath}:{timestamp}\\n\")\n",
    "\n",
    "            time.sleep(cooldown if fully_updated else 0.1)\n",
    "\n",
    "\n",
    "        except Exception as error:\n",
    "            print(f\"An error occurred during backup: {error}\")\n",
    "\n",
    "\n",
    "if autobackups:\n",
    "    autobackups = False\n",
    "    print(\"Autobackup Disabled\")\n",
    "else:\n",
    "    autobackups = True\n",
    "    print(\"Autobackup Enabled\") \n",
    "# @markdown ### ⚙️ Train Settings\n",
    "total_epoch = 800  # @param {type:\"integer\"}\n",
    "batch_size = 15  # @param {type:\"slider\", min:1, max:25, step:0}\n",
    "gpu = 0\n",
    "sr = int(sample_rate.rstrip(\"k\")) * 1000\n",
    "pretrained = True  # @param{type:\"boolean\"}\n",
    "cleanup = False  # @param{type:\"boolean\"}\n",
    "cache_data_in_gpu = False  # @param{type:\"boolean\"}\n",
    "tensorboard = True  # @param{type:\"boolean\"}\n",
    "# @markdown ### ➡️ Choose how many epochs your model will be stored\n",
    "save_every_epoch = 10  # @param {type:\"slider\", min:1, max:100, step:0}\n",
    "save_only_latest = False  # @param{type:\"boolean\"}\n",
    "save_every_weights = False  # @param{type:\"boolean\"}\n",
    "overtraining_detector = False  # @param{type:\"boolean\"}\n",
    "overtraining_threshold = 50  # @param {type:\"slider\", min:1, max:100, step:0}\n",
    "# @markdown ### ❓ Optional\n",
    "# @markdown In case you select custom pretrained, you will have to download the pretraineds and enter the path of the pretraineds.\n",
    "custom_pretrained = False  # @param{type:\"boolean\"}\n",
    "g_pretrained_path = \"/content/Applio/rvc/models/pretraineds/pretraineds_custom/G48k.pth\"  # @param {type:\"string\"}\n",
    "d_pretrained_path = \"/content/Applio/rvc/models/pretraineds/pretraineds_custom/D48k.pth\"  # @param {type:\"string\"}\n",
    "\n",
    "if \"pretrained\" not in globals():\n",
    "    pretrained = True\n",
    "\n",
    "if \"custom_pretrained\" not in globals():\n",
    "    custom_pretrained = False\n",
    "\n",
    "if \"g_pretrained_path\" not in globals():\n",
    "    g_pretrained_path = \"Custom Path\"\n",
    "\n",
    "if \"d_pretrained_path\" not in globals():\n",
    "    d_pretrained_path = \"Custom Path\"\n",
    "\n",
    "\n",
    "def start_train():\n",
    "    if tensorboard == True:\n",
    "        %load_ext tensorboard\n",
    "        %tensorboard --logdir /content/Applio/logs/\n",
    "    !python core.py train --model_name \"{model_name}\" --save_every_epoch \"{save_every_epoch}\" --save_only_latest \"{save_only_latest}\" --save_every_weights \"{save_every_weights}\" --total_epoch \"{total_epoch}\" --sample_rate \"{sr}\" --batch_size \"{batch_size}\" --gpu \"{gpu}\" --pretrained \"{pretrained}\" --custom_pretrained \"{custom_pretrained}\" --g_pretrained_path \"{g_pretrained_path}\" --d_pretrained_path \"{d_pretrained_path}\" --overtraining_detector \"{overtraining_detector}\" --overtraining_threshold \"{overtraining_threshold}\" --cleanup \"{cleanup}\" --cache_data_in_gpu \"{cache_data_in_gpu}\"\n",
    "\n",
    "\n",
    "server_thread = threading.Thread(target=start_train)\n",
    "server_thread.start()\n",
    "\n",
    "if auto_backups:\n",
    "    backup_files()\n",
    "else:\n",
    "    while True:\n",
    "        time.sleep(10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "cellView": "form",
    "id": "bHLs5AT4Q1ck"
   },
   "outputs": [],
   "source": [
    "# @title Generate index file\n",
    "index_algorithm = \"Auto\"  # @param [\"Auto\", \"Faiss\", \"KMeans\"] {allow-input: false}\n",
    "!python core.py index --model_name \"{model_name}\" --index_algorithm \"{index_algorithm}\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "cellView": "form",
    "id": "X_eU_SoiHIQg"
   },
   "outputs": [],
   "source": [
    "# @title Save model\n",
    "# @markdown Enter the name of the model and the steps. You can find it in your `/content/Applio/logs` folder.\n",
    "%cd /content\n",
    "import os, shutil, sys\n",
    "\n",
    "model_name = \"Darwin\"  # @param {type:\"string\"}\n",
    "model_epoch = 800  # @param {type:\"integer\"}\n",
    "save_big_file = False  # @param {type:\"boolean\"}\n",
    "\n",
    "if os.path.exists(\"/content/zips\"):\n",
    "    shutil.rmtree(\"/content/zips\")\n",
    "print(\"Removed zips.\")\n",
    "\n",
    "os.makedirs(f\"/content/zips/{model_name}/\", exist_ok=True)\n",
    "print(\"Created zips.\")\n",
    "\n",
    "logs_folder = f\"/content/Applio/logs/{model_name}/\"\n",
    "weight_file = None\n",
    "if not os.path.exists(logs_folder):\n",
    "    print(f\"Model folder not found.\")\n",
    "    sys.exit(\"\")\n",
    "\n",
    "for filename in os.listdir(logs_folder):\n",
    "    if filename.startswith(f\"{model_name}_{model_epoch}e\") and filename.endswith(\".pth\"):\n",
    "        weight_file = filename\n",
    "        break\n",
    "if weight_file is None:\n",
    "    print(\"There is no weight file with that name\")\n",
    "    sys.exit(\"\")\n",
    "if not save_big_file:\n",
    "    !cp {logs_folder}added_*.index /content/zips/{model_name}/\n",
    "    !cp {logs_folder}total_*.npy /content/zips/{model_name}/\n",
    "    !cp {logs_folder}{weight_file} /content/zips/{model_name}/\n",
    "    %cd /content/zips\n",
    "    !zip -r {model_name}.zip {model_name}\n",
    "if save_big_file:\n",
    "    %cd /content/Applio\n",
    "    latest_steps = -1\n",
    "    logs_folder = \"./logs/\" + model_name\n",
    "    for filename in os.listdir(logs_folder):\n",
    "        if filename.startswith(\"G_\") and filename.endswith(\".pth\"):\n",
    "            steps = int(filename.split(\"_\")[1].split(\".\")[0])\n",
    "            if steps > latest_steps:\n",
    "                latest_steps = steps\n",
    "    MODELZIP = model_name + \".zip\"\n",
    "    !mkdir -p /content/zips\n",
    "    ZIPFILEPATH = os.path.join(\"/content/zips\", MODELZIP)\n",
    "    for filename in os.listdir(logs_folder):\n",
    "        if \"G_\" in filename or \"D_\" in filename:\n",
    "            if str(latest_steps) in filename:\n",
    "                !zip -r {ZIPFILEPATH} {os.path.join(logs_folder, filename)}\n",
    "        else:\n",
    "            !zip -r {ZIPFILEPATH} {os.path.join(logs_folder, filename)}\n",
    "\n",
    "!mkdir -p /content/drive/MyDrive/RVC_Backup/\n",
    "shutil.move(\n",
    "    f\"/content/zips/{model_name}.zip\",\n",
    "    f\"/content/drive/MyDrive/RVC_Backup/{model_name}.zip\",\n",
    ")\n",
    "%cd /content/Applio\n",
    "shutil.rmtree(\"/content/zips\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "OaKoymXsyEYN"
   },
   "source": [
    "# Resume-training"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "cellView": "form",
    "id": "d3KgLAYnyHkP"
   },
   "outputs": [],
   "source": [
    "# @title Load a Backup\n",
    "from google.colab import drive\n",
    "import os\n",
    "import shutil\n",
    "\n",
    "# @markdown Put the exact name you put as your Model Name in Applio.\n",
    "modelname = \"My-Project\"  # @param {type:\"string\"}\n",
    "source_path = \"/content/drive/MyDrive/RVC_Backup/\" + modelname\n",
    "destination_path = \"/content/Applio/logs/\" + modelname\n",
    "backup_timestamps_file = \"last_backup_timestamps.txt\"\n",
    "if not os.path.exists(source_path):\n",
    "    print(\n",
    "        \"The model folder does not exist. Please verify the name is correct or check your Google Drive.\"\n",
    "    )\n",
    "else:\n",
    "    time_ = os.path.join(\"/content/drive/MyDrive/RVC_Backup/\", backup_timestamps_file)\n",
    "    time__ = os.path.join(\"/content/Applio/logs/\", backup_timestamps_file)\n",
    "    if os.path.exists(time_):\n",
    "        shutil.copy(time_, time__)\n",
    "    shutil.copytree(source_path, destination_path)\n",
    "    print(\"Model backup loaded successfully.\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "cellView": "form",
    "id": "sc9DzvRCyJ2d"
   },
   "outputs": [],
   "source": [
    "# @title Set training variables\n",
    "# @markdown ### ➡️ Use the same as you did previously\n",
    "model_name = \"Darwin\"  # @param {type:\"string\"}\n",
    "sample_rate = \"40k\"  # @param [\"32k\", \"40k\", \"48k\"] {allow-input: false}\n",
    "f0_method = \"rmvpe\"  # @param [\"crepe\", \"crepe-tiny\", \"rmvpe\"] {allow-input: false}\n",
    "hop_length = 128  # @param {type:\"slider\", min:1, max:512, step:0}\n",
    "sr = int(sample_rate.rstrip(\"k\")) * 1000"
   ]
  }
 ],
 "metadata": {
  "accelerator": "GPU",
  "colab": {
   "collapsed_sections": [
    "ymMCTSD6m8qV"
   ],
   "provenance": [],
   "toc_visible": true
  },
  "kernelspec": {
   "display_name": "Python 3",
   "name": "python3"
  },
  "language_info": {
   "name": "python"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}