mirror of
https://github.com/index-tts/index-tts.git
synced 2026-07-02 16:37:05 +00:00
Improve model download and config availability handling (#703)
* fix: reuse existing model cache * fix: handle empty config model directory * fix: use uv to run tests * fix cli config fallback after download * clarify cli config recheck * document cli config recheck side effect * refactor: restructure CLI with download/infer subcommands and clean up tests - Add 'indextts download' subcommand with --version 1/2 support - Add 'indextts infer' subcommand using v1 or v2 engine - Replace old test files with test_v1.py and test_v2.py - Add tests/conftest.py to exclude padding_test.py from collection - Update CI to run pytest -m 'not gpu' instead of local_check.py - Update PR template with simplified checklist --------- Co-authored-by: nanaoto <10inspiral@gmail.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
This commit is contained in:
parent
800532f010
commit
2210a0a409
14 changed files with 705 additions and 133 deletions
30
.github/pull_request_template.md
vendored
Normal file
30
.github/pull_request_template.md
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
## Description
|
||||
|
||||
<!-- Briefly describe what this PR does and why. -->
|
||||
|
||||
## Type of change
|
||||
|
||||
- [ ] Bug fix (non-breaking change that fixes an issue)
|
||||
- [ ] New feature (non-breaking change that adds functionality)
|
||||
- [ ] Breaking change (fix or feature that changes existing behaviour)
|
||||
- [ ] Documentation / comments only
|
||||
- [ ] Build / CI / dependency change
|
||||
- [ ] Refactor (no behaviour change)
|
||||
|
||||
## How has this been tested?
|
||||
|
||||
<!-- List test files and the command used. -->
|
||||
|
||||
```bash
|
||||
uv run pytest -m "not gpu"
|
||||
```
|
||||
|
||||
## Checklist
|
||||
|
||||
- [ ] Tests pass locally: `uv run pytest -m "not gpu"` (no GPU) or `uv run pytest tests/test_v2.py` (with GPU)
|
||||
- [ ] I have added or updated tests to cover my changes (where applicable).
|
||||
- [ ] I have added or updated docstrings for any changed public functions.
|
||||
|
||||
## Screenshots / output (if relevant)
|
||||
|
||||
<!-- Paste terminal output or screenshots here. -->
|
||||
30
.github/workflows/ci.yml
vendored
Normal file
30
.github/workflows/ci.yml
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
name: CI
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
lightweight-checks:
|
||||
name: Lightweight Python checks
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.10"
|
||||
|
||||
- name: Set up uv
|
||||
uses: astral-sh/setup-uv@v5
|
||||
|
||||
- name: Run tests (no GPU)
|
||||
run: uv run --extra test pytest -m "not gpu" -v
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -33,3 +33,6 @@ desktop.ini
|
|||
# Example audio files (downloaded on demand, not stored in git)
|
||||
/examples/*.wav
|
||||
/tests/sample_prompt.wav
|
||||
|
||||
.claude
|
||||
.memsearch
|
||||
|
|
|
|||
141
indextts/cli.py
141
indextts/cli.py
|
|
@ -1,43 +1,90 @@
|
|||
import os
|
||||
import sys
|
||||
import warnings
|
||||
# Suppress warnings from tensorflow and other libraries
|
||||
|
||||
warnings.filterwarnings("ignore", category=UserWarning)
|
||||
warnings.filterwarnings("ignore", category=FutureWarning)
|
||||
def main():
|
||||
import argparse
|
||||
parser = argparse.ArgumentParser(description="IndexTTS Command Line")
|
||||
parser.add_argument("text", type=str, help="Text to be synthesized")
|
||||
parser.add_argument("-v", "--voice", type=str, required=True, help="Path to the audio prompt file (wav format)")
|
||||
parser.add_argument("-o", "--output_path", type=str, default="gen.wav", help="Path to the output wav file")
|
||||
parser.add_argument("-c", "--config", type=str, default="checkpoints/config.yaml", help="Path to the config file. Default is 'checkpoints/config.yaml'")
|
||||
parser.add_argument("--model_dir", type=str, default="checkpoints", help="Path to the model directory. Default is 'checkpoints'")
|
||||
parser.add_argument("--fp16", action="store_true", default=False, help="Use FP16 for inference if available")
|
||||
parser.add_argument("-f", "--force", action="store_true", default=False, help="Force to overwrite the output file if it exists")
|
||||
parser.add_argument("-d", "--device", type=str, default=None, help="Device to run the model on (cpu, cuda, mps, xpu)." )
|
||||
args = parser.parse_args()
|
||||
|
||||
_REQUIRED_MODEL_FILES = (
|
||||
"bpe.model",
|
||||
"gpt.pth",
|
||||
"s2mel.pth",
|
||||
"wav2vec2bert_stats.pt",
|
||||
)
|
||||
|
||||
_MODEL_REPOS = {
|
||||
"1": "IndexTeam/IndexTTS",
|
||||
"2": "IndexTeam/IndexTTS-2",
|
||||
}
|
||||
|
||||
|
||||
def _cmd_download(args):
|
||||
"""Download all model files (main weights + auxiliary models)."""
|
||||
model_dir = args.model_dir
|
||||
version = args.version
|
||||
repo_id = _MODEL_REPOS[version]
|
||||
|
||||
# 1. Main model weights
|
||||
missing = [f for f in _REQUIRED_MODEL_FILES if not os.path.exists(os.path.join(model_dir, f))]
|
||||
if missing:
|
||||
print(f">> Downloading IndexTTS v{version} model to {model_dir}...")
|
||||
from indextts.utils.model_download import snapshot_download
|
||||
snapshot_download(repo_id, local_dir=model_dir)
|
||||
|
||||
still_missing = [f for f in _REQUIRED_MODEL_FILES if not os.path.exists(os.path.join(model_dir, f))]
|
||||
if still_missing:
|
||||
print(f"ERROR: Still missing after download: {', '.join(still_missing)}")
|
||||
sys.exit(1)
|
||||
else:
|
||||
print(f">> Main model files already present in {model_dir}.")
|
||||
|
||||
# 2. config.yaml
|
||||
from indextts.utils.model_download import ensure_config_available
|
||||
ensure_config_available(model_dir)
|
||||
|
||||
# 3. Auxiliary models (v2 only: w2v-bert, bigvgan, campplus, semantic_codec)
|
||||
if version == "2":
|
||||
from indextts.utils.model_download import ensure_models_available
|
||||
ensure_models_available(model_dir)
|
||||
|
||||
print(f">> IndexTTS v{version} models downloaded successfully.")
|
||||
|
||||
|
||||
def _cmd_infer(args):
|
||||
"""Run TTS inference."""
|
||||
if len(args.text.strip()) == 0:
|
||||
print("ERROR: Text is empty.")
|
||||
parser.print_help()
|
||||
sys.exit(1)
|
||||
if not os.path.exists(args.voice):
|
||||
print(f"Audio prompt file {args.voice} does not exist.")
|
||||
parser.print_help()
|
||||
sys.exit(1)
|
||||
if not os.path.exists(args.config):
|
||||
print(f"Config file {args.config} does not exist.")
|
||||
parser.print_help()
|
||||
sys.exit(1)
|
||||
|
||||
requested_config = args.config
|
||||
if not os.path.exists(requested_config):
|
||||
from indextts.utils.model_download import ensure_config_available
|
||||
config_dir = os.path.dirname(requested_config) or "."
|
||||
try:
|
||||
ensure_config_available(config_dir)
|
||||
except Exception as e:
|
||||
print(f"Failed to download config.yaml: {e}")
|
||||
downloaded_config = os.path.join(config_dir, "config.yaml")
|
||||
if os.path.exists(requested_config):
|
||||
args.config = requested_config
|
||||
elif os.path.exists(downloaded_config):
|
||||
print(f"Config file {requested_config} does not exist. Using {downloaded_config} instead.")
|
||||
args.config = downloaded_config
|
||||
else:
|
||||
print(f"Config file {requested_config} does not exist.")
|
||||
sys.exit(1)
|
||||
|
||||
output_path = args.output_path
|
||||
if os.path.exists(output_path):
|
||||
if not args.force:
|
||||
print(f"ERROR: Output file {output_path} already exists. Use --force to overwrite.")
|
||||
parser.print_help()
|
||||
sys.exit(1)
|
||||
else:
|
||||
os.remove(output_path)
|
||||
|
||||
|
||||
try:
|
||||
import torch
|
||||
except ImportError:
|
||||
|
|
@ -53,13 +100,53 @@ def main():
|
|||
args.device = "mps"
|
||||
else:
|
||||
args.device = "cpu"
|
||||
args.fp16 = False # Disable FP16 on CPU
|
||||
args.fp16 = False
|
||||
print("WARNING: Running on CPU may be slow.")
|
||||
|
||||
# TODO: Add CLI support for IndexTTS2.
|
||||
from indextts.infer import IndexTTS
|
||||
tts = IndexTTS(cfg_path=args.config, model_dir=args.model_dir, use_fp16=args.fp16, device=args.device)
|
||||
tts.infer(audio_prompt=args.voice, text=args.text.strip(), output_path=output_path)
|
||||
version = args.version
|
||||
if version == "1":
|
||||
from indextts.infer import IndexTTS
|
||||
tts = IndexTTS(cfg_path=args.config, model_dir=args.model_dir, use_fp16=args.fp16, device=args.device)
|
||||
tts.infer(audio_prompt=args.voice, text=args.text.strip(), output_path=output_path)
|
||||
else:
|
||||
from indextts.infer_v2 import IndexTTS2
|
||||
tts = IndexTTS2(cfg_path=args.config, model_dir=args.model_dir, use_fp16=args.fp16, device=args.device)
|
||||
tts.infer(spk_audio_prompt=args.voice, text=args.text.strip(), output_path=output_path)
|
||||
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(description="IndexTTS Command Line")
|
||||
subparsers = parser.add_subparsers(dest="command")
|
||||
|
||||
# -- download subcommand --
|
||||
dl_parser = subparsers.add_parser("download", help="Download model files")
|
||||
dl_parser.add_argument("--model-dir", type=str, default="checkpoints", help="Model directory")
|
||||
dl_parser.add_argument("--version", type=str, choices=["1", "2"], default="2", help="Model version (default: 2)")
|
||||
|
||||
# -- infer subcommand --
|
||||
infer_parser = subparsers.add_parser("infer", help="Run TTS inference")
|
||||
infer_parser.add_argument("text", type=str, help="Text to be synthesized")
|
||||
infer_parser.add_argument("-v", "--voice", type=str, required=True, help="Path to the audio prompt file")
|
||||
infer_parser.add_argument("-o", "--output_path", type=str, default="gen.wav", help="Path to the output wav file")
|
||||
infer_parser.add_argument("-c", "--config", type=str, default="checkpoints/config.yaml", help="Path to the config file")
|
||||
infer_parser.add_argument("--model-dir", type=str, default="checkpoints", help="Path to the model directory")
|
||||
infer_parser.add_argument("--version", type=str, choices=["1", "2"], default="2", help="Model version (default: 2)")
|
||||
infer_parser.add_argument("--fp16", action="store_true", default=False, help="Use FP16 for inference")
|
||||
infer_parser.add_argument("-f", "--force", action="store_true", default=False, help="Overwrite output file if exists")
|
||||
infer_parser.add_argument("-d", "--device", type=str, default=None, help="Device (cpu, cuda, mps, xpu)")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.command == "download":
|
||||
_cmd_download(args)
|
||||
elif args.command == "infer":
|
||||
_cmd_infer(args)
|
||||
else:
|
||||
parser.print_help()
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ Spaces or ModelScope Studio, depending on the detected network environment.
|
|||
|
||||
The example files are hosted at:
|
||||
- HuggingFace: https://huggingface.co/spaces/IndexTeam/IndexTTS-2-Demo
|
||||
- ModelScope: https://modelscope.cn/studios/IndexTeam/IndexTTS-2-Demo
|
||||
- ModelScope: https://modelscope.cn/studio/IndexTeam/IndexTTS-2-Demo
|
||||
|
||||
File names are determined from ``examples/cases.jsonl``.
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -63,10 +63,73 @@ def _download_single_file(repo_id: str, filename: str, local_path: str) -> str:
|
|||
return local_path
|
||||
|
||||
|
||||
def ensure_config_available(model_dir: str) -> None:
|
||||
"""Download only ``config.yaml`` if it is missing from *model_dir*."""
|
||||
model_dir = model_dir or "."
|
||||
config_path = os.path.join(model_dir, "config.yaml")
|
||||
if os.path.isfile(config_path):
|
||||
return
|
||||
print(f">> config.yaml not found in {model_dir}, downloading...")
|
||||
_download_single_file("IndexTeam/IndexTTS-2", "config.yaml", config_path)
|
||||
print(">> config.yaml downloaded.")
|
||||
|
||||
|
||||
def _find_hf_cache_snapshot(cache_dir: str, repo_id: str) -> str | None:
|
||||
"""Locate *repo_id* in a HuggingFace Hub cache directory."""
|
||||
repo_key = repo_id.replace("/", "--")
|
||||
models_dir = os.path.join(cache_dir, f"models--{repo_key}")
|
||||
if not os.path.isdir(models_dir):
|
||||
return None
|
||||
snapshots_dir = os.path.join(models_dir, "snapshots")
|
||||
if not os.path.isdir(snapshots_dir):
|
||||
return None
|
||||
# Prefer the commit hash recorded in refs/main
|
||||
refs_main = os.path.join(models_dir, "refs", "main")
|
||||
if os.path.isfile(refs_main):
|
||||
with open(refs_main) as f:
|
||||
commit_hash = f.read().strip()
|
||||
snapshot = os.path.join(snapshots_dir, commit_hash)
|
||||
if os.path.isdir(snapshot) and os.listdir(snapshot):
|
||||
return snapshot
|
||||
|
||||
# Some caches may not have refs/main. Reuse only when there is no ambiguity.
|
||||
try:
|
||||
entries = [
|
||||
os.path.join(snapshots_dir, e)
|
||||
for e in os.listdir(snapshots_dir)
|
||||
if os.path.isdir(os.path.join(snapshots_dir, e))
|
||||
]
|
||||
if len(entries) == 1:
|
||||
return entries[0]
|
||||
except OSError:
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
def _locate_snapshot(repo_id: str, local_cache_dir: str) -> str | None:
|
||||
"""Search local and default HuggingFace caches for *repo_id*."""
|
||||
default_hf_cache = os.environ.get(
|
||||
"HF_HUB_CACHE",
|
||||
os.path.join(os.path.expanduser("~"), ".cache", "huggingface", "hub"),
|
||||
)
|
||||
search_dirs = [local_cache_dir]
|
||||
if os.path.abspath(default_hf_cache) != os.path.abspath(local_cache_dir):
|
||||
search_dirs.append(default_hf_cache)
|
||||
for d in search_dirs:
|
||||
snapshot = _find_hf_cache_snapshot(d, repo_id)
|
||||
if snapshot:
|
||||
return snapshot
|
||||
return None
|
||||
|
||||
|
||||
def ensure_models_available(model_dir: str, bigvgan_repo: str = _BIGVGAN_REPO) -> dict:
|
||||
"""
|
||||
Download all auxiliary models to ``{model_dir}/hf_cache/`` if missing.
|
||||
|
||||
If files already exist in the old standard HuggingFace Hub cache layout
|
||||
(``models--{owner}--{name}/snapshots/{hash}/``), they are copied into the
|
||||
new flat layout instead of being re-downloaded.
|
||||
|
||||
Call this once at startup before creating ``IndexTTS2``.
|
||||
|
||||
Returns a dict of local paths:
|
||||
|
|
@ -79,34 +142,66 @@ def ensure_models_available(model_dir: str, bigvgan_repo: str = _BIGVGAN_REPO) -
|
|||
os.makedirs(cache_dir, exist_ok=True)
|
||||
paths = {}
|
||||
|
||||
# 1. w2v-bert-2.0 (full repo — needed by SeamlessM4T and Wav2Vec2BertModel)
|
||||
# w2v-bert-2.0 is a full repo needed by SeamlessM4T and Wav2Vec2BertModel.
|
||||
w2v_dir = os.path.join(cache_dir, "w2v-bert-2.0")
|
||||
if not os.path.isdir(w2v_dir) or not os.listdir(w2v_dir):
|
||||
print(f">> Downloading w2v-bert-2.0 to {w2v_dir}...")
|
||||
snapshot_download("facebook/w2v-bert-2.0", local_dir=w2v_dir)
|
||||
old_snapshot = _locate_snapshot("facebook/w2v-bert-2.0", cache_dir)
|
||||
if old_snapshot:
|
||||
print(f">> Migrating w2v-bert-2.0 from existing HF cache to {w2v_dir}...")
|
||||
shutil.copytree(old_snapshot, w2v_dir, dirs_exist_ok=True)
|
||||
else:
|
||||
print(f">> Downloading w2v-bert-2.0 to {w2v_dir}...")
|
||||
snapshot_download("facebook/w2v-bert-2.0", local_dir=w2v_dir)
|
||||
paths["w2v_bert"] = w2v_dir
|
||||
|
||||
# 2. MaskGCT semantic codec
|
||||
maskgct_path = os.path.join(cache_dir, "semantic_codec_model.safetensors")
|
||||
if not os.path.isfile(maskgct_path):
|
||||
print(f">> Downloading MaskGCT semantic codec to {maskgct_path}...")
|
||||
_download_single_file("amphion/MaskGCT", "semantic_codec/model.safetensors", maskgct_path)
|
||||
paths["semantic_codec"] = maskgct_path
|
||||
for key, repo_id, remote_file, local_file, label in (
|
||||
(
|
||||
"semantic_codec",
|
||||
"amphion/MaskGCT",
|
||||
"semantic_codec/model.safetensors",
|
||||
os.path.join(cache_dir, "semantic_codec_model.safetensors"),
|
||||
"MaskGCT semantic codec",
|
||||
),
|
||||
(
|
||||
"campplus",
|
||||
"funasr/campplus",
|
||||
"campplus_cn_common.bin",
|
||||
os.path.join(cache_dir, "campplus_cn_common.bin"),
|
||||
"CAMPPlus",
|
||||
),
|
||||
):
|
||||
if not os.path.isfile(local_file):
|
||||
old_snapshot = _locate_snapshot(repo_id, cache_dir)
|
||||
old_file = os.path.join(old_snapshot, *remote_file.split("/")) if old_snapshot else None
|
||||
if old_file and os.path.isfile(old_file):
|
||||
print(f">> Migrating {label} from existing HF cache...")
|
||||
shutil.copy2(old_file, local_file)
|
||||
else:
|
||||
print(f">> Downloading {label} to {local_file}...")
|
||||
_download_single_file(repo_id, remote_file, local_file)
|
||||
paths[key] = local_file
|
||||
|
||||
# 3. CAMPPlus speaker embedding model
|
||||
campplus_path = os.path.join(cache_dir, "campplus_cn_common.bin")
|
||||
if not os.path.isfile(campplus_path):
|
||||
print(f">> Downloading CAMPPlus to {campplus_path}...")
|
||||
_download_single_file("funasr/campplus", "campplus_cn_common.bin", campplus_path)
|
||||
paths["campplus"] = campplus_path
|
||||
|
||||
# 4. BigVGAN vocoder (config + weights)
|
||||
# BigVGAN vocoder (config + weights)
|
||||
bigvgan_dir = os.path.join(cache_dir, "bigvgan")
|
||||
if not os.path.isdir(bigvgan_dir) or not os.path.isfile(os.path.join(bigvgan_dir, "config.json")):
|
||||
print(f">> Downloading BigVGAN to {bigvgan_dir}...")
|
||||
os.makedirs(bigvgan_dir, exist_ok=True)
|
||||
_download_single_file(bigvgan_repo, "config.json", os.path.join(bigvgan_dir, "config.json"))
|
||||
_download_single_file(bigvgan_repo, "bigvgan_generator.pt", os.path.join(bigvgan_dir, "bigvgan_generator.pt"))
|
||||
bigvgan_files = ("config.json", "bigvgan_generator.pt")
|
||||
missing_bigvgan = [
|
||||
f for f in bigvgan_files
|
||||
if not os.path.isfile(os.path.join(bigvgan_dir, f))
|
||||
]
|
||||
if missing_bigvgan:
|
||||
old_snapshot = _locate_snapshot(bigvgan_repo, cache_dir)
|
||||
if old_snapshot and all(os.path.isfile(os.path.join(old_snapshot, f)) for f in missing_bigvgan):
|
||||
print(f">> Migrating BigVGAN from existing HF cache to {bigvgan_dir}...")
|
||||
os.makedirs(bigvgan_dir, exist_ok=True)
|
||||
for fname in missing_bigvgan:
|
||||
src = os.path.join(old_snapshot, fname)
|
||||
dst = os.path.join(bigvgan_dir, fname)
|
||||
shutil.copy2(src, dst)
|
||||
else:
|
||||
print(f">> Downloading BigVGAN to {bigvgan_dir}...")
|
||||
os.makedirs(bigvgan_dir, exist_ok=True)
|
||||
for fname in missing_bigvgan:
|
||||
_download_single_file(bigvgan_repo, fname, os.path.join(bigvgan_dir, fname))
|
||||
paths["bigvgan"] = bigvgan_dir
|
||||
|
||||
print(">> All auxiliary models ready.")
|
||||
|
|
|
|||
|
|
@ -71,6 +71,15 @@ webui = [
|
|||
deepspeed = [
|
||||
"deepspeed==0.17.1",
|
||||
]
|
||||
test = [
|
||||
"pytest>=7.0",
|
||||
"requests>=2.28",
|
||||
]
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
markers = [
|
||||
"gpu: tests that require GPU and model checkpoints (deselect with '-m not gpu')",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://github.com/index-tts/index-tts"
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
{"prompt_audio":"sample_prompt.wav","text":"IndexTTS 正式发布1.0版本了,效果666","infer_mode":0}
|
||||
{"prompt_audio":"sample_prompt.wav","text":"大家好,我现在正在bilibili 体验 ai 科技,说实话,来之前我绝对想不到!AI技术已经发展到这样匪夷所思的地步了!","infer_mode":0}
|
||||
{"prompt_audio":"sample_prompt.wav","text":"晕XUAN4是一种GAN3觉","infer_mode":0}
|
||||
{"prompt_audio":"sample_prompt.wav","text":"最zhong4要的是:不要chong2蹈覆辙","infer_mode":0}
|
||||
{"prompt_audio":"sample_prompt.wav","text":"ni3 dao4 di3 xing2 bu5 xing2 a5?gei3 wo3 chong2 zuo4 yi5 bian4!","infer_mode":0}
|
||||
{"prompt_audio":"sample_prompt.wav","text":"Matt Hougan, chief investment officer at Bitwise, predicts Bitcoin (BTC) will reach $200,000 by the end of 2025 due to a supply shock from heightened institutional demand. In an interview with Cointelegraph at Consensus 2025 in Toronto, the executive said that Bitwise's Bitcoin price prediction model is driven exclusively by supply and demand metrics. \"I think eventually that will exhaust sellers at the $100,000 level where we have been stuck, and I think the next stopping point above that is $200,000,\" the executive said.","infer_mode":1}
|
||||
{"prompt_audio":"sample_prompt.wav","text":"《盗梦空间》(英语:Inception)是由美国华纳兄弟影片公司出品的电影,由克里斯托弗·诺兰(Christopher Edward Nolan)执导并编剧,莱昂纳多·迪卡普里奥(Leonardo Wilhelm DiCaprio)、玛丽昂·歌迪亚、约瑟夫·高登-莱维特、艾利奥特·佩吉、汤姆·哈迪等联袂主演,2010年7月16日在美国上映,2010年9月1日在中国内地上映,2020年8月28日在中国内地重映。豆瓣评分:9.4,IMDB 8.8。影片剧情游走于梦境与现实之间,被定义为“发生在意识结构内的当代动作科幻片”,讲述了由 Leonardo 扮演的造梦师,带领特工团队进入他人梦境,从他人的潜意识中盗取机密,并重塑他人梦境的故事。","infer_mode":1}
|
||||
{"prompt_audio":"sample_prompt.wav","text":"清晨拉开窗帘,阳光洒在窗台的Bloomixy花艺礼盒上——薰衣草香薰蜡烛唤醒嗅觉,永生花束折射出晨露般光泽。设计师将“自然绽放美学”融入每个细节:手工陶瓷花瓶可作首饰收纳,香薰精油含依兰依兰舒缓配方。限量款附赠《365天插花灵感手册》,让每个平凡日子都有花开仪式感。宴会厅灯光暗下的刹那,Glimmeria星月系列耳坠开始发光——瑞士冷珐琅工艺让蓝宝石如银河流动,钛合金骨架仅3.2g无负重感。设计师秘密:内置微型重力感应器,随步伐产生0.01mm振幅,打造“行走的星光”。七夕限定礼盒含星座定制铭牌,让爱意如星辰永恒闪耀。","infer_mode":1}
|
||||
{"prompt_audio":"sample_prompt.wav","text":"当地时间15日,随着特朗普与阿联酋敲定2000亿美元协议,特朗普的中东之行正式收官。特朗普已宣布获得沙特6000亿美元和卡塔尔2430亿美元投资承诺。商业协议成为特朗普重返白宫后首次外访的核心成果。香港英文媒体《南华早报》(South China Morning Post)称,特朗普访问期间提出了以经济合作为驱动的中东及南亚和平计划。分析人士称,该战略包含多项旨在遏制中国在这些地区影响力的措施。英国伦敦国王学院安全研究教授安德烈亚斯·克里格(Dr. Andreas Krieg)对半岛电视台表示,特朗普访问海湾地区的主要目标有三个:其一,以军工投资和能源合作的形式获得海湾国家的切实承诺;其二,加强与“让美国再次伟大”运动结盟的外交伙伴关系,维持美国外交影响力;其三:将海湾国家重新定位为美国在从加沙到伊朗等地区危机前线的调解人,这样就可以不用增强军事部署。安德烈亚斯·克里格直言:“海湾国家不会为美国牺牲与中国的关系,他们的战略自主性远超特朗普想象。”美国有线电视新闻网(CNN)报道称,特朗普到访的三个能源富国,每个国家都对美国有着长长的诉求清单。尽管这些国家豪掷重金,但美国并未实现所有诉求。","infer_mode":1}
|
||||
2
tests/conftest.py
Normal file
2
tests/conftest.py
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
# padding_test.py is a manual script, not a pytest test module.
|
||||
collect_ignore = ["padding_test.py"]
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
import os
|
||||
from indextts.infer import IndexTTS
|
||||
from indextts.utils.examples_downloader import ensure_test_sample_available
|
||||
|
||||
if __name__ == "__main__":
|
||||
model_dir = "checkpoints"
|
||||
cfg_path = f"{model_dir}/config.yaml"
|
||||
if not os.path.exists(cfg_path):
|
||||
raise SystemExit(
|
||||
f"Model config not found: {cfg_path}. "
|
||||
"Please download the model first (e.g. run webui.py or see README)."
|
||||
)
|
||||
prompt_wav = ensure_test_sample_available()
|
||||
tts = IndexTTS(cfg_path=cfg_path, model_dir=model_dir, use_fp16=True, use_cuda_kernel=False)
|
||||
# 单音频推理测试
|
||||
text="晕 XUAN4 是 一 种 GAN3 觉"
|
||||
tts.infer(audio_prompt=prompt_wav, text=text, output_path=f"outputs/{text[:20]}.wav", verbose=True)
|
||||
text='大家好,我现在正在bilibili 体验 ai 科技,说实话,来之前我绝对想不到!AI技术已经发展到这样匪夷所思的地步了!'
|
||||
tts.infer(audio_prompt=prompt_wav, text=text, output_path=f"outputs/{text[:20]}.wav", verbose=True)
|
||||
text="There is a vehicle arriving in dock number 7?"
|
||||
tts.infer(audio_prompt=prompt_wav, text=text, output_path=f"outputs/{text[:20]}.wav", verbose=True)
|
||||
text = "“我爱你!”的英语是“I love you!”"
|
||||
tts.infer(audio_prompt=prompt_wav, text=text, output_path=f"outputs/{text[:20]}.wav", verbose=True)
|
||||
text = "Joseph Gordon-Levitt is an American actor"
|
||||
tts.infer(audio_prompt=prompt_wav, text=text, output_path=f"outputs/{text[:20]}.wav", verbose=True)
|
||||
text = "约瑟夫·高登-莱维特是美国演员"
|
||||
tts.infer(audio_prompt=prompt_wav, text=text, output_path=f"outputs/{text[:20]}.wav", verbose=True)
|
||||
text = "蒂莫西·唐纳德·库克(英文名:Timothy Donald Cook),通称蒂姆·库克(Tim Cook),现任苹果公司首席执行官。"
|
||||
tts.infer(audio_prompt=prompt_wav, text=text, output_path="outputs/蒂莫西·唐纳德·库克.wav", verbose=True)
|
||||
# 并行推理测试
|
||||
text="亲爱的伙伴们,大家好!每一次的努力都是为了更好的未来,要善于从失败中汲取经验,让我们一起勇敢前行,迈向更加美好的明天!"
|
||||
tts.infer_fast(audio_prompt=prompt_wav, text=text, output_path=f"outputs/{text[:20]}.wav", verbose=True)
|
||||
text="The weather is really nice today, perfect for studying at home.Thank you!"
|
||||
tts.infer_fast(audio_prompt=prompt_wav, text=text, output_path=f"outputs/{text[:20]}.wav", verbose=True)
|
||||
text='''叶远随口答应一声,一定帮忙云云。
|
||||
教授看叶远的样子也知道,这事情多半是黄了。
|
||||
谁得到这样的东西也不会轻易贡献出来,这是很大的一笔财富。
|
||||
叶远回来后,又自己做了几次试验,发现空间湖水对一些外伤也有很大的帮助。
|
||||
找来一只断了腿的兔子,喝下空间湖水,一天时间,兔子就完全好了。
|
||||
还想多做几次试验,可是身边没有试验的对象,就先放到一边,了解空间湖水可以饮用,而且对人有利,这些就足够了。
|
||||
感谢您的收听,下期再见!
|
||||
'''.replace("\n", "")
|
||||
tts.infer_fast(audio_prompt=prompt_wav, text=text, output_path=f"outputs/{text[:20]}.wav", verbose=True)
|
||||
# 长文本推理测试
|
||||
text = """《盗梦空间》是由美国华纳兄弟影片公司出品的电影,由克里斯托弗·诺兰执导并编剧,
|
||||
莱昂纳多·迪卡普里奥、玛丽昂·歌迪亚、约瑟夫·高登-莱维特、艾利奥特·佩吉、汤姆·哈迪等联袂主演,
|
||||
2010年7月16日在美国上映,2010年9月1日在中国内地上映,2020年8月28日在中国内地重映。
|
||||
影片剧情游走于梦境与现实之间,被定义为“发生在意识结构内的当代动作科幻片”,
|
||||
讲述了由莱昂纳多·迪卡普里奥扮演的造梦师,带领特工团队进入他人梦境,从他人的潜意识中盗取机密,并重塑他人梦境的故事。
|
||||
""".replace("\n", "")
|
||||
tts.infer_fast(audio_prompt=prompt_wav, text=text, output_path=f"outputs/{text[:20]}.wav", verbose=True)
|
||||
97
tests/test_v1.py
Normal file
97
tests/test_v1.py
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
"""
|
||||
IndexTTS v1 regression tests -- require GPU and model checkpoints.
|
||||
|
||||
Run with:
|
||||
uv run --extra test pytest tests/test_v1.py -v
|
||||
"""
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
pytestmark = pytest.mark.gpu
|
||||
|
||||
CHECKPOINTS_DIR = Path("checkpoints")
|
||||
CONFIG_PATH = CHECKPOINTS_DIR / "config.yaml"
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def tts_model():
|
||||
pytest.importorskip("torch")
|
||||
if not CONFIG_PATH.exists():
|
||||
pytest.skip(f"Checkpoints not found at {CHECKPOINTS_DIR}")
|
||||
|
||||
from indextts.infer import IndexTTS
|
||||
return IndexTTS(
|
||||
cfg_path=str(CONFIG_PATH),
|
||||
model_dir=str(CHECKPOINTS_DIR),
|
||||
use_fp16=True,
|
||||
use_cuda_kernel=False,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def prompt_wav():
|
||||
from indextts.utils.examples_downloader import ensure_test_sample_available
|
||||
return ensure_test_sample_available()
|
||||
|
||||
|
||||
# -- infer (single segment) ---------------------------------------------------
|
||||
|
||||
INFER_TEXTS = [
|
||||
"晕 XUAN4 是 一 种 GAN3 觉",
|
||||
"大家好,我现在正在bilibili体验ai科技,说实话,来之前我绝对想不到!",
|
||||
"There is a vehicle arriving in dock number 7?",
|
||||
"Joseph Gordon-Levitt is an American actor",
|
||||
"约瑟夫·高登-莱维特是美国演员",
|
||||
"蒂莫西·唐纳德·库克,通称蒂姆·库克,现任苹果公司首席执行官。",
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("text", INFER_TEXTS, ids=lambda t: t[:20])
|
||||
def test_infer(tts_model, prompt_wav, text, tmp_path):
|
||||
out = tmp_path / "out.wav"
|
||||
tts_model.infer(audio_prompt=prompt_wav, text=text, output_path=str(out), verbose=True)
|
||||
assert out.exists() and out.stat().st_size > 1000
|
||||
|
||||
|
||||
# -- infer_fast (parallel segments) --------------------------------------------
|
||||
|
||||
INFER_FAST_TEXTS = [
|
||||
"亲爱的伙伴们,大家好!每一次的努力都是为了更好的未来,让我们一起勇敢前行,迈向更加美好的明天!",
|
||||
"The weather is really nice today, perfect for studying at home. Thank you!",
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("text", INFER_FAST_TEXTS, ids=lambda t: t[:20])
|
||||
def test_infer_fast(tts_model, prompt_wav, text, tmp_path):
|
||||
out = tmp_path / "out.wav"
|
||||
tts_model.infer_fast(audio_prompt=prompt_wav, text=text, output_path=str(out), verbose=True)
|
||||
assert out.exists() and out.stat().st_size > 1000
|
||||
|
||||
|
||||
# -- infer_fast long text ------------------------------------------------------
|
||||
|
||||
LONG_TEXTS = [
|
||||
(
|
||||
"叶远随口答应一声,一定帮忙云云。"
|
||||
"教授看叶远的样子也知道,这事情多半是黄了。"
|
||||
"谁得到这样的东西也不会轻易贡献出来,这是很大的一笔财富。"
|
||||
"叶远回来后,又自己做了几次试验,发现空间湖水对一些外伤也有很大的帮助。"
|
||||
"找来一只断了腿的兔子,喝下空间湖水,一天时间,兔子就完全好了。"
|
||||
"感谢您的收听,下期再见!"
|
||||
),
|
||||
(
|
||||
"《盗梦空间》是由美国华纳兄弟影片公司出品的电影,由克里斯托弗·诺兰执导并编剧,"
|
||||
"莱昂纳多·迪卡普里奥、玛丽昂·歌迪亚、约瑟夫·高登-莱维特、艾利奥特·佩吉、"
|
||||
"汤姆·哈迪等联袂主演,2010年7月16日在美国上映,2010年9月1日在中国内地上映。"
|
||||
"影片剧情游走于梦境与现实之间,讲述了由莱昂纳多扮演的造梦师,"
|
||||
"带领特工团队进入他人梦境,从他人的潜意识中盗取机密,并重塑他人梦境的故事。"
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("text", LONG_TEXTS, ids=["novel", "movie"])
|
||||
def test_infer_fast_long(tts_model, prompt_wav, text, tmp_path):
|
||||
out = tmp_path / "out.wav"
|
||||
tts_model.infer_fast(audio_prompt=prompt_wav, text=text, output_path=str(out), verbose=True)
|
||||
assert out.exists() and out.stat().st_size > 5000
|
||||
177
tests/test_v2.py
Normal file
177
tests/test_v2.py
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
"""
|
||||
IndexTTS v2 tests.
|
||||
|
||||
Run with:
|
||||
uv run --extra test pytest tests/test_v2.py -v
|
||||
|
||||
CI only (no GPU):
|
||||
uv run --extra test pytest tests/test_v2.py -v -m "not gpu"
|
||||
"""
|
||||
import importlib
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
CHECKPOINTS_DIR = Path("checkpoints")
|
||||
CONFIG_PATH = CHECKPOINTS_DIR / "config.yaml"
|
||||
|
||||
|
||||
# -- Fixtures ------------------------------------------------------------------
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def tts_model():
|
||||
pytest.importorskip("torch")
|
||||
if not CONFIG_PATH.exists():
|
||||
pytest.skip(f"Checkpoints not found at {CHECKPOINTS_DIR}")
|
||||
|
||||
from indextts.infer_v2 import IndexTTS2
|
||||
return IndexTTS2(
|
||||
cfg_path=str(CONFIG_PATH),
|
||||
model_dir=str(CHECKPOINTS_DIR),
|
||||
use_fp16=True,
|
||||
use_cuda_kernel=False,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def prompt_wav():
|
||||
from indextts.utils.examples_downloader import ensure_test_sample_available
|
||||
return ensure_test_sample_available()
|
||||
|
||||
|
||||
# -- Download URL checks (no GPU) ---------------------------------------------
|
||||
|
||||
def _get_download_urls():
|
||||
"""Return URLs that match the actual network environment (CN vs intl)."""
|
||||
from indextts.utils.network_detection import need_proxy
|
||||
if need_proxy():
|
||||
# China: test ModelScope endpoints
|
||||
return [
|
||||
"https://modelscope.cn/studio/IndexTeam/IndexTTS-2-Demo/resolve/master/examples/voice_01.wav",
|
||||
"https://modelscope.cn/models/AI-ModelScope/w2v-bert-2.0/resolve/master/config.json",
|
||||
]
|
||||
else:
|
||||
# International: test HuggingFace endpoints
|
||||
return [
|
||||
"https://huggingface.co/spaces/IndexTeam/IndexTTS-2-Demo/resolve/main/examples/voice_01.wav",
|
||||
"https://huggingface.co/nvidia/bigvgan_v2_22khz_80band_256x/resolve/main/config.json",
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("url", _get_download_urls())
|
||||
def test_download_url_reachable(url, tmp_path):
|
||||
"""Use the actual _download_file function to verify URLs are reachable."""
|
||||
from indextts.utils.examples_downloader import _download_file
|
||||
|
||||
dest = tmp_path / "downloaded"
|
||||
# Real download, same code path as production. No min_size so we don't
|
||||
# need to pull a full large file — just verify it doesn't raise.
|
||||
_download_file(url, str(dest), timeout=30)
|
||||
assert dest.exists() and dest.stat().st_size > 0
|
||||
|
||||
|
||||
# -- Model download logic (no GPU) --------------------------------------------
|
||||
|
||||
def test_legacy_cache_compatibility(tmp_path, monkeypatch):
|
||||
"""ensure_models_available preserves existing cache and skips re-download."""
|
||||
from indextts.utils import model_download
|
||||
from indextts.utils.model_download import ensure_models_available
|
||||
|
||||
download_calls = []
|
||||
original_download = model_download._download_single_file
|
||||
|
||||
def mock_download(*args, **kwargs):
|
||||
download_calls.append(args)
|
||||
return original_download(*args, **kwargs)
|
||||
|
||||
monkeypatch.setattr(model_download, "_download_single_file", mock_download)
|
||||
|
||||
model_dir = tmp_path / "checkpoints"
|
||||
cache_dir = model_dir / "hf_cache"
|
||||
cache_dir.mkdir(parents=True)
|
||||
|
||||
w2v_dir = cache_dir / "w2v-bert-2.0"
|
||||
w2v_dir.mkdir()
|
||||
(w2v_dir / "config.json").write_text('{"test": true}')
|
||||
|
||||
bigvgan_dir = cache_dir / "bigvgan"
|
||||
bigvgan_dir.mkdir()
|
||||
(bigvgan_dir / "config.json").write_text('{"test": true}')
|
||||
(bigvgan_dir / "bigvgan_generator.pt").write_bytes(b"fake")
|
||||
|
||||
campplus = cache_dir / "campplus_cn_common.bin"
|
||||
campplus.write_bytes(b"fake_campplus")
|
||||
|
||||
semantic = cache_dir / "semantic_codec_model.safetensors"
|
||||
semantic.write_bytes(b"fake_semantic")
|
||||
|
||||
paths = ensure_models_available(str(model_dir))
|
||||
|
||||
# Files preserved
|
||||
assert (w2v_dir / "config.json").exists()
|
||||
assert campplus.exists()
|
||||
assert semantic.exists()
|
||||
assert (bigvgan_dir / "bigvgan_generator.pt").exists()
|
||||
|
||||
# No download triggered
|
||||
assert len(download_calls) == 0, f"Unexpected downloads: {download_calls}"
|
||||
|
||||
|
||||
# -- Inference (GPU required) --------------------------------------------------
|
||||
|
||||
INFER_TEXTS = [
|
||||
"大家好,这是一段测试语音。",
|
||||
"There is a vehicle arriving in dock number 7?",
|
||||
"Joseph Gordon-Levitt is an American actor.",
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.gpu
|
||||
@pytest.mark.parametrize("text", INFER_TEXTS, ids=lambda t: t[:20])
|
||||
def test_infer(tts_model, prompt_wav, text, tmp_path):
|
||||
out = tmp_path / "out.wav"
|
||||
tts_model.infer(spk_audio_prompt=prompt_wav, text=text, output_path=str(out))
|
||||
assert out.exists() and out.stat().st_size > 1000
|
||||
|
||||
|
||||
@pytest.mark.gpu
|
||||
def test_infer_with_emotion_vector(tts_model, prompt_wav, tmp_path):
|
||||
"""infer() with explicit emotion vector."""
|
||||
emo_vec = [0.6, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.2]
|
||||
out = tmp_path / "emo.wav"
|
||||
tts_model.infer(
|
||||
spk_audio_prompt=prompt_wav,
|
||||
text="今天天气真好,心情特别愉快!",
|
||||
output_path=str(out),
|
||||
emo_vector=emo_vec,
|
||||
)
|
||||
assert out.exists() and out.stat().st_size > 1000
|
||||
|
||||
|
||||
@pytest.mark.gpu
|
||||
def test_infer_with_emo_text(tts_model, prompt_wav, tmp_path):
|
||||
"""infer() with use_emo_text auto-detection."""
|
||||
out = tmp_path / "emo_text.wav"
|
||||
tts_model.infer(
|
||||
spk_audio_prompt=prompt_wav,
|
||||
text="这件事让我非常生气!",
|
||||
output_path=str(out),
|
||||
use_emo_text=True,
|
||||
)
|
||||
assert out.exists() and out.stat().st_size > 1000
|
||||
|
||||
|
||||
# -- Long text (GPU required) -------------------------------------------------
|
||||
|
||||
@pytest.mark.gpu
|
||||
def test_infer_long_text(tts_model, prompt_wav, tmp_path):
|
||||
text = (
|
||||
"《盗梦空间》是由美国华纳兄弟影片公司出品的电影,由克里斯托弗诺兰执导并编剧,"
|
||||
"莱昂纳多迪卡普里奥、玛丽昂歌迪亚、约瑟夫高登莱维特、艾利奥特佩吉、"
|
||||
"汤姆哈迪等联袂主演,2010年7月16日在美国上映。"
|
||||
"影片剧情游走于梦境与现实之间,讲述了由莱昂纳多扮演的造梦师,"
|
||||
"带领特工团队进入他人梦境,从他人的潜意识中盗取机密的故事。"
|
||||
)
|
||||
out = tmp_path / "long.wav"
|
||||
tts_model.infer(spk_audio_prompt=prompt_wav, text=text, output_path=str(out))
|
||||
assert out.exists() and out.stat().st_size > 5000
|
||||
140
uv.lock
generated
140
uv.lock
generated
|
|
@ -1202,6 +1202,10 @@ dependencies = [
|
|||
deepspeed = [
|
||||
{ name = "deepspeed" },
|
||||
]
|
||||
test = [
|
||||
{ name = "pytest" },
|
||||
{ name = "requests" },
|
||||
]
|
||||
webui = [
|
||||
{ name = "gradio" },
|
||||
]
|
||||
|
|
@ -1229,6 +1233,8 @@ requires-dist = [
|
|||
{ name = "omegaconf", specifier = ">=2.3.0" },
|
||||
{ name = "opencv-python", specifier = "==4.9.0.80" },
|
||||
{ name = "pandas", specifier = "==2.3.2" },
|
||||
{ name = "pytest", marker = "extra == 'test'", specifier = ">=7.0" },
|
||||
{ name = "requests", marker = "extra == 'test'", specifier = ">=2.28" },
|
||||
{ name = "safetensors", specifier = "==0.5.2" },
|
||||
{ name = "sentencepiece", specifier = ">=0.2.1" },
|
||||
{ name = "tensorboard", specifier = "==2.9.1" },
|
||||
|
|
@ -1243,7 +1249,7 @@ requires-dist = [
|
|||
{ name = "wetext", marker = "sys_platform != 'linux'", specifier = ">=0.0.9" },
|
||||
{ name = "wetextprocessing", marker = "sys_platform == 'linux'" },
|
||||
]
|
||||
provides-extras = ["webui", "deepspeed"]
|
||||
provides-extras = ["webui", "deepspeed", "test"]
|
||||
|
||||
[[package]]
|
||||
name = "inflect"
|
||||
|
|
@ -1258,6 +1264,15 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/8a/eb/427ed2b20a38a4ee29f24dbe4ae2dafab198674fe9a85e3d6adf9e5f5f41/inflect-7.5.0-py3-none-any.whl", hash = "sha256:2aea70e5e70c35d8350b8097396ec155ffd68def678c7ff97f51aa69c1d92344", size = 35197, upload-time = "2024-12-28T17:11:15.931Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iniconfig"
|
||||
version = "2.3.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipython"
|
||||
version = "8.37.0"
|
||||
|
|
@ -2397,6 +2412,15 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/40/4b/2028861e724d3bd36227adfa20d3fd24c3fc6d52032f4a93c133be5d17ce/platformdirs-4.4.0-py3-none-any.whl", hash = "sha256:abd01743f24e5287cd7a5db3752faf1a2d65353f38ec26d98e25a6db65958c85", size = 18654, upload-time = "2025-08-26T14:32:02.735Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pluggy"
|
||||
version = "1.6.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pooch"
|
||||
version = "1.8.2"
|
||||
|
|
@ -2696,6 +2720,24 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/1a/22/60cd92bd3ec00948800984410f4cf5ded5bd8e9b715729f3642efe0edb3d/pystoi-0.4.1-py2.py3-none-any.whl", hash = "sha256:e277b671663d26d35a2416c9c8010a74084e6c3970354506398051a554896939", size = 8218, upload-time = "2023-12-29T16:48:03.748Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pytest"
|
||||
version = "9.0.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
||||
{ name = "exceptiongroup", marker = "python_full_version < '3.11'" },
|
||||
{ name = "iniconfig" },
|
||||
{ name = "packaging" },
|
||||
{ name = "pluggy" },
|
||||
{ name = "pygments" },
|
||||
{ name = "tomli", marker = "python_full_version < '3.11'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", size = 1572165, upload-time = "2026-04-07T17:16:18.027Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "python-dateutil"
|
||||
version = "2.9.0.post0"
|
||||
|
|
@ -3459,6 +3501,60 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/44/69/d21eb253fa91622da25585d362a874fa4710be600f0ea9446d8d0217cec1/tokenizers-0.21.0-cp39-abi3-win_amd64.whl", hash = "sha256:87841da5a25a3a5f70c102de371db120f41873b854ba65e52bccd57df5a3780c", size = 2389192, upload-time = "2024-11-27T13:11:25.724Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tomli"
|
||||
version = "2.4.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/22/de/48c59722572767841493b26183a0d1cc411d54fd759c5607c4590b6563a6/tomli-2.4.1.tar.gz", hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f", size = 17543, upload-time = "2026-03-25T20:22:03.828Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/f4/11/db3d5885d8528263d8adc260bb2d28ebf1270b96e98f0e0268d32b8d9900/tomli-2.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30", size = 154704, upload-time = "2026-03-25T20:21:10.473Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6d/f7/675db52c7e46064a9aa928885a9b20f4124ecb9bc2e1ce74c9106648d202/tomli-2.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a", size = 149454, upload-time = "2026-03-25T20:21:12.036Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/61/71/81c50943cf953efa35bce7646caab3cf457a7d8c030b27cfb40d7235f9ee/tomli-2.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076", size = 237561, upload-time = "2026-03-25T20:21:13.098Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/48/c1/f41d9cb618acccca7df82aaf682f9b49013c9397212cb9f53219e3abac37/tomli-2.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9", size = 243824, upload-time = "2026-03-25T20:21:14.569Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/22/e4/5a816ecdd1f8ca51fb756ef684b90f2780afc52fc67f987e3c61d800a46d/tomli-2.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c", size = 242227, upload-time = "2026-03-25T20:21:15.712Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6b/49/2b2a0ef529aa6eec245d25f0c703e020a73955ad7edf73e7f54ddc608aa5/tomli-2.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc", size = 247859, upload-time = "2026-03-25T20:21:17.001Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/83/bd/6c1a630eaca337e1e78c5903104f831bda934c426f9231429396ce3c3467/tomli-2.4.1-cp311-cp311-win32.whl", hash = "sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049", size = 97204, upload-time = "2026-03-25T20:21:18.079Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/42/59/71461df1a885647e10b6bb7802d0b8e66480c61f3f43079e0dcd315b3954/tomli-2.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e", size = 108084, upload-time = "2026-03-25T20:21:18.978Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b8/83/dceca96142499c069475b790e7913b1044c1a4337e700751f48ed723f883/tomli-2.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece", size = 95285, upload-time = "2026-03-25T20:21:20.309Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c1/ba/42f134a3fe2b370f555f44b1d72feebb94debcab01676bf918d0cb70e9aa/tomli-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a", size = 155924, upload-time = "2026-03-25T20:21:21.626Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/dc/c7/62d7a17c26487ade21c5422b646110f2162f1fcc95980ef7f63e73c68f14/tomli-2.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085", size = 150018, upload-time = "2026-03-25T20:21:23.002Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5c/05/79d13d7c15f13bdef410bdd49a6485b1c37d28968314eabee452c22a7fda/tomli-2.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9", size = 244948, upload-time = "2026-03-25T20:21:24.04Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/10/90/d62ce007a1c80d0b2c93e02cab211224756240884751b94ca72df8a875ca/tomli-2.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5", size = 253341, upload-time = "2026-03-25T20:21:25.177Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1a/7e/caf6496d60152ad4ed09282c1885cca4eea150bfd007da84aea07bcc0a3e/tomli-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585", size = 248159, upload-time = "2026-03-25T20:21:26.364Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/99/e7/c6f69c3120de34bbd882c6fba7975f3d7a746e9218e56ab46a1bc4b42552/tomli-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1", size = 253290, upload-time = "2026-03-25T20:21:27.46Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d6/2f/4a3c322f22c5c66c4b836ec58211641a4067364f5dcdd7b974b4c5da300c/tomli-2.4.1-cp312-cp312-win32.whl", hash = "sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917", size = 98141, upload-time = "2026-03-25T20:21:28.492Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/24/22/4daacd05391b92c55759d55eaee21e1dfaea86ce5c571f10083360adf534/tomli-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9", size = 108847, upload-time = "2026-03-25T20:21:29.386Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/68/fd/70e768887666ddd9e9f5d85129e84910f2db2796f9096aa02b721a53098d/tomli-2.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257", size = 95088, upload-time = "2026-03-25T20:21:30.677Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/07/06/b823a7e818c756d9a7123ba2cda7d07bc2dd32835648d1a7b7b7a05d848d/tomli-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54", size = 155866, upload-time = "2026-03-25T20:21:31.65Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/14/6f/12645cf7f08e1a20c7eb8c297c6f11d31c1b50f316a7e7e1e1de6e2e7b7e/tomli-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a", size = 149887, upload-time = "2026-03-25T20:21:33.028Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5c/e0/90637574e5e7212c09099c67ad349b04ec4d6020324539297b634a0192b0/tomli-2.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897", size = 243704, upload-time = "2026-03-25T20:21:34.51Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/10/8f/d3ddb16c5a4befdf31a23307f72828686ab2096f068eaf56631e136c1fdd/tomli-2.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f", size = 251628, upload-time = "2026-03-25T20:21:36.012Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e3/f1/dbeeb9116715abee2485bf0a12d07a8f31af94d71608c171c45f64c0469d/tomli-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d", size = 247180, upload-time = "2026-03-25T20:21:37.136Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d3/74/16336ffd19ed4da28a70959f92f506233bd7cfc2332b20bdb01591e8b1d1/tomli-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5", size = 251674, upload-time = "2026-03-25T20:21:38.298Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/16/f9/229fa3434c590ddf6c0aa9af64d3af4b752540686cace29e6281e3458469/tomli-2.4.1-cp313-cp313-win32.whl", hash = "sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd", size = 97976, upload-time = "2026-03-25T20:21:39.316Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6a/1e/71dfd96bcc1c775420cb8befe7a9d35f2e5b1309798f009dca17b7708c1e/tomli-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36", size = 108755, upload-time = "2026-03-25T20:21:40.248Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/83/7a/d34f422a021d62420b78f5c538e5b102f62bea616d1d75a13f0a88acb04a/tomli-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd", size = 95265, upload-time = "2026-03-25T20:21:41.219Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3c/fb/9a5c8d27dbab540869f7c1f8eb0abb3244189ce780ba9cd73f3770662072/tomli-2.4.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf", size = 155726, upload-time = "2026-03-25T20:21:42.23Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/62/05/d2f816630cc771ad836af54f5001f47a6f611d2d39535364f148b6a92d6b/tomli-2.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac", size = 149859, upload-time = "2026-03-25T20:21:43.386Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ce/48/66341bdb858ad9bd0ceab5a86f90eddab127cf8b046418009f2125630ecb/tomli-2.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662", size = 244713, upload-time = "2026-03-25T20:21:44.474Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/df/6d/c5fad00d82b3c7a3ab6189bd4b10e60466f22cfe8a08a9394185c8a8111c/tomli-2.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853", size = 252084, upload-time = "2026-03-25T20:21:45.62Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/00/71/3a69e86f3eafe8c7a59d008d245888051005bd657760e96d5fbfb0b740c2/tomli-2.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15", size = 247973, upload-time = "2026-03-25T20:21:46.937Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/67/50/361e986652847fec4bd5e4a0208752fbe64689c603c7ae5ea7cb16b1c0ca/tomli-2.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba", size = 256223, upload-time = "2026-03-25T20:21:48.467Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8c/9a/b4173689a9203472e5467217e0154b00e260621caa227b6fa01feab16998/tomli-2.4.1-cp314-cp314-win32.whl", hash = "sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6", size = 98973, upload-time = "2026-03-25T20:21:49.526Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/14/58/640ac93bf230cd27d002462c9af0d837779f8773bc03dee06b5835208214/tomli-2.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7", size = 109082, upload-time = "2026-03-25T20:21:50.506Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d5/2f/702d5e05b227401c1068f0d386d79a589bb12bf64c3d2c72ce0631e3bc49/tomli-2.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232", size = 96490, upload-time = "2026-03-25T20:21:51.474Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/45/4b/b877b05c8ba62927d9865dd980e34a755de541eb65fffba52b4cc495d4d2/tomli-2.4.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4", size = 164263, upload-time = "2026-03-25T20:21:52.543Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/24/79/6ab420d37a270b89f7195dec5448f79400d9e9c1826df982f3f8e97b24fd/tomli-2.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c", size = 160736, upload-time = "2026-03-25T20:21:53.674Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/02/e0/3630057d8eb170310785723ed5adcdfb7d50cb7e6455f85ba8a3deed642b/tomli-2.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d", size = 270717, upload-time = "2026-03-25T20:21:55.129Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7a/b4/1613716072e544d1a7891f548d8f9ec6ce2faf42ca65acae01d76ea06bb0/tomli-2.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41", size = 278461, upload-time = "2026-03-25T20:21:56.228Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/05/38/30f541baf6a3f6df77b3df16b01ba319221389e2da59427e221ef417ac0c/tomli-2.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c", size = 274855, upload-time = "2026-03-25T20:21:57.653Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/77/a3/ec9dd4fd2c38e98de34223b995a3b34813e6bdadf86c75314c928350ed14/tomli-2.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f", size = 283144, upload-time = "2026-03-25T20:21:59.089Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ef/be/605a6261cac79fba2ec0c9827e986e00323a1945700969b8ee0b30d85453/tomli-2.4.1-cp314-cp314t-win32.whl", hash = "sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8", size = 108683, upload-time = "2026-03-25T20:22:00.214Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/12/64/da524626d3b9cc40c168a13da8335fe1c51be12c0a63685cc6db7308daae/tomli-2.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26", size = 121196, upload-time = "2026-03-25T20:22:01.169Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5a/cd/e80b62269fc78fc36c9af5a6b89c835baa8af28ff5ad28c7028d60860320/tomli-2.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396", size = 100393, upload-time = "2026-03-25T20:22:02.137Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583, upload-time = "2026-03-25T20:22:03.012Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tomlkit"
|
||||
version = "0.13.3"
|
||||
|
|
@ -3540,16 +3636,16 @@ dependencies = [
|
|||
{ name = "typing-extensions", marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
|
||||
]
|
||||
wheels = [
|
||||
{ url = "https://download.pytorch.org/whl/cu128/torch-2.8.0%2Bcu128-cp310-cp310-manylinux_2_28_x86_64.whl" },
|
||||
{ url = "https://download.pytorch.org/whl/cu128/torch-2.8.0%2Bcu128-cp310-cp310-win_amd64.whl" },
|
||||
{ url = "https://download.pytorch.org/whl/cu128/torch-2.8.0%2Bcu128-cp311-cp311-manylinux_2_28_x86_64.whl" },
|
||||
{ url = "https://download.pytorch.org/whl/cu128/torch-2.8.0%2Bcu128-cp311-cp311-win_amd64.whl" },
|
||||
{ url = "https://download.pytorch.org/whl/cu128/torch-2.8.0%2Bcu128-cp312-cp312-manylinux_2_28_x86_64.whl" },
|
||||
{ url = "https://download.pytorch.org/whl/cu128/torch-2.8.0%2Bcu128-cp312-cp312-win_amd64.whl" },
|
||||
{ url = "https://download.pytorch.org/whl/cu128/torch-2.8.0%2Bcu128-cp313-cp313-manylinux_2_28_x86_64.whl" },
|
||||
{ url = "https://download.pytorch.org/whl/cu128/torch-2.8.0%2Bcu128-cp313-cp313-win_amd64.whl" },
|
||||
{ url = "https://download.pytorch.org/whl/cu128/torch-2.8.0%2Bcu128-cp313-cp313t-manylinux_2_28_x86_64.whl" },
|
||||
{ url = "https://download.pytorch.org/whl/cu128/torch-2.8.0%2Bcu128-cp313-cp313t-win_amd64.whl" },
|
||||
{ url = "https://download-r2.pytorch.org/whl/cu128/torch-2.8.0%2Bcu128-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:0c96999d15cf1f13dd7c913e0b21a9a355538e6cfc10861a17158320292f5954", upload-time = "2025-10-01T23:47:19Z" },
|
||||
{ url = "https://download-r2.pytorch.org/whl/cu128/torch-2.8.0%2Bcu128-cp310-cp310-win_amd64.whl", hash = "sha256:43938e9a174c90e5eb9e906532b2f1e21532bbfa5a61b65193b4f54714d34f9e", upload-time = "2025-10-01T23:47:42Z" },
|
||||
{ url = "https://download-r2.pytorch.org/whl/cu128/torch-2.8.0%2Bcu128-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:039b9dcdd6bdbaa10a8a5cd6be22c4cb3e3589a341e5f904cbb571ca28f55bed", upload-time = "2025-10-01T23:49:06Z" },
|
||||
{ url = "https://download-r2.pytorch.org/whl/cu128/torch-2.8.0%2Bcu128-cp311-cp311-win_amd64.whl", hash = "sha256:34c55443aafd31046a7963b63d30bc3b628ee4a704f826796c865fdfd05bb596", upload-time = "2025-10-01T23:49:30Z" },
|
||||
{ url = "https://download-r2.pytorch.org/whl/cu128/torch-2.8.0%2Bcu128-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:4354fc05bb79b208d6995a04ca1ceef6a9547b1c4334435574353d381c55087c", upload-time = "2025-10-01T23:51:02Z" },
|
||||
{ url = "https://download-r2.pytorch.org/whl/cu128/torch-2.8.0%2Bcu128-cp312-cp312-win_amd64.whl", hash = "sha256:0ad925202387f4e7314302a1b4f8860fa824357f9b1466d7992bf276370ebcff", upload-time = "2025-10-01T23:51:26Z" },
|
||||
{ url = "https://download-r2.pytorch.org/whl/cu128/torch-2.8.0%2Bcu128-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:3a852369a38dec343d45ecd0bc3660f79b88a23e0c878d18707f7c13bf49538f", upload-time = "2025-10-01T23:52:56Z" },
|
||||
{ url = "https://download-r2.pytorch.org/whl/cu128/torch-2.8.0%2Bcu128-cp313-cp313-win_amd64.whl", hash = "sha256:9e20646802b7fc295c1f8b45fefcfc9fb2e4ec9cbe8593443cd2b9cc307c8405", upload-time = "2025-10-01T23:53:20Z" },
|
||||
{ url = "https://download-r2.pytorch.org/whl/cu128/torch-2.8.0%2Bcu128-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:4295a22d69408e93d25f51e8d5d579345b6b802383e9414b0f3853ed433d53ae", upload-time = "2025-10-01T23:54:56Z" },
|
||||
{ url = "https://download-r2.pytorch.org/whl/cu128/torch-2.8.0%2Bcu128-cp313-cp313t-win_amd64.whl", hash = "sha256:970b4f4661fa7b44f6a7e6df65de7fc4a6fff2af610dc415c1d695ca5f1f37d2", upload-time = "2025-10-01T23:55:23Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -3612,16 +3708,16 @@ dependencies = [
|
|||
{ name = "torch", version = "2.8.0+cu128", source = { registry = "https://download.pytorch.org/whl/cu128" }, marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
|
||||
]
|
||||
wheels = [
|
||||
{ url = "https://download.pytorch.org/whl/cu128/torchaudio-2.8.0%2Bcu128-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a0161e95285a0b716de210fee0392151d601e7da3cc86595008d826abff48a8c" },
|
||||
{ url = "https://download.pytorch.org/whl/cu128/torchaudio-2.8.0%2Bcu128-cp310-cp310-win_amd64.whl", hash = "sha256:5d7a9d913e2744573ed3b7ec2f781ed39833c81c9c41859973ec10ac174c2366" },
|
||||
{ url = "https://download.pytorch.org/whl/cu128/torchaudio-2.8.0%2Bcu128-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:f4409df567d0723a7a3a89d32c7552a17e0ff6f137ea26a0d268c665259b2995" },
|
||||
{ url = "https://download.pytorch.org/whl/cu128/torchaudio-2.8.0%2Bcu128-cp311-cp311-win_amd64.whl", hash = "sha256:7a1eb6154e05b8056b34c7a41495e09d57f79eb0180eb4e7f3bb2a61845ca8ea" },
|
||||
{ url = "https://download.pytorch.org/whl/cu128/torchaudio-2.8.0%2Bcu128-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:145b8a0c21cfcaa1705c67173c5d439087e0e120d5da9bc344746f937901d243" },
|
||||
{ url = "https://download.pytorch.org/whl/cu128/torchaudio-2.8.0%2Bcu128-cp312-cp312-win_amd64.whl", hash = "sha256:cce3a60cd9a97f7360c8f95504ac349311fb7d6b9b826135936764f4de5f782d" },
|
||||
{ url = "https://download.pytorch.org/whl/cu128/torchaudio-2.8.0%2Bcu128-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:410bb8ea46225efe658e5d27a3802c181a2255913003621a5d25a51aca8018d9" },
|
||||
{ url = "https://download.pytorch.org/whl/cu128/torchaudio-2.8.0%2Bcu128-cp313-cp313-win_amd64.whl", hash = "sha256:3146bbd48992d215f6bb1aef9626d734c3180b377791ded2a4d4d2c0e63c0cc2" },
|
||||
{ url = "https://download.pytorch.org/whl/cu128/torchaudio-2.8.0%2Bcu128-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:04b410f93337fc6c16576d0c88e2a31091aef9d1fd212ebb8cd26899dba175e0" },
|
||||
{ url = "https://download.pytorch.org/whl/cu128/torchaudio-2.8.0%2Bcu128-cp313-cp313t-win_amd64.whl", hash = "sha256:1054e0a7613cac54ed9b3784a5fcbe023748a70004d9cca74c5f9ae00a1fdfd1" },
|
||||
{ url = "https://download-r2.pytorch.org/whl/cu128/torchaudio-2.8.0%2Bcu128-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a0161e95285a0b716de210fee0392151d601e7da3cc86595008d826abff48a8c", upload-time = "2025-08-05T20:12:07Z" },
|
||||
{ url = "https://download-r2.pytorch.org/whl/cu128/torchaudio-2.8.0%2Bcu128-cp310-cp310-win_amd64.whl", hash = "sha256:5d7a9d913e2744573ed3b7ec2f781ed39833c81c9c41859973ec10ac174c2366", upload-time = "2025-08-05T20:12:07Z" },
|
||||
{ url = "https://download-r2.pytorch.org/whl/cu128/torchaudio-2.8.0%2Bcu128-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:f4409df567d0723a7a3a89d32c7552a17e0ff6f137ea26a0d268c665259b2995", upload-time = "2025-08-05T20:12:07Z" },
|
||||
{ url = "https://download-r2.pytorch.org/whl/cu128/torchaudio-2.8.0%2Bcu128-cp311-cp311-win_amd64.whl", hash = "sha256:7a1eb6154e05b8056b34c7a41495e09d57f79eb0180eb4e7f3bb2a61845ca8ea", upload-time = "2025-08-05T20:12:07Z" },
|
||||
{ url = "https://download-r2.pytorch.org/whl/cu128/torchaudio-2.8.0%2Bcu128-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:145b8a0c21cfcaa1705c67173c5d439087e0e120d5da9bc344746f937901d243", upload-time = "2025-08-05T20:12:07Z" },
|
||||
{ url = "https://download-r2.pytorch.org/whl/cu128/torchaudio-2.8.0%2Bcu128-cp312-cp312-win_amd64.whl", hash = "sha256:cce3a60cd9a97f7360c8f95504ac349311fb7d6b9b826135936764f4de5f782d", upload-time = "2025-08-05T20:12:07Z" },
|
||||
{ url = "https://download-r2.pytorch.org/whl/cu128/torchaudio-2.8.0%2Bcu128-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:410bb8ea46225efe658e5d27a3802c181a2255913003621a5d25a51aca8018d9", upload-time = "2025-08-05T20:12:07Z" },
|
||||
{ url = "https://download-r2.pytorch.org/whl/cu128/torchaudio-2.8.0%2Bcu128-cp313-cp313-win_amd64.whl", hash = "sha256:3146bbd48992d215f6bb1aef9626d734c3180b377791ded2a4d4d2c0e63c0cc2", upload-time = "2025-08-05T20:12:07Z" },
|
||||
{ url = "https://download-r2.pytorch.org/whl/cu128/torchaudio-2.8.0%2Bcu128-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:04b410f93337fc6c16576d0c88e2a31091aef9d1fd212ebb8cd26899dba175e0", upload-time = "2025-08-05T20:12:07Z" },
|
||||
{ url = "https://download-r2.pytorch.org/whl/cu128/torchaudio-2.8.0%2Bcu128-cp313-cp313t-win_amd64.whl", hash = "sha256:1054e0a7613cac54ed9b3784a5fcbe023748a70004d9cca74c5f9ae00a1fdfd1", upload-time = "2025-08-05T20:12:07Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -3846,7 +3942,7 @@ name = "wetext"
|
|||
version = "0.1.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "kaldifst" },
|
||||
{ name = "kaldifst", marker = "platform_machine != 'aarch64' or sys_platform != 'linux'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f8/5c/742a6de1b6cd3215d5610ae0e4580f761c5dbf2e9e246afe76ed64c3f622/wetext-0.1.0.tar.gz", hash = "sha256:0f9645bc4b6c90119d54dd9f752db40497630a05af80e627f114204cc78f3ad5", size = 1457764, upload-time = "2025-09-08T14:23:47.85Z" }
|
||||
wheels = [
|
||||
|
|
|
|||
8
webui.py
8
webui.py
|
|
@ -32,7 +32,6 @@ parser.add_argument("--gui_seg_tokens", type=int, default=120, help="GUI: Max to
|
|||
cmd_args = parser.parse_args()
|
||||
|
||||
required_files = [
|
||||
"config.yaml",
|
||||
"bpe.model",
|
||||
"gpt.pth",
|
||||
"s2mel.pth",
|
||||
|
|
@ -56,6 +55,13 @@ if missing:
|
|||
sys.exit(1)
|
||||
print("Model downloaded successfully.")
|
||||
|
||||
from indextts.utils.model_download import ensure_config_available
|
||||
try:
|
||||
ensure_config_available(cmd_args.model_dir)
|
||||
except Exception as e:
|
||||
print(f"Failed to download config.yaml: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
import gradio as gr
|
||||
from indextts.infer_v2 import IndexTTS2
|
||||
from indextts.utils.examples_downloader import ensure_examples_available
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue