Skill: axdlee/siliconflow-media (version 1.0.0)

--- name: siliconflow-media description: SiliconFlow 多模态服务，支持图片生成(FLUX/Qwen)、视频生成(Wan)、TTS语音合成、ASR语音识别。使用代金券支付。 metadata: openclaw: emoji: "🚀" requires: env: - SILICONFLOW_API_KEY primaryEnv: SILICONFLOW_API_KEY --- # SiliconFlow 媒体服务 SiliconFlow 提供丰富的 AI 模型服务，支持代金券支付（当前余额 3000+）。 ## 环境变量 - `SILICONFLOW_API_KEY` - SiliconFlow API Key ## 🎨 图片生成 ```bash uv run {baseDir}/scripts/image_gen.py --prompt "描述" --filename "output.png" [--model MODEL] ``` **可用模型**： | 参数 | 模型 | 说明 | |------|------|------| | `flux` (默认) | FLUX.1-schnell | 快速高质量，约 5-10 秒 | | `flux-dev` | FLUX.1-dev | 开发版 | | `flux-pro` | FLUX.1-pro | 专业版 | | `qwen` | Qwen/Qwen-Image | 通义图片生成 | | `qwen-edit` | Qwen/Qwen-Image-Edit | 图片编辑 | | `qwen-edit-2509` | Qwen/Qwen-Image-Edit-2509 | 最新编辑版 | **示例**： ```bash # FLUX 快速生成 uv run {baseDir}/scripts/image_gen.py --prompt "a cute robot assistant" --filename "robot.png" # Qwen 生成 uv run {baseDir}/scripts/image_gen.py --prompt "山水画" --filename "landscape.png" --model qwen ``` ## 🎬 视频生成 ```bash # 文生视频 uv run {baseDir}/scripts/video_gen.py --prompt "描述" --filename "output.mp4" # 图生视频 uv run {baseDir}/scripts/video_gen.py --prompt "描述" --image "input.png" --filename "output.mp4" ``` **模型**： - 文生视频: `Wan-AI/Wan2.2-T2V-A14B` - 图生视频: `Wan-AI/Wan2.2-I2V-A14B` ⚠️ 视频生成时间较长（约 2-5 分钟） ## 🎤 语音合成 (TTS) ```bash uv run {baseDir}/scripts/tts.py --text "要合成的文字" --filename "output.mp3" [--model MODEL] ``` **可用模型**： | 参数 | 模型 | 说明 | |------|------|------| | `fish-speech` (默认) | fish-speech-1.5 | Fish Audio 高质量 | | `cosyvoice` | CosyVoice2-0.5B | 阿里语音克隆 | | `indextts` | IndexTTS-2 | Index TTS | | `moss` | MOSS-TTSD-v0.5 | MOSS 多语言 | **示例**： ```bash uv run {baseDir}/scripts/tts.py --text "你好世界" --filename "hello.mp3" ``` ## 👂 语音识别 (ASR) ```bash uv run {baseDir}/scripts/asr.py --audio "input.mp3" [--model MODEL] ``` **可用模型**： | 参数 | 模型 | 说明 | |------|------|------| | `sensevoice` (默认) | SenseVoiceSmall | 阿里语音识别 | | `teleai` | TeleSpeechASR | TeleAI 识别 | **示例**： ```bash uv run {baseDir}/scripts/asr.py --audio "recording.mp3" ``` ## 注意事项 1. ✅ 费用从代金券扣除，无需额外付费 2. ⏱️ 图片生成约 5-10 秒 3. ⏱️ 视频生成约 2-5 分钟（耐心等待） 4. 📝 所有脚本会打印 `MEDIA:` 行用于自动附加文件

path: scripts/tts.py
content: #!/usr/bin/env python3
# /// script
# requires-python = ">=3.10"
# dependencies = [
#     "requests>=2.28.0",
# ]
# ///
"""
Text-to-Speech using SiliconFlow models.

Usage:
    uv run tts.py --text "要合成的文字" --filename "output.mp3" [--model MODEL]
    
Examples:
    uv run tts.py --text "你好世界" --filename "hello.mp3"
    uv run tts.py --text "Hello world" --filename "hello.mp3" --model cosyvoice
"""

import argparse
import os
import sys
from pathlib import Path

import requests


SILICONFLOW_URL = "https://api.siliconflow.cn/v1/audio/speech"

MODELS = {
    "fish-speech": "fishaudio/fish-speech-1.5",
    "cosyvoice": "FunAudioLLM/CosyVoice2-0.5B",
    "indextts": "IndexTeam/IndexTTS-2",
    "moss": "fnlp/MOSS-TTSD-v0.5",
}

