
Media Downloader
- 574 installs
- 398 repo stars
- Updated January 25, 2026
- yizhiyanhua-ai/media-downloader
media-downloader is a bilingual coding-agent skill that searches and downloads stock images, video clips, and YouTube segments from a plain-language brief so developers assembling landing pages can source visual assets w
About
media-downloader is a Generative Media skill from yizhiyanhua-ai/media-downloader that accepts natural-language requests in English or Chinese to search, download, and auto-trim images and video clips. Trigger phrases include "download media", "download images", "find video", and `/media`. Example flows download batches such as five cat images or sunset photos based on a one-line description. The skill supports video segment extraction from YouTube alongside stock imagery. Developers reach for media-downloader when prototyping landing pages, marketing hero sections, or demo content and need licensed visuals fetched and clipped without leaving the agent session.
- Natural-language triggers in Chinese and English (e.g. download images, find video, /media)
- Image search and download from stock libraries with on-demand Pexels API key setup only when needed
- YouTube download and automatic trim to a requested time range or duration
- Free-stock video clips with smart length trimming to match briefs
- Bilingual user guidance so non-English builders can drive the same workflows
Media Downloader by the numbers
- 574 all-time installs (skills.sh)
- +2 installs in the week ending Jul 28, 2026 (Skillselion tracking)
- Ranked #374 of 1,340 Generative Media skills by installs in the Skillselion catalog
- Security screen: HIGH risk (skills.sh audit)
- Data as of Jul 28, 2026 (Skillselion catalog sync)
npx skills add https://github.com/yizhiyanhua-ai/media-downloader --skill media-downloaderAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 574 |
|---|---|
| repo stars | ★ 398 |
| Security audit | 1 / 3 scanners passed |
| Last updated | January 25, 2026 |
| Repository | yizhiyanhua-ai/media-downloader ↗ |
How do you download stock images from a text prompt?
Fetch stock images, stock clips, or YouTube segments from a plain-language brief so content and landing assets ship faster.
Who is it for?
Developers prototyping landing pages or demo UIs who need stock images or clipped video segments from a conversational brief.
Skip if: Production pipelines requiring verified commercial licenses, DRM-protected sources, or programmatic CDN asset management at scale.
When should I use this skill?
The user says "download images", "find video", "download media", "/media", or describes visual assets needed for a page in English or Chinese.
What you get
Local image files, trimmed video clips, and organized media folders ready for landing-page or prototype insertion.
- Downloaded image files
- trimmed video clips
By the numbers
- Example workflow downloads 5 images per single descriptive request
Files
🎬 Media Downloader / 智能媒体下载器
只需告诉我你想要什么,我就会帮你找到并下载相关的图片和视频!
Just tell me what you want, and I'll find and download relevant images and videos for you!
---
🚀 我能帮你做什么?/ What Can I Do?
| 你说... / You say... | 我会... / I will... |
|---|---|
| "下载一些可爱的猫咪图片" | 搜索并下载 5 张猫咪图片 |
| "Download sunset photos" | Search and download sunset images |
| "找一段海浪的视频,15秒左右" | 下载一段 15 秒的海浪视频 |
| "Get me a 30-second cooking video" | Download a trimmed cooking clip |
| "下载这个 YouTube 视频的 1:30-2:00" | 下载并自动剪辑指定片段 |
---
✨ 功能特点 / Features
- 🖼️ 图片下载 - 从专业图库搜索高清图片
- 🎬 视频素材 - 获取免费商用视频片段
- 📺 YouTube 下载 - 支持下载和剪辑
- ✂️ 智能剪辑 - 自动裁剪到你需要的长度
- 🌍 中英双语 - 支持中文和英文指令
---
📋 Skill 执行逻辑 / Skill Execution Logic
🔑 API Key 按需配置 / On-Demand API Key Configuration
重要:不要在用户安装时就要求配置 API Key!按以下逻辑处理:
1. 用户下载 YouTube 视频时:直接执行,不需要任何 API Key 2. 用户首次下载图片时:
- 检查
PEXELS_API_KEY环境变量是否存在 - 如果不存在,引导用户配置:
检测到你还没有配置图库 API Key。下载图片需要一个免费的 API Key。
请按以下步骤操作:
1. 打开 https://www.pexels.com,点击 Join 注册(支持 Google/Apple 一键注册)
2. 注册后访问 https://www.pexels.com/api/,点击 Your API Key
3. 复制显示的密钥,粘贴给我- 用户提供 API Key 后,帮用户保存到系统环境变量:
echo 'export PEXELS_API_KEY="用户的密钥"' >> ~/.zshrc && source ~/.zshrc- 然后继续完成图片下载任务
🎬 YouTube 下载优先 / YouTube First
当用户说「下载视频」但没有指定来源时,优先推荐 YouTube:
- YouTube 不需要 API Key
- 内容更丰富
- 支持时间段裁剪
---
💬 使用示例 / Examples
下载图片 / Download Images
"帮我下载 5 张星空的图片"
"Download 10 coffee shop photos"
"找一些适合做壁纸的风景图"下载视频素材 / Download Video Clips
"下载一段城市夜景的视频,30秒以内"
"Find me a 15-second ocean wave video"
"找一些适合做背景的自然风光视频"YouTube 下载与剪辑 / YouTube Download & Trim
"下载这个视频:https://youtube.com/watch?v=xxx"
"下载这个 YouTube 视频的第 2 分钟到第 3 分钟"
"只下载这个视频的音频"---
📁 下载位置 / Download Location
所有文件默认保存在:
~/.claude/skills/media-downloader/downloads/---
❓ 常见问题 / FAQ
Q: 为什么搜索图片没有结果?
A: 请确认已配置 API Key。运行 status 命令检查配置状态。
Q: YouTube 视频下载失败?
A: YouTube 下载不需要 API Key,但需要安装 yt-dlp。运行 pip install yt-dlp 安装。
Q: 视频剪辑功能不工作?
A: 需要安装 ffmpeg。macOS 用户运行 brew install ffmpeg。
Q: 这些图片/视频可以商用吗?
A: Pexels、Pixabay、Unsplash 的素材都可以免费商用,无需署名(但署名是一种礼貌)。
---
🛠️ CLI 命令参考 / CLI Reference
供高级用户直接使用命令行:
# 检查配置状态
media_cli.py status
# 下载图片
media_cli.py image "关键词" -n 数量 -o 输出目录
# 下载视频素材
media_cli.py video "关键词" -d 最大时长 -n 数量
# 下载 YouTube 视频
media_cli.py youtube "URL" --start 开始秒数 --end 结束秒数
# 搜索媒体(不下载)
media_cli.py search "关键词" --type image/video/all
# 剪辑本地视频
media_cli.py trim 输入文件 --start 开始 --end 结束---
📦 支持的素材来源 / Supported Sources
| 来源 Source | 类型 Type | 特点 Features |
|---|---|---|
| Pexels | 图片 + 视频 | 高质量,更新快 |
| Pixabay | 图片 + 视频 | 数量多,种类全 |
| Unsplash | 图片 | 艺术感强,适合壁纸 |
| YouTube | 视频 | 内容丰富,支持剪辑 |
---
🎬 开始使用吧!直接告诉我你想要什么图片或视频!
🎬 Start using! Just tell me what images or videos you want!
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
*.egg-info/
dist/
build/
# Downloads and cache (user data)
downloads/
cache/
# macOS
.DS_Store
# IDE
.idea/
.vscode/
*.swp
# Environment
.env
*.local
#!/usr/bin/env python3
"""
Media Downloader - 智能媒体下载器
支持图片、视频搜索下载和自动剪辑
"""
import os
import sys
import json
import argparse
import subprocess
from datetime import datetime
from typing import Optional, List, Dict
from urllib.parse import quote_plus
# 尝试导入 requests
try:
import requests
REQUESTS_AVAILABLE = True
except ImportError:
REQUESTS_AVAILABLE = False
# 配置
SKILL_DIR = os.path.dirname(os.path.abspath(__file__))
DOWNLOADS_DIR = os.path.join(SKILL_DIR, 'downloads')
CACHE_DIR = os.path.join(SKILL_DIR, 'cache')
# 确保目录存在
os.makedirs(DOWNLOADS_DIR, exist_ok=True)
os.makedirs(CACHE_DIR, exist_ok=True)
# API Keys (可以从环境变量获取)
PEXELS_API_KEY = os.environ.get('PEXELS_API_KEY', '')
UNSPLASH_ACCESS_KEY = os.environ.get('UNSPLASH_ACCESS_KEY', '')
PIXABAY_API_KEY = os.environ.get('PIXABAY_API_KEY', '')
class ImageDownloader:
"""图片下载器"""
def __init__(self):
self.session = requests.Session() if REQUESTS_AVAILABLE else None
def search_pexels(self, query: str, count: int = 5) -> List[Dict]:
"""从 Pexels 搜索图片"""
if not PEXELS_API_KEY:
return []
url = f"https://api.pexels.com/v1/search?query={quote_plus(query)}&per_page={count}"
headers = {"Authorization": PEXELS_API_KEY}
try:
resp = self.session.get(url, headers=headers, timeout=10)
if resp.status_code == 200:
data = resp.json()
return [
{
"url": photo["src"]["large"],
"original": photo["src"]["original"],
"photographer": photo["photographer"],
"source": "pexels",
"id": photo["id"],
}
for photo in data.get("photos", [])
]
except Exception as e:
print(f"Pexels 搜索失败: {e}")
return []
def search_unsplash(self, query: str, count: int = 5) -> List[Dict]:
"""从 Unsplash 搜索图片"""
if not UNSPLASH_ACCESS_KEY:
return []
url = f"https://api.unsplash.com/search/photos?query={quote_plus(query)}&per_page={count}"
headers = {"Authorization": f"Client-ID {UNSPLASH_ACCESS_KEY}"}
try:
resp = self.session.get(url, headers=headers, timeout=10)
if resp.status_code == 200:
data = resp.json()
return [
{
"url": photo["urls"]["regular"],
"original": photo["urls"]["full"],
"photographer": photo["user"]["name"],
"source": "unsplash",
"id": photo["id"],
}
for photo in data.get("results", [])
]
except Exception as e:
print(f"Unsplash 搜索失败: {e}")
return []
def search_pixabay(self, query: str, count: int = 5) -> List[Dict]:
"""从 Pixabay 搜索图片"""
if not PIXABAY_API_KEY:
return []
url = f"https://pixabay.com/api/?key={PIXABAY_API_KEY}&q={quote_plus(query)}&per_page={count}&image_type=photo"
try:
resp = self.session.get(url, timeout=10)
if resp.status_code == 200:
data = resp.json()
return [
{
"url": hit["largeImageURL"],
"original": hit["largeImageURL"],
"photographer": hit["user"],
"source": "pixabay",
"id": hit["id"],
}
for hit in data.get("hits", [])
]
except Exception as e:
print(f"Pixabay 搜索失败: {e}")
return []
def search_all(self, query: str, count: int = 5) -> List[Dict]:
"""从所有来源搜索图片"""
results = []
results.extend(self.search_pexels(query, count))
results.extend(self.search_unsplash(query, count))
results.extend(self.search_pixabay(query, count))
return results[:count]
def download(self, url: str, filename: str, output_dir: str = None) -> Optional[str]:
"""下载图片"""
if not REQUESTS_AVAILABLE:
print("❌ 需要安装 requests: pip install requests")
return None
output_dir = output_dir or DOWNLOADS_DIR
os.makedirs(output_dir, exist_ok=True)
filepath = os.path.join(output_dir, filename)
try:
resp = self.session.get(url, timeout=30, stream=True)
if resp.status_code == 200:
with open(filepath, 'wb') as f:
for chunk in resp.iter_content(chunk_size=8192):
f.write(chunk)
return filepath
except Exception as e:
print(f"下载失败: {e}")
return None
class VideoDownloader:
"""视频下载器"""
def __init__(self):
self.session = requests.Session() if REQUESTS_AVAILABLE else None
self.ytdlp_available = self._check_ytdlp()
self.ffmpeg_available = self._check_ffmpeg()
def _check_ytdlp(self) -> bool:
"""检查 yt-dlp 是否可用"""
try:
result = subprocess.run(['yt-dlp', '--version'], capture_output=True)
return result.returncode == 0
except FileNotFoundError:
return False
def _check_ffmpeg(self) -> bool:
"""检查 ffmpeg 是否可用"""
try:
result = subprocess.run(['ffmpeg', '-version'], capture_output=True)
return result.returncode == 0
except FileNotFoundError:
return False
def search_pexels_videos(self, query: str, count: int = 5) -> List[Dict]:
"""从 Pexels 搜索视频"""
if not PEXELS_API_KEY:
return []
url = f"https://api.pexels.com/videos/search?query={quote_plus(query)}&per_page={count}"
headers = {"Authorization": PEXELS_API_KEY}
try:
resp = self.session.get(url, headers=headers, timeout=10)
if resp.status_code == 200:
data = resp.json()
results = []
for video in data.get("videos", []):
# 获取最佳质量视频
files = video.get("video_files", [])
best = max(files, key=lambda x: x.get("width", 0)) if files else None
if best:
results.append({
"url": best["link"],
"duration": video.get("duration", 0),
"width": best.get("width", 0),
"height": best.get("height", 0),
"source": "pexels",
"id": video["id"],
})
return results
except Exception as e:
print(f"Pexels Videos 搜索失败: {e}")
return []
def search_pixabay_videos(self, query: str, count: int = 5) -> List[Dict]:
"""从 Pixabay 搜索视频"""
if not PIXABAY_API_KEY:
return []
url = f"https://pixabay.com/api/videos/?key={PIXABAY_API_KEY}&q={quote_plus(query)}&per_page={count}"
try:
resp = self.session.get(url, timeout=10)
if resp.status_code == 200:
data = resp.json()
results = []
for hit in data.get("hits", []):
videos = hit.get("videos", {})
# 优先选择大尺寸
for quality in ["large", "medium", "small"]:
if quality in videos:
results.append({
"url": videos[quality]["url"],
"duration": hit.get("duration", 0),
"width": videos[quality].get("width", 0),
"height": videos[quality].get("height", 0),
"source": "pixabay",
"id": hit["id"],
})
break
return results
except Exception as e:
print(f"Pixabay Videos 搜索失败: {e}")
return []
def search_youtube(self, query: str, count: int = 5) -> List[Dict]:
"""搜索 YouTube 视频 (使用 yt-dlp)"""
if not self.ytdlp_available:
return []
try:
cmd = [
'yt-dlp',
f'ytsearch{count}:{query}',
'--dump-json',
'--no-download',
'--flat-playlist',
]
result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
if result.returncode == 0:
results = []
for line in result.stdout.strip().split('\n'):
if line:
try:
data = json.loads(line)
results.append({
"url": f"https://www.youtube.com/watch?v={data.get('id', '')}",
"title": data.get("title", ""),
"duration": data.get("duration", 0),
"channel": data.get("channel", ""),
"source": "youtube",
"id": data.get("id", ""),
})
except json.JSONDecodeError:
pass
return results
except Exception as e:
print(f"YouTube 搜索失败: {e}")
return []
def download_video(self, url: str, filename: str, output_dir: str = None) -> Optional[str]:
"""下载视频文件"""
if not REQUESTS_AVAILABLE:
return None
output_dir = output_dir or DOWNLOADS_DIR
os.makedirs(output_dir, exist_ok=True)
filepath = os.path.join(output_dir, filename)
try:
resp = self.session.get(url, timeout=60, stream=True)
if resp.status_code == 200:
with open(filepath, 'wb') as f:
for chunk in resp.iter_content(chunk_size=8192):
f.write(chunk)
return filepath
except Exception as e:
print(f"下载失败: {e}")
return None
def download_youtube(self, url: str, output_dir: str = None,
audio_only: bool = False) -> Optional[str]:
"""从 YouTube 下载视频"""
if not self.ytdlp_available:
print("❌ 需要安装 yt-dlp: pip install yt-dlp")
return None
output_dir = output_dir or DOWNLOADS_DIR
os.makedirs(output_dir, exist_ok=True)
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
output_template = os.path.join(output_dir, f"yt_{timestamp}_%(title)s.%(ext)s")
cmd = ['yt-dlp', '-o', output_template]
if audio_only:
cmd.extend(['--extract-audio', '--audio-format', 'mp3'])
else:
# 优先下载 mp4 格式,便于后续剪辑
cmd.extend(['-f', 'bestvideo[height<=1080][ext=mp4]+bestaudio[ext=m4a]/best[height<=1080][ext=mp4]/best'])
cmd.extend(['--merge-output-format', 'mp4'])
cmd.append(url)
try:
result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
if result.returncode == 0:
# 找到下载的文件
for f in os.listdir(output_dir):
if f.startswith(f"yt_{timestamp}_"):
return os.path.join(output_dir, f)
except subprocess.TimeoutExpired:
print("❌ 下载超时")
except Exception as e:
print(f"❌ 下载失败: {e}")
return None
class VideoTrimmer:
"""视频剪辑器"""
def __init__(self):
self.ffmpeg_available = self._check_ffmpeg()
def _check_ffmpeg(self) -> bool:
try:
result = subprocess.run(['ffmpeg', '-version'], capture_output=True)
return result.returncode == 0
except FileNotFoundError:
return False
def get_duration(self, filepath: str) -> float:
"""获取视频时长"""
if not self.ffmpeg_available:
return 0
try:
cmd = [
'ffprobe', '-v', 'error',
'-show_entries', 'format=duration',
'-of', 'default=noprint_wrappers=1:nokey=1',
filepath
]
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode == 0:
return float(result.stdout.strip())
except:
pass
return 0
def trim(self, input_path: str, output_path: str,
start: float = 0, end: float = None, duration: float = None) -> bool:
"""剪辑视频"""
if not self.ffmpeg_available:
print("❌ 需要安装 ffmpeg: brew install ffmpeg")
return False
# 使用 -ss 在 -i 之前进行快速定位,然后在之后使用 -ss 0 精确定位
cmd = ['ffmpeg', '-y']
if start > 0:
cmd.extend(['-ss', str(start)])
cmd.extend(['-i', input_path])
if end:
# 计算实际需要的时长
target_duration = end - start
cmd.extend(['-t', str(target_duration)])
elif duration:
cmd.extend(['-t', str(duration)])
# 使用重新编码以确保精确剪辑
cmd.extend(['-c:v', 'libx264', '-preset', 'fast', '-c:a', 'aac', output_path])
try:
result = subprocess.run(cmd, capture_output=True, timeout=120)
return result.returncode == 0
except Exception as e:
print(f"❌ 剪辑失败: {e}")
return False
def auto_trim(self, input_path: str, output_path: str,
target_duration: float = 30) -> bool:
"""自动剪辑到目标时长 (从中间取)"""
total_duration = self.get_duration(input_path)
if total_duration <= target_duration:
# 不需要剪辑
import shutil
shutil.copy(input_path, output_path)
return True
# 从中间取
start = (total_duration - target_duration) / 2
return self.trim(input_path, output_path, start=start, duration=target_duration)
# ═══════════════════════════════════════════════════════════
# CLI 命令
# ═══════════════════════════════════════════════════════════
def cmd_image(args):
"""下载图片"""
if not REQUESTS_AVAILABLE:
print("❌ 需要安装 requests: pip install requests")
return 1
downloader = ImageDownloader()
print(f"🔍 搜索图片: {args.query}")
results = downloader.search_all(args.query, args.count * 2)
if not results:
print("❌ 未找到图片。请检查 API Key 配置:")
print(" export PEXELS_API_KEY=your_key")
print(" export UNSPLASH_ACCESS_KEY=your_key")
print(" export PIXABAY_API_KEY=your_key")
return 1
print(f"✅ 找到 {len(results)} 张图片,开始下载...")
output_dir = args.output or DOWNLOADS_DIR
downloaded = 0
for i, img in enumerate(results[:args.count]):
ext = img["url"].split(".")[-1].split("?")[0][:4]
if ext not in ["jpg", "jpeg", "png", "gif", "webp"]:
ext = "jpg"
filename = f"{args.query.replace(' ', '_')}_{i+1}_{img['source']}.{ext}"
print(f" ⬇️ 下载 {i+1}/{args.count}: {filename}")
path = downloader.download(img["url"], filename, output_dir)
if path:
downloaded += 1
print()
print(f"✅ 下载完成: {downloaded} 张图片")
print(f"📁 保存位置: {output_dir}")
return 0
def cmd_video(args):
"""下载视频"""
if not REQUESTS_AVAILABLE:
print("❌ 需要安装 requests: pip install requests")
return 1
downloader = VideoDownloader()
trimmer = VideoTrimmer()
print(f"🔍 搜索视频: {args.query}")
# 搜索免费视频
results = []
results.extend(downloader.search_pexels_videos(args.query, args.count))
results.extend(downloader.search_pixabay_videos(args.query, args.count))
if not results:
print("❌ 未找到视频。请检查 API Key 配置")
return 1
# 过滤时长
if args.duration:
results = [v for v in results if v.get("duration", 0) <= args.duration * 1.5]
print(f"✅ 找到 {len(results)} 个视频")
output_dir = args.output or DOWNLOADS_DIR
downloaded = 0
for i, video in enumerate(results[:args.count]):
filename = f"{args.query.replace(' ', '_')}_{i+1}_{video['source']}.mp4"
print(f" ⬇️ 下载 {i+1}/{args.count}: {filename} ({video.get('duration', '?')}s)")
path = downloader.download_video(video["url"], filename, output_dir)
if path:
# 检查是否需要剪辑
if args.duration:
duration = trimmer.get_duration(path)
if duration > args.duration:
print(f" ✂️ 剪辑到 {args.duration} 秒...")
trimmed_path = path.replace(".mp4", "_trimmed.mp4")
if trimmer.auto_trim(path, trimmed_path, args.duration):
os.remove(path)
os.rename(trimmed_path, path)
downloaded += 1
print()
print(f"✅ 下载完成: {downloaded} 个视频")
print(f"📁 保存位置: {output_dir}")
return 0
def cmd_youtube(args):
"""从 YouTube 下载"""
downloader = VideoDownloader()
trimmer = VideoTrimmer()
if not downloader.ytdlp_available:
print("❌ 需要安装 yt-dlp: pip install yt-dlp")
return 1
print(f"⬇️ 下载 YouTube 视频: {args.url}")
output_dir = args.output or DOWNLOADS_DIR
path = downloader.download_youtube(args.url, output_dir, args.audio_only)
if not path:
print("❌ 下载失败")
return 1
print(f"✅ 下载完成: {os.path.basename(path)}")
# 剪辑
if args.start is not None or args.end is not None:
if not trimmer.ffmpeg_available:
print("⚠️ 需要 ffmpeg 进行剪辑: brew install ffmpeg")
else:
start = args.start or 0
print(f"✂️ 剪辑: {start}s - {args.end}s")
trimmed_path = path.rsplit(".", 1)[0] + "_trimmed." + path.rsplit(".", 1)[1]
if trimmer.trim(path, trimmed_path, start=start, end=args.end):
os.remove(path)
os.rename(trimmed_path, path)
print(f"✅ 剪辑完成")
else:
print("❌ 剪辑失败")
print(f"📁 保存位置: {path}")
return 0
def cmd_search(args):
"""搜索媒体"""
if not REQUESTS_AVAILABLE:
print("❌ 需要安装 requests")
return 1
print(f"🔍 搜索: {args.query}")
print()
if args.type in ["image", "all"]:
img_dl = ImageDownloader()
images = img_dl.search_all(args.query, args.count)
if images:
print(f"📷 图片 ({len(images)} 张):")
for img in images[:5]:
print(f" • [{img['source']}] {img['photographer']}")
print()
if args.type in ["video", "all"]:
vid_dl = VideoDownloader()
videos = []
videos.extend(vid_dl.search_pexels_videos(args.query, args.count))
videos.extend(vid_dl.search_pixabay_videos(args.query, args.count))
if videos:
print(f"🎬 视频 ({len(videos)} 个):")
for vid in videos[:5]:
print(f" • [{vid['source']}] {vid.get('duration', '?')}s - {vid.get('width', '?')}x{vid.get('height', '?')}")
print()
# YouTube
yt_results = vid_dl.search_youtube(args.query, args.count)
if yt_results:
print(f"📺 YouTube ({len(yt_results)} 个):")
for vid in yt_results[:5]:
print(f" • {vid['title'][:50]}...")
print(f" {vid['url']}")
print()
return 0
def cmd_trim(args):
"""剪辑视频"""
trimmer = VideoTrimmer()
if not trimmer.ffmpeg_available:
print("❌ 需要安装 ffmpeg: brew install ffmpeg")
return 1
if not os.path.exists(args.input):
print(f"❌ 文件不存在: {args.input}")
return 1
output = args.output or args.input.rsplit(".", 1)[0] + "_trimmed." + args.input.rsplit(".", 1)[1]
print(f"✂️ 剪辑视频: {args.input}")
if args.duration:
success = trimmer.auto_trim(args.input, output, args.duration)
else:
success = trimmer.trim(args.input, output, start=args.start or 0, end=args.end)
if success:
duration = trimmer.get_duration(output)
print(f"✅ 剪辑完成: {output} ({duration:.1f}s)")
return 0
else:
print("❌ 剪辑失败")
return 1
def cmd_status(args):
"""Check configuration status / 检查配置状态"""
print("╔═══════════════════════════════════════════════════════════╗")
print("║ Media Downloader - Status / 配置状态 ║")
print("╠═══════════════════════════════════════════════════════════╣")
# API Keys
print("║ API Keys: ║")
pexels = "✅ Configured" if PEXELS_API_KEY else "❌ Not set"
unsplash = "✅ Configured" if UNSPLASH_ACCESS_KEY else "❌ Not set"
pixabay = "✅ Configured" if PIXABAY_API_KEY else "❌ Not set"
print(f"║ Pexels: {pexels:<42} ║")
print(f"║ Unsplash: {unsplash:<42} ║")
print(f"║ Pixabay: {pixabay:<42} ║")
print("╠═══════════════════════════════════════════════════════════╣")
# Tools
print("║ Tools / 工具: ║")
import shutil
ytdlp = "✅ Available" if shutil.which("yt-dlp") else "❌ Not found"
ffmpeg = "✅ Available" if shutil.which("ffmpeg") else "❌ Not found"
requests_status = "✅ Available" if REQUESTS_AVAILABLE else "❌ Not installed"
print(f"║ yt-dlp: {ytdlp:<42} ║")
print(f"║ ffmpeg: {ffmpeg:<42} ║")
print(f"║ requests: {requests_status:<42} ║")
print("╠═══════════════════════════════════════════════════════════╣")
# Features
print("║ Features / 功能: ║")
img_ready = "✅" if (PEXELS_API_KEY or UNSPLASH_ACCESS_KEY or PIXABAY_API_KEY) else "❌"
vid_ready = "✅" if (PEXELS_API_KEY or PIXABAY_API_KEY) else "❌"
yt_ready = "✅" if shutil.which("yt-dlp") else "❌"
trim_ready = "✅" if shutil.which("ffmpeg") else "❌"
print(f"║ {img_ready} Image download / 图片下载 ║")
print(f"║ {vid_ready} Stock video download / 视频素材下载 ║")
print(f"║ {yt_ready} YouTube download / YouTube 下载 ║")
print(f"║ {trim_ready} Video trimming / 视频剪辑 ║")
print("╚═══════════════════════════════════════════════════════════╝")
if not (PEXELS_API_KEY or UNSPLASH_ACCESS_KEY or PIXABAY_API_KEY):
print()
print("💡 Tip: Set API keys to enable image/video search:")
print(" export PEXELS_API_KEY=your_key")
print(" export UNSPLASH_ACCESS_KEY=your_key")
print(" export PIXABAY_API_KEY=your_key")
return 0
def main():
parser = argparse.ArgumentParser(
description="Media Downloader - Smart media downloader / 智能媒体下载器"
)
subparsers = parser.add_subparsers(dest="command", help="Available commands / 可用命令")
# image command / 图片命令
img_parser = subparsers.add_parser("image", help="Download images / 下载图片")
img_parser.add_argument("query", help="Search keywords / 搜索关键词")
img_parser.add_argument("--count", "-n", type=int, default=5, help="Number to download / 下载数量")
img_parser.add_argument("--output", "-o", help="Output directory / 输出目录")
# video command / 视频命令
vid_parser = subparsers.add_parser("video", help="Download stock videos / 下载视频素材")
vid_parser.add_argument("query", help="Search keywords / 搜索关键词")
vid_parser.add_argument("--count", "-n", type=int, default=3, help="Number to download / 下载数量")
vid_parser.add_argument("--duration", "-d", type=int, default=60, help="Max duration (seconds) / 最大时长(秒)")
vid_parser.add_argument("--output", "-o", help="Output directory / 输出目录")
# youtube command / YouTube 命令
yt_parser = subparsers.add_parser("youtube", help="Download YouTube videos / 下载 YouTube 视频")
yt_parser.add_argument("url", help="YouTube URL")
yt_parser.add_argument("--start", "-s", type=float, help="Start time (seconds) / 开始时间(秒)")
yt_parser.add_argument("--end", "-e", type=float, help="End time (seconds) / 结束时间(秒)")
yt_parser.add_argument("--audio-only", "-a", action="store_true", help="Download audio only / 仅下载音频")
yt_parser.add_argument("--output", "-o", help="Output directory / 输出目录")
# search command / 搜索命令
search_parser = subparsers.add_parser("search", help="Search media / 搜索媒体")
search_parser.add_argument("query", help="Search keywords / 搜索关键词")
search_parser.add_argument("--type", "-t", choices=["image", "video", "all"], default="all", help="Media type / 媒体类型")
search_parser.add_argument("--count", "-n", type=int, default=5, help="Number of results / 结果数量")
# trim command / 剪辑命令
trim_parser = subparsers.add_parser("trim", help="Trim video / 剪辑视频")
trim_parser.add_argument("input", help="Input video file / 输入视频文件")
trim_parser.add_argument("--start", "-s", type=float, help="Start time (seconds) / 开始时间(秒)")
trim_parser.add_argument("--end", "-e", type=float, help="End time (seconds) / 结束时间(秒)")
trim_parser.add_argument("--duration", "-d", type=float, help="Target duration (seconds) / 目标时长(秒)")
trim_parser.add_argument("--output", "-o", help="Output file / 输出文件")
# status command / 状态命令
subparsers.add_parser("status", help="Check configuration status / 检查配置状态")
args = parser.parse_args()
if args.command == "image":
return cmd_image(args)
elif args.command == "video":
return cmd_video(args)
elif args.command == "youtube":
return cmd_youtube(args)
elif args.command == "search":
return cmd_search(args)
elif args.command == "trim":
return cmd_trim(args)
elif args.command == "status":
return cmd_status(args)
else:
parser.print_help()
print()
print("Examples / 示例:")
print(" python media_cli.py status # Check config / 检查配置")
print(" python media_cli.py image 'cute cats' -n 5 # Download images / 下载图片")
print(" python media_cli.py video 'sunset' -d 30 # Download videos / 下载视频")
print(" python media_cli.py youtube 'URL' -s 60 -e 90 # YouTube + trim / 下载剪辑")
print(" python media_cli.py search 'nature' -t video # Search / 搜索")
print(" python media_cli.py trim video.mp4 -d 30 # Trim video / 剪辑视频")
return 0
if __name__ == "__main__":
sys.exit(main())
🎬 智能媒体下载器
根据你的描述自动搜索和下载图片、视频片段,支持视频自动剪辑。
🇬🇧 English
---
🚀 我能帮你做什么?
| 你说... | 我会... |
|---|---|
| "下载一些可爱的猫咪图片" | 搜索并下载 5 张猫咪图片 |
| "找一段海浪的视频,15秒左右" | 下载一段 15 秒的海浪视频 |
| "下载一段 30 秒的烹饪视频" | 下载并剪辑烹饪视频 |
| "下载这个 YouTube 视频的 1:30-2:00" | 下载并自动剪辑指定片段 |
---
✨ 功能特点
- 🖼️ 图片下载 - 从专业图库搜索高清图片
- 🎬 视频素材 - 获取免费商用视频片段
- 📺 YouTube 下载 - 支持下载和剪辑
- ✂️ 智能剪辑 - 自动裁剪到你需要的长度
- 🌍 中英双语 - 支持中文和英文指令
---
⚡ 一句话安装
在 Claude Code 中对 Claude 说:
"帮我安装 https://github.com/yizhiyanhua-ai/media-downloader.git 这个 skill 和它的所有依赖,并配置 yt-dlp 使用浏览器 cookies"
Claude 会自动完成:
- 下载 skill 到正确位置
- 安装 yt-dlp、ffmpeg 等依赖工具
- 配置浏览器 cookies(解决 YouTube「确认你不是机器人」的问题)
- 检查安装状态
你只需要在 Claude 询问时点击「允许」就行了!
---
🔑 关于 API Key
💡 按需配置:安装时不需要配置任何 API Key!
>
- 下载 YouTube 视频:不需要 API Key,安装完成即可使用
- 下载图片:首次下载时,Claude 会自动引导你配置 Pexels API Key
首次下载图片时会发生什么?
当你第一次说「帮我下载一些猫咪图片」时,Claude 会:
1. 检测到你还没有配置图库 API Key 2. 引导你去 https://www.pexels.com 注册(支持 Google/Apple 一键注册) 3. 帮你获取 API Key 并自动保存到系统环境变量 4. 然后继续完成图片下载
整个过程只需要几分钟,而且只需要配置一次!
---
📋 更多图库(可选)
Pexels 已经能满足大部分需求。如果你想要更多图片来源,可以对 Claude 说:
"帮我配置 Pixabay API Key" 或 "帮我配置 Unsplash API Key"
Claude 会引导你完成注册和配置。
---
💬 使用示例
⚠️ 重要提示:使用前请先对 Claude 说 "检查一下 media-downloader 的状态",确保所有依赖工具已安装完成!
下载图片
"帮我下载 5 张星空的图片"
"下载 10 张咖啡店的照片"
"找一些适合做壁纸的风景图"下载视频素材
💡 推荐:如果你需要下载视频,优先使用 YouTube!YouTube 视频内容丰富、质量高,而且不需要额外的 API Key。
"下载这个视频:https://youtube.com/watch?v=xxx"
"下载这个 YouTube 视频的第 2 分钟到第 3 分钟"
"只下载这个视频的音频"如果你需要从素材库下载短视频片段:
"下载一段城市夜景的视频,30秒以内"
"找一段 15 秒的海浪视频"
"找一些适合做背景的自然风光视频"---
📁 下载位置
所有文件默认保存在:
~/.claude/skills/media-downloader/downloads/自定义下载目录
你可以使用 -o 或 --output 参数指定下载位置:
# 下载图片到指定文件夹
media_cli.py image "猫咪" -o ~/Pictures/cats
# 下载视频到桌面
media_cli.py video "日落" -o ~/Desktop
# 下载 YouTube 视频到当前目录
media_cli.py youtube "URL" -o .或者直接告诉我你想保存到哪里:
"下载 5 张猫咪图片到桌面"
"把视频保存到 ~/Videos/project 文件夹"---
❓ 常见问题
Q: YouTube 提示「需要登录验证」或「确认你不是机器人」?
这是 YouTube 的反爬虫机制。解决方法是让 yt-dlp 使用你浏览器的登录状态:
方法一:对 Claude 说(推荐)
"帮我配置 yt-dlp 使用浏览器 cookies"
Claude 会帮你设置好。
方法二:手动配置
1. 确保你已经在浏览器(Chrome/Firefox/Edge)中登录了 YouTube 2. 下载视频时添加 --cookies-from-browser chrome 参数:
yt-dlp --cookies-from-browser chrome "YouTube视频链接"💡 提示:把chrome换成你使用的浏览器:firefox、edge、safari、brave等
Q: 为什么搜索图片没有结果?
A: 请确认已配置 API Key。运行 status 命令检查配置状态。
Q: YouTube 视频下载失败?
A: YouTube 下载不需要 API Key,但需要安装 yt-dlp。运行 pip install yt-dlp 安装。
Q: 视频剪辑功能不工作?
A: 需要安装 ffmpeg。macOS 用户运行 brew install ffmpeg。
Q: 这些图片/视频可以商用吗?
A: Pexels、Pixabay、Unsplash 的素材都可以免费商用,无需署名(但署名是一种礼貌)。
---
🛠️ CLI 命令参考
供高级用户直接使用命令行:
# 检查配置状态
media_cli.py status
# 下载图片
media_cli.py image "关键词" -n 数量 -o 输出目录
# 下载视频素材
media_cli.py video "关键词" -d 最大时长 -n 数量
# 下载 YouTube 视频
media_cli.py youtube "URL" --start 开始秒数 --end 结束秒数
# 搜索媒体(不下载)
media_cli.py search "关键词" --type image/video/all
# 剪辑本地视频
media_cli.py trim 输入文件 --start 开始 --end 结束---
📦 支持的素材来源
| 来源 | 类型 | 特点 |
|---|---|---|
| Pexels | 图片 + 视频 | 高质量,更新快 |
| Pixabay | 图片 + 视频 | 数量多,种类全 |
| Unsplash | 图片 | 艺术感强,适合壁纸 |
| YouTube | 视频 | 内容丰富,支持剪辑 |
---
📄 许可证
MIT License
---
🎬 开始使用吧!直接告诉我你想要什么图片或视频!
🎬 Media Downloader
Smart media downloader - Automatically search and download images/video clips based on your description, with auto-trimming support.
🇨🇳 中文文档
---
🚀 What Can I Do?
| You say... | I will... |
|---|---|
| "Download some cute cat pictures" | Search and download 5 cat images |
| "Find a 15-second ocean wave video" | Download a 15s ocean wave clip |
| "Get me a 30-second cooking video" | Download a trimmed cooking clip |
| "Download this YouTube video from 1:30 to 2:00" | Download and auto-trim the specified segment |
---
✨ Features
- 🖼️ Image Download - Search HD images from professional stock libraries
- 🎬 Video Clips - Get free commercial-use video footage
- 📺 YouTube Download - Download and trim support
- ✂️ Smart Trimming - Auto-crop to your desired length
- 🌍 Bilingual - Supports both Chinese and English commands
---
⚡ One-Line Install
In Claude Code, just say:
"Help me install https://github.com/yizhiyanhua-ai/media-downloader.git and all its dependencies, and configure yt-dlp to use browser cookies"
Claude will automatically:
- Download the skill to the correct location
- Install yt-dlp, ffmpeg and other dependencies
- Configure browser cookies (fixes YouTube "confirm you're not a bot" issue)
- Check installation status
Just click "Allow" when Claude asks for permission!
---
🔑 About API Keys
💡 On-Demand Configuration: No API Key needed during installation!
>
- YouTube Videos: No API Key required, works right after installation
- Image Downloads: Claude will guide you to configure Pexels API Key on first use
What Happens When You First Download Images?
When you first say "Download some cat pictures", Claude will:
1. Detect that you haven't configured a stock photo API Key yet 2. Guide you to register at https://www.pexels.com (Google/Apple quick signup supported) 3. Help you get the API Key and automatically save it to system environment variables 4. Then continue with the image download
The whole process takes just a few minutes, and you only need to do it once!
---
📋 More Image Sources (Optional)
Pexels covers most needs. If you want more image sources, just tell Claude:
"Help me configure Pixabay API Key" or "Help me configure Unsplash API Key"
Claude will guide you through registration and configuration.
---
💬 Usage Examples
⚠️ Important: Before using, tell Claude "Check the status of media-downloader" to make sure all dependencies are installed!
Download Images
"Download 5 starry sky images"
"Download 10 coffee shop photos"
"Find some landscape images suitable for wallpapers"Download Videos
💡 Recommended: If you need to download videos, use YouTube first! YouTube has rich content, high quality, and doesn't require an extra API Key.
"Download this video: https://youtube.com/watch?v=xxx"
"Download minute 2 to minute 3 of this YouTube video"
"Only download the audio from this video"If you need short video clips from stock libraries:
"Download a city night video, under 30 seconds"
"Find me a 15-second ocean wave video"
"Find some natural scenery videos suitable for backgrounds"---
📁 Download Location
By default, all files are saved to:
~/.claude/skills/media-downloader/downloads/Custom Download Directory
You can specify a custom download location using the -o or --output option:
# Download images to a specific folder
media_cli.py image "cats" -o ~/Pictures/cats
# Download videos to Desktop
media_cli.py video "sunset" -o ~/Desktop
# Download YouTube video to current directory
media_cli.py youtube "URL" -o .Or simply tell me where you want the files:
"Download 5 cat images to my Desktop"
"Save the video to ~/Videos/project"---
❓ FAQ
Q: YouTube says "Sign in to confirm you're not a bot"?
This is YouTube's anti-bot mechanism. The solution is to let yt-dlp use your browser's login session:
Method 1: Tell Claude (Recommended)
"Help me configure yt-dlp to use browser cookies"
Claude will set it up for you.
Method 2: Manual Configuration
1. Make sure you're logged into YouTube in your browser (Chrome/Firefox/Edge) 2. Add --cookies-from-browser chrome when downloading:
yt-dlp --cookies-from-browser chrome "YouTube_URL"💡 Tip: Replacechromewith your browser:firefox,edge,safari,brave, etc.
Q: Why are there no image search results?
A: Please confirm API Key is configured. Run status command to check configuration status.
Q: YouTube video download failed?
A: YouTube download doesn't need an API Key, but requires yt-dlp. Run pip install yt-dlp to install.
Q: Video trimming doesn't work?
A: ffmpeg is required. macOS users run brew install ffmpeg.
Q: Can these images/videos be used commercially?
A: Assets from Pexels, Pixabay, and Unsplash can all be used commercially for free, no attribution required (though attribution is appreciated).
---
🛠️ CLI Reference
For advanced users using command line directly:
# Check configuration status
media_cli.py status
# Download images
media_cli.py image "keywords" -n count -o output_dir
# Download stock videos
media_cli.py video "keywords" -d max_duration -n count
# Download YouTube video
media_cli.py youtube "URL" --start start_seconds --end end_seconds
# Search media (no download)
media_cli.py search "keywords" --type image/video/all
# Trim local video
media_cli.py trim input_file --start start --end end---
📦 Supported Sources
| Source | Type | Features |
|---|---|---|
| Pexels | Images + Videos | High quality, frequently updated |
| Pixabay | Images + Videos | Large quantity, diverse categories |
| Unsplash | Images | Artistic, great for wallpapers |
| YouTube | Videos | Rich content, trimming support |
---
📄 License
MIT License
---
🎬 Start using! Just tell me what images or videos you want!
Related skills
FAQ
What languages does media-downloader accept?
media-downloader accepts English and Chinese prompts. Triggers include "download images", "find video", "download media", "/media", and Chinese phrases like "下载图片" and "找视频".
Can media-downloader trim YouTube videos?
media-downloader supports automatic video clipping, including YouTube segment extraction, alongside stock image search. The user describes the desired clip in plain language and receives a trimmed local file.
Is Media Downloader safe to install?
skills.sh reports 1 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.