Skill: jinkang19940922/music-downloader-jinkang (version 1.0.1)

# Music Downloader Skill 从各大音乐平台下载音乐歌曲，包含音频、封面、元数据、歌词。 ## 激活条件 用户提到以下关键词时激活： - 下载音乐 - 歌曲下载 - music download - 放歌 - 唱首歌 - 点歌 或用户提供： - 音乐链接（YouTube、SoundCloud 等） - 歌名+歌手 ## 支持平台 | 平台 | 支持状态 | |------|---------| | YouTube | ✅ 完全支持 | | SoundCloud | ✅ 完全支持 | | Vimeo | ✅ 完全支持 | | 网易云音乐 | ⚠️ 需要 cookies | | QQ音乐 | ⚠️ 部分支持 | | Spotify | ⚠️ 需要 API | | 其他 1000+ 站点 | ✅ via yt-dlp | ## 功能特点 1. **自动最优音质** - 自动选择最佳音质，自动降级直到成功 2. **封面嵌入** - 自动抓取并嵌入封面图片 3. **元数据** - 标题、艺术家、专辑、年份 4. **歌词下载** - 自动下载 .lrc 歌词文件 ## 使用方式 ### 方式1：通过 URL 下载 ``` 下载 https://www.youtube.com/watch?v=xxx ``` ### 方式2：搜索下载 ``` 下载 演员 薛之谦 下载 周杰伦 晴天 下载 歌名 歌手 ``` ### 方式3：指定输出目录 ``` 下载 演员 到 /other/music 下载 晴天 到 /home/node/.openclaw/other/music ``` ## 输出规格 | 项目 | 格式 | |------|------| | 音频 | MP3（自动最优） | | 封面 | 嵌入封面 | | 元数据 | 标题、艺术家、专辑 | | 歌词 | .lrc 文件 | | 默认路径 | /other/music/ （公共空间） | ## 命令行参数（可选） ``` -q, --quality 音质: auto(默认)/128/192/320 -f, --format 格式: mp3(默认)/flac/m4a -o, --output 输出目录 ``` ## 使用示例 ``` 下载 演员 薛之谦 下载 演员 薛之谦 -q 320 下载 https://youtu.be/xxx 下载 歌名 -o /other/music ``` ## 执行文件 - Python 脚本：`skills/music-downloader/music_downloader.py` - 调用方式：直接运行 Python 脚本 ## 注意事项 - YouTube 搜索效果最好 - 部分平台需要代理/梯子 - 受限于平台 DRM，部分歌曲可能无法下载

path: music_downloader.py
content: #!/usr/bin/env python3
"""
Music Downloader - 从各大音乐平台下载音乐（音频+封面+元数据+歌词）
"""

import os
import re
import sys
import json
import subprocess
import argparse
from pathlib import Path

# 配置
OUTPUT_DIR = "/other/music"  # 公共空间 14TB
PROXY = "http://192.168.10.222:7890"

def parse_args():
    parser = argparse.ArgumentParser(description="Music Downloader")
    parser.add_argument("query", nargs="?", help="歌曲 URL 或 歌名")
    parser.add_argument("--output", "-o", default=OUTPUT_DIR, help="输出目录")
    parser.add_argument("--artist", "-a", default=None, help="指定艺术家（搜索时用）")
    parser.add_argument("--format", "-f", default="mp3", choices=["mp3", "flac", "m4a"], help="音频格式")
    parser.add_argument("--quality", "-q", default="auto", help="音频比特率 (auto/128/192/320), auto=自动最优")
    return parser.parse_args()

def get_video_info(url):
    """获取视频信息"""
    cmd = [
        "yt-dlp",
        "--dump-json",
        "--no-download",
        "--no-playlist",
        url
    ]
    env = os.environ.copy()
    env["http_proxy"] = PROXY
    env["https_proxy"] = PROXY
    
    try:
        result = subprocess.run(cmd, capture_output=True, text=True, env=env, timeout=30)
        if result.returncode == 0:
            return json.loads(result.stdout)
    except Exception as e:
        print(f"Error getting info: {e}")
    return None