DEFAULT_VOICES = {
    "fishaudio/fish-speech-1.5": "fishaudio/fish-speech-1.5:anna",
    "FunAudioLLM/CosyVoice2-0.5B": "FunAudioLLM/CosyVoice2-0.5B:alex",
    "IndexTeam/IndexTTS-2": "IndexTeam/IndexTTS-2:default",
    "fnlp/MOSS-TTSD-v0.5": "fnlp/MOSS-TTSD-v0.5:default",
}


def get_api_key() -> str:
    key = os.environ.get("SILICONFLOW_API_KEY")
    if not key:
        print("Error: SILICONFLOW_API_KEY not set", file=sys.stderr)
        sys.exit(1)
    return key


def text_to_speech(text: str, output_path: Path, model: str, api_key: str) -> bool:
    model_id = MODELS.get(model, MODELS["fish-speech"])
    voice = DEFAULT_VOICES.get(model_id, f"{model_id}:default")
    
    payload = {
        "model": model_id,
        "input": text,
        "voice": voice,
    }
    
    headers = {
        "Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json"
    }
    
    print(f"🎤 Synthesizing speech with {model_id}...")
    
    try:
        response = requests.post(SILICONFLOW_URL, json=payload, headers=headers, timeout=120)
        response.raise_for_status()
        
        with open(output_path, "wb") as f:
            f.write(response.content)
        
        print(f"✅ Audio saved: {output_path.resolve()}")
        print(f"MEDIA: {output_path.resolve()}")
        return True
            
    except Exception as e:
        print(f"❌ Error: {e}", file=sys.stderr)
        return False


def main():
    parser = argparse.ArgumentParser(description="Text-to-Speech with SiliconFlow")
    parser.add_argument("--text", "-t", required=True, help="Text to synthesize")
    parser.add_argument("--filename", "-f", required=True, help="Output filename (e.g., output.mp3)")
    parser.add_argument("--model", "-m", choices=list(MODELS.keys()), default="fish-speech",
                        help="TTS model (default: fish-speech)")
    
    args = parser.parse_args()
    
    output_path = Path(args.filename)
    output_path.parent.mkdir(parents=True, exist_ok=True)
    
    [REDACTED_SECRET])
    
    if not text_to_speech(args.text, output_path, args.model, api_key):
        sys.exit(1)


if __name__ == "__main__":
    main()


path: scripts/asr.py
content: #!/usr/bin/env python3
# /// script
# requires-python = ">=3.10"
# dependencies = [
#     "requests>=2.28.0",
# ]
# ///
"""
Automatic Speech Recognition using SiliconFlow models.

Usage:
    uv run asr.py --audio "input.mp3" [--model MODEL]
    
Examples:
    uv run asr.py --audio "recording.mp3"
    uv run asr.py --audio "voice.m4a" --model teleai
"""

import argparse
import os
import sys
from pathlib import Path

import requests


SILICONFLOW_URL = "https://api.siliconflow.cn/v1/audio/transcriptions"

MODELS = {
    "sensevoice": "FunAudioLLM/SenseVoiceSmall",
    "teleai": "TeleAI/TeleSpeechASR",
}


def get_api_key() -> str:
    key = os.environ.get("SILICONFLOW_API_KEY")
    if not key:
        print("Error: SILICONFLOW_API_KEY not set", file=sys.stderr)
        sys.exit(1)
    return key


def transcribe(audio_path: str, model: str, api_key: str) -> str:
    model_id = MODELS.get(model, MODELS["sensevoice"])
    
    with open(audio_path, "rb") as f:
        audio_data = f.read()
    
    files = {
        "file": (Path(audio_path).name, audio_data),
        "model": (None, model_id),
    }
    
    headers = {
        "Authorization": f"Bearer {api_key}",
    }
    
    print(f"👂 Transcribing with {model_id}...")
    
    try:
        response = requests.post(SILICONFLOW_URL, files=files, headers=headers, timeout=120)
        response.raise_for_status()
        
        data = response.json()
        
        if "text" in data:
            return data["text"]
        elif "results" in data:
            return " ".join(r.get("text", "") for r in data["results"])
        else:
            return str(data)
            
    except Exception as e:
        print(f"❌ Error: {e}", file=sys.stderr)
        return None


def main():
    parser = argparse.ArgumentParser(description="Speech recognition with SiliconFlow")
    parser.add_argument("--audio", "-a", required=True, help="Audio file path")
    parser.add_argument("--model", "-m", choices=list(MODELS.keys()), default="sensevoice",
                        help="ASR model (default: sensevoice)")
    
    args = parser.parse_args()
    
    if not os.path.exists(args.audio):
        print(f"❌ Audio file not found: {args.audio}", file=sys.stderr)
        sys.exit(1)
    
    [REDACTED_SECRET])
    
    result = transcribe(args.audio, args.model, api_key)
    
    if result:
        print(f"\n📝 Transcription:\n{result}")
    else:
        sys.exit(1)


