| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104 |
- #!/usr/bin/env python3
- """下载所有阿里云音色样本到 project/voices/ 目录"""
- import json, os, re, requests, time, sys
- OUTPUT_DIR = "voices"
- os.makedirs(OUTPUT_DIR, exist_ok=True)
- # 先获取全部音色URL(不只是前80个)
- print("正在通过浏览器提取全部音色...")
- import subprocess
- result = subprocess.run([
- "npx", "playwright-cli", "run-code",
- r"""async (page) => {
- const data = await page.evaluate(() => {
- const result = [];
- document.querySelectorAll('audio').forEach(audio => {
- const src = audio.querySelector('source')?.src || audio.src;
- if (src) {
- const tr = audio.closest('tr');
- const text = tr?.textContent || '';
- const vm = text.match(/voice\s*参数\s*[::]\s*([a-z]\w*)/i);
- const nm = text.match(/名称\s*[::]\s*(\S+)/);
- result.push({url: src, voice: vm?vm[1]:'', name: nm?nm[1]:''});
- }
- });
- return result;
- });
- return JSON.stringify(data);
- }"""], capture_output=True, text=True, timeout=60,
- cwd=r"c:/Users/caoyg/ai/audio-tts/audio_codebuddy")
- output = result.stdout
- # 提取真正的 JSON
- json_match = re.search(r'"(\[.*\])"', output, re.DOTALL)
- if not json_match:
- print("无法解析输出,前500字符:", output[:500])
- sys.exit(1)
- json_str = json_match.group(1)
- # 反转义
- json_str = json_str.replace('\\"', '"').replace('\\\\', '\\')
- voices = json.loads(json_str)
- print(f"找到 {len(voices)} 个音色样本\n")
- # 按模型分类计数器
- stats = {}
- downloaded = 0
- failed = 0
- skipped = 0
- for i, v in enumerate(voices):
- url = v['url']
- voice = v['voice']
- name = v.get('name', '')
-
- if not url:
- skipped += 1
- continue
-
- # 清理文件名
- safe_name = f"{name}_{voice}" if name else voice
- safe_name = re.sub(r'[\\/:*?"<>|]', '_', safe_name)
- safe_name = voice # 简单用voice参数名
- fname = f"{voice}.mp3"
- fpath = os.path.join(OUTPUT_DIR, fname)
-
- if os.path.exists(fpath):
- stats[voice] = stats.get(voice, 0) + 1
- skipped += 1
- continue
-
- # 进度
- pct = (i+1) * 100 // len(voices)
- bar = "█" * (pct // 5) + "░" * (20 - pct // 5)
- print(f"\r[{bar}] {pct}% {i+1}/{len(voices)} {voice:25s}", end='', flush=True)
-
- try:
- resp = requests.get(url, timeout=60, headers={
- 'Referer': 'https://help.aliyun.com/',
- 'User-Agent': 'Mozilla/5.0'
- })
- if resp.status_code == 200 and len(resp.content) > 100:
- with open(fpath, 'wb') as f:
- f.write(resp.content)
- downloaded += 1
- else:
- failed += 1
- except Exception as e:
- failed += 1
- pass
-
- time.sleep(0.2)
- print(f"\n\n完成!下载 {downloaded} 个, 跳过 {skipped} 个, 失败 {failed} 个")
- print(f"文件保存在: {OUTPUT_DIR}/")
- # 生成索引
- with open(os.path.join(OUTPUT_DIR, "INDEX.md"), "w", encoding="utf-8") as f:
- f.write("# 阿里云百炼 官方音色样本\n\n")
- f.write(f"共 {len(voices)} 个音色,来源:help.aliyun.com 官方帮助中心\n\n")
- f.write("| # | 音色名 | voice参数 | 文件名 |\n")
- f.write("|---|--------|----------|--------|\n")
- for i, v in enumerate(voices):
- f.write(f"| {i+1} | {v['name']} | `{v['voice']}` | [{v['voice']}.mp3]({v['voice']}.mp3) |\n")
|