diff --git a/plugins/douyu/main.py b/plugins/douyu/main.py
index 3b2d60e..5d7a039 100644
--- a/plugins/douyu/main.py
+++ b/plugins/douyu/main.py
@@ -891,18 +891,18 @@ class DouyuPlugin(MessagePluginInterface):
@staticmethod
def _normalize_audience_points(points: List[Dict[str, Any]], limit: int = 720) -> List[Dict[str, Any]]:
- normalized: List[Dict[str, Any]] = []
- seen = set()
+ normalized_map: Dict[str, Dict[str, Any]] = {}
for item in points or []:
timestamp = str(item.get("timestamp") or "").strip()
- if not timestamp or timestamp in seen:
+ if not timestamp:
continue
- seen.add(timestamp)
- normalized.append({
+ minute_key = timestamp[:16]
+ normalized_map[minute_key] = {
"timestamp": timestamp,
"vip_count": int(item.get("vip_count", 0) or 0),
"diamond_count": int(item.get("diamond_count", 0) or 0),
- })
+ }
+ normalized = list(normalized_map.values())
normalized.sort(key=lambda row: row.get("timestamp", ""))
if len(normalized) > limit:
normalized = normalized[-limit:]
@@ -1183,7 +1183,16 @@ class DouyuPlugin(MessagePluginInterface):
def _build_audience_trend(self, sessions: List[Dict[str, Any]]) -> Dict[str, Any]:
points: List[Dict[str, Any]] = []
+ segment_start_times: List[str] = []
+ segment_end_times: List[str] = []
for session in sessions:
+ for segment in session.get("segments", []) or []:
+ start_time = str(segment.get("start_time") or "").strip()
+ end_time = str(segment.get("end_time") or "").strip()
+ if start_time:
+ segment_start_times.append(start_time)
+ if end_time:
+ segment_end_times.append(end_time)
for item in session.get("audience_points", []) or []:
point = {
"timestamp": str(item.get("timestamp") or "").strip(),
@@ -1198,6 +1207,16 @@ class DouyuPlugin(MessagePluginInterface):
vip_values = [int(item.get("vip_count", 0) or 0) for item in points]
diamond_values = [int(item.get("diamond_count", 0) or 0) for item in points]
labels = [str(item.get("timestamp") or "")[-8:-3] for item in points]
+ session_start = min(segment_start_times) if segment_start_times else ""
+ session_end = max(segment_end_times) if segment_end_times else str(points[-1].get("timestamp") or "")
+ first_point_time = str(points[0].get("timestamp") or "")
+ last_point_time = str(points[-1].get("timestamp") or "")
+ leading_gap_minutes = 0
+ if session_start and first_point_time:
+ start_dt = self._parse_session_time(session_start)
+ point_dt = self._parse_session_time(first_point_time)
+ if start_dt and point_dt:
+ leading_gap_minutes = max(int((point_dt - start_dt).total_seconds() // 60), 0)
return {
"points": points,
"summary": {
@@ -1209,6 +1228,11 @@ class DouyuPlugin(MessagePluginInterface):
"diamond_max": max(diamond_values),
"diamond_latest": diamond_values[-1],
"labels": labels,
+ "session_start": session_start,
+ "session_end": session_end,
+ "first_point_time": first_point_time,
+ "last_point_time": last_point_time,
+ "leading_gap_minutes": leading_gap_minutes,
},
}
diff --git a/plugins/douyu/report_template.py b/plugins/douyu/report_template.py
index 539e45e..16dc80f 100644
--- a/plugins/douyu/report_template.py
+++ b/plugins/douyu/report_template.py
@@ -209,28 +209,56 @@ def _render_audience_trend_chart(audience_trend: Dict[str, Any]) -> str:
plot_width = width - padding_left - padding_right
plot_height = height - padding_top - padding_bottom
- vip_values = [int(item.get("vip_count", 0) or 0) for item in points]
- diamond_values = [int(item.get("diamond_count", 0) or 0) for item in points]
- labels = [str(item.get("timestamp") or "")[-8:-3] for item in points]
+ summary = audience_trend.get("summary", {}) or {}
+
+ def _compress_chart_points(raw_points: List[Dict[str, Any]], max_points: int = 36) -> List[Dict[str, Any]]:
+ if len(raw_points) <= max_points:
+ return list(raw_points)
+ bucket_size = max(len(raw_points) / max_points, 1)
+ compressed: List[Dict[str, Any]] = []
+ for idx in range(max_points):
+ start = int(round(idx * bucket_size))
+ end = int(round((idx + 1) * bucket_size))
+ bucket = raw_points[start:end] or raw_points[start:start + 1]
+ if not bucket:
+ continue
+ compressed.append(dict(bucket[-1]))
+ first = raw_points[0]
+ last = raw_points[-1]
+ if compressed:
+ compressed[0] = dict(first)
+ compressed[-1] = dict(last)
+ return compressed
+
+ chart_points = _compress_chart_points(points, max_points=36)
+ vip_values = [int(item.get("vip_count", 0) or 0) for item in chart_points]
+ diamond_values = [int(item.get("diamond_count", 0) or 0) for item in chart_points]
+ labels = [str(item.get("timestamp") or "")[-8:-3] for item in chart_points]
vip_min = min(vip_values)
vip_max = max(vip_values)
diamond_min = min(diamond_values)
diamond_max = max(diamond_values)
- vip_span = max(vip_max - vip_min, 1)
- diamond_span = max(diamond_max - diamond_min, 1)
- step_x = plot_width / max(len(points) - 1, 1)
+ vip_padding = max(int((vip_max - vip_min) * 0.12), 60)
+ diamond_padding = 1 if diamond_max - diamond_min <= 2 else max(int((diamond_max - diamond_min) * 0.2), 1)
+ vip_display_min = max(vip_min - vip_padding, 0)
+ vip_display_max = vip_max + vip_padding
+ diamond_display_min = max(diamond_min - diamond_padding, 0)
+ diamond_display_max = diamond_max + diamond_padding
+ vip_span = max(vip_display_max - vip_display_min, 1)
+ diamond_span = max(diamond_display_max - diamond_display_min, 1)
+ step_x = plot_width / max(len(chart_points) - 1, 1)
bar_width = max(min(step_x * 0.58, 18), 6)
def x_at(index: int) -> float:
return padding_left + step_x * index
def y_vip(value: int) -> float:
- ratio = (value - vip_min) / vip_span if vip_span else 0
+ ratio = (value - vip_display_min) / vip_span if vip_span else 0
return padding_top + plot_height - ratio * plot_height
def y_diamond(value: int) -> float:
- ratio = (value - diamond_min) / diamond_span if diamond_span else 0
+ ratio = (value - diamond_display_min) / diamond_span if diamond_span else 0
return padding_top + plot_height - ratio * plot_height
grid_lines = []
@@ -239,8 +267,8 @@ def _render_audience_trend_chart(audience_trend: Dict[str, Any]) -> str:
for idx in range(5):
ratio = idx / 4
y = padding_top + plot_height - ratio * plot_height
- vip_tick = round(vip_min + vip_span * ratio)
- diamond_tick = round(diamond_min + diamond_span * ratio)
+ vip_tick = round(vip_display_min + vip_span * ratio)
+ diamond_tick = round(diamond_display_min + diamond_span * ratio)
grid_lines.append(
f'