if __name__ == "__main__":
    main()


path: scripts/image_gen.py
content: #!/usr/bin/env python3
# /// script
# requires-python = ">=3.10"
# dependencies = [
#     "requests>=2.28.0",
#     "pillow>=10.0.0",
# ]
# ///
"""
Generate images using SiliconFlow FLUX or Qwen models.

Usage:
    uv run image_gen.py --prompt "description" --filename "output.png" [--model flux|qwen]
    
Examples:
    uv run image_gen.py --prompt "a cute robot" --filename "robot.png"
    uv run image_gen.py --prompt "山水画" --filename "landscape.png" --model qwen
"""

import argparse
import os
import sys
import random
import time
from pathlib import Path
from io import BytesIO

import requests
from PIL import Image as PILImage


SILICONFLOW_URL = "https://api.siliconflow.cn/v1/images/generations"

MODELS = {
    "flux": "black-forest-labs/FLUX.1-schnell",
    "flux-dev": "black-forest-labs/FLUX.1-dev", 
    "flux-pro": "black-forest-labs/FLUX.1-pro",
    "qwen": "Qwen/Qwen-Image",
    "qwen-edit": "Qwen/Qwen-Image-Edit",
    "qwen-edit-2509": "Qwen/Qwen-Image-Edit-2509",
}


def get_api_key() -> str:
    key = os.environ.get("SILICONFLOW_API_KEY")
    if not key:
        print("Error: SILICONFLOW_API_KEY not set", file=sys.stderr)
        sys.exit(1)
    return key


def generate_image(prompt: str, output_path: Path, model: str, api_key: str, size: str = "1024x1024") -> bool:
    model_id = MODELS.get(model, MODELS["flux"])
    
    payload = {
        "model": model_id,
        "prompt": prompt,
        "image_size": size,
        "num_inference_steps": 20,
        "seed": random.randint(1, 999999999)
    }
    
    headers = {
        "Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json"
    }
    
    print(f"🎨 Generating image with {model_id}...")
    start_time = time.time()
    
    try:
        response = requests.post(SILICONFLOW_URL, json=payload, headers=headers, timeout=120)
        response.raise_for_status()
        
        data = response.json()
        
        if "images" in data and len(data["images"]) > 0:
            image_url = data["images"][0]["url"]
            
            print(f"📥 Downloading image...")
            img_response = requests.get(image_url, timeout=60)
            img_response.raise_for_status()
            
            image = PILImage.open(BytesIO(img_response.content))
            if image.mode == 'RGBA':
                rgb_image = PILImage.new('RGB', image.size, (255, 255, 255))
                rgb_image.paste(image, mask=image.split()[3])
                rgb_image.save(str(output_path), 'PNG')
            else:
                image.save(str(output_path), 'PNG')
            
            elapsed = time.time() - start_time
            print(f"✅ Image saved: {output_path.resolve()}")
            print(f"⏱️  Generation time: {elapsed:.1f}s")
            print(f"MEDIA: {output_path.resolve()}")
            return True
        else:
            print(f"❌ Unexpected response: {data}", file=sys.stderr)
            return False
            
    except Exception as e:
        print(f"❌ Error: {e}", file=sys.stderr)
        return False


def main():
    parser = argparse.ArgumentParser(description="Generate images with SiliconFlow")
    parser.add_argument("--prompt", "-p", required=True, help="Image description")
    parser.add_argument("--filename", "-f", required=True, help="Output filename")
    parser.add_argument("--model", "-m", choices=list(MODELS.keys()), default="flux",
                        help="Model to use (default: flux)")
    parser.add_argument("--size", "-s", default="1024x1024", help="Image size (default: 1024x1024)")
    
    args = parser.parse_args()
    
    output_path = Path(args.filename)
    output_path.parent.mkdir(parents=True, exist_ok=True)
    
    [REDACTED_SECRET])
    
    if not generate_image(args.prompt, output_path, args.model, api_key, args.size):
        sys.exit(1)


if __name__ == "__main__":
    main()


path: scripts/video_gen.py
content: #!/usr/bin/env python3
# /// script
# requires-python = ">=3.10"
# dependencies = [
#     "requests>=2.28.0",
# ]
# ///
"""
Generate videos using SiliconFlow Wan models.

Usage:
    # Text to video
    uv run video_gen.py --prompt "description" --filename "output.mp4"
    
    # Image to video
    uv run video_gen.py --prompt "description" --image "input.png" --filename "output.mp4"
    
Examples:
    uv run video_gen.py --prompt "a cat walking in a garden" --filename "cat.mp4"
    uv run video_gen.py --prompt "make this image come alive" --image "photo.png" --filename "animated.mp4"
"""

