- 移动阶段并行处理文件,按原子计数上报累计进度 - 扫描遍历阶段实时上报已发现文件数,界面不再卡在 0/0 - 新增 /api/scan/ws WebSocket 实时推送扫描状态;前端断线指数退避自动重连,并以低频轮询兜底 - 新增统计看板与媒体库筛选/排序,目录选择器路径不存在时回退到最近可用父目录 - 存放目录支持自定义并持久化,默认输出到 KMP已整理
53 lines
1.3 KiB
Go
53 lines
1.3 KiB
Go
// 状态订阅发布:将 ScanService 的状态变更实时推送给订阅者(WebSocket 等)。
|
|
package service
|
|
|
|
import "sync"
|
|
|
|
// StatusListener 状态订阅通道(缓冲 1,保底容纳一条未消费状态)
|
|
type StatusListener chan ScanStatus
|
|
|
|
// StatusHub 管理状态订阅者;发布时若消费者来不及消费则丢弃旧消息,
|
|
// 保证订阅者拿到的始终是最新状态。
|
|
type StatusHub struct {
|
|
mu sync.RWMutex
|
|
subs map[StatusListener]struct{}
|
|
}
|
|
|
|
// NewStatusHub 创建状态订阅中心
|
|
func NewStatusHub() *StatusHub {
|
|
return &StatusHub{subs: make(map[StatusListener]struct{})}
|
|
}
|
|
|
|
// Subscribe 注册订阅,返回频道与取消函数。取消后频道被关闭,写方需停止发送。
|
|
func (h *StatusHub) Subscribe() (StatusListener, func()) {
|
|
h.mu.Lock()
|
|
defer h.mu.Unlock()
|
|
ch := make(StatusListener, 1)
|
|
h.subs[ch] = struct{}{}
|
|
return ch, func() {
|
|
h.mu.Lock()
|
|
if _, ok := h.subs[ch]; ok {
|
|
delete(h.subs, ch)
|
|
close(ch)
|
|
}
|
|
h.mu.Unlock()
|
|
}
|
|
}
|
|
|
|
// Publish 向所有订阅者推送最新状态(非阻塞;缓冲满时覆盖为最新状态)
|
|
func (h *StatusHub) Publish(st ScanStatus) {
|
|
h.mu.RLock()
|
|
defer h.mu.RUnlock()
|
|
for ch := range h.subs {
|
|
select {
|
|
case ch <- st:
|
|
default:
|
|
select {
|
|
case <-ch:
|
|
default:
|
|
}
|
|
ch <- st
|
|
}
|
|
}
|
|
}
|