Skip to content

Commit 51cc2ba

Browse files
author
Taois
committed
feat: 更新版本,增加完善的定时任务功能
1 parent b518c8e commit 51cc2ba

32 files changed

+992
-68
lines changed

.env.development

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,7 @@ EPG_URL = https://iptv.crestekk.cn/epgphp/index.php?ch={name}&date={date}
2222
LOGO_URL = https://live.mxdyeah.top/logo/{name}.png
2323
LIVE_URL = 'https://livetv.wqwqwq.sbs/tv.m3u'
2424
# LIVE_URL = './lives/tv.m3u'
25-
MAX_TASK = 8
25+
MAX_TASK = 8
26+
dingding_webhook=
27+
wechat_webhook=
28+
tx_news_guonei_api_key=

README.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# drpyS(drpy-node)
22

33
nodejs作为服务端的drpy实现。全面升级异步写法
4-
~~积极开发中,每日一更~~,当前进度 `66%`
4+
~~积极开发中,每日一更~~,当前进度 `68%`
55
~~找工作中,随缘更新~~
66
上班当牛马,下班要带娃,阶段性佛系趁娃睡觉熬夜更新
77

@@ -13,6 +13,8 @@ nodejs作为服务端的drpy实现。全面升级异步写法
1313
* [接口压测教程](/docs/httpTest.md)
1414
* [央视点播解析工具](/proxy/央视大全[官]/index.html)
1515
* [cookie管理插件](/apps/cookie-butler/index.html)
16+
* [cron表达式插件](/apps/cron-generator/index.html)
17+
* [一些接口说明](docs/apidoc.md)
1618
* [本站防止爬虫协议](/robots.txt)
1719
* [本项目主页-免翻](https://github.com/hjdhnx/drpy-node)
1820
* [DS源适配猫影视](https://github.com/hjdhnx/CatPawOpen/tree/ds-cat)
@@ -21,7 +23,11 @@ nodejs作为服务端的drpy实现。全面升级异步写法
2123

2224
## 更新记录
2325

24-
### 20250806
26+
### 20250810
27+
28+
更新至V1.2.8
29+
30+
### 20250808
2531

2632
更新至V1.2.7
2733

@@ -65,6 +71,7 @@ todo:
6571
* [puppeteer使用教程](docs/pupInstall.md)
6672
* [drpyS源属性说明](docs/ruleAttr.md)
6773
* [drpy2写源简述](docs/ruleDesc.md)
74+
* [关姐算法搭建说明](docs/suanfa.md)
6875

6976
## 问题说明
7077

apps/cron-generator/index.html

Lines changed: 305 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,305 @@
1+
<!doctype html>
2+
<html lang="zh-CN">
3+
<head>
4+
<meta charset="utf-8" />
5+
<meta name="viewport" content="width=device-width,initial-scale=1" />
6+
<title>Cron 表达式生成器</title>
7+
<style>
8+
:root{--bg:#0f1724;--card:#0b1220;--muted:#94a3b8;--accent:#7c3aed;--glass: rgba(255,255,255,0.03)}
9+
html,body{height:100%;margin:0;font-family:Inter,system-ui,-apple-system,Segoe UI,Roboto,'Helvetica Neue',Arial;color:#e6eef8;background:linear-gradient(180deg,#071025 0%, #081226 60%);-webkit-font-smoothing:antialiased}
10+
.wrap{max-width:980px;margin:28px auto;padding:20px}
11+
.card{background:linear-gradient(180deg, rgba(255,255,255,0.02), rgba(255,255,255,0.01));border-radius:14px;padding:18px;box-shadow:0 6px 30px rgba(2,6,23,0.6);border:1px solid rgba(255,255,255,0.03)}
12+
h1{margin:0 0 6px;font-size:20px}
13+
p.lead{margin:0 0 16px;color:var(--muted);font-size:13px}
14+
15+
.grid{display:grid;grid-template-columns:1fr 320px;gap:16px}
16+
@media(max-width:880px){.grid{grid-template-columns:1fr} .right{order:2}}
17+
18+
.field{display:flex;flex-direction:column;margin-bottom:10px}
19+
label{font-size:13px;color:var(--muted);margin-bottom:6px}
20+
select,input[type="time"],input[type="number"],input[type="text"]{background:var(--glass);border:1px solid rgba(255,255,255,0.04);padding:10px;border-radius:8px;color:#e6eef8}
21+
.row{display:flex;gap:10px}
22+
.btn{display:inline-flex;align-items:center;gap:8px;padding:10px 12px;border-radius:10px;border:none;cursor:pointer}
23+
.btn-primary{background:linear-gradient(90deg,var(--accent),#4f46e5);color:white}
24+
.btn-ghost{background:transparent;border:1px solid rgba(255,255,255,0.04);color:var(--muted)}
25+
26+
.preset-list{display:flex;flex-wrap:wrap;gap:8px}
27+
.chip{background:rgba(255,255,255,0.03);padding:8px 10px;border-radius:999px;font-size:13px;color:var(--muted);cursor:pointer;border:1px solid rgba(255,255,255,0.02)}
28+
29+
pre.preview{background:#020617;padding:12px;border-radius:8px;color:#c7f9d7;overflow:auto}
30+
.meta{font-size:13px;color:var(--muted);margin-top:8px}
31+
32+
.small{font-size:12px;color:var(--muted)}
33+
.footer{display:flex;justify-content:space-between;align-items:center;margin-top:12px}
34+
35+
/* mobile tweaks */
36+
@media(max-width:420px){.wrap{padding:12px} h1{font-size:18px}}
37+
</style>
38+
</head>
39+
<body>
40+
<div class="wrap">
41+
<div class="card">
42+
<h1>Cron 表达式生成器</h1>
43+
<p class="lead">以友好的交互方式选择时间/频率,自动生成支持 6 字段(秒 分 时 日 月 周)格式的 cron 表达式。移动端自适应。</p>
44+
45+
<div class="grid">
46+
<div class="left">
47+
<div class="field">
48+
<label>调度类型</label>
49+
<select id="mode">
50+
<option value="every">每隔(间隔)</option>
51+
<option value="daily">每天特定时间</option>
52+
<option value="weekly">每周(指定星期几)</option>
53+
<option value="monthly">每月(指定日)</option>
54+
<option value="custom">高级自定义(直接编辑字段)</option>
55+
</select>
56+
</div>
57+
58+
<div id="intervalBox" class="field">
59+
<label>间隔设置</label>
60+
<div class="row">
61+
<select id="intervalUnit">
62+
<option value="minutes">分钟</option>
63+
<option value="hours">小时</option>
64+
<option value="days"></option>
65+
</select>
66+
<input id="intervalValue" type="number" min="1" value="5" style="width:100px" />
67+
</div>
68+
<div class="small">示例:每 5 分钟</div>
69+
</div>
70+
71+
<div id="dailyBox" class="field" style="display:none">
72+
<label>每天执行时间</label>
73+
<input id="dailyTime" type="time" value="09:00" />
74+
<div class="small">指定本地时间(例如 Asia/Shanghai)</div>
75+
</div>
76+
77+
<div id="weeklyBox" class="field" style="display:none">
78+
<label>每周选择</label>
79+
<div class="row" style="flex-wrap:wrap">
80+
<label><input type="checkbox" value="0" class="weekday" /> 周日</label>
81+
<label><input type="checkbox" value="1" class="weekday" /> 周一</label>
82+
<label><input type="checkbox" value="2" class="weekday" /> 周二</label>
83+
<label><input type="checkbox" value="3" class="weekday" /> 周三</label>
84+
<label><input type="checkbox" value="4" class="weekday" /> 周四</label>
85+
<label><input type="checkbox" value="5" class="weekday" /> 周五</label>
86+
<label><input type="checkbox" value="6" class="weekday" /> 周六</label>
87+
</div>
88+
<div class="field" style="margin-top:8px">
89+
<label>执行时间</label>
90+
<input id="weeklyTime" type="time" value="09:00" />
91+
</div>
92+
</div>
93+
94+
<div id="monthlyBox" class="field" style="display:none">
95+
<label>每月的哪一天</label>
96+
<input id="monthDay" type="number" min="1" max="31" value="1" />
97+
<div class="field" style="margin-top:8px">
98+
<label>执行时间</label>
99+
<input id="monthlyTime" type="time" value="09:00" />
100+
</div>
101+
</div>
102+
103+
<div id="customBox" class="field" style="display:none">
104+
<label>自定义字段(秒 分 时 日 月 周)</label>
105+
<input id="customFields" type="text" placeholder="例: 0 0 9 * * *" />
106+
<div class="small">请提供 6 个字段的 cron 表达式,秒为首位。</div>
107+
</div>
108+
109+
<div class="field">
110+
<label>时区(可选)</label>
111+
<select id="timezone">
112+
<option value="">默认 (系统时区)</option>
113+
<option value="Asia/Shanghai">Asia/Shanghai (北京时间)</option>
114+
<option value="UTC">UTC</option>
115+
<option value="Europe/London">Europe/London</option>
116+
<option value="America/New_York">America/New_York</option>
117+
</select>
118+
<div class="small">如果你希望在特定时区执行,请选择。否则使用服务器默认时区。</div>
119+
</div>
120+
121+
<div class="field">
122+
<label>执行次数限制(说明)</label>
123+
<input id="limitCount" type="number" min="0" value="0" />
124+
<div class="small">注意:cron 本身无法限制总执行次数。填写 >0 仅会在下方显示建议的实现方式。</div>
125+
</div>
126+
127+
<div style="margin-top:10px;display:flex;gap:8px;flex-wrap:wrap">
128+
<button id="generate" class="btn btn-primary">生成 Cron</button>
129+
<button id="copy" class="btn btn-ghost">复制到剪贴板</button>
130+
<button id="reset" class="btn btn-ghost">重置</button>
131+
</div>
132+
133+
<div style="margin-top:12px">
134+
<div class="preset-list">
135+
<div class="chip" data-preset="@every_minute">每分钟</div>
136+
<div class="chip" data-preset="@hourly">每小时</div>
137+
<div class="chip" data-preset="@daily">每天 00:00</div>
138+
<div class="chip" data-preset="@weekday9">工作日 09:00</div>
139+
<div class="chip" data-preset="@monthly1">每月 1 日 09:00</div>
140+
</div>
141+
</div>
142+
143+
</div>
144+
145+
<div class="right card" style="padding:14px">
146+
<h2 style="margin-top:0">预览</h2>
147+
<pre id="cronPreview" class="preview"># 生成的 cron 表达式会在这里显示</pre>
148+
<div class="meta" id="metaZone">时区: <span id="metaTz">系统时区</span></div>
149+
<div class="meta" id="metaLimit" style="margin-top:6px;color:var(--muted)"></div>
150+
<div class="footer">
151+
<div class="small">支持 6 字段 cron(秒 分 时 日 月 周)</div>
152+
<div style="display:flex;gap:8px">
153+
<button id="explain" class="btn btn-ghost">字段说明</button>
154+
<button id="download" class="btn btn-ghost">下载 .txt</button>
155+
</div>
156+
</div>
157+
</div>
158+
</div>
159+
160+
</div>
161+
</div>
162+
163+
<script>
164+
// DOM refs
165+
const mode = document.getElementById('mode');
166+
const intervalBox = document.getElementById('intervalBox');
167+
const dailyBox = document.getElementById('dailyBox');
168+
const weeklyBox = document.getElementById('weeklyBox');
169+
const monthlyBox = document.getElementById('monthlyBox');
170+
const customBox = document.getElementById('customBox');
171+
const intervalUnit = document.getElementById('intervalUnit');
172+
const intervalValue = document.getElementById('intervalValue');
173+
const dailyTime = document.getElementById('dailyTime');
174+
const weeklyTime = document.getElementById('weeklyTime');
175+
const monthDay = document.getElementById('monthDay');
176+
const monthlyTime = document.getElementById('monthlyTime');
177+
const customFields = document.getElementById('customFields');
178+
const timezone = document.getElementById('timezone');
179+
const generate = document.getElementById('generate');
180+
const copyBtn = document.getElementById('copy');
181+
const cronPreview = document.getElementById('cronPreview');
182+
const metaTz = document.getElementById('metaTz');
183+
const limitCount = document.getElementById('limitCount');
184+
const reset = document.getElementById('reset');
185+
const chips = document.querySelectorAll('.chip');
186+
const explain = document.getElementById('explain');
187+
const download = document.getElementById('download');
188+
189+
function showBoxes() {
190+
intervalBox.style.display = mode.value === 'every' ? 'block' : 'none';
191+
dailyBox.style.display = mode.value === 'daily' ? 'block' : 'none';
192+
weeklyBox.style.display = mode.value === 'weekly' ? 'block' : 'none';
193+
monthlyBox.style.display = mode.value === 'monthly' ? 'block' : 'none';
194+
customBox.style.display = mode.value === 'custom' ? 'block' : 'none';
195+
}
196+
197+
mode.addEventListener('change', showBoxes);
198+
showBoxes();
199+
200+
chips.forEach(c => c.addEventListener('click', (e) => {
201+
const p = e.currentTarget.dataset.preset;
202+
if(p==='@every_minute'){ mode.value='every'; intervalUnit.value='minutes'; intervalValue.value=1; }
203+
if(p==='@hourly'){ mode.value='daily'; dailyTime.value='00:00'; }
204+
if(p==='@daily'){ mode.value='daily'; dailyTime.value='00:00'; }
205+
if(p==='@weekday9'){ mode.value='weekly'; weeklyTime.value='09:00'; document.querySelectorAll('.weekday').forEach(cb=>{cb.checked = ['1','2','3','4','5'].includes(cb.value)}); }
206+
if(p==='@monthly1'){ mode.value='monthly'; monthDay.value=1; monthlyTime.value='09:00'; }
207+
showBoxes();
208+
}));
209+
210+
function pad(v){ return v.toString().padStart(2,'0'); }
211+
212+
function buildCron(){
213+
let cron = '';
214+
const tz = timezone.value;
215+
metaTz.textContent = tz || '系统时区';
216+
217+
if(mode.value==='every'){
218+
const unit = intervalUnit.value;
219+
const val = Math.max(1, Math.floor(Number(intervalValue.value)||1));
220+
if(unit==='minutes'){
221+
// 每 N 分钟: 秒 分 时 日 月 周 -> 0 */N * * * *
222+
cron = `0 */${val} * * * *`;
223+
} else if(unit==='hours'){
224+
// 每 N 小时: 0 0 */N * * *
225+
cron = `0 0 */${val} * * *`;
226+
} else if(unit==='days'){
227+
// 每 N 天: 0 0 0 */N * * (执行于每天 00:00 的间隔)
228+
cron = `0 0 0 */${val} * *`;
229+
}
230+
}
231+
232+
if(mode.value==='daily'){
233+
// dailyTime -> HH:MM
234+
const [hh,mm] = dailyTime.value.split(':');
235+
cron = `0 ${parseInt(mm,10)} ${parseInt(hh,10)} * * *`;
236+
}
237+
238+
if(mode.value==='weekly'){
239+
const days = Array.from(document.querySelectorAll('.weekday:checked')).map(i=>i.value);
240+
const [hh,mm] = weeklyTime.value.split(':');
241+
const dayField = days.length ? days.join(',') : '*';
242+
cron = `0 ${parseInt(mm,10)} ${parseInt(hh,10)} * * ${dayField}`;
243+
}
244+
245+
if(mode.value==='monthly'){
246+
const day = Math.min(31, Math.max(1, Number(monthDay.value||1)));
247+
const [hh,mm] = monthlyTime.value.split(':');
248+
cron = `0 ${parseInt(mm,10)} ${parseInt(hh,10)} ${day} * *`;
249+
}
250+
251+
if(mode.value==='custom'){
252+
const txt = customFields.value.trim();
253+
cron = txt || '';
254+
}
255+
256+
if(!cron) cron = '# 请选择调度后点击 生成 Cron';
257+
return { cron, tz };
258+
}
259+
260+
function validateSixFields(expr){
261+
if(!expr) return false;
262+
const parts = expr.trim().split(/\s+/);
263+
return parts.length === 6;
264+
}
265+
266+
generate.addEventListener('click', ()=>{
267+
const {cron, tz} = buildCron();
268+
cronPreview.textContent = cron + (tz?`\n(timezone: ${tz})`:'');
269+
270+
// 显示 limit 建议
271+
const limit = Number(limitCount.value||0);
272+
const metaLimit = document.getElementById('metaLimit');
273+
if(limit>0){
274+
metaLimit.textContent = `执行次数限制:你填写了 ${limit} 次。建议在你的任务代码中记录并在达到次数后停止调度,或用外部调度器配合。`;
275+
} else { metaLimit.textContent = '' }
276+
});
277+
278+
copyBtn.addEventListener('click', async ()=>{
279+
const text = cronPreview.textContent;
280+
try{
281+
await navigator.clipboard.writeText(text);
282+
copyBtn.textContent = '已复制';
283+
setTimeout(()=>copyBtn.textContent='复制到剪贴板',1200);
284+
}catch(e){
285+
alert('复制失败,请手动复制');
286+
}
287+
});
288+
289+
reset.addEventListener('click', ()=>{ location.reload(); });
290+
291+
explain.addEventListener('click', ()=>{
292+
alert('字段说明(6 字段):\n秒 分 时 日 月 周\n示例: 0 0 9 * * * -> 每天 09:00 执行(秒:0 分:0 时:9)');
293+
});
294+
295+
download.addEventListener('click', ()=>{
296+
const blob = new Blob([cronPreview.textContent], {type:'text/plain;charset=utf-8'});
297+
const url = URL.createObjectURL(blob);
298+
const a = document.createElement('a'); a.href = url; a.download = 'cron.txt'; a.click(); URL.revokeObjectURL(url);
299+
});
300+
301+
// 初始生成
302+
document.getElementById('generate').click();
303+
</script>
304+
</body>
305+
</html>

apps/cron-generator/package.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"name": "cron-generator",
3+
"pluginName": "定时任务表达式生成器",
4+
"version": "0.0.1",
5+
"description": "小白也能生成想要的定时任务表达式,任你掌控!",
6+
"main": "index.html",
7+
"logo": "",
8+
"pluginType": "ui",
9+
"author": "hjdhnx",
10+
"dependencies": {}
11+
}

0 commit comments

Comments
 (0)