Initial commit: skills library
- 70 skills with code and documentation - Add .gitignore (ignore __pycache__, output/, temp/, venv/) - Clean up test intermediates and caches
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
---
|
||||
name: youtube-downloader
|
||||
description: 使用 Savenow API 下载 YouTube 视频/音频。调用第三方 API 直接获取下载链接,无需 cookies。当用户提到下载 YouTube、视频下载、YT 下载时触发此技能。
|
||||
---
|
||||
|
||||
# YouTube Downloader Skill
|
||||
|
||||
使用 Savenow API 下载 YouTube 视频/音频的技能。
|
||||
|
||||
## 概述
|
||||
|
||||
- **API 来源**: 从 Chrome 插件 "Youtube Tools All in one" (Tampermonkey 脚本) 提取
|
||||
- **API 服务**: savenow.to / lbserver.xyz
|
||||
- **API Key**: `dfcb6d76f2f6a9894gjkege8a4ab232222` (来自插件内置)
|
||||
- **无需 cookies**: 直接通过 API 调用,无需登录验证
|
||||
- **稳定性**: 已验证可用,视频和音频都能下载
|
||||
|
||||
## 适用场景
|
||||
|
||||
- 下载 YouTube 视频(mp4, webm 等格式)
|
||||
- 下载 YouTube 音频(mp3, flac, m4a 等格式)
|
||||
- 无需 cookies / 登录验证
|
||||
|
||||
## 核心参数
|
||||
|
||||
- **API Base**: `https://p.savenow.to` 或 `https://p.lbserver.xyz`
|
||||
- **API Key**: `dfcb6d76f2f6a9894gjkege8a4ab232222` (来自 Tampermonkey 插件 Youtube Tools All in one)
|
||||
- **备用 API**: `https://dub.io` (通过 `https://dubs.io/wp-json/tools/v1/download-video`)
|
||||
|
||||
## 视频格式
|
||||
|
||||
| format 值 | 说明 |
|
||||
|-----------|------|
|
||||
| 144 | 144p Mp4 |
|
||||
| 240 | 240p Mp4 |
|
||||
| 360 | 360p Mp4 |
|
||||
| 480 | 480p Mp4 |
|
||||
| 720 | 720p HD Mp4 |
|
||||
| 1080 | 1080p FULL HD Mp4 |
|
||||
| 4k | 2160p 4K WEBM |
|
||||
| 8k | 4320p 8K WEBM |
|
||||
|
||||
## 音频格式
|
||||
|
||||
| format 值 | 说明 |
|
||||
|-----------|------|
|
||||
| mp3 | Audio MP3 |
|
||||
| m4a | Audio M4A |
|
||||
| flac | Audio FLAC |
|
||||
| wav | Audio WAV |
|
||||
| aac | Audio AAC |
|
||||
| opus | Audio OPUS |
|
||||
| ogg | Audio OGG |
|
||||
|
||||
## 使用方法
|
||||
|
||||
### 1. 请求下载
|
||||
|
||||
```bash
|
||||
curl "https://p.savenow.to/ajax/download.php?copyright=0&allow_extended_duration=1&format=mp3&url=VIDEO_URL&api=API_KEY"
|
||||
```
|
||||
|
||||
返回 JSON:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"id": "1CXjdNLFuewLXLBpOMu3BGJ",
|
||||
"title": "视频标题",
|
||||
"info": {
|
||||
"title": "视频标题"
|
||||
},
|
||||
"progress_url": "https://p.savenow.to/api/progress?id=1CXjdNLFuewLXLBpOMu3BGJ"
|
||||
}
|
||||
```
|
||||
|
||||
**获取标题(Python)**:
|
||||
```python
|
||||
import json
|
||||
import re
|
||||
|
||||
with open('response.json', 'rb') as f:
|
||||
content = f.read()
|
||||
|
||||
match = re.search(rb'"title":"([^"]+)"', content)
|
||||
escaped = match.group(1).decode('ascii')
|
||||
title = bytes(escaped, 'ascii').decode('unicode_escape')
|
||||
# title 就是视频标题
|
||||
```
|
||||
|
||||
参数说明:
|
||||
- `format`: 视频或音频格式
|
||||
- `url`: YouTube 视频 URL
|
||||
- `api`: API Key: `dfcb6d76f2f6a9894gjkege8a4ab232222`
|
||||
- `copyright`: 0
|
||||
- `allow_extended_duration`: 1
|
||||
|
||||
返回 JSON:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"id": "1CXjdNLFuewLXLBpOMu3BGJ",
|
||||
"progress_url": "https://p.savenow.to/api/progress?id=1CXjdNLFuewLXLBpOMu3BGJ"
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 轮询下载链接
|
||||
|
||||
```bash
|
||||
curl "https://p.savenow.to/api/progress?id=VIDEO_ID"
|
||||
```
|
||||
|
||||
当 `success: 1` 且 `progress: 1000` 时,返回 `download_url`。
|
||||
|
||||
### 3. 下载文件
|
||||
|
||||
```bash
|
||||
curl -L -o output.mp3 "DOWNLOAD_URL"
|
||||
```
|
||||
|
||||
## Python 示例
|
||||
|
||||
```python
|
||||
import requests
|
||||
import time
|
||||
|
||||
API_KEY = 'dfcb6d76f2f6a9894gjkege8a4ab232222'
|
||||
BASE_URL = 'https://p.savenow.to'
|
||||
|
||||
def download_youtube(url, format='mp3', output_path='output.mp3'):
|
||||
# 1. 请求下载
|
||||
params = {
|
||||
'copyright': '0',
|
||||
'allow_extended_duration': '1',
|
||||
'format': format,
|
||||
'url': url,
|
||||
'api': API_KEY
|
||||
}
|
||||
resp = requests.get(f'{BASE_URL}/ajax/download.php', params=params)
|
||||
data = resp.json()
|
||||
|
||||
if not data.get('success'):
|
||||
raise Exception(f'Request failed: {data}')
|
||||
|
||||
progress_id = data['id']
|
||||
progress_url = data['progress_url']
|
||||
|
||||
# 2. 轮询直到完成
|
||||
while True:
|
||||
time.sleep(3)
|
||||
resp = requests.get(progress_url)
|
||||
status = resp.json()
|
||||
|
||||
if status.get('success') == 1 and status.get('progress') == 1000:
|
||||
download_url = status['download_url']
|
||||
break
|
||||
|
||||
# 3. 下载文件
|
||||
resp = requests.get(download_url, stream=True)
|
||||
with open(output_path, 'wb') as f:
|
||||
for chunk in resp.iter_content(chunk_size=8192):
|
||||
f.write(chunk)
|
||||
|
||||
return output_path
|
||||
|
||||
# 使用
|
||||
download_youtube('https://www.youtube.com/watch?v=VIDEO_ID', 'mp3', 'video.mp3')
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **API 不稳定时**: 会自动尝试备用 API (`p.lbserver.xyz`)
|
||||
2. **下载链接有效期**: 获取后尽快下载
|
||||
3. **支持地区**: API 可能因地区不同而有差异
|
||||
4. **商业用途**: API 为免费使用,但有商业合作联系方式
|
||||
|
||||
## 参考
|
||||
|
||||
- 插件源码: `Youtube Tools All in one local download mp3 mp4`
|
||||
- API 来源: `savenow.to`
|
||||
@@ -0,0 +1,43 @@
|
||||
# YouTube Downloader 配置文件
|
||||
|
||||
## API 配置
|
||||
|
||||
- **API Key**: dfcb6d76f2f6a9894gjkege8a4ab232222
|
||||
- **来源**: 从 Tampermonkey 插件 "Youtube Tools All in one local download mp3 mp4 HIGH QUALITY" 提取
|
||||
- **插件版本**: 2.4.3.2
|
||||
- **插件文件**: `Youtube Tools All in one local download mp3 mp4 HIGT QUALITY return dislikes and more-2.4.3.2.user.js`
|
||||
|
||||
## API 端点
|
||||
|
||||
- **主 API**: https://p.savenow.to
|
||||
- **备用 API 1**: https://p.lbserver.xyz
|
||||
- **备用 API 2**: https://dub.io (通过 JSON API)
|
||||
|
||||
## 使用方式
|
||||
|
||||
### 命令行
|
||||
|
||||
```bash
|
||||
python .opencode/skills/youtube-downloader/scripts/youtube_dl.py "YouTube_URL" [format]
|
||||
```
|
||||
|
||||
示例:
|
||||
```bash
|
||||
# 下载 MP3
|
||||
python .opencode/skills/youtube-downloader/scripts/youtube_dl.py "https://www.youtube.com/watch?v=xxx" mp3
|
||||
|
||||
# 下载 1080p 视频
|
||||
python .opencode/skills/youtube-downloader/scripts/youtube_dl.py "https://www.youtube.com/watch?v=xxx" 1080
|
||||
```
|
||||
|
||||
### 格式说明
|
||||
|
||||
视频格式: 144, 240, 360, 480, 720, 1080, 4k, 8k
|
||||
音频格式: mp3, m4a, flac, wav, aac, opus, ogg
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. API 为免费使用,但可能有频率限制
|
||||
2. 下载链接有有效期,建议获取后尽快下载
|
||||
3. 部分视频可能因版权或地区限制无法下载
|
||||
4. 如遇到 API 不稳定,可尝试备用 API
|
||||
@@ -0,0 +1,4 @@
|
||||
# API 配置(本地使用,不提交到 Git)
|
||||
API_KEY=dfcb6d76f2f6a9894gjkege8a4ab232222
|
||||
API_BASE=https://p.savenow.to
|
||||
API_BACKUP=https://p.lbserver.xyz
|
||||
@@ -0,0 +1,2 @@
|
||||
# youtube-downloader - dependencies
|
||||
requests>=0.0.1
|
||||
@@ -0,0 +1,172 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
YouTube 视频下载器
|
||||
使用 Savenow API 下载 YouTube 视频/音频
|
||||
|
||||
使用方法:
|
||||
python youtube_dl.py <YouTube_URL> [format]
|
||||
|
||||
示例:
|
||||
python youtube_dl.py "https://www.youtube.com/watch?v=xxx" mp3
|
||||
python youtube_dl.py "https://www.youtube.com/watch?v=xxx" 1080
|
||||
"""
|
||||
|
||||
import requests
|
||||
import time
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
import json
|
||||
|
||||
API_KEY = "dfcb6d76f2f6a9894gjkege8a4ab232222"
|
||||
API_BASES = ["https://p.savenow.to", "https://p.lbserver.xyz"]
|
||||
|
||||
VIDEO_FORMATS = {
|
||||
"144": "144p Mp4",
|
||||
"240": "240p Mp4",
|
||||
"360": "360p Mp4",
|
||||
"480": "480p Mp4",
|
||||
"720": "720p HD Mp4",
|
||||
"1080": "1080p FULL HD Mp4",
|
||||
"4k": "2160p 4K WEBM",
|
||||
"8k": "4320p 8K WEBM",
|
||||
}
|
||||
|
||||
AUDIO_FORMATS = {
|
||||
"mp3": "Audio MP3",
|
||||
"m4a": "Audio M4A",
|
||||
"flac": "Audio FLAC",
|
||||
"wav": "Audio WAV",
|
||||
"aac": "Audio AAC",
|
||||
"opus": "Audio OPUS",
|
||||
"ogg": "Audio OGG",
|
||||
}
|
||||
|
||||
|
||||
def get_title_from_response(response_text):
|
||||
"""从 API 响应中提取视频标题"""
|
||||
try:
|
||||
match = re.search(rb'"title":"([^"]+)"', response_text.encode("utf-8"))
|
||||
if match:
|
||||
escaped = match.group(1).decode("ascii")
|
||||
title = bytes(escaped, "ascii").decode("unicode_escape")
|
||||
return title
|
||||
except:
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
def request_download(url, format="mp3"):
|
||||
"""请求下载,返回 progress_id"""
|
||||
params = {
|
||||
"copyright": "0",
|
||||
"allow_extended_duration": "1",
|
||||
"format": format,
|
||||
"url": url,
|
||||
"api": API_KEY,
|
||||
}
|
||||
|
||||
for base in API_BASES:
|
||||
try:
|
||||
resp = requests.get(f"{base}/ajax/download.php", params=params, timeout=30)
|
||||
data = resp.json()
|
||||
if data.get("success"):
|
||||
return (
|
||||
data.get("id"),
|
||||
data.get("progress_url"),
|
||||
data.get("info", {}).get("title", ""),
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"尝试 {base} 失败: {e}")
|
||||
continue
|
||||
|
||||
raise Exception("所有 API 都失败了")
|
||||
|
||||
|
||||
def poll_progress(progress_url, title):
|
||||
"""轮询直到下载完成,返回下载链接"""
|
||||
print("等待处理中...", end="", flush=True)
|
||||
|
||||
while True:
|
||||
time.sleep(3)
|
||||
resp = requests.get(progress_url, timeout=30)
|
||||
data = resp.json()
|
||||
|
||||
progress = data.get("progress", 0) / 10
|
||||
status = data.get("success", 0)
|
||||
|
||||
if status == 1 and progress == 100:
|
||||
return data.get("download_url"), data.get("alternative_download_urls", [])
|
||||
|
||||
print(".", end="", flush=True)
|
||||
|
||||
return None, []
|
||||
|
||||
|
||||
def download_file(download_url, output_path):
|
||||
"""下载文件"""
|
||||
print(f"\n正在下载: {output_path}")
|
||||
|
||||
resp = requests.get(download_url, stream=True)
|
||||
total = int(resp.headers.get("content-length", 0))
|
||||
|
||||
downloaded = 0
|
||||
with open(output_path, "wb") as f:
|
||||
for chunk in resp.iter_content(chunk_size=8192):
|
||||
if chunk:
|
||||
f.write(chunk)
|
||||
downloaded += len(chunk)
|
||||
if total:
|
||||
percent = downloaded * 100 / total
|
||||
print(f"\r进度: {percent:.1f}%", end="", flush=True)
|
||||
|
||||
print("\n下载完成!")
|
||||
return output_path
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print(__doc__)
|
||||
print("\n可用格式:")
|
||||
print("视频:", ", ".join(VIDEO_FORMATS.keys()))
|
||||
print("音频:", ", ".join(AUDIO_FORMATS.keys()))
|
||||
sys.exit(1)
|
||||
|
||||
url = sys.argv[1]
|
||||
format = sys.argv[2] if len(sys.argv) > 2 else "mp3"
|
||||
|
||||
# 获取标题
|
||||
print(f"请求下载: {url}")
|
||||
print(f"格式: {format}")
|
||||
|
||||
progress_id, progress_url, title = request_download(url, format)
|
||||
print(f"视频标题: {title}")
|
||||
|
||||
# 轮询下载链接
|
||||
download_url, alternatives = poll_progress(progress_url, title)
|
||||
|
||||
if not download_url and alternatives:
|
||||
download_url = alternatives[0].get("url")
|
||||
|
||||
if not download_url:
|
||||
print("获取下载链接失败!")
|
||||
sys.exit(1)
|
||||
|
||||
# 确定文件扩展名
|
||||
ext = (
|
||||
format
|
||||
if format in ["mp3", "m4a", "flac", "wav", "aac", "opus", "ogg"]
|
||||
else "mp4"
|
||||
)
|
||||
|
||||
# 生成文件名
|
||||
safe_title = re.sub(r'[\\/:*?"<>|]', "_", title or "video")
|
||||
output_path = f"{safe_title}({format}p).{ext}"
|
||||
|
||||
# 下载
|
||||
download_file(download_url, output_path)
|
||||
print(f"已保存: {output_path}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user