webui: 录制时间范围改用TimePicker,选择时间 (#1211)

* 录制时间范围前端改为选择

* 配置文件注释

* fixx

* bug fix

* Update TemplateModal.tsx

* 修改css
This commit is contained in:
Guthrie authored and GitHub committed 2025-04-02 09:37:10 +00:00
1 parent a34bcdad6b
commit 4cf8de648c
6 files changed
+75 -54

No files matched your search

+1 -1
View File
@@ -101,7 +101,7 @@ export interface LiveStreamerEntity {
upload_id?: number;
status?: string | React.ReactNode;
format?: string;
time_range?: string;
time_range?: string | Date[];
excluded_keywords?: string[];
preprocessor?: Record<'run', string>[];
segment_processor?: Record<'run', string>[];
+26 -5
View File
@@ -78,6 +78,7 @@ const TemplateModal: React.FC<TemplateModalProps> = ({ children, entity, onOk })
remark: values?.remark?.trim(),
url: values?.url?.trim(),
format: values?.format?.trim(),
time_range: JSON.stringify(values?.time_range?.map((date: Date) => date.toISOString())),
}
await onOk(values)
setVisible(false)
@@ -104,6 +105,15 @@ const TemplateModal: React.FC<TemplateModalProps> = ({ children, entity, onOk })
}
})
try {
if (entity && entity.time_range && typeof entity.time_range === "string") {
const tr: string[] = JSON.parse(entity.time_range)
entity.time_range = tr.map(t => new Date(t))
}
} catch (e) {
console.error(e)
}
return (
<>
{childrenWithProps}
@@ -205,18 +215,29 @@ const TemplateModal: React.FC<TemplateModalProps> = ({ children, entity, onOk })
style={{ width: 176 }}
helpText="视频保存格式。不支持stream-gears下载器和Youtube平台。"
/>
<Collapse keepDOM>
<Collapse.Panel header="更多设置" itemKey="processors">
<Form.Input
<Form.TimePicker
field="time_range"
type="timeRange"
placeholder=" "
extraText={
<div style={{ fontSize: '14px' }}>
格式:&apos;01:00:00-02:00:00&apos;(时:分:秒-时:分:秒)
如果设置了录制时间范围,不在时间范围内,将不进行录制<br/>
下载器需使用ffmpeg或streamlink
</div>
}
label="录制时间范围"
placeholder="01:00:00-02:00:00"
label={{
text: '录制时间范围',
optional: true,
style: {
fontSize: '18px',
marginBottom: '4px',
paddingBottom: '8px',
borderBottom: '1px solid var(--semi-color-border)',
}
}}
style={{ width: 176 }}
/>
+19 -19
View File
@@ -1,7 +1,8 @@
import asyncio
import json
import httpx
from datetime import datetime, time, timezone, timedelta
from datetime import datetime, timezone
from biliup.config import config
import logging
@@ -32,26 +33,25 @@ logger = logging.getLogger('biliup')
def check_timerange(name):
time_range = config['streamers'].get(name, {}).get('time_range')
now = datetime.now(tz=timezone(timedelta(hours=8))).time()
logger.debug(f"{name}: 校验时间范围 {time_range} 当前时间 {now.strftime('%H:%M:%S')}")
if not time_range or '-' not in time_range:
return True
try:
start_time, end_time = map(time_string_to_time, time_range.split('-'))
except (ValueError, IndexError) as e:
logger.exception(f"Invalid time range format: {e}")
time_range_str = config['streamers'].get(name, {}).get('time_range')
if not time_range_str:
return True
time_range = json.loads(time_range_str)
if not isinstance(time_range, (list, tuple)) or len(time_range) != 2:
return True
start = datetime.fromisoformat(time_range[0].replace('Z', '+00:00')).time()
end = datetime.fromisoformat(time_range[1].replace('Z', '+00:00')).time()
except Exception as e:
logger.error(f'parsing time range {e}')
return True
if start_time > end_time:
is_in_range = now >= start_time or now <= end_time
else:
is_in_range = start_time <= now <= end_time
return is_in_range
now = datetime.now(timezone.utc).time()
# Normal interval (e.g. 16:00 → 20:00)
if start <= end:
return start <= now <= end
def time_string_to_time(time_string):
h, m, s = map(int, time_string.split(':'))
return time(h, m, s)
# Cross‑midnight (e.g. 23:00 → 04:00)
return now >= start or now <= end
+27 -27
View File
@@ -1,5 +1,6 @@
import asyncio
import inspect
import json
import logging
import os
import queue
@@ -11,7 +12,7 @@ import shutil
from abc import ABC, abstractmethod
from typing import AsyncGenerator, List, Callable, Optional
from urllib.parse import urlparse
from datetime import datetime, timedelta
from datetime import datetime, timezone
import requests
from requests.utils import DEFAULT_ACCEPT_ENCODING
@@ -599,41 +600,40 @@ def get_valid_filename(name):
return s
def get_duration(segment_time_str, time_range):
def get_duration(segment_time_str, time_range_str):
"""
计算当前时间到给定结束时间的时差
如果计算的时差大于segment_time,则返回segment_time。
"""
if not time_range or '-' not in time_range:
return segment_time_str
end_time_str = time_range.split('-')[1]
now = datetime.now()
end_time_today_str = now.strftime("%Y-%m-%d") + " " + end_time_str
end_time_today = datetime.strptime(end_time_today_str, "%Y-%m-%d %H:%M:%S")
# 判断结束时间是否是第二天的时间
if now > end_time_today:
end_time_today += timedelta(days=1)
time_diff = end_time_today - now
if segment_time_str:
segment_time_parts = list(map(int, segment_time_str.split(":")))
segment_time = timedelta(hours=segment_time_parts[0],
minutes=segment_time_parts[1], seconds=segment_time_parts[2])
if time_diff > segment_time:
try:
time_range = json.loads(time_range_str)
if not isinstance(time_range, (list, tuple)) or len(time_range) != 2:
return segment_time_str
end_time = datetime.fromisoformat(time_range[1].replace('Z', '+00:00')).time()
except Exception as e:
return segment_time_str
# 增加10s,防止time_diff过小多次执行下载
if time_diff.total_seconds() <= 60:
time_diff = time_diff + timedelta(seconds=10)
now = datetime.now(timezone.utc).time()
now_sec = now.hour * 3600 + now.minute * 60 + now.second
end_sec = end_time.hour * 3600 + end_time.minute * 60 + end_time.second
hours, remainder = divmod(time_diff.total_seconds(), 3600)
minutes, seconds = divmod(remainder, 60)
# 计算到结束时间的秒数
diff = end_sec - now_sec if end_sec >= now_sec else (24 * 3600 - now_sec + end_sec)
to_parameter = f"{int(hours):02}:{int(minutes):02}:{int(seconds):02}"
try:
h, m, s = map(int, segment_time_str.split(':'))
segment_sec = h * 3600 + m * 60 + s
except Exception:
return segment_time_str
return to_parameter
if diff > segment_sec:
return segment_time_str
hours = diff // 3600
minutes = (diff % 3600) // 60
seconds = diff % 60
return f"{hours:02}:{minutes:02}:{seconds:02}"
class BatchCheck(ABC):
+1 -1
View File
@@ -229,7 +229,7 @@ user_cookie = "cookies.json" ### 使用指定的账号上传
#use_live_cover = true # 获取BILIBILI直播间封面并作为投稿封面。此封面优先级低于单个主播指定的自定义封面。
tags = ["biliup", "视频标签"]
#format = "mp4" ### 视频保存格式。如需使用mp4格式,必须切换downloader为ffmpeg或者streamlink,youtube不支持。
#time_range = "01:00:00-23:00:00" ### 录制时间范围
#time_range: "[\"2025-03-26T16:00:00.000Z\",\"2025-03-27T15:59:59.000Z\"]" ### 录制时间范围 ISO 8601 格式的时间数组字符串
#excluded_keywords: ### 如果房间名包含关键词,则停止或不录制该场直播,每个关键词需单独一行
### 开始下载直播时触发,将按自定义顺序执行自定义操作 注:preprocessor仅支持shell指令
#preprocessor = [
+1 -1
View File
@@ -223,7 +223,7 @@ streamers:
tags:
- biliup
- 视频标签
#time_range: "01:00:00-23:00:00" ### 录制时间范围
#time_range: "[\"2025-03-26T16:00:00.000Z\",\"2025-03-27T15:59:59.000Z\"]" ### 录制时间范围 ISO 8601 格式的时间数组字符串
#excluded_keywords: ### 如果房间名包含关键词,则停止或不录制该场直播,每个关键词需单独一行
#preprocessor: ### 开始下载直播时触发,将按自定义顺序执行自定义操作 注:preprocessor仅支持shell指令
#- run: sh ./run.sh, ### 执行任意命令,等同于在shell中运行,preprocessor输出的数据为JSON格式,包含主播名字(name)、开播地址(url)和开播时间(start_time)(时间戳)