Install any skill in seconds. Free to start, no credit card required.
Get Started Free →drpy视频源创建与调试技能。当用户需要创建、修改、调试drpy视频源(用于TVBox、海阔视界、ZYPlayer等播放器)时使用此技能。包括drpy源属性配置、模板继承、正则表达式编写、本地代理设置、不同类型源(影视/听书/漫画/小说)支持。
.claude/skills/leoyeai-drpy-source-creator/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 246% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 125% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 160% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 192% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 115% | 0% |
drpy源是一种用于TVBox、海阔视界、ZYPlayer等播放器的视频源格式,使用JavaScript编写,支持动态内容抓取和解析。本技能提供完整的drpy源创建指南,包含属性配置、模板继承、正则表达式编写、常见问题调试等。
以下是基础的drpy源模板:
javascriptvar rule = { // 影视|听书|漫画|小说 类型: '影视', // 规则标题 title: '示例源', // 网页的域名根 host: 'https://www.example.com', // 网站的首页链接 homeUrl: '/latest/', // 分类页面链接 url: '/fyclass/fypage.html', // 搜索链接 searchUrl: '/vodsearch/紧箍咒/page/fypage.html', // 是否启用全局搜索 searchable: 1, // 是否启用快速搜索 quickSearch: 0, // 是否启用筛选 filterable: 0, // 请求头 headers: { 'User-Agent': 'MOBILE_UA', }, // 请求超时(毫秒) timeout: 5000, // 是否需要调用免嗅lazy函数 play_parse: true, // 静态分类名称 class_name: '电影&电视剧&动漫&综艺', // 静态分类标识 class_url: '1&2&3&4', }
第一步:获取网站源码
pythonimport requests headers = {'User-Agent': 'Mozilla/5.0...'} resp = requests.get('https://www.pitv.cc/', headers=headers) html = resp.text
第二步:确认关键选择器
✓ .hl-vod-list - 列表容器存在
✓ .hl-list-item - 列表项存在
✓ a&&title - 标题属性存在
✓ a&&data-original - 图片属性存在
✗ .remarks - 状态class不存在(实际是.hl-pic-text span)第三步:修正选择器
javascript// 错误(参考代码) 推荐: '.hl-vod-list;li;a&&title;a&&data-original;.remarks&&Text;a&&href', // 正确(实际HTML结构) 推荐: '.hl-vod-list;li;a&&title;a&&data-original;.hl-pic-text span&&Text;a&&href',
第四步:验证搜索功能
第五步:最终可用配置
javascriptvar rule = { title: '皮皮影视', host: 'https://www.pitv.cc', url: '/show/fyclassfyfilter/page/fypage/', searchable: 0, // 搜索需要验证码,禁用 class_name: '剧集&电影&动漫', class_url: '1&2&3', 推荐: '.hl-vod-list;li;a&&title;a&&data-original;.hl-pic-text span&&Text;a&&href', 一级: '.hl-vod-list&&.hl-list-item;a&&title;a&&data-original;.hl-pic-text span&&Text;a&&href', 二级: { title: 'h1&&Text', img: '.hl-lazy&&data-original', tabs: '.hl-plays-from&&a', lists: '.hl-plays-list:eq(#id)&&a', }, }
| 属性 | 类型 | 说明 | 示例 | |------|------|------|------| | 类型 | string | 源类型:'影视'、'听书'、'漫画'、'小说' | '影视' | | title | string | 规则标题,在播放器中显示的名称 | '鸭奈飞' | | host | string | 网站域名根,包含http/https协议 | 'https://yanetflix.com' | | homeUrl | string | 网站首页链接,用于获取分类和推荐 | '/latest/' | | url | string | 分类页面链接模板 | '/vod/show/id/fyclass/page/fypage.html' | | searchUrl | string | 搜索链接模板 | '/vodsearch/紧箍咒/page/fypage.html' | | searchable | number | 是否启用全局搜索 | 1(启用)或0(禁用) | | quickSearch | number | 是否启用快速搜索 | 1(启用)或0(禁用) | | filterable | number | 是否启用筛选 | 1(启用)或0(禁用) |
javascript// 静态分类配置 class_name: '电影&电视剧&动漫&综艺', class_url: '1&2&3&4', // 动态分类获取(推荐) class_parse: '#side-menu:lt(1) li;a&&Text;a&&href;com/(.*?)/', // 格式:列表选择器;标题选择器;链接选择器;正则提取(可选)
javascript// 请求头配置 headers: { 'User-Agent': 'MOBILE_UA', 'Cookie': 'searchneed=ok', 'Referer': 'https://www.example.com/' }, // 超时设置(毫秒) timeout: 5000, // 动态获取host(优先级最高) hostJs: async function () { let {HOST} = this; return HOST }, // 预处理(初始化时执行一次) 预处理: async function () { let {HOST} = this; return HOST },
drpy源的核心解析函数,用于获取不同页面的内容:
javascript// 推荐内容获取 推荐: async function () { let {input} = this; // input为homeUrl的内容 return [] }, // 一级列表获取 一级: async function () { let {input} = this; // input为url模板填入后的链接 return [] }, // 二级详情获取 二级: async function () { let {input} = this; // input为一级返回的链接 return {} }, // 搜索内容获取 搜索: async function () { let {input} = this; // input为searchUrl模板填入后的链接 return [] },
javascript// 免嗅播放解析 play_parse: true, // lazy函数处理播放地址 lazy: async function () { let {input} = this; return { url: input, parse: 0 } }, // 辅助嗅探规则 sniffer: 1, isVideo: "http((?!http).){26,}\\.(m3u8|mp4|flv|avi|mkv|wmv|mpg|mpeg|mov|ts|3gp|rm|rmvb|asf|m4a|mp3|wma)",
javascriptvar rule = Object.assign(muban.mxpro, { title: '鸭奈飞', host: 'https://yanetflix.com', url: '/index.php/vod/show/id/fyclass/page/fypage.html', class_parse: `.navbar-items li:gt(1):lt(6);a&&Text;a&&href;.*/(.*?).html`, });
javascriptvar rule = { title: 'cokemv', 模板: 'mxpro', host: 'https://cokemv.me', class_parse: `.navbar-items li:gt(1):lt(7);a&&Text;a&&href;/(\\d+).html`, }
javascriptvar rule = { 模板: '自动', 模板修改: $js.toString(() => { Object.assign(muban.自动.二级, { tab_text: 'div--small&&Text', }); }), title: '剧圈圈[自动]', host: 'https://www.jqqzx.cc/', url: '/vodshow/id/fyclass/page/fypage.html', searchUrl: '/vodsearch**/page/fypage.html', class_parse: '.navbar-items li:gt(2):lt(8);a&&Text;a&&href;.*/(.*?)\.html', cate_exclude: '今日更新|热榜', }
标准的视频源,支持电影、电视剧、动漫等内容。
用法与影视源一致,播放音频内容。
需要在lazy函数处理后返回pics://协议:
javascriptlazy: async function () { return "pics://图片链接1,图片链接2,图片链接3"; }
需要在lazy函数处理后返回novel://协议:
javascriptlazy: async function () { return 'novel://{"title":"章节名称","content":"章节内容"}'; }
在字符串属性(如class_parse)中使用正则表达式时需要注意转义:
| 需求 | JavaScript正则 | drpy字符串写法 | |------|---------------|----------------| | 数字 | /(\d+)/ | (\\d+) | | 任意字符 | /.*/ | .* | | 非贪婪匹配 | /(.*?)/ | (.*?) | | 多个分组 | /(\d+)-(\w+)/ | (\\d+)-(\\w+) |
javascriptproxy_rule: `js: log(input); input = [200,'text;plain','hello drpy'] `,
本地代理的input参数格式为三元素数组: [status_code, content_type, data]
示例:
javascript// 返回M3U8文件 input = [200,'application/vnd.apple.mpegurl', m3u8_content]; // 返回文本 input = [200,'text/plain', 'hello world'];
host是否正确timeout值(默认5000毫秒)log()函数调试输出play_parse设置lazy函数返回正确格式sniffer规则是否匹配视频链接class_parse格式正确bash# 安装uglify-js npm install uglify-js -g # 压缩JS文件 uglifyjs source.js -o source.min.js
where uglifyjsC:\Users\username\AppData\Roaming\npm\uglifyjs.cmd$FileName$ -o $FileNameWithoutExtension$.min.js$FileDir$log()函数输出调试信息javascript// 加密 RSA.encode(data, key, option); // 解密 RSA.decode(data, key, option);
javascript// 线路顺序 tab_order: ['lzm3u8', 'wjm3u8', '1080zyk'], // 线路名替换 tab_rename: { 'lzm3u8': '量子资源', '1080zyk': '1080看', }, // 移除线路 tab_remove: ['tkm3u8'],
javascript// 图片替换 图片替换: 'https://old-domain.com/=>https://new-domain.com/', // 图片来源(添加referer) 图片来源: '@Referer=http://www.example.com@User-Agent=custom-ua',
在drpy源的二级详情或其他交互页面中,可以通过返回action对象来定义按钮行为:
javascript二级: async function () { return { // 视频详情信息 vod_name: "影片名称", vod_play_from: "播放来源", vod_play_url: "第1集$播放地址", // Action配置 action: { button: 2, // 按钮类型:0-关闭, 1-取消, 2-确定和取消, 3-确定/取消/重置, 4-确定/取消/重置/预览 // 其他action属性... }, }; },
| 值 | 按钮组合 | 适用场景 | |----|----------|----------| | 0 | 关闭按钮 | 只提供关闭功能 | | 1 | 取消按钮 | 简单确认场景 | | 2 | 确定和取消 | 需要用户确认的操作 | | 3 | 确定、取消、重置 | 表单或设置页面 | | 4 | 确定、取消、重置、预览 | 复杂的编辑页面 |
除了按钮,action还可以包含其他指令:
javascriptaction: { button: 2, message: "操作提示信息", redirect: "目标URL", // 其他自定义属性 },
drpy源可以通过压缩减少文件大小:
bash# 安装uglify-js npm install uglify-js -g # 压缩JS文件 uglifyjs source.js -o source.min.js # 推荐配置 uglifyjs source.js -o source.min.js -c -m --comments '/@license|@preserve/'
where uglifyjsC:\Users\username\AppData\Roaming\npm\uglifyjs.cmd$FileName$ -o $FileNameWithoutExtension$.min.js$FileDir$详细格式化指南见:formatting.md
症状:分类能显示,但点击后列表为空
原因:参考代码的选择器与实际HTML不符
排查方法:
pythonimport requests from bs4 import BeautifulSoup headers = {'User-Agent': 'Mozilla/5.0...'} html = requests.get('https://example.com/', headers=headers).text soup = BeautifulSoup(html, 'html.parser') # 检查选择器是否存在 print('hl-vod-list:', bool(soup.find(class_='hl-vod-list'))) print('hl-list-item:', bool(soup.find(class_='hl-list-item'))) print('remarks:', bool(soup.find(class_='remarks'))) # 可能不存在 # 查找实际的class名 for elem in soup.find_all(class_=True): if 'pic' in str(elem.get('class')) or 'text' in str(elem.get('class')): print(elem.name, elem.get('class'))
解决方案:
javascript// 错误 推荐: '.hl-vod-list;li;a&&title;a&&data-original;.remarks&&Text;a&&href', // 正确(根据实际HTML调整) 推荐: '.hl-vod-list;li;a&&title;a&&data-original;.hl-pic-text span&&Text;a&&href',
症状:搜索返回空结果或报错
排查方法:
python# 测试搜索URL search_resp = requests.get('https://example.com/search/关键词/', headers=headers) print('状态码:', search_resp.status_code) print('是否包含验证码:', '验证码' in search_resp.text) print('是否包含结果:', 'hl-vod-list' in search_resp.text)
常见情况:
searchable: 0 禁用搜索症状:能进入详情页,但无播放列表
排查方法:
python# 获取详情页HTML detail_html = requests.get('https://example.com/vod/123/', headers=headers).text # 检查关键元素 print('h1:', 'h1' in detail_html) print('hl-plays-list:', 'hl-plays-list' in detail_html) print('hl-plays-from:', 'hl-plays-from' in detail_html)
解决方案:
javascript// 根据实际HTML调整二级选择器 二级: { title: 'h1&&Text', // 使用h1而不是.hl-dc-title img: '.hl-lazy&&data-original', tabs: '.hl-plays-from&&a', // 播放源 lists: '.hl-plays-list:eq(#id)&&a', // 剧集列表 }
症状:分类点击后跳转错误页面
排查方法:
python# 测试URL模板 # 参考代码: /show/fyclassfyfilter/page/fypage/ # 实际测试 for cls in ['1', '2', '3']: url = f'https://example.com/show/{cls}--------/page/1/' resp = requests.get(url, headers=headers) print(f'{url} -> {resp.status_code}')
解决方案:
javascript// 确认URL格式 url: '/show/fyclassfyfilter/page/fypage/', // fyclass和fyfilter连在一起 // 或 url: '/type/fyclass/', // 简单的分类格式
javascript推荐: `js: let html = request(input); log('首页HTML长度:', html.length); log('是否包含视频列表:', html.includes('hl-vod-list')); let items = pdfa(html, '.hl-vod-list li'); log('提取到视频数量:', items.length); // 继续解析... `,
javascript// 先测试基础功能 var rule = { title: '测试源', host: 'https://example.com', url: '/type/fyclass/', class_name: '分类1&分类2', class_url: '1&2', 推荐: 'a;a&&Text;;;a&&href', // 只取标题和链接 一级: 'a;a&&Text;;;a&&href', 二级: '*', lazy: '' };
| 任务 | 参考文档 | |------|----------| | 新建一个drpy源 | 基础模板 | | 了解所有属性含义 | 属性详解 | | 使用模板继承 | 模板继承 | | 编写解析函数 | 解析函数 | | 调试源问题 | 问题排查 | | 压缩源代码 | 代码格式化 | | 分析网站结构 | analyze_site.py |
核心原则:
提示:创建drpy源时,建议先使用简单的模板测试基础功能,再逐步添加高级功能。使用模板继承可以大大简化开发过程。
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 11,053 | 7,959 | -28% | 1 | 1 | 0% | 2,061 | 7,140 | +246% | 0 | 0 | — |
case-02 | pass→pass | 18,564 | 13,986 | -25% | 1 | 1 | 0% | 3,248 | 8,375 | +158% | 0 | 0 | — |
case-03 | fail→pass | 15,590 | 13,990 | -10% | 1 | 1 | 0% | 3,680 | 8,266 | +125% | 0 | 0 | — |
case-04 | fail→pass | 12,783 | 8,608 | -33% | 1 | 1 | 0% | 2,763 | 7,182 | +160% | 0 | 0 | — |
case-05 | pass→pass | 8,829 | 6,066 | -31% | 1 | 1 | 0% | 1,804 | 6,510 | +261% | 0 | 0 | — |
case-06 | pass→pass | 14,789 | 8,272 | -44% | 1 | 1 | 0% | 1,573 | 6,075 | +286% | 0 | 0 | — |
case-07 | fail→pass | 10,550 | 6,727 | -36% | 1 | 1 | 0% | 2,314 | 6,765 | +192% | 0 | 0 | — |
case-08 | pass→pass | 8,494 | 6,050 | -29% | 1 | 1 | 0% | 1,543 | 6,471 | +319% | 0 | 0 | — |
case-09 | pass→pass | 6,091 | 2,787 | -54% | 1 | 1 | 0% | 1,195 | 6,043 | +406% | 0 | 0 | — |
case-10 | pass→pass | 5,602 | 3,968 | -29% | 1 | 1 | 0% | 1,244 | 6,196 | +398% | 0 | 0 | — |
case-11 | fail→pass | 13,819 | 3,394 | -75% | 1 | 1 | 0% | 2,838 | 6,099 | +115% | 0 | 0 | — |
case-12 | pass→pass | 4,070 | 2,917 | -28% | 1 | 1 | 0% | 897 | 5,953 | +564% | 0 | 0 | — |
case-13 | fail→pass | 9,572 | 3,014 | -69% | 1 | 1 | 0% | 1,930 | 5,979 | +210% | 0 | 0 | — |
case-14 | pass→pass | 4,462 | 3,319 | -26% | 1 | 1 | 0% | 783 | 5,958 | +661% | 0 | 0 | — |
case-15 | pass→pass | 11,418 | 11,297 | -1% | 1 | 1 | 0% | 2,551 | 7,485 | +193% | 0 | 0 | — |
case-16 | fail→pass | 14,115 | 8,078 | -43% | 1 | 1 | 0% | 2,885 | 7,180 | +149% | 0 | 0 | — |
case-17 | pass→pass | 5,262 | 3,671 | -30% | 1 | 1 | 0% | 914 | 6,039 | +561% | 0 | 0 | — |
case-18 | fail→pass | 9,317 | 4,625 | -50% | 1 | 1 | 0% | 1,774 | 6,197 | +249% | 0 | 0 | — |
case-19 | pass→pass | 7,505 | 4,040 | -46% | 1 | 1 | 0% | 1,464 | 6,180 | +322% | 0 | 0 | — |
case-20 | pass→pass | 11,558 | 14,160 | +23% | 1 | 1 | 0% | 2,434 | 8,595 | +253% | 0 | 0 | — |
case-21 | pass→pass | 9,732 | 10,365 | +7% | 1 | 1 | 0% | 2,016 | 7,626 | +278% | 0 | 0 | — |
case-22 | fail→fail | 6,393 | 9,183 | +44% | 1 | 1 | 0% | 1,300 | 7,349 | +465% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +36 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.