def download_music(url, output_dir, audio_format="mp3", quality="auto"):
    """下载音乐"""
    # 确保输出目录存在
    Path(output_dir).mkdir(parents=True, exist_ok=True)
    
    # 输出文件名模板
    output_template = f"{output_dir}/%(title)s - %(artist)s.%(ext)s"
    
    # 构建命令 - 自动选择最佳音质
    # -f bestaudio/best: 自动选最佳音轨
    # --audio-quality 0: 最高质量
    # --write-subs: 下载字幕/歌词
    # --write-auto-subs: 下载自动字幕
    
    if quality == "auto":
        # 自动最优：不指定质量，让 yt-dlp 选最好的
        cmd = [
            "yt-dlp",
            "-f", "bestaudio/best",
            "--extract-audio",
            "--audio-format", audio_format,
            "--embed-thumbnail",
            "--add-metadata",
            "--write-subs",
            "--write-auto-subs",
            "--sub-format", "lrc",
            "--no-playlist",
            "-o", output_template,
            url
        ]
    else:
        # 指定质量
        cmd = [
            "yt-dlp",
            "-f", "bestaudio/best",
            "--extract-audio",
            "--audio-format", audio_format,
            "--audio-quality", quality + "k",
            "--embed-thumbnail",
            "--add-metadata",
            "--write-subs",
            "--write-auto-subs",
            "--sub-format", "lrc",
            "--no-playlist",
            "-o", output_template,
            url
        ]
    
    env = os.environ.copy()
    env["http_proxy"] = PROXY
    env["https_proxy"] = PROXY
    
    print(f"⬇️ 正在下载: {url}")
    print(f"📁 输出目录: {output_dir}")
    
    try:
        result = subprocess.run(cmd, capture_output=True, text=True, env=env, timeout=600)
        if result.returncode == 0:
            print("✅ 下载完成!")
            print(result.stdout)
            return True
        else:
            print(f"❌ 下载失败: {result.stderr}")
            return False
    except subprocess.TimeoutExpired:
        print("❌ 下载超时")
        return False
    except Exception as e:
        print(f"❌ 错误: {e}")
        return False

def search_and_download(query, output_dir, audio_format="mp3", quality="320"):
    """搜索并下载"""
    # 使用 yt-dlp 的搜索功能
    search_query = f"ytsearch20:{query}"
    
    # 先搜索看看有哪些结果
    cmd = [
        "yt-dlp",
        "--dump-json",
        "--no-download",
        "--flat-playlist",
        "--max-downloads", "5",
        search_query
    ]
    
    env = os.environ.copy()
    env["http_proxy"] = PROXY
    env["https_proxy"] = PROXY
    
    try:
        result = subprocess.run(cmd, capture_output=True, text=True, env=env, timeout=60)
        if result.returncode == 0:
            results = [json.loads(line) for line in result.stdout.strip().split('\n') if line]
            if results:
                print(f"🔍 找到 {len(results)} 个结果:")
                for i, r in enumerate(results[:5], 1):
                    print(f"  {i}. {r.get('title', 'Unknown')} - {r.get('artist', 'Unknown')}")
                
                # 下载第一个结果
                first_result = results[0]
                url = first_result.get('id', '')
                full_url = f"https://www.youtube.com/watch?v={url}"
                return download_music(full_url, output_dir, audio_format, quality)
    except Exception as e:
        print(f"❌ 搜索失败: {e}")
    
    return False

def main():
    args = parse_args()
    
    if not args.query:
        print("Usage: music_downloader <URL 或 歌名> [--output DIR] [--format mp3|flac|m4a] [--quality 128|192|320]")
        print("Examples:")
        print("  music_downloader https://www.youtube.com/watch?v=xxx")
        print("  music_downloader '演员 薛之谦' -o /other/music")
        sys.exit(1)
    
    # 判断是 URL 还是搜索
    url_patterns = [
        r"^https?://",
        r"^http?://",
        r"^www\.",
        r"^youtu\.be",
    ]
    
    is_url = any(re.match(p, args.query) for p in url_patterns)
    
    if is_url:
        # 直接下载
        success = download_music(args.query, args.output, args.format, args.quality)
    else:
        # 搜索并下载
        query = args.query
        if args.artist:
            query = f"{query} {args.artist}"
        success = search_and_download(query, args.output, args.format, args.quality)
    
    sys.exit(0 if success else 1)

if __name__ == "__main__":
    main()

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