import argparse
import os
import sys
import time
import base64
from pathlib import Path

import requests


SILICONFLOW_URL = "https://api.siliconflow.cn/v1/video/generations"

MODELS = {
    "t2v": "Wan-AI/Wan2.2-T2V-A14B",  # Text to video
    "i2v": "Wan-AI/Wan2.2-I2V-A14B",  # Image to video
}


def get_api_key() -> str:
    key = os.environ.get("SILICONFLOW_API_KEY")
    if not key:
        print("Error: SILICONFLOW_API_KEY not set", file=sys.stderr)
        sys.exit(1)
    return key


def encode_image_to_base64(image_path: str) -> str:
    with open(image_path, "rb") as f:
        return base64.b64encode(f.read()).decode("utf-8")


def generate_video(prompt: str, output_path: Path, api_key: str, image_path: str = None) -> bool:
    if image_path:
        model = MODELS["i2v"]
        print(f"🎬 Generating video from image using {model}...")
    else:
        model = MODELS["t2v"]
        print(f"🎬 Generating video from text using {model}...")
    
    payload = {
        "model": model,
        "prompt": prompt,
    }
    
    if image_path:
        payload["image"] = encode_image_to_base64(image_path)
    
    headers = {
        "Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json"
    }
    
    start_time = time.time()
    print("⏳ Submitting generation request...")
    
    try:
        response = requests.post(SILICONFLOW_URL, json=payload, headers=headers, timeout=300)
        response.raise_for_status()
        
        data = response.json()
        
        # Check for direct video URL
        if "videos" in data and len(data["videos"]) > 0:
            video_url = data["videos"][0].get("url")
            if video_url:
                print(f"📥 Downloading video...")
                video_response = requests.get(video_url, timeout=300)
                video_response.raise_for_status()
                
                with open(output_path, "wb") as f:
                    f.write(video_response.content)
                
                elapsed = time.time() - start_time
                print(f"✅ Video saved: {output_path.resolve()}")
                print(f"⏱️  Total time: {elapsed:.1f}s")
                print(f"MEDIA: {output_path.resolve()}")
                return True
        
        # Check for async task
        if "id" in data:
            task_id = data["id"]
            print(f"⏳ Task submitted, polling for result... (task_id: {task_id})")
            
            status_url = f"https://api.siliconflow.cn/v1/video/generations/{task_id}"
            max_wait = 600  # 10 minutes max
            
            while time.time() - start_time < max_wait:
                time.sleep(10)
                
                status_response = requests.get(status_url, headers=headers, timeout=30)
                status_response.raise_for_status()
                status_data = status_response.json()
                
                status = status_data.get("status", "unknown")
                elapsed = time.time() - start_time
                print(f"  [{elapsed:.0f}s] Status: {status}")
                
                if status == "succeeded" and "videos" in status_data:
                    video_url = status_data["videos"][0].get("url")
                    if video_url:
                        print(f"📥 Downloading video...")
                        video_response = requests.get(video_url, timeout=300)
                        video_response.raise_for_status()
                        
                        with open(output_path, "wb") as f:
                            f.write(video_response.content)
                        
                        elapsed = time.time() - start_time
                        print(f"✅ Video saved: {output_path.resolve()}")
                        print(f"⏱️  Total time: {elapsed:.1f}s")
                        print(f"MEDIA: {output_path.resolve()}")
                        return True
                
                elif status == "failed":
                    print(f"❌ Video generation failed: {status_data}", file=sys.stderr)
                    return False
            
            print("❌ Timeout waiting for video", file=sys.stderr)
            return False
        
        print(f"❌ Unexpected response: {data}", file=sys.stderr)
        return False
            
    except Exception as e:
        print(f"❌ Error: {e}", file=sys.stderr)
        return False


def main():
    parser = argparse.ArgumentParser(description="Generate videos with SiliconFlow")
    parser.add_argument("--prompt", "-p", required=True, help="Video description")
    parser.add_argument("--filename", "-f", required=True, help="Output filename (e.g., video.mp4)")
    parser.add_argument("--image", "-i", help="Input image for image-to-video")
    
    args = parser.parse_args()
    
    output_path = Path(args.filename)
    output_path.parent.mkdir(parents=True, exist_ok=True)
    
    [REDACTED_SECRET])
    
    if not generate_video(args.prompt, output_path, api_key, args.image):
        sys.exit(1)


if __name__ == "__main__":
    main()


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (HIGH)