diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..b76be56 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,3 @@ +/.venv +dist +__pycache__/ diff --git a/.env-docker b/.env-docker new file mode 100644 index 0000000..faf2924 --- /dev/null +++ b/.env-docker @@ -0,0 +1,8 @@ +weight_root=/weights +weight_uvr5_root=/assets/uvr5_weights +index_root=/indices +rmvpe_root=/assets/rmvpe +hubert_path=/assets/hubert_base.pt +save_uvr_path=/assets/uvr5_weights +TEMP=/app/TEMP +pretrained=/assets/pretrained diff --git a/.gitignore b/.gitignore index 46fbe4d..ebdbe07 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +/.venv +/assets dist __pycache__/ *.py[cod] diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..388e4ee --- /dev/null +++ b/Dockerfile @@ -0,0 +1,35 @@ +FROM alpine:3.19.1 as assets + +RUN apk add --update \ + bash \ + git \ + git-lfs + +COPY --chmod=755 ./assets-download.sh /assets-download.sh + +RUN /assets-download.sh 88e42f0cb3662ddc0dd263a4814206ce96d53214 assets + +FROM python:3.10.14-bullseye as app + +SHELL [ "/bin/bash", "-c" ] + +RUN apt update && \ + apt install -y \ + libsndfile1 \ + libsndfile1-dev + +COPY --from=assets /assets /assets + +WORKDIR /app + +RUN pip install "poetry==1.7.1" + +COPY ./pyproject.toml . + +RUN poetry install + +COPY ./rvc ./rvc + +COPY ./.env-docker ./.env + +CMD [ "poetry", "run", "poe", "rvc-api" ] diff --git a/README.md b/README.md index c6b5066..a485420 100644 --- a/README.md +++ b/README.md @@ -140,3 +140,32 @@ curl -X 'POST' \ -F 'modelpath={model.pth}' \ -F 'input={input audio path}' ``` + +### Docker Usage + +Build and run via script: + +```bash +./docker-run.sh +``` + +**Or** use manually: + +1. Build: + + ```bash + docker build -t "rvc" . + ``` + +2. Run: + + ```bash + docker run -it \ + -p 8000:8000 \ + -v "${PWD}/assets/weights:/weights:ro" \ + -v "${PWD}/assets/indices:/indices:ro" \ + -v "${PWD}/assets/audios:/audios:ro" \ + "rvc" + ``` + +Notice assumption that weights, indices and input audios are stored in `current-directory/assets` diff --git a/api-request.sh b/api-request.sh new file mode 100755 index 0000000..41979eb --- /dev/null +++ b/api-request.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env bash +# +# Runs request to RVC API on localhost. + +set -e + +host="http://127.0.0.1:8000" + +url="${host}/inference" + +url+="?res_type=json" + +model_path="" +index_path="" +input_audio="" +output_audio_suffix="" + +while [ $# -gt 0 ]; do + if [ "$1" == "--model_path" ]; then + model_path="$2" + elif [ "$1" == "--index_file" ]; then + index_path="$2" + elif [ "$1" == "--input_audio" ]; then + input_audio="$2" + else + arg_name="${1#--}" + arg_value="$2" + + url+="&${arg_name}=${arg_value}" + output_audio_suffix+="-${arg_name}_${arg_value}" + fi + + shift + shift +done + +model_path_base="$(basename "${model_path}")" +model_path_base_without_ext="${model_path_base%.*}" +index_path_base="$(basename "${index_path}")" +input_audio_base="$(basename "${input_audio}")" +input_audio_dirname="$(dirname "${input_audio}")" +output_audio_base_without_ext="${input_audio_base%.*}" +output_audio="${input_audio_dirname}/${output_audio_base_without_ext}-${model_path_base_without_ext}${output_audio_suffix}.wav" + +cp "${model_path}" "./assets/weights/${model_path_base}" +cp "${input_audio}" "./assets/audios/${input_audio_base}" + +if [ -f "${index_path}" ]; then + url+="&index_file=${index_path_base}" + cp "${index_path}" "./assets/indices/${index_path_base}" +fi + +curl -X "POST" "${url}" \ + -H "accept: application/json" \ + -H "Content-Type: multipart/form-data" \ + -F "modelpath=${model_path_base}" \ + -F "input_audio=/audios/${input_audio_base}" \ +| jq -r '.audio' \ +| base64 -d > "${output_audio}" diff --git a/assets-download.sh b/assets-download.sh new file mode 100644 index 0000000..aa745b4 --- /dev/null +++ b/assets-download.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +# +# Downloads required large files for RVC. + +set -e + +REPO_FOLDER="VoiceConversionWebUI" + +assets_commit_hash="$1" +assets_dir="$2" + +export GIT_CLONE_PROTECTION_ACTIVE=false +export GIT_LFS_SKIP_SMUDGE=1 + +git clone https://huggingface.co/lj1995/VoiceConversionWebUI "${REPO_FOLDER}" + +pushd "${REPO_FOLDER}" + +git checkout "${assets_commit_hash}" + +unset GIT_LFS_SKIP_SMUDGE +unset GIT_CLONE_PROTECTION_ACTIVE + +git lfs pull --include="hubert_base.pt" +git lfs pull --include="pretrained" +git lfs pull --include="uvr5_weights" +git lfs pull --include="rmvpe.pt" +git lfs pull --include="rmvpe.onnx" + +popd + +mkdir -p "${assets_dir}" + +cp "${REPO_FOLDER}/hubert_base.pt" "${assets_dir}/hubert_base.pt" + +mkdir -p "${assets_dir}/rmvpe" + +cp "${REPO_FOLDER}/rmvpe.pt" "${assets_dir}/rmvpe/rmvpe.pt" +cp "${REPO_FOLDER}/rmvpe.onnx" "${assets_dir}/rmvpe/rmvpe.onnx" + +cp -r "${REPO_FOLDER}/pretrained" "${assets_dir}/pretrained" +cp -r "${REPO_FOLDER}/uvr5_weights" "${assets_dir}/uvr5_weights" diff --git a/docker-run.sh b/docker-run.sh new file mode 100755 index 0000000..a9bf5a7 --- /dev/null +++ b/docker-run.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +# +# Runs RVC API in Docker. + +set -e + +tag="rvc" + +docker build -t "${tag}" . + +docker run -it \ + -p 8000:8000 \ + -v "${PWD}/assets/weights:/weights:ro" \ + -v "${PWD}/assets/indices:/indices:ro" \ + -v "${PWD}/assets/audios:/audios:ro" \ + "${tag}" diff --git a/pyproject.toml b/pyproject.toml index 86be1cc..fdc1164 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,9 +25,9 @@ pydub = "^0.25.1" click = "^8.1.7" tensorboardx = "^2.6.2.2" poethepoet = "^0.24.4" -uvicorn = {version = "^0.26.0", optional=true} +uvicorn = "^0.26.0" fastapi = "^0.109.0" -python-multipart = {version = "^0.0.6", optional=true} +python-multipart = "^0.0.6" numba = "0.59.0rc1" [tool.poetry.extras] @@ -37,7 +37,7 @@ api = ["uvicorn", "fastapi"] rvc = "rvc.wrapper.cli.cli:main" [tool.poe.tasks] -rvc-api = "uvicorn rvc.wrapper.api.api:app --reload" +rvc-api = "uvicorn rvc.wrapper.api.api:app --host 0.0.0.0 --port 8000 --reload" [build-system] requires = ["poetry-